-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add first implementation of Network of Terms provider (#63)
Requires some more work, but is already functional. See also: https://coli-conc.gbv.de/publications/2023-09-12-SWIB23-Lightning-Talk.pdf
- Loading branch information
1 parent
3394b19
commit 5782cca
Showing
2 changed files
with
184 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
import BaseProvider from "./base-provider.js" | ||
import * as errors from "../errors/index.js" | ||
import { listOfCapabilities } from "../utils/index.js" | ||
import axios from "axios" | ||
import jskos from "jskos-tools" | ||
|
||
/** | ||
* TODOs: | ||
* - [ ] Clean up conversion to JSKOS | ||
* - [ ] Notations? (might be possible if NoT provided URI namespace) | ||
* - [ ] Source languages (see https://github.com/netwerk-digitaal-erfgoed/network-of-terms/issues/1105) | ||
* - [ ] Clean up GraphQL query strings | ||
* - [ ] Better error handling | ||
* - [ ] Implement getTop (if possible) | ||
* - [ ] Implement getNarrower and getAncestors (already returned by getConcepts, but methods should be implemented nonetheless) | ||
* - [ ] Decide on providerType URI | ||
* - [ ] More testing required | ||
*/ | ||
|
||
/** | ||
* Add this entry to registries: | ||
{ | ||
"provider": "NoTApi", | ||
"uri": "http://coli-conc.gbv.de/registry/not-api", | ||
"api": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/graphql", | ||
"notation": [ | ||
"NoT" | ||
] | ||
} | ||
*/ | ||
|
||
const cache = { | ||
schemes: [], | ||
} | ||
|
||
export default class NoTApiProvider extends BaseProvider { | ||
|
||
_prepare() { | ||
this.has.schemes = true | ||
this.has.top = false | ||
this.has.data = true | ||
this.has.concepts = true | ||
this.has.narrower = false | ||
this.has.ancestors = false | ||
this.has.suggest = true | ||
this.has.search = true | ||
// Explicitly set other capabilities to false | ||
listOfCapabilities.filter(c => !this.has[c]).forEach(c => { | ||
this.has[c] = false | ||
}) | ||
} | ||
|
||
/** | ||
* Used by `registryForScheme` (see src/lib/CocodaSDK.js) to determine a provider config for a concept schceme. | ||
* | ||
* @param {Object} options | ||
* @param {Object} options.url API URL for server | ||
* @returns {Object} provider configuration | ||
*/ | ||
static _registryConfigForBartocApiConfig({ url } = {}) { | ||
if (!url) { | ||
return null | ||
} | ||
return { | ||
api: url, | ||
} | ||
} | ||
|
||
async getSchemes() { | ||
if (!cache.schemes.length) { | ||
const result = await axios.post(this._api.api, { | ||
query: "query sources { sources { name uri description alternateName } }", | ||
operationName: "sources", | ||
}) | ||
const schemes = result?.data?.data?.sources || [] | ||
if (schemes.length) { | ||
cache.schemes = schemes.map(scheme => { | ||
const jskos = { | ||
uri: scheme.uri, | ||
prefLabel: { und: scheme.name }, | ||
} | ||
if (scheme.desciption) { | ||
jskos.description = { und: [scheme.description] } | ||
} | ||
if (scheme.alternateName) { | ||
jskos.notation = [scheme.alternateName] | ||
} | ||
return jskos | ||
}) | ||
} else { | ||
return [] | ||
} | ||
} | ||
return cache.schemes | ||
} | ||
|
||
// async getTop() { | ||
// } | ||
|
||
async getConcepts({ concepts }) { | ||
if (!concepts) { | ||
throw new errors.InvalidOrMissingParameterError({ parameter: "concepts" }) | ||
} | ||
if (!Array.isArray(concepts)) { | ||
concepts = [concepts] | ||
} | ||
const result = await axios.post(this._api.api, { | ||
query: `query { lookup( uris: [${concepts.map(c => `"${c.uri}"`)}], ) { uri source { ... on Source { uri } } result { ... on Term { uri prefLabel scopeNote altLabel broader { uri } narrower { uri } } } } }`, | ||
}) | ||
return (result.data?.data?.lookup || []).map(entry => { | ||
const concept = { | ||
uri: entry.uri, | ||
inScheme: [cache.schemes.find(scheme => jskos.compare(scheme, { uri: entry.source.uri }))], | ||
} | ||
if (entry.result?.prefLabel?.[0]) { | ||
concept.prefLabel = { und: entry.result.prefLabel[0] } | ||
} | ||
if (entry.result?.altLabel?.[0]) { | ||
concept.altLabel = { und: entry.result.altLabel } | ||
} | ||
if (entry.result?.scopeNote?.[0]) { | ||
concept.scopeNote = { und: entry.result.scopeNote } | ||
} | ||
if (entry.result?.broader?.length) { | ||
concept.broader = entry.result.broader | ||
} | ||
if (entry.result?.narrower?.length) { | ||
concept.narrower = entry.result.narrower | ||
} | ||
return concept | ||
}) | ||
} | ||
|
||
// async getNarrower({ concept }) { | ||
// } | ||
|
||
// async getAncestors({ concept }) { | ||
// } | ||
|
||
async suggest(config) { | ||
const search = config.search | ||
const results = await this.search(config) | ||
return [ | ||
search, | ||
results.map(r => jskos.prefLabel(r, { fallbackToUri: false })), | ||
[], | ||
results.map(r => r.uri), | ||
] | ||
} | ||
|
||
async search({ scheme, search }) { | ||
if (!search) { | ||
throw new errors.InvalidOrMissingParameterError({ parameter: "search" }) | ||
} | ||
if (!scheme || !jskos.isContainedIn(scheme, cache.schemes)) { | ||
throw new errors.InvalidOrMissingParameterError({ parameter: "scheme" }) | ||
} | ||
const result = await axios.post(this._api.api, { | ||
query: `query { terms( sources: ["${scheme.uri}"] query: "${search}" ) { source { uri } result { ... on Terms { terms { uri prefLabel scopeNote } } } }}`, | ||
}) | ||
return (result.data?.data?.terms?.[0]?.result?.terms || []).map(concept => { | ||
const jskos = { | ||
uri: concept.uri, | ||
inScheme: [scheme], | ||
} | ||
if (concept.prefLabel?.[0]) { | ||
jskos.prefLabel = { und: concept.prefLabel[0] } | ||
} | ||
if (concept.altLabel?.[0]) { | ||
jskos.altLabel = { und: concept.altLabel } | ||
} | ||
if (concept.scopeNote?.[0]) { | ||
jskos.scopeNote = { und: concept.scopeNote[0] } | ||
} | ||
return jskos | ||
}) | ||
} | ||
|
||
} | ||
|
||
NoTApiProvider.providerName = "NoTApi" | ||
// NoTApiProvider.providerType = "http://bartoc.org/api-type/not" |