- From: Dietrich Schulten <ds@escalon.de>
- Date: Sat, 21 Feb 2015 16:18:10 +0100
- To: Kev Kirkland <kev@dataunity.org>
- CC: Hydra <public-hydra@w3.org>
- Message-ID: <54E8A1B2.6060309@escalon.de>
Hi, Am 20. Februar 2015 11:41:26 schrieb Kev Kirkland <kev@dataunity.org>: > I'm no expert, but it looks like the schema.org example you have is doing > a different thing to a REST API. The link above is more like data modelling > of what is on offer, whereas REST (Representational *State Transfer*) > should model the current state and how to change it. That is exactly what I am after :) The need to document attributes and state transitions somewhere in an online documentation has driven me crazy, that is why I am on this list. It should not be that way. If you look at the way generic clients work with newsfeeds and blog posts, you quickly realize that it should not be necessary to code a special client for every single rest api out there. The example I gave at https://lists.w3.org/Archives/Public/public-vocabs/2015Feb/0092.html shows what is on offer, but not the state transitions, because it seems pretty unclear to me how to attach the necessary links in a semantically correct way, so that every client would understand what to do (with http://schema.org/potentialAction, see also [1]). The question is a very practical one: exactly how do I express the state transitions the client can use, without introducing new attributes, but solely based on schema.org for product descriptions and offerings and hydra for interactions. And, *most important*: in a self-describing way, so that clients can GET a cup of coffee without a human who programs every single interaction. > If you imagine being at the coffee counter. The first state might be coffee > choices, so your API would return something like: > > CoffeeChoice: > Link: Choose Americano > Link: Choose Latte So let's do it. I have the following as entrypoint. Where do I attach the link to post to /orders? { "@context": "http://schema.org/", "@type": "CafeOrCoffeeShop", "@id": "http://example.com/store", "name": "Kaffeehaus Hagen", "makesOffer": [ { "@type": "Offer", "@id": "http://example.com/offers/1234", "itemOffered": { "@type": "Product", "@id": "http://example.com/products/latte", "name": "Latte Macchiato" }, "price": 2.8, "priceCurrency": "EUR", }] } In hydra I describe a possible http method as a hydra:Operation of a Resource. That description could be found in the ApiDocumentation where I could define a supportedClass with supportedOperation. Or I may describe it right here as a hydra:operation property. The target url of the operation is the @id of the subject which has the :operation property. That means, in the object above I can define an operation on the CafeOrCoffeeShop, the Offer or the Product, because they have an @id. We want to POST somewhere. But to what resource? We cannot POST to the shop, the offer or the product resource to create an order. We need to POST to some resource which will create an order for us, and we want to describe the body of the POST. We can try to enhance the product in the object above like this: ... surrounding Offer ... { "@context": { "hydra": "http://www.w3.org/ns/hydra/core#", "hydra:property": { "@type": "@vocab"} }, "@type": "Product", "@id": "http://example.com/products/latte", "name": "Latte Macchiato", "order": { "@id": "http://example.com/orders" "hydra:operation" : { "hydra:method": "POST", "@type": "OrderAction", "hydra:expects": { "@type": "Product", "hydra:supportedProperty": [ { "hydra:property": "name", "hydra:required": true } ] }, "hydra:returns": "Order" } }, } ... surrounding Offer continued. Note that I had to add a property "order" to express the target URI of the POST. This tells the client that it may POST to "http://example.com/orders" and use the following body: { "@context": "http://schema.org/" "@type": "Product", "name": <a product name> } If name=Latte Macchiato then the response could be: 201 Created Location: http://example.com/orders/1234 { "@context": { "@vocab": "http://schema.org/", "hydra": "http://www.w3.org/ns/hydra/core#", "hydra:property": { "@type": "@vocab"} }, "@type": "Order", "@id": "http://example.com/orders/1234" "orderedItem": [ { "@type": "Product", "name": "Latte Macchiato" }] ... } Now we could add links to the response for follow-up transitions. Not so bad at first sight, but there are issues both in schema.org and in hydra: 1.) There is no http://schema.org/order property, neither on schema:Product nor could I find anything else like it. So what do I do? Coming from an ordinary REST background, I need some rel the client can use to look up the next transition. If the rel is not publicly defined (like http://schema.org/order) how does the client achieve its goal? 2.) I can say with hydra that the POST supports a product name property, however what I really want to say is that the product name to POST is "Latte Macchiato". In an html form I would use a readonly input. I could use schema:readOnlyValue=true with schema:defaultValue="Latte Macchiato", but my feeling is, we should be explicit how these things may be expressed in hydra. Without a defaultValue the client needs to know how to apply the product name from the surrounding offer for a latte macchiato to the nested operation, which seems a bit too much. That is, the above won't work, really. We aren't quite there yet. Another possibility would be to add a hydra:Collection without 'manages block' to the shop: { "@context": "http://schema.org/", "@type": "CafeOrCoffeeShop", "@id": "http://example.com/store", "name": "Kaffeehaus Hagen", "hydra:collection": { "@id": "http://example.com/orders", "hydra:operation": [ { "hydra:httpMethod": "POST", "@type": "OrderAction", "hydra:expects": { "@type": "Product", "hydra:supportedProperty": [ { "hydra:property": "name", "hydra:required": true }] }, "hydra:returns": "Order" }] }, "makesOffer": [.. Offers with nested products ..] } I start to like some aspects of the new collection design :) Omitting the manages block solves the problem of the missing schema:order property, but not the problem that a generic client must be awfully smart to apply the latte macchiato which is on offer when posting to the /orders resource. Ah yes, and finally it occurred to me that I could use hydra:collection on the Product and describe a POST which orders a Latte Macchiato without putting the burden to the client to apply products to the offer. Just because with hydra, I can :) { "@context": { "@vocab": "http://schema.org", "hydra": "http://www.w3.org/ns/hydra/core#", "hydra:property": { "@type": "@vocab"} } "@type": "CafeOrCoffeeShop", "@id": "http://example.com/store", "name": "Kaffeehaus Hagen", "makesOffer": [ { "@type": "Offer", "@id": "http://example.com/offers/1234", "itemOffered": { "@type": "Product", "@id": "http://example.com/products/latte", "name": "Latte Macchiato", "hydra:collection": { "@id": "http://example.com/orders" "hydra:operation" : { "hydra:method": "POST", "@type": "OrderAction", "hydra:expects": { "@type": "Product", "hydra:supportedProperty": [ { "hydra:property": "name", "hydra:required": true, "defaultValue": "Latte Macchiato", "readOnlyValue": true } ] }, "hydra:returns": "Order" } } }, "price": 2.8, "priceCurrency": "EUR", }] } Comments? Is it a recommendable practice to use hydra:collection in case there is no dedicated extension rel (aka linked data property) which precisely matches the interaction needs - such as the missing http://schema.org/order property in the example above? Should we have a construct in hydra which is equivalent to a read-only or hidden input field? In the example above above I had to borrow from schema.org. Right now there seem to be many (too many?) ways how the typical transitions in an application may be designed. My feeling is, we need to document a robust way how servers may add arbitrary transitions in the application state. The approach shown last has interesting consequences for a generic hydra client. While in REST we normally assume that a client simply looks for a rel which tells it about possible transitions (rel="order", href="http://example.com/orders"), a hydra client might be required to look for objects with hydra:operation properties in order to find transitions which change resource states. I.e. a hydra client would not look for a particular rel or attribute ("http://schema.org/order") to learn how to order, but has to look for a hydra:operation nested inside any property of the desired schema:Product which matches its goal. To identify the goal, the operation @type becomes vital (here: OrderAction). In this case: if http://schema.org/order existed, servers could use both schema:order and hydra:collection to attach a transition which creates an order. Clients must look inside both. We will have a hard time to explain why this is not an attempt to re-introduce RPC (client looks up the order() procedure on the product object). My main argument is right now, that to perform an OrderAction, multiple http method invocations may be involved. But I have yet to show how that would look like. Based on the latest design above, I'll continue with the next steps: - pay for the order: POST payment to /payments. Where do I attach the payment link? - continue ordering: PUT an extended order with another product to /order/1234. - update my latte macchiato order and add an extra shot, not separately in an extra bowl, but right inside my latte macchiato - track the delivery After so much talk about digital coffee, I'll now go and get one in real life :o) Best regards, Dietrich [1] http://schema.org/docs/actions.html
Received on Saturday, 21 February 2015 15:18:53 UTC