Re: Inheritance / composition of term definitions

Thank you both for your responses!

On 26 Nov 2015, at 19:38, Markus Lanthaler wrote:

> On Wednesday, November 25, 2015 6:40 PM, Dietrich Schulten wrote:
>> On 19 Nov 2015 at 17:19, Lukas Graf wrote:
>>> I’m currently involved in designing and implementing a REST API for a CMS (Plone) that
>>> is supposed to eventually be hypermedia driven, using JSON-LD and Hydra.
> 
> Very cool! I'm already looking forward to see the first prototype.

Development is happening at https://github.com/plone/plone.restapi and preliminary docs are at http://plonerestapi.readthedocs.org

(The demo linked in the README unfortunately is currently pretty outdated, updating that is on my list of todos).

The API has been initiated with a docs-first approach, and large parts of the implementation are currently just stubbed out. Also, the parts concerning JSON-LD and Hydra are still very much in flux.


>>> On a most basic level, our CMS makes a distinction between contentish types and folderish
>>> types (Items and Containers if you will). The CMS supports user-defined content types with
>>> their own schema, so JSON-LD contexts for those will need to be generated dynamically by
>>> the API.
> 
> Not necessarily. You can use @vocab [1] to automatically map all non-otherwise mapped properties. But it obviously depends on what exactly you need.

The thing is, the CMS allows developers (and even end-users) to create custom content types with their own schemata, validators etc.. Even through the web, in a declarative fashion without much programming. This is one of the primary use cases for this CMS, lots of people don't even bother with the stock content types but primarily implement their own (Plone happens to sit in a weird place between a CMS application and a CMS *framework* actually).

So there's no way we could know in advance what content types and schemata our users will come up. My idea to address this is to allow content type developers to annotate their schemata with some sort of semantic hints (tagged values maybe), for example if they'd like to indicate that they content type is a schema.org/Person. With these hints and the basic information provided by the content types schema, we would then dynamically generate contexts for each content type. Without additional hints these would basically boil down to an enumeration of the available fields, whether they're required for that CT or not, and the field's types.

But @vocab seems like a very useful tool to manage namespacing and avoid littering our attributes with prefixes.

>>> If possible, I’d also like to be able to keep attribute names as defined in the domain specific
>>> model - meaning, I’d like to name the list of users in a group "users", participants of a
>>> meeting "participants", etc., instead of naming them all "member".
> 
> Why would you want to switch to a generic container type such as Hydra's Collection then?

Because I thought we had to ;-)

The goal is to use Hydra to provide discoverability for our API. Eventually, we would like to enable people to build smart clients that can figure out on their own that X is a folderish type where resources of type Y can be added to, and use the information from type Y's context to provide the user with a form to enter the required fields. So similar to what your Hydra Console does for the /users or /issues endpoints of the api-demo.

I don't quite understand yet what exactly the requirements are for a service to be able to fullfill that. Does the containing resource necessarily have to be of type hydra:Collection (or a subclass thereof)? Or are those capabilities mainly enabled by the hydra:Operation's defined for /users?

>>> - Keep the name "rooms" (instead of "member"), as that's would be the
>>> attribute name used in the content types data model, which API consumers
>>> would be familiar with
>> 
>> Perfectly possible, as long as there is no need to address the rooms
>> as a resource, with its own url that resolves to a collection of
>> rooms.
> 
> Exactly. The collection would sit between the building and the room in this example. Do you need that?

Ah, I think Dietrich is onto something here - this might be the main point where I got confused.

We definitely do need to be able to access contentish resources (a single room in this case) via their own URL.

But: I now realize that the example I've constructed here is flawed in the sense that it misrepresents how our CMS works - if a content type is folderish, its children are accessed via an attribute that's called the same for all folderish types, so there would be no "rooms" attribute on our data model. The case were we're dealing with named collections only applies to lists of references, not containment. Sorry about that, I somehow managed to mix those two together :-/

So the statement I made above "Keep the name "rooms" (instead of "member")" doesn't actually apply. 

However, there might still be a need for us to have the collection sit in beetween the container and the contained object, because our containers also have their own metadata needs to be accessible. So if we were to simply map containment to HTTP like

GET /buildings/main/ - retrieves the collection members of the "main" building
GET /buildings/main/lobby - retrieves the "lobby" room
(POST /buildings/main/ - create a new "Room" resource in the "main" building)

we wouldn't have a way to access the collection's metadata (the building's "address" for example). So for that reason it seems we do need some intermediate level between the container and the contentish resource.

>>> - Have the term be part of the domain specific JSON-LD context
> [...]
>>> - Still associate the "rooms" term with "hydra:member",
>>> so that Hydra capable clients have the necessary information to figure
>>> out that the "rooms" property contains the collection members
> 
> What would they gain by that? Do you want to allow users to filter the rooms for example?

That's definitely a use case as well, but we'll handle that via a separate search / query endpoint. My idea was that any client that is Hydra capable (?) could then identify /buildings as a collection, and have (or know where to find) the necessary information on how to add a resource to that collection, without any other knowledge about the underlying domain-specific models.

Also, batching / pagination will definitely be something we'll need to implement. So hydra's PartialCollectionView would possibly come into play for that.

> You could definitely make the "rooms" property a sub-property of hydra:member but for that to be useful we would need to require all clients understand such things.

After Dietrich and your explanations, I don't think that's what we want (or need).

>> The hydra:member attribute belongs on a hydra:Collection. A
>> hydra:Collection is mainly useful in situations where you need to
>> address a collection resource with a single URl, e.g. because you want
>> to POST to it or to offer a UriTemplate to search the collection. The
>> new collection construct in the working group wiki, which is not in
>> the spec yet, shows how this works:
>> 
>>   "@context":"http://example.org/", 
>>   "@type":"Building", 
>>   "@id":"http://example.org/buildings/main", 
>>   "name":"Main Building", 
>>   "address": "...", 
>>    "hydra:collection" : [{
>>      "@id":"/rooms",
>>      "hydra:operation" : ... 
>>       "hydra:manages" : {
>>           "hydra:subject" : "/buildings/main", 
>>            "hydra:property" : "rooms" 
>>       } 
>>      "hydra:member":[ 
>>        { 
>>         "@type":"Room", 
>>         "@id":"http://example.org/buildings/main/lobby", 
>>         "name":"Lobby" 
>>        }, 
>>        { 
>>         "@type":"Room", 
>>         "@id":"http://example.org/buildings/main/blue-room", 
>>         "name":"Blue Room" 
>>        } 
>>     ] 
>>   }] 
>> } 
>> 
>> The new Collection has a "manages" attribute which allows to say that
>> the items are rooms of the building.
> 
> Yep, that could be used as well. But given that these will be custom vocabularies AFAICT, it would be much easier to simply point "rooms" to the collection of rooms instead of individual rooms.

That does in fact look interesting. But this does exhibit the deep nesting I wanted to avoid (an array "hydra:member" inside a "hydra:collection").

>> Three contexts, two external ones, and a local one:
>> 
>> "@context": [ 
>>    "http://example.org/context.jsonld", 
>>     "http://www.w3.org/ns/hydra/context.jsonld", 
>>    { 
>>      "rooms": "hydra:member" 
>>     } 
>> ]
>> 
>> ... assuming that the external base context is not returned at the
>> root of example.org, but as a resource context.jsonld below it, and
>> that resource is expected to return a context. If you just want to
>> define a default prefix for all attributes without prefix, you want
>> @vocab within the local context. I would expect rooms to be mapped to
>> a URL like http://example.org/vocab#rooms in your domain specific
>> context.


Yes, just using http://example.org as the IRI for our domain specific context was a simplification for the sake of brevity. In reality, there will probably one context per content type, and it would maybe be referenced as http://example.org/contexts/Building.jsonld. But then many content types also include some standard DublinCore metadata, so that would be a separate context, possibly even hosted externally.

But as for namespacing of properties / terms, I think with @vocab and local contexts we have all the tools required to manage that.

>> Last one wins. 
> 
> Yes, correct. You can't map a single term/property to multiple IRIs.

At this point I'm thinking I shouldn't be trying to.

>> Or at least I am not sure about your motivation to alias your rooms
>> with :member :-) 
> 
> Shedding some light on your motivation to do so would indeed be helpful.

I can't necessarily speak for other contributors to our API here, but what I'd like to accomplish with Hydra is to point your Hydra console (or a Hydra client reference implementation, if there is / will be be such a thing) at our API, and basically have it provide the same capabilities that it provides for your api-demo endpoint.

hydra:member is what we see being used in the example section "5.1 Collections" of the Hydra spec ;-) But honestly, it's not quite clear to me what exactly we would gain with using hydra:Collection, resp. what we'd lose if we wouldn't be using it.

In the case of PartialCollectionView it's much more concrete: Providing a standardized way to represent pagination URLs would satisfy the hypermedia-driven aspect for our API for that case, where a client wouldn't have to build their own URLs.

I hope I was able to provide *some* more background on our motivation and use cases.

And while I'm trying to keep this Hydra related and want to avoid this thread becoming a support case for our own cause, I really do appreciate you guys taking the time to find out what it is we need.

Thanks,

Lukas

Received on Friday, 27 November 2015 16:05:17 UTC