RE: ldp-ISSUE-20 (POSTed resources): Identifying and naming POSTed resources [Use Cases and Requirements]

See
<http://www.w3.org/2012/ldp/wiki/Use_Cases_And_Requirements#UC-BPC2:_Create_
resource_within_a_container>

 

This use-case is about creating an RDF resource within a container using
HTTP POST.

 

The issue to be determined boils down to 'what is the base URI of a POSTed
document.?'

 

In Turtle URIs in angle brackets may be relative URIs (relative to the
base), so '<>' is the base URI itself.

However, depending on how you interpret the standards, the base URI might be
the URI of the container or the URI of the resource. 

Whichever it is we have solutions (A) and (B) below. 

In either case the response is 201 Created with the 'Location' header
identifying the new resource.

 

A) The base URI is the URI of the resource:

This is in many ways the neater solution. It is less verbose and has the
advantage that we can refer directly to the resource.

 

<> a foaf:PersonalProfileDocument;

   foaf:primaryTopic <#me> .

 

<#me> a foaf:Person;

     foaf:name "Henry" .

                

                

B) The base URI is the URI of the container:

This approach requires the container to Skolemize the anonymous resource
(See <http://www.w3.org/wiki/BnodeSkolemization>).

 

<> rdfs:member [

      a helios_bt:BugtrackerIssue;

      dc:identifier "58365";

      dc:type           "bug";

   ]

 

I would have used the same example, but since (in this interpretation) the
URI of the resource isn't known until it is created, we would have to make
assertions about #me in a separate step.

This also has the slightly ugly disadvantage that the client needs to know
and use the membership predicate (rdfs:member).

 

The Standards

-------------

 

RFC 3986: Uniform Resource Identifier (URI): Generic Syntax

 

5.1.  Establishing a Base URI

The base URI of a reference can be established in one of four ways,
discussed below in order of precedence:

 

1. Base URI Embedded in Content

2. Base URI of the encapsulating entity

3. URI used to retrieve the entity

4. Default Base URI (application-dependent)

 

In the context of an RDF POST:

1. For example, the current base URI may be altered in a Turtle document
using the @base directive.

2. For a document that is enclosed within another entity, such as a message
or archive, the retrieval context is that entity.

3. For a POST there is no retrieval URI.

4. The default can be determined by the Linked Data Platform.

 

My reading of this is that in the absence of an embedded @base, for a
resource in a container, the retrieval context is the container.

This would be identical with the request URI.

 

RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1

 

9.5 POST

"The actual function performed by the POST method is determined by the

   server and is usually dependent on the Request-URI. The posted entity

   is subordinate to that URI in the same way that a file is subordinate

   to a directory containing it, a news article is subordinate to a

   newsgroup to which it is posted, or a record is subordinate to a

   database."

 

This isn't really much help. It reinforces the idea that the posted resource
is subordinate to the container identified by the request-URI. Yet it
doesn't define the base URI for the subordinate entity.

 

14.14 Content-Location

   "The value of Content-Location also defines the base URI for the entity."


   However.. "The meaning of the Content-Location header in PUT or POST
requests is undefined"

   

My reading of this is inconclusive.

 

   

 

--

Steve Battle
Semantic Engineer

Mobile: +44 (0)7503 624 613

E-mail:  <mailto:steve.battle@sysemia.co.uk> steve.battle@sysemia.co.uk
Web:  <http://www.sysemia.com/> www.sysemia.com

 

Sysemia Limited
The Innovation Centre, Bristol &  Bath Science Park, Dirac Crescent,
Emerson's Green, Bristol BS16 7FR
Registered in England and Wales. Company Number: 7555456

DISCLAIMER

Information contained in this e-mail is intended for the use of the
addressee only, and is confidential and may also be privileged. If you
receive this message in error, please advise us immediately. If you are not
the intended recipient(s), please note that any form of distribution,
copying or use of this communication or the information in it is strictly
prohibited and may be unlawful. Attachments to this e-mail may contain
software viruses which may damage your systems. Sysemia Ltd have taken
reasonable steps to minimise this risk, but we advise that any attachments
are virus checked before they are opened.

 

From: Henry Story [mailto:henry.story@bblfish.net] 
Sent: 09 October 2012 10:36
To: Linked Data Platform (LDP) Working Group
Subject: Re: ldp-ISSUE-20 (POSTed resources): Identifying and naming POSTed
resources [Use Cases and Requirements]

 

 

On 8 Oct 2012, at 19:35, Henry Story <henry.story@bblfish.net> wrote:






On 8 Oct 2012, at 11:52, Linked Data Platform (LDP) Working Group Issue
Tracker <sysbot+tracker@w3.org> wrote:




ldp-ISSUE-20 (POSTed resources): Identifying and naming POSTed resources
[Use Cases and Requirements]

http://www.w3.org/2012/ldp/track/issues/20

Raised by: Steve Battle
On product: Use Cases and Requirements

Regarding use-case :
<http://www.w3.org/2012/ldp/wiki/Use_Cases_And_Requirements#UC-BPC2:_Create_
resource_within_a_container>

User-story
<http://www.w3.org/2012/ldp/wiki/Use_Cases_And_Requirements#Hosting_POSTed_R
esources> raises questions about POSTed resources.

* How is the inserted resource identified?
The use-case scenario assumes that the inserted resource is identified by
including its relation via the membership predicate, to the membership
subject.
e.g.

<> rdfs:member [
    a helios_bt:BugtrackerIssue;
    dc:identifier           "58365";
    dc:type      "bug";
    helios_bt:isInBugtracker eg:bugtracker
 ]







* How does the created resource relate to the RDF description? 
See user-story
<http://www.w3.org/2012/ldp/wiki/Use_Cases_And_Requirements#Hosting POSTed
Resources>.

The example above assumes that the object of the insert is an anonymous
(existentially quantified) resource that can be skolemized to produce a URI
that can be returned in the Location header.

e.g. The response the the POST

HTTP/1.1 201 Created
Location: http://example.com/bugtracker/a0001
ETag: W/"1234567890"



I think the answer to this problem is simple and requires some text in
section 4.3
http://www.w3.org/2012/ldp/hg/ldbp.html#http-post

to the effect that: one should POST an RDF document to a collection with
relative 
URIs such that the relatives URIs in the document will be resolve relative
to the 
URI created by the server for that resource.

So if you post

------------------------
<> a foaf:PersonalProfileDocument;
  foaf:primaryTopic <#me> .

<#me> a foaf:Person;
    foaf:name "Henry" .
------------------------

to a collection 

http://profile.example/2012/

then the server will create a resource http://profile.example/2012/93
against which the above document with relative URLs will be resolved
so that one ends up with a document which is isomorphic to

------------------------
<http://profile.example/2012/93> a foaf:PersonalProfileDocument;
  foaf:primaryTopic <#me> .

<http://profile.example/2012/93#me> a foaf:Person;
    foaf:name "Henry" .
------------------------

 

Btw, I had implemented this a while ago here, so it does work, and pretty
nicely too:

 

https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/ReadW
riteWeb.scala#l150

 

 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l150>  150 case POST(_) & RequestContentType(ct) if
representation == DirectoryRepr =>

 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l151>    151               val createType =
Representation.fromAcceptedContentTypes(List(ct))
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l152>    152               r.create(createType) failMap { t
=> NotFound ~> ResponseString(t.getStackTraceString)} flatMap { rNew =>
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l153>    153                 Post.parse(Body.stream(req),
rNew.name, ct) match {
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l154>    154                   case PostRDF(model) => {
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l155>    155                     logger.info("RDF content:\n"
+ model.toString())
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l156>    156                     for {
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l157>    157                       _ <- rNew.save(model)
failMap {
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l158>    158                         t => InternalServerError
~> ResponseString(t.getStackTraceString)
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l159>    159                       }
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l160>    160                     } yield Created ~>
ResponseHeader("Location",Seq(rNew.name.toString))
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l161>    161                   }
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l162>    162                   case PostBinary(is) => {
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l163>    163                     for (_ <- rNew.putStream(is)
failMap { t=> InternalServerError ~> ResponseString(t.getStackTraceString)})
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l164>    164                     yield Created ~>
ResponseHeader("Location",Seq(rNew.name.toString))
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l165>    165                   }
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l166>    166                   case _ => {
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l167>    167                     logger.info("Couldn't parse
the request")
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l168>    168                     (BadRequest ~>
ResponseString("You MUST provide valid content for given Content-Type: " +
ct)).success
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l169>    169                   }
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l170>    170                 }
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l171>    171               }
 
<https://dvcs.w3.org/hg/read-write-web/file/258d2757ef3d/src/main/scala/Read
WriteWeb.scala#l172>    172

 

rNew is the new resource created in the collection.

Post.parse parses the inputstream and de-relativises all URLs to the new
url. 

( Of course one would get the same effect if one just placed the unparsed
document at that location with its relative urls )

 

so the advantage of this is that it does not even require the rdf to be
parsed to work correctly.

 

Henry














* Should POST support a user supplied local-name 'hint'; e.g. based on the
supplied rdfs:label, to support more human-readable URIs?


yes, Atom has something on this I think 
http://tools.ietf.org/html/rfc5023#section-9.7

If it is good enough one should probably use that as they spent enormous
amounts of time
discussing that.




Alternatively, an owl:sameAs could be used in the above to provide a
user-friendly name.





Social Web Architect
http://bblfish.net/

 

Social Web Architect
http://bblfish.net/

 

Received on Tuesday, 9 October 2012 10:39:31 UTC