cocoda-sdk 3.4.13 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -88
- package/dist/cjs/index.cjs +857 -66
- package/dist/cocoda-sdk.js +10 -10
- package/dist/cocoda-sdk.js.LICENSES.txt +3 -3
- package/dist/cocoda-sdk.js.map +4 -4
- package/dist/esm/providers/base-provider.js +30 -1
- package/dist/esm/providers/concept-api-provider.js +45 -34
- package/dist/esm/providers/contexts/context_jskos.js +240 -0
- package/dist/esm/providers/contexts/context_mod.js +59 -0
- package/dist/esm/providers/index.js +4 -0
- package/dist/esm/providers/mod-api-provider.js +452 -0
- package/dist/esm/providers/ols-api-provider.js +325 -0
- package/dist/esm/providers/skosmos-api-provider.js +0 -28
- package/package.json +13 -9
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
import BaseProvider from "./base-provider.js";
|
|
2
|
+
class ModApiProvider extends BaseProvider {
|
|
3
|
+
// #### PROPERTIES ####
|
|
4
|
+
// - providerName (This is how a provider is identified in a "registry" object in field `provider`.)
|
|
5
|
+
static providerName = "ModApi";
|
|
6
|
+
// - providerType (Optional BARTOC API type URI. Supported types: https://github.com/gbv/bartoc.org/blob/main/data/bartoc-api-types.concepts.csv, the URI prefix is "http://bartoc.org/api-type/".)
|
|
7
|
+
static providerType = "http://bartoc.org/en/node/20333";
|
|
8
|
+
// - supports (Optional object of supported capabilities. The keys should be values from this list: https://github.com/gbv/cocoda-sdk/blob/9145952398d6828004beb395c1d392a4d24e9288/src/utils/index.js#L159-L174; values should be a boolean. `false` values can be left out. They will be used to initialize `this.has` (see below). Alternatively, `this.has` can be filled in `_prepare` or `_setup`.)
|
|
9
|
+
static supports = {
|
|
10
|
+
schemes: true,
|
|
11
|
+
top: false,
|
|
12
|
+
data: false,
|
|
13
|
+
concepts: true,
|
|
14
|
+
narrower: false,
|
|
15
|
+
ancestors: false,
|
|
16
|
+
types: false,
|
|
17
|
+
suggest: false,
|
|
18
|
+
search: false,
|
|
19
|
+
auth: false,
|
|
20
|
+
mappings: false,
|
|
21
|
+
concordances: false,
|
|
22
|
+
annotations: false,
|
|
23
|
+
occurrences: false
|
|
24
|
+
};
|
|
25
|
+
// #### CUSTOM METHODS ####
|
|
26
|
+
/**
|
|
27
|
+
* Constructs the full API URL for a given endpoint.
|
|
28
|
+
* @param {Array} parts - Array of api parts (e.g., "[artifacts, <schemeShort>]")
|
|
29
|
+
* @param {Object} params - An object containing query parameters as key-value pairs.
|
|
30
|
+
* @returns {string} The full URL. Returns undefined if any part is undefined.
|
|
31
|
+
* @private
|
|
32
|
+
*/
|
|
33
|
+
_getApiUrl(parts, params) {
|
|
34
|
+
let result = this.uri || "";
|
|
35
|
+
if (result.endsWith("/")) {
|
|
36
|
+
result = result.slice(0, -1);
|
|
37
|
+
}
|
|
38
|
+
for (const part of parts) {
|
|
39
|
+
if (part) {
|
|
40
|
+
result += "/" + part;
|
|
41
|
+
} else {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (params) {
|
|
46
|
+
const paramString = Object.keys(params).map((k) => `${k}=${encodeURIComponent(params[k])}`).join("&");
|
|
47
|
+
result += (result.includes("?") ? "&" : "?") + paramString;
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
/*
|
|
52
|
+
_artefactToJSKOS(artefact) {
|
|
53
|
+
switch (this._jskos.transformation) {
|
|
54
|
+
//case "jsonld":
|
|
55
|
+
// return this._modToJskosJsonLD(artefact)
|
|
56
|
+
case "manual":
|
|
57
|
+
return this._modToJskosManual(artefact)
|
|
58
|
+
default:
|
|
59
|
+
// If no specific transformation is set, default to JSON-LD conversion
|
|
60
|
+
return this._modToJskosJsonLD(artefact)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
_modToJskosJsonLD(artefact) {
|
|
65
|
+
if (artefact["@id"]) {
|
|
66
|
+
delete artefact["@id"]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
artefact["@context"] = context_mod["@context"]
|
|
70
|
+
|
|
71
|
+
return jsonld
|
|
72
|
+
.expand(artefact)
|
|
73
|
+
.then((expanded) => jsonld.compact(expanded, context_jskos))
|
|
74
|
+
.then((compacted) => {
|
|
75
|
+
jskos.clean(compacted)
|
|
76
|
+
delete compacted["@context"]
|
|
77
|
+
for (const key in compacted) {
|
|
78
|
+
if (compacted[key]?.["@none"]) {
|
|
79
|
+
compacted[key][this._language] = compacted[key]["@none"]
|
|
80
|
+
delete compacted[key]["@none"]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
compacted = jskos.clean(compacted)
|
|
84
|
+
compacted = this._repairJsonLD(compacted)
|
|
85
|
+
|
|
86
|
+
return compacted
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
_repairJsonLD(json){
|
|
91
|
+
// This function is used to repair the JSON-LD context, translating erroneous keys
|
|
92
|
+
const map = {
|
|
93
|
+
narrower: "http://www.w3.org/2004/02/skos/core#narrower",
|
|
94
|
+
altLabel: "http://www.w3.org/2004/02/skos/core#altLabel",
|
|
95
|
+
definition: "http://www.w3.org/2004/02/skos/core#definition",
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (const key in map) {
|
|
99
|
+
if (json[map[key]]) {
|
|
100
|
+
json[key] = json[map[key]]
|
|
101
|
+
delete json[map[key]]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return json
|
|
105
|
+
}
|
|
106
|
+
*/
|
|
107
|
+
// _modToJskosManual(artefact) {
|
|
108
|
+
_artefactToJSKOS(artefact) {
|
|
109
|
+
const lan = artefact.language || this._language || "en";
|
|
110
|
+
const concept = {};
|
|
111
|
+
if (artefact.subject) {
|
|
112
|
+
concept.subject = [artefact.subject];
|
|
113
|
+
}
|
|
114
|
+
if (artefact.includedInDataCatalog) {
|
|
115
|
+
concept.api = [artefact.includedInDataCatalog];
|
|
116
|
+
}
|
|
117
|
+
if (artefact["@type"]) {
|
|
118
|
+
concept["@type"] = artefact["@type"];
|
|
119
|
+
}
|
|
120
|
+
if (artefact.type) {
|
|
121
|
+
concept.type = [artefact.type];
|
|
122
|
+
}
|
|
123
|
+
if (artefact.source_name) {
|
|
124
|
+
concept.notation = [artefact.source_name];
|
|
125
|
+
}
|
|
126
|
+
if (artefact.short_form) {
|
|
127
|
+
if (!concept.notation) {
|
|
128
|
+
concept.notation = [artefact.short_form];
|
|
129
|
+
} else {
|
|
130
|
+
concept.notation.push(artefact.short_form);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (artefact.label) {
|
|
134
|
+
concept.prefLabel = {};
|
|
135
|
+
concept.prefLabel[lan] = [];
|
|
136
|
+
concept.prefLabel[lan].push(artefact.label);
|
|
137
|
+
}
|
|
138
|
+
if (artefact.synonyms) {
|
|
139
|
+
concept.altLabel = {};
|
|
140
|
+
concept.altLabel[lan] = artefact.synonyms;
|
|
141
|
+
}
|
|
142
|
+
if (artefact.descriptions) {
|
|
143
|
+
concept.definition = {};
|
|
144
|
+
concept.definition[lan] = artefact.descriptions;
|
|
145
|
+
}
|
|
146
|
+
if (artefact.language) {
|
|
147
|
+
concept.languages = artefact.language;
|
|
148
|
+
}
|
|
149
|
+
if (artefact["@id"]) {
|
|
150
|
+
concept.uri = artefact["@id"];
|
|
151
|
+
}
|
|
152
|
+
if (artefact.iri) {
|
|
153
|
+
concept.iri = artefact.iri;
|
|
154
|
+
}
|
|
155
|
+
if (artefact.identifier) {
|
|
156
|
+
concept.identifier = [artefact.identifier];
|
|
157
|
+
}
|
|
158
|
+
if (artefact.source) {
|
|
159
|
+
concept.source = [artefact.source];
|
|
160
|
+
}
|
|
161
|
+
if (artefact.source_url) {
|
|
162
|
+
concept.namespace = artefact.source_url;
|
|
163
|
+
}
|
|
164
|
+
if (artefact.landingPage) {
|
|
165
|
+
concept.url = artefact.landingPage;
|
|
166
|
+
}
|
|
167
|
+
if (artefact.version) {
|
|
168
|
+
concept.version = artefact.version;
|
|
169
|
+
}
|
|
170
|
+
if (artefact.modified) {
|
|
171
|
+
concept.modified = artefact.modified;
|
|
172
|
+
}
|
|
173
|
+
if (artefact.created) {
|
|
174
|
+
concept.created = artefact.created;
|
|
175
|
+
}
|
|
176
|
+
if (artefact.hasFormat) {
|
|
177
|
+
concept.format = artefact.hasFormat;
|
|
178
|
+
}
|
|
179
|
+
if (artefact.license) {
|
|
180
|
+
concept.license = [artefact.license];
|
|
181
|
+
}
|
|
182
|
+
if (artefact.creator) {
|
|
183
|
+
concept.creator = artefact.creator;
|
|
184
|
+
}
|
|
185
|
+
if (artefact.contributor) {
|
|
186
|
+
concept.contributor = {};
|
|
187
|
+
concept.contributor.prefLabel = {};
|
|
188
|
+
concept.contributor.prefLabel[lan] = artefact.contributor;
|
|
189
|
+
}
|
|
190
|
+
if (artefact.publisher) {
|
|
191
|
+
concept.publisher = {};
|
|
192
|
+
concept.publisher[lan] = artefact.publisher;
|
|
193
|
+
}
|
|
194
|
+
if (artefact.released) {
|
|
195
|
+
concept.issued = artefact.released;
|
|
196
|
+
}
|
|
197
|
+
if (artefact.children) {
|
|
198
|
+
concept.narrower = {};
|
|
199
|
+
concept.narrower[lan] = artefact.children;
|
|
200
|
+
}
|
|
201
|
+
return concept;
|
|
202
|
+
}
|
|
203
|
+
// #### API REQUESTS ####
|
|
204
|
+
async _request(url, ..._config) {
|
|
205
|
+
if (!url) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const result = await this.axios({
|
|
209
|
+
method: "get",
|
|
210
|
+
url,
|
|
211
|
+
headers: {
|
|
212
|
+
"Content-Type": "application/json",
|
|
213
|
+
Accept: "application/json"
|
|
214
|
+
},
|
|
215
|
+
..._config
|
|
216
|
+
});
|
|
217
|
+
if (!result?._url || Object.keys(result).length != 1) {
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// API REQUESTS SCHEMES
|
|
222
|
+
async _getSchemesMod() {
|
|
223
|
+
const url = this._getApiUrl(["artefacts"], null);
|
|
224
|
+
return await this._request(url);
|
|
225
|
+
}
|
|
226
|
+
async _getSchemesModLimit(limit) {
|
|
227
|
+
const artifacts = await this._getSchemesMod();
|
|
228
|
+
if (limit && limit > 0) {
|
|
229
|
+
return artifacts.slice(0, limit);
|
|
230
|
+
}
|
|
231
|
+
return artifacts;
|
|
232
|
+
}
|
|
233
|
+
async _getSchemeMod(schemeParam) {
|
|
234
|
+
if (schemeParam.short) {
|
|
235
|
+
return await this._getSchemeFromShort(schemeParam.short);
|
|
236
|
+
} else if (schemeParam.uri) {
|
|
237
|
+
return await this._getSchemeFromUri(schemeParam.uri);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async _getSchemeFromShort(short) {
|
|
241
|
+
const url = this._getApiUrl(["artefacts", short], null);
|
|
242
|
+
return await this._request(url);
|
|
243
|
+
}
|
|
244
|
+
async _getSchemeFromUri(uri) {
|
|
245
|
+
const schemesMod = await this._getSchemesMod();
|
|
246
|
+
if (!schemesMod) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
for (const scheme of await schemesMod) {
|
|
250
|
+
if (scheme.source == uri || scheme.source_url == uri || scheme.source_name == uri || scheme["@id"] == uri || scheme.iri == uri || scheme.includedInDataCatalog && scheme.includedInDataCatalog.includes(uri)) {
|
|
251
|
+
return await scheme;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// API REQUESTS CONCEPTS
|
|
256
|
+
async _getConceptsMod(scheme) {
|
|
257
|
+
let schemeShort = await this._schemeShortFromObj(scheme);
|
|
258
|
+
if (!schemeShort) {
|
|
259
|
+
return [];
|
|
260
|
+
}
|
|
261
|
+
const url = this._getApiUrl(["artefacts", schemeShort, "resources", "concepts"], null);
|
|
262
|
+
const pageOne = await this._request(url);
|
|
263
|
+
if (!pageOne) {
|
|
264
|
+
return [];
|
|
265
|
+
}
|
|
266
|
+
const { page, totalPages, member: conceptsOne } = pageOne;
|
|
267
|
+
let concepts = [];
|
|
268
|
+
for (const concept of conceptsOne) {
|
|
269
|
+
if (concept) {
|
|
270
|
+
concepts.push(concept);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
for (let p = page + 1; p <= totalPages; p++) {
|
|
274
|
+
const urlPage = this._getApiUrl(["artefacts", schemeShort, "resources", "concepts"], { page: p });
|
|
275
|
+
const pageP = await this._request(urlPage);
|
|
276
|
+
if (!pageP) {
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
const { member: conceptsNew } = pageP;
|
|
280
|
+
for (const concept of conceptsNew) {
|
|
281
|
+
if (concept) {
|
|
282
|
+
concepts.push(concept);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return concepts;
|
|
287
|
+
}
|
|
288
|
+
async _getConceptsModLimit(scheme, limit) {
|
|
289
|
+
let concepts = await this._getConceptsMod(scheme);
|
|
290
|
+
if (limit && limit > 0) {
|
|
291
|
+
return concepts.slice(0, limit);
|
|
292
|
+
}
|
|
293
|
+
return concepts;
|
|
294
|
+
}
|
|
295
|
+
async _getConceptMod(concept) {
|
|
296
|
+
const { conceptNotation, schemeShort } = await this._conceptNotationFromObj(concept);
|
|
297
|
+
const url = this._getApiUrl(["artefacts", schemeShort, "resources", "concepts", conceptNotation], null);
|
|
298
|
+
return await this._request(url);
|
|
299
|
+
}
|
|
300
|
+
// UTILITIES
|
|
301
|
+
_containsString(obj, searchString) {
|
|
302
|
+
for (const key in obj) {
|
|
303
|
+
const value = obj[key];
|
|
304
|
+
if (typeof value === "string" && value.includes(searchString)) {
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
if (typeof value === "object" && value !== null) {
|
|
308
|
+
if (this.containsString(value, searchString)) {
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
async _getSchemesContaining(partstring) {
|
|
316
|
+
let schemes = [];
|
|
317
|
+
const schemesMod = await this._getSchemesMod();
|
|
318
|
+
for (const scheme of schemesMod) {
|
|
319
|
+
if (this.containsString(scheme, partstring)) {
|
|
320
|
+
schemes.push(scheme);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return schemes;
|
|
324
|
+
}
|
|
325
|
+
async _getSchemeShort(uri) {
|
|
326
|
+
const schemeMod = await this._getSchemeFromUri(uri);
|
|
327
|
+
if (schemeMod) {
|
|
328
|
+
return await schemeMod.short_form.toLowerCase();
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
_getconceptNotation(uri) {
|
|
332
|
+
return uri.split("/").pop();
|
|
333
|
+
}
|
|
334
|
+
async _schemeShortFromObj(scheme) {
|
|
335
|
+
if (scheme.short) {
|
|
336
|
+
return scheme.short;
|
|
337
|
+
} else if (scheme.uri) {
|
|
338
|
+
return await this._getSchemeShort(scheme.uri);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
async _conceptNotationFromObj(concept) {
|
|
342
|
+
if (!concept.inScheme || !concept.inScheme[0]) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
let schemeShort = await this._schemeShortFromObj(concept.inScheme[0]);
|
|
346
|
+
let conceptNotation = concept.notation;
|
|
347
|
+
if (!conceptNotation) {
|
|
348
|
+
conceptNotation = await this._getconceptNotation(concept.uri);
|
|
349
|
+
}
|
|
350
|
+
return { conceptNotation, schemeShort };
|
|
351
|
+
}
|
|
352
|
+
// #### OVERRIDE METHODS ####
|
|
353
|
+
/**
|
|
354
|
+
* will be called before the registry is initialized (i.e. it's `/status` endpoint is queries if necessasry)
|
|
355
|
+
* @private
|
|
356
|
+
*/
|
|
357
|
+
_prepare() {
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Sets up provider-specific properties.
|
|
361
|
+
* Enables support for mappings in this provider.
|
|
362
|
+
* will be called after registry is initialized (i.e. it's `/status` endpoint is queries if necessary), should be used to set properties on this.has and custom preparations
|
|
363
|
+
* @private
|
|
364
|
+
*/
|
|
365
|
+
_setup() {
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Retrieves all concept schemes from the MOD API.
|
|
369
|
+
*
|
|
370
|
+
* @param {Object} [params={}] - Optional parameters for the request.
|
|
371
|
+
* @returns {Promise<Array>} An array of JSKOS concept schemes.
|
|
372
|
+
* @async
|
|
373
|
+
*/
|
|
374
|
+
async getSchemes({ schemes, limit, ..._config }) {
|
|
375
|
+
let schemes_results = [];
|
|
376
|
+
let artefacts = [];
|
|
377
|
+
if (schemes) {
|
|
378
|
+
for (const s of schemes) {
|
|
379
|
+
let sc = await this._getSchemeMod(s);
|
|
380
|
+
if (sc) {
|
|
381
|
+
artefacts.push(sc);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
} else {
|
|
385
|
+
artefacts = await this._getSchemesModLimit(limit);
|
|
386
|
+
}
|
|
387
|
+
for (const artefact of artefacts) {
|
|
388
|
+
let scheme = await this._artefactToJSKOS(artefact);
|
|
389
|
+
if (scheme) {
|
|
390
|
+
schemes_results.push(scheme);
|
|
391
|
+
} else {
|
|
392
|
+
console.warn("JSKOS transformation failed for artefact: ", artefact);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return schemes_results;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Retrieves all concepts from the MOD API.
|
|
399
|
+
*
|
|
400
|
+
* @param {Object} params - The options object.
|
|
401
|
+
* @param {string[]} params.concepts - List of concept objects to request specific concepts.
|
|
402
|
+
* @param {string} params.scheme - A scheme object to request concepts from a specific scheme.
|
|
403
|
+
* @param {number} [params.limit] - Optional limit for results when requesting concepts from a scheme.
|
|
404
|
+
* @param {Object} [params._config] - Additional config options.
|
|
405
|
+
* @returns {Promise<Array>} An array of JSKOS concepts.
|
|
406
|
+
* @async
|
|
407
|
+
*/
|
|
408
|
+
async getConcepts({ concepts, scheme, limit, ..._config }) {
|
|
409
|
+
let concept_results = [];
|
|
410
|
+
if (concepts) {
|
|
411
|
+
for (const concept of concepts) {
|
|
412
|
+
let conceptMod = await this._getConceptMod(concept);
|
|
413
|
+
if (conceptMod) {
|
|
414
|
+
const concept2 = await this._artefactToJSKOS(conceptMod);
|
|
415
|
+
if (concept2) {
|
|
416
|
+
concept_results.push(concept2);
|
|
417
|
+
} else {
|
|
418
|
+
console.warn("JSKOS transformation failed for concept: ", conceptMod);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
} else if (scheme) {
|
|
423
|
+
const conceptsMod = await this._getConceptsModLimit(scheme, limit);
|
|
424
|
+
for (const conceptMod of conceptsMod) {
|
|
425
|
+
const conceptJ = await this._artefactToJSKOS(conceptMod);
|
|
426
|
+
if (conceptJ) {
|
|
427
|
+
concept_results.push(conceptJ);
|
|
428
|
+
} else {
|
|
429
|
+
console.warn("JSKOS transformation failed for concept: ", conceptMod);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return concept_results;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* @private
|
|
437
|
+
*/
|
|
438
|
+
get _language() {
|
|
439
|
+
return this._jskos.language || this.languages[0] || this._defaultLanguages[0] || "en";
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Retrieves an array of mappings.
|
|
443
|
+
* @returns {Array} An array containing mapping objects.
|
|
444
|
+
*/
|
|
445
|
+
getMappings() {
|
|
446
|
+
const mappings = [];
|
|
447
|
+
return mappings;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
export {
|
|
451
|
+
ModApiProvider as default
|
|
452
|
+
};
|