PATCH Use Case and Implemented Proposal

Hi all,

The need for a PATCH format for LDP was brought up again in last week's meeting with TimBL [1].  To summarize briefly, Tim made the point that LDP has no generic way to update data without a PATCH format and Arnaud said that the WG wanted one but hadn't yet been able to define it.  Arnaud called for a halt on PATCH formats in May [2]

The two main approaches would seem to be Andy's proposal [3] which uses basic triple patterns to define what to add or remove from a store and Sandro's TurtlePatch [4].

This message suggests a third approach based on a strictly defined subset of SPARQL Update.  Our approach is already implemented in the (Apache 2 licensed) Callimachus Project [5] and documented later in this message.  Henry has already mentioned that a subset of SPARQL Update could work [6] for the WG's purposes.

A strict subset of SPARQL Update is used in the Callimachus implementation to support the following constructs in a PATCH request. However, each clause can only contain zero or more basic graph patterns and only the default graph can be used.
     * DELETE DATA
     * INSERT DATA
     * DELETE WHERE
     * INSERT WHERE
     * DELETE INSERT WHERE

Callimachus further restricts the SPARQL Update spec such that all nodes are connected and all URIs, in the subject position, before a '#' (if present), are the same.

Callimachus enforces this by walking the syntax tree of the parsed SPARQL Update request and flags any construct that does fit with the above. Callimachus uses the Sesame query model, which is a bit different than the SPARQRL grammer. The code that walks the SPARQL Update tree and flags constructs that are not explicitly listed is available online at [7].

Then it verifies the request body [8] by checking that all URIs in the subject position (before '#') are the same [9], all nodes are
connected [10], all present constructs are permitted[7], and finally it makes sure no blank nodes are left disconnected[11].

To try this out, fire up a Callimachus instance and run the follow curl commands:

################################
# Discover the LDPR of the root resource by looknig for rel="describedby"
################################
$ curl -iX OPTIONS http://localhost:8080/

HTTP/1.1 204 No Content
Allow: OPTIONS, TRACE, GET, HEAD, POST, PATCH, DELETE, PUT
Access-Control-Allow-Methods: OPTIONS, TRACE, GET, HEAD, POST, PATCH, DELETE, PUT
Access-Control-Allow-Headers: Accept,Accept-Charset,Accept-Encoding,Accept-Language,Authorization,Cache-Control,Content-Encoding,Content-Language,Content-Length,Content-Location,Content-MD5,Content-Type,If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,Location,Range,Cookie,Referer,Slug
Link: <http://localhost:8080/?discussion>; rel="comments"; type="text/html"
Link: <http://localhost:8080/?search>; rel="search"; type="application/opensearchdescription+xml"
Link: <http://localhost:8080/?describe>; rel="describedby"; type="text/html application/rdf+xml;q=0.4 text/turtle;q=0.5"; title="RDF Describe"
Link: <http://localhost:8080/?realms>; rel="http://callimachusproject.org/rdf/2009/framework#realms"; type="application/atom+xml;q=0.9"
Link: <http://localhost:8080/?contents>; rel="contents"; type="application/atom+xml;q=0.9"
Link: <http://localhost:8080/?edit>; rel="edit-form"; type="text/html"
Link: <http://localhost:8080/?history>; rel="version-history"; type="text/html application/atom+xml;q=0.9"
Link: <http://localhost:8080/?view>; rel="alternate"; type="text/html"
Link: <http://localhost:8080/?archive>; rel="http://callimachusproject.org/rdf/2009/framework#archive"
Link: <http://localhost:8080/?changes>; rel="alternate"; type="text/html"
Cache-Control: public
Vary: Accept
Vary: Origin
Vary: Access-Control-Request-Method
Content-Version: "/callimachus/changes/2013/09/05/t140ee789cecx1971"
ETag: W/"216e0b6d-a038f4fe"
Last-Modified: Thu, 05 Sep 2013 15:59:13 GMT
Access-Control-Allow-Origin: http://localhost:8080
Access-Control-Expose-Headers: Content-Length, Allow, Access-Control-Allow-Methods, Access-Control-Allow-Headers, Link, Cache-Control, Vary, Content-Version, ETag, Last-Modified, Access-Control-Allow-Origin, Content-Type, Content-Encoding, Date, Server
Date: Thu, 05 Sep 2013 16:55:48 GMT
Server: Callimachus/1.2-beta-10


################################
# Create a new resource, by POSTing the RDF as sparql-update
################################
$ curl -i --digest --user james --data-binary @- -H content-type:application/sparql-update http://localhost:8080/?describe
BASE <http://localhost:8080/>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
INSERT DATA {
   <sun> a skos:Concept, </callimachus/1.0/types/Concept> ;
       skos:prefLabel "Sun" ;
       skos:definition "The great luminary" .
}

HTTP/1.1 201 Created
Location: http://localhost:8080/sun
Content-Version: "/callimachus/changes/2013/09/05/t140ee789cecx2216"
Derived-From: "/callimachus/changes/2013/09/05/t140ee789cecx2190"
ETag: W/"216e64d0-62cbb087"
Content-Type: text/uri-list;charset=UTF-8
Access-Control-Allow-Origin: http://localhost:8080
Access-Control-Expose-Headers: Location, Content-Version, Derived-From, ETag, Content-Type, Access-Control-Allow-Origin, Content-Length, Content-Encoding, Date, Server
Date: Thu, 05 Sep 2013 17:02:45 GMT
Content-Length: 25
Content-Encoding: identity
Server: Callimachus/1.2-beta-10

http://localhost:8080/sun


################################
# Discover the LDPR of the newly created resource
################################
$ curl -iX OPTIONS http://localhost:8080/sun

HTTP/1.1 204 No Content
Allow: OPTIONS, TRACE, POST, PATCH, GET, HEAD, DELETE
Access-Control-Allow-Methods: OPTIONS, TRACE, POST, PATCH, GET, HEAD, DELETE
Access-Control-Allow-Headers: Accept,Accept-Charset,Accept-Encoding,Accept-Language,Authorization,Cache-Control,Content-Encoding,Content-Language,Content-Length,Content-Location,Content-MD5,Content-Type,If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,Location,Range
Link: <http://localhost:8080/sun?history>; rel="version-history"; type="text/html application/atom+xml;q=0.9"
Link: <http://localhost:8080/sun?discussion>; rel="comments"; type="text/html"
Link: <http://localhost:8080/sun?describe>; rel="describedby"; type="text/html application/rdf+xml;q=0.4 text/turtle;q=0.5"; title="RDF Describe"
Link: <http://localhost:8080/sun?edit>; rel="edit-form"; type="text/html"
Link: <http://localhost:8080/sun?view>; rel="alternate"; type="text/html"
Cache-Control: public
Vary: Accept
Vary: Origin
Vary: Access-Control-Request-Method
Content-Version: "/callimachus/changes/2013/09/05/t140ee789cecx2216"
ETag: W/"216e64d0"
Last-Modified: Thu, 05 Sep 2013 17:02:45 GMT
Access-Control-Allow-Origin: http://localhost:8080
Access-Control-Expose-Headers: Content-Length, Allow, Access-Control-Allow-Methods, Access-Control-Allow-Headers, Link, Cache-Control, Vary, Content-Version, ETag, Last-Modified, Access-Control-Allow-Origin, Content-Type, Content-Encoding, Date, Server
Date: Thu, 05 Sep 2013 17:03:44 GMT
Server: Callimachus/1.2-beta-10


################################
# Now update the resource using a subset of sparql-update
################################
$ curl --digest --user james -X PATCH --data-binary @- -H content-type:application/sparql-update http://localhost:8080/sun?describe
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
DELETE DATA {
   </sun> skos:definition "The great luminary" .
};
INSERT DATA {
   </sun> skos:definition "The lamp of day" .
};

HTTP/1.1 204 No Content
Content-Version: "/callimachus/changes/2013/09/05/t140ee789cecx2245"
Derived-From: "/callimachus/changes/2013/09/05/t140ee789cecx2241"
ETag: W/"216e652c-469953d3"
Access-Control-Allow-Origin: http://localhost:8080
Access-Control-Expose-Headers: Content-Length, Content-Version, Derived-From, ETag, Access-Control-Allow-Origin, Content-Type, Content-Encoding, Date, Server
Date: Thu, 05 Sep 2013 17:05:48 GMT
Server: Callimachus/1.2-beta-10


With the above, you can try out these other requests below.

The following are some examples of valid PATCH requests:


PATCH /sun?describe HTTP/1.1
Host: localhost:8080
Content-Type:application/sparql-update
Transfer-Encoding: identity

BASE <http://localhost:8080/>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
DELETE DATA {
   </sun> skos:definition "The great luminary" .
};
INSERT DATA {
   </sun> skos:definition "The lamp of day" .
};


PATCH /sun?describe HTTP/1.1
Host: localhost:8080
Content-Type:application/sparql-update
Transfer-Encoding: identity

BASE <http://localhost:8080/>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
INSERT DATA {
   </sun> skos:scopeNode "sun is part of our solar system" .
}


PATCH /sun?describe HTTP/1.1
Host: localhost:8080
Content-Type:application/sparql-update
Transfer-Encoding: identity

BASE <http://localhost:8080/>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
DELETE DATA {
   </sun> skos:scopeNode "sun is part of our solar system" .
};
INSERT {
   </sun> skos:scopeNode ("sun is part of our " <solarSystem>) .
} WHERE {};


PATCH /sun?describe HTTP/1.1
Host: localhost:8080
Content-Type:application/sparql-update
Transfer-Encoding: identity

BASE <http://localhost:8080/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
DELETE WHERE {
   </sun> skos:scopeNode ?list0 .
   ?list0 rdf:first "sun is part of our "; rdf:rest ?list1 .
   ?list1 rdf:first <solarSystem>; rdf:rest rdf:nil .
};
INSERT {
   </sun> skos:scopeNode ("sun is in our " <solarSystem>) .
} WHERE {};


The following requests are INVALID as outlined below.

PATCH /sun?describe HTTP/1.1
Host: localhost:8080
Content-Type:application/sparql-update
Transfer-Encoding: identity

BASE <http://localhost:8080/>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
DELETE {
   </sun> skos:definition ?definition .
} INSERT {
   </sun> skos:definition ?newvalue .
} WHERE {
   </sun> skos:definition ?definition .
   BIND (concat(?definition,"!") AS ?newvalue)
};

HTTP/1.1 400 Only basic graph patterns are permitted


PATCH /sun?describe HTTP/1.1
Host: localhost:8080
Content-Type:application/sparql-update
Transfer-Encoding: identity

BASE <http://localhost:8080/>
CREATE GRAPH </sun> 

HTTP/1.1 500 Unsupported type of update statement: Create


PATCH /sun?describe HTTP/1.1
Host: localhost:8080
Content-Type:application/sparql-update
Transfer-Encoding: identity

BASE <http://localhost:8080/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
DELETE WHERE {
   </sun> skos:scopeNode ("sun is part of our " <solarSystem>) .
};
INSERT {
   </sun> skos:scopeNode ("sun is in our " <solarSystem>) .
} WHERE {};

HTTP/1.1 500 DELETE WHERE may not contain blank nodes


Using a sparql-update grammer parser it was fairly easy to limited the
tokens to a subset that can be validated easily, yet useful to the
client.


Regards,
Dave, with massive work by James Leigh
--
http://about.me/david_wood


[1] http://www.w3.org/2013/meeting/ldp/2013-09-03
[2] http://lists.w3.org/Archives/Public/public-ldp-wg/2013May/0233.html
[3] http://afs.github.io/rdf-patch/
[4] http://www.w3.org/2001/sw/wiki/TurtlePatch
[5] http://callimachusproject.org
[6] http://lists.w3.org/Archives/Public/public-ldp-wg/2013May/0239.html
[7] https://code.google.com/p/callimachus/source/browse/tags/1.1.2/src/org/callimachusproject/form/helpers/TripleAnalyzer.java#220
[8] https://code.google.com/p/callimachus/source/browse/tags/1.1.2/src/org/callimachusproject/form/helpers/EntityUpdater.java#105
[9] https://code.google.com/p/callimachus/source/browse/tags/1.1.2/src/org/callimachusproject/form/helpers/TripleVerifier.java#176
[10] https://code.google.com/p/callimachus/source/browse/tags/1.1.2/src/org/callimachusproject/form/helpers/TripleVerifier.java#289
[11] https://code.google.com/p/callimachus/source/browse/tags/1.1.2/src/org/callimachusproject/form/helpers/EntityUpdater.java#98

Received on Sunday, 8 September 2013 01:09:55 UTC