diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 00000000..d370b2fb --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2016", + "jsx": "preserve" + }, + "exclude": ["node_modules", "**/node_modules/*"], + "js-import.excludeFilesToScan": "packages/*/{es,lib,build}/**" +} diff --git a/packages/iiif-graphql/package.json b/packages/iiif-graphql/package.json index 86100386..9ca71612 100644 --- a/packages/iiif-graphql/package.json +++ b/packages/iiif-graphql/package.json @@ -12,19 +12,19 @@ "test": "echo \"No tests yet\"" }, "dependencies": { - "apollo-cache-inmemory": "^1.3.5", - "apollo-client": "^2.4.2", + "apollo-cache-inmemory": "^1.3.7", + "apollo-client": "^2.4.4", "apollo-link": "^1.2.3", "apollo-link-schema": "^1.1.1", "apollo-link-set-context": "^0.5.4", "dataloader": "^1.4.0", "graphql": "^14.0.2", "graphql-tag": "^2.10.0", - "graphql-tools": "^4.0.1", + "graphql-tools": "^4.0.2", "iiif-redux": "1.0.0", - "react": "^16.5.2", + "react": "^16.6.0", "react-apollo": "^2.2.4", - "react-dom": "^16.5.2" + "react-dom": "^16.6.0" }, "devDependencies": { "babel-plugin-graphql-tag": "^1.6.0", diff --git a/packages/iiif-graphql/src/components/HelloWorld.js b/packages/iiif-graphql/src/components/HelloWorld.js index d97a9470..43c310b3 100644 --- a/packages/iiif-graphql/src/components/HelloWorld.js +++ b/packages/iiif-graphql/src/components/HelloWorld.js @@ -4,9 +4,7 @@ import gql from 'graphql-tag'; const queryText = gql` query { - getCollection( - collectionId: "https://view.nls.uk/collections/7446/74466699.json" - ) { + collection(id: "https://view.nls.uk/collections/7446/74466699.json") { id type label @@ -15,9 +13,7 @@ const queryText = gql` value } } - getManifest( - manifestId: "https://wellcomelibrary.org/iiif/b20432033/manifest" - ) { + manifest(id: "https://wellcomelibrary.org/iiif/b20432033/manifest") { id type label diff --git a/packages/iiif-graphql/src/context.js b/packages/iiif-graphql/src/context.js index 08352b87..d85b0650 100644 --- a/packages/iiif-graphql/src/context.js +++ b/packages/iiif-graphql/src/context.js @@ -1,12 +1,77 @@ import { createStore } from 'iiif-redux'; +import { doesResourceExist } from 'iiif-redux/es/api/dereferenced'; +import { iiifResourceRequestUnknownAsync } from 'iiif-redux/es/spaces/iiif-resource'; +import { mappings as presentation2Mapping } from 'iiif-redux/es/api/2.x'; +import { mappings as presentation3Mapping } from 'iiif-redux/es/api/3.x'; +import DataLoader from 'dataloader'; const store = createStore(); +const iiifLoader = new DataLoader(keys => + Promise.all( + keys.map(id => store.dispatch(iiifResourceRequestUnknownAsync(id))) + ) +); + +function mapping(version) { + if (version === 3) { + return presentation3Mapping; + } + return presentation2Mapping; +} + +function select(selector) { + return selector(store.getState()); +} + +async function loadResource(id, type) { + return await iiifLoader.load(id); +} + +const selectById = (id, type) => { + return state => state.resources[type][id]; +}; + +function resourceExists(id, type) { + return select(doesResourceExist(selectById(id, type), type)); +} + +function fieldExists(id, type, field) { + const value = getFieldValue(id, type, field); + + return typeof value !== 'undefined' && value !== null; +} + +function resourceDereferenced(id, type) {} + +function getFieldValue(id, type, field) { + const version = getPresentationVersionFromResource( + select(selectById(id, type)) + ); + const api = mapping(version)(selectById(id, type)); + const singleField = api[field]; + return select(singleField); +} + +const combinedResolver = (id, type, field) => { + if (!resourceExists(id, type)) { + return loadResource(id, type).then(() => combinedResolver(id, type, field)); + } + if (!fieldExists(id, type, field)) { + if (resourceDereferenced(id, type)) { + return null; + } + return loadResource(id, type).then(() => combinedResolver(id, type, field)); + } + return getFieldValue(id, type, field); +}; + const context = req => ({ store, query: selector => { return selector(store.getState()); }, + combinedResolver, }); export default context; diff --git a/packages/iiif-graphql/src/resolvers/index.js b/packages/iiif-graphql/src/resolvers/index.js index 67af6d71..61282f7f 100644 --- a/packages/iiif-graphql/src/resolvers/index.js +++ b/packages/iiif-graphql/src/resolvers/index.js @@ -32,29 +32,6 @@ import { getSchemaVersionForResource } from 'iiif-redux/es/api/schema-version'; // - All of the above for parent resource // - Return resources as simple objects with ID field (and schema/type). Maybe? -function loadResource(id, type) {} - -function resourceExists(id, type) {} - -function fieldExists(id, type, field) {} - -function resourceDereferenced(id, type) {} - -function getFieldValue(id, type, field) {} - -const combinedResolver = (id, type, field) => { - if (!resourceExists(id, type)) { - return loadResource(id, type).then(() => combinedResolver(id, type, field)); - } - if (!fieldExists(id, type, field)) { - if (resourceDereferenced(id, type)) { - return null; - } - return loadResource(id, type).then(() => combinedResolver(id, type, field)); - } - return getFieldValue(id, type, field); -}; - const reduxResolver = (typeFunc, stateType) => apiFunc => ( data, opt, @@ -184,6 +161,8 @@ const resolvers = { Manifest: { type: () => 'Manifest', summary: manifestResolver('getSummary'), + // label: (obj, _, context) => + // context.combinedResolver(obj.id, 'manifest', 'label'), }, Collection: { type: () => 'Collection', diff --git a/packages/iiif-redux/src/api/2.x/annotation-list.js b/packages/iiif-redux/src/api/2.x/annotation-list.js index 938b1de2..e8b80285 100644 --- a/packages/iiif-redux/src/api/2.x/annotation-list.js +++ b/packages/iiif-redux/src/api/2.x/annotation-list.js @@ -13,6 +13,7 @@ import { import * as linking from './iiif/linking'; import * as paging from './iiif/paging'; import * as structural from './iiif/structural'; +import { standardFieldMappingFactory } from '../../utility/new/standardFieldMappingFactory'; const annotationList = memoize(selector => { /************************************************** @@ -50,7 +51,14 @@ const annotationList = memoize(selector => { const getAttribution = createSelector(selector, descriptive.getAttribution); - const getLogo = createSelector(selector, descriptive.getLogo); + const getLogoIds = createSelector(selector, descriptive.getLogo); + const getLogo = createSelector( + getLogoIds, + getAllImages, + (logoIds, allImages) => { + return logoIds.map(logoId => allImages[logoId] || logoId); + } + ); const getLicense = createSelector(selector, descriptive.getLicense); @@ -166,6 +174,7 @@ const annotationList = memoize(selector => { getDescription, getMetadata, getAttribution, + getLogoIds, getLogo, getLicense, getThumbnail, @@ -198,4 +207,6 @@ const annotationList = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(annotationList); + export default annotationList; diff --git a/packages/iiif-redux/src/api/2.x/annotation.js b/packages/iiif-redux/src/api/2.x/annotation.js index cb14af0b..797c53ee 100644 --- a/packages/iiif-redux/src/api/2.x/annotation.js +++ b/packages/iiif-redux/src/api/2.x/annotation.js @@ -12,6 +12,7 @@ import { } from '../all'; import * as linking from './iiif/linking'; import { isImageService } from '../../constants/services'; +import { standardFieldMappingFactory } from '../../utility/new/standardFieldMappingFactory'; const annotation = memoize(selector => { /************************************************** * Technical properties @@ -51,7 +52,14 @@ const annotation = memoize(selector => { const getAttribution = createSelector(selector, descriptive.getAttribution); - const getLogo = createSelector(selector, descriptive.getLogo); + const getLogoIds = createSelector(selector, descriptive.getLogo); + const getLogo = createSelector( + getLogoIds, + getAllImages, + (logoIds, allImages) => { + return logoIds.map(logoId => allImages[logoId] || logoId); + } + ); const getLicense = createSelector(selector, descriptive.getLicense); @@ -169,6 +177,7 @@ const annotation = memoize(selector => { getThumbnail, getAttribution, getLicense, + getLogoIds, getLogo, // Linking getSeeAlso, @@ -192,4 +201,6 @@ const annotation = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(annotation); + export default annotation; diff --git a/packages/iiif-redux/src/api/2.x/canvas.js b/packages/iiif-redux/src/api/2.x/canvas.js index de42f6eb..2cc37d4b 100644 --- a/packages/iiif-redux/src/api/2.x/canvas.js +++ b/packages/iiif-redux/src/api/2.x/canvas.js @@ -13,6 +13,7 @@ import { getAllServices, } from '../all'; import { isImageService } from '../../constants/services'; +import { standardFieldMappingFactory } from '../../utility/new/standardFieldMappingFactory'; const canvas = memoize(selector => { /************************************************** @@ -56,7 +57,14 @@ const canvas = memoize(selector => { const getAttribution = createSelector(selector, descriptive.getAttribution); - const getLogo = createSelector(selector, descriptive.getLogo); + const getLogoIds = createSelector(selector, descriptive.getLogo); + const getLogo = createSelector( + getLogoIds, + getAllImages, + (logoIds, allImages) => { + return logoIds.map(logoId => allImages[logoId] || logoId); + } + ); const getLicense = createSelector(selector, descriptive.getLicense); @@ -200,6 +208,7 @@ const canvas = memoize(selector => { getThumbnail, getAttribution, getLicense, + getLogoIds, getLogo, // Linking getWithinIds, @@ -222,4 +231,6 @@ const canvas = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(canvas); + export default canvas; diff --git a/packages/iiif-redux/src/api/2.x/collection.js b/packages/iiif-redux/src/api/2.x/collection.js index 2e0b99d7..117ffe7d 100644 --- a/packages/iiif-redux/src/api/2.x/collection.js +++ b/packages/iiif-redux/src/api/2.x/collection.js @@ -16,6 +16,7 @@ import { } from '../all'; import mapById from '../../utility/mapById'; import mapAllById from '../../utility/mapAllById'; +import { standardFieldMappingFactory } from '../../utility/new/standardFieldMappingFactory'; const collection = memoize(selector => { /************************************************** @@ -53,7 +54,14 @@ const collection = memoize(selector => { const getAttribution = createSelector(selector, descriptive.getAttribution); - const getLogo = createSelector(selector, descriptive.getLogo); + const getLogoIds = createSelector(selector, descriptive.getLogo); + const getLogo = createSelector( + getLogoIds, + getAllImages, + (logoIds, allImages) => { + return logoIds.map(logoId => allImages[logoId] || logoId); + } + ); const getLicense = createSelector(selector, descriptive.getLicense); @@ -200,6 +208,7 @@ const collection = memoize(selector => { getDescription, getMetadata, getAttribution, + getLogoIds, getLogo, getLicense, getThumbnailId, @@ -241,4 +250,6 @@ const collection = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(collection); + export default collection; diff --git a/packages/iiif-redux/src/api/2.x/external-resource.js b/packages/iiif-redux/src/api/2.x/external-resource.js index d3c51ee5..b12218c8 100644 --- a/packages/iiif-redux/src/api/2.x/external-resource.js +++ b/packages/iiif-redux/src/api/2.x/external-resource.js @@ -9,6 +9,7 @@ import { getAllLayers, getAllServices, } from '../all'; +import { standardFieldMappingFactory } from '../../../es/utility/new/standardFieldMappingFactory'; const externalResource = memoize(selector => { /************************************************** @@ -55,7 +56,14 @@ const externalResource = memoize(selector => { const getAttribution = createSelector(selector, descriptive.getAttribution); - const getLogo = createSelector(selector, descriptive.getLogo); + const getLogoIds = createSelector(selector, descriptive.getLogo); + const getLogo = createSelector( + getLogoIds, + getAllImages, + (logoIds, allImages) => { + return logoIds.map(logoId => allImages[logoId] || logoId); + } + ); const getLicense = createSelector(selector, descriptive.getLicense); @@ -138,6 +146,7 @@ const externalResource = memoize(selector => { getDescription, getMetadata, getAttribution, + getLogoIds, getLogo, getLicense, getThumbnail, @@ -156,4 +165,6 @@ const externalResource = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(externalResource); + export default externalResource; diff --git a/packages/iiif-redux/src/api/2.x/iiif/descriptive.js b/packages/iiif-redux/src/api/2.x/iiif/descriptive.js index 48abe4de..cb1a947d 100644 --- a/packages/iiif-redux/src/api/2.x/iiif/descriptive.js +++ b/packages/iiif-redux/src/api/2.x/iiif/descriptive.js @@ -26,12 +26,9 @@ const getLicense = resource => { const getLogo = resource => { if (!resource.logo) { - return null; - } - if (Array.isArray(resource.logo)) { - return resource.logo[0]['@id'] || resource.logo[0]; + return []; } - return resource.logo['@id'] || resource.logo; + return resource.logo; }; export { diff --git a/packages/iiif-redux/src/api/2.x/image-resource.js b/packages/iiif-redux/src/api/2.x/image-resource.js index 8b0dbe19..ea4974f0 100644 --- a/packages/iiif-redux/src/api/2.x/image-resource.js +++ b/packages/iiif-redux/src/api/2.x/image-resource.js @@ -9,6 +9,7 @@ import { getAllLayers, getAllServices, } from '../all'; +import { standardFieldMappingFactory } from '../../../es/utility/new/standardFieldMappingFactory'; const imageResource = memoize(selector => { /************************************************** @@ -55,7 +56,14 @@ const imageResource = memoize(selector => { const getAttribution = createSelector(selector, descriptive.getAttribution); - const getLogo = createSelector(selector, descriptive.getLogo); + const getLogoIds = createSelector(selector, descriptive.getLogo); + const getLogo = createSelector( + getLogoIds, + getAllImages, + (logoIds, allImages) => { + return logoIds.map(logoId => allImages[logoId] || logoId); + } + ); const getLicense = createSelector(selector, descriptive.getLicense); @@ -138,6 +146,7 @@ const imageResource = memoize(selector => { getDescription, getMetadata, getAttribution, + getLogoIds, getLogo, getLicense, getThumbnail, @@ -156,4 +165,6 @@ const imageResource = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(imageResource); + export default imageResource; diff --git a/packages/iiif-redux/src/api/2.x/index.js b/packages/iiif-redux/src/api/2.x/index.js new file mode 100644 index 00000000..d5021a2b --- /dev/null +++ b/packages/iiif-redux/src/api/2.x/index.js @@ -0,0 +1,44 @@ +import annotationList, { + mappings as annotationListMapping, +} from './annotation-list'; +import annotation, { mappings as annotationMapping } from './annotation'; +import canvas, { mappings as canvasMapping } from './canvas'; +import collection, { mappings as collectionMapping } from './collection'; +import externalResource, { + mappings as externalResourceMapping, +} from './external-resource'; +import imageResource, { + mappings as imageResourceMapping, +} from './image-resource'; +import manifest, { mappings as manifestMapping } from './manifest'; +import range, { mappings as rangeMapping } from './range'; +import sequence, { mappings as sequenceMapping } from './sequence'; + +export const mappings = { + annotationList: annotationListMapping, + annotation: annotationMapping, + canvas: canvasMapping, + collection: collectionMapping, + externalResource: externalResourceMapping, + imageResource: imageResourceMapping, + manifest: manifestMapping, + range: rangeMapping, + sequence: sequenceMapping, + + // Aliases. + annotationPage: annotationListMapping, + contentResources: imageResourceMapping, // @todo change when image/external get merged. +}; + +export { + annotationCollection, + annotationList, + annotation, + canvas, + collection, + externalResource, + imageResource, + manifest, + range, + sequence, +}; diff --git a/packages/iiif-redux/src/api/2.x/manifest.js b/packages/iiif-redux/src/api/2.x/manifest.js index 2829cd12..6cc00f1a 100644 --- a/packages/iiif-redux/src/api/2.x/manifest.js +++ b/packages/iiif-redux/src/api/2.x/manifest.js @@ -14,6 +14,7 @@ import { } from '../all'; import * as linking from './iiif/linking'; import * as structural from './iiif/structural'; +import { standardFieldMappingFactory } from '../../utility/new/standardFieldMappingFactory'; const manifest = memoize(selector => { /************************************************** @@ -59,7 +60,15 @@ const manifest = memoize(selector => { const getMetadata = createSelector(selector, descriptive.getMetadata); const getAttribution = createSelector(selector, descriptive.getAttribution); - const getLogo = createSelector(selector, descriptive.getLogo); + + const getLogoIds = createSelector(selector, descriptive.getLogo); + const getLogo = createSelector( + getLogoIds, + getAllImages, + (logoIds, allImages) => { + return logoIds.map(logoId => allImages[logoId] || logoId); + } + ); const getLicense = createSelector(selector, descriptive.getLicense); @@ -196,6 +205,7 @@ const manifest = memoize(selector => { getDescription, getMetadata, getAttribution, + getLogoIds, getLogo, getLicense, getThumbnailId, @@ -225,4 +235,6 @@ const manifest = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(manifest); + export default manifest; diff --git a/packages/iiif-redux/src/api/2.x/range.js b/packages/iiif-redux/src/api/2.x/range.js index 27f24b9d..c4d428f4 100644 --- a/packages/iiif-redux/src/api/2.x/range.js +++ b/packages/iiif-redux/src/api/2.x/range.js @@ -12,6 +12,7 @@ import { createSelector } from 'reselect'; import * as descriptive from './iiif/descriptive'; import * as linking from './iiif/linking'; import * as structural from './iiif/structural'; +import { standardFieldMappingFactory } from '../../../es/utility/new/standardFieldMappingFactory'; const range = memoize(selector => { /************************************************** @@ -60,7 +61,14 @@ const range = memoize(selector => { const getAttribution = createSelector(selector, descriptive.getAttribution); - const getLogo = createSelector(selector, descriptive.getLogo); + const getLogoIds = createSelector(selector, descriptive.getLogo); + const getLogo = createSelector( + getLogoIds, + getAllImages, + (logoIds, allImages) => { + return logoIds.map(logoId => allImages[logoId] || logoId); + } + ); const getLicense = createSelector(selector, descriptive.getLicense); @@ -195,6 +203,7 @@ const range = memoize(selector => { getThumbnailId, getAttribution, getLicense, + getLogoIds, getLogo, // linking. @@ -221,4 +230,6 @@ const range = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(range); + export default range; diff --git a/packages/iiif-redux/src/api/2.x/sequence.js b/packages/iiif-redux/src/api/2.x/sequence.js index c45d1a72..48585369 100644 --- a/packages/iiif-redux/src/api/2.x/sequence.js +++ b/packages/iiif-redux/src/api/2.x/sequence.js @@ -12,6 +12,7 @@ import { getAllCanvases, getAllImages, } from '../all'; +import { standardFieldMappingFactory } from '../../../es/utility/new/standardFieldMappingFactory'; const sequence = memoize(selector => { /************************************************** @@ -59,7 +60,14 @@ const sequence = memoize(selector => { const getAttribution = createSelector(selector, descriptive.getAttribution); - const getLogo = createSelector(selector, descriptive.getLogo); + const getLogoIds = createSelector(selector, descriptive.getLogo); + const getLogo = createSelector( + getLogoIds, + getAllImages, + (logoIds, allImages) => { + return logoIds.map(logoId => allImages[logoId] || logoId); + } + ); const getLicense = createSelector(selector, descriptive.getLicense); @@ -154,6 +162,7 @@ const sequence = memoize(selector => { getDescription, getMetadata, getAttribution, + getLogoIds, getLogo, getLicense, getThumbnailId, @@ -177,4 +186,6 @@ const sequence = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(sequence); + export default sequence; diff --git a/packages/iiif-redux/src/api/3.x/annotation-collection.js b/packages/iiif-redux/src/api/3.x/annotation-collection.js index cbf90044..9f3a8356 100644 --- a/packages/iiif-redux/src/api/3.x/annotation-collection.js +++ b/packages/iiif-redux/src/api/3.x/annotation-collection.js @@ -12,6 +12,7 @@ import { import mapByIdOrId from '../../utility/mapByIdOrId'; import mapAllResources from '../../utility/mapAllResources'; import mapAllById from '../../utility/mapAllById'; +import { standardFieldMappingFactory } from '../../utility/new/standardFieldMappingFactory'; const annotationCollection = memoize(selector => { /** @@ -138,4 +139,6 @@ const annotationCollection = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(annotationCollection); + export default annotationCollection; diff --git a/packages/iiif-redux/src/api/3.x/annotation-page.js b/packages/iiif-redux/src/api/3.x/annotation-page.js index fcdb1178..463ce494 100644 --- a/packages/iiif-redux/src/api/3.x/annotation-page.js +++ b/packages/iiif-redux/src/api/3.x/annotation-page.js @@ -14,6 +14,7 @@ import { import mapByIdOrId from '../../utility/mapByIdOrId'; import mapAllResources from '../../utility/mapAllResources'; import mapAllById from '../../utility/mapAllById'; +import { standardFieldMappingFactory } from '../../../es/utility/new/standardFieldMappingFactory'; const annotationPage = memoize(selector => { /** @@ -154,4 +155,6 @@ const annotationPage = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(annotationPage); + export default annotationPage; diff --git a/packages/iiif-redux/src/api/3.x/annotation.js b/packages/iiif-redux/src/api/3.x/annotation.js index 2fe93702..9b6fc161 100644 --- a/packages/iiif-redux/src/api/3.x/annotation.js +++ b/packages/iiif-redux/src/api/3.x/annotation.js @@ -15,6 +15,7 @@ import mapByIdOrId from '../../utility/mapByIdOrId'; import mapAllResources from '../../utility/mapAllResources'; import mapAllById from '../../utility/mapAllById'; import mapAllByIdOrId from '../../utility/mapAllByIdOrId'; +import { standardFieldMappingFactory } from '../../../es/utility/new/standardFieldMappingFactory'; const annotation = memoize(selector => { /** @@ -167,4 +168,6 @@ const annotation = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(annotation); + export default annotation; diff --git a/packages/iiif-redux/src/api/3.x/canvas.js b/packages/iiif-redux/src/api/3.x/canvas.js index 337db57d..68a1d48d 100644 --- a/packages/iiif-redux/src/api/3.x/canvas.js +++ b/packages/iiif-redux/src/api/3.x/canvas.js @@ -15,6 +15,7 @@ import { import mapByIdOrId from '../../utility/mapByIdOrId'; import mapAllResources from '../../utility/mapAllResources'; import mapAllById from '../../utility/mapAllById'; +import { standardFieldMappingFactory } from '../../utility/new/standardFieldMappingFactory'; const canvas = memoize(selector => { /** @@ -195,4 +196,6 @@ const canvas = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(canvas); + export default canvas; diff --git a/packages/iiif-redux/src/api/3.x/collection.js b/packages/iiif-redux/src/api/3.x/collection.js index 74118a22..2fefa405 100644 --- a/packages/iiif-redux/src/api/3.x/collection.js +++ b/packages/iiif-redux/src/api/3.x/collection.js @@ -17,6 +17,7 @@ import mapById from '../../utility/mapById'; import mapAllById from '../../utility/mapAllById'; import mapAllResources from '../../utility/mapAllResources'; import mapByIdOrId from '../../utility/mapByIdOrId'; +import { standardFieldMappingFactory } from '../../../es/utility/new/standardFieldMappingFactory'; const collection = memoize(selector => { /** @@ -219,4 +220,6 @@ const collection = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(collection); + export default collection; diff --git a/packages/iiif-redux/src/api/3.x/content-resource.js b/packages/iiif-redux/src/api/3.x/content-resource.js index ecf79658..084648d6 100644 --- a/packages/iiif-redux/src/api/3.x/content-resource.js +++ b/packages/iiif-redux/src/api/3.x/content-resource.js @@ -14,8 +14,9 @@ import { import mapByIdOrId from '../../utility/mapByIdOrId'; import mapAllResources from '../../utility/mapAllResources'; import mapAllById from '../../utility/mapAllById'; +import { standardFieldMappingFactory } from '../../../es/utility/new/standardFieldMappingFactory'; -const canvas = memoize(selector => { +const contentResource = memoize(selector => { /** * Technical properties * @@ -176,4 +177,6 @@ const canvas = memoize(selector => { }; }); -export default canvas; +export const mappings = standardFieldMappingFactory(contentResource); + +export default contentResource; diff --git a/packages/iiif-redux/src/api/3.x/index.js b/packages/iiif-redux/src/api/3.x/index.js new file mode 100644 index 00000000..7aa9326b --- /dev/null +++ b/packages/iiif-redux/src/api/3.x/index.js @@ -0,0 +1,36 @@ +import annotationCollection, { + mappings as annotationCollectionMapping, +} from './annotation-collection'; +import annotationPage, { + mappings as annotationPageMapping, +} from './annotation-page'; +import annotation, { mappings as annotationMapping } from './annotation'; +import canvas, { mappings as canvasMapping } from './canvas'; +import collection, { mappings as collectionMapping } from './collection'; +import contentResource, { + mappings as contentResourceMapping, +} from './content-resource'; +import manifest, { mappings as manifestMapping } from './manifest'; +import range, { mappings as rangeMapping } from './range'; + +export const mappings = { + annotationCollection: annotationCollectionMapping, + annotationPage: annotationPageMapping, + annotation: annotationMapping, + canvas: canvasMapping, + collection: collectionMapping, + contentResource: contentResourceMapping, + manifest: manifestMapping, + range: rangeMapping, +}; + +export { + annotationCollection, + annotationPage, + annotation, + canvas, + collection, + contentResource, + manifest, + range, +}; diff --git a/packages/iiif-redux/src/api/3.x/manifest.js b/packages/iiif-redux/src/api/3.x/manifest.js index 8541553f..878728c8 100644 --- a/packages/iiif-redux/src/api/3.x/manifest.js +++ b/packages/iiif-redux/src/api/3.x/manifest.js @@ -17,6 +17,7 @@ import { import mapByIdOrId from '../../utility/mapByIdOrId'; import mapAllResources from '../../utility/mapAllResources'; import mapAllById from '../../utility/mapAllById'; +import { standardFieldMappingFactory } from '../../../es/utility/new/standardFieldMappingFactory'; const manifest = memoize(selector => { /** @@ -216,4 +217,6 @@ const manifest = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(manifest); + export default manifest; diff --git a/packages/iiif-redux/src/api/3.x/range.js b/packages/iiif-redux/src/api/3.x/range.js index 13d225d0..948dd029 100644 --- a/packages/iiif-redux/src/api/3.x/range.js +++ b/packages/iiif-redux/src/api/3.x/range.js @@ -20,6 +20,7 @@ import { import mapByIdOrId from '../../utility/mapByIdOrId'; import mapAllResources from '../../utility/mapAllResources'; import mapAllById from '../../utility/mapAllById'; +import { standardFieldMappingFactory } from '../../../es/utility/new/standardFieldMappingFactory'; const range = memoize(selector => { /** @@ -255,4 +256,6 @@ const range = memoize(selector => { }; }); +export const mappings = standardFieldMappingFactory(range); + export default range; diff --git a/packages/iiif-redux/src/utility/new/standardFieldMappingFactory.js b/packages/iiif-redux/src/utility/new/standardFieldMappingFactory.js new file mode 100644 index 00000000..eb78ab61 --- /dev/null +++ b/packages/iiif-redux/src/utility/new/standardFieldMappingFactory.js @@ -0,0 +1,122 @@ +import memoize from 'lodash.memoize'; + +function optional(func, msg) { + if (func) return func; + return () => { + throw new Error(msg); + }; +} + +function nullable(func) { + if (func) return func; + return () => null; +} + +function fallbacks(options) { + // Find first non-null option. + return options.find(Boolean); +} + +function alias(from, to, obj) { + obj[to] = obj[from]; +} + +export function standardFieldMappingFactory(apiFactory, aliases = true) { + return memoize(selector => { + const api = apiFactory(selector); + + const fieldMapping = { + // Technical properties. + id: api.getId, + type: api.getType, + format: optional(api.getFormat), + profile: optional(api.getProfile), + height: optional(api.getHeight), + width: optional(api.getWidth), + height: optional(api.getHeight), + duration: optional(api.getDuration), + viewingDirection: optional(api.getViewingDirection), + behaviour: optional(api.getBehaviour), + timeMode: optional(api.getTimeMode), + // Descriptive properties. + label: api.getLabel, + metadata: api.getMetadata, + summary: fallbacks([api.getSummary, api.getDescription]), + thumbnailId: api.getThumbnailId, + thumbnail: api.getThumbnail, + posterCanvas: optional(api.getPosterCanvas), + requiredStatement: fallbacks([ + api.getRequiredStatement, + api.getAttribution, + ]), + rights: fallbacks([api.getRights, api.getLicense]), + navDate: optional(api.getNavDate), + language: optional(api.getLanguage), + // Linking properties + seeAlsoIds: api.getSeeAlsoIds, + seeAlso: api.getSeeAlso, + serviceIds: api.getServiceIds, + service: api.getService, + logoId: api.getLogoIds, + logo: api.getLogo, + homepageIds: fallbacks([api.getHomepageId, api.getRelatedIds]), + homepage: fallbacks([api.getHomepage, api.getRelated]), + renderingIds: api.getRenderingIds, + rendering: api.getRendering, + partOfId: fallbacks([api.getPartOfId, api.getWithinIds]), + partOf: fallbacks([api.getPartOf, api.getWithin]), + startId: optional(fallbacks([api.getStartId, api.getStartCanvasId])), + start: optional(fallbacks([api.getStart, api.getStartCanvas])), + supplementary: nullable(api.getSupplementary), // No equivelent in Presentation 2. + // Structural. + itemIds: optional( + fallbacks([ + api.getItems, + api.getMembers, + api.getSequences, + api.getCanvases, + api.getResources, + api.getImages, + api.getRanges, + ]) + ), + items: optional( + fallbacks([ + api.getItems, + api.getMembers, + api.getSequences, + api.getCanvases, + api.getResources, + api.getImages, + api.getRanges, + ]) + ), + // Part of the core API, as items isn't as descriptive. + collectionIds: optional(api.getCollectionIds), + collections: optional(api.getCollections), + manifestIds: optional(api.getManifestIds), + manifests: optional(api.getManifests), + canvasIds: optional(api.getCanvasIds), + canvases: optional(api.getCanvases), + + structreIds: optional(fallbacks([api.getStructureIds, api.getRangeIds])), + structures: optional(fallbacks([api.getStructures, api.getRanges])), + // This may conflict (painting vs non-painting between 2.x to 3.x) + annotationIds: optional(api.getAnnotationIds), + annotations: optional(api.getAnnotations), + }; + + if (aliases) { + // QoL aliases. + alias('service', 'services', fieldMapping); + alias('rights', 'license', fieldMapping); + alias('rights', 'licence', fieldMapping); + alias('homepage', 'homepages', fieldMapping); + alias('rendering', 'renderings', fieldMapping); + alias('structures', 'ranges', fieldMapping); + alias('structureIds', 'rangeIds', fieldMapping); + } + + return fieldMapping; + }); +} diff --git a/yarn.lock b/yarn.lock index 2329b177..88d8860a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1278,9 +1278,9 @@ dependencies: "@types/node" "*" -"@types/async@2.0.49": - version "2.0.49" - resolved "http://registry.npmjs.org/@types/async/-/async-2.0.49.tgz#92e33d13f74c895cb9a7f38ba97db8431ed14bc0" +"@types/async@2.0.50": + version "2.0.50" + resolved "https://registry.yarnpkg.com/@types/async/-/async-2.0.50.tgz#117540e026d64e1846093abbd5adc7e27fda7bcb" "@types/body-parser@*", "@types/body-parser@1.17.0": version "1.17.0" @@ -1958,33 +1958,33 @@ apollo-cache-control@^0.2.5: apollo-server-env "^2.0.3" graphql-extensions "^0.2.1" -apollo-cache-inmemory@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.3.5.tgz#7628ee598271cfffa611e15a7ce67652a84b07b0" +apollo-cache-inmemory@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.3.7.tgz#a9dabead5939e19068040c97c7c1009619a9df37" dependencies: - apollo-cache "^1.1.17" - apollo-utilities "^1.0.21" + apollo-cache "^1.1.19" + apollo-utilities "^1.0.24" optimism "^0.6.6" -apollo-cache@1.1.17, apollo-cache@^1.1.17: - version "1.1.17" - resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.1.17.tgz#1fcca8423125223723b97fd72808be91a1a76490" +apollo-cache@1.1.19, apollo-cache@^1.1.19: + version "1.1.19" + resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.1.19.tgz#a9ff2b6c70ab888c29ea038d8642b5698f115a3a" dependencies: - apollo-utilities "^1.0.21" + apollo-utilities "^1.0.24" -apollo-client@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-2.4.2.tgz#d2f044d8740723bf98a6d8d8b9684ee8c36150e6" +apollo-client@^2.4.4: + version "2.4.4" + resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-2.4.4.tgz#b96d4fc2dd856b24552bb2e2da466219b0d4eb1f" dependencies: "@types/zen-observable" "^0.8.0" - apollo-cache "1.1.17" + apollo-cache "1.1.19" apollo-link "^1.0.0" apollo-link-dedup "^1.0.0" - apollo-utilities "1.0.21" + apollo-utilities "1.0.24" symbol-observable "^1.0.2" zen-observable "^0.8.0" optionalDependencies: - "@types/async" "2.0.49" + "@types/async" "2.0.50" apollo-datasource@^0.1.3: version "0.1.3" @@ -2116,7 +2116,13 @@ apollo-tracing@^0.2.5: apollo-server-env "^2.0.3" graphql-extensions "^0.2.1" -apollo-utilities@1.0.21, apollo-utilities@^1.0.0, apollo-utilities@^1.0.1, apollo-utilities@^1.0.21: +apollo-utilities@1.0.24, apollo-utilities@^1.0.24: + version "1.0.24" + resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.0.24.tgz#77d2d3008bb9ee52aa76b799ce7a8d6cb6cf8a1c" + dependencies: + fast-json-stable-stringify "^2.0.0" + +apollo-utilities@^1.0.0, apollo-utilities@^1.0.1: version "1.0.21" resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.0.21.tgz#cb8b5779fe275850b16046ff8373f4af2de90765" dependencies: @@ -7181,9 +7187,9 @@ graphql-tools@^3.0.4: iterall "^1.1.3" uuid "^3.1.0" -graphql-tools@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-4.0.1.tgz#c995a4e25c2967d108c975e508322d12969c8c0e" +graphql-tools@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-4.0.2.tgz#9da22974cc6bf6524ed4f4af35556fd15aa6516d" dependencies: apollo-link "^1.2.3" apollo-utilities "^1.0.1" @@ -12880,7 +12886,7 @@ react-deep-force-update@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/react-deep-force-update/-/react-deep-force-update-1.1.1.tgz#bcd31478027b64b3339f108921ab520b4313dc2c" -react-dom@16.6.0: +react-dom@16.6.0, react-dom@^16.6.0: version "16.6.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.6.0.tgz#6375b8391e019a632a89a0988bce85f0cc87a92f" dependencies: @@ -12898,15 +12904,6 @@ react-dom@^16.3.2: object-assign "^4.1.1" prop-types "^15.6.0" -react-dom@^16.5.2: - version "16.5.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.5.2.tgz#b69ee47aa20bab5327b2b9d7c1fe2a30f2cfa9d7" - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - schedule "^0.5.0" - react-draggable@^3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-3.0.5.tgz#c031e0ed4313531f9409d6cd84c8ebcec0ddfe2d" @@ -13028,7 +13025,7 @@ react-transform-hmr@1.0.4: global "^4.3.0" react-proxy "^1.1.7" -react@16.6.0: +react@16.6.0, react@^16.6.0: version "16.6.0" resolved "https://registry.yarnpkg.com/react/-/react-16.6.0.tgz#b34761cfaf3e30f5508bc732fb4736730b7da246" dependencies: @@ -13046,15 +13043,6 @@ react@^16.3.2: object-assign "^4.1.1" prop-types "^15.6.0" -react@^16.5.2: - version "16.5.2" - resolved "https://registry.yarnpkg.com/react/-/react-16.5.2.tgz#19f6b444ed139baa45609eee6dc3d318b3895d42" - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - schedule "^0.5.0" - read-cmd-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.1.tgz#2d5d157786a37c055d22077c32c53f8329e91c7b" @@ -13833,12 +13821,6 @@ sax@^1.2.4, sax@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" -schedule@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/schedule/-/schedule-0.5.0.tgz#c128fffa0b402488b08b55ae74bb9df55cc29cc8" - dependencies: - object-assign "^4.1.1" - scheduler@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.10.0.tgz#7988de90fe7edccc774ea175a783e69c40c521e1"