RE: Getting around in Event API Demo

Hi Dietrich,

Since most questions have already been answered, I'm focusing on those that
haven't (or need more explanation) in this mail.

On 7 Okt 2014 at 09:03, Dietrich Schulten wrote:
> The meat of the event-api is at
> http://www.markus-lanthaler.com/hydra/event-api/vocab. Observation: the
> vocab is quite lengthy for a simple use case. 241 LOC, much of it seems
> repetitive.

I haven't optimized this at all yet. That being said, I don't think this
will cause any performance problems in practice. Typically, the overhead due
to this manifests itself only in the very first request.



> We are talking about a pretty standard situation: you can POST, PUT
> and DELETE to the /events resource. If you POST or PUT, your request
> should contain an object that has attributes as defined by
> http://schema.org/Event. Only some of them are required, most are not
> supported.

So what's standard about this? :-) I know of no standard that defines this.
It is certainly a common pattern but not more.


> My general feeling is: it should not be necessary to maintain such a
> large description. Somehow we must leverage convention over
> configuration and the existing vocabs in such a way that I only need
> to define things I cannot take for granted.

So, in your opinion, what things could we take for granted?


> And we must be more DRY.

Yes, definitely. Optimizing this is on my to do list.


> I want to keep my context short so I can embed it. With Javascript
> clients in mind, I'd rather have everything in the resource that is
> needed to work with it.

The Hydra ApiDocumentation we are talking about here and a JSON-LD context
are two entirely different things. The former describes the service
interface, the latter defines the mapping from terms (think JSON property
names etc.) to IRIs.


> First of all, http://schema.org/event appears on Place, Organization
> and a number of Actions. In RDF, are we free to use schema:event on an
> EntryPoint? Can I state
> 
> "schema:event rdfs:domain hydra:EntryPoint"
> 
> somewhere in my context to allow this usage? And do I have to, don't I
> say that already by using schema:event on a hydra:EntryPoint? In the
> json world no "static typing" exists that would prevent me from adding
> any attribute.

In RDF, basically everything is allowed unless it is explicitly forbidden.
You also need to keep in mind that RDF is typically defined in terms of
entailments and not constraints. In this case here that would mean that 

   schema:event rdfs:domain hydra:EntryPoint

doesn't say that the event property can *only* be used on EntryPoints but
that all resources that the event property is used on *are* in fact
EntryPoints. So, with the above statement, the following snippet

  </resource> rdf:type ex:JustAResource ;
              schema:event ...

isn't invalid. It is completely fine.. but you need to keep in mind that
RDFS-aware clients would interpret this as

  </resource> rdf:type ex:JustAResource ;
              rdf:type hydra:EntryPoint;    <--- this is inferred
              schema:event ...

This is probably the most confusing aspect of RDF (and RDFSchema, which
doesn't define "schemas" in the traditional sense).


> Second: Assuming that we are allowed to use http://schema.org/event:
> Values of schema:event are expected to be of type Event, not of type @id.

This is a JSON-LD convention. Type-coercing a property to @id allows you to
use string values instead of @id-objects:

    {
    "@context": {
      "event": { "@id": "http://schema.org/event", "@type": "@id" }
    },
    ...
    "event": "http://example.com/my-event"
  }

is equivalent to

  {
    "@context": {
      "event": "http://schema.org/event"
    },
    ...
    "event": { "@id": "http://example.com/my-event" }
  }

but looks nicer if you don't have to attach other properties to /my-event.
If you wouldn't type-coerce "event" to @id in the first example, the URL
wouldn't be interpreted as a URL but just as a string. In other words, it
would be the same as

  {
    "@context": {
      "event": "http://schema.org/event"
    },
    ...
    "event": { "@value": "http://example.com/my-event" }
  }


> It would be easy to program clients so that they understand the
> difference and transparently resolve links for data. But does RDF
> allow that? Is it correct to use a link to events instead of embedding
> the events themselves? If not, can Hydra introduce such a convention?

Yes, it is effectively the same. The only difference is that you need more
HTTP roundtrips to retrieve the data.


> Or can I at least redefine
> 
> "schema:event rdfs:range jsonld:@id"
> 
> somewhere in my @context?

No


> On to the EventCollection. My wishful-thinking EventCollection:
> 
> {
>   "@context": {
>     "@vocab": "http://schema.org",
>     "hydra": "http://www.w3.org/ns/hydra/core#"
>   },
>   "@id": "http://www.markus-lanthaler.com/hydra/event-api/events/",
>   "@type": "hydra:Collection",
>   "hydra:member": [
>     {
>       "@id": "http://www.markus-lanthaler.com/hydra/event-api/events/1",
>       "@type" : "hydra:Link"

The resource /events/1 isn't a hydra:Link but a schema.org/Event or
hydra:Resource as others pointed out already.


>     },
>     {
>       "@id": "http://www.markus-lanthaler.com/hydra/event-api/events/2",
>       "@type" : "hydra:Link"
>     }
>   ],
>   "hydra:operation": [
>     {
>       "method": "hydra:CreateResourceOperation",
>       "hydra:supportedProperty" : [

You do realize that by doing it this way you reduce re-usability compared to
how we currently do it in Hydra, right? Currently, you would just have to
specify 

   "hydra:expects": "Event"

and bind the supported properties to that class. But what surprises me more
is


>         "name",
>         "description",
>         "startDate",
>         "endDate",
>         {
>           "property": "performer",
>           "required" : false,
>           "hydra:supportedProperty" : [
>             "name"
>           ]

This nesting of supported properties. This would effectively say that a
property supports another property. I think what you actually wanna say is
that the value (range) of that property supports this other property, right?

>         }
>       ]
>     }
>   ]
> }
> 
> I assumed that we could have several conventions here.
> 
> 1. It is usually not necessary to know the type of the collection
> members in order to create them, since the properties we send are by
> definition in the domain of a Thing, e.g. Event.

Right, but in most cases it makes the server's implementation much much
simpler since it is easier for the server to find the right resource in the
graph that the client sends in the request.


> Therefore I simply used hydra:Collection.

This hasn't much to do with the type of the collection's members.


> Maybe Collection could have a "hydra:of" Property so that one could say
>   "@type": {
>     "@id": "hydra:Collection",
>     "hydra:of" : "Event"
>   }
> rather than defining a custom class.
> But right now I do not see the need for a hydra:of or for typed
> collections. Well, I guess there *is* a reason why you introduced the
> EventCollection :) Could you explain?

I introduced EventCollection because I needed a specialization of the
generic Collection class to associate an operation to it that then
automatically applies to all its instances. Assume we also have a collection
for performers. In that case I can't associate the "create event operation"
to hydra:Collection because then it would also apply to performer
collections. Obviously, if you define operations inline instead of defining
them in the ApiDocumentation at the class level you don't need to introduce
a specialized class. I explained this in my talk at APIconUK:

   http://youtu.be/fJCtaNRxg9M?t=21m30s
   slides: http://slidesha.re/1tB6TFC


> 2. The usual case is that an operation is supported when the resource
> comes with the appropriate affordance. Mostly it is not something that
> is always supported, since that does not depend on the Class, but on
> the usage context. E.g. posting a new Event is probably not supported
> if you do not have sufficient rights to do so. The server simply does
> not include it if it is not supported. I can hardly imagine cases
> where a state-changing operation is *always* supported on a resource.
> Hence I put the operation into the resource, not in the documentation.

That's a decision you have to make. Nevertheless, you can use classes as
"markers" to enable/disable functionality at runtime (kind of feature
flags). That way, instead of having to include the complete definition of an
operation, you simply add or remove a class from that specific instance
depending on the user's permissions.


> 3. It is usually only necessary to list the names of supported
> properties for an operation, so instead of saying "property": "xxx"
> many times I just listed the property names.

Yeah, we track this as part of ISSUE-37 [1].


> 4. There is no need to describe the meaning of status codes in the
> resource. I am strongly against overriding the meaning of existing or
> defining custom HTTP status codes anyway :) The body of an error

We do not want to override it but add some more details.


> response is the place to give hints how to resolve a problem, and it
> should probably follow
> http://tools.ietf.org/html/draft-ietf-appsawg-http-problem. Could

It's quite closely aligned already AFAICT.


> hydra:Error be redefined in such a way that a json-ld client accepting
> ld+json could by convention get the below error response (with the
> standard properties type, title, status, detail, instance and two
> extension properties)?

What would we need to redefine to achieve that? Please also have a look at
ISSUE-27 [2] and ISSUE-39 [3].


> 
> HTTP/1.1 403 Forbidden
> Content-Type: application/problem+json

You either need to change the content type to application/ld+json or add an
HTTP Link header [4] to make it clear to clients that this can be
interpreted as JSON-LD.

> Content-Language: en
> 
> {
>   "@context":
>   {
>       "@vocab" : "http://www.w3.org/ns/hydra/core#",
>       "balance" : "http://bank.example.com#balance",
>       "accounts" : "http://bank.example.com#accounts"
>   }
>   "@type": "Error",
>   "type": "http://example.com/probs/out-of-credit",
>   "title": "You do not have enough credit.",
>   "status": 403,
>   "detail": "Your current balance is 30, but that costs 50.",
>   "instance": "http://example.net/account/12345/msgs/abc",
>   "balance": 30,
>   "accounts": ["http://example.net/account/12345",
>                "http://example.net/account/67890"]
> }
> 
> - ------------------------
> 
> Up next, Event:
> 
> {
>   "@context": {
>     "@vocab": "http://schema.org",
>     "hydra": "http://www.w3.org/ns/hydra/core#"
>   },
>   "@id": "http://www.markus-lanthaler.com/hydra/event-api/events/1",
>   "@type": "Event",
>   "name": "Walk off the Earth - REVO tour",
>   "description": "Live in concert",
>   "startDate": "2014-06-14T20:00:00Z",
>   "endDate": "2004-06-14T23:34:30Z"
>   "hydra:operation": [
>     {"hydra:method": "DELETE"},
>     {
>       "hydra:method": "PUT",
>       "hydra:supportedProperty" : [
>         "name",
>         "description",
>         "startDate",
>         "endDate",
>         {
>           "property": "performer",
>           "required" : false,
>           "hydra:supportedProperty" : [
>             "name"
>           ]
>         }
>       ]
>     }
>   ]
>  }
> Since I have sufficient rights, I can delete and replace the event.
> When I replace an Event, I must send the same data as for a creation.

OK, and how should a machine figure that out?


> Clearly our operations overlap with http://schema.org/Action.
> I tried to add a schema:ReviewAction onto the Event above, but I ran
> into several problems. That is worth a different discussion, however.
> 
> - ------------------------
> 
> Conclusion:
> 
> In my opinion, this should be all I need to say for the simple CRUD use
> case under discussion. But I guess there are some pitfalls I fell into.
> 
> I guess the event-api as it is today tries to solve a slightly
> different problem, maybe to keep the json resource as terse as possible.

The goal of that demo API is to show that a JSON-LD + Hydra powered Web API
can look almost exactly the same as a traditional JSON-based Web API but
nevertheless have the advantage of self-describing messages (aka
well-defined meaning) and support for hypermedia (links and operations). It
also acts as a show case of how to upgrade an existing JSON-based API to se
JSON-LD + Hydra.


> I see that quite relaxed, my idea is, if you request application/json
instead
> of ld+json, then the server returns a response as terse as can be.

But that changes the interpretation completely. In other words, the
responses lose *all* their meaning.


Thanks for taking the time to write all of this down,
Markus


[1] https://github.com/HydraCG/Specifications/issues/37
[2] https://github.com/HydraCG/Specifications/issues/27
[3] https://github.com/HydraCG/Specifications/issues/39
[4] http://www.w3.org/TR/json-ld/#interpreting-json-as-json-ld


--
Markus Lanthaler
@markuslanthaler

Received on Monday, 13 October 2014 15:25:51 UTC