diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62c8935 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6cc0fc0 --- /dev/null +++ b/README.md @@ -0,0 +1,499 @@ +## IIIF-LDP-HATEOAS-API + +This repository contains an alternative client-server interaction model to the IIIF Presentation API 3.0. + +- Rather than enforcing a Domain API by means of concrete monolithic JSON serializations, it provides a functional interface. + +- The interface is served from an RDF dataset graph backed by Trellis LDP and Activity Stream Events. + +- The data model is pure RDF with JSON-LD being the serialization Content-Type. + +- The intent is to facilitate "smart" clients that depend on LDP / HATEOAS and a modular service architecture. + +- It will be built similarly to the GitHub API. + +## Examples + +All Objects indicated with `:some-object` are `` + +#### Summary Representation + +These responses always contain an `@list`. + +List all organizations + +> GET /organizations + +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/organizations", + "type": "Collection", + "items": [] +} +``` + +Fetch collections for an organization + +Where Organization Resource IRI = `` + +> GET /orgs/:org/collections + +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/orgs/org-UUID/collections", + "type": "Collection", + "items": [] +} +``` + +Fetch manifests for a collection + +Where Collection Resource IRI = `` + +> GET /orgs/:org/collections/:collection/manifests + +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/orgs/org-UUID/collections/collection-UUID/manifests", + "type": "Collection", + "items": [] +} +``` +Fetch all manifests for an organization + +> GET /orgs/:org/manifests + +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/orgs/org-UUID/manifests", + "type": "Collection", + "items": [] +} +``` + +Fetch ranges for a manifest + +Where Manifest Resource IRI = `` + +> GET /manifests/:org/:manifest/ranges + + +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/manifests/org-UUID/manifest-UUID/ranges", + "type": "Range", + "items": [] +} +``` + +Fetch all annotations for a manifest + +> GET /manifests/:org/:manifest/annotations + +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/manifests/org-UUID/manifest-UUID/annotations", + "items": [] +} +``` +Fetch sequences for a manifest + +> GET /manifests/:org/:manifest/sequences + + +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/manifests/org-UUID/manifest-UUID/sequences", + "type": "Sequence", + "items": [] +} +``` +Fetch objects for a sequence + +Where Sequence Resource IRI = `` + +> GET /manifests/:org/:manifest/sequences/:sequence/canvases + +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/manifests/org-UUID/manifest-UUID/sequences/sequence-UUID/canvases", + "type": "Sequence", + "items": [] +} +``` +Fetch annotations for a canvas + +Where Canvas Resource IRI = `` + +> GET /manifests/:org/:manifest/canvases/:canvas/annotations + +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/manifests/org-UUID/manifest-UUID/canvases/canvas-UUID/annotations", + "items": [] +} +``` +Note here that annotations are NEVER embedded objects. The request URI provides the criteria that allows the association to a canvas +using the `target` property of an annotation. + +#### Detailed Representation + +Fetch a collection for an organization + +> GET /collections/:org/:collection +```apache +Status: 200 OK +Link: ; rel="next", + ; rel="collection" +``` +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/collections/org-UUID/collection-UUID", + "type": "Collection", + "label": "Top Level Collection for Example Organization", + "renderingHint": "top", + "description": "Description of Collection", + "attribution": "Provided by Example Organization" +} +``` + +Fetch manifest properties + +> GET /manifests/:org/:manifest +```apache +Status: 200 OK +Link: ; rel="next", + ; rel="collection" +``` +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/manifests/org-UUID/manifest-UUID", + "type": "Manifest", + "label": { + "en": [ + "Book 1" + ] + }, + "renderingDirection": "right-to-left", + "renderingHint": [ + "paged" + ], + "navDate": "1856-01-01T00:00:00Z", + "rights": [ + { + "id": "https://api.repository.org/repository/binaries/binary-UUID", + "type": "Text", + "language": "en", + "format": "text/html" + } + ], + "attribution": { + "en": [ + "Provided by Example Organization" + ] + }, + "logo": { + "id": "https://api.repository.org/repository/binaries/binary-UUID", + "service": { + "id": "https://example.org/service/inst1", + "type": "ImageService3", + "profile": "level2" + } + }, + "related": [ + { + "id": "https://api.repository.org/repository/binaries/binary-UUID", + "type": "Video", + "label": { + "en": [ + "Video discussing this book" + ] + }, + "format": "video/mpeg" + } + ], + "service": [ + { + "id": "https://example.org/service/example", + "type": "Service", + "profile": "https://example.org/docs/example-service.html" + } + ], + "seeAlso": [ + { + "id": "https://api.repository.org/repository/binaries/binary-UUID", + "type": "Dataset", + "format": "text/xml", + "profile": "https://example.org/profiles/bibliographic" + } + ], + "rendering": [ + { + "id": "https://api.repository.org/repository/binaries/binary-UUID", + "type": "Text", + "label": { + "en": [ + "Download as PDF" + ] + }, + "format": "application/pdf" + } + ], + "within": [ + { + "id": "https://api.repository.org/orgs/org-UUID/collections/collection-UUID", + "type": "Collection" + } + ], + "startCanvas": { + "id": "https://api.repository.org/manifests/org-UUID/manifest-UUID/canvases/canvas-UUID", + "type": "Canvas" + } +} +``` + +Fetch metadata for a manifest + + +> GET /manifests/:org/:manifest/metadata +```apache +Status: 200 OK +Link: ; rel="next", + ; rel="collection" +``` +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/manifests/org-UUID/manifest-UUID", + "type": "Manifest", + "metadata": [ + { + "label": { + "en": [ + "Author" + ] + }, + "value": { + "@none": [ + "Anne Author" + ] + } + }, + { + "label": { + "en": [ + "Published" + ] + }, + "value": { + "en": [ + "Paris, circa 1400" + ], + "fr": [ + "Paris, environ 1400" + ] + } + }, + { + "label": { + "en": [ + "Notes" + ] + }, + "value": { + "en": [ + "Text of note 1", + "Text of note 2" + ] + } + }, + { + "label": { + "en": [ + "Source" + ] + }, + "value": { + "@none": [ + "From: Some Collection" + ] + } + } + ] +} +``` + +Fetch a manifest sequence + +> GET /manifests/:org/:manifest/sequences/:sequence + +```apache +Status: 200 OK +Link: ; rel="next", + ; rel="collection" +``` +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/manifests/org-UUID/manifest-UUID/sequences/sequence-UUID", + "type": "Sequence", + "label": { + "en": [ + "Current Page Order" + ] + }, + "renderingDirection": "left-to-right", + "renderingHint": [ + "paged" + ], + "startCanvas": "https://api.repository.org/manifests/org-UUID/manifest-UUID/canvases/canvas-UUID" +} +``` + +Fetch a canvas + +> GET /manifests/:org/:manifest/canvases/:canvas + +```apache +Status: 200 OK +Link: ; rel="next", + ; rel="collection" +``` +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/manifests/org-UUID/manifest-UUID/canvases/canvas-UUID", + "type": "Canvas", + "label": { + "@none": [ + "p. 1" + ] + }, + "height": 1000, + "width": 750, + "duration": 180.0 +} +``` + +Fetch a canvas annotation + +> GET /manifests/:org/:manifest/annotations/:annotation + +```apache +Status: 200 OK +Link: ; rel="collection" +``` +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/manifests/org-UUID/manifest-UUID/annotations/annotation-UUID", + "type": "Annotation", + "motivation": "painting", + "body": { + "id": "https://api.repository.org/repository/binaries/binary-UUID", + "type": "Image", + "format": "image/jpeg", + "service": { + "id": "https://example.org/images/book1-page1", + "type": "ImageService3", + "profile": "level2" + }, + "height": 2000, + "width": 1500 + }, + "target": "https://api.repository.org/manifests/org-UUID/manifest-UUID/canvases/canvas-UUID" +} +``` + +### No Recursion + +A single detail will never include summary lists, but only the properties for that resource. This supports +service encapsulation and caching. + +### HATEOAS + +A response for a resource will include Link Headers with paged relations to its associated members if they exist. + +> GET /manifests/:org/:manifest/canvases/:canvas + +#### Response +```apache +Status: 200 OK +Link: ; rel="next", + ; rel="collection" +``` +```json +{ + "@context": [ + "https://www.w3.org/ns/anno.jsonld", + "https://iiif.io/api/presentation/3.0/context.json" + ], + "id": "https://api.repository.org/manifests/manifest-UUID/canvases/canvas-UUID", + "type": "Canvas", + "label": { + "@none": [ + "p. 1" + ] + }, + "height": 1000, + "width": 750, + "duration": 180.0 +} +``` + diff --git a/src/main/java/resources/examples/annotation-page.json b/src/main/java/resources/examples/annotation-page.json new file mode 100644 index 0000000..2150e35 --- /dev/null +++ b/src/main/java/resources/examples/annotation-page.json @@ -0,0 +1,8 @@ +{ + "@context": [ + "http://www.w3.org/ns/anno.jsonld", + "http://iiif.io/api/presentation/3.0/context.json" + ], + "id": "http://my-repository.org/repository/manifests/manifest-UUID/annotations/anno-UUID", + "items": [] +} \ No newline at end of file diff --git a/src/main/java/resources/examples/annotation.json b/src/main/java/resources/examples/annotation.json new file mode 100644 index 0000000..ac05caf --- /dev/null +++ b/src/main/java/resources/examples/annotation.json @@ -0,0 +1,22 @@ +{ + "@context": [ + "http://www.w3.org/ns/anno.jsonld", + "http://iiif.io/api/presentation/3.0/context.json" + ], + "id": "http://my-repository.org/repository/manifests/manifest-UUID/annotations/annotation-UUID", + "type": "Annotation", + "motivation": "painting", + "body": { + "id": "http://my-repository.org/repository/binaries/binary-UUID", + "type": "Image", + "format": "image/jpeg", + "service": { + "id": "http://example.org/images/book1-page1", + "type": "ImageService3", + "profile": "level2" + }, + "height": 2000, + "width": 1500 + }, + "target": "http://my-repository.org/repository/manifests/manifest-UUID/canvases/canvas-UUID" +} \ No newline at end of file diff --git a/src/main/java/resources/examples/canvas.json b/src/main/java/resources/examples/canvas.json new file mode 100644 index 0000000..70ff4dd --- /dev/null +++ b/src/main/java/resources/examples/canvas.json @@ -0,0 +1,16 @@ +{ + "@context": [ + "http://www.w3.org/ns/anno.jsonld", + "http://iiif.io/api/presentation/3.0/context.json" + ], + "id": "http://my-repository.org/repository/manifests/manifest-UUID/canvases/canvas-UUID", + "type": "Canvas", + "label": { + "@none": [ + "p. 1" + ] + }, + "height": 1000, + "width": 750, + "duration": 180.0 +} \ No newline at end of file diff --git a/src/main/java/resources/examples/canvases.json b/src/main/java/resources/examples/canvases.json new file mode 100644 index 0000000..7ad9443 --- /dev/null +++ b/src/main/java/resources/examples/canvases.json @@ -0,0 +1,8 @@ +{ + "@context": [ + "http://www.w3.org/ns/anno.jsonld", + "http://iiif.io/api/presentation/3.0/context.json" + ], + "id": "http://my-repository.org/repository/manifests/manifest-UUID/canvases", + "items": [] +} \ No newline at end of file diff --git a/src/main/java/resources/examples/collection.json b/src/main/java/resources/examples/collection.json new file mode 100644 index 0000000..fd68fa8 --- /dev/null +++ b/src/main/java/resources/examples/collection.json @@ -0,0 +1,12 @@ +{ + "@context": [ + "http://www.w3.org/ns/anno.jsonld", + "http://iiif.io/api/presentation/3.0/context.json" + ], + "id": "http://my-repository.org/repository/collections/collections-UUID", + "type": "Collection", + "label": "Top Level Collection for Example Organization", + "renderingHint": "top", + "description": "Description of Collection", + "attribution": "Provided by Example Organization" +} \ No newline at end of file diff --git a/src/main/java/resources/examples/collections.json b/src/main/java/resources/examples/collections.json new file mode 100644 index 0000000..c65db43 --- /dev/null +++ b/src/main/java/resources/examples/collections.json @@ -0,0 +1,9 @@ +{ + "@context": [ + "http://www.w3.org/ns/anno.jsonld", + "http://iiif.io/api/presentation/3.0/context.json" + ], + "id": "http://my-repository.org/repository/orgs/org-UUID/collections", + "type": "Collection", + "items": [] +} \ No newline at end of file diff --git a/src/main/java/resources/examples/manifest-properties.json b/src/main/java/resources/examples/manifest-properties.json new file mode 100644 index 0000000..e108500 --- /dev/null +++ b/src/main/java/resources/examples/manifest-properties.json @@ -0,0 +1,88 @@ +{ + "@context": [ + "http://www.w3.org/ns/anno.jsonld", + "http://iiif.io/api/presentation/3.0/context.json" + ], + "id": "http://my-repository.org/repository/manifests/manifest-UUID", + "type": "Manifest", + "label": { + "en": [ + "Book 1" + ] + }, + "renderingDirection": "right-to-left", + "renderingHint": [ + "paged" + ], + "navDate": "1856-01-01T00:00:00Z", + "rights": [ + { + "id": "http://my-repository.org/repository/binaries/binary-UUID", + "type": "Text", + "language": "en", + "format": "text/html" + } + ], + "attribution": { + "en": [ + "Provided by Example Organization" + ] + }, + "logo": { + "id": "http://my-repository.org/repository/binaries/binary-UUID", + "service": { + "id": "http://example.org/service/inst1", + "type": "ImageService3", + "profile": "level2" + } + }, + "related": [ + { + "id": "http://my-repository.org/repository/binaries/binary-UUID", + "type": "Video", + "label": { + "en": [ + "Video discussing this book" + ] + }, + "format": "video/mpeg" + } + ], + "service": [ + { + "id": "http://example.org/service/example", + "type": "Service", + "profile": "http://example.org/docs/example-service.html" + } + ], + "seeAlso": [ + { + "id": "http://my-repository.org/repository/binaries/binary-UUID", + "type": "Dataset", + "format": "text/xml", + "profile": "http://example.org/profiles/bibliographic" + } + ], + "rendering": [ + { + "id": "http://my-repository.org/repository/binaries/binary-UUID", + "type": "Text", + "label": { + "en": [ + "Download as PDF" + ] + }, + "format": "application/pdf" + } + ], + "within": [ + { + "id": "http://my-repository.org/repository/orgs/org-UUID/collections/collection-UUID", + "type": "Collection" + } + ], + "startCanvas": { + "id": "http://my-repository.org/repository/manifests/manifest-UUID/canvases/canvas-UUID", + "type": "Canvas" + } +} \ No newline at end of file diff --git a/src/main/java/resources/examples/manifests.json b/src/main/java/resources/examples/manifests.json new file mode 100644 index 0000000..8870396 --- /dev/null +++ b/src/main/java/resources/examples/manifests.json @@ -0,0 +1,9 @@ +{ + "@context": [ + "http://www.w3.org/ns/anno.jsonld", + "http://iiif.io/api/presentation/3.0/context.json" + ], + "id": "http://my-repository.org/repository/orgs/org-UUID/manifests", + "type": "Collection", + "items": [] +} \ No newline at end of file diff --git a/src/main/java/resources/examples/metadata.json b/src/main/java/resources/examples/metadata.json new file mode 100644 index 0000000..ae99203 --- /dev/null +++ b/src/main/java/resources/examples/metadata.json @@ -0,0 +1,62 @@ +{ + "@context": [ + "http://www.w3.org/ns/anno.jsonld", + "http://iiif.io/api/presentation/3.0/context.json" + ], + "id": "http://my-repository.org/repository/manifests/manifest-UUID", + "type": "Manifest", + "metadata": [ + { + "label": { + "en": [ + "Author" + ] + }, + "value": { + "@none": [ + "Anne Author" + ] + } + }, + { + "label": { + "en": [ + "Published" + ] + }, + "value": { + "en": [ + "Paris, circa 1400" + ], + "fr": [ + "Paris, environ 1400" + ] + } + }, + { + "label": { + "en": [ + "Notes" + ] + }, + "value": { + "en": [ + "Text of note 1", + "Text of note 2" + ] + } + }, + { + "label": { + "en": [ + "Source" + ] + }, + "value": { + "@none": [ + "From: Some Collection" + ] + } + } + ] +} \ No newline at end of file diff --git a/src/main/java/resources/examples/organizations.json b/src/main/java/resources/examples/organizations.json new file mode 100644 index 0000000..329abae --- /dev/null +++ b/src/main/java/resources/examples/organizations.json @@ -0,0 +1,9 @@ +{ + "@context": [ + "http://www.w3.org/ns/anno.jsonld", + "http://iiif.io/api/presentation/3.0/context.json" + ], + "id": "http://my-repository.org/repository/orgs", + "type": "Collection", + "items": [] +} \ No newline at end of file diff --git a/src/main/java/resources/examples/ranges.json b/src/main/java/resources/examples/ranges.json new file mode 100644 index 0000000..dfec3f3 --- /dev/null +++ b/src/main/java/resources/examples/ranges.json @@ -0,0 +1,9 @@ +{ + "@context": [ + "http://www.w3.org/ns/anno.jsonld", + "http://iiif.io/api/presentation/3.0/context.json" + ], + "id": "http://my-repository.org/repository/manifests/manifest-UUID/ranges", + "type": "Range", + "items": [] +} \ No newline at end of file diff --git a/src/main/java/resources/examples/sequence.json b/src/main/java/resources/examples/sequence.json new file mode 100644 index 0000000..48c9526 --- /dev/null +++ b/src/main/java/resources/examples/sequence.json @@ -0,0 +1,18 @@ +{ + "@context": [ + "http://www.w3.org/ns/anno.jsonld", + "http://iiif.io/api/presentation/3.0/context.json" + ], + "id": "http://my-repository.org/repository/manifests/manifest-UUID/sequences/sequence-UUID", + "type": "Sequence", + "label": { + "en": [ + "Current Page Order" + ] + }, + "renderingDirection": "left-to-right", + "renderingHint": [ + "paged" + ], + "startCanvas": "http://my-repository.org/repository/manifests/manifest-UUID/canvases/canvas-UUID" +} \ No newline at end of file diff --git a/src/main/java/resources/examples/sequences.json b/src/main/java/resources/examples/sequences.json new file mode 100644 index 0000000..37c69cd --- /dev/null +++ b/src/main/java/resources/examples/sequences.json @@ -0,0 +1,9 @@ +{ + "@context": [ + "http://www.w3.org/ns/anno.jsonld", + "http://iiif.io/api/presentation/3.0/context.json" + ], + "id": "http://my-repository.org/repository/manifests/manifest-UUID/sequences", + "type": "Sequence", + "items": [] +} \ No newline at end of file