Skip to content
Soumya Brahma edited this page Feb 4, 2016 · 88 revisions

⚠️The API name has been changed

The API name has changed from Research Object Storage and Retrieval API (ROSR API) to Research Object API (RO API).

Table of Contents

API function overview

The Research Object API allows:

  • Storing and retrieving research objects.
  • Storing and retrieving resources aggregated within the research objects.
  • Annotating the aggregated resources, including the research object itself.
RO API allows creating research objects and retrieving them in a number of formats – as RDF metadata, a ZIP archive or an HTML page. Resources can be aggregated and de-aggregated by means of proxies, which simplifies handling resources that are stored outside of the service’s domain. Resources can be annotated using RDF graphs.

The RO API is based heavily on the Research Object model, version 0.1 at the moment of writing. It therefore uses the Open Annotation and the Object Reuse and Exchange models. The mechanism of using proxies and annotations as gateways for aggregated resources and annotation bodies has been based on the AtomPub specification.

⚠️ Missing

The following functions are missing

* Specify RDF classes of resources that are added via API (i.e. query parameters)

API usage

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.

Get a list of Research Objects

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: ...
The response format may be an RDF file or a list of URIs.

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.

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_id/
S: Content-Type: text/turtle
S: Content-Length: ...
S:
S: (the initial manifest)

Create a new or upload an existing RO as a zip file.

It's possible to create a completely new RO from a set of different files and folders collected in a one zip file. RO service aggregates all sent resources including folders and maintains their structure. The operation is performed in the background. User receives link to the operation status object as a server response. Status object contains information about the process status, the final uri of processed RO, possible error messages if an error occurred. Status object knows how many resources were submitted and how many of them were already processed by server.

C: POST /zip/create HTTP/1.1
C: Host: example.com
C: Content-Type: application/zip
C: Content-Length: ...
C: Accept: text/turtle
C: Slug: ro_id
C: Authorization: Bearer h480djs93hd8
C:
C: (the zip file with some files or/and directories)

S: HTTP/1.1 201 Created
S: Location: http://example.com/zip/create/12345
S:
S: {
S:   "target": "http://example.com/ROs/ro_id/",
S:   "status": "running",
S:   "submitted_resources": "15",
S:   "processed_resources": "3"
S: }
Get a copy of job status (running).
C: GET [http://example.com/zip/create/12345] HTTP/1.1
C: Accept: application/json

S: HTTP/1.1 200 OK
S: Content-Type: application/json
S:
S: {
S:   "target": "http://example.com/ROs/ro_id/",
S:   "status": "running",
S:   "submitted_resources": "15",
S:   "processed_resources": "8"
S: }
Get a job status (finished)
C: GET [http://example.com/zip/create/12345] HTTP/1.1
C: Accept: application/json

S: HTTP/1.1 200 OK
S: Content-Type: application/json
S:
S: {
S:   "target": "http://example.com/ROs/ro_id/",
S:   "status": "done",
S:   "submitted_resources": "15",
S:   "processed_resources": "15"
S: }
It's possible to upload to a server an existing RO. Once server receives a zip with an existing RO, server unpacks and processes the given artifact in a background process in a few simple steps:

1. Look for the .ro/manifest.rdf. If not found, 400 Bad Request is returned. 1. Create the RO. 1. Scan for each resource aggregated in the RO and add it to the RO. 1. If there is anything else in the zip which was not aggregated, log it but don't return error.

User receives link to the operation status object as a server response. Status object contains information about process status, the final uri of processed RO and possible error messages if an error occurred. Status object knows how many resources were submitted and how many of them were already processed by server.

C: POST /zip/upload HTTP/1.1
C: Host: example.com
C: Content-Type: application/zip
C: Content-Length: ...
C: Accept: text/turtle
C: Slug: ro_id
C: Authorization: Bearer h480djs93hd8
C:
C: (the zip with manifest and resources)

S: HTTP/1.1 201 Created
S: Location: [http://example.com/zip/upload/12345]
S:
S: {
S:   "target": "http://example.com/ROs/ro_id/",
S:   "status": "running",
S:   "submitted_resources": "15",
S:   "processed_resources": "3"
S: }
Get a job status (running).
C: GET http://example.com/zip/upload/12345 HTTP/1.1
C: Accept: application/json

S: HTTP/1.1 200 OK
S: Content-Type: application/json
S:
S: {
S:   "target": "http://example.com/ROs/ro_id/",
S:   "status": "running",
S:   "submitted_resources": "15",
S:   "processed_resources": "10"
S: }
Get a copy job status (finished).
C: GET http://example.com/zip/upload/12345 HTTP/1.1
C: Accept: application/json

S: HTTP/1.1 200 OK
S: Content-Type: application/json
S:
S: {
S:   "target": "http://example.com/ROs/ro_id/",
S:   "status": "done",
S:   "submitted_resources": "15",
S:   "processed_resources": "15"
S: }

Get a Research Object

What do you get when you ask for an RO? This implementation is related to the page ??RO dereferencing:

  • If "text/html" is requested, the response is a 303 redirection to an HTML page representing the RO.
  • 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 (example)
ROs/ro1/ application/zip 303 See Other zippedROs/ro1/
ROs/ro1/ text/html 303 See Other sandbox.../portal?ro=...ROs/ro1/
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 ?RO dereferencing.

This example assumes retrieving the RO metadata, i.e. the manifest.

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> ;
(...)
This example assumes retrieving a zip file.
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)

Delete a Research Object

C: DELETE /ROs/ro_id/ HTTP/1.1
C: Host: example.com
C: Authorization: Bearer h480djs93hd8

S: HTTP/1.1 204 No Content

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 RO API. The structure of a proxy is described by ORE.

The API design here uses similar patterns to AtomPub, with an RO being analogous to an Atom feed, a proxy for an Atom item, and resource content for an Atom media resource. The Slug: header field is defined by AtomPub (http://tools.ietf.org/html/rfc5023).

Aggregating resources involves 2 steps, though in most cases one of them can be omitted:

  1. Creating a proxy for a resource.
  2. Uploading a resource.
The first step is recognised 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 a new internal resource is being aggregated (in which it is a relative URI reference), or in the request body otherwise. If the resource URI is not indicated in the Slug: header nor in the request body, the server should generate any URI it sees fit, i.e. using a random UUID. The server will return 409 Conflict if the resulting URI has already been aggregated in this research object. Client applications should not, however, make assumptions about the URI of the resulting resource but should, instead, use the ore:proxyFor link value returned by the service.

Resources are aggregated in the following ways:

  1. External resources: only create a proxy, pointing to the external resource.
  2. 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.
  3. 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. The URI of the external resource is given in the supplied proxy description. The content type application/vnd.wf4ever.proxy signals to the RO service that a new proxy is being created.

A application/vnd.wf4ever.proxy entity is an RDF/XML expression that describes just one ore:Proxy resource. Other information present in the RDF is ignored. Behaviour is undefined if there is not exactly one ore:Proxy resource described, and a service MAY reject such a request as ill-formed. (Future proposals may accept any format of RDF, and use a separate mechanism to signal that it is intended to be interpreted as a proxy description.)

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: <rdf:RDF
C:   xmlns:ore="http://www.openarchives.org/ore/terms/"
C:   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" >
C:   <ore:Proxy>
C:     <ore:proxyFor rdf:resource="http://external.example.com/resource.uri" />
C:   </ore:Proxy>
C: </rdf:RDF>

S: HTTP/1.1 201 Created
S: Location: https://sandbox/rodl/ROs/ro1/.ro/proxies/9a3fdd90-becf-11e1-afa7-0800200c9a34
S: Link: <http://external.example.com/resource.uri>; rel="http://www.openarchives.org/ore/terms/proxyFor"
S: Content-Type: application/rdf+xml
S: Content-Length: ...
S:
S: <rdf:RDF
S:   xmlns:ore="http://www.openarchives.org/ore/terms/"
S:   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" >
S:  <rdf:Description rdf:about="https://sandbox/rodl/ROs/ro1/.ro/proxies/9a3fdd90-becf-11e1-afa7-0800200c9a34">
S:    <rdf:type rdf:resource="http://www.openarchives.org/ore/terms/Proxy"/>
S:    <ore:proxyIn rdf:resource="https://sandbox/rodl/ROs/ro1/"/>
S:    <ore:proxyFor rdf:resource="http://external.example.com/resource.uri" />
S:  </rdf:Description>
S: </rdf:RDF>
The example below demonstrates adding a proxy for an internal resource, and then putting the new resource content at the server-indicated URI. The RO-internal name for the new resource is indicated using a Slug: header (following AtomPub). The supplied entity body is a Proxy description from which the proxyFor property is omitted.
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
C:
C: <rdf:RDF
C:   xmlns:ore="http://www.openarchives.org/ore/terms/"
C:   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" >
C:   <ore:Proxy>
C:   </ore:Proxy>
C: </rdf:RDF>

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"
S: Content-Type: application/rdf+xml
S: Content-Length: ...
S:
S: <rdf:RDF
S:   xmlns:ore="http://www.openarchives.org/ore/terms/"
S:   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" >
S:  <rdf:Description rdf:about="http://example.org/ROs/ro_id/.ro/proxies/d953d020-becc-11e1-afa7-0800200c9a66">
S:    <rdf:type rdf:resource="http://www.openarchives.org/ore/terms/Proxy"/>
S:    <ore:proxyIn rdf:resource="http://example.org/ROs/ro_id/"/>
S:    <ore:proxyFor rdf:resource="http://example.org/ROs/ro_id/foo/bar.txt" />
S:  </rdf:Description>
S: </rdf:RDF>

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: Content-Type: application/rdf+xml
S: Content-Length: ...
S:
S: <rdf:RDF
S:   xmlns:ore="http://www.openarchives.org/ore/terms/"
S:   xmlns:dct="http://purl.org/dc/terms/"
S:   xmlns:ro="http://purl.org/wf4ever/ro#"
S:   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" >
S:  <rdf:Description rdf:about="http://example.com/ROs/ro_id/foo/bar.txt">
S:    <dct:creator rdf:resource="http://test2.myopenid.com"/>
S:    <dct:created rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2011-12-02T15:02:11Z</dct:created>
S:    <rdf:type rdf:resource="http://www.openarchives.org/ore/terms/AggregatedResource"/>
S:    <rdf:type rdf:resource="http://purl.org/wf4ever/ro#Resource"/>
S:  </rdf:Description>
S: </rdf:RDF>
The example below demonstrates the short-cut mechanism aggregating an internal resource by simply posting its content to the RO. The Slug: header is used to indicate the desired path within the RO. The server responds with the proxy description.
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"
S: Content-Type: application/rdf+xml
S: Content-Length: ...
S:
S: <rdf:RDF
S:   xmlns:ore="http://www.openarchives.org/ore/terms/"
S:   xmlns:dct="http://purl.org/dc/terms/"
S:   xmlns:ro="http://purl.org/wf4ever/ro#"
S:   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" >
S:  <rdf:Description rdf:about="http://example.org/ROs/ro_id/.ro/proxies/d953d020-becc-11e1-afa7-0800200c9a66">
S:    <rdf:type rdf:resource="http://www.openarchives.org/ore/terms/Proxy"/>
S:    <ore:proxyIn rdf:resource="http://example.org/ROs/ro_id/"/>
S:    <ore:proxyFor rdf:resource="http://example.org/ROs/ro_id/foo/bar.txt" />
S:  </rdf:Description>
S:  <rdf:Description rdf:about="http://example.com/ROs/ro_id/foo/bar.txt">
S:    <dct:creator rdf:resource="http://test2.myopenid.com"/>
S:    <dct:created rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2011-12-02T15:02:11Z</dct:created>
S:    <rdf:type rdf:resource="http://www.openarchives.org/ore/terms/AggregatedResource"/>
S:    <rdf:type rdf:resource="http://purl.org/wf4ever/ro#Resource"/>
S:  </rdf:Description>
S: </rdf:RDF>

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.

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
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

Get an aggregated resource

The default response when dereferencing the resource URI is the resource content. Resource metadata can be found in the manifest.

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
When dereferencing the aggregated resource proxy, the client is redirected to the resource that the proxy is for.
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"
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 (example) 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 OK graph.ttl?original=graph.rdf based on Accept HTTP header
graph no application/rdf+xml 302 OK graph.rdf?original=graph based on Accept HTTP header
graph no - 302 OK graph.rdf?original=graph default: RDF/XML

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 resources that are under control of the RO service (such as aggregation resource maps) will result in 403 Forbidden.

The example below demonstrated de-aggregating an external resource.

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
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.
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

Clone this wiki locally