zazuko / kopflos

kopflos - Linked Data APIs
MIT License
14 stars 6 forks source link

Annotated query handler #170

Open tpluscode opened 2 months ago

tpluscode commented 2 months ago

To simplify implementing resources backed by a SPARQL query, I'd like to have a handler, possibly exported from a dedicated package, that would make it possible declaratively. For example

<>
  a kl:ResourceShape ;
  kl:api </api> ;
  sh:targetClass api:ExternalResource ;
  kl:handler
    [
      a kl:Handler ;
      kl:method "GET" ;
      code:implementedBy
        [
          a code:EcmaScriptModule ;
          code:link <node:@kopflos-cms/query#construct> ;
        ] ;
      code:arguments
        [
          arg:endpoint "lindas" ;
          arg:query
            [
              a code:SparqlQuery ;
              code:link <file:queries/external-resource.rq>
            ] ;
          arg:variables
            [
              arg:this [ sh:path schema:sameAs ] ;
            ] ;
        ]
    ] ;
.

Given the above, when a request for an instance of api:ExternalResource would be made, the handler would load the query from queries/external-resource.rq and run it against env.sparql.lindas endpoint.

If arg:endpoint would be omitted, the default endpoint should be used.

arg:variable maps resource contents to SPARQL inputs. The handler would follow sh:path from the request subject. Might also allow an imperative way

Open questions

  1. What happens when there are multiple values found by resolving arg:variables? Especially in the case of multiple variables. Do we just create VALUES clause with the cartesian product?
  2. How could we support federated queries?
tpluscode commented 2 months ago

A problem with federated queries may be that not all stores allow the use of variables like SERVCE ?service.

For that, we may need a way to replace variables instead of generating VALUES. That should fail when multiple values are found though.

[] arg:variables [
  arg:externalEndpoint [ 
    sh:values "lindas"^^kl:endpoint ; # might need a type so that this is not loaded as a plain string
    kl:inlineVariable true ; # to replace in query text
  ] ;
] ;

The above will replace SERVICE ?externalEndpoint with SERVICE <...lindas query endpoint URL...>

giacomociti commented 2 months ago

for example, the query for shared-dimension resources could be:

PREFIX schema: <http://schema.org/>

CONSTRUCT { ?s ?p ?o }
WHERE {
  BIND(NOW() AS ?now)
  $this </api/schema/dimension> ?dimension .

  SERVICE <https://int.lindas.admin.ch/query> {
    ?s ?p ?o ;
      schema:inDefinedTermSet ?dimension .
    OPTIONAL { ?s schema:validFrom ?validFrom }
    OPTIONAL { ?s schema:validThrough ?validThrough }
    FILTER (!BOUND(?validFrom) || ?validFrom <= ?now)
    FILTER (!BOUND(?validThrough) || ?validThrough > ?now)
  }
}

avoiding the need for arg:variables, and assuming only the $this parameter to be replaced with the IRI of the target resource.