Skip to content

Commit

Permalink
Merge pull request #232 from nulib/5088-top-as-iiif
Browse files Browse the repository at this point in the history
Add ?as=iiif param to top level collections endpoint.
  • Loading branch information
mathewjordan committed Jul 24, 2024
2 parents 6c84bdc + f954cdf commit bdea6b2
Show file tree
Hide file tree
Showing 9 changed files with 293 additions and 80 deletions.
1 change: 1 addition & 0 deletions docs/docs/spec/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ paths:
tags:
- Collection
parameters:
- $ref: "./types.yaml#/components/parameters/as"
- $ref: "./types.yaml#/components/parameters/page"
- $ref: "./types.yaml#/components/parameters/size"
- $ref: "./types.yaml#/components/parameters/sort"
Expand Down
5 changes: 4 additions & 1 deletion node/src/api/request/models.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ function extractRequestedModels(requestedModels) {

function validModels(models, format) {
if (format === "iiif") {
return models.length == 1 && models.every((model) => model === "works");
return (
models.length == 1 &&
models.every((model) => model === "works" || "collections")
);
}
return models.every(isAllowed);
}
Expand Down
167 changes: 121 additions & 46 deletions node/src/api/response/iiif/collection.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { dcApiEndpoint, dcUrl } = require("../../../environment");
const { transformError } = require("../error");
const { provider, nulLogo } = require("./presentation-api/provider");

async function transform(response, pager) {
if (response.statusCode === 200) {
Expand Down Expand Up @@ -27,32 +28,63 @@ async function buildCollection(responseBody, pageInfo) {
collectionSummary = "",
},
},
query_url,
} = pageInfo;

/**
* if the query_url pathname is `/collections` then we
* know this is a top-level "collection of collections"
*/
const { pathname } = new URL(query_url);
const isTopCollection = pathname.split("/").pop() === "collections";
const collectionId = parseCollectionId(pageInfo.query_url);

let result = {
"@context": ["http://iiif.io/api/presentation/3/context.json"],
id: collectionId(pageInfo),
id: iiifCollectionId(pageInfo),
type: "Collection",
label: { none: [collectionLabel] },
summary: { none: [collectionSummary] },
...(collectionSummary && {
summary: {
none: [`${collectionSummary}`],
},
}),
items: getItems(responseBody?.hits?.hits, pageInfo, isTopCollection),
requiredStatement: {
label: {
none: ["Attribution"],
},
value: {
none: ["Courtesy of Northwestern University Libraries"],
},
},
provider: [provider],
logo: [nulLogo],
seeAlso: [
{
id: isTopCollection
? `${dcApiEndpoint()}/collections`
: getLinkingPropertyId(pageInfo, dcApiEndpoint(), "query"),
type: "Dataset",
format: "application/json",
label: {
none: ["Northwestern University Libraries Digital Collections API"],
},
},
],
homepage: [
{
id: homepageUrl(pageInfo),
id: isTopCollection ? dcUrl() : getLinkingPropertyId(pageInfo, dcUrl()),
type: "Text",
format: "text/html",
label: {
none: [collectionLabel],
},
},
],

items: getItems(responseBody?.hits?.hits, pageInfo),
};

if (pageInfo.options?.parameterOverrides) {
const collectionId = new URL(pageInfo.query_url).pathname
.split("/")
.reverse()[0];
if (!isTopCollection && pageInfo.options?.parameterOverrides) {
const thumbnailId = `${dcApiEndpoint()}/collections/${collectionId}/thumbnail`;
result.thumbnail = [
{
Expand All @@ -68,8 +100,9 @@ async function buildCollection(responseBody, pageInfo) {
return result;
}

function getItems(hits, pageInfo) {
const items = hits.map((item) => loadItem(item["_source"]));
function getItems(hits, pageInfo, isTopCollection) {
const itemType = isTopCollection ? "Collection" : "Manifest";
const items = hits.map((item) => loadItem(item["_source"], itemType));

if (pageInfo?.next_url) {
items.push({
Expand All @@ -84,27 +117,29 @@ function getItems(hits, pageInfo) {
return items;
}

function collectionId(pageInfo) {
function iiifCollectionId(pageInfo) {
let collectionId = new URL(pageInfo.query_url);
if (pageInfo.current_page > 1) {
collectionId.searchParams.set("page", pageInfo.current_page);
}
return collectionId;
}

function homepageUrl(pageInfo) {
function parseCollectionId(query_url) {
return new URL(query_url).pathname.split("/").reverse()[0];
}

function getLinkingPropertyId(pageInfo, baseUrl, queryParam = "q") {
let result;

if (pageInfo.options?.parameterOverrides) {
const collectionId = new URL(pageInfo.query_url).pathname
.split("/")
.reverse()[0];
result = new URL(`/collections/${collectionId}`, dcUrl());
const collectionId = parseCollectionId(pageInfo.query_url);
result = new URL(`/collections/${collectionId}`, baseUrl);
} else {
result = new URL("/search", dcUrl());
result = new URL("/search", baseUrl);
if (pageInfo.options?.queryStringParameters?.query) {
result.searchParams.set(
"q",
queryParam,
pageInfo.options.queryStringParameters.query
);
}
Expand All @@ -120,36 +155,76 @@ function homepageUrl(pageInfo) {
return result;
}

function loadItem(item) {
return {
id: item.iiif_manifest,
type: "Manifest",
homepage: [
{
id: new URL(`/items/${item.id}`, dcUrl()),
type: "Text",
format: "text/html",
label: {
none: [`${item.title}`],
function loadItem(item, itemType) {
if (itemType === "Manifest") {
return {
id: item.iiif_manifest,
type: "Manifest",
homepage: [
{
id: new URL(`/items/${item.id}`, dcUrl()),
type: "Text",
format: "text/html",
label: {
none: [`${item.title}`],
},
},
],
label: {
none: [`${item.title}`],
},
],
label: {
none: [`${item.title}`],
},
summary: {
none: [`${item.work_type}`],
},
thumbnail: [
{
id: item.thumbnail,
format: "image/jpeg",
type: "Image",
width: 400,
height: 400,
summary: {
none: [`${item.work_type}`],
},
],
};
thumbnail: [
{
id: item.thumbnail,
format: "image/jpeg",
type: "Image",
width: 400,
height: 400,
},
],
};
}

if (itemType === "Collection") {
return {
id: `${item.api_link}?as=iiif`,
type: "Collection",
label: {
none: [`${item.title}`],
},
...(item.description && {
summary: {
none: [`${item.description}`],
},
}),
...(item.thumbnail && {
thumbnail: [
{
id: item.thumbnail,
type: "Image",
format: "image/jpeg",
width: 400,
height: 400,
},
],
}),
...(item.canonical_link && {
homepage: [
{
id: new URL(`/collections/${item.id}`, dcUrl()),
type: "Text",
format: "text/html",
label: {
none: [`${item.title}`],
},
},
],
}),
};
}
}

module.exports = { transform };
32 changes: 2 additions & 30 deletions node/src/api/response/iiif/manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const { metadataLabelFields } = require("./presentation-api/metadata");
const {
buildPlaceholderCanvas,
} = require("./presentation-api/placeholder-canvas");
const { nulLogo, provider } = require("./presentation-api/provider");

function transform(response) {
if (response.statusCode === 200) {
Expand Down Expand Up @@ -260,37 +261,8 @@ function transform(response) {
}
}

/** Add logo manually (w/o IIIF Builder) */
const nulLogo = {
id: "https://iiif.dc.library.northwestern.edu/iiif/2/00000000-0000-0000-0000-000000000003/full/pct:50/0/default.webp",
type: "Image",
format: "image/webp",
height: 139,
width: 1190,
};
jsonManifest.logo = [nulLogo];

/** Add provider manually (w/o IIIF Builder) */
const provider = {
id: "https://www.library.northwestern.edu/",
type: "Agent",
label: { none: ["Northwestern University Libraries"] },
homepage: [
{
id: "https://dc.library.northwestern.edu/",
type: "Text",
label: {
none: [
"Northwestern University Libraries Digital Collections Homepage",
],
},
format: "text/html",
language: ["en"],
},
],
logo: [nulLogo],
};
jsonManifest.provider = [provider];
jsonManifest.logo = [nulLogo];

return {
statusCode: 200,
Expand Down
29 changes: 29 additions & 0 deletions node/src/api/response/iiif/presentation-api/provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const nulLogo = {
id: "https://iiif.dc.library.northwestern.edu/iiif/2/00000000-0000-0000-0000-000000000003/full/pct:50/0/default.webp",
type: "Image",
format: "image/webp",
height: 139,
width: 1190,
};

const provider = {
id: "https://www.library.northwestern.edu/",
type: "Agent",
label: { none: ["Northwestern University Libraries"] },
homepage: [
{
id: "https://dc.library.northwestern.edu/",
type: "Text",
label: {
none: [
"Northwestern University Libraries Digital Collections Homepage",
],
},
format: "text/html",
language: ["en"],
},
],
logo: [nulLogo],
};

module.exports = { nulLogo, provider };
26 changes: 23 additions & 3 deletions node/src/handlers/get-collections.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
const { doSearch } = require("./search-runner");
const { wrap } = require("./middleware");

const getCollections = async (event) => {
event.pathParameters.models = "collections";
event.body = { query: { match_all: {} } };
return doSearch(event, { includeToken: false });
};

const getCollectionsAsIiif = async (event) => {
event.pathParameters.models = "collections";
event.body = { query: { match_all: {} } };
event.queryStringParameters.collectionLabel =
"Northwestern University Libraries Digital Collections";
event.queryStringParameters.collectionSummary =
"Explore digital resources from the Northwestern University Library collections – including letters, photographs, diaries, maps, and audiovisual materials.";

return doSearch(event, {
includeToken: false,
parameterOverrides: { as: "iiif" },
});
};

/**
* A simple function to get Collections
*/
exports.handler = wrap(async (event) => {
event.pathParameters.models = "collections";
event.body = { query: { match_all: {} } };
return doSearch(event, { includeToken: false });
return event.queryStringParameters?.as === "iiif"
? getCollectionsAsIiif(event)
: getCollections(event);
});
26 changes: 26 additions & 0 deletions node/test/integration/get-collections.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,31 @@ describe("Collections route", () => {
const url = new URL(query_url);
expect(url.pathname).to.eq("/api/v2/search/collections");
});

it("returns top level collection as a IIIF collection", async () => {
mock
.post("/dc-v2-collection/_search", makeQuery({ size: 10, from: 0 }))
.reply(200, helpers.testFixture("mocks/collections.json"));

const event = helpers
.mockEvent("GET", "/collections")
.queryParams({ as: "iiif" })
.render();
const result = await handler(event);
expect(result.statusCode).to.eq(200);
expect(result).to.have.header(
"content-type",
/application\/json;.*charset=UTF-8/
);
const resultBody = JSON.parse(result.body);
expect(resultBody.type).to.eq("Collection");
expect(resultBody.label.none[0]).to.eq(
"Northwestern University Libraries Digital Collections"
);
expect(resultBody.summary.none[0]).to.eq(
"Explore digital resources from the Northwestern University Library collections – including letters, photographs, diaries, maps, and audiovisual materials."
);
expect(resultBody.items.length).to.eq(69);
});
});
});
Loading

0 comments on commit bdea6b2

Please sign in to comment.