From 03cce11d861e6d3b13a020cbcb17248074fefea9 Mon Sep 17 00:00:00 2001 From: Stefan Peters Date: Fri, 29 Nov 2024 11:54:00 +0100 Subject: [PATCH] Add support for multiple JSKOS API backends (#55) --- README.md | 2 +- src/client/store.js | 45 ++++++++++++++++++++++++++++------- src/client/views/HomeView.vue | 8 ++++--- src/client/views/ItemView.vue | 3 ++- src/server/backend.js | 38 ++++++++++++++++++++++------- src/server/server.js | 7 ++++++ 6 files changed, 81 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index e409e41..96d0017 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Instances of jskos-proxy are configured with environment variables, in local fil - `NAMESPACE` - URI namespace of all objects served via this proxy. Must end with a slash (default: `http://example.org/`) - `BASE` - Path under which the application will be hosted on. Must end with a slash. (Default: `/`) - If `NAMESPACE` is `http://example.org/some-path/`, but `http://example.org/` itself is not served by jskos-proxy, you need to set `BASE` to `/some-path/`. -- `BACKEND` - JSKOS API base URL +- `BACKEND` - JSKOS API base URLs (seperated by `,`) - `TITLE` - Title of the service (default `JSKOS Proxy`) - `QUICK_SELECTION` - comma separated list of vocabulary URIs to prominently show at the start page diff --git a/src/client/store.js b/src/client/store.js index c7482f0..3e11364 100644 --- a/src/client/store.js +++ b/src/client/store.js @@ -44,6 +44,13 @@ export function setLocale(value) { } } +import { cdk } from "cocoda-sdk" + +export const registries = config.backend.split(",").map(base => cdk.initializeRegistry({ + provider: "ConceptApi", + status: `${base}status`, +})) + export const schemeFetchPromise = fetch( config.namespace.pathname, { @@ -51,7 +58,18 @@ export const schemeFetchPromise = fetch( signal: AbortSignal.timeout(20000), }, ).then(res => res.json()).then(data => { - state.schemes = data + state.schemes = data.map(scheme => { + scheme = new jskos.ConceptScheme(scheme) + // Add _registry to scheme (via special REGISTRY field provided by backend) + scheme._registry = registries.find(registry => registry._api?.status === scheme.REGISTRY?.status) + // If there's an identifier with the current namespace, use it as the main identifier + const identifier = (scheme.identifier || []).find(i => i.startsWith(config.namespace)) + if (identifier) { + scheme.identifier.push(scheme.uri) + scheme.uri = identifier + } + return scheme + }) }).catch(() => { console.error("Error loading schemes from backend.") // TODO: Add retry mechanism @@ -61,13 +79,16 @@ export const schemeFetchPromise = fetch( export const schemes = computed(() => state.schemes) export const schemesAsConceptSchemes = computed(() => state.schemes?.map(scheme => new jskos.ConceptScheme(scheme)) || []) -import { cdk } from "cocoda-sdk" +export async function getRegistryForScheme(scheme) { + await schemeFetchPromise + return state.schemes.find(s => jskos.compare(s, scheme))?._registry +} -export const registry = cdk.initializeRegistry({ - provider: "ConceptApi", - // ? Does "config.backend" always have a trailing slash? - status: `${config.backend}status`, -}) +export async function getRegistryForUri(uri) { + await schemeFetchPromise + // Find scheme where URI matches namespace + return await getRegistryForScheme(state.schemes.find(scheme => scheme.notationFromUri(uri))) +} export function getConcept(concept) { for (const uri of jskos.getAllUris(concept)) { @@ -142,6 +163,10 @@ export function saveConceptsWithOptions(options) { let properties export async function loadConcept(uri) { + const registry = await getRegistryForUri(uri) + if (!registry) { + return null + } if (!properties) { // Adjust properties for concept details properties = registry._defaultParams?.properties || "" @@ -169,7 +194,11 @@ export async function loadTop(scheme) { return scheme?.topConcepts } console.time(`loadTop ${scheme.uri}`) - const topConcepts = await registry.getTop({ scheme: { uri: scheme.uri }, params: { properties: "" } }) + const registry = await getRegistryForScheme(scheme) + if (!registry) { + return null + } + const topConcepts = await registry.getTop({ scheme: { uri: scheme.uri, identifier: scheme.identifier }, params: { properties: "" } }) topConcepts.forEach(concept => { concept.ancestors = [] }) diff --git a/src/client/views/HomeView.vue b/src/client/views/HomeView.vue index 7274a32..1998bbc 100644 --- a/src/client/views/HomeView.vue +++ b/src/client/views/HomeView.vue @@ -1,7 +1,7 @@