Open lanthaler opened 11 years ago
PROPOSAL: Do nothing as it can easily be achieved by reversing "operation". Add a reverse property "target" to Hydra's JSON-LD context to simplify it's usage in practice and describe it in the specification:
{
"@type": "hydra:Operation",
"target": { "@id": "/some-resource" },
"method": "POST",
"expects": {
"supportedProperties": {
"property": "rsvpStatus",
"required": true
}
}
}
The only issue I see with this solution is that "target" was recently introduced into schema.org but that's probably not that much of an issues in practice. In fact, we could also encourage (or at least support) schema.org/target as alternative. Thoughts?
Is that how a complete example would look like?
{
"@context": {},
"@type": "Event",
"name": "John's Birthday Party",
"operations": {
"@type": "RsvpAction",
"target": "https://events-organizer.com/rsvp?eventId=123",
"method": "POST",
"expects": {
"supportedProperties": {
"property": "rsvpStatus",
"required": true
}
}
}
}
In general agree with adding target
. I've been thinking about something similar which I would use internally to uniformly represent operations.
I would model it so that any operation without an explicit target assumed the parent resource as one. So
{
"@id": "http://example.com/resource",
"operations": {
// details
}
}
was equivalent to
{
"@id": "http://example.com/resource",
"operations": {
"target": "http://example.com/resource",
// details
}
}
Is that aligned with your idea?
Mostly. I envisioned the target property to be a simple inverse of the operation property. Thus you couldn’t use both at the same time. We would need another mechanism to link from resource to the operation. I was thinking about action. I would call the abstract thing that happens (becoming a friend of someone, ordering something) an Action and the bytes on the wire (HTTP or other request) an Operation. WDYT?
This distinction between Action and Operation makes sense.
Regarding target, I was first thinking about a hidden artifact. Something the a client library produces when applying the ApiDocumentation to a representation. Mostly to simplify processing down the line.
I think that the other use of target is what Jay Zawar asked about in his email. Currently the only option is to have an additional artificial link similarly to the rsvp example above, right?
Regarding target, I was first thinking about a hidden artifact.
I prefer to make things explicit than to require each client to infer such information. In other words, I prefer the smarts in the message rather then algorithms
Yes, without something like an action property, an additional artificial link similarly to the rsvp example above would be required.
I prefer to make things explicit than to require each client to infer such information
I agree. Explicit is better. But I haven't found an elegant way anyway when processing supported operations. Consider
{
"@id": "some:collection",
"member": [
{ "@id": "member:one", "@type": "Person" },
{ "@id": "member:two", "@type": "Person" }
]
}
and
{
"@type": "ApiDocumentation",
"supportedClass": [{
"@id": "Person",
"supportedOperation": [{
"@id": "PokePerson"
}]
}]
}
In this example each Person
has a supported operation but it will be invoked on the Person's URI.
In Heracles, I process supported operations and return an Operation
object which has the correct target URL. This way the Operation
can be then passed to other code detached from the actual Person
object. It's just one step away from allowing explicit targets
As discussed in #134, we have the alternatives href
, target
, targetUrl
and endpoint
mentioned. Are there more alternatives? What are their pros and cons? My preference so far is target
as I feel targetUrl
is unnecessary since its @type
is going to be @id
(which is an IRI).
In case of create with PUT #141 we need to also handle URI Templates #118 no just IRIs. Unless we explore direction similar to hydra:search
for cases where we need URI Templates. I don't like it since this wouldn't allow to model create with POST and create with PUT (based on URI Template) in the same way eg. schema:CreateAction
.
I don't like it since this wouldn't allow to model create with POST and create with PUT
I don't follow. Creating is fine both with PUT and POST. The former in the case when the server doesn't know the resource's URI up front.
POST != create. Only PUT creates resourcers in terms of HTTP terms from those two.
I know you're right about the spec but it only means that PUT is used to create or update a resource identified by the requested URL. Otherwise nothing forbids new resources from being created by side effects of a POST. In fact it's such a common pattern
POST a new representation to a collection
POST /members HTTP/1.1
{
"name": "Tomasz",
"description": "I want to be a member"
}
The server returns a link to the new resource
HTTP/1.1 201 Created
Link: </members/tomasz>
There you go. I created a new resource using POST.
Using POST
to create resources is much more common than using PUT
, since PUT
requires the client to know the URI beforehand. The POST
method can be used to mean anything according to HTTP semantics and "create" is one such, very common meaning.
In my comment I just wanted to propose that operation doesn't always have to specify target - an IRI, which we can think of as a resource with an @id
(denoted by that IRI)
As @lanthaler suggested we don't need to define a new property for that but just rely on using hydra:operation
in reverse direction - JSON-LD @context
in spec non-normative appendix can include an alias for that.
In cases like CreateAction
with PUT #141 sometimes client will not get IRI but Uri Template / hydra:IriTemplate
, based on which clients constructs the IRI which denotes the resource on which to perform the operation. In that case I don't think we can use hydra:operation
in reverse direction
The most common example of using hydra:IriTemplate
comes with hydra:search
, the only property which definition includes hydra:search rdfs:range hydra:IriTemplate
. Also already widely deployed thanks to Triple Pattern Fragments http://www.hydra-cg.com/spec/latest/triple-pattern-fragments/#controls
What I don't like about trying to use similar approach to creating a new resource (something like hydra:create
). It seems to result in two different ways of advertising the create operation
{
"@id": "/api/events",
"title": "List of events",
"@type": "hydra:Collection",
"manages": {
"property": "rdf:type",
"object": "schema:Event"
},
"operation": {
"@type": ["hydra:Operation", "schema:CreateAction"],
"title": "Create new event",
"method": "POST",
"expects": "schema:Event"
}
}
and following how hydra:search
works, something like:
{
"@id": "/api/events",
"title": "List of events",
"@type": "hydra:Collection",
"manages": {
"property": "rdf:type",
"object": "schema:Event"
},
"hydra:create": {
"@type": "hydra:IriTemplate",
"hydra:template": "/api/events/{uuid}",
"hydra:mapping": {
"hydra:variable": "uuid",
"hydra:property": "id:uuid",
"hydra:required": true
}
}
}
While I would like something aligned with the first example using schema:CreateAction
{
"@id": "/api/events",
"title": "List of events",
"@type": "hydra:Collection",
"manages": {
"property": "rdf:type",
"object": "schema:Event"
},
"operation": {
"@type": ["hydra:Operation", "schema:CreateAction"],
"title": "Create new event",
"method": "PUT",
"expects": "schema:Event",
"targetTemplate": {
"@type": "hydra:IriTemplate",
"hydra:template": "/api/events/{?uuid}",
"hydra:mapping": {
"hydra:variable": "uuid",
"hydra:property": "id:uuid",
"hydra:required": true
}
}
}
}
But then if we consider (an alias) @reverse
of hydra:operation
as target
, it seems to conflict with targetTemplate
in a way.
@lanthaler: Mostly. I envisioned the target property to be a simple inverse of the operation property. Thus you couldn’t use both at the same time. We would need another mechanism to link from resource to the operation. I was thinking about action. I would call the abstract thing that happens (becoming a friend of someone, ordering something) an Action and the bytes on the wire (HTTP or other request) an Operation. WDYT?
I also like this distinction and we also have related conversation in #2 in case above if we would state
{
"@id": "/api/events",
"action": {
"@type": ["hydra:Operation", "schema:CreateAction"]
}
}
We would have to provide an explicit target
or targetTemplate
, in that case it might make sense to define target
property and for simple cases use it in @reverse
direction (possibly making operation
an alias). This also would allow use of schema:SearchAction
UC#7 Searching events
{
"@context": "/api/context.jsonld",
"@id": "/api/events",
"@type": "hydra:Collection",
"manages": {
"property": "rdf:type",
"object": "schema:Event"
},
"totalItems": 100,
"members": [ ... ],
"action": [
{
"@type": ["hydra:Operation", "schema:CreateAction"],
"title": "Create new event",
"method": "POST",
"expects": "schema:Event",
"target": "/api/events"
}, {
"@type": ["hydra:Operation", "schema:SearchAction"],
"title": "Search evenst",
"method": "GET",
"returns": "hydra:PartialCollectionView",
"targetTemplate": {
"@type": "IriTemplate",
"hydra:template": "/api/events{?search}",
"hydra:variableRepresentation": "hydra:BasicRepresentation",
"hydra:mapping": {
"@type": "hydra:IriTemplateMapping",
"variable": "search",
"property": "hydra:freetextQuery",
"required": true
}
}
}
]
}
At the same time it would mean breaking change to hydra:search
which @RubenVerborgh may strongly object since TPF deployments rely on it.
I know you're right about the spec but it only means that PUT is used to create or update a resource >identified by the requested URL. Otherwise nothing forbids new resources from being created by side >effects of a POST. In fact it's such a common pattern
This not a rule - it's just happens it is used in some circumstances. I imagine that POST'ing a resource to /members
may end up with a Location
header point to a URL that is not related to the newly created resource in any way - it may point client to a resource from which it can obtain i.e. status of the task submitted for processing. In case of members, it may be a process of user verification, etc.
I think we should work on this issue taking into account conversations in #100 and #118 and solution should resolve all 3 issues.
continuing from: https://github.com/HydraCG/Specifications/issues/141#issuecomment-336729288
@elf-pavlik: since
hydra:Operation
doesn't provide a way to specifyhydra:IriTemplate
it seems that we may not useschema:CreateAction
with both HTTP POST and HTTP PUT@lanthaler: I realize it is not properly documented but the intention was always that operations would work on IriTemplates` just as they work on resources...
Does it mean that if we start from an instance of hydra:Operation
and follow hydra:operation
in reverse direction, then
1) if we get an instance of hydra:Resource
we use IRI that denotes that resource (JSON-LD @id
)
2) if we get an instance of hydra:IriTemplate
, we ignore IRI that denotes that template (JSON-LD @id
) and instead use IRI based on hydra:template
and hydra:mapping
@lanthaler: otherwise things like hydra:search wouldn't work.
Looking at example of hydra:search
http://www.hydra-cg.com/spec/latest/triple-pattern-fragments/#controls I don't see hydra:Operation
or hydra:operation
used there at all. I see hydra:search
relating an instance of hydra:Resource
directly to an instance of hydra:IriTemplate
.
Does it mean that if we start from an instance of
hydra:Operation
and followhydra:operation
in reverse direction, then [...]
That is how I understand it. Speaking in terms of triple patterns the client looks for
?resource hydra:operation ?operation
Like you say ?resource
either has an identifier or template and mapping. A blank node which isn't a template naturally must result in an error.
I think Hydra handles all the use cases presented by Schema.org so far quite nicely, the only thing that we really should discuss is how to handle one-click actions. For example, look at how GMail describes a RSVP action:
In Hydra, at the moment you would have to do something like
The
expects
part could be replaced by a class instead of defining the supported properties inline, something like"expects": "RsvpResponse"
. So the only thing that isn't really as elegant as GMails example is that Hydra needs a specific propertyrsvp
instead of justactions
. This is essentially a specialized link relation. The advantage is that it allows us to associate the supported operations directly to the definition of the link relation so that example could really be simplified toBut that means that a client either needs to know how
rsvp
is defined or look it up (which generally shouldn't be a problem). Some may wonder why we can't usehydra:operation
orhydra:supportedOperations
in a similar way to GMail'saction
property. The reason is that hydra:operation is defined to specify the operation of the current resource as the example above illustrates. On the other hand,hydra:supportedOperations
is defined to describe the operations allowed by instances of a specific class or values of specific properties (link targets).Do we want to introduce a property similar to GMail's
action
?