Linked Data Templates (LDT) is a uniform protocol for read-write Linked Data. This document defines an abstract syntax (a data model) of Linked Data applications with SPARQL backends, and semantics of CRUD interactions against their resources. [[LINKED-DATA]][[sparql11-overview]]

LDT can be used to design ontology-driven Web application programming interfaces (APIs). It provides facilities to define interactions with application resources declaratively using SPARQL commands. It also provides a standard method to evaluate requests into responses over application ontology and dataset.

Introduction

Linked Data is a method for publishing structured data, including relationships between data, on the Web using a common data model (RDF). The purpose of Linked Data Templates is to provide means to define read-write Linked Data APIs declaratively using SPARQL and specify a uniform interaction protocol for them.

LDT reconciles ontology-driven Semantic Web, read-write Linked Data and the SPARQL query language. LDT architecture decouples software from domain- or application-specific operations, enabling reuse of the machine-processable operation descriptions (for composition, reasoning etc.) as well as generic implementations of both server and client software. It builds on existing standards; no changes or additions to other specifications are required.

Ontology as a declarative representation of the application's operations, dependency on SPARQL and the formal valuation of interactions are the main features that distinguish LDT from other Linked Data specifications such as Linked Data Platform [[LDP]].

This document defines a precise semantics for SPARQL-backed Linked Data applications, providing a specification of how Create, Read, Update, Delete (CRUD) interactions change the state of their resources. It does so by establishing the following:

The main LDT concepts are explained below.

Applications

LDT architecture
Main components of LDT architecture

An LDT application represents a data space identified by its base URI, in which application resource URIs are usually (but not necessarily) relative to the base URI. The only interface an application provides is RESTful Linked Data: application produces RDF representations when resource URIs are dereferenced, and consumes RDF representations when requested to change resource state.

Services

A service is a datasource from which an LDT application retrieves, and in which it stores RDF data with resource representations. The only interface a service MUST provide is SPARQL 1.1 Protocol [[[!sparql11-protocol]]]. That way an application is agnostic to the service implementation details: it can be a native RDF triplestore, an RDBMS or XML database with an RDF wrapper, or any other compliant datasource.

Ontologies

An ontology is an efficient way to define application structure declaratively as a set of instructions for resource representation processing. LDT application uses a single RDF ontology to define operations specific to that application. Ontologies can be exposed and dereferenced, as well as imported and reused by other LDT applications or third-party software.

Operations

An operation maps a certain URI pattern to a certain SPARQL command. During request processing, an LDT processor matches request URI against operations and selects the best match (if such exists) that will drive the RDF CRUD processing. Operations are defined using the LDT vocabulary, while the SPARQL commands are defined using SPIN RDF syntax and use a "magic" variable ?this which is bound to the request URI value.

Hypermedia

Hypermedia, used here in terms of Hypermedia As The Engine Of Application State (HATEOAS), is the final constraint of REST. It enables loose server/client coupling by requiring the client to navigate by transitioning between server-provided application states. It implies that those states need to be URI-identified and provided in response body. LDT addresses this by providing operation parameters that are used to validate and describe requested application states.

Abstract syntax

This section defines the data model of LDT applications.

REST

In this section we define the abstract syntax REpresentational State Transfer. It sufficient to model a RESTful API independently of the transport protocol.

Request
Request
Response
Response
Body
Message-body
MediaType
Media-type
Status
Status
IRI
IRI
Method
Interaction-method

Request := Method IRI | Method IRI MediaType Body
Response := Status MediaType Body | Status IRI

Linked Data

In this section, we specialize the REST abstract syntax by constraining the message body to RDF serializations and read-write interactions to CRUD.

LDRequest
RDF-request ⊂ Request
LDResponse
RDF-response ⊂ Response
RDFMediaType
RDF-media-type ⊂ Media-type
Dataset
Dataset ⊂ Body
CRUDMethod
CRUD-method ⊂ Method
CRUDStatus
CRUD-status ⊂ Status

LDRequest := CRUDMethod IRI | CRUDMethod IRI RDFMediaType Dataset
LDResponse := CRUDStatus RDFMediaType Dataset | CRUDStatus IRI
CRUDMethod := Create | Read | Delete | Update
CRUDStatus := NotFound | Created | NoContent | OK

Semantics

We use denotational semantics as the formalism for Linked Data semantics.

Domains

Method
Interaction method [[webarch]]
IRIabs
non-relative IRI identifier [[!rdf11-concepts]]
MediaType
Media Type [[!rfc2046]]
Body
Representation [[!webarch]]
Request
Method × IRIabs × MediaType × Body
RDFMediaType
{ text/turtle, text/trig, application/n-triples, application/n-quads, application/ld+json, application/rdf+xml, … } Writing RDF graphs [[rdf11-primer]]
CRUDMethod
{ Create, Read, Delete, Update }
Dataset
RDF dataset [[!rdf11-concepts]][[!sparql11-query]]
LDRequest
CRUDMethod × IRIabs × RDFMediaType × Dataset
CRUDStatus
{ NotFound, Created, NoContent, OK }
Ontology
{ Operation1, …, OperationN } × Precedence × { Ontology1, …, OntologyM }
App
IRI × Ontology × Dataset
Here we define an ontology with a Dataset, yet the implementation/vocabulary uses a Service which is an interface for the Dataset.
QueryVariable
Query Variable [[!sparql11-query]]
RDFTerm
RDF Term [[!sparql11-query]]
QueryBinding
QueryVariable × RDFTerm
Describe
DESCRIBE [[!sparql11-query]]
Construct
CONSTRUCT [[!sparql11-query]]
Query
Describe + Construct
Delete
DELETE [[!sparql11-update]]
InsertData
INSERT DATA [[!sparql11-update]]
Update
Delete + InsertData
Command
Query + Delete
IRIPattern
URI template [[!JAX-RS]]
Parameter
QueryVariable × IRI × Boolean
Operation
IRIPattern × Query × Delete × { Parameter1, …, ParameterN } × Priority
Argument
Parameter × RDFTerm
Class
Class [[!RDF-MT]]

Functions

absolutePath
IRIIRIabs
absolutePath(iri) = fn:substring-before(absoluteIri, "?") = absolutePath ∈ IRIabs [[!xpath-functions-31]]
queryString
IRI → String
queryString(iri) = fn:substring-after(absoluteIri, "?") = queryString ∈ String [[!xpath-functions-31]]
method
Request↓1 → Method
iri
Request↓2 → IRI
mediaType
Request↓3 → MediaType
body
Request↓4 → Body
operations
Ontology↓1 → { Operation1, …, OperationN }
precedence
Ontology↓2 → Precedence
imports
Ontology↓3 → { Ontology1, …, OntologyM }
base
App↓1 → IRI
ontology
App↓2 → Ontology
dataset
App↓3 → Dataset
relativize
IRI × IRIIRIrelIRI
relativize(absoluteIri1, absoluteIri2) = relativeIri
The processor MUST implement relativize as defined in Relative Resolution [[!rfc3986]].
app
IRIApp
app(iri) = appminPath ∈ App such that
fn:string-length(relativize(base(appminPath), iri)) ≤ fn:string-length(relativize(base(app), iri))
for all app ∈ App : app ≠ appminPath [[xpath-functions-31]]
apply
Command × QueryBindingCommand
insertData
DatasetRequest → InsertData
execQuery
Query × DatasetbeforeDataset
execUpdate
Update × DatasetbeforeDatasetafter
join
Update × UpdateUpdate
name
Parameter↓1 → QueryVariable
valueType
Parameter↓2 → IRI
optional
Parameter↓3 → Boolean
iriPattern
Operation↓1 → IRIPattern
query
Operation↓2 → Query
delete
Operation↓3 → Delete
parameters
Operation↓4 → { Parameter1, …, ParameterN }
priority
Operation↓5 → Priority
matchIRIPattern
IRIrel × IRIPattern → Boolean
The processor MUST implement matchIRIPattern as defined in Request Matching, using C = Ontology, TZ = iriPattern(operation) [[!JAX-RS]]
matchrel
IRIrel × OntologyOperation
matchrel(irirel, ontology) = operationmatch ∈ Ontology :
matchIRIPattern(irirel, iriPattern(operationmatch)) = true
match
IRI × IRI × OntologyOperation
match(iri, base, ontology) = matchrel(relativize(base, absolutePath(iri)), ontology) = operationmatch ∈ Operation
Priority and conflict resolution have not been addressed yet
reqMatch
Request × AppOperation
reqMatch(request, app) = match(iri(request), base(app), match(app))
decodeUri
String → String
decodeUri(encoded) = decoded such that fn:encode-for-uri(decoded) = encoded [[xpath-functions-31]]
cast
RDFTerm × IRIRDFTerm
cast(inputValue, targetType) = targetValue ∈ RDFTerm
The processor MUST implement cast as defined in XPath Constructor Functions [[!sparql11-query]]
paramByName
String × OperationParameter
paramByName(name, operation) = param ∈ parameters(operation) : name(param) = name
arguments
String × Operation → { Argument1, …, ArgumentN }
arguments(queryString, operation) = { (paramByName(decodeUri(fn:tokenize(argString, "=")↓1)), cast(decodeUri(fn:tokenize(argString, "=")↓2), valueType(paramByName(decodeUri(fn:tokenize(argString, "=")↓1))))) } : argString ∈ fn:tokenize(queryString, "&") [[xpath-functions-31]]
parameter
Argument↓1 → Parameter
value
Argument↓2 → RDFTerm
type
RDFTermClass
type(term) = class ∈ Class
skolemizeTerm
RDFTermIRI
skolemizeTerm(term) = isBlank(term) = true : build(pathTemplate(type(term)), propertyMap(term)) []
term [[!sparql11-query]]
Skolemization definition not complete. See also Replacing Blank Nodes with IRIs [[rdf11-concepts]]
skolemize
DatasetDataset
skolemize(dataset) = skolemizeTerm(term), term ∈ dataset
conNeg
RequestMediaType
The processor MUST implement conNeg as defined in Determining the MediaType of Responses [[!JAX-RS]]
created
LDRequestIRI
created(rdfRequest) = iri ∈ skolemize(body(rdfRequest))
The processor MUST provide an implementation of created that satisfies the above definition
append
QueryBinding × QueryBindingQueryBinding
append(binding1, binding2) = binding1 + binding2
argBindings
{ Argument1, …, ArgumentN } → QueryBinding
argBindings(arguments) = (name(parameter(arg1)), value(arg1)) + … + (name(parameter(argN)), value(argN)) : argi ∈ arguments
merge
Dataset × DatasetDataset
merge(dataset1, dataset2) = datasetmerged ∈ Dataset
The processor MUST implement merge as defined in RDF Dataset [[!sparql11-query]]
state
IRI × { Argument1, …, ArgumentN } → Dataset
This is the function that should describe the current application state. How do we convert from sets to triples? Parameters need to have IRIs.

Valuation functions

Evaluation of response to request ∈ LDRequest:

LDResponse : LDRequest × DatasetbeforeLDResponse × Datasetafter

LDResponse [[CRUDStatus RDFMediaType Dataset]] (request)
LDResponse [[CRUDStatus RDFMediaType Dataset]] (app(iri(request))) (request)
LDResponse [[CRUDStatus RDFMediaType Dataset]] (app) (request)
LDResponse [[CRUDStatus]] (app) (request) LDResponse [[RDFMediaType]] (app) (request) LDResponse [[Dataset]] (app) (request)
LDResponse [[CRUDStatus IRI]] (request)
LDResponse [[CRUDStatus IRI]] (app(iri(request))) (request)
LDResponse [[CRUDStatus IRI]] (app) (request)
LDResponse [[CRUDStatus]] (app) (request) LDResponse [[IRI]] (app) (request)
LDResponse [[CRUDStatus]] (request)
LDResponse [[CRUDStatus]] (app(iri(request))) (request)
LDResponse [[CRUDStatus]] (app) (request)
reqMatch(request, app) = {} → NotFound []
method(request) = CreateCreated []
method(request) = DeleteNoContent []
OK
LDResponse [[RDFMediaType]] (app) (request)
conNeg(request)
LDResponse [[IRI]] (app) (request)
method(request) = Createcreated(request) []
{}
LDResponse [[Dataset]] (request)
LDResponse [[Dataset]] (app(iri(request))) (request)
LDResponse [[Dataset]] (app) (request)
method(request) = Readmerge(execQuery(apply(query(reqMatch(request, app)), append((?this, absolutePath(iri(request))), argBindings(arguments(queryString(iri(request)), reqMatch(request, app)))))), state(iri(request), arguments(queryString(iri(request)), reqMatch(request, app)))) []
method(request) = CreateexecUpdate(insertData(skolemize(body(request)))) []
method(request) = UpdateexecUpdate(join(apply(delete(reqMatch(request, app)), append((?this, absolutePath(iri(request))), argBindings(arguments(queryString(iri(request)), reqMatch(request, app))))), insertData(body(request)))) []
method(request) = DeleteexecUpdate(apply(delete(reqMatch(request, app)), append((?this, absolutePath(iri(request))), argBindings(arguments(queryString(iri(request)), reqMatch(request, app)))))) []
{}
Does this address Datasetafter (execUpdate side-effects) properly? Maybe we can avoid them by defining Service? [[!sparql11-service-description]]

LDT over HTTP

Abstract syntax

HTTPRequest
HTTP-request ⊂ Request
HTTPResponse
HTTP-response ⊂ Response
HTTPStatus
HTTP-status ⊂ Status
HTTPMethod
HTTP-method ⊂ Method

HTTPRequest := HTTPMethod IRI | HTTPMethod IRI MediaType Body
HTTPResponse := HTTPStatus MediaType Body | HTTPStatus IRI
HTTPMethod := GET | POST | PUT | DELETE
HTTPStatus := 200 OK | 201 Created | 204 No Content | 404 Not Found | 405 Method Not Allowed | 406 Not Acceptable

Semantics

Domains

HTTPRequest
HTTPMethod × IRIabs × Body
HTTPResponse
HTTPStatus × MediaType × IRIabs × Body
HTTPMethod
{ GET, POST, PUT, DELETE } ⊂ Request Methods [[!HTTP11]]
HTTPStatus
{ 200 OK, 201 Created, 204 No Content, 404 Not Found, 405 Method Not Allowed, 406 Not Acceptable } ⊂ Response Status Codes [[!HTTP11]]

Functions

httpMethodToCRUDMethod
HTTPMethodCRUDMethod
httpMethodToCRUDMethod(httpMethod) = httpMethod = POSTCreate []
httpMethod = GETRead []
httpMethod = PUTUpdate []
httpMethod = DELETEDelete []
{}
crudStatusToHTTPStatus
CRUDStatusHTTPStatus
crudStatusToHTTPStatus(status) = status = NotFound404 Not Found []
status = Created201 Created []
status = NoContent204 No Content []
status = OK200 OK []
{}

Valuation functions

Evaluation of response to request ∈ HTTPRequest:

HTTPResponse : HTTPRequestHTTPResponse

HTTPResponse [[HTTPStatus MediaType Body]] (httpRequest)
HTTPResponse [[HTTPStatus]] (httpRequest) HTTPResponse [[MediaType]] (httpRequest) HTTPResponse [[Body]] (httpRequest)
HTTPResponse [[HTTPStatus IRI]] (httpRequest)
HTTPResponse [[HTTPStatus]] (httpRequest) HTTPResponse [[IRI]] (httpRequest)
HTTPResponse [[HTTPStatus]] (httpRequest)
httpMethodToCRUDMethod(method(httpRequest)) = {} → 405 Method Not Allowed []
mediaType(httpRequest) ∉ RDFMediaType406 Not Acceptable []
crudStatusToHTTPStatus(LDResponse [[CRUDStatus]] ((httpMethodToCRUDMethod(method(httpRequest)), iri(httpRequest), mediaType(httpRequest), body(httpRequest))))
HTTPResponse [[MediaType]] (httpRequest)
LDResponse [[RDFMediaType]] (httpMethodToCRUDMethod(method(httpRequest)), iri(httpRequest), mediaType(httpRequest), body(httpRequest))
HTTPResponse [[Body]] (httpRequest)
httpMethodToCRUDMethod(method(httpRequest)) = {} → {} []
body(httpRequest) ∉ Dataset → {} []
LDResponse [[Dataset]] (httpMethodToCRUDMethod(method(httpRequest)), iri(httpRequest), mediaType(httpRequest), body(httpRequest))
HTTPResponse [[IRI]] (httpRequest)
httpMethodToCRUDMethod(method(httpRequest)) = {} → {} []
LDResponse [[IRI]] (httpMethodToCRUDMethod(method(httpRequest)), iri(httpRequest), mediaType(httpRequest), body(httpRequest))

Examples

We will use the following set members for all the subsequent response valuation examples:

appApp
app(iri(request)) = (<http://linkeddatahub.com/>, ontology, dataset)
matchOperation
reqMatch(request, app) = ("/people/{familyName}", describeWithTopic, deleteWithTopic, { graphParam = (?g, <http://www.w3.org/2000/01/rdf-schema#Resource>, true) }, 0)
describeWithTopicDescribe
PREFIX  foaf: <http://xmlns.com/foaf/0.1/>

DESCRIBE ?this ?primaryTopic
WHERE
  { GRAPH ?g
      { ?this  ?p  ?o
        OPTIONAL
          { ?this     foaf:primaryTopic  ?primaryTopic .
            ?primaryTopic
                      ?primaryTopicP     ?primaryTopicO
          }
      }
  }
deleteWithTopicDelete
PREFIX  foaf: <http://xmlns.com/foaf/0.1/>

DELETE {
  GRAPH ?g {
    ?this ?p ?o .
    ?primaryTopic ?primaryTopicP ?primaryTopicO .
  }
}
WHERE
  { GRAPH ?g
      { ?this ?p ?o
        OPTIONAL
          { ?this foaf:primaryTopic ?primaryTopic .
            ?primaryTopic ?primaryTopicP ?primaryTopicO
          }
      }
  }

Ontology

The following example shows the ontology of our example application. Operations used in response valuation are encoded as RDF terms using LDT vocabulary and SPIN vocabulary:

@prefix :     <http://linkeddatahub.com/ns#> .
@prefix ldt:  <http://www.w3.org/ns/ldt#> .
@prefix owl:  <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sp:   <http://spinrdf.org/sp#> .
@prefix spl:  <http://spinrdf.org/spl#> .

: a ldt:Ontology ;
  owl:imports ldt:, sp: ;
  rdfs:label "Example ontology" .

:PersonItemTemplate a ldt:Template ;
  rdfs:label "Person item" ;
  ldt:path "/people/{familyName}" ;
  ldt:param :GraphParam ;
  ldt:query :DescribeWithTopic ;
  ldt:update :DeleteWithTopic ;
  rdfs:isDefinedBy : .

:GraphParam a ldt:Parameter ;
  rdfs:label "Graph parameter" ;
  spl:predicate :g ;
  spl:valueType rdfs:Resource ;
  spl:optional true ;
  rdfs:isDefinedBy : .

:DescribeWithTopic a ldt:Query, sp:Describe ;
  rdfs:label "Describe with topic" ;
  sp:text """PREFIX  foaf: <http://xmlns.com/foaf/0.1/>

DESCRIBE ?this ?primaryTopic
WHERE
  { GRAPH ?g
      { ?this  ?p  ?o
        OPTIONAL
          { ?this     foaf:primaryTopic  ?primaryTopic .
            ?primaryTopic
                      ?primaryTopicP     ?primaryTopicO
          }
      }
  }""" ;
  rdfs:isDefinedBy : .

:DeleteWithTopic a ldt:Update, sp:Modify ;
  rdfs:label "Delete with topic" ;
  sp:text """PREFIX  foaf: <http://xmlns.com/foaf/0.1/>

DELETE {
  GRAPH ?g {
    ?this ?p ?o .
    ?primaryTopic ?primaryTopicP ?primaryTopicO .
  }
}
WHERE
  { GRAPH ?g
      { ?this ?p ?o
        OPTIONAL
          { ?this foaf:primaryTopic ?primaryTopic .
            ?primaryTopic ?primaryTopicP ?primaryTopicO
          }
      }
  }""" ;
  rdfs:isDefinedBy : .

Update over HTTP

The following example shows how an HTTP PUT request with an RDF dataset as its body evaluates as Linked Data Update request on an LDT application. The evaluation result is a successful HTTP response and a SPARQL update execution.
SPARQL update execution replaces the existing description of the requested resource in the application dataset with the one from the request body. The updated application dataset is a side-effect of the Update operation.

HTTP PUT request putRequest with request body putBody

PUT /people/Berners-Lee HTTP/1.1
Host: linkeddatahub.com
Accept: text/turtle, text/trig
Content-Type: text/trig

@base         <http://linkeddatahub.com/people/Berners-Lee> .
@prefix ldtc: <http://www.w3.org/ns/ldt/core#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix owl:  <http://www.w3.org/2002/07/owl#> .

<../graphs/c5f34fe9-0456-48e8-a371-04be71529762>
{

  <> a ldtc:Document ;
    foaf:primaryTopic <#this> .

  <#this> a foaf:Person ;
    foaf:isPrimaryTopicOf <> ;
    owl:sameAs <https://www.w3.org/People/Berners-Lee/card#i> .

}

Valuation of putRequest

HTTPResponse [[HTTPStatus MediaType Body]] (putRequest) = HTTPResponse [[HTTPStatus]] (putRequest) HTTPResponse [[MediaType]] (putRequest) HTTPResponse [[Body]] (putRequest) =

crudStatusToHTTPStatus(LDResponse [[Status]] ((httpMethodToCRUDMethod(method(putRequest)), iri(putRequest), body(putRequest))))
LDResponse [[RDFMediaType]] (httpMethodToCRUDMethod(method(putRequest)), iri(putRequest), body(putRequest))
LDResponse [[Dataset]] (httpMethodToCRUDMethod(method(putRequest)), iri(putRequest), body(putRequest)) =

crudStatusToHTTPStatus(LDResponse [[Status]] ((httpMethodToCRUDMethod(PUT), <http://linkeddatahub.com/people/Berners-Lee>, putBody)))
LDResponse [[RDFMediaType]] (httpMethodToCRUDMethod(PUT), <http://linkeddatahub.com/people/Berners-Lee>, putBody)
LDResponse [[Dataset]] (httpMethodToCRUDMethod(PUT), <http://linkeddatahub.com/people/Berners-Lee>, putBody) =

crudStatusToHTTPStatus(LDResponse [[Status]] ((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody)))
LDResponse [[RDFMediaType]] (Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody)
LDResponse [[Dataset]] (Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody) =

crudStatusToHTTPStatus(LDResponse [[Status]] (app(iri((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody)))) ((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody)))
LDResponse [[RDFMediaType]] (app(iri((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody)))) ((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody))
LDResponse [[Dataset]] (app(iri((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody)))) ((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody)) =

crudStatusToHTTPStatus(LDResponse [[Status]] (app(<http://linkeddatahub.com/people/Berners-Lee>)) ((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody)))
LDResponse [[RDFMediaType]] (app(<http://linkeddatahub.com/people/Berners-Lee>)) ((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody))
LDResponse [[Dataset]] (app(<http://linkeddatahub.com/people/Berners-Lee>)) ((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody)) =

crudStatusToHTTPStatus(LDResponse [[Status]] (app) ((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody)))
LDResponse [[RDFMediaType]] (app) ((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody))
LDResponse [[Dataset]] (app) ((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody)) =

200 OK
LDResponse [[RDFMediaType]] (app) ((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody))
LDResponse [[Dataset]] (app) ((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody)) =

200 OK
conNeg((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody))
execUpdate(join(apply(delete(reqMatch((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody), app)), append((?this, absolutePath(iri((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody)))), argBindings(arguments(queryString(iri((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody))), reqMatch((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody), app)))))), insertData(data((Update, <http://linkeddatahub.com/people/Berners-Lee>, putBody))))) =

200 OK
text/turtle
execUpdate(join(apply(delete(match), append((?this, absolutePath(<http://linkeddatahub.com/people/Berners-Lee>)), argBindings(arguments(queryString(<http://linkeddatahub.com/people/Berners-Lee>), match))))), insertData(putBody))) =

200 OK
text/turtle
execUpdate(join(apply(delete(("/people/{familyName}", describeWithTopic, deleteWithTopic, { graphParam = (?g, <http://www.w3.org/2000/01/rdf-schema#Resource>, true) }, 0)), append((?this, <http://linkeddatahub.com/people/Berners-Lee>), argBindings(arguments("", match))))), insertData(putBody))) =

200 OK
text/turtle
execUpdate(join(apply(deleteWithTopic, append((?this, <http://linkeddatahub.com/people/Berners-Lee>), { }))), insertData(@base         <http://linkeddatahub.com/people/Berners-Lee> .
@prefix ldtc: <http://www.w3.org/ns/ldt/core#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix owl:  <http://www.w3.org/2002/07/owl#> .

<../graphs/c5f34fe9-0456-48e8-a371-04be71529762>
{

  <> a ldtc:Document ;
    foaf:primaryTopic <#this> .

  <#this> a foaf:Person ;
    foaf:isPrimaryTopicOf <> ;
    owl:sameAs <https://www.w3.org/People/Berners-Lee/card#i> .

}))) =

200 OK
text/turtle
execUpdate(join(apply(PREFIX  foaf: <http://xmlns.com/foaf/0.1/>

DELETE {
  GRAPH ?g {
    ?this ?p ?o .
    ?primaryTopic ?primaryTopicP ?primaryTopicO .
  }
}
WHERE
  { GRAPH ?g
      { ?this ?p ?o
        OPTIONAL
          { ?this foaf:primaryTopic ?primaryTopic .
            ?primaryTopic ?primaryTopicP ?primaryTopicO
          }
      }
  }, (?this, <http://linkeddatahub.com/people/Berners-Lee>)), BASE    <http://linkeddatahub.com/>
PREFIX  ldtc: <http://www.w3.org/ns/ldt/core#>
PREFIX  owl:  <http://www.w3.org/2002/07/owl#>
PREFIX  foaf: <http://xmlns.com/foaf/0.1/>

INSERT DATA {
  GRAPH <../graphs/c5f34fe9-0456-48e8-a371-04be71529762> {
    <people/Berners-Lee> a ldtc:Document .
    <people/Berners-Lee> foaf:primaryTopic <people/Berners-Lee#this> .
    <people/Berners-Lee#this> a foaf:Person .
    <people/Berners-Lee#this> foaf:isPrimaryTopicOf <people/Berners-Lee> .
    <people/Berners-Lee#this> owl:sameAs <https://www.w3.org/People/Berners-Lee/card#i> .
  }
})) =

200 OK
text/turtle
execUpdate(join(BASE    <http://linkeddatahub.com/>
PREFIX  foaf: <http://xmlns.com/foaf/0.1/>

DELETE {
  GRAPH ?g {
    <people/Berners-Lee> ?p ?o .
    ?primaryTopic ?primaryTopicP ?primaryTopicO .
  }
}
WHERE
  { GRAPH ?g
      { <people/Berners-Lee> ?p ?o
        OPTIONAL
          { <people/Berners-Lee> foaf:primaryTopic ?primaryTopic .
            ?primaryTopic ?primaryTopicP ?primaryTopicO
          }
      }
  }, BASE    <http://linkeddatahub.com/>
PREFIX  ldtc: <http://www.w3.org/ns/ldt/core#>
PREFIX  owl:  <http://www.w3.org/2002/07/owl#>
PREFIX  foaf: <http://xmlns.com/foaf/0.1/>

INSERT DATA {
  GRAPH <../graphs/c5f34fe9-0456-48e8-a371-04be71529762> {
    <people/Berners-Lee> a ldtc:Document .
    <people/Berners-Lee> foaf:primaryTopic <people/Berners-Lee#this> .
    <people/Berners-Lee#this> a foaf:Person .
    <people/Berners-Lee#this> foaf:isPrimaryTopicOf <people/Berners-Lee> .
    <people/Berners-Lee#this> owl:sameAs <https://www.w3.org/People/Berners-Lee/card#i> .
  }
})) =

200 OK
text/turtle
execUpdate(BASE    <http://linkeddatahub.com/>
PREFIX  foaf: <http://xmlns.com/foaf/0.1/>

DELETE {
  GRAPH ?g {
    <people/Berners-Lee> ?p ?o .
    ?primaryTopic ?primaryTopicP ?primaryTopicO .
  }
}
WHERE
  { GRAPH ?g
      { <people/Berners-Lee> ?p ?o
        OPTIONAL
          { <people/Berners-Lee> foaf:primaryTopic ?primaryTopic .
            ?primaryTopic ?primaryTopicP ?primaryTopicO
          }
      }
  } ;

BASE    <http://linkeddatahub.com/>
PREFIX  ldtc: <http://www.w3.org/ns/ldt/core#>
PREFIX  owl:  <http://www.w3.org/2002/07/owl#>
PREFIX  foaf: <http://xmlns.com/foaf/0.1/>

INSERT DATA {
  GRAPH <../graphs/c5f34fe9-0456-48e8-a371-04be71529762> {
    <people/Berners-Lee> a ldtc:Document .
    <people/Berners-Lee> foaf:primaryTopic <people/Berners-Lee#this> .
    <people/Berners-Lee#this> a foaf:Person .
    <people/Berners-Lee#this> foaf:isPrimaryTopicOf <people/Berners-Lee> .
    <people/Berners-Lee#this> owl:sameAs <https://www.w3.org/People/Berners-Lee/card#i> .
  }
})

HTTP response

HTTP/1.1 200 OK

Read over HTTP

The following example shows how an HTTP GET evaluates as Linked Data Read request on an LDT application. The evaluation result is a successful HTTP response with a body that is an RDF dataset. The dataset is a result of a SPARQL query execution on the application dataset, augmented with hypermedia arguments.
The request dereferences the IRI and retrieves the same representation that was stored by the previous example.

HTTP GET request getRequest with query string arguments

GET /people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762 HTTP/1.1
Host: linkeddatahub.com
Accept: text/turtle, text/trig

Valuation of getRequest

HTTPResponse [[HTTPStatus MediaType Body]] (getRequest) = HTTPResponse [[HTTPStatus]] (getRequest) HTTPResponse [[MediaType]] (getRequest) HTTPResponse [[Body]] (getRequest) =

crudStatusToHTTPStatus(LDResponse [[Status]] ((httpMethodToCRUDMethod(method(getRequest)), iri(getRequest), body(getRequest))))
LDResponse [[RDFMediaType]] (httpMethodToCRUDMethod(method(getRequest)), iri(getRequest), body(getRequest))
LDResponse [[Dataset]] (httpMethodToCRUDMethod(method(getRequest)), iri(getRequest), body(getRequest)) =

crudStatusToHTTPStatus(LDResponse [[Status]] ((httpMethodToCRUDMethod(GET), <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)))
LDResponse [[RDFMediaType]] (httpMethodToCRUDMethod(GET), <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)
LDResponse [[Dataset]] (httpMethodToCRUDMethod(GET), <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody) =

crudStatusToHTTPStatus(LDResponse [[Status]] (Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)))
LDResponse [[RDFMediaType]] (Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)
LDResponse [[Dataset]] (Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody) =

crudStatusToHTTPStatus(LDResponse [[Status]] (app(iri((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)))) ((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)))
LDResponse [[RDFMediaType]] (app(iri((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)))) ((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody))
LDResponse [[Dataset]] (app(iri((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)))) ((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)) =

crudStatusToHTTPStatus(LDResponse [[Status]] (app(<http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>)) ((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)))
LDResponse [[RDFMediaType]] (app(<http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>)) ((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody))
LDResponse [[Dataset]] (app(<http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>)) ((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)) =

crudStatusToHTTPStatus(LDResponse [[Status]] (app) ((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)))
LDResponse [[RDFMediaType]] (app) ((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody))
LDResponse [[Dataset]] (app) ((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)) =

200 OK
LDResponse [[RDFMediaType]] (app) ((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody))
LDResponse [[Dataset]] (app) ((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)) =

200 OK
conNeg((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody))
merge(execQuery(apply(query(reqMatch((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody), app)), append((?this, absolutePath(iri((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)))), argBindings(arguments(queryString(iri((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody))), reqMatch((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody), app)))))), state(iri((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody)), arguments(queryString(iri((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody))), reqMatch((Read, <http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, getBody), app)))) =

200 OK
text/trig
merge(execQuery(apply(query(match), append((?this, absolutePath(<http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>)), argBindings(arguments(queryString(<http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>), match))))),
state(<http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, arguments(queryString(<http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>), match))) =

200 OK
text/trig
merge(execQuery(apply(describeWithTopic, append((?this, <http://linkeddatahub.com/people/Berners-Lee>), argBindings(arguments("g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762", match))))), state(<http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, arguments("g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762", match))) =

200 OK
text/trig
merge(execQuery(apply(describeWithTopic, append((?this, <http://linkeddatahub.com/people/Berners-Lee>), argBindings({ (paramByName(decodeUri(fn:tokenize("g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762", "=")↓1)), cast(decodeUri(fn:tokenize("g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762", "=")↓2), valueType(paramByName(decodeUri(fn:tokenize("g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762", "=")↓1))))) })))), state(<http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, { (paramByName(decodeUri(fn:tokenize("g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762", "=")↓1)), cast(decodeUri(fn:tokenize("g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762", "=")↓2), valueType(paramByName(decodeUri(fn:tokenize("g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762", "=")↓1))))) })) =

200 OK
text/trig
merge(execQuery(apply(describeWithTopic, append((?this, <http://linkeddatahub.com/people/Berners-Lee>), argBindings({ (paramByName("g"), cast("http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762", valueType(paramByName("g")))) })))), state(<http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, { (paramByName("g"), cast("http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762", valueType(paramByName("g")))) })) =

200 OK
text/trig
merge(execQuery(apply(describeWithTopic, append((?this, <http://linkeddatahub.com/people/Berners-Lee>), (?g, <http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762>)))), state(<http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, { (graphParam, <http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762>) })) =

200 OK
text/trig
merge(execQuery(apply(PREFIX  foaf: <http://xmlns.com/foaf/0.1/>

DESCRIBE ?this ?primaryTopic
WHERE
  { GRAPH ?g
      { ?this  ?p  ?o
        OPTIONAL
          { ?this     foaf:primaryTopic  ?primaryTopic .
            ?primaryTopic
                      ?primaryTopicP     ?primaryTopicO
          }
      }
  }, append((?this, <http://linkeddatahub.com/people/Berners-Lee>), (?g, <http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762>)))), state(<http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, { (graphParam, <http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762>) })) =

200 OK
text/trig
merge(execQuery(BASE    <http://linkeddatahub.com/>
PREFIX  foaf: <http://xmlns.com/foaf/0.1/>

DESCRIBE <people/Berners-Lee> ?primaryTopic
WHERE
  { GRAPH <graphs/c5f34fe9-0456-48e8-a371-04be71529762>
      { <people/Berners-Lee>  ?p  ?o
        OPTIONAL
          { <people/Berners-Lee>     foaf:primaryTopic  ?primaryTopic .
            ?primaryTopic
                      ?primaryTopicP     ?primaryTopicO
          }
      }
  }), state(<http://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, { (graphParam, <http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762>) }))

HTTP response

HTTP/1.1 200 OK
Content-Type: text/trig

@base         <http://linkeddatahub.com/people/Berners-Lee> .
@prefix ldt:  <http://www.w3.org/ns/ldt#> .
@prefix ldtc: <http://www.w3.org/ns/ldt/core#> .
@prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix owl:  <http://www.w3.org/2002/07/owl#> .

<../graphs/c5f34fe9-0456-48e8-a371-04be71529762>
{

  <> a ldtc:Document ;
    foaf:primaryTopic <#this> .

  <#this> a foaf:Person ;
    foaf:isPrimaryTopicOf <> ;
    owl:sameAs <https://www.w3.org/People/Berners-Lee/card#i> .

}

<?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762> ldt:arg [
  a <http://linkeddatahub.com/ns#GraphParam> ;
  ldt:paramName "g" ;
  rdf:value <http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762>
] .

Vocabulary

Namespaces used in this section
Prefix Namespace URI
ldt: http://www.w3.org/ns/ldt#
rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
rdfs: http://www.w3.org/2000/01/rdf-schema#
xsd: http://www.w3.org/2001/XMLSchema#
sd: http://www.w3.org/ns/sparql-service-description#

Application

Applications are orthogonal to operations. Does it make sense to put them under the same namespace?

Application
Domain Property Range Cardinality
App ldt:Application ldt:base rdfs:Resource IRI +
App ldt:Application ldt:ontology ldt:Ontology Ontology 1
App ldt:Application ldt:service sd:Service ???? 1

Operation

Operation
Domain Property Range Cardinality
Operation ldt:Template ldt:path xsd:string IRIPattern 1
Operation ldt:Template ldt:query ldt:Query Query 1
Operation ldt:Template rdfs:isDefinedBy ldt:Ontology Ontology 1
Operation ldt:Template ldt:update ldt:Update Update ?
Operation ldt:Template ldt:param ldt:Parameter Parameter *
Operation ldt:Template rdfs:subClassOf rdfs:Class Operation *

Should we align set names with RDF terms? I.e. ldt:Operation instead of ldt:Template?
The range of the rdfs: properties is not accurate here in terms of RDFS.

Operation (HTTP)
Domain Property Range Cardinality
Operation ldt:Template ldt:lang rdf:List(xsd:string) ?
Operation ldt:Template ldt:cacheControl xsd:string ?

The HTTP properties are not part of LDT over HTTP yet. We need to introduce HTTP headers.
Also core and HTTP probably should not be under the same namespace.

Hypermedia

Argument
Domain Property Range Cardinality
IRI rdfs:Resource ldt:arg Argument *