Open elf-pavlik opened 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.
And what are the best practices found around the web?
Would you volunteer to research how a handful of popular/well-known APIs do that? :-)
Sure, I can look around
Awesome. Thanks
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
/events
collection which contains its members works like ldp:Container
/people/alice/events
collection which only references its members works more like basic ldp:Resource
Well, DELETE
ing /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
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?
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.
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?
Schema.org has InsertAction for adding things to collections. Maybe we should just leverage that and try to get a RemoveAction added.
I've just created PR #140 which uses schema:AddAction
, I'll take a look at schema:InsertAction
and update that PR...
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...
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
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"
}
While I agree it's crude, it's better than nothing.
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.
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
https://developer.vimeo.com/api/endpoints/likes
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
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
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
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
Some observations:
PUT /users/{user_id}/likes/{video_id}
server will update both
/users/{user_id}/likes
/videos/{video_id}/likes
PUT /users/{user_id}/channels/{channel_id}
server will update both
/users/{user_id}/channels
/channels/{channel_id}/users
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'
"/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).
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 (?).
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
]
}
"/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",
...
}
}
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.
@elf-pavlik it would seem that @container: @graph
is not a thing: http://tinyurl.com/yd92lycl
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:
container
. The pattern is not provided though. Also a {key}
notion is introduced, where that 'key' is a property within body of each of the resources.JIRA
:
There are two examples interesting. One of them are related to actors which can be assigned to project roles:
{
"id": 10360,
"categorisedActors": {
"atlassian-user-role-actor": [
"admin"
],
"atlassian-group-role-actor": [
"jira-developers"
]
}
}
{ "user" : ["admin"] }
or
{ "group" : ["jira-developers"] }
Another example is related to users and groups. It seems to stick more to a dedicated server script resource.
{ "name": "charlie" }
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 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
... and not part of the official JSON-LD spec.
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.
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.
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
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.
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.
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"
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).
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...
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.
/users/elfpavlik/likes
)/users/elfpavlik
(with rel
) and/or /videos/144522067
(with rev
)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.
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
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
?
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...
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?
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" ]
}
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.
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.
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.
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)
@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"
}
]
}
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 likerel
andrev
in link relations orldp:hasMemberRelation
&ldp:isMemberOfRelation
https://www.w3.org/ns/ldp
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
?
I was thinking target
myself. And would be a resource or IRI Template
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
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 usingschema:AddAction
rather thanschema:CreateAction
.What would such action reference with
hydra:expects
? It pretty much just needs an@id
which should get added tohydra: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 justldp:Resource
to represent such collection and add statements withhydra:member
using HTTP PATCH