Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signposting #1055

Merged
merged 3 commits into from
Mar 14, 2019
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions docs/technical-documentation/rest-signposting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Signposting

Signposting is a technique used in RESTful APIs where other relevant resources are exposed to clients as `Link` headers in
`GET` and `HEAD` requests. These `Link` headers follow a standard format as specified in [RFC8288](
https://tools.ietf.org/html/rfc8288). Drupal already makes use of this technique for content entities, and Islandora takes it
even further by providing additional `Link` headers that enable the client to navigate the repository and discover
additional information about various resources. Because the links are returned in response headers, they can be relied upon
without having to parse the message body. This makes them consistent across all serialization formats that can be returned in a message body
(XML, JSON, JSONLD, etc...).

As a general precaution, link headers for Drupal entities are not exposed to users that do not have the permissions to view
the entity linked in the header. So making GET and HEAD requests as anonymous will yield a different set of headers than
what an authenticated user would see. For example, anonymous users don't have the `view media` permission, so they will not
see the link headers for media associated with a node.

## Link Headers Provided by Islandora

### Alternate Representations
Other representations generated by different serializers available through Drupal's REST API are exposed as link headers
with `rel="alternate"` and `type` equal to the mimetype that will be received when dereferencing the link. For example,
if an entity in Drupal has a JSONLD representation, then the link header returned in a GET or HEAD response would look like

`Link: <http://example.org/node/1?_format=jsonld>; rel="alternate"; type="application/ld+json"`

### Referenced Entities

Entity reference fields are exposed as link headers with `rel="related"` and a title equal to the entity reference field's display label.
For example, if `http://example.org/node/1` has an entity reference field name "Associated Content" that references
`http://example.org/node/2`, then the link header returned in a GET or HEAD response would look like

`Link: <http://example.org/node/2>; rel="related"; title="Associated Content"`

### Referenced Taxonomy Terms

Entity reference fields for taxonomy terms get special handling. The taxonomy terms used to tag content are exposed as link headers
with `rel="tag"` and a title equal to the taxonomy term's display label. If the term has an external uri in a controlled vocabulary,
then that uri is provided. Otherwise, the local Drupal uri is provided. For example, if a piece of content is tagged with
mjordan marked this conversation as resolved.
Show resolved Hide resolved
`taxonomy/term/1`, which has a display label of "Example Term", then the link header returned in a GET or HEAD response would look like

`Link: <http://example.org/taxonomy/term/1>; rel="tag"; title="Example Term"`

If instead the term were to have the `field_external_uri` field with a value of `http://exampletwo.org/vocab#term` then the link
header would look like

`Link: <http://exampletwo.org/vocab#term>; rel="tag"; title="Example Term"`.

### Associated Media

Media entities belonging to nodes are exposed as link headers with `rel="related"` and a title equal to the display label of
their `field_media_use` taxonomy term. For example, if a Media is tagged as `Original File` indicating
that it is the initial file uploaded, the link header returned in a GET or HEAD response for a node would look like

`Link: <http://example.org/media/1>; rel="related"; title="Original File"`.

### Source Files

Files that are the source for Media entities are exposed as Link headers in the GET and HEAD responses with `rel="describes"`.
mjordan marked this conversation as resolved.
Show resolved Hide resolved
The endpoint to edit the contents of the source file is also exposed using `rel="edit-media"`. For example, if
`http://example.org/media/1` has the source file `http://example.org/file.txt`, then a GET or HEAD response would contain
both

- `Link: <http://example.org/file.txt>; rel="describes"`
- `Link: <http://example.org/media/1/source>; rel="edit-media"`

## Examples

### Requesting a Node

After creating a Node, adding it to a Collection, uploading a file and kicking off derivatives, the link headers returned
mjordan marked this conversation as resolved.
Show resolved Hide resolved
for said Node would look like the following. Note that non-Link headers have been removed for brevity:

```bash
vagrant@claw:~$ curl -I http://localhost:8000/node/1?_format=json
HTTP/1.1 200 OK
...
# These are provided by Drupal core
Link: <http://localhost:8000/node/1>; rel="canonical"
Link: <http://localhost:8000/node/1/delete>; rel="https://drupal.org/link-relations/delete-form"
Link: <http://localhost:8000/admin/content/node/delete?node=1>; rel="https://drupal.org/link-relations/delete-multiple-form"
Link: <http://localhost:8000/node/1/edit>; rel="edit-form"
Link: <http://localhost:8000/node/1/revisions>; rel="version-history"
Link: <http://localhost:8000/node/1>; rel="https://drupal.org/link-relations/revision"
Link: <http://localhost:8000/node?node=1>; rel="https://drupal.org/link-relations/create"

# These are provided by Islandora
Link: <http://localhost:8000/node/2>; rel="related"; title="Member of"
Link: <http://purl.org/coar/resource_type/c_c513>; rel="tag"; title="Image"
Link: <http://localhost:8000/taxonomy/term/11>; rel="tag"; title="Still Image"
Link: <http://localhost:8000/media/1>; rel="related"; title="Original File"
Link: <http://localhost:8000/media/2>; rel="related"; title="Service File"
Link: <http://localhost:8000/media/3>; rel="related"; title="Thumbnail Image"
Link: <http://localhost:8000/node/1?_format=jsonld>; rel="alternate"; type="application/ld+json"
```

### Requesting a Media
If we were to inspect one of the Media associated with this Node (which we would've gotten in the response above), the
results would look like:

```bash
vagrant@claw:~$ curl -I http://localhost:8000/media/1?_format=json
HTTP/1.1 200 OK
...

# These are provided by Drupal core
Link: <http://localhost:8000/media/add>; rel="https://drupal.org/link-relations/add-page"
Link: <http://localhost:8000/media/add/image>; rel="https://drupal.org/link-relations/add-form"
Link: <http://localhost:8000/media/1>; rel="canonical"
Link: <http://localhost:8000/admin/content/media>; rel="collection"
Link: <http://localhost:8000/media/1/delete>; rel="https://drupal.org/link-relations/delete-form"
Link: <http://localhost:8000/media/delete?media=1>; rel="https://drupal.org/link-relations/delete-multiple-form"
Link: <http://localhost:8000/media/1/edit>; rel="edit-form"
Link: <http://localhost:8000/media/1>; rel="https://drupal.org/link-relations/revision"

# These are provided by Islandora
Link: <http://localhost:8000/node/1>; rel="related"; title="Media of"
Link: <http://pcdm.org/use#OriginalFile>; rel="tag"; title="Original File"
Link: <http://localhost:8000/media/1?_format=jsonld>; rel="alternate"; type="application/ld+json"
Link: <http://localhost:8000/_flysystem/fedora/2019-03/IF-Org-Chart_0.jpg>; rel="describes"; type="image/jpeg"
```