Closed tpluscode closed 3 years ago
Aha, an the elephant in the room: Removing elements from collection :D
Maybe LINK
and UNLINK
method could be used while otherwise keeping similar payloads as above?
What does it mean existing user representations ?
Guess that should be rephrased simply as existing users
, as in having URI identifiers.
Given the proposed request with only the ids, I agree they do not even need to "exist" in the sense of having actual representations.
On the other hand, I'd expect a typical API to be slightly closed world, so that such request would in fact verify that a LINK
ed resource in fact does exist. Not a necessity though
I gave some thoughts and I see several ways of achieving it.
hydra:expects xsd:anyUri
or hydra:expects rdf:Resource
. The former seems ugly as it forces the client to send a barrow of literals (I intentionally didn't use a bag word), the latter feels so generic as it is vague on what server will do with the provided statements (as this is the only way for RDF) while it needs just IRIs.
As an alternative I'd use hydra:expects rdf:Statement
where each reified statement would say collection is-in-relation-with member
, where that relation actually doesn't matter.
I don't know other ways of saying give me an IRI of a resource - it's all I care about.In all cases, I'd recommend using schema actions to denote an intent of the operation as the pure protocol may be not enough
Any news on this one @tpluscode ?
This is a design question and itself it probably does not have one answer.
With human audience in mind I would probably go for a diff-like payload, which would simply list members to add and members to remove. The exact specifics are irrelevant as long as the server provides a precise description of such a payload and it is enough for a (human) client to figure out.
The request could be a PATCH
with changes described using the Changeset vocabulary
prefix change: <http://purl.org/vocab/changeset/schema#>
# remove /member/1
# add /member/2
[
change:removal [
rdf:subject </collection> ;
rdf:predicate hydra:member ;
rdf:object </member/1> ;
] ;
change:addition [
rdf:subject </collection> ;
rdf:predicate hydra:member ;
rdf:object </member/2> ;
]
]
The downside is that each collection would need a specific expects
, for example done with SHACL
<> a sh:NodeShape ;
sh:property [
sh:name "Remove collection member" ;
sh:path change:removal ;
sh:node [
sh:property [
sh:path rdf:subject ;
sh:hasValue </collection> ;
dash:hidden true ;
sh:minCount 1 ;
sh:maxCount 1 ;
], [
sh:path rdf:predicate ;
sh:hasValue hydra:member ;
dash:hidden true ;
sh:minCount 1 ;
sh:maxCount 1 ;
], [
sh:path rdf:object ;
]
]
] ,
sh:property [
sh:name "Add collection member" ;
sh:path change:addition ;
sh:node [
sh:property [
sh:path rdf:subject ;
sh:hasValue </collection> ;
dash:hidden true ;
sh:minCount 1 ;
sh:maxCount 1 ;
], [
sh:path rdf:predicate ;
sh:hasValue hydra:member ;
dash:hidden true ;
sh:minCount 1 ;
sh:maxCount 1 ;
], [
sh:path rdf:object ;
]
]
] ;
With the rdf:subject
and rdf:object
pinned to specific values, hidden, and restricted to exactly one value, all the UI would need is to provide UI for selecting members to add/remove (rdf:object
)
Describe your API requirement
This a question not specifically related to Hydra as I have seen similar discussion for other systems too.
A typical scenario for using a collection resource, in our case an instance of
hydra:Collection
, is to do aPOST
request with the intended member representationFor the above request, the server could create a new identifier, for example by slugifying the name to produce a URL
/user/john-doe
.The question is, how an API would expose the functionality of adding existing resources for the client to understand.
Solution(s)
I consider implementing a generic supported operation handler, which would work slightly differently depending on the operation's definition.
Base operation
The core would be just a POST, expecting
api:User
as payloadOperation to create users
By adding the
schema:CreateAction
type, the client would be informed that the operation creates new users. A request containing<> a api:User
or[] a api:User
would have these resources created with a server-defined URI as mentioned aboveOperation to add resources to collection
Now the fun part: How to change an operation so that the client and the server know that they should send existing resources. And what would the request look like?
By typing the operation as
schema:UpdateAction
but not the other, the server would only allow payloads with existing user representations. It should be enough to send just the identifiers, right? To add two members to collection, could that be eitheror, using a different
expects
:Having it both ways?
Given what I've just realised when writing the previous section, two alternative operations could be necessary to have a single collection resource allow both kinds of requests. Combined: