- From: Markus Lanthaler <markus.lanthaler@gmx.net>
- Date: Fri, 24 Jan 2014 14:30:02 +0100
- To: "'James Langley'" <jdhlangley@gmail.com>
- Cc: <public-hydra@w3.org>
On Thursday, January 23, 2014 9:09 PM, James Langley wrote: >> What kind of tooling would you need/like to have? What programming language >> are you using to implement your API/clients? > > We're using Java for the API. We need to be able to treat the client > response much like an XML DOM - the data would flow through a couple > of layers in our architecture, and each layer should have the > capability to modify the response. > Siren4J provides a FluentBuilder API which allows us to construct a > Siren payload easily enough, but doesn't seem to give the capability > to modify it. There are other similar libraries for pure JSON > handling, which again either assume you want to map to / from a POJO > and a JSON representation, or allow you build a JSON object and > convert it to a String. But we haven't yet found anything that would > allow us to manipulate a JSON DOM like we could an XML DOM. So you want to manipulate the JSON representation directly instead of mapping it to domain POJOs, right? In my JSON-LD processor [1] I've implemented a first version of a "node API" as I call it. That's very similar to the FluentBuilder API you talk about. It's in PHP but I'm sure you get the idea: // get the default graph from the JSON-LD request doc. $graph = $request->getGraph(); $graph->getNode('http://example.com/node1') ->setType('http://example.com/vocab#SomeClass') ->setProperty('http://example.com/vocab#propertyA', 'A') ->removeProperty('http://example.com/vocab#propertyB'); $serialized = JsonLD::toString($graph->toJsonLd()); Is that more or less what you are looking for? I'm not aware of a Java implementation but there exists a Java JSON-LD processor [2] that implements the difficult parts. You would just have to extend it with a "node API". The biggest difference here is that JSON-LD deals with graphs whereas XML DOMs represent trees but as soon as you introduce links you end up dealing with graphs anyways. > We haven't found any library that understands Hydra by default. AFAIK there exists none at the moment. That being said, a Hydra description is also just a JSON-LD graph. You just need to interpret the data properly. > Hydra gives us some nice features from a testing perspective - the > "returns" feature allows us to validate our API output automatically, > and we can re-use the same validation to apply to client-supplied data > for the "expects" side of things. It also seems that we can use the > same Hydra context as both our test blueprint and our developer > documentation, which is nice. Right, that's one of the unique selling points of Hydra I would say. I discussed that in detail in the "Model Your Application Domain, Not Your JSON Structures" paper [3]. > It also seems that we can pass better semantics back to the clients > (to support i18n, l10n, and accessibility issues) than Siren allows. Yep, you get that more or less for free. > (Because you don't seem to be able to assign a Siren Class to a Siren > Property, only to an Entity.) I plan to raise that on the Siren Google > Group later. You mean defining the "range" of a property? I thought that's possible but after re-checking the documentation I see it isn't. Apparently field types are limited to the input types specified by HTML5 [4-5]. > Where Siren seems to have an advantage is where we want the API to > change behaviour at runtime depending on the current server state, > i.e. a Class should be POSTable sometimes but not alway;, That's something we discussed in the context of authorization in the past, see ISSUE-7 [6]. The general approach would be to either define a property or a class that you include/exclude based on the resource state. Something like { "@id": "just-an-entity", "@type": [ "BlogPost", "PublishedBlogPost" ], "comments": "just-an-entity/comments" } In this case, the operations that are supported on all posts are associated to the "BlogPost" class. The operations only applicable to published posts are associated to the "PublishedBlogPost" class. Similarly, the "comments" property is just included on posts that can be commented on. I see that a lot of people run into this question, I've thus created ISSUE-23 [7] so that we don't forget to explicitly mention it in the spec. > or a POST may return one Class in some circumstances, and another > Class at other times (and the two Classes are not both semantically > subclasses of the same super Class). A static Hydra context doesn't > allow this. Do you need to document the circumstances under which each class is returned or is it enough to know that you may get different responses? Depending on whether you are willing to use other vocabularies or not, you might be able to describe that using OWL. You could create a union class [8]: { "@context": { "owl": "http://www.w3.org/2002/07/owl#", "owl:unionOf": { "@type": "@id" } }, "@id": "People", "@type": "owl:Class", "owl:unionOf": [ "Man", "Woman" ] } and use it in "returns". Depending on what we decide to do with multiple values of "returns" and "expects" (are requests/responses instances of all classes or just of at least one class; ISSUE-22 [9]) > (We have been thinking about serving different Hydra contexts at > different times to get round this, but I'm not convinced taht's a > god way to go.) In most cases I agree it isn't a good way to address these issues. One reasonable exception is if the operations depend purely on the authentication of the user. In such a case I would say it makes sense to dynamically serve different contexts and either serve them from different URLs or include a Vary: Authorization header (or something similar) to avoid caching problems. > And because of the lack of an upfront API document, I suspect Siren > client developers are more likely to code generic clients that can > cope with any Siren response, whereas a Hydra client developer might > code a client specific to our Context. That's a valid concern. I deliberately left out a mechanism to describe target URLs in a Hydra description to avoid that. So, in any case, a developer would need to find the URL to POST to by inspecting responses he gets. It's then only a very small step to retrieve and parse the Hydra description dynamically as well. But you are right in principle. It should, however, also be noted that you don't have to use a separate Hydra description at all. You can also embed operations directly into responses as Siren does. See example 6 in the spec [10]: { "@context": "http://www.w3.org/ns/hydra/context.jsonld", "@id": "/an-issue", "title": "Do not rely on upfront API document :-)", "description": "This issue can be deleted with an HTTP DELETE request", "operations": [ { "@type": "DeleteResourceOperation", "method": "DELETE", "title": "Delete this issue" } ] } The disadvantage of doing so is obviously an increased overhead. Typically, in Web APIs responses don't vary much and thus it makes sense (IMO, anyway) to move those descriptions into a separate document that can be cached for a *long* time. Btw. please don't conflate a JSON-LD context (which maps JSON elements to URLs) with a Hydra description (which describes your service). Cheers, Markus [1] https://github.com/lanthaler/JsonLD [2] https://github.com/jsonld-java/jsonld-java [3] http://m.lanthi.com/wsrest2013-paper [4] https://github.com/kevinswiber/siren?source=c#type-1 [5] http://www.w3.org/TR/html5/forms.html#attr-input-type-keywords [6] https://github.com/HydraCG/Specifications/issues/7 [7] https://github.com/HydraCG/Specifications/issues/23 [8] http://www.w3.org/TR/owl2-syntax/#Union_of_Class_Expressions [9] https://github.com/HydraCG/Specifications/issues/22 [10] http://www.hydra-cg.com/spec/latest/core/#adding-affordances-to-representati ons -- Markus Lanthaler @markuslanthaler
Received on Friday, 24 January 2014 13:30:36 UTC