|
Key
This line was removed.
This word was removed. This word was added.
This line was added.
|
Comment:
Changes (2)
View Page Historyh1. Introduction
h2. Goals
Version 5 of ROSRS has been the most extensively used version so far. Based on this experience we would like to create version 6, with the following goals in mind:
* Fill the gaps discovered in version 5.
* Be fully RESTful and therefore consistent with other project APIs.
* Implement all project models: RO and ROEVO.
h2. Requirements
These requirements should be resolved against ?[RO SRS REST interface - ver. 5|docs:RO SRS REST interface - ver. 5].
* Add annotations via API (i.e. a dedicated RDF resource)
* Add resource references via API, such as external resources, web services or databases.
* Specify RDF classes of resources that are added via API (i.e. query paramaters)
* Handle RO versions (see ?[docs:RO identification & versioning])
h1. API function overview
The Research Object Storage and Retrieval API is intended to allow:
* storing and retrieving Research Objects
* storing and retrieving resources aggregated within the Research Objects
* annotating the aggregated resources, including the Research Object itself
* defining relations between aggregated resources, including the Research Object itself?
{info:title=Scope}The first two bullets above correspond to the scope of ROSR 5. The second 2 bullets might therefore be part of ROSR 6 or form a separate API, namely the annotation API.{info}
h1. API usage
h2. Authorization
Authorization and access control policy is out of scope of this document but for simplicity, the use of OAuth 2.0 with the Bearer authorization schema will be assumed.
h2. Get a list of Research Objects
{code}
C: GET /ROs/ HTTP/1.1
C: Host: example.com
C: Accept: text/plain
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 200 OK
S: Content-Type: text/uri-list
S: Content-Length: ...
S:
S: http://example.com/ROs/ro1/
S: http://example.com/ROs/ro2/
S: ...
{code}
{note:title=Response format}
The response format is subject to discussion, it may well be an RDF format instead of a list of URIs.
{note}
h2. Create a new Research Object
The Research Object id can be any string. If this id is already in use, 409 Conflict will be returned.
{code}
C: POST /ROs/ HTTP/1.1
C: Host: example.com
C: Content-Type: text/plain
C: Content-Length: ...
C: Accept: text/turtle
C: Slug: ro id
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 201 Created
S: Location: http://example.com/ROs/ro%20id/
S: Content-Type: text/turtle
S: Content-Length: ...
S:
S: (the initial manifest)
{code}
h2. Get a Research Object
What do you get when you ask for an RO? This implementation is related to the page ??[docs:RO dereferencing]:
* If "text/html" is requested, the response is a 303 redirection to the Portal.
* If any RDF type is requested, the response is a 303 redirection to the manifest.
* If "application/zip", "multipart/related" or anything else (or nothing in particular) is requested, the response is a 303 redirection to a URI */zippedROs/:roid/*. This is a specific resource only for downloading ROs as ZIP files. Requests to this resource will return a ZIP file regardless of the Accept header, which in case of web browsers is out of control of the user.
|| RO requested (example) \\ || Accept HTTP header \\ || Response code \\ || Response Location HTTP header \\ || Notes ||
| ROs/ro1/ \\ | application/zip \\ | 303 See Other \\ | zippedROs/ro1/ | |
| ROs/ro1/ \\ | text/html | 303 See Other \\ | sandbox.../portal?ro=...ROs/ro1/ | Portal page |
| ROs/ro1/ \\ | application/rdf+xml \\ | 303 See Other | ROs/ro1/.ro/manifest.rdf | |
| ROs/ro1/ \\ | text/turtle \\ | 303 See Other \\ | ROs/ro1/.ro/manifest.ttl?original=manifest.rdf \\ | \\ |
| ROs/ro1/ \\ | \- | 303 See Other \\ | zippedROs/ro1/ \\ | \\ |
Links to all 3 formats available are included as link headers or HTML links described in the ?[docs:RO dereferencing].
This example assumes retrieving the RO metadata, i.e. the manifest.
{code}
C: GET /ROs/ro_id/ HTTP/1.1
C: Host: example.com
C: Accept: text/turtle
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 303 See Other
S: Location: http://example.com/ROs/ro_id/.ro/manifest.ttl?original=manifest.rdf
C: GET /ROs/ro_id/.ro/manifest.ttl?original=manifest.rdf HTTP/1.1
C: Host: example.com
C: Accept: text/turtle
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 200 OK
S: Content-Type: text/turtle
S: Content-Length: ...
S:
S: <http://example.com/ROs/ro_id/.ro/manifest.rdf> a <http://purl.org/wf4ever/ro#Manifest> ;
(...)
{code}
This example assumes retrieving a zip file.
{code}
C: GET /ROs/ro_id/ HTTP/1.1
C: Host: example.com
C: Accept: application/zip
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 303 See Other
S: Location: http://example.com/zippedROs/ro_id/
C: GET /zippedROs/ro_id/ HTTP/1.1
C: Host: example.com
C: Accept: application/zip
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 200 OK
S: Content-Type: application/zip
S: Content-Length: ...
S:
S: (content here)
{code}
h2. Delete a Research Object
{code}
C: DELETE /ROs/ro_id/ HTTP/1.1
C: Host: example.com
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 204 No Content
{code}
h2. Aggregate a resource
Each resource aggregated in the research object has an associated proxy. Proxies are references to the resource in the context of the research object and in the case of external resources, their representations in the ROSR API.
Aggregating resources involves 2 steps, though in most cases one of them can be omitted:
# Creating a proxy for a resource.
# Uploading a resource.
The first step is recognized by making a request with MIME type application/vnd.wf4ever.proxy and using a resource URI not pointed to by an existing proxy. When a proxy is created, the proxy target is automatically aggregated in the research object.
The resource URI is indicated either using the Slug header if it's a relative URI reference, or in the request body otherwise. The server will return 409 Conflict if the resulting URI has already been aggregated in this research object.
Resources are aggregated in the following ways:
# External resources: only create a proxy, pointing to the external resource.
# Internal resources: only upload a resource. The service will create the proxy automatically and will return the URI of the proxy rather than the URI of the resource itself.
# Internal resources, the long way: create a proxy and then upload the resource. This is the only method to add a proxy as an aggregated resource (a rare case).
The example below demonstrates aggregating an external resource.
{code}
C: POST /ROs/ro1/ HTTP/1.1
C: Host: example.com
C: Content-Type: application/vnd.wf4ever.proxy
C: Content-Length: ...
C: Authorization: Bearer h480djs93hd8
C:
C: http://example.com/external.txt
S: HTTP/1.1 201 Created
S: Location: https://sandbox/rodl/ROs/ro1/.ro/proxies/9a3fdd90-becf-11e1-afa7-0800200c9a34
S: Link: http://example.com/external.txt; rel="http://www.openarchives.org/ore/terms/proxyFor"
{code}
The example below demonstrates aggregating an internal resource.
{code}
C: POST /ROs/ro_id/ HTTP/1.1
C: Host: example.com
C: Content-Type: text/plain
C: Content-Length: ...
C: Slug: foo/bar.txt
C: Authorization: Bearer h480djs93hd8
C:
C: Lorem ipsum
S: HTTP/1.1 201 Created
S: Location: http://example.org/ROs/ro_id/.ro/proxies/d953d020-becc-11e1-afa7-0800200c9a66
S: Link: http://example.com/ROs/ro_id/foo/bar.txt; rel="http://www.openarchives.org/ore/terms/proxyFor"
{code}
The example below demonstrates adding a proxy and then aggregating an internal resource.
{code}
C: POST /ROs/ro_id/ HTTP/1.1
C: Host: example.com
C: Content-Type: application/vnd.wf4ever.proxy
C: Slug: foo/bar.txt
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 201 Created
S: Location: http://example.org/ROs/ro_id/.ro/proxies/d953d020-becc-11e1-afa7-0800200c9a66
S: Link: http://example.com/ROs/ro_id/foo/bar.txt; rel="http://www.openarchives.org/ore/terms/proxyFor"
C: GET /ROs/ro_id/foo/bar.txt HTTP/1.1
C: Host: example.com
S: HTTP/1.1 404 Not Found
C: PUT /ROs/ro_id/foo/bar.txt HTTP/1.1
C: Host: example.com
C: Content-Type: text/plain
C:
C: This is the important note
S: HTTP/1.1 201 Created
S: Location: http://example.com/ROs/ro_id/foo/bar.txt
S: Link: http://example.com/ROs/ro_id/foo/bar.txt; rel="http://www.openarchives.org/ore/terms/proxyFor"
{code}
{note:title=Proxies URI}
The .ro/ resource is dedicated to storing content generated by ROSR service. Requests to store content inside it will result in 403 Forbidden.
{note}
h2. Update an aggregated resource
The example below demonstrates updating an internal resource. Updating external resources is out of control of the service.
Clients may use the proxy URI or, for internal resources, the resource URI.
Trying to create new content in this way or trying to update the manifest will result in 403 Forbidden.
{code}
C: PUT /ROs/ro1/.ro/proxies/d953d020-becc-11e1-afa7-0800200c9a66 HTTP/1.1
C: Host: example.com
C: Content-Type: text/plain
C: Content-Length: ...
C: Authorization: Bearer h480djs93hd8
C:
C: Lorem ipsum dolor sit amet
S: HTTP/1.1 307 Temporary Redirect
S: Location: http://example.com/ROs/ro_id/foo/bar.txt
C: PUT /ROs/ro_id/foo/bar.txt HTTP/1.1
C: Host: example.com
C: Content-Type: text/plain
C: Content-Length: ...
C: Authorization: Bearer h480djs93hd8
C:
C: Lorem ipsum dolor sit amet
S: HTTP/1.1 200 OK
{code}
When a resource that is RO metadata is modified, different formats can be used by means of content negotiation. For updating such resources, the clients may use either resource URIs or the format-specific URIs. Responses for *PUT*:
|| Resource requested (example) \\ || URI extension \\ || Content-Type HTTP header \\ || Response code \\ || Request body RDF format \\ ||
| graph.rdf \\ | yes \\ | application/rdf+xml \\ | 200 OK \\ | based on URI extension and Content-Type HTTP header \\ |
| graph.rdf \\ | yes \\ | \- \\ | 200 OK \\ | based on URI extension \\ |
| graph.rdf \\ | yes \\ | text/turtle \\ | 200 OK \\ | based on Content-Type HTTP header \\ |
| graph \\ | no \\ | application/rdf+xml | 200 OK \\ | based on Content-Type HTTP header \\ |
| graph \\ | no \\ | \- \\ | 200 OK \\ | default: RDF/XML \\ |
h2. Get an aggregated resource
The default response when dereferencing the resource URI is the resource content. Resource metadata can be found in the manifest.
{code}
C: GET /ROs/ro1/foo/bar.txt HTTP/1.1
C: Host: example.com
C: Accept: text/plain
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 200 OK
S: Content-Type: text/plain
S: Content-Length: ...
S:
S: Lorem ipsum
{code}
When dereferencing the aggregated resource proxy, the client is redirected to the resource that the proxy is for.
{code}
C: GET /ROs/ro1/.ro/proxies/9a3fdd90-becf-11e1-afa7-0800200c9a34 HTTP/1.1
C: Host: example.com
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 303 See Other
S: Location: http://example.com/external.txt
S: Link: <http://example.com/ROs/ro1/>; rel="up"
{code}
When retrieving resources that are RDF RO metadata, content negotiation is allowed.
* Specific RDF format can be asked using content negotiation, i.e. adding an "Accept: text/turtle" header to the request.
* If the URI has no extension or has an extension that does not match the requested format, the server will return 302 Found redirecting the client to the format-specific resource. See below.
* The format-specific resource can be used by clients to access the resource directly in a specific format without content negotiation. Note the "original" query param that identifies the format-specific version of another resource.
The table below demonstrates the expected responses for *GET* requests to named graphs.
|| Resource requested (example) \\ || URI extension \\ || Accept HTTP header \\ || Response code \\ || Response Location HTTP header \\ || Response RDF format \\ ||
| graph.rdf \\ | yes \\ | application/rdf+xml \\ | 200 OK \\ | \- \\ | based on URI extension and Accept HTTP header \\ |
| graph.rdf \\ | yes \\ | \- \\ | 200 OK \\ | \- \\ | based on URI extension \\ |
| graph.rdf \\ | yes \\ | text/turtle \\ | 302 Found \\ | graph.ttl?original=graph.rdf \\ | based on Accept HTTP header \\ |
| graph \\ | no \\ | application/rdf+xml \\ | 302 Found \\ | graph.rdf?original=graph \\ | based on Accept HTTP header \\ |
| graph \\ | no \\ | \- \\ | 302 Found \\ | graph.rdf?original=graph \\ | default: RDF/XML \\ |
{note:title=302 vs. 303}
Some redirection in this API is done using 302 Found, some using 303 See Other. Is it on purpose and correct in all cases?
{note}
h2. Remove an aggregated resource
Removing has a slightly different meaning for internal and external resources:
* For internal resources, it means de-aggregating the resource, deleting its proxy from the manifest and deleting the resource itself.
* For external resources, it means de-aggregating the resource and deleting its proxy from the manifest.
Clients may use the proxy URI or, for internal resources, the resource URI.
For resources that have different RDF formats (i.e. RO metadata), the clients may use either resource URIs or the format-specific URIs.
Trying to remove .ro/manifest.rdf will result in 403 Forbidden.
The example below demonstrated de-aggregating an external resource.
{code}
C: DELETE /ROs/ro_id/.ro/proxies/9a3fdd90-becf-11e1-afa7-0800200c9a34 HTTP/1.1
C: Host: example.com
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 204 No Content
{code}
The example below demonstrated de-aggregating an internal resource. If the internal resource has no content (i.e. only a proxy was created), there will be no redirection and the proxy will be deleted.
{code}
C: DELETE /ROs/ro_id/.ro/proxies/9a3fdd90-becf-11e1-afa7-0800200c9a34 HTTP/1.1
C: Host: example.com
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 307 Temporary Redirect
S: Location: [http://example.com/ROs/ro_id/foo/bar.txt]
C: DELETE /ROs/ro_id/.ro/foo/bar.txt HTTP/1.1
C: Host: example.com
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 204 No Content
{code}
h2. Annotate a resource
In order to annotate a resource, an annotation has to be created in the manifest, pointing to a set of annotated resources and an annotation body. The annotated resources must be resources already aggregated by the research object. The annotation body may an internal or external resource. In the first case, it will also have a proxy as all internal resources aggregated by the research object. The annotation body does not have to exist when annotating a resource.
Annotating resources involves 2 steps, though in some cases one of them can be omitted:
# Creating an annotation for a resource or resources.
# Aggregating an annotation body.
The first step is recognized by making a request with MIME type application/vnd.wf4ever.annotation. The second step is recognized by aggregating a resource that is either referenced in an existing annotation or has a Link header {{ao:annotates}}.
Resources are annotated in the following ways:
# External annotation bodies: only create an annotation, pointing to the external annotation body. Optionally, aggregate the external annotation body in the research object.
# Internal annotation bodies: only aggregate an annotation body and use the {{ao:annotates}} Link header. The service will create the proxy and the annotation automatically and will return the URI of the annotation.
# Internal annotation bodies, the long way: create an annotation and then upload the annotation body, or the other way round. This is the only method to add an annotation as an annotation body (a rare case). It can als be used to convert an existing aggregated resource into an annotation body.
The example below demonstrates annotating a resource foo/bar.txt using an external annotation body.
{code}
C: POST /ROs/ro1/ HTTP/1.1
C: Host: example.com
C: Content-Type: application/vnd.wf4ever.annotation
C: Content-Length: ...
C: Authorization: Bearer h480djs93hd8
C:
C: {
C: "annotationBody": "http://example.com/external.txt",
C: "annotatesResource": ["http://example.com/ROs/ro_id/foo/bar.txt"]
C: }
S: HTTP/1.1 201 Created
S: Location: http://example.com/ROs/ro_id/.ro/annotations/4a00dc90-c02c-11e1-afa7-0800200c9a66
S: Link: <http://example.com/ROs/ro_id/foo/bar.txt>; rel="http://purl.org/ao/annotatesResource"
S: Link: <http://example.com/external.txt>; rel="http://purl.org/ao/annotationBody"
{code}
The example below demonstrates annotating the same resource using an internal annotation body that will be created as "foo/ann.ttl". Note that the proxy URI for the annotation body is not returned, even though it is created.
{code}
C: POST http://example.com/ROs/ro_id/ HTTP/1.1
C: Link: <http://example.com/ROs/ro_id/foo/bar.txt>; rel="http://purl.org/ao/annotates"
C: Slug: foo/ann.ttl
C: Content-Type: text/turtle
C:
C: <http://example.com/ROs/ro_id/foo/bar.txt> dcterm:description "How great!" .
S: HTTP/1.1 201 Created
S: Location: http://example.com/ROs/ro_id/.ro/annotations/4a00dc90-c02c-11e1-afa7-0800200c9a66
S: Link: <http://example.com/ROs/ro_id/foo/bar.txt>; rel="http://purl.org/ao/annotatesResource"
S: Link: <http://example.com/external.txt>; rel="http://purl.org/ao/annotationBody"
{code}
The example below demonstrates adding an annotation body and then creating the annotation. Note that the use of the resource URI is recommended over using the proxy URI.
{code}
C: POST http://example.com/ROs/ro_id/ HTTP/1.1
C: Content-Type: text/turtle
C: Slug: foo/ann.ttl
C:
C: <http://example.com/ROs/ro_id/foo/bar.txt> dcterm:description "How great!" .
S: HTTP/1.1 201 Created
S: Location: http://example.com/ROs/ro_id/.ro/proxies/9a3fdd90-becf-11e1-afa7-0800200c9a34
S: Link: http://example.com/ROs/ro_id/foo/ann.ttl; rel="http://www.openarchives.org/ore/terms/proxyFor"
C: POST http://example.com/ROs/ro_id/ HTTP/1.1
C: Content-Type: application/vnd.wf4ever.annotation
C:
C: {
C: "annotationBody": "http://example.com/ROs/ro_id/foo/ann.ttl"
C: "annotatesResource": ["http://example.com/ROs/ro_id/foo/bar.txt"]
C: }
S: HTTP/1.1 201 Created
S: Location: http://example.com/ROs/ro_id/.ro/annotations/4a00dc90-c02c-11e1-afa7-0800200c9a66
S: Link: <http://example.com/ROs/ro_id/foo/bar.txt>; rel="http://purl.org/ao/annotatesResource"
S: Link: <http://example.com/ROs/ro_id/foo/ann.ttl>"
{code}
h2. Update an annotation
The example below demonstrates updating an existing annotation. Trying to create a new annotation in this way will result in 403 Forbidden.
{code}
C: PUT /ROs/ro_id/.ro/annotations/4a00dc90-c02c-11e1-afa7-0800200c9a66 HTTP/1.1
C: Host: example.com
C: Content-Type: application/vnd.wf4ever.annotation
C: Content-Length: ...
C: Authorization: Bearer h480djs93hd8
C:
C: {
C: "annotationBody": "http://example.com/external.txt",
C: "annotatesResource": ["http://example.com/ROs/ro_id/foo/bar.txt"]
C: }
S: HTTP/1.1 200 OK
S: Link: <http://example.com/ROs/ro_id/foo/bar.txt>; rel="http://purl.org/ao/annotatesResource"
S: Link: <http://example.com/external.txt>; rel="http://purl.org/ao/annotationBody"
{code}
h2. Get an annotation
When dereferencing an annotation, the client is redirected to the annotation body.
{code}
C: GET /ROs/ro_id/.ro/annotations/4a00dc90-c02c-11e1-afa7-0800200c9a66 HTTP/1.1
C: Host: example.com
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 303 See Other
S: Location: http://example.com/ROs/ro_id/foo/ann.ttl
S: Link: <http://example.com/ROs/ro1/>; rel="up"
{code}
Note that since an annotation body is always an RDF graph, content negotiation is allowed. If you do content negotiation in the first request, you will be redirected to a format-specific resource.
{code}
C: GET /ROs/ro_id/.ro/annotations/4a00dc90-c02c-11e1-afa7-0800200c9a66 HTTP/1.1
C: Host: example.com
C: Accept: application/rdf+xml
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 303 See Other
S: Location: http://example.com/ROs/ro_id/foo/ann.rdf?original=ann.ttl
S: Link: <http://example.com/ROs/ro1/>; rel="up"
{code}
h2. Remove an annotation
Removing an annotation does not remove the annotation body, even if it is an internal resource. For this reason, note that the annotation URI must be used, not the annotation body URI.
{code}
C: DELETE /ROs/ro_id/.ro/annotations/4a00dc90-c02c-11e1-afa7-0800200c9a66 HTTP/1.1
C: Host: example.com
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 204 No Content
C: GET /ROs/ro_id/foo/ann.ttl HTTP/1.1
C: Host: example.com
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 200 OK
S: Content-Type: text/turtle
S:
S: <http://example.com/ROs/ro_id/foo/bar.txt> dcterm:description "How great!" .
{code}
h1. Link relations
{note:title=A missing link}
As Graham pointed out below, we miss a link from the ROs collection (/ROs/) to individual ROs (/ROs/ro_id/). For simplicity, I'd suggest using {{ore:aggregates}} for that.
{note}
h2. ore:aggregates
This relation comes from the ?[docs:Research Object Vocabulary Specification v0.1] and is used to indicate that a resource is aggregated by the Research Object. Ore:aggregates links the RO with {{ro:Resources}} and with {{ro:AggregatedAnnotations}}. It does NOT link it with {{ore:Proxies}} and annotation bodies.
h2. ro:annotatesAggregatedResource
This relation comes from the ?[docs:Research Object Vocabulary Specification v0.1] and is used to indicate that a resource that is {{ore:aggregated}} by the RO is annotated by another resource, called the annotation body.
h2. ao:body
This relation comes from the ?[docs:Research Object Vocabulary Specification v0.1] and it links a resource that is an {{rdfs:Graph}} (the annotation body) with an annotation. By combining the {{ro:annotatesAggregatedResource}} and {{ao:body}}, annotation bodies are linked with aggregated resources that they annotate.
h1. Resources and formats
@@Identify resources that are accessed or manipulated by this API, and their possible formats
The description below is based on ?[docs:Research Object Vocabulary Specification v0.1].
A *research object* aggregates *aggregated resources*. *Aggregated resources* can be *internal* (a serialization of the resource specific to the research object is stored by the service) or *external* (the service only holds a reference to the resource). A specific type of internal aggregated resources are *aggregated annotations*, which annotate other aggregated resource of the same research object.
{warning:title=Problem}
The definition of an aggregated resource is not good. The RO can aggregate:
* internal files
* references to remote resources
* metadata files, such as annotations or even the manifest itself
Is it one or three resources, and what formats are anticipated?
{warning}
h2. Research Object
A Research Object can have 3 different formats, as described in [docs:RO dereferencing]. These are:
* a ZIP file with all internal resources and metadata.
* an RDF file, i.e. its manifest.
* an HTML page.
h2. Aggregated resource
An internal aggregated resource can have 2 formats:
* an RDF file with its metadata. @@TODO put an example
* its content.
An external aggregated resource has only 1 format - its metadata. @@TODO put an example
{warning:title=Problem}
Annotations are a missing resource.
Also, some statements may belong to the manifest more than to an annotation, for example the RDF class of a resource (i.e. wfdesc:Document). How should these be added and deleted if the manifest can't be download and uploaded?
{warning}
h1. Cache considerations
@@Note anticipated cacheability constraints on resource representations exchanged by this API
h1. Security considerations
This service stores data provided by users and allows to manipulate it, so access control is a crucial part of the API.
In order to authorize requests, OAuth 2 should be used. Bearer authorization schema is recommended since it is as simple as possible while providing the necessary security level if used with HTTPS.
Access control policy, i.e. mechanisms of deciding if the request can be performed based on the access token used, is out of scope of this document.
h1. References
@@Links to related specifications, notes, etc.
h1. Review/feedback comments by GK (remove from final document)
h2. Notes by GK while implementing
List of ROs (response to GET to service URI) should be identified as a resource, and format(s) described.
Review MIME type names. Should be registered (at least vnd.wf4ever, or whatever) - need to check procedure.
Consider using RDF+content-features as alternative to MIME type?
Link header requires URI enclosed in <...> (e.g. Link: http://example.com/ROs/ro_id/foo/bar.txt;) ... See: http://tools.ietf.org/html/rfc5988#section-5
Link: header in response to PUT on long-form creation of internal aggregated resource looks wrong.
Did we decide that deleting the proxy for an internal resource would just delete the proxy and corresponding resource without further redirection? (Otherwise, we could have problem with internal resource proxy where the resource had not been created. Also, I think it's easier to use.)
@@@@
h2. Second review by GK
h3. Get a list of ROs
Provision for any other information, which might be used to select an RO?
Imagine you're using this URI to create a portal. How is a client to know which of the listed ROs is the one they want?
h3. Creating an RO
Still need clarity about what the target URI is - IO thik it the URI of the ROSRS service.
Use of Slug. I think it's OK, even though this is not AtomPub, but make usre your use is consistent with [http://tools.ietf.org/html/rfc5023#section-9.7] \- also I'd suggest making an explicit reference to this.
I'm reminded that I keep on meaning to suggest Sword 2 as an alternative submission protocol ([http://swordapp.org/sword-v2/sword-v2-specifications/]). I haven't read the very latest version, and I think there may be some areas where there are some loose ends or over-specification, but it does have a significant community of support. I don't think this should divert our present efforts, as we still need to fully understand what we're trying to do, but at some stage it would be good to do a detailed comparison.
POST to create a new RO: is the request entity-body empty?
What, if anything, is returned in the response entity body? I'd suggest a copy of the initial manifest could be returned (which would include the resource Id and more).
h3. GET a research object
I think this has been done to death - skipping. Let's just see what happens when we implement it :-)
BUT, the issue of how to discover the URI for an RO seems to be missing
h3. DELETE a research object
Note that this works only for internal ROs. External RO dealt with separately - see \[[later?|later?]\]
h3. Add an aggregated resource
You seem to be creating a new "well known" URI for this purpose (.ro/aggregated), which is not very REST. Given an RO URI, how does one discover the URI to POST to? Personally, I'd consider using POST direct to the RO URI for adding new aggregations (and possibly new annotations - as an annotation is, per model, just a particular kind of aggregated resource).
(We discussed this, but I'm not sure there's a conclusion.)
Two suggestions:
1. (Direct
Internal resources: POST content to RO
External resources: GET/HEAD RO and get link to proxy aggregation resource; POST to proxy with URI of external resource. Need format for post.
2. (AtomPub model - all via proxies)
All resources: POST to RO: content or special MIME type with proxy description. If content, service creates proxy and returns URI.
To POST proxy description, as aggregated resource create a new proxy description for it, and post that.
Proxy description should handle internal content OR reference to URI.
(NOTE: proxy description is not an aggregated resource. It may appear in the manifest. It does not (necessarily) have a separate physical existence within the RO)
h3. Update aggregated resource
PUT to resource URI if internal (or external, but that's not for RODL)
To discover URI, get manifest (see previous)
Suggest avoid direct update of manifest (or I don't understand table here).
Separately, how to update proxy description?
h3. Remove an aggregated resource
Assuming URI of internal resource OR URI of proxy for internal OR external resource
I'm not finding the examples very helpful - need to state where the various URIs come from / what they denote.
h3. Proxy discovery (new topic?)
. Manifest
All proxies arer described in the RO manifest
2. Proxy aggregation
(see (1) above) Retrieve proxy aggregation to obtain a list of proxy descriptions
Then:
DELETE proxy to de-aggregate resource
GET proxy to obtain full description ... probably no more than is provided by the list of proxy descriptions
h3. Link relations
ore:aggregates - NOTE: may need to include anchor parameter with Link: header if redirected from RO to RO manifest
rdfs:Graph \-> rdfg:Graph
I think we're missing a relation to locate proxies for an RO (need to be able to navigate from RO, can't rely on naviogating from aggregated resopurce, as it may be external).
*Extra*
I'm thinking that it would be useful to create "Hypertext Application State" navigation map (HASMap?) to summarize the links between resources that an application can navigate.
h3. Annotations
Unlike proxies, annotations *are* aggregated resources
Unlike resources, annotations do not (necessarily) have a physical existence in an RO - they may be resources described in the RO manifest
Suggest: create by POST to proxy aggregation or RO, with sufficient distinguishing information and content and/or URI of annotation body.
The annotation resource itself is created in the manifest (else there's no way to link from manifest to aggregation). The annotation body may be created as a separate resource in an appropriate location in the RO, such as .ro/...
My code doesn't use ro:annotatesAggregatedResource. Rather, I discover the annotation and use that. Feels to me that it's redundant in the model.
[http://www.wf4ever-project.org/wiki/display/docs/Research+Object+Vocabulary+Specification+v0.1] doesn't obviously show use of annotations with proxies.
h2. Comments
h3. API function overview
It's not clear if managing aggregations (other than by rewriting the manifest) should be a function. E.g. adding resources to an RO; having the service deal with internal/external resources, etc. This might be a candidate for a separate API.
{color:#008000}Piotr: what would be the scope of this API then - RO discovery and retrieval?{color}
h3. API usage
Unlike other APIs we've considered so far in this sprint, this API supports a number of different usage patterns for different purposes. I'm not sure if this is indicative that there are too many functions covered in a single API.
In these examples, what is considered to be the service URI? (In the document as presented, it's not clear if you are using client-side knowledge of API structures rather than HATEOAS principles to determine the URIs for different API functions. I would like to see the API usage summary show how you can navigate from the service URI to a desired resource based on assumed information.
I could imagine, for example, that the RODL service URI is:
[http://example.org/ROs/]
In which case the get list and create RO APIs could be just right. Then, I would expect that [http://example.org/ROs/ro_id/] is a URI listed by the function to get a list of ROs. But how would a client know *which* RO they wanted to retrieve if all you provide is a list or URIs. You could retrieve every RO and examine its content, but that seems to be extraordinarily inefficient. This question does rather beg how the API is intended to support RO discovery, which I suspect is a larger question that the design of this API.
*Getting a research object*: "Note that the client should repeat the content negotiation with the manifest, otherwise the RDF/XML format will be returned." I assume this means that the client should re-send the Accept header used with the original request. One might consider that the initial redirect should be to an appropriately formatted manifest based on the original Accept header, so that if the new request does not include an Accept header then the originally requested format is returned.
{color:#008000}Piotr: yes, that would indeed be more consistent.{color}
*Add an aggregated resource*. This isn't entirely clear to me. I think you are doing PUT / DELETE of a URI of a resource within the RO. Do these operations also update the manifest? I think this is OK for DELETE, and for updating an existing resource in an RO, as the URIs can be discovered from the manifest. But for creating a new resource within the RO, I am, concerned that the client is being control of the RO server's URI space. Although the general expectation is that the service will reflect the local file structure of an RO in its URIs, I don't think that should be assumed by the URI. Therefore, I would suggest a POST to the RO with an additional header (somewhat like AtomPub Slug:-) that indicates the relative reference to the new resource within the RO, and let the server allocate a URI.
{color:#008000}Piotr: We may change PUT to POST for the resources. Although as you said, it would be good if the server stuck to the URI suggested for sync purposes, and I don't see a reason why it wouldn't.{color}
*Get an aggregated resource*. Seems to be assuming that all resources are RDF. Or, from the example, negotiating RDF returns metadata about the resource. A problem here is: what happens if the resource *is* RDF/XML? (I think the suggestion to get the original if no content negotiation is very dubious, and might even break cacheing (**)) I would expect to retrieve this metadata via the manifest and/or annotations. I think this needs some discussion.
{color:#008000}Piotr: There is no content negotiation and the query param "content" is used to ask for the content. Maybe it should be stated more clearly. At the moment a part of manifest.rdf is returned that describes the resource, and I think it was supposed to be in conformance with the Linked Data principles?{color}
(**) or, at least, rather surprising: GET with Accept RDF returns one value, get without Accept RDF returns a different RDF value.
There's some mention of resource URIs or format-specific URIs for RO components. I think this needs exploring - I wasn't expecting there to be format variations for RO components. (We had a discussion about something similar in the ResourceSync working group last week, and the feeling there was to avoid having the repository provide alternative formats for resources if they weren't explicitly deposited. The complications get difficult to manage if the repository is also doing optional format conversions.) Unless I'm misunderstanding your aim here, I think this is a topic for wider discussion/review.
{color:#008000}Piotr: resources can be named graphs (i.e. annotation bodies), in which case the client might want to do content negotiation.{color}
h3. Link relations
I think it's the key manifest properties (relations) that count here - ore:aggregates and maybe the aggregated annotation property. There are two levels to consider:
# finding ROs in the repository, which is (maybe) handled by list ROs (is there an RDF format for the RO listing?); maybe also a link to the sparql endpoint for the repository?
# finding resources and annotations within an RO.
h3. Resources
*Aggregated resource*. I'm uneasy about this - see above. Simple case would be that an aggregated resource is simply whatever was aggregated.
The list of ROs is also a resource. Also the RO manifest, (RO aggregated values - you have this), RO annotations - all the different values that can be retrieved using HTTP GET; all the things that have their own URIs. SPARQL end point for repository? Maybe want do discuss options when repository service URI is dereferenced? Use references to RO model, etc, where possible.
{color:#008000}Piotr: RO can aggregate anything, a fair point. However, some resources are treated differently than others and I think we must account for that in the API.{color}
h3. Cache considerations
Using PUT to update resources should ensure that cached values in intervening proxies get invalidated. Because RO contents are mutable, values should be served with restricted Cache-control: max-age values, or even no-cache, so old values don't get stuck in other caches. For ROs that are known to be immutable, longer max-age values may be used (cf. ROEVO). Similar considerations apply to other resources - RO listing, manifests, etc.
h3. Security considerations
Reference to user and access control model when available. Also reference to OAuth mechanisms you are using.
h3. Wrap up
That's it for now. I think further discussion would be good. This API is pretty central to the project, so we probably want to be sure we're all on the same page with it.
h2. API discussion - chat log
This is the chat log from an audio discussion of the API proposals and review notes. We didn't really discuss anything other than options for updating RO aggregated resources.
Present: Piotr, Raul, Kevin, Graham
{code}
[] Piotr Holubowicz added gklyne, kevin.page, Raul Palma to this conversation
[] Piotr Holubowicz: I've opened a separate channel
[] gklyne: Good. I'm here.
[] Piotr Holubowicz:
[] kevin.page: trying to keep muted as I know the builtin mic is crap :-)
[] Piotr Holubowicz: [http://www.wf4ever-project.org/wiki/display/docs/RO+SRS+interface+6+-+discussion]
[] kevin.page: yeah, I'm about 70% through the page :-)
[] kevin.page: I think so too, maybe
[] kevin.page: e.g. annotations
[] kevin.page: Storage & Retrieval
[] gklyne: Scope of API[s]
[] gklyne: (1) RO creaton, discovery, retrieval
[] gklyne: (2) RO manipulation of aggregated resources in RO
[] gklyne: (3) ROP manipulation of annotations
[] gklyne: For discussion, not actually agreed (yet)
[] kevin.page: yes :-)
[] kevin.page: resources are key
[] gklyne: Piotr: suggest re-ordering of chapters.
[] Raul Palma: annotations are aggregated resources
[] kevin.page: \+1
[] gklyne: Resources:
[] gklyne: (a) research Object
[] gklyne: (b) list of research objects
[] kevin.page: I think you've started that enumeration in your review comments, Graham...
[] gklyne: :@kevin sure... but this is our chance to discuss
[] gklyne: What's "the model" here?
[] kevin.page: but it is a way to navigate the API
[] kevin.page: from a starting point that Graham refers to as the Service URI
[] gklyne: (c) service (may be presented as list of ROs)
[] gklyne: (d.) aggregated resource (i.e. a resourcve that is aggregated by an RO)
[] gklyne: ... may be internal (i.e. part of the RO) or external (via ORE proxy)
[] gklyne: (e.) aggregated annotation
[] gklyne: ... annotations may behave slightly differently?
[] gklyne: ... (piotr: "different weight of interaction")
[] gklyne: Focus on internal resources - currently client has to construct the URI.
[] gklyne: q\+
[] gklyne: q\+ to reiterate the solution I propose
[] kevin.page: I think my call just dropped
[] gklyne: Propose: POST to RO with information about internal RO path. Client can't assume what the resulting URI will be. But Client *may* assume that subsequently retrieved RO has the new resource at the indicated location in the RO package.
[] gklyne: It seems the call is down. Don't know about chat.
[] kevin.page: I've lost audio
[] kevin.page: and call just dropped
[] gklyne: Yep...
[] kevin.page: piotr showing offline... needs more battery? ;-)
[] gklyne: Ack.
[] gklyne: @kevin:
[] kevin.page: the PUT implies the client needs to construct the RO path?
[] kevin.page: or is it a generic PUT?
[] kevin.page: (for each RO, or a query param, or both)
[] gklyne: to deal with your "indirected upload" I suggest mabve a new MIME content type in the POST body with the URI to retrieve by service.
[] gklyne: PUT MUST use URI of resource, otherwsie caching is broken.
[] kevin.page: for a new aggregated resource?
[] kevin.page: (sure for existing URIs)
[] gklyne: for anything. That's web architecture. caches are entitles to remember and serve content of PUT
[] kevin.page: well yes, but what is the URI?
[] kevin.page: :-) history fixed
[] gklyne: [https://github.com/wf4ever/ro-catalogue/blob/master/v0.1/trivial/.ro/manifest.rdf]
[] gklyne: How to delete an aggregated resource...
[] gklyne: proxy?
[] Raul Palma: (external)
[] gklyne: Kevin: If there is no requirement at info model level for a proxy, we shouldn;'t introduce one for the API alone.
[] gklyne: Good point.
[] gklyne: q\+ to raise model suggested to kevin earlier
[] gklyne: @piotr if the URI can be used for DELETE, is must also be GETable
[] kevin.page: but if there's a proxy it is a DELETE
[] gklyne: ACTION: graham write up non-proxy proposal; piotr write up proxy proposal
{code}
NOTE: discussion of PUT from about \[21/06/2012 14:43:39\] is due to original typo by GK which was subsequently edited in the chat log. Probably shoukd be disregarded.
(My non-proxy proposals are just below)
h2. Proposals for de-aggregating external resources from an RO
Both of these approaches focus on deleting an external aggregated resource from an RO, but are easily extensible to handle other required operations that don't fit within the capabilities of simple GET/PUT/DELETE HTTP operations (e.g. Kevin's suggestion of adding an internal resource indicated by reference).
h3. POST to RO with special content-type
This approach depends on introducing a new content-type with POST which, instead of being added as a new internal aggregated resource,m is recognized as processed as an instruction to modify the RO aggregated content. E.g.
{code}
c: POST /path/to/ro/
c: content-type: application/vnd.wf4ever.ro-update
c:
c: REMOVE <URI-of-aggregated-resource>
{code}
Disadvantage: reserved MIME content type cannot be added to aggregation. Feels a bit ad hoc.
h3. New link relation
This approach introduces a new link relation (somewhat like AtomPub's 'media-edit' link relation) associated with an RO which can be used for manipulating the RO content.
{code}
c: HEAD /path/to/ro
s: 200 OK
s: Link: <RO-edit-URI>; rel="http://purg.org/ro/edit"
c: POST <RO-edit-URI>
c: content-type: application/vnd.wf4ever.ro-update
c:
c: REMOVE <URI-of-aggregated-resource>
{code}
In this case, any content type could be used, and it would be possible to use different content types for different syntax of editing commands.
It would also be possible to include the edit link in the RO manifest without distorting the RO model (discuss?).
h3. ORE Proxies as resources
Although there have been some confusion on this, the RO model [mandates|http://wf4ever.github.com/ro/#d3e285] that a {{ro:Resource}} always have a {{ore:Proxy}} for representing that resource as it has been aggregated in an {{ro:ResearchObject}}:
{quote}An ro:Resource is an ore:AggregatedResource which ore:isAggregatedBy an ro:ResearchObject.
This specialisation *requires that there exists an ore:Proxy* which is ore:proxyFor this resource, and which is ore:proxyIn the same ro:ResearchObject the resource ore:isAggregatedBy. Any annotations on such a proxy will descrive the ro:Resource within that particular ro:ResearchObject, in particular dct:creator and dct:created on the proxy will specify who added the resource to the aggregation at what time.{quote}
(My emphasis)
Thus even if an {{ore:Proxy}} has not been stated in the RO manifest, one is implied for every aggregated {{ro:Resource}}. Note that the [ORE specification|http://www.openarchives.org/ore/1.0/datamodel#Proxies] does however mandate that if there exists a proxy, it must be literally asserted in the manifest. It also requires that "_A Proxy URI (URI-P) MUST be unique to both an Aggregation (URI-A) and to a particular Aggregated Resource (URI-AR) of that Aggregation._" - and so given {{ore:proxyIn}} and {{ore:proxyFor}} the proxy is uniquely identified.
In the RO model, ORE proxies were initially thought to be useful for contextualised annotations:
{code:lang=javascript|title=http://www.example.com/ro1/manifest}<> ore:aggregates :ann3 ;
ore:aggregates <helloworld.t2flow>, <whyThisWorkflow.ttl>, :ann3 ;
ore:isDescribedBy <manifest> .
:workflowProxy a ore:Proxy ;
ore:proxyFor <helloworld.t2flow> ;
ore:proxyIn <> .
:ann3 a ro:Annotation, ao:GraphAnnotation ;
ao:annotatesResource :workflowProxy ;
ao:body <whyThisWorkflow.ttl> ;
dct:creator _:stian ;
dct:createdAt "2011-07-14T16:21:14Z"^^xsd:dateTime .
{code}
{code:lang=javascript|title=http://www.example.com/ro1/whyThisWorkflow.ttl}# ...
:workflowProxy dct:description "Best workflow I could find for now"@en;
{code}
In this example from the [RO primer|http://wf4ever.github.com/ro-primer/#anno-wf-3], the proxy for {{<whyThisWorkflow.ttl>}} is identified with a hash URI {{manifest#workflowProxy}} and can't be accessed as a separate HTTP resource.
Stian's suggestion is to give real HTTP URIs to these these proxies and use them for explicit RODL operations on aggregations, in particular for external resources.
Below is a set of suggested sections for dealing with aggregations. It might need an introductory text about aggregation vs. proxy.
h3. Finding aggregated resources
{code}
C: GET http://example.org/ROs/ro_id/ HTTP/1.1
C: Accept: text/turtle
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 303 See Other
S: Location: http://example.org/rodl/ROs/ro_id/.ro/manifest.ttl?original=manifest.rdf
C: GET http://example.org/ROs/ro_id/.ro/manifest.ttl?original=manifest.rdf HTTP/1.1
S: HTTP/1.1 200 OK
S: Content-Type: text/turtle
S:
S: (..)
@base <http://example.org/rodl/ROs/ro_id/> # Climb out of .ro/
@prefix ro: <http://purl.org/wf4ever/ro#> .
# ...
<> a ro:ResearchObject
ore:aggregates <helloworld.t2flow>, <http://example.com/external.txt> ;
ore:isDescribedBy <.ro/manifest.rdf> ;
ore:similarTo <.ro/aggregated/> .
<.ro/aggregated/> dcterms:format "text/uri-list" .
<.ro/aggregated/1> a ore:Proxy;
ore:proxyIn <> ;
ore:proxyFor <helloworld.t2flow> .
<.ro/aggregated/2> a ore:Proxy;
ore:proxyIn <> ;
ore:proxyFor <http://example.com/external.txt> .
{code}
Each resource aggregated with *ore:aggregates* has a corresponding *ore:Proxy* marked by *ore:proxyFor*. The proxy represent the membership of the aggregated resource in this particular research object. This can be accessed as a resource on its own, as shown below.
*ore:similarTo* gives a link to a resource of all proxies (all aggregations).
{note}
Do we need an API specific relation instead of {{ore:similarTo}}, perhaps listed as a {{Link}} header rather than in the manifest? Should not change the RO model for this, but we could also have our own namespace for the API, say {{rodl:aggregated}}.
{note}
h3. Retrieving all aggregations
Following the link from *ore:similarTo* in the manifest:
{code}
C: GET http://example.org/ROs/.ro/aggregated/ HTTP/1.1
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 200 OK
S: Content-Type: text/uri-list
S: Link: <http://example.org/ROs/ro_id/>; rel="alternate"
S:
S: http://example.org/ROs/.ro/aggregated/1
S: http://example.org/ROs/.ro/aggregated/2
{code}
This list is not required to be in any particular order, but MUST include all proxies in the manifest which are {{ore:proxyFor}} the resource object.
The response SHOULD include a {{alternate}} Link to the resource object.
h3. Retrieving an aggregated resource
HEAD or GET on the proxy for <helloworld.t2flow> gives a 303 redirect to the aggregated resource:
{code}
C: HEAD http://example.org/ROs/ro_id/.ro/aggregated/1 HTTP/1.1
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 303 See Other
S: Location: http://example.org/ROs/ro_id/helloworld.t2flow
S: Link: <http://example.org/ROs/ro_id/>; rel="up"
{code}
The response SHOULD include an {{up}} relation pointing to the aggregating resource object.
RODL clients might not normally perform this requests as the same URIs can be found in the manifest, but this response is useful for Linked Data clients following annotations about {{<}}{{[http://example.org/ROs/ro_id/.ro/aggregated/1]}}{{>}}.
Retrieving a proxy for an external resource behaves the same as for an internal resource:
{code}
C: GET http://example.org/ROs/ro_id/.ro/aggregated/2 HTTP/1.1
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 303 See Other
S: Location: http://example.com/external.txt
S: Link: <http://example.org/ROs/ro_id/>; rel="up"
{code}
h3. Deleting a proxy (unaggregating)
Deleting a proxy means the resource will no longer be aggregated:
{code}
C: DELETE http://example.org/ROs/ro_id/.ro/aggregated/2 HTTP/1.1
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 204 No content
{code}
Attempting to retrieve {{[http://example.org/ROs/ro_id/.ro/aggregated/2]}} SHOULD give a {{410 Gone}}, alternatively {{404 Not Found}}.
{code}
C: DELETE http://example.org/ROs/ro_id/.ro/aggregated/2 HTTP/1.1
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 410 Gone
{code}
The server SHOULD NOT reuse the URI of the deleted proxy for later aggregations added to the resource object, as there might still exists annotations about the previous aggregation within or outside the resource object. The simplest way to implement this is probably to use a random UUID to generate the proxy URIs rather than use sequential numbers as in these examples.
Retrieving the RO manifest again after the {{DELETE}} shows the RO no longer aggregates <[http://example.com/external.txt]>:
{code}
@base <http://example.org/ROs/ro_id/> # Climb out of .ro/
# @prefix ...
<> a ro:ResearchObject
ore:aggregates <helloworld.t2flow>;
ore:isDescribedBy <.ro/manifest> .
<.ro/aggregated/1> a ore:Proxy;
ore:proxyIn <> ;
ore:proxyFor <helloworld.t2flow> .
{code}
An attempt to delete an internal resource SHOULD give a 307 redirection to the actual resource rather than unaggregating the resource - because no longer aggregating the internal resource would leave it dangling. The server SHOULD NOT allow resources to be remain uploaded but not aggregated (unless it also implements some {{301 Moved Permanently}} mechanism for such "garbage collected" internal resources.)
It is NOT RECOMMENDED to automatically delete the internal resource on a proxy {{DELETE}}, as the client might not be aware of which resources are internal and not.
{code}
C: DELETE http://example.org/ROs/ro_id/.ro/aggregated/1 HTTP/1.1
C: Authorization: Bearer h480djs93hd8
S: HTTP/1.1 307 Temporary Redirect
S: Location: http://example.org/ROs/ro_id/helloworld.t2flow
S: Content-Type: text/plain
S:
S: Deleting this aggregation requires deletion of the aggregated internal resource.
{code}
{info}
307 means that the client should re-issue {{DELETE}} to {{[http://example.org/ROs/ro_id/helloworld.t2flow]}} so that it is aware of actually deleting the resource itself as well.
{info}
{{DELETE}} of an internal resource SHOULD also delete the corresponding aggregation.
h3. Adding a proxy (aggregating an external resource)
Following the link from *ore:similarTo* in the manifest:
{code}
C: POST http://example.org/ROs/ro_id/.ro/aggregated/ HTTP/1.1
C: Authorization: Bearer h480djs93hd8
C: Content-Type: text/uri-list
C:
C: http://example.com/new.txt
S: HTTP/1.1 201 Created
S: Location: http://example.org/ROs/ro_id/.ro/aggregated/3
{code}
The error conditions for invalid URIs are the same as listed under _Replacing a proxy_ below.
The server MAY respond with {{202 Accepted}} if the returned location is not immediately available, or the aggregation is not immediately added to the manifest of the resource object. In this case it SHOULD include a {{Location}} to where the proxy will become available. (This is an example of where the client might want to perform a {{HEAD}} request on the proxy.)
If multiple URIs are listed in the request, the server MAY refuse the request:
{code}
C: POST http://example.org/ROs/ro_id/.ro/aggregated/ HTTP/1.1
C: Authorization: Bearer h480djs93hd8
C: Content-Type: text/uri-list
C:
C: http://example.com/new.txt
C: http://example.com/other.txt
C: http://example.net/third.txt
S: HTTP/1.1 400 Bad Request
S: Content-Type: text/plain
S:
S: Only a single URI supported in URI list.
{code}
h4. Multiple aggregations (experimental)
the server MAY choose to aggregate each of the resources if the body contains multiple URIs. In this case the server MUST NOT respond with 201 Created, but MAY respond with 200 OK or {{202 Accepted}} and a response body linking to each of the (to be) newly created aggregations:
{code}
C: POST http://example.org/ROs/ro_id/.ro/aggregated/ HTTP/1.1
C: Authorization: Bearer h480djs93hd8
C: Content-Type: text/uri-list
C:
C: http://example.com/new.txt
C: http://example.com/other.txt
C: http://example.net/third.txt
S: HTTP/1.1 200 OK
S: Content-Type: text/uri-list
S:
S: http://example.org/ROs/ro_id/.ro/aggregated/4
S: http://example.org/ROs/ro_id/.ro/aggregated/5
S: http://example.org/ROs/ro_id/.ro/aggregated/7
{code}
This list is not required to be in corresponding order to the requested list, but must include all the created aggregations.
If any of the resources in such a batch request can't be aggregated, the server MAY return an error, in which case it SHOULD NOT create any of the aggregations in the request list.
h3. Replacing a proxy
The aggregation for an external resource MAY be updated to reflect an updated location, for instance because it has been 301 Moved Permanently.
{code}
C: PUT http://example.org/ROs/ro_id/.ro/aggregated/2 HTTP/1.1
C: Authorization: Bearer h480djs93hd8
C: Content-Type: text/uri-list
C:
C: http://example.com/renamed.txt
S: HTTP/1.1 204 No Content
{code}
Attempting to replace a proxy that is currently pointing to an internal resource would imply abandoning the internal resource, and so brings up the same situation as under {{DELETE}}:
{code}
C: PUT http://example.org/ROs/ro_id/.ro/aggregated/1 HTTP/1.1
C: Authorization: Bearer h480djs93hd8
C: Content-Type: text/uri-list
C:
C: http://example.com/other.txt
S: HTTP/1.1 405 Method Not Allowed
S: Allow: GET, HEAD
S: Link: <http://example.org/ROs/ro_id/helloworld.t2flow>; rel="related"
S: Content-Type: text/plain
S:
S: Replacing this aggregation requires deleting the aggregated internal resource.
{code}
The response SHOULD include a {{related}} link to the existing internal resource.
The server MUST refuse an attempt to replace a single aggregation with multiple URIs:
{code}
C: PUT http://example.org/ROs/ro_id/.ro/aggregated/2 HTTP/1.1
C: Authorization: Bearer h480djs93hd8
C: Content-Type: text/uri-list
C:
C: http://example.com/new.txt
C: http://example.com/other.txt
C: http://example.net/third.txt
S: HTTP/1.1 400 Bad Request
S: Content-Type: text/plain
S:
S: Only a single URI supported in URI list when replacing.
{code}
h3. Common errors when adding or replacing proxies
This sections lists errors which can happen both for adding and replacing proxies. Thus in the examples following one may interchange {{PUT .ro/aggregated/2}} with {{POST ./ro/aggregated/}} and get the same error message.
h4. Duplicate aggregations
Attempting to add or replace a proxy to point to an already aggregated resource (internal or external)
would imply a duplicate aggregation, which MUST NOT be allowed:
{code}
C: PUT http://example.org/ROs/ro_id/.ro/aggregated/2 HTTP/1.1
C: Authorization: Bearer h480djs93hd8
C: Content-Type: text/uri-list
C:
C: http://example.org/ROs/ro_id/helloworld.t2flow
S: HTTP/1.1 409 Conflict
S: Content-Type: text/plain
S: Link: <http://example.org/ROs/ro_id/.ro/aggregated/1>; rel="related"
S:
S: The resource is already aggregated.
{code}
The response SHOULD include a {{related}} link to the already existing resource.
h4. Aggregating non-existing resources (experimental)
The server MAY check if the resource behind the aggregated URI is available, and MAY refuse aggregating if the resource is {{404 Not Found}} or {{410 Gone}}:
{code}
C: POST http://example.org/ROs/ro_id/.ro/aggregated/ HTTP/1.1
C: Authorization: Bearer h480djs93hd8
C: Content-Type: text/uri-list
C:
C: http://example.com/404
S: HTTP/1.1 403 Forbidden
S: Content-Type: text/plain
S:
S: The aggregated resource was not found.
S:
S: HTTP Trace:
S:
S: HEAD http://example.com/404 HTTP/1.1
S: (..)
S: HTTP/1.1 404 Not Found
{code}
However, such checks SHOULD NOT fail a resource which responds with other error codes, such as {{401 Unauthorized}}, and should not take unnecessary time to complete. Thus this feature is mainly useful for servers who can easily check availability of internal resources aggregated across ROs.
h4. Aggregating non-existing internal resources
The server MAY respond with a {{403 Forbidden}} message if the URI points to a resource that is not aggregated in the same RO, but would have been if it was PUT to using this API (ie. it would have been an internal resource of the RO). The server MAY however choose to accept such aggregations, for instance to support mirroring of ROs, in which case a later PUT to the aggregated resource should preserve this proxy.
h4. Aggregating its own resource object
Attempting to aggregate the actual resource object itself or any of its proxy resources SHOULD fail with a {{403 Forbidden}} (although aggregation of other resource objects are permitted):
{code}
C: POST http://example.org/ROs/ro_id/.ro/aggregated/ HTTP/1.1
C: Authorization: Bearer h480djs93hd8
C: Content-Type: text/uri-list
C:
C: http://example.org/ROs/ro_id/.ro/aggregated/1
S: HTTP/1.1 403 Forbidden
S: Content-Type: text/plain
S:
S: Can't aggregate a proxy from the same research object.
{code}
h3. \---------\-
h3. GK: Concern about using proxies for de-aggregation
This is riddled with erorrs of detail, but I hope it exposes the basic concern
{code}
http://dom1.example.com/ro orte:aggregates http://dom1.example.com/resource
http://dom1.example.com/resource ro:hasProxy http://dom1.example.com/proxy1
http://dom1.example.com/resource ro:hasProxy http://dom2.example.com/proxy2
{code}
I understand we have:
{code}
http://dom1.example.com/proxy1 owl:sameAs http://dom2.example.com/proxy2
{code}
But then, if we require:
{code}
C: DELETE http://dom1.example.com/proxy1
{code}
to de-aggregate the resource [http://dom1.example.com/resource]
what about:
{code}
C: DELETE http://dom2.example.com/proxy2
{code}
which won't be seen by the service hosting the RO.
{info}
Stian: No, you can't have two proxies for the same resource in the same research object. You can have two ROs with a proxies each, both pointing to the same resource. However I've updated the text above so that deleting a proxy would not delete the resource. (If it is an internal resource, it would also refuse to delete the proxy)
{info}