DOIP API for HTTP Clients

This section describes the Digital Object Interface Protocol (DOIP) API that HTTP clients can use to interact with Cordra. DOIP API is an RPC-style API designed to be comfortable to develop against, with easy-to-construct URLs and JSON message bodies that convey the essence of DOIP requests and responses. Readers familiar with the DOIP v2 specification may refer to Design Approach to learn how the DOIP message model is mapped to this API.

The design of this API is a manifestation of the Digital Object Architecture in which all API requests are seen uniformly as

  • a specified client
  • performing a specified operation
  • on a specified digital object

where a digital object conforms to a specified data model presenting an identifier, a type, and a collection of attributes and elements. The service (Cordra, in this case) managing the digital objects is also considered a digital object.

In all cases, identification of the digital object, operation, service, or client is as a Handle:

  • the client identifier is the identifier of its User digital object in the system
  • the Cordra service identifier is a fixed identifier (<prefix>/service for purposes of this document)
  • the identifiers of the available operations are documented below
  • the identifier of the target digital object is the identifier of the relevant object in the system.

Request format

A request can be made with either GET or POST. Only some operations (generally read-only operations) allow GET. Requests target a URL, which is <base URL>/doip. The base URL is constant and will be the domain name (and optionally port) where Cordra is made available. The examples in this document will use /doip as a shorthand for the target URL.

The query string (after ?) of the URL accepts the following parameters:

  • operationId: required, the identifier of the operation to perform
  • targetId: required, the identifier of the target object
  • clientId: the identifier of the caller; this can be implicit in the authentication header sent, in which case it need not be included as a query parameter
  • attributes: arbitrary JSON to inform the operation

For convenience, attributes as a JSON object can be expressed as multiple properties with dot separated names. Each such property is interpreted as a string and combined into the structure of a JSON object. For example,

GET /doip?...&attributes={"query":"foo","pageSize":"10"}

is exactly the same as

GET /doip?...&attributes.query=foo&attributes.pageSize=10

Finally the “input” to the operation is the POST body, when present. In general it does not matter which Content-Type header is sent, except to avoid certain tools defaulting to the special value application/x-www-form-urlencoded. See next section for more details about this special value.

POST for long query strings

In certain circumstances (for example, very complicated search queries), it is possible for the query string of the URL to become long enough to potentially be an issue for HTTP servers or middleware. In such a circumstance the same request can be sent using POST with Content-Type: application/x-www-form-urlencoded, as is common in HTTP APIs. Such a POST behaves as if the POST body were appended to the query string of the URL.

For example

GET /doip?...&attributes.query=something%20very%20long

could be equivalently sent as

POST /doip?...
Content-Type: application/x-www-form-urlencoded

attributes.query=something%20very%20long

For this reason it is important to set an appropriate Content-Type header for a POST body intended to be the input to an operation; often this will be Content-Type: application/json.

Authentication

Authentication can be sent using the standard HTTP Authorization: header, which is translated into the “authentication” property of the DOIP request.

For example, authentication via username and password uses the standard HTTP Basic auth:

Authorization: Basic dXNlckBleGFtcGxlLm9yZzpwYXNzd29yZA==

and authentication can use an access token (bearer token) acquired using the Access Token operations:

Authorization: Bearer 1frevxlceojr3ylc92q2awh3e

See below for detail on acquiring access tokens.

Response format

The response will include a header Doip-Response, the value of which is a JSON object, which has a “status” property indicating the DOIP status of the response, and sometimes an “attributes” property with additional data about the response.

The HTTP status code of the response will be set as appropriate for the DOIP status code, and will be one of

200 The request was processed successfully
400 There was something wrong with the structure or content of the request
401 The client must authenticate to perform the attempted operation
403 The client was not permitted to perform the attempted operation
404 The requested digital object could not be found
500 There was an internal server error

The DOIP status code (and corresponding HTTP status code) indicates the nature of the response; responses with DOIP status code other than “0.DOIP/Status.001” (HTTP status code other than 200) are error responses. Error responses will return a response body of Content-Type: application/json which is an object with (at minimum) the property “message”, a description of the error.

The output of a successful response is contained in the response body. It will often have Content-Type: application/json. Details about the structure of the response body is given is the description of each of the operations.

Summary of Operations

A summary of various operations along with their identifiers and descriptions is listed below. Operation identifiers that begin with 0.DOIP are defined in DOIP v2 specification. Operation identifiers that begin with 20.DOIP represent Cordra-specific operations.

Operation Target Description
20.DOIP/Op.Auth.Token Service Create a new access token
20.DOIP/Op.Auth.Introspect Service Get access token information
20.DOIP/Op.Auth.Revoke Service Invalidate access token
0.DOIP/Op.Create Service Create a digital object
0.DOIP/Op.Retrieve Object Retrieve a digital object
0.DOIP/Op.Update Object Update a digital object
0.DOIP/Op.Delete Object Delete a digital object
0.DOIP/Op.Search Service Search for digital objects

Aliases

For the convenience of users, short aliases are enabled for referring to the service identifier and to operation identifiers.

In the case of the service identifier, targetId=service is considered to be a shortcut for targetId=<prefix>/service.

The following table shows operation aliases usable with Cordra.

Operation Alias Operation Handle
Auth.Token 20.DOIP/Op.Auth.Token
Auth.Introspect 20.DOIP/Op.Auth.Introspect
Auth.Revoke 20.DOIP/Op.Auth.Revoke
Create 0.DOIP/Op.Create
Retrieve 0.DOIP/Op.Retrieve
Update 0.DOIP/Op.Update
Delete 0.DOIP/Op.Delete

Digital Object JSON Structure

Many of the operations shown below work with the structure of a Digital Object. In general, a Digital Object has a unique resolvable identifier under “id”, a “type”, further JSON called “attributes”, and metadata about “elements” which are (in general) binary payloads associated with the object such as images.

As used with Cordra, “attributes” will always have two top-level properties “content” (the actual type-specific JSON content of the digital object) and “metadata” which is Cordra-maintained metadata such as creation and modification timestamps. Note that depending on the authenticated user, some of the information may be omitted.

For many applications, the most useful part of the structure will be the JSON content of the digital object under “content”.

Operation Details

Access Token Operations

The access token operations can be used to obtain an access token, check its status, and delete it.

A valid access token can be provided, instead of authentication credentials, for various operations. The system provides an access token only after a successful authentication, and by default the token is valid for 30 minutes from last use. Each valid use renews the lifetime.

Security and performance improvements are usually noted with the use of tokens instead of authentication credentials.

Access token may be sent to operations (other than create access token operation) using an Authorization Bearer header. For example:

Authorization: Bearer ACCESS_TOKEN

Create a new access token: 20.DOIP/Op.Auth.Token

Only POST. Target is the service <prefix>/service.

The operation input is a JSON object specifying “grant_type”: “password”, to indicate that the user is authenticating via username/password, together with the user object identifier (or username) and the password. Note that the HTTPS transport ensures that this is sent encrypted over then network.

The operation output is a JSON object specifying the “access_token” together with certain other informative fields notably “userId”.

The properties of the response object:

  • access_token: The newly created access token.
  • token_type: Always “Bearer”.
  • active: Whether or not the token is active; always “true” for successful calls of the /auth/token API.
  • username: Username of the authenticated user
  • userId: UserId of the authenticated user

Example request:

POST /doip?operation=20.DOIP/Op.Auth.Token&targetId=<prefix>/service targetId=<prefix>/service&attributes.full=true
Content-Type: application/json;charset=utf-8

{
    "grant_type": "password",
    "username": "test@example.org",
    "password": "password",
}

Response:

HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8

{
    "access_token": "14f874o9i4ohgjv19bds6wvov",
    "token_type": "Bearer",
    "active": "true",
    "username": "test@example.org",
    "userId": "<prefix>/NVSJGF5G"
}

Check the status of an access token: 20.DOIP/Op.Auth.Introspect

Only POST. Target is the service <prefix>/service.

The operation input is a JSON object specifying “token” the access token to introspect.

The operation output is a JSON object specifying whether the supplied token is “active” and also certain other informative fields notably “userId”.

The properties of the response object:

  • active: Whether or not the token is active.
  • username: Username of the authenticated user
  • userId: UserId of the authenticated user

Example request:

POST /doip?operation=20.DOIP/Op.Auth.Introspect&targetId=<prefix>/service&attributes.full=true
Content-Type: application/json;charset=utf-8

{
    "token": "14f874o9i4ohgjv19bds6wvov"
}

Response:

HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8

{
    "active": "true",
    "username": "test@example.org",
    "userId": "<prefix>/NVSJGF5G"
}

Delete specified access token: 20.DOIP/Op.Auth.Revoke

Only POST. Target is the service <prefix>/service.

Token revocation would typically occur when a user logs out of the system.

Example request:

POST /doip?operation=20.DOIP/Op.Auth.Revoke&targetId=<prefix>/service
Content-Type: application/json;charset=utf-8

{
    "token": "14f874o9i4ohgjv19bds6wvov"
}

Response:

HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8

{
    "active": "false"
}

CRUD Operations

Object creation: 0.DOIP/Op.Create

Only POST. Target is the service object <prefix>/service.

Creation of objects sends operation 0.DOIP/Op.Create to the service object <prefix>/service. Regardless of the type of the digital object in the system, the same operation is used. The input should be a Digital Object specifying the “type” to be created and the “content”. The output will be the Digital Object with its new “id” and “metadata” and possibly changes to the “content” automatically populated by the system.

Example request:

POST /doip?operation=0.DOIP/Op.Create&targetId=<prefix>/service
Content-Type: application/json;charset=utf-8

{
    "type": "Document",
    "attributes": {
        "content": {
            "name": "My Document",
            ...
        }
    }
}

Response:

HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8

{
    "id": "<prefix>/MZ6W9D3T",
    "type": "Document",
    "attributes": {
        "content": {
            "id": "<prefix>/MZ6W9D3T",
            "name": "My Document",
            ...
        },
        "metadata": {
            "createdOn": 1607709592349,
            "createdBy": "admin",
            "modifiedOn": 1607709906109,
            "modifiedBy": "admin"
        }
    }
}

Object retrieval: 0.DOIP/Op.Retrieve

Allows GET or POST. Target is the specific digital object to be retrieved.

Retrieval of objects from the system is done using the operation 0.DOIP/Op.Retrieve. Regardless of the type of the digital object in the system, the same operation is used. Retrieval returns a JSON response with a Digital Object structure, which includes the identifier, the type, system-maintained metadata, plus of course the type-specific content of the object.

An element (instead of object metadata) can be retrieved using the request attribute attributes.element=elementName.

Example request:

GET /doip?operation=0.DOIP/Op.Retrieve&targetId=<prefix>/MZ6W9D3T

Response:

HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8

{
    "id": "<prefix>/MZ6W9D3T",
    "type": "Document",
    "attributes": {
        "content": {
            "id": "<prefix>/MZ6W9D3T",
            "name": "My Document",
            ...
        },
        "metadata": {
            "createdOn": 1607709592349,
            "createdBy": "admin",
            "modifiedOn": 1607709906109,
            "modifiedBy": "admin"
        }
    }
}

Object update: 0.DOIP/Op.Update

Only POST. Target is the digital object to be updated.

To update an object send operation 0.DOIP/Op.Update with the target the object to be updated. Regardless of the type of the digital object in the system, the same operation is used. The input should be a Digital Object specifying the new “content” (keeping the “id”, “type”, and other properties not in the content is fine but optional) as a complete replacement. The output will be the Digital Object with its new “metadata” and possibly changes to the “content” automatically populated by the system.

Example request:

POST /doip?operation=0.DOIP/Op.Update&targetId=<prefix>/MZ6W9D3T
Content-Type: application/json;charset=utf-8

{
    "attributes": {
        "content": {
            "name": "My Document",
            "description": "Updated description",
            ...
        }
    }
}

Response:

HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8

{
    "id": "<prefix>/MZ6W9D3T",
    "type": "Document",
    "attributes": {
        "content": {
            "id": "<prefix>/MZ6W9D3T",
            "name": "My Document",
            "description": "Updated description",
            ...
        },
        "metadata": {
            "createdOn": 1607709592349,
            "createdBy": "admin",
            "modifiedOn": 1607899161123,
            "modifiedBy": "admin"
        }
    }
}

Object deletion: 0.DOIP/Op.Delete

Only POST. Target is the digital object to be deleted.

To delete an object send operation 0.DOIP/Op.Delete with the target the object to be deleted. Regardless of the type of the digital object in the system, the same operation is used. The input and output (in the case of success) are empty.

Example request:

POST /doip?operation=0.DOIP/Op.Delete&targetId=<prefix>/MZ6W9D3T

Response:

HTTP/1.1 200 OK

Design Approach

This section describes the design approach followed to map messages that conform to DOIP v2 specification to requests and responses as used by this DOIP API. Readers of this appendix are expected to be familiar with the DOIP v2 specification.

Request Mapping

A request to this DOIP API can be made with either GET or POST, which gets mapped to request form defined in the DOIP v2 specification. Only some DOIP operations (generally read-only operations) allow GET; this is part of the specification of the operation.

The examples in this document will use /doip, but the full URL should be constructed using the endpoint of the Cordra service.

The components of a DOIP request (section 7.2.1 of the DOIP v2 specification) are obtained from the HTTP request as follows.

  • requestId: from the query parameter requestId. Can be omitted as the HTTP transport provides its own mechanisms for associating responses to requests.
  • operationId: from the query parameter operationId, required
  • targetId: from the query parameter targetId, required
  • clientId: from the query parameter clientId; can be omitted if the authentication provides it implicitly
  • authentication: from the standard HTTP Authorization: header; see details below
  • attributes: from the query parameter attributes and for convenience various query parameters; see details below
  • input: from the HTTP POST body; see details below

Authentication

Authentication can be sent using the standard HTTP Authorization: header, which is translated into the “authentication” property of the DOIP request.

Authorization: Basic is translated into an “authentication” object with “username” and “password” properties.

Authorization: Bearer is translated into an “authentication” object with a “token” property.

For Authorization: Doip, the rest of the Authorization: header is Base64-decoded and parsed as JSON to provide an arbitrary “authentication” property for the request.

Attributes

For convenience, attributes as a JSON object for the DOIP request can be expressed as multiple properties in the API request with dot separated names. Each such property is interpreted as a string and combined into the structure of a JSON object. For example,

GET /doip?...&attributes={"query":"foo","pageSize":"10"}

is exactly the same as

GET /doip?...&attributes.query=foo&attributes.pageSize=10
Attributes from headers

For convenience, two standard HTTP headers map to specific DOIP request attributes. The Content-Type: header from the API request is copied into an property “mediaType” of the attributes object. The filename from a Content-Disposition header is copied into a property “filename” of the attributes object.

POST for long query strings

See POST for long query strings above.

Input

For a GET or a POST with empty body, the input property of the corresponding DOIP request is assumed to be empty.

If the Content-Type: of the HTTP request is application/x-www-form-urlencoded, the input will be assumed empty and the POST body is used to populate the query string parameters.

If the Content-Type: of the HTTP request is application/json or ends in +json, the entire body represents a DOIP input which is a single JSON segment.

If the Content-Type: of the HTTP request begins with multipart/, the body represents a multi-segment DOIP input, where each segment is JSON or bytes depending on whether the Content-Type of the corresponding part is application/json or ends in +json.

Otherwise, the entire body represents a DOIP input which is a single bytes segment.

Response Mapping

The components of a DOIP response (section 7.2.2 of the DOIPv2 specification) are mapped into an API response as follows.

The DOIP response “requestId”, “status”, and “attributes” are mapped into the HTTP response header Doip-Response, the value of which is a JSON object serialized using only ASCII for maximum HTTP compatibility.

The HTTP status code of the response will be set according to the DOIP status code, and (for basic DOIP status codes) will be one of

200 0.DOIP/Status.001 The request was processed successfully
400 0.DOIP/Status.101, 0.DOIP/Status.200 There was something wrong with the structure or content of the request
401 0.DOIP/Status.102 The client must authenticate to perform the attempted operation
403 0.DOIP/Status.103 The client was not permitted to perform the attempted operation
404 0.DOIP/Status.104 The requested digital object could not be found
409 0.DOIP/Status.105 There was a conflict preventing the request from being executed
500 0.DOIP/Status.500 There was an internal server error

Custom DOIP statuses need to specify the expected corresponding HTTP status code; otherwise 200 is used as a default.

Headers from attributes

For convenience, two standard HTTP headers are populated in the API response according to specific DOIP response attributes. A property “mediaType” of the response attributes is copied into the Content-Type: header. A property “filename” of the response attributes is copied into the Content-Disposition: header.

Output

The DOIP response “output” is contained in the API response body. A JSON segment is written with Content-Type: application/json. Multiple segments are written with a multipart Content-Type. Other Content-Type values indicate a single bytes segment.