HydraCG / Specifications

Specifications created by the Hydra W3C Community Group
Other
138 stars 26 forks source link

Adding already existing resources as collection members #134

Open elf-pavlik opened 6 years ago

elf-pavlik commented 6 years ago

Currently in our Use Cases we only have situation where we 5. Create a new event using operation advertised by the collection of events, which automatically adds it to that collection.

I would like to add use case of adding already existing resources to collections. For example if we have People in this API, each person could reference a 'personal collection of events' using hydra:collection. But we wouldn't want to create new events in that collection but instead just add already existing events. Possibly using schema:AddAction rather than schema:CreateAction.

What would such action reference with hydra:expects? It pretty much just needs an @id which should get added to hydra:member of that collection.

Once we roughly agree on approach to that I will make PR with this additional use case!

This issue somehow reminds me of https://tools.ietf.org/id/draft-snell-link-method-01.html

@RubenVerborgh do you know how currently Solid handles that? Probably it wouldn't use ldp:Container but just ldp:Resource to represent such collection and add statements with hydra:member using HTTP PATCH

lanthaler commented 6 years ago

Yeah, there are multiple options. The Link/Unlink methods seem to express the intent in the most explicit way, POSTing an schema:Person with the @id already set to a collection would be fine too I guess but opens a can of worms if the property values differ. Removing it from a collection is trickier though.

tpluscode commented 6 years ago

And what are the best practices found around the web?

lanthaler commented 6 years ago

Would you volunteer to research how a handful of popular/well-known APIs do that? :-)

tpluscode commented 6 years ago

Sure, I can look around

lanthaler commented 6 years ago

Awesome. Thanks

elf-pavlik commented 6 years ago

POSTing an schema:Person with the @id already set to a collection would be fine too I guess but opens a can of worms if the property values differ.

They payload of request to /people/alice/events collection could include just { "@id": "/events/42" } since we only want to add reference, updating that event itself would require request directly to /events/42.

Removing it from a collection is trickier though.

Good point, here HTTP verb like UNLINK would come very handy. I think may need PATCH to get this 'remove reference' functionality.

BTW if we DELETE /events/42 do we rely on the server to remove it from /events collection members? Similar to https://www.w3.org/TR/ldp/#ldpc-HTTP_DELETE

I have impression that

alien-mcl commented 6 years ago

Well, DELETEing /events/42 should remove that very resource, so further calls to that URL should end up with 4XX. Other thing is whether the 42 event should be removed from the events collection. That is up to the server as it knows whether there actually is an events collection - it may be just a filtered view of some data without any formal requirements (i.e. in SQL world it could be a table of events, bu here it can be a bag of concepts) As for adding - client still can PUT /people/alice/events/42, but this will require it to gain knowledge on how to create valid URL's

asbjornu commented 6 years ago

Removing it from a collection is trickier though. Good point, here HTTP verb like UNLINK would come very handy. I think may need PATCH to get this 'remove reference' functionality.

I'd vote for PATCH with JSON Patch. Also, to add links to a collection, perhaps @dret's Linkset format can be used?

tpluscode commented 6 years ago

BTW if we DELETE /events/42 do we rely on the server to remove it from /events collection members?

It's probably the sensible thing to do. But I don't think Hydra should mandate any 'correct' behaviour.

I think it's beside the point here, which is how to add existing resources as collection members. Could those be resources from external APIs?

@asbjornu The problem is that Hydra still doesn't have a way to allow defining what media type a given operation supports.

Would that be a valid exercise for this use case? Use JSON Patch (or else) to support adding and removing collection elements? Or any other custom need, for that matter.

elf-pavlik commented 6 years ago

I created #137 do discuss if spec should mandate for servers to maintain membership triple for deleted resources. This way we can stay focused here on the original issue.

I think it's beside the point here, which is how to add existing resources as collection members. Could those be resources from external APIs?

I think we can start with resources in the same dataset but eventually it has to work for any resource on the web.

I'd vote for PATCH with JSON Patch. Also, to add links to a collection, perhaps @dret's Linkset format can be used?

JSON Patch may require specific framing of JSON-LD, Also how do we specify details of the PATCH? Would spec would have to define AddMemberOperation and RemoveMemberOperation providing a template of a JSON patch and assumed JSON-LD frame?

lanthaler commented 6 years ago

Schema.org has InsertAction for adding things to collections. Maybe we should just leverage that and try to get a RemoveAction added.

elf-pavlik commented 6 years ago

I've just created PR #140 which uses schema:AddAction, I'll take a look at schema:InsertAction and update that PR...

elf-pavlik commented 6 years ago

Looking at Solid HTTPS REST API Spec : Creating content : Alternative: Using SPARQL

It seems that using HTTP PATCH

PATCH /events/1/attendees HTTP/1.1
Host: events.example
Content-Type: application/sparql-update

to add a member one could just use

PREFIX hydra: <http://www.w3.org/ns/hydra/core#>
INSERT DATA { </events/1/attendees> hydra:member </people/1> }

and to remove

PREFIX hydra: <http://www.w3.org/ns/hydra/core#>
DELETE DATA { </events/1/attendees> hydra:member </people/1> }

I've also noticed that @RubenVerborgh did some work on N3 Patch for Solid but I didn't get chance to read any of the documentation about it...

alien-mcl commented 6 years ago

Ugh - not sure. We could exploit SPARQL endpoint to the fullest completely making ReST obsolete (complex queries, transformations etc.). Also this could also cause security issues - exposing underlying data source to those queries is tempting, but that data source could be blocked for modifications. I don't think it's a good direction

tpluscode commented 6 years ago

Is it our call to make? If we can have Hydra describe such affordance, why not allow it? Even if it's as ~simple~ crude as

operation: {
   method: "PATCH",
   contentType: "application/sparql-update"
}
asbjornu commented 6 years ago

While I agree it's crude, it's better than nothing.

elf-pavlik commented 6 years ago

If we decide to go with PATCH this practically means opening a broader conversation related to use of this HTTP method and making topic of this issue a first relevant use case.

BTW https://www.w3.org/TR/ldp/#h-ldpr-http_patch

Per [RFC5789], this HTTP method is optional and this specification does not require LDP servers to support it. When a LDP server supports this method, this specification imposes the following new requirements for LDPRs.

Any server-imposed constraints on LDPR creation or update must be advertised to clients.

4.2.7.1 LDP servers that support PATCH MUST include an Accept-Patch HTTP response header [RFC5789] on HTTP OPTIONS requests, listing patch document media type(s) supported by the server.

elf-pavlik commented 6 years ago

Following-up on my ACTION from last telecon, I chose a Vimeo API to review: https://developer.vimeo.com/api/endpoints

Below you can find endpoints relevant to what concerns this issue


Like

https://developer.vimeo.com/api/endpoints/likes

Get all videos a user has liked

https://developer.vimeo.com/api/endpoints/likes#GET/users/{user_id}/likes

GET /users/{user_id}/likes

eg.

GET /users/elfpavlik/likes
HTTP 200 OK

Get all users who have liked this video

https://developer.vimeo.com/api/endpoints/likes#GET/videos/{video_id}/likes

GET /videos/{video_id}/likes

eg.

GET /videos/144522067/likes
HTTP 200 OK

Like a video

https://developer.vimeo.com/api/endpoints/likes#PUT/users/{user_id}/likes/{video_id}

PUT /users/{user_id}/likes/{video_id}

eg.

PUT /users/elfpavlik/likes/144522067
HTTP 204 No Content

Unlike a video

https://developer.vimeo.com/api/endpoints/likes#DELETE/users/{user_id}/likes/{video_id}

DELETE /users/{user_id}/likes/{video_id}

eg.

DELETE /users/elfpavlik/likes/144522067
HTTP 204 No Content

Watch Later

Channels


Some observations:

elf-pavlik commented 6 years ago

JSON-LD 1.1 should include "@container": "@graph" construct (just implemented in jsonld.js https://github.com/json-ld/json-ld.org/issues/195#issuecomment-328984904) which would enable expressing collections like in Vimeo API as follows:

{
  "@context": {
    ...
    "member": { "@id": "hydra:member", "@container": "@graph" }
  }
}
{
  "@id": "/users/elfpavlik/likes",
  "manages": {
    "subject": "/users/elfpavlik",
    "property": "sor:likes"
  },
  "member": {
    "/users/elfpavlik/likes/144522067": "/videos/144522067",
    ...
  }
}
{
  "@id": "/videos/144522067/likes",
  "manages": {
    "property": "sor:likes",
    "object": "/videos/144522067"
  },
  "member": {
    "/users/elfpavlik/likes/144522067": "/users/elfpavlik",
    ...
  }
}

Which again seems to take us to a case where an intent to perform and Action on some Thing, needs a way to discover which Operation on which Resource can represent it https://github.com/HydraCG/Specifications/issues/2#issuecomment-269825428 In this case Like (Action) on the video (Thing) requires Create (Operation) on the collection (Resource) representing likes of an agent. Similarly, Unlike (Action) on the video (Thing) requires Delete (Operation) on the (Resource) representing the 'liking'

tpluscode commented 6 years ago

"/users/elfpavlik/likes/144522067": "/users/elfpavlik"

Are you sure that is correct? Wouldn't it be smth more like

"member" [{
  "@id": "/users/elfpavlik/likes/144522067",
  "video": "/videos/144522067",
  "user": "/users/elfpavlik"
}]

This way it makes sense to DELETE /users/elfpavlik/likes/144522067 (actual resource).

tpluscode commented 6 years ago

I haven't yet found a relevant resource in Foxy Cart.

Here's an example from Huddle - to Assign Company Manager permissions

POST /people/companies/123/managers HTTP/1.1
Content-Type: application/vnd.huddle.data+json

{
  "rel": "member",
  "href": "/people/companies/123/members/654"
}

So it takes the approach of explicitly adding a link relation. What I find pretty vague is the media type. application/vnd.huddle.data+json looks just like a fancy name for plain JSON. There isn't a general document structure. It takes different, although admittedly similar, shapes for various resources. Schemas seem to be only documented in plain text (?).

lanthaler commented 6 years ago

I looked at a couple of Google's APIs. Most of them follow the model Pavlik outlined above. YouTube's API for instance has the notion of PlaylistItems to add and remove items from a playlist. The membership is thus a resource by itself that can be created and deleted.

An example that hasn't come up yet can be found in Gmail's API to modify labels of a message. It "emulates a PATCH" to modify the message:

POST https://www.googleapis.com/gmail/v1/users/userId/messages/id/modify

{
  "addLabelIds": [
    string
  ],
  "removeLabelIds": [
    string
  ]
}
elf-pavlik commented 6 years ago

"/users/elfpavlik/likes/144522067": "/users/elfpavlik"

Are you sure that is correct? Wouldn't it be smth more like

As I understood "@container": "@graph" would make it easier to express Quads in compacted JSON-LD. Named graph denoted by "/users/elfpavlik/likes/144522067" could contain just two Triples:

GET /users/elfpavlik/likes/144522067
{
  "@context": { ... },
  "@graph": [
    {
      "@id": "/users/elfpavlik",
      "sor:likes": "/videos/144522067"
    }
    {
      "@id": "/users/elfpavlik/likes",
      "member": "/videos/144522067"
    }
  ]
}

But we get all that information directly from the collection thanks to definition of manages block

{
  "@context": { ... },
  "@id": "/users/elfpavlik/likes",
  "manages": {
    "subject": "/users/elfpavlik",
    "property": "sor:likes"
  },
  "member": {
    "/users/elfpavlik/likes/144522067": "/videos/144522067",
    ...
  }
}
asbjornu commented 6 years ago

I have to say I like Vimeo's approach of using PUT to like a video, making the request idempotent. As Twitter uses POST favorites/create/:id, I often find myself double-liking the same tweet, ending up un-liking it because Tweetbot seems to have implemented some kind of idempotency protection themselves. I don't know Twitter's API, but I assume that's due to favouriting the same Tweet twice causes an error.

tpluscode commented 6 years ago

@elf-pavlik it would seem that @container: @graph is not a thing: http://tinyurl.com/yd92lycl

alien-mcl commented 6 years ago

Fulfilling my action, here are my findings regarding Confluence and Jira ReST APIs.

CONFLUENCE: Example set of operations is related to the 'space's properties:

JIRA: There are two examples interesting. One of them are related to actors which can be assigned to project roles:

Another example is related to users and groups. It seems to stick more to a dedicated server script resource.

In both example sets client needs to know on how to create URLs and the pattern is not provided. Also a notion of a resource key is used here (groupname or username)

elf-pavlik commented 6 years ago

@elf-pavlik it would seem that "@container": "@graph" is not a thing: http://tinyurl.com/yd92lycl

Added on a branch of jsonld.js just 20 days ago so not released yet and not available in the playground https://github.com/digitalbazaar/jsonld.js/commit/f6a91c0642dbdfa29bf680085a8f5e6f5693e4b3

lanthaler commented 6 years ago

... and not part of the official JSON-LD spec.

elf-pavlik commented 6 years ago

I think "@container": "@graph" suggestion from my side came as bit of a distraction. Server could respond with expanded and flattened JSON-LD or client could even negotiate for Trig or N-Quads. I just found it interesting as a possible way to format example snippets in an elegant way.

What I find important to this case here relates to the shared understanding between clients and servers. In case of liking a video or having interest in an event or having attended an event, it seems that interaction would rely on shared understanding of particular instance of rdf:Property, for liking something like sor:likes and for interests something like cco:interest. In addition hydra Collection Design provides hydra:manages block which can reference any instance of rdf:Properety with hydra:property. Having that shared understanding, to create particular relationship (expressed with particular instance of rdf:Property) client would have to 'add' to the collection which hydra:manages that property for intended resource. For liking on Vimeo, each person has such collection explained in https://developer.vimeo.com/api/endpoints/likes#GET/users/{user_id}/likes

I see one possible gotcha here, to have the hydra:manages block work as intended, for collection:

{
  "@context": { ... },
  "@id": "/users/elfpavlik/likes",
  "manages": {
    "subject": "/users/elfpavlik",
    "property": "sor:likes"
  },
  "member": [...]
}

We want to IRI denoting the video to appear as member, not the new resource we would PUT, so "member": ["/videos/144522067"] NOT "member": ["/users/elfpavlik/likes/144522067"] (which differs from how ldp:contains works in LDP).

If instead of having a resource (named graph) like

GET /users/elfpavlik/likes/144522067
{
  "@context": { ... },
  "@graph": [
    {
      "@id": "/users/elfpavlik",
      "sor:likes": "/videos/144522067"
    }
  ]
}

we would like to have a resource representing a Qualified Relation ('foo:Liking' or whatever), it seems that it would not work any more with intended design of hydra:manages block. We can't anymore rely on direct (binary) relationships expressed with instances of rdf:Property. IMO we shouldn't expect that vocabularies on which shared understanding depends will provide qualified version for each relationship.

If for cases like above, we intend hydra:member to work more like ldp:contains, we might need to adjust hydra:manages to always have subject, property, object where either s or o would have an array. something like:

{
  "@context": { ... },
  "@id": "/users/elfpavlik/likes",
  "manages": {
    "subject": "/users/elfpavlik",
    "property": "sor:likes",
    "object": ["/videos/144522067"]
  },
  "member": ["/users/elfpavlik/likes/144522067"]
}

In above, client still don't know which resource it would have to DELETE to remove triple with particular object (as unlike), so we might needs something like:

 [
  {
    "@id": "https://media.example/users/elfpavlik/likes",
    "@graph": [{
      "@id": "https://media.example/users/elfpavlik/likes",
      "http://www.w3.org/ns/hydra/core#manages": {
        "http://www.w3.org/ns/hydra/core#subject": "https://media.example/users/elfpavlik",
        "http://www.w3.org/ns/hydra/core#property": "http://purl.org/net/soron/likes",
        "http://www.w3.org/ns/hydra/core#object": ["https://media.example/videos/144522067"]
      },
      "http://www.w3.org/ns/hydra/core#member": ["https://media.example/users/elfpavlik/likes/144522067"]
    }]
  },
  {
    "@id": "https://media.example/users/elfpavlik/likes/144522067",
    "@graph": [{
      "@id": "https://media.example/users/elfpavlik",
      "http://purl.org/net/soron/likes": "https://media.example/videos/144522067"
    }]
  }
]

"@container": "@graph" seemed to me like a potential alternative but possibly leaving to much implicit

{
  "@context": { ... },
  "@id": "/users/elfpavlik/likes",
  "manages": {
    "subject": "/users/elfpavlik",
    "property": "sor:likes"
  },
  "member": {
    "/users/elfpavlik/likes/144522067": "/videos/144522067"
  }
}

But at least here client knows clearly which resource it has to DELETE to remove which member.

alien-mcl commented 6 years ago

The more I read this the more complicated it seems. First - graphs - what for do we need those? It's kind'a RDFish and I believe it's main purpose was to organize data. In those examples I've got feeling that the graph acts a bigger role. BTW, are we going to force in the spec requirement of using the graphs?

HTTP LINK/UNLINK seems more natural as these are supposed to create relations between resources. With those you could LINK /users/elfpavlik with /videos/144522067 by a sor:likes relation, i.e:

LINK /users/elfpavlik
Link: </videos/144522067>; rel="http://purl.org/net/soron/likes"

This way client doesn't need to know anything about URL's and how to compose those as required in PUT. It still has all the PUT's benefits like idempodency. The resulting collection resource available would be actually a view over sor:likes relations:

GET /users/elfpavlik/likes
{
  "@context": { ... },
  "@id": "/users/elfpavlik/likes",
  "manages": {
    "subject": "/users/elfpavlik",
    "property": "sor:likes"
  },
  "member": {
    "/videos/144522067"
  }
}

UNLINK would act as a retraction of the relations created effectively removing items from collection without actually removing the resources. Drawback of that approach is that client would need to create the resources in the API first, then it would link them to any other resource required.

elf-pavlik commented 6 years ago

The resulting collection resource available would be actually a view over sor:likes relations

I also like seeing those collections as views on the dataset, and as I commented in https://github.com/HydraCG/Specifications/issues/137#issuecomment-333602000 we may want to take in consideration hydra:Collection with hydra:manages together with Triple Pattern Fragments

Mostly for that reason, currently I prefer LINK/UNLINK and PUT/DELETE new resource (a named graph) which just add desired triple to a dataset. PATCH on the collection seems to not treat it as view similar to TPF, and in cases like Vimeo patching one collection and affecting the other one seems awkward - which one should I patch? Let's just try both! As for PUT/DELETE vs LINK/UNLINK, I see some benefits in PUT/DELETE

  1. Clients sends payload with content type supported by the server, doesn't have to use HTTP headers
  2. Client can include a digital signature in the payload which some datasets might have as require

This way client doesn't need to know anything about URL's and how to compose those as required in PUT. It still has all the PUT's benefits like idempodency.

I see it as another advantage over PATCH which HTTP doesn't define as idempotent.

The way I tend to see Hydra role here, it shouldn't impose any of the choices above but provide clear way for the service to advertise chosen approach and client should have ability to achieve intended goal no matter which choice the server made. Client would just look for action of type act:Link or act:Unlink and the action would provide all the information on how to perform an operation (eg. HTTP LINK/UNLINK), possibly even delegating this action to an operation on another resource (eg. HTTP PUT/DELETE)

BTW whenever we talk here about PUT/DELETE combination, any server could choose POST/DELETE instead and currently we don't even seem to have way to create new resources with PUT #141

First - graphs - what for do we need those?

In case of Vimeo where we have two distinct collections

{
  "@id": "/users/elfpavlik/likes",
  "manages": {
    "subject": "/users/elfpavlik",
    "property": "sor:likes"
  },
  "member": {
    "/users/elfpavlik/likes/144522067": "/videos/144522067",
    ...
  }
}
{
  "@id": "/videos/144522067/likes",
  "manages": {
    "property": "sor:likes",
    "object": "/videos/144522067"
  },
  "member": {
    "/users/elfpavlik/likes/144522067": "/users/elfpavlik",
    ...
  }
}

client could see that DELETE /users/elfpavlik/likes/144522067 would result in removal of that membership triple from both collections. It also provides a convenient place to assert in the dataset </users/elfpavlik> sor:likes </videos/144522067> statement which the manages block entails. I remember seeing somewhere mention of Quad Pattern Fragments, maybe @RubenVerborgh could chime in on that development.

alien-mcl commented 6 years ago

Hmm. I grow up to think about semantic actions. Indeed it would make the client much simplier. I tried to start developing #141 and I've stumbled upon a choice: complicated logic on how to perform a simple operation of adding a resource to the collection or simple marked operation teling client exactly on what to do. Benefit would be to decouple logic of of the HTTP protocol, enabling applicability to other protocols (in theory at least). These would limit to very basic operations like deleting, updating, creating, linking and possibly searching/filtering. I believe these semantic operations should be defined in the spec either with native Hydra namespace based terms or explicit terms reused from other vocabularies. I wouldn't like to replace one difficult to implement logic related to protocol specific verbs with another complicated logic of figuring out which action does what (from the generic client point of view).

I'll try to implement #141 with the semantic actions approach and we'll see how it behaves.

elf-pavlik commented 6 years ago

On which resource do you plan to advertise such linking action? The video (eg. /videos/144522067) or that collection view (eg. /videos/144522067/likes)?

BTW I just noticed another small nuance with HTTP LINK and HTTP UNLINK, client would need to use sometimes rel and sometimes rev (to use property in reverse/inverse direction)

LINK /users/elfpavlik
Link: </videos/144522067>; rel="http://purl.org/net/soron/likes"
LINK /videos/144522067
Link: </users/elfpavlik>; rev="http://purl.org/net/soron/likes"
lanthaler commented 6 years ago

I believe these semantic operations should be defined in the spec either with native Hydra namespace based terms or explicit terms reused from other vocabularies

They were part of the vocabulary at the beginning but we decided to remove them (see #11). I think we should at least give a clear recommendation (which I think should be schema.org given its popularity).

tpluscode commented 6 years ago

On which resource do you plan to advertise such linking action? The video (eg. /videos/144522067) or that collection view (eg. /videos/144522067/likes)?

It doesn't really matter. Like we discussed at the call, I think it's up to the server to advertise one or both. And side effects are outside of the scope of hypermedia controls.

So, if you DELETE /users/elfpavlik/likes/144522067 (or UNLINK) it does not matter that the appropriate triple is removed from both sides. Remember we're not necessarily talking about RDF at all. Deleting could mean removing a single row from a linking table in SQL. Both representations get affected automatically.

That said, I think we should be careful with comparing to LDP. That spec is targeted at manipulating RDF. Hydra is a vocabulary for defining Hypermedia, which is storage format agnostic. Coincidentally we use RDF for resource representations.

I believe these semantic operations should be defined in the spec either with native Hydra namespace based terms or explicit terms reused from other vocabularies.

There are operations which will commonly occur in various APIs as much as link relations. I'd encourage reuse where possible but API-specific vocabularies are necessary. And we're not discouraging API-specific links either, are we?

But, if I follow the logic here, these semantic operations should only define why the operations should be invoked, not why. The mechanics should be completely in the scope of the controls. That way a client can look for an operation ex:like_video. But whether it will do a PUT or POST or a LINK request is discovered at runtime and can change over time...

elf-pavlik commented 6 years ago

But whether it will do a PUT or POST or a LINK request is discovered at runtime and can change over time...

If we continue with the liking a video example.

But in all cases above IMO client needs a way to follow its nose from /videos/144522067 and only rely on understanding Hydra plus sor:likes (or some other property in other similar use cases)

Personally I would prefer some kind of generic foo:Link and foo:Unlink actions which would specify any instance of rdf:Property (or its reverse) rather than tying to define action type for any possible property.

tpluscode commented 6 years ago

PUT would happen on a resource which doesn't event exist #141 and client would need a URI Template

NIT: Not necessarily. Imagine this. If the authenticated user is /users/elfpavlik and that user requested GET /videos/144522067. The representation of the latter could include a sor:likes PUT operation on resource /users/elfpavlik/likes/144522067. No need for a template.

I find the proposed foo:Link and foo:Unlink too generic and with too loose semantics. Although they too could be useful

elf-pavlik commented 6 years ago

Keeping the meaning of sor:likes as in

    {
      "@id": "/users/elfpavlik",
      "sor:likes": "/videos/144522067"
    }

How would you use sor:likes to express operation that client would perform by PUT /users/elfpavlik/likes/144522067 ?

tpluscode commented 6 years ago

Sorry, my bad. I meant a semantic operation and not a link relation.

Hm, has Hydra completely lost that ability now @lanthaler? I don't see any mention in the spec not that the built-in operations have been removed...

alien-mcl commented 6 years ago

Yep - built-in operations have ben removed some time ago. As for the example with videos - I agree with @elf-pavlik - how client will know that in /videos/144522067 only the part 144522067 should be used in the PUT url call?

tpluscode commented 6 years ago

how client will know that in /videos/144522067 only the part 144522067 should be used in the PUT url call?

I just explained that he doesn't necessarily have to. The representation can include the complete identifier where to PUT.

Yep - built-in operations have ben removed some time ago.

Yes, yes. But this is still valid, right?

{
  "@type" : [ "hydra:Operation", "sor:LikeVideo" ]
}
alien-mcl commented 6 years ago

I believe so. It's still RDF after all. I remember also that for client simplicity we agreed that these kind of statements should be explicit so client doesn't need to use any reasoning to find it out.

I find the proposed foo:Link and foo:Unlink too generic and with too loose semantics. Although they too >could be useful

Well, I'd prefer generic simple operations so a generic client can still do something about it. Server could still provide more semantic operations extending a base set.

tpluscode commented 6 years ago

Well, I'd prefer generic simple operations so a generic client can still do something about it

With a sor:LikeVideo the client still can "do something". After all the description includes all the required details: the expected body, the URI (possibly template), the method, in the future possibly the media type(s).

My issue is, as I understood foo:Link/foo:Unlink is that it would prescribe some details of how to invoke the operation (specific body, media type, HTTP method?). If that is not the case, then I find it useless and we should only ever use plain hydra:Operation in the core while allowing custom, API-specific terms which can simplify discovery and building of the UI.

alien-mcl commented 6 years ago

Semantic operation like foo:Link/foo:Unlink are pretty useful from hydra:Collection point of view - having a strong semantic behind client will be 100% sure whether the operation actually adds a resource to the collection or not as it would explicitly create a relation between subject and object. With unspecified semantics of sor:LikeVideo client has no clue on what will happen. Generic hydra:Operation just says Something will happen if you do that. Having an operation of type sor:LikeVideo makes no difference here - something will happen.

elf-pavlik commented 6 years ago

Yes, yes. But this is still valid, right?

   {
     "@type" : [ "hydra:Operation", "sor:LikeVideo" ]
   }

I don't think so, for values of "@type" one should use instances of rdfs:Class but we have sor:likes a rdf:Property so using it this way seems invalid.

As I mentioned before, I don't like idea of having to create and instance of rdfs:Class for all the existing instances of rdf:Property which someone would like to use with Hydra, just so that we can get by with "@type". I'd like something more align with manages block, which in turn works very much like rel and rev in link relations and ldp:hasMemberRelation & ldp:isMemberOfRelation https://www.w3.org/ns/ldp

With a generic action foo:Link a resource could use multiple instances of it: (assuming authenticated as elfpavlik)

{
  "@context": { ... },
  "@id": "/videos/144522067",
  "operation": [
    {
      "@type": ["hydra:Operation", "foo:Link"],
      "method": "LINK",
      "links": {
        "subject": "/users/elfpavlik",
        "property": "sor:likes",
        "object": "/videos/144522067"
      }
    },
    {
      "@type": ["hydra:Operation", "foo:Link"],
      "method": "LINK",
      "links": {
        "subject": "/users/elfpavlik",
        "property": "cco:interest",
        "object": "/videos/144522067"
      }
    }
  ]
}

I just explained that he doesn't necessarily have to. The representation can include the complete identifier where to PUT.

@tpluscode let's say server generates IRI clients should PUT to, how do you imagine including it in the representation of /videos/144522067 (snippet please)

asbjornu commented 6 years ago

@elf-pavlik: I would envision it to look something like this:

{
  "@context": { ... },
  "@id": "/videos/144522067",
  "operation": [
    {
      "@type": ["hydra:Operation", "foo:Like"],
      "method": "PUT",
      "@id": "/users/elfpavlik/likes/144522067"
    }
  ]
}
elf-pavlik commented 6 years ago

I don't think we can just use @id because we start conflating the operation with a resource we perform this operation on. So far we mostly used blank nodes to express operations, see #121

As I mentioned before, I would like to search for solution which relies on use of established instances of rdf:Property, like for example cco:interest and not having to create an new instance of rdfs:Class for any already existing property we would like to use. Once again:

I'd like something more align with manages block, which in turn works very much like rel and rev in link relations or ldp:hasMemberRelation & ldp:isMemberOfRelation https://www.w3.org/ns/ldp

asbjornu commented 6 years ago

Okay, that makes sense. Not sure what the property representing the URI on which to perform the operation should be called, though. href, target, endpoint?

tpluscode commented 6 years ago

I was thinking target myself. And would be a resource or IRI Template

elf-pavlik commented 6 years ago

I think we touch an issue more general that what concerns this issue here. I searched the history and have found an issue which we may pick-up to discuss this more broad target issue #3