Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/reference/command-line-interface.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ ldh create container --parent "$LDH_BASE" --title "Categories" --slug categories
<pre>item=$(ldh create item --container "$LDH_BASE" --title "Example" --slug example)</pre>
<p><samp>ldh push</samp> writes many documents in one run and prints one line per written document URL or upload URI, in write order; progress and skipped entries go to standard error, and the first failed request stops the run.</p>
<p>Exit codes are <samp>0</samp> on success, <samp>1</samp> on an HTTP error status or runtime failure (the message goes to standard error; add <code>--verbose</code> for a stack trace), and <samp>2</samp> on a usage error.</p>
<p>Redirects are not followed and count as failures, with their <code>Location</code> on standard error: a <code>PUT</code> to a URI without a trailing slash is answered <samp>308 Permanent Redirect</samp> and writes nothing. A write answered <samp>303 See Other</samp> succeeds; <samp>ldh get --head</samp> reports any status, and <samp>ldh get --timegate</samp> takes its <samp>302 Found</samp>.</p>
</div>
<div>
<h2 id="usage">Usage</h2>
Expand Down Expand Up @@ -398,6 +399,7 @@ ldh add file \\
"$doc"</pre>
<p><samp>add file</samp> prints the content-addressed URI of the upload (<samp>{base}uploads/{sha1}</samp>) rather than the document URI — the
<a href="../../tutorial/data/">Data</a> stage of the tutorial feeds exactly that URI into a CSV import.</p>
<p>It appends the file's description to the document (a multipart <code>POST</code>); with <code>--replace</code> it replaces the document's description with it instead (a multipart <code>PUT</code>).</p>
<p>See also the <a href="../../user-guide/import-data/">data import user guides</a>.</p>
<h3 id="request-bodies">Request bodies</h3>
<p><samp>ldh put</samp> and <samp>ldh post</samp> read RDF from an optional <samp>FILE</samp> argument, recognizing the syntax from its extension (<samp>ldh put "$LDH_BASE" root.ttl</samp>), or from standard input, where <code>-t</code>/<code>--content-type</code> is required since a stream carries no name. Either way relative URIs resolve against the target URI. <samp>ldh patch</samp> reads a SPARQL 1.1 update from standard input, validates it and sends it verbatim.</p>
Expand Down
4 changes: 3 additions & 1 deletion docs/reference/configuration.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,9 @@ HOST=ec2-54-235-229-141.compute-1.amazonaws.com</pre>
<dd>Set to <samp>false</samp> if you are not using the self-signed <em>server</em> certificate (e.g. using a LetsEncrypt certificate instead). Not to be confused with the WebID client certificate.
Defaults to <samp>true</samp>.</dd>
<dt><samp>MAX_CONTENT_LENGTH</samp></dt>
<dd>Maximum allowed request body size (<samp>nginx</samp> has a separate setting for this). Defaults to <samp>2097152</samp>.</dd>
<dd>Maximum allowed request body size, in bytes; a larger body is refused with <samp>413 Payload Too Large</samp>. Defaults to <samp>2097152</samp>.</dd>
<dt><samp>MAX_UPLOAD_LENGTH</samp></dt>
<dd>Maximum size, in bytes, of a file upload (a <code>multipart/form-data</code> request body); a larger one is refused with <samp>413 Payload Too Large</samp>. Defaults to <samp>MAX_CONTENT_LENGTH</samp>. <samp>nginx</samp> limits request bodies to this value when it is set, and to <samp>MAX_CONTENT_LENGTH</samp> otherwise.</dd>
<dt><samp>MAX_QUERY_RESPONSE_LENGTH</samp></dt>
<dd>The longest query result, in bytes, that the <a href="../http-api/sparql/"><samp>/sparql</samp> endpoint</a> answers a query with; a longer one is refused with <samp>502 Bad Gateway</samp>. Counted on the triplestore's response to the platform, not on what the caller receives. Defaults to <samp>5242880</samp> (5 MiB).</dd>
<dt><samp>HTTP_MAX_THREADS</samp></dt>
Expand Down
27 changes: 18 additions & 9 deletions docs/reference/http-api/documents.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
</div>
<div>
<h2 id="methods">Methods</h2>
<p>The API also supports the <code>PATCH</code> HTTP method which is <a href="https://www.w3.org/TR/sparql11-http-rdf-update/#http-patch">optional in GSP</a>. It accepts graph-scoped SPARQL updates that will modify the requested document. Only the <code>INSERT/WHERE</code> and <code>DELETE WHERE</code> forms are supported; <code>GRAPH</code> patterns are not allowed.</p>
<p>The API also supports the <code>PATCH</code> HTTP method which is <a href="https://www.w3.org/TR/sparql11-http-rdf-update/#http-patch">optional in GSP</a>. It accepts graph-scoped SPARQL updates that will modify the requested document. Only a single <code>DELETE</code>/<code>INSERT</code> … <code>WHERE</code> or <code>DELETE WHERE</code> operation is supported; <code>GRAPH</code>, <code>WITH</code>, <code>USING</code> and <code>USING NAMED</code> are not allowed (see <a href="#patch-example">graph-scoped updates</a>).</p>
<p>A <code>multipart/form-data</code> body uploads files: a <code>POST</code> appends their descriptions to the document and a <code>PUT</code> replaces the document with them, as the same writes with an RDF body do. The upload limit applies to such a body instead of the request body limit (see <a href="../../configuration/#http">configuration</a>).</p>
<p>Trailing slashes in document URIs are enforced using <code>308 Permanent Redirect</code> responses.</p>
<table>
<caption class="ac-vh">HTTP methods and the responses they return</caption>
Expand Down Expand Up @@ -75,7 +76,7 @@
<tr>
<th rowspan="7" scope="row"><code id="ld-put">PUT</code></th>
<td rowspan="7">Upserts a document</td>
<td rowspan="7"><code>200 OK</code><br></br><code>201 Created</code><br></br><code>308 Permanent Redirect</code></td>
<td rowspan="7"><code>204 No Content</code> (replaced)<br></br><code>201 Created</code> (created)<br></br><code>308 Permanent Redirect</code></td>
<td rowspan="2"><code>400 Bad Request</code></td>
<td>RDF syntax error</td>
</tr>
Expand Down Expand Up @@ -121,7 +122,7 @@
<td rowspan="3">Modifies a document using SPARQL Update</td>
<td rowspan="3"><code>204 No Content</code></td>
<td rowspan="3"><code>422 Unprocessable Entity</code></td>
<td>SPARQL update is not a single graph-scoped <code>INSERT</code>/<code>DELETE</code> operation, or removes an <code>rdf:type</code></td>
<td>SPARQL update is not a single <code>DELETE</code>/<code>INSERT</code> … <code>WHERE</code> or <code>DELETE WHERE</code> operation, uses <code>GRAPH</code>, <code>WITH</code>, <code>USING</code> or <code>USING NAMED</code>, or removes an <code>rdf:type</code></td>
</tr>
<tr>
<td><a href="../../administration/ontologies/#constraints">Constraint</a> violation in the updated document</td>
Expand All @@ -132,6 +133,7 @@
</tbody>
</table>
<p>Any method may additionally answer <code>403 Forbidden</code> — see <a href="../#acl">Access control</a>. Any method that writes may answer <code>428 Precondition Required</code> when the request carries no precondition, or <code>412 Precondition Failed</code> when the one it carries is out of date — see <a href="#preconditions">Conditional writes</a>.</p>
<p>A <code>POST</code> or <code>PATCH</code> answers with the <code>ETag</code> of the document it leaves, so the next write can quote it. A <code>PUT</code> answers with no <code>ETag</code> or <code>Last-Modified</code>: the document it stores is not the payload — the server adds its metadata and skolemizes blank nodes — and <a href="https://www.rfc-editor.org/rfc/rfc9110#section-9.3.4" target="_blank">RFC 9110</a> allows a <code>PUT</code> response a validator only for a representation saved unchanged. A client that writes again after a <code>PUT</code> reads the tag with <code>HEAD</code>.</p>
</div>
<div>
<h2 id="document-metadata">Document metadata</h2>
Expand All @@ -145,6 +147,7 @@
<pre>PUT /regions/ HTTP/1.1
Host: localhost:4443
Content-Type: text/turtle
If-None-Match: *

@prefix dh: &lt;https://w3id.org/atomgraph/linkeddatahub/document-hierarchy#&gt; .
@prefix dct: &lt;http://purl.org/dc/terms/&gt; .
Expand All @@ -168,7 +171,8 @@ Content-Type: text/turtle
<p>The HTTP request to produce a new item can be empty:</p>
<pre>PUT /regions/1/ HTTP/1.1
Host: localhost:4443
Content-Type: text/turtle</pre>
Content-Type: text/turtle
If-None-Match: *</pre>
<p>It will create an item document with the following triples:</p>
<pre>@prefix dh: &lt;https://w3id.org/atomgraph/linkeddatahub/document-hierarchy#&gt; .
@prefix dct: &lt;http://purl.org/dc/terms/&gt; .
Expand All @@ -185,10 +189,12 @@ Content-Type: text/turtle</pre>
</div>
<div>
<h2 id="patch-example">Graph-scoped updates</h2>
<p>A <code>PATCH</code> applies a SPARQL update scoped to the requested document's graph. The request body must contain a single update operation, in one of two forms: <code>DELETE</code>/<code>INSERT</code> with a <code>WHERE</code> clause (either template can be omitted, giving plain <code>INSERT { } WHERE { }</code> or <code>DELETE { } WHERE { }</code>), or the <code>DELETE WHERE</code> shorthand. Every other form is rejected with <samp>422 Unprocessable Entity</samp>, as is a body with more than one operation. Appending a description to the Chai product:</p>
<p>A <code>PATCH</code> applies a SPARQL update scoped to the requested document's graph. The request body must contain a single update operation, in one of two forms: <code>DELETE</code>/<code>INSERT</code> with a <code>WHERE</code> clause (either template can be omitted, giving plain <code>INSERT { } WHERE { }</code> or <code>DELETE { } WHERE { }</code>), or the <code>DELETE WHERE</code> shorthand. Every other form is rejected with <samp>422 Unprocessable Entity</samp>, as is a body with more than one operation. Like any write to an existing document, it quotes the document's entity tag (see <a href="#preconditions">Conditional writes</a>). Appending a description to the Chai product:</p>
<pre>PATCH /products/1/ HTTP/1.1
Host: localhost:4443
Accept: application/n-triples
Content-Type: application/sparql-update
If-Match: "1e635b4-ntriples"

PREFIX schema: &lt;https://schema.org/&gt;

Expand All @@ -197,11 +203,13 @@ INSERT
&lt;https://localhost:4443/products/1/#this&gt; schema:description "A delicate black tea blend" .
}
WHERE {}</pre>
<p>The update executes against that one graph, so <code>GRAPH</code> patterns are not allowed — this request is rejected with
<p>The update executes against that one graph, so it cannot name another: <code>GRAPH</code> is not allowed in a template, in the <code>WHERE</code> pattern (including <code>EXISTS</code> and sub-queries) or in a <code>DELETE WHERE</code>, and neither are <code>WITH</code>, <code>USING</code> and <code>USING NAMED</code>. This request is rejected with
<samp>422 Unprocessable Entity</samp>:</p>
<pre>PATCH /products/1/ HTTP/1.1
Host: localhost:4443
Accept: application/n-triples
Content-Type: application/sparql-update
If-Match: "1e635b4-ntriples"

PREFIX schema: &lt;https://schema.org/&gt;

Expand Down Expand Up @@ -233,10 +241,10 @@ WHERE {}</pre>
<p>A write to a document that already exists must say which state it was written against. Every write reads the document's graph, changes it in memory and writes the whole graph back, so two writers that name no precondition overwrite each other with nothing to show that anything was lost.</p>
<p>Such a write is answered <code>428 Precondition Required</code>. To make it conditional, send either:</p>
<ul>
<li><code>If-Match</code> with the document's current <code>ETag</code>, to modify what you read</li>
<li><code>If-Match</code> with the document's current <code>ETag</code>, to modify what you read. <code>If-Match: *</code> asserts only that some state exists and does not count</li>
<li><code>If-None-Match: *</code>, to create a document only if it does not exist yet</li>
</ul>
<p>Creating a document needs no precondition — there is no prior state to have been written against — so a <code>PUT</code> to a URL that does not exist is accepted as it always was.</p>
<p>Creating a document needs no precondition — there is no prior state to have been written against — so a <code>PUT</code> to a URL that does not exist is accepted without one. A <code>PUT</code> that quotes an <code>If-Match</code> to a URL that does not exist is refused <code>412 Precondition Failed</code>: the state it was written against is gone, and a document deleted in the meantime is not recreated.</p>
<p>If the document changed since you read it, the write is refused <code>412 Precondition Failed</code>, and that response carries the document's current <code>ETag</code> so you can re-read, reapply your change and retry without an extra request. <code>If-None-Match: *</code> against a document that already exists is likewise refused <code>412</code>, which is how a client can create-or-append in two steps.</p>
<div class="ac-alert va-informative" role="status">
<span class="ac-alert-ic"><span aria-hidden="true" class="msi outline">info</span></span>
Expand All @@ -255,7 +263,8 @@ Accept: application/n-triples
Content-Type: application/sparql-update
If-Match: "f731c06f5ac4fec6490dff1399bd5440"

HTTP/1.1 204 No Content</pre>
HTTP/1.1 204 No Content
ETag: "9b0e4d1c27a3f58e6c41d2a7b8f05e93"</pre>
<p><code>HEAD</code> is answered for any access mode the agent holds, not <code>acl:Read</code> alone, so an agent granted <code>acl:Write</code> or <code>acl:Append</code> can read the validator its writes have to quote without being able to read the document. <code>GET</code> still requires <code>acl:Read</code>.</p>
<p>The <a href="../../command-line-interface/">command line interface</a> does this for you: every <samp>ldh</samp> command that writes reads the document's validator first and sends it.</p>
</div>
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/http-api/system-endpoints.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
</tr>
<tr>
<th scope="row"><samp>settings</samp></th>
<td>The dataspace's own description in the dataspace configuration. <code>PATCH</code> with a SPARQL update edits it — this is how <a href="../../administration/packages/">packages</a> are installed and uninstalled. Editing is restricted to end-user dataspaces: a <code>PATCH</code> on an admin dataspace's settings answers <samp>422 Unprocessable Entity</samp></td>
<td>The dataspace's own description in the dataspace configuration. <code>PATCH</code> with a SPARQL update edits it — this is how <a href="../../administration/packages/">packages</a> are installed and uninstalled. The update takes the same forms as a <a href="../documents/#patch-example">document <code>PATCH</code></a>, and is held to the same <a href="../documents/#preconditions">conditional write</a> terms: it quotes the settings' <code>ETag</code> in <code>If-Match</code> or is answered <samp>428 Precondition Required</samp>, a stale tag is answered <samp>412 Precondition Failed</samp>, and a successful update answers <samp>204 No Content</samp> with the new <code>ETag</code>. Editing is restricted to end-user dataspaces: a <code>PATCH</code> on an admin dataspace's settings answers <samp>422 Unprocessable Entity</samp></td>
</tr>
</tbody>
<tbody>
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/migrating-from-5x.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@
<span class="ac-alert-ic"><span aria-hidden="true" class="msi outline">warning</span></span>
<div class="ac-alert-body">
<p class="ac-alert-text">A write to a document that already exists must be conditional: <code>If-Match</code> with the document's current
<code>ETag</code>, or <code>If-None-Match: *</code> to create. An unconditional write is answered <code>428 Precondition Required</code>.</p>
<code>ETag</code>, or <code>If-None-Match: *</code> to create. An unconditional write, or one with <code>If-Match: *</code>, is answered <code>428 Precondition Required</code>.</p>
</div>
</div>
<p>A client that wrote unconditionally now reads the tag first, as the <a href="../command-line-interface/"><samp>ldh</samp> CLI</a> does for you. An entity tag
Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide/upload-file.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
<li>Drop it. The document reloads in <samp>Read</samp> mode with the file among its resources; an image is shown inline</li>
</ol>
<video aria-label="Dropping a file onto a document" controls="controls" preload="metadata" src="/uploads/798343ac386c0c67027a5fda6105547238d4b038"></video>
<p>A file larger than the instance's request limit is refused before it reaches the platform, and the page says so without navigating away.</p>
<p>A file larger than the instance's <a href="../../reference/configuration/#http" target="_blank">upload limit</a> is refused before it reaches the platform, and the page says so without navigating away.</p>
<p>Alternatively, upload through a form:</p>
<ol>
<li>Click the <span class="ac-btn in-primary ap-solid sz-md">Create <span aria-hidden="true" class="msi caret">expand_more</span></span> dropdown at the bottom of the page</li>
Expand Down
Loading