A very basic REST interface for islandora. It allows for the manipulation of objects, data-streams, and relationships. Only supports JSON.
Clone or download
Pull request Compare This branch is 93 commits ahead of nigelgbanks:7.x.
willtp87 Merge pull request #30 from jordandukart/7.x
Could be a non-set authorization header.
Latest commit c7edb9a Dec 14, 2018

README.md

BUILD STATUS

Current build status: Build Status

CONTENTS OF THIS FILE

SUMMARY

This module provides a number of REST end points for fetching/manipulating objects, datastreams, and object relationships from islandora.

The basic structure of the module is such that we define Drupal Menu’s for each resource type. Currently the resources are objects, datastreams, relationships, and solr. Each Menu defines a callback that generates the HTTP responses for any HTTP Methods requested of the given resource GET, PUT, POST, DELETE.

This callback will load a file dedicated to the particular resource located in includes folder and call a function associated with the HTTP method.

So for example if I make a GET request for an object, it will auto load the includes/object.inc file and call a function mapped to the GET method:

function islandora_rest_object_get_response(array $parameters) { ... }

If it were a PUT request for an object it would be:

function islandora_rest_object_put_response(array $parameters) { ... }

These functions are responsible for performing any required actions, say purge an object, etc. As well as generating a response for the client which will typically be a JSON response.

REQUIREMENTS

INSTALLATION

Assumes the following global XACML polices have been removed from:

$FEDORA_HOME/data/fedora-xacml-policies/repository-policies/default

  • deny-inactive-or-deleted-objects-or-datastreams-if-not-administrator.xml
  • deny-policy-management-if-not-administrator.xml
  • deny-purge-datastream-if-active-or-inactive.xml
  • deny-purge-object-if-active-or-inactive.xml

This module will still function with those policies in place but the tests this module defines will fail. Also when installing Islandora don't forget to deploy the policies it includes!

Islandora SOLR Search is an optional dependancy that will allow you to perform SOLR searches via this REST API. Follow the directions it provides if you wish to enable SOLR searches.

CONFIGURATION

For each of the REST end-points defined below in the documentation section there exists a corresponding Drupal Permission (with the exception of SOLR which uses the permissions defined by the Islandora SOLR Search module).

After enabling this module navigate to www.yoursite.com/admin/people/permissions and enable the features you want to expose. Note that XACML is still enforced on all REST end-points and access can still be denied regardless of what Drupal Permissions are enabled.

DOCUMENTATION

In an effort to simplify the datastream REST end-point multi-part responses for GET requests were investigated. With the intention of returning both the content and properties through a single request. While this is possible it is not well supported by jQuery, there exists a plug-in, I am unsure if this plugin works with all browsers. To get around this issue GET requests for a datastream can return either the content or properties but not both for the moment.

Also since PUT / DELETE support is lacking in IE6-9, we've provided the ability to mock PUT / DELETE requests as POST requests by adding an additional form-data field method to inform the server which method was actually intended.

At the moment multi-part PUT requests such as the one required to modify an existing datastream's content and properties are not implemented you can mock these PUT requests using aforementioned mechanism. POST and include an additional form-data field method with the value PUT.

Adding support for multi-part PUT requests is possible but would require writing or using and HTTP Request parsing library as PUT is not well supported in PHP.

Since we don't have an HTTP Request parsing library PUT requests are expecting raw application/json content as the request body.

Authorization

In its most common use case - JavaScript executing AJAX callbacks inside module code - no authorization is needed. For external requests, however, an additional step is required.

Drupal doesn't support basic authorization out of the box; any script or other such code that needs authentication to hit the various REST endpoints should first make a POST request to the site login page and then maintain cookies for future requests.

For example, using cURL on a UNIX-like system, and replacing USERNAME, PASSWORD, and SITE_URL with those respective items:

curl --cookie-jar ./cookies.txt -X POST -H "Content-Type: multipart/form-data" -F "name=USERNAME" -F "pass=PASSWORD" -F "form_id=user_login" -F "op=Log in" "http://SITE_URL/user/login"

In this case:

  • name and pass represent the login credentials
  • form_id represents the machine name of the form being POSTed to
  • op represents the element on the page being used to execute the POST

The stored session data in cookies.txt could then be used for future cURL requests to the API.

Of course, a more comprehensive solution is almost certainly desirable so that passwords are not stored in history - Bash or otherwise - but the same principles apply.

Alternatively the islandora_rest_api_auth submodule exists which can allow for token based user authentication instead.

Documentation Key

{variable} Required Parameter.

[variable] Optional Parameter no default defined, NULL or empty string likely be used.

[variable, ’default’] Optional Parameter and it’s default.

Common Responses

Unless otherwise specified each end-point can return the following responses.

Response: 400 Bad Request

Content-Type: application/json
Name Description
message A message saying the request was invalid.

400 errors will be triggered by invalid information being provided to a query. In these cases, the request should not be repeated, but rather fixed before another request is made.

Response: 401 Unauthorized

No response body.

In general 401 means an anonymous user attempted some action and was denied. Either the action is not granted to anonymous uses in the Drupal permissions, or XACML denied the action for the anonymous user.

Response: 403 Forbidden

No response body.

In general 403 means an authenticated user attempted some action and was denied. Either the authenticated user does not have a Drupal role that grants him/her permission to perform that action, or XACML denied the action for that user.

Response: 404 Not Found

No response body.

In general a 404 will occur, when a user tries to perform an action on a object or datastream that doesn't not exist. Or XACML is hiding that object or datastream from the user.

404 Responses can be returned even if the user was not determined to have permission to perform the requested action, as the resource must be first fetched from fedora before the users permission can be determined.

Response: 500 Internal Server Error

Content-Type: application/json
Name Description
message A detail description of the error.

Any problem can trigger a 500 error, but in general you'll find that this is typically an error returned by Fedora.

Describe An Existing Object

URL syntax

islandora/rest/v1/object/{pid}

HTTP Method

GET

Headers

Accept: application/json

Get Parameters

Name Description
pid Persistent identifier of the object

Response: 200 OK

Content-Type: application/json
Name Description
pid Persistent identifier of the requested object
label The object’s label
models An array of the objects models.
state Object’s state, either “A”, “I”, “D”
owner The objects owner
created Created date of the object, yyyy-MM-ddTHH:mm:ssZ
modified Last modified date of the object, yyyy-MM-ddTHH:mm:ssZ
datastreams Any array of objects each describing a data stream. See GET datastream for more details.

Example Response

{
 "pid": "islandora:root",
 "label": "Root Object",
 "owner": "fedoraAdmin",
 "models": ["islandora:collectionCModel"],
 "state": "A",
 "created": "2013-05-27T09:53:39.286Z",
 "modified": "2013-06-24T04:20:26.190Z",
 "datastreams": [{
   "dsid": "RELS-EXT",
   "label": "Fedora Object to Object Relationship Metadata.",
   "state": "A",
   "size": 1173,
   "mimeType": "application\/rdf+xml",
   "controlGroup": "X",
   "created": "2013-06-23T07:28:32.787Z",
   "versionable": true,
   "versions": []
 }]
}

Create A New Object

URL syntax

islandora/rest/v1/object

HTTP Method

POST

Headers

Accept: application/json

POST (form-data)

Name Description
pid Persistent identifier of the object, if not given the namespace will be used to create a new one (optional).
label The label of the new object (optional).
owner The owner of the new object, if not given it will be the currently logged in (optional).
namespace Used to create a PID for a new empty object if pid is not given (optional).

Response: 201 Created

Content-Type: application/json

Returns the same response as a GET Object request.

Modify An Existing Object’S Properties

URL syntax

islandora/rest/v1/object/{pid}

HTTP Method

PUT

Headers

Accept: application/json

Content-Type: application/json

Get Variables

Name Description
pid Persistent identifier of the object, if not given the namespace will be used to create a new one.

Request Body (raw)

Content-Type: application/json
Name Description
label The new label of the object (optional)
owner The new owner of the object (optional)
state The new state of the object, either “A”, “I”, “D” (optional)

Only given variables will change the object the others will retain their original values.

Response: 200 OK

Content-Type: application/json
Name Description
pid Persistent identifier of the requested object
label The object’s label
state Object’s state, either “A”, “I”, “D”
owner The objects owner
modified Last modified date of the object, yyyy-MM-ddTHH:mm:ssZ

Delete An Existing Object

URL syntax

islandora/rest/v1/object/{pid}

HTTP Method

DELETE

Get Variables
Name Description
pid Persistent identifier of the object.

Response: 200 OK

No response body.

List Existing Relationships

URL syntax

islandora/rest/v1/object/{pid}/relationship?[predicate][uri][object][literal, false]

HTTP Method

GET

Headers

Accept: application/json

Get Variables

Name Description
pid Persistent identifier of the object.
predicate The predicate to limit the results to. (optional)
uri The uri of the predicate, required if predicate is present.
object The object to limit the results to. (optional)
literal True if the object is literal, false otherwise. Defaults to false. (optional)

Response: 200 OK

Content-Type: application/json

A JSON array with each field containing the following values.

Name Description
predicate object that describes the predicate
object An object that describes the object.

Example Response

[{
  "predicate": {
    "value": "hasModel",
    "alias": "fedora-model",
    "namespace": "info:fedora/fedora-system:def/model#"
  },
  "object": {
    "literal": false,
    "value": "islandora:collectionCModel"
  }
}]

Add A New Relationship

URL syntax

islandora/rest/v1/object/{pid}/relationship

HTTP Method

POST

Headers

Accept: application/json

Get Variables

Name Description
pid Persistent identifier of the object.

Post (form-data)

Name Description
uri The predicate URI for the given predicate.
predicate The predicate of the relationship.
object Object of the relationship.
type The type of the relationship object. Can be either 'uri', 'string', 'int', 'date', 'none'. Defaults to 'uri'.

Response: 201 Created

No response body.

Remove An Existing Relationship

URL syntax

islandora/rest/v1/object/{pid}/relationship

HTTP Method

DELETE

Headers

Content-Type: application/json

Get Variables

Name Description
pid Persistent identifier of the object.

Request Body (raw)

Content-Type: application/json
Name Description
uri The uri of the predicate, required if predicate is present.
predicate The predicate to limit the remove to (optional)
object The object to limit the remove to (optional)
literal True if the object is literal, false otherwise. Defaults to false (optional)

Response: 200 Ok

No response body.

Describe A Existing Datastream

URL syntax

islandora/rest/v1/object/{pid}/datastream/{dsid}?[content, true][version]

HTTP Method

GET

Headers

Accept: application/json

Get Variables

Name Description
pid Persistent identifier of the object
dsid Data stream Identifier.
content True to return the datastream’s content, False to return it’s properties.
version The version of the datastream to return, identified by its created date of the datastream in ISO 8601 format yyyy-MM-ddTHH:mm:ssZ (optional) If not given the latest version of the data stream will be returned.

Response: 200 OK

Content-Type: application/json

If content is false these properties are returned. When requesting a specific version of the data stream, these values are limited to a subset described here.

Name Description
dsid The datastream's persistent identifier
label The datastream's label
size The datastream's size in bytes
state The datastream’s state, either “A”, “I”, “D”
mimeType The datastream’s MIME Type
controlGroup The datastream's control group, either X, M, E, R
versionable A boolean value if the datastream is versionable
created Created date of the datastream, yyyy-MM-ddTHH:mm:ssZ
checksumType The checksum type, e.g., SHA-1, MD5. Value is "DISABLED" if checksums are not enabled for the datastream.
checksum The checksum value. Value is "none" if no checksum is available.
versions Any array of objects each describing each datastream version not including the latest, contains a subset of the fields described here.

Example Response

{
  "dsid": "RELS-EXT",
  "label": "Fedora Object to Object Relationship Metadata.",
  "state": "A",
  "size": 1173,
  "mimeType": "application\/rdf+xml",
  "controlGroup": "X",
  "created": "2013-06-23T07:28:32.787Z",
  "checksumType": "SHA-1",
  "checksum": "46dbb3122dc1a140a5f344934251d7c1f680ff28",
  "versionable": true,
  "versions": [{
    "label": "Old Label.",
    "state": "A",
    "size": "1000",
    "mimeType": "application\/rdf+xml",
    "controlGroup": "X",
    "created": "2013-05-23T06:26:32.787Z"
  }]
}

If content is true the datastream’s content is returned, and the appropriate content-type will be set by the server for the datastream’s content.

Create A New Datastream

URL syntax

islandora/rest/v1/object/{pid}/datastream

HTTP Method

POST

Headers

Accept: application/json

Get Variables

Name Description
pid Persistent identifier of the object to add the datastream to.

Post (form-data)

Name Description
dsid The new datastream's persistent identifier
label The new datastream's label (optional)
state The new datastream’s state, either “A”, “I”, “D” (optional) Defaults to “A”
mimeType The new datastream’s MIME Type (optional) if not provided then it is guessed from the uploaded file.
checksumType The new datastream’s checksum type (optional), e.g., SHA-1, MD5. Defaults to 'DISABLED'.
controlGroup The new datastream's control group, either X, M, E, R
versionable A boolean value if the datastream is versionable (optional) Defaults to true
multipart file as request content File to use as the datastream’s content

Response: 201 Created

Content-Type: application/json

Returns the same response as a GET Datastream request.

Modify An Existing Datastreams Properties And Content

URL syntax

islandora/rest/v1/object/{pid}/datastream/{dsid}

HTTP Method

PUT

Headers

Accept: application/json

Content-Type: application/json

Get Variables

Name Description
pid Persistent identifier of the object to add the datastream to.
dsid The persistent datastream identifier.

Request Body (raw)

Content-Type: application/json
Name Description
label The datastream's new label (optional)
state The datastream’s new state, either “A”, “I”, “D” (optional)
mimeType The new datastream’s MIME Type (optional) if not provided then it is guessed from the uploaded file.
checksumType The datastream’s checksum type (optional), e.g., SHA-1, MD5. Defaults to 'DISABLED'.
versionable A boolean value if the datastream is versionable (optional) Defaults to true
multipart file as request content File to replace existing datastream (for Managed datastreams)

Response: 200 Ok

Content-Type: application/json

Returns the same response as a GET Datastream request.

Delete An Existing Datastream

URL syntax

islandora/rest/v1/object/{pid}/datastream/{dsid}

HTTP Method

DELETE

Headers

Accept: application/json

Get Variables

Name Description
pid Persistent identifier of the object, if not given the namespace will be used to create a new one.
dsid Data stream Identifier.

Response: 200 OK

No response body.

Search For Objects

This is a light wrapper for the SOLR own end point. It incorporates XACML alters to restrict the search results for the given user. The Islandora Solr Search 'Search the Solr index' permission is used for authentication instead of any custom permissions from this module.

Because parameters are passed through to Solr carte blanche, invalid requests may trigger 400 errors. Apache_Solr_Service does not provide details about the error in these cases, so a generic message is given.

URL syntax

islandora/rest/v1/solr/{query}

HTTP Method

GET

Headers

Accept: application/json

Get Variables

Name Description
query The SOLR query to execute.
... See the SOLR documentation for the additional parameters it can take.

Response: 200 OK

Content-Type: application/json

The result will be unmodified server side since all the query types support the return of JSON directly to the user. So this will more or less just delegate to those respective end points. See the documentation for SOLR for more info.

Permission Mappings

Islandora Core Permissions mapping

To ensure all object restrictions are respected on Islandora REST requests, core islandora permissions are mapped in the following way:

Islandora Rest and Islandora Core permissions mapping

Rest Permission Name Islandora Core Permission
ISLANDORA_REST_OBJECT_GET_PERM ISLANDORA_VIEW_OBJECTS
ISLANDORA_REST_OBJECT_PUT_PERM ISLANDORA_MANAGE_PROPERTIES
ISLANDORA_REST_OBJECT_POST_PERM ISLANDORA_INGEST
ISLANDORA_REST_OBJECT_DELETE_PERM ISLANDORA_PURGE
ISLANDORA_REST_DATASTREAM_GET_PERM ISLANDORA_VIEW_OBJECTS
ISLANDORA_REST_DATASTREAM_PUT_PERM ISLANDORA_METADATA_EDIT
ISLANDORA_REST_DATASTREAM_POST_PERM ISLANDORA_ADD_DS
ISLANDORA_REST_DATASTREAM_DELETE_PERM ISLANDORA_PURGE
ISLANDORA_REST_DATASTREAM_TOKEN_GET_PERM ISLANDORA_VIEW_OBJECTS
ISLANDORA_REST_RELATIONSHIP_GET_PERM ISLANDORA_VIEW_OBJECTS
ISLANDORA_REST_RELATIONSHIP_POST_PERM ISLANDORA_METADATA_EDIT
ISLANDORA_REST_RELATIONSHIP_DELETE_PERM ISLANDORA_PURGE

License

GPLv3

TODO

  • Update Solution Packs to use datastream ingested/modified hooks rather than object ingested hooks.
  • Move DC transform logic out of XML Forms and have it use ingested/modified hooks instead.
  • Add support for purging previous versions of datastreams
  • Add checksum support to datastream end-points.
  • Add describe json end-point for the repository.
  • Make PUT requests support multi-part form data, populate $_FILES.
  • Investigate a making a jQuery plugin to ease interaction with REST API?