Annotated query handler
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
- What happens when there are multiple values found by resolving
arg:variables? Especially in the case of multiple variables. Do we just createVALUESclause with the cartesian product? - How could we support federated queries?
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...>
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.