HydraCG / Specifications

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

hydra:search with HTTP POST #149

Open elf-pavlik opened 6 years ago

elf-pavlik commented 6 years ago

In various examples of hydra:search I always see it using 'a default operation` of the tempate- HTTP GET. Also currently its definition includes

hydra:search a hydra:TemplatedLink ;
             rdfs:range hydra:IriTemplate .

I can think of a cases where service would choose to use HTTP POST to perform search for example with https://www.w3.org/TR/sparql11-protocol/#query-via-post-direct using application/sparql-query content type.

In that case it seems that we would have hydra:Resource accepting the POST, not hydra:IriTemplate and also hydra:search seems an instance of hydra:Link not hydra:TemplatedLink.

For collection that provides more then one search mechanism, we might get something like:

{
  "@context": { ... },
  "@type": "hydra:Collection",
  "search": [{
    "@type": "IriTemplate",
    "template": "http://api.example.com/issues{?q}",
    "variableRepresentation": "BasicRepresentation",
    "mapping": [{
      "@type": "IriTemplateMapping",
      "variable": "q",
      "property": "hydra:freetextQuery",
      "required": true
    }]
  }, {
    "@id": "http://api.example.com/issues",
    "operation": [{
      "@type": ["Operation", "schema:SearchAction"],
      "method": "POST",
      "???:contentType": "application/sparql-query"
    }, {
      "@type": ["Operation", "schema:CreateAction"],
      "method": "POST",
      "expects": "foo:Issue",
      "???:contentType": "application/ld+json"
    }]
  }]
}

In this case, besides the hydra:search, hydra:(Templated)Link we also seem to need schema:SearchAction to select intended operation on the resource. We can't any more rely on 'a default operation' (HTTP GET) as we did with the hydra:IriTemplate.

@RubenVerborgh do you have any experience with using hydra:search via HTTP POST (possibly also application/sparql-query)?

tpluscode commented 6 years ago

I think you managed to smoosh many separate issues in here :)

First let's start with the search link. Does is link to the /issues collection (to itself)? I think what you're looking for is simply

{
  "@id": "http://api.example.com/issues",
  "operation": [
    {
      "@type": [ "Operation", "schema:SearchAction" ],
      "method": "POST",
      "???:contentType": "application/sparql-query"
    }
  ]
}

The ???:contentType relation is a different story. I'm pretty sure this has been brought up on already on the list and there's #22. It mentions schema:contentType but it has some extra semantics. I'd rather have our own under hydra namespace.

Maybe hydra:accept would make sense akin to HTTP? It could also allow an array.

elf-pavlik commented 6 years ago

Good catch, I think we would have search directly on the collection

{
  "@context": { ... },
  "@id": "http://api.example.com/issues",
  "@type": "hydra:Collection",
  "search": {
    "@type": "IriTemplate",
    "template": "http://api.example.com/issues{?q}",
    "variableRepresentation": "BasicRepresentation",
    "mapping": [{
      "@type": "IriTemplateMapping",
      "variable": "q",
      "property": "hydra:freetextQuery",
      "required": true
    }]
  },
  "operation": [{
    "@type": ["Operation", "schema:SearchAction"],
    "method": "POST",
    "???:contentType": "application/sparql-query"
    }, {
    "@type": ["Operation", "schema:CreateAction"],
    "method": "POST",
    "expects": "foo:Issue",
    "???:contentType": "application/ld+json"
  }]
}

Or collection would 'delegate' it to different resource via hydra:search

{
  "@context": { ... },
  "@id": "http://api.example.com/issues",
  "@type": "hydra:Collection",
  "search": [{
    "@type": "IriTemplate",
    "template": "http://api.example.com/issues/search{?q}",
    "variableRepresentation": "BasicRepresentation",
    "mapping": [{
      "@type": "IriTemplateMapping",
      "variable": "q",
      "property": "hydra:freetextQuery",
      "required": true
    }]
  }, {
    "@id": "http://api.example.com/issues/search",
    "operation": {
      "@type": ["Operation", "schema:SearchAction"],
      "method": "POST",
      "???:contentType": "application/sparql-query"
    }
  }],
  "operation": {
    "@type": ["Operation", "schema:CreateAction"],
    "method": "POST",
    "expects": "foo:Issue",
    "???:contentType": "application/ld+json"
  }
}

So client needs to check collection for:

I think with just added in #143 hydra:memberTemplate #16 and schema:AddAction we may head in similar direction, or at list for now:

I think in this issue we can focus on hydra:search and schema:SearchAction with direct GET & POST as well as delegating to other resources and templates. Once we get better picture of that we can see how it works with other links and templated links like hydra:memberTemplate.

lanthaler commented 6 years ago

I'm actually inclined to get rid of hydra:search and use operations instead.

tpluscode commented 6 years ago

Also for GET searches?

lanthaler commented 6 years ago

Yes, if we would have an answer for issue #3, i.e., a way to associate a resource to an operation whose target is another resource or an IRI template.

alien-mcl commented 6 years ago

Would it be OK then to assume that hydra:Link is a shortcut of a hydra:Operation with hydra:method set to GET?

As for the hydra:accept - we already have hydra:expects. Why not loosening it to something more generic than hydra:Class, let's say hydra:Resource. This way you could introduce various ways of describing what's expected, i.e. resource of type hydra:Class would imply an RDF resource, but hydra-ext:HttpCompliantResource would have a description for HTTP protocol specific stuff like content type, etc.

asbjornu commented 6 years ago

Perhaps an orthogonal, but still related, issue: Would it be good for Hydra to incubate and ensure the registration of an HTTP SEARCH method?

lanthaler commented 6 years ago

Would it be OK then to assume that hydra:Link is a shortcut of a hydra:Operation with hydra:method set to GET?

hydra:Link is a subclass of rdf:Property. If

:prop a hydra:Link .
:x :prop </y> .

The client can assume that /y is a dereferenceable resource, i.e., a GET is supported.

As for the hydra:accept - we already have hydra:expects.

Let's discuss support for different media types in a separate issue.

Would it be good for Hydra to incubate and ensure the registration of an HTTP SEARCH method?

I would prefer to stay focused on Hydra as a group... that doesn't mean someone from the group can do so though.

alien-mcl commented 3 years ago

I gave it many thoughts and I think this issue should be taken to another level - how to mint operations with IRI template.

I think current Heracles.ts implementation already allows this kind of constructs. I've started from an assumption that a link is actually a shortcut of an operation that is using GET by default.

</api> hydra:collection </api/users>.
</api> api:users [
  hydra:IriTemplate;
  hydra:template "/api/users{?search}".

api:users a 
  rdfs:subPropertyOf hydra:search;
  hydra:operation [hydra:method "POST"].

This way we don't need to create any new terms and everything seems OK compared against existing vocabulary and it's constratins. The client could use either a direct collection, GET search results (as the api:users is sub-property of search) or POST same things as an optional alternative to GET based search.

What do you think about it? We could add a paragraph to the spec to clarify how to interprete such constructs.

tpluscode commented 3 years ago

Yes, I agree that operations supported by property should be enough. Rather than having a concrete URI as the object of a predicate, we have a template. The client must construct the final URI first and then perform any of the supported operation 🎉

The detail is, like you say, the actual action of minting the identifier. Search is easier, but in the past I may have proposed this method also for creating new resources

# ApiDocumentation
api:newUser hydra:supportedOperation 
[
  hydra:title "Create User" ;
  hydra:method "PUT" ;
] .

# Entrypoint
<> api:newUser [
  hydra:template "/user/{name}" ;
  hydra:mapping
  [
    hydra:variable "name" ;
    hydra:property schema:name ;
  ] ;
] .

# New user to be created
[] schema:name "John" .

Given the resources above, the client would make a request to /user/John, because the value of schema:name property is mapped to the variable name in the URI template. This is in fact the same method I prefer to collection filtering using templates too.


Besides, I think it only needs one change (unless you mean inline operation)

-hydra:operation [hydra:method "POST"]
+hydra:supportedOperation [hydra:method "POST"]
alien-mcl commented 3 years ago

Actually, I meant hydra:operation. Description of the hydra:supportedOperation says An operation supported by instances of the specific Hydra class or the target of the Hydra link - in this case, a link does not meet any of these requirements (it's neither a class nor a target of the link). On the other size, hydra:operation has a range of hydra:Resource, thus it would be ok to have it attached to the link itself. Logic seems consistent - the link has an operation which changes the way it should be invoked, but the relation remains.

tpluscode commented 3 years ago

Hm, I don't think I agree with this train of thought.

My understanding is that with api:users hydra:operation ?operation, you would have the final request got to api:users which is not the desired behaviour.

hydra:supportedOperation is the opposite, that the constructed template becomes the target

alien-mcl commented 3 years ago

Ok. You're right - I think I'll create a PR with additional section regarding templated operations and I'd change that description.