From 297df4529ab9c819afb3632f2f9fba47b55828ba Mon Sep 17 00:00:00 2001 From: David de Boer Date: Sat, 20 Jul 2024 20:01:37 +0200 Subject: [PATCH] feat: Add genres to GraphQL API (#1397) --- jest.config.js | 16 +- package-lock.json | 358 ++++++++++++++---- package.json | 2 +- .../network-of-terms-catalog/package.json | 2 + .../network-of-terms-catalog/src/genre.ts | 50 +++ .../network-of-terms-catalog/src/index.ts | 1 + .../test/catalog.test.ts | 2 +- .../test/shacl.test.ts | 4 +- packages/network-of-terms-graphql/README.md | 4 + .../network-of-terms-graphql/src/resolvers.ts | 21 +- .../network-of-terms-graphql/src/schema.ts | 10 +- .../test/server.test.ts | 14 +- .../src/helpers/logger.ts | 2 +- packages/network-of-terms-query/src/query.ts | 2 +- .../network-of-terms-query/test/query.test.ts | 4 +- .../test/search/query-mode.test.ts | 2 +- .../test/score.test.ts | 2 +- .../test/server.test.ts | 10 +- tsconfig.build.json | 6 +- 19 files changed, 400 insertions(+), 112 deletions(-) create mode 100644 packages/network-of-terms-catalog/src/genre.ts diff --git a/jest.config.js b/jest.config.js index 38e90394c..b21bc3b8b 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,4 @@ export default { - preset: 'ts-jest/presets/default-esm', testTimeout: 60000, extensionsToTreatAsEsm: ['.ts'], collectCoverage: true, @@ -11,19 +10,14 @@ export default { ], coverageThreshold: { global: { - lines: 91.33, - statements: 91.33, - branches: 95.97, - functions: 92.37, + lines: 91.38, + statements: 91.38, + branches: 95.08, + functions: 92.56, }, }, transform: { - '^.+\\.ts?$': [ - 'ts-jest', - { - useESM: true, - }, - ], + '^.+\\.ts$': '@swc/jest', }, moduleNameMapper: { '^@netwerk-digitaal-erfgoed/(.*)$': '/packages/$1/src/', diff --git a/package-lock.json b/package-lock.json index b33ffe971..77bbd56a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,13 +13,13 @@ "@commitlint/config-conventional": "^19.2.2", "@qiwi/multi-semantic-release": "^5.0.0", "@rdfjs/types": "^1.1.0", + "@swc/jest": "^0.2.36", "@types/jest": "^29.5.12", "@types/node": "^20.14.10", "gts": "^5.3.1", "husky": "^9.0.11", "jest": "^29.7.0", "jest-coverage-thresholds-bumper": "^1.1.0", - "ts-jest": "^29.2.2", "typescript": "^5.5.3" }, "engines": { @@ -6564,6 +6564,18 @@ } } }, + "node_modules/@jest/create-cache-key-function": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", + "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jest/environment": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", @@ -8120,6 +8132,248 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/@swc/core": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.0.tgz", + "integrity": "sha512-d4vMzH6ICllDwlPuhset2h8gu/USHdbyfJim+2hQEdxC0UONtfpmu38XBgNqRjStrji1Q5M10jfeUZL3cu1i8g==", + "dev": true, + "hasInstallScript": true, + "peer": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.9" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.7.0", + "@swc/core-darwin-x64": "1.7.0", + "@swc/core-linux-arm-gnueabihf": "1.7.0", + "@swc/core-linux-arm64-gnu": "1.7.0", + "@swc/core-linux-arm64-musl": "1.7.0", + "@swc/core-linux-x64-gnu": "1.7.0", + "@swc/core-linux-x64-musl": "1.7.0", + "@swc/core-win32-arm64-msvc": "1.7.0", + "@swc/core-win32-ia32-msvc": "1.7.0", + "@swc/core-win32-x64-msvc": "1.7.0" + }, + "peerDependencies": { + "@swc/helpers": "*" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.0.tgz", + "integrity": "sha512-2ylhM7f0HwUwLrFYZAe/dse8PCbPsYcJS3Dt7Q8NT3PUn7vy6QOMxNcOPPuDrnmaXqQQO3oxdmRapguTxaat9g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.0.tgz", + "integrity": "sha512-SgVnN4gT1Rb9YfTkp4FCUITqSs7Yj0uB2SUciu5CV3HuGvS5YXCUzh+KrwpLFtx8NIgivISKcNnb41mJi98X8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.0.tgz", + "integrity": "sha512-+Z9Dayart1iKJQEJJ9N/KS4z5EdXJE3WPFikY0jonKTo4Dd8RuyVz5yLvqcIMeVdz/SwximATaL6iJXw7hZS9A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.0.tgz", + "integrity": "sha512-UnLrCiZ1EI4shznJn0xP6DLgsXUSwtfsdgHhGYCrvbgVBBve3S9iFgVFEB3SPl7Q/TdowNbrN4zHU0oChfiNfw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.0.tgz", + "integrity": "sha512-H724UANA+ptsfwKRr9mnaDa9cb5fw0oFysiGKTgb3DMYcgk3Od0jMTnXVPFSVpo7FlmyxeC9K8ueUPBOoOK6XA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.0.tgz", + "integrity": "sha512-SY3HA0K0Dpqt1HIfMLGpwL4hd4UaL2xHP5oZXPlRQPhUDZrbb4PbI3ZJnh66c63eL4ZR8EJ+HRFI0Alx5p69Zw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.0.tgz", + "integrity": "sha512-cEJ2ebtV1v/5Ilb55E05J6F5SrHKQWzUttIhR5Mkayyo+yvPslcpByuFC3D+J7X1ebziTOBpWuMpUdjLfh3SMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.0.tgz", + "integrity": "sha512-ecQOOmzEssz+m0pR4xDYCGuvn3E/l0nQ3tk5jp1NA1lsAy4bMV0YbYCHjptYvWL/UjhIerIp3IlCJ8x5DodSog==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.0.tgz", + "integrity": "sha512-gz81seZkRn3zMnVOc7L5k6F4vQC82gIxmHiL+GedK+A37XI/X26AASU3zxvORnqQbwQYXQ+AEVckxBmFlz3v2g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.0.tgz", + "integrity": "sha512-b5Fd1xEOw9uqBpj2lqsaR4Iq9UhiL84hNDcEsi6DQA7Y1l85waQAslTbS0E4/pJ1PISAs0jW0zIGLco1eaWBOg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true + }, + "node_modules/@swc/jest": { + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.36.tgz", + "integrity": "sha512-8X80dp81ugxs4a11z1ka43FPhP+/e+mJNXJSxiNYk8gIX/jPBtY4gQTrKu/KIoco8bzKuPI5lUxjfLiGsfvnlw==", + "dev": true, + "dependencies": { + "@jest/create-cache-key-function": "^29.7.0", + "@swc/counter": "^0.1.3", + "jsonc-parser": "^3.2.0" + }, + "engines": { + "npm": ">= 7.0.0" + }, + "peerDependencies": { + "@swc/core": "*" + } + }, + "node_modules/@swc/types": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.9.tgz", + "integrity": "sha512-qKnCno++jzcJ4lM4NTfYifm1EFSCeIfKiAHAfkENZAV5Kl9PjJIyd2yeeVv6c/2CckuLyv2NmRC5pv6pm2WQBg==", + "dev": true, + "peer": true, + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, "node_modules/@tpluscode/rdf-ns-builders": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@tpluscode/rdf-ns-builders/-/rdf-ns-builders-4.1.0.tgz", @@ -9357,18 +9611,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -13823,6 +14065,12 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true + }, "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -14382,6 +14630,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/memoize": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.0.0.tgz", + "integrity": "sha512-H6cBLgsi6vMWOcCpvVCdFFnl3kerEXbrYh9q+lY6VXvQSmM6CkmV08VOwT+WE2tzIEqRPFfAq3fm4v/UIW6mSA==", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/memoize?sponsor=1" + } + }, "node_modules/meow": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", @@ -14564,6 +14826,17 @@ "node": ">=6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -20110,63 +20383,6 @@ "node": ">= 14.0.0" } }, - "node_modules/ts-jest": { - "version": "29.2.2", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.2.tgz", - "integrity": "sha512-sSW7OooaKT34AAngP6k1VS669a0HdLxkQZnlC7T76sckGCokXFnvJ3yRlQZGRTAoV5K19HfSgCiSwWOSIfcYlg==", - "dev": true, - "dependencies": { - "bs-logger": "0.x", - "ejs": "^3.0.0", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -20884,9 +21100,11 @@ "license": "EUPL-1.2", "dependencies": { "@comunica/bindings-factory": "^3.2.0", + "@comunica/query-sparql": "^3.2.0", "@comunica/query-sparql-rdfjs": "^3.2.0", "@netwerk-digitaal-erfgoed/network-of-terms-query": "*", "globby": "^14.0.2", + "memoize": "^10.0.0", "rdf-parse": "^2.3.3", "rdf-store-stream": "^2.0.1" }, diff --git a/package.json b/package.json index 59e807f09..b76a116f6 100644 --- a/package.json +++ b/package.json @@ -10,13 +10,13 @@ "@commitlint/config-conventional": "^19.2.2", "@qiwi/multi-semantic-release": "^5.0.0", "@rdfjs/types": "^1.1.0", + "@swc/jest": "^0.2.36", "@types/jest": "^29.5.12", "@types/node": "^20.14.10", "gts": "^5.3.1", "husky": "^9.0.11", "jest": "^29.7.0", "jest-coverage-thresholds-bumper": "^1.1.0", - "ts-jest": "^29.2.2", "typescript": "^5.5.3" }, "scripts": { diff --git a/packages/network-of-terms-catalog/package.json b/packages/network-of-terms-catalog/package.json index 020446ee6..5dbd4d393 100644 --- a/packages/network-of-terms-catalog/package.json +++ b/packages/network-of-terms-catalog/package.json @@ -43,9 +43,11 @@ "homepage": "https://github.com/netwerk-digitaal-erfgoed/network-of-terms-catalog#readme", "dependencies": { "@comunica/bindings-factory": "^3.2.0", + "@comunica/query-sparql": "^3.2.0", "@comunica/query-sparql-rdfjs": "^3.2.0", "@netwerk-digitaal-erfgoed/network-of-terms-query": "*", "globby": "^14.0.2", + "memoize": "^10.0.0", "rdf-parse": "^2.3.3", "rdf-store-stream": "^2.0.1" } diff --git a/packages/network-of-terms-catalog/src/genre.ts b/packages/network-of-terms-catalog/src/genre.ts new file mode 100644 index 000000000..51ce3688e --- /dev/null +++ b/packages/network-of-terms-catalog/src/genre.ts @@ -0,0 +1,50 @@ +import {IRI} from '@netwerk-digitaal-erfgoed/network-of-terms-query'; +import {QueryEngine} from '@comunica/query-sparql'; +import memoize from 'memoize'; + +const queryEngine = new QueryEngine(); + +export class Genre { + constructor( + public readonly iri: IRI, + public readonly name: string + ) {} +} + +const doDereferenceGenre = async (genre: IRI): Promise => { + // We have to fetch first using a single Accept header value of application/rdf+xml, because the Poolparty server + // does not properly handle complex Accept headers such as those sent by Comunica. + const result = await fetch(genre.toString(), { + headers: { + Accept: 'application/rdf+xml', + }, + }); + const xml = await result.text(); + try { + const data = await queryEngine.queryBindings( + `SELECT ?prefLabel WHERE { + ?s a ; + ?prefLabel . + } LIMIT 1`, + { + sources: [ + { + type: 'serialized', + value: xml, + mediaType: 'application/rdf+xml', + }, + ], + } + ); + const bindings = await data.toArray({limit: 1}); + return new Genre(genre, bindings[0].get('prefLabel')?.value ?? ''); + } catch (error) { + console.error(error); + return null; + } +}; + +export const dereferenceGenre = memoize(doDereferenceGenre, { + cacheKey: String, + maxAge: 86400, +}); diff --git a/packages/network-of-terms-catalog/src/index.ts b/packages/network-of-terms-catalog/src/index.ts index d11229829..cf9e655f4 100644 --- a/packages/network-of-terms-catalog/src/index.ts +++ b/packages/network-of-terms-catalog/src/index.ts @@ -1 +1,2 @@ export * from './getCatalog.js'; +export * from './genre.js'; diff --git a/packages/network-of-terms-catalog/test/catalog.test.ts b/packages/network-of-terms-catalog/test/catalog.test.ts index cd6809c73..4c4c8f5c1 100644 --- a/packages/network-of-terms-catalog/test/catalog.test.ts +++ b/packages/network-of-terms-catalog/test/catalog.test.ts @@ -6,7 +6,7 @@ import { IRI, SparqlDistribution, } from '@netwerk-digitaal-erfgoed/network-of-terms-query'; -import {getCatalog, fromFile, fromStore} from '../src'; +import {getCatalog, fromFile, fromStore} from '../src/index.js'; import {dirname, resolve} from 'path'; import {fileURLToPath} from 'url'; diff --git a/packages/network-of-terms-catalog/test/shacl.test.ts b/packages/network-of-terms-catalog/test/shacl.test.ts index b1729afab..cdb8ae6fe 100644 --- a/packages/network-of-terms-catalog/test/shacl.test.ts +++ b/packages/network-of-terms-catalog/test/shacl.test.ts @@ -3,9 +3,9 @@ import * as fs from 'fs'; import rdf from 'rdf-ext'; import SHACLValidator from 'rdf-validate-shacl'; import ValidationReport from 'rdf-validate-shacl'; -import DatasetExt from 'rdf-ext/lib/Dataset'; import {dirname, resolve} from 'path'; import {fileURLToPath} from 'url'; +import {DatasetCore} from '@rdfjs/types'; describe('Dataset', () => { it('validates against SHACL', async () => { @@ -42,7 +42,7 @@ describe('Dataset', () => { const listDatasets = () => fs.readdirSync(catalogPath + '/datasets'); -const containsDatasetNode = (data: DatasetExt): boolean => +const containsDatasetNode = (data: DatasetCore): boolean => data.match( null, rdf.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), diff --git a/packages/network-of-terms-graphql/README.md b/packages/network-of-terms-graphql/README.md index c70c7ebc9..beea01283 100644 --- a/packages/network-of-terms-graphql/README.md +++ b/packages/network-of-terms-graphql/README.md @@ -71,6 +71,10 @@ query Sources { name alternateName } + genres { + uri + name + } features { type url diff --git a/packages/network-of-terms-graphql/src/resolvers.ts b/packages/network-of-terms-graphql/src/resolvers.ts index 5b6dff6d6..031fe6045 100644 --- a/packages/network-of-terms-graphql/src/resolvers.ts +++ b/packages/network-of-terms-graphql/src/resolvers.ts @@ -22,6 +22,7 @@ import { TimeoutError, } from '@netwerk-digitaal-erfgoed/network-of-terms-query'; import * as RDF from '@rdfjs/types'; +import {dereferenceGenre} from '@netwerk-digitaal-erfgoed/network-of-terms-catalog'; // eslint-disable-next-line @typescript-eslint/no-explicit-any async function listSources(object: any, args: any, context: any): Promise { @@ -138,7 +139,7 @@ function term(term: Term) { }; } -function source(distribution: Distribution, dataset: Dataset) { +async function source(distribution: Distribution, dataset: Dataset) { return { uri: distribution.iri, name: dataset.name, @@ -151,14 +152,16 @@ function source(distribution: Distribution, dataset: Dataset) { name: creator.name, alternateName: creator.alternateName, })), - features: distribution.features.map((feature: Feature) => { - return { - type: Object.entries(FeatureType).find( - ([_, val]) => val === feature.type - )?.[0], - url: feature.url.toString(), - }; - }), + genres: dataset.genres.map(async genre => ({ + uri: genre.toString(), + name: (await dereferenceGenre(genre))?.name ?? 'Unknown', + })), + features: distribution.features.map((feature: Feature) => ({ + type: Object.entries(FeatureType).find( + ([_, val]) => val === feature.type + )?.[0], + url: feature.url.toString(), + })), }; } diff --git a/packages/network-of-terms-graphql/src/schema.ts b/packages/network-of-terms-graphql/src/schema.ts index 1556f5497..da85f87b5 100644 --- a/packages/network-of-terms-graphql/src/schema.ts +++ b/packages/network-of-terms-graphql/src/schema.ts @@ -9,6 +9,7 @@ export const schema = ` description: String! creators: [Creator]! features: [Feature]! + genres: [Genre]! inLanguage: [String]! mainEntityOfPage: [String]! } @@ -34,7 +35,14 @@ export const schema = ` "Reconciliation Service API" RECONCILIATION } - + + """ + A genre (category) that a source provides terms about. + """ + type Genre { + uri: ID! + name: String! + } """ A description of a concept or entity, expressed in the SKOS vocabulary, used to describe objects. diff --git a/packages/network-of-terms-graphql/test/server.test.ts b/packages/network-of-terms-graphql/test/server.test.ts index 454abac5a..175bdd51d 100644 --- a/packages/network-of-terms-graphql/test/server.test.ts +++ b/packages/network-of-terms-graphql/test/server.test.ts @@ -1,11 +1,11 @@ import {FastifyInstance} from 'fastify'; -import {server} from '../src/server'; -import {config} from '../src/config'; +import {server} from '../src/server.js'; +import {config} from '../src/config.js'; import { startDistributionSparqlEndpoint, testCatalog, teardown, -} from '../../network-of-terms-query/src/server-test'; +} from '../../network-of-terms-query/src/server-test.js'; let httpServer: FastifyInstance; const catalog = testCatalog(3000); @@ -32,6 +32,10 @@ describe('Server', () => { name alternateName } + genres { + uri + name + } features { type url @@ -51,6 +55,10 @@ describe('Server', () => { type: 'RECONCILIATION', url: 'https://example.com/reconcile/rkd', }); + expect(body.data.sources[0].genres).toContainEqual({ + uri: 'https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Personen', + name: 'Personen', + }); }); it('responds to GraphQL terms query when source does not exist', async () => { diff --git a/packages/network-of-terms-query/src/helpers/logger.ts b/packages/network-of-terms-query/src/helpers/logger.ts index fb405219d..cfdbb1d4f 100644 --- a/packages/network-of-terms-query/src/helpers/logger.ts +++ b/packages/network-of-terms-query/src/helpers/logger.ts @@ -38,7 +38,7 @@ export function getCliLogger(options: GetLoggerOptions): Pino.Logger { }, }); const destinationStdErr = Pino.destination(2); - return Pino(loggerOptions, destinationStdErr); + return Pino.pino(loggerOptions, destinationStdErr); } export function getHttpLogger(options: GetLoggerOptions): Pino.LoggerOptions { diff --git a/packages/network-of-terms-query/src/query.ts b/packages/network-of-terms-query/src/query.ts index 22883ec3f..f623b9ec1 100644 --- a/packages/network-of-terms-query/src/query.ts +++ b/packages/network-of-terms-query/src/query.ts @@ -54,7 +54,7 @@ export class QueryTermsService { constructor(options: {comunica?: QueryEngine; logger?: Pino.Logger} = {}) { this.engine = options.comunica || new QueryEngine(); - this.logger = options.logger || Pino(); + this.logger = options.logger || Pino.pino(); } async search( diff --git a/packages/network-of-terms-query/test/query.test.ts b/packages/network-of-terms-query/test/query.test.ts index 01d201390..221c21526 100644 --- a/packages/network-of-terms-query/test/query.test.ts +++ b/packages/network-of-terms-query/test/query.test.ts @@ -1,5 +1,5 @@ -import {testCatalog} from '../src/server-test'; -import {IRI, QueryMode, QueryTermsService} from '../src'; +import {testCatalog} from '../src/server-test.js'; +import {IRI, QueryMode, QueryTermsService} from '../src/index.js'; import {QueryEngine} from '@comunica/query-sparql'; import {ArrayIterator} from 'asynciterator'; import {jest} from '@jest/globals'; diff --git a/packages/network-of-terms-query/test/search/query-mode.test.ts b/packages/network-of-terms-query/test/search/query-mode.test.ts index 46d94a4c7..a89bd3052 100644 --- a/packages/network-of-terms-query/test/search/query-mode.test.ts +++ b/packages/network-of-terms-query/test/search/query-mode.test.ts @@ -1,4 +1,4 @@ -import {QueryMode, queryVariants} from '../../src/search/query-mode'; +import {QueryMode, queryVariants} from '../../src/index.js'; describe('Search query', () => { it('transforms simple query', () => { diff --git a/packages/network-of-terms-reconciliation/test/score.test.ts b/packages/network-of-terms-reconciliation/test/score.test.ts index 8f7f35244..a9e36ce3b 100644 --- a/packages/network-of-terms-reconciliation/test/score.test.ts +++ b/packages/network-of-terms-reconciliation/test/score.test.ts @@ -1,4 +1,4 @@ -import {calculateMatchingScore} from '../src/score'; +import {calculateMatchingScore} from '../src/score.js'; describe('Score', () => { it('ignores punctuation', () => { diff --git a/packages/network-of-terms-reconciliation/test/server.test.ts b/packages/network-of-terms-reconciliation/test/server.test.ts index 0459511e4..c385bd388 100644 --- a/packages/network-of-terms-reconciliation/test/server.test.ts +++ b/packages/network-of-terms-reconciliation/test/server.test.ts @@ -1,13 +1,13 @@ import {FastifyInstance} from 'fastify'; -import {server} from '../src/server'; -import {ReconciliationQueryBatch} from '../src/query'; +import {server} from '../src/server.js'; +import {ReconciliationQueryBatch} from '../src/query.js'; import { startDistributionSparqlEndpoint, testCatalog, teardown, -} from '../../network-of-terms-query/src/server-test'; -import {DataExtensionQuery} from '../src/data-extension'; -import {config} from '../src/config'; +} from '../../network-of-terms-query/src/server-test.js'; +import {DataExtensionQuery} from '../src/data-extension.js'; +import {config} from '../src/config.js'; let httpServer: FastifyInstance; const catalog = testCatalog(3001); diff --git a/tsconfig.build.json b/tsconfig.build.json index 5bd572dcd..88be6d9ca 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -4,13 +4,13 @@ "sourceMap": true, "target": "es2022", "lib": [ - "es2023" + "esnext" ], - "module": "esnext", + "module": "nodenext", "declarationMap": true, "skipLibCheck": true, "esModuleInterop": true, - "moduleResolution": "node", + "moduleResolution": "nodenext", "resolveJsonModule": true, "declaration": true, "forceConsistentCasingInFileNames": true