cocoda-sdk 3.1.0 → 3.2.2
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 +2 -0
- package/dist/cjs/index.cjs +261 -32
- package/dist/cocoda-sdk.js +6 -6
- package/dist/cocoda-sdk.js.LICENSES.txt +207 -0
- package/dist/cocoda-sdk.js.map +3 -3
- package/dist/esm/providers/base-provider.js +6 -4
- package/dist/esm/providers/index.js +2 -0
- package/dist/esm/providers/mappings-api-provider.js +10 -1
- package/dist/esm/providers/occurrences-api-provider.js +11 -19
- package/dist/esm/providers/skohub-provider.js +228 -0
- package/package.json +8 -7
|
@@ -41,10 +41,12 @@ class BaseProvider {
|
|
|
41
41
|
this._config = {};
|
|
42
42
|
this.setRetryConfig();
|
|
43
43
|
this.axios.interceptors.request.use((config) => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
_.
|
|
44
|
+
if (!config._skipAdditionalParameters) {
|
|
45
|
+
const language = _.uniq([].concat(_.get(config, "params.language", "").split(","), this.languages, this._defaultLanguages).filter((lang) => lang != "")).join(",");
|
|
46
|
+
_.set(config, "params.language", language);
|
|
47
|
+
if (this.has.auth && this._auth.bearerToken && !_.get(config, "headers.Authorization")) {
|
|
48
|
+
_.set(config, "headers.Authorization", `Bearer ${this._auth.bearerToken}`);
|
|
49
|
+
}
|
|
48
50
|
}
|
|
49
51
|
if (config.url.startsWith("http:") && typeof window !== "undefined" && window.location.protocol == "https:") {
|
|
50
52
|
throw new axios.Cancel("Can't call http API from https.");
|
|
@@ -7,6 +7,7 @@ import ReconciliationApiProvider from "./reconciliation-api-provider.js";
|
|
|
7
7
|
import LabelSearchSuggestionProvider from "./label-search-suggestion-provider.js";
|
|
8
8
|
import SkosmosApiProvider from "./skosmos-api-provider.js";
|
|
9
9
|
import LocApiProvider from "./loc-api-provider.js";
|
|
10
|
+
import SkohubProvider from "./skohub-provider.js";
|
|
10
11
|
export {
|
|
11
12
|
BaseProvider,
|
|
12
13
|
ConceptApiProvider,
|
|
@@ -16,5 +17,6 @@ export {
|
|
|
16
17
|
MappingsApiProvider,
|
|
17
18
|
OccurrencesApiProvider,
|
|
18
19
|
ReconciliationApiProvider,
|
|
20
|
+
SkohubProvider,
|
|
19
21
|
SkosmosApiProvider
|
|
20
22
|
};
|
|
@@ -78,7 +78,7 @@ class MappingsApiProvider extends BaseProvider {
|
|
|
78
78
|
throw error;
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
-
async getMappings({ from, fromScheme, to, toScheme, creator, type, partOf, offset, limit, direction, mode, identifier, cardinality, sort, order, ...config }) {
|
|
81
|
+
async getMappings({ from, fromScheme, to, toScheme, creator, type, partOf, offset, limit, direction, mode, identifier, cardinality, annotatedBy, annotatedFor, annotatedWith, sort, order, ...config }) {
|
|
82
82
|
let params = {}, url = this._api.mappings;
|
|
83
83
|
if (from) {
|
|
84
84
|
params.from = _.isString(from) ? from : from.uri;
|
|
@@ -113,6 +113,15 @@ class MappingsApiProvider extends BaseProvider {
|
|
|
113
113
|
if (cardinality) {
|
|
114
114
|
params.cardinality = cardinality;
|
|
115
115
|
}
|
|
116
|
+
if (annotatedBy) {
|
|
117
|
+
params.annotatedBy = annotatedBy;
|
|
118
|
+
}
|
|
119
|
+
if (annotatedFor) {
|
|
120
|
+
params.annotatedFor = annotatedFor;
|
|
121
|
+
}
|
|
122
|
+
if (annotatedWith) {
|
|
123
|
+
params.annotatedWith = annotatedWith;
|
|
124
|
+
}
|
|
116
125
|
if (mode) {
|
|
117
126
|
params.mode = mode;
|
|
118
127
|
}
|
|
@@ -36,8 +36,10 @@ class OccurrencesApiProvider extends BaseProvider {
|
|
|
36
36
|
}
|
|
37
37
|
async getMappings(config) {
|
|
38
38
|
const occurrences = await this.getOccurrences(config);
|
|
39
|
-
const
|
|
40
|
-
const
|
|
39
|
+
const from = config.from;
|
|
40
|
+
const fromScheme = _.get(from, "inScheme[0]") || config.fromScheme;
|
|
41
|
+
const to = config.to;
|
|
42
|
+
const toScheme = _.get(to, "inScheme[0]") || config.toScheme;
|
|
41
43
|
const mappings = [];
|
|
42
44
|
for (let occurrence of occurrences) {
|
|
43
45
|
if (!occurrence) {
|
|
@@ -58,7 +60,7 @@ class OccurrencesApiProvider extends BaseProvider {
|
|
|
58
60
|
mapping.to = { memberSet: [] };
|
|
59
61
|
}
|
|
60
62
|
mapping.toScheme = _.get(occurrence, "memberSet[1].inScheme[0]");
|
|
61
|
-
if (
|
|
63
|
+
if (from && jskos.compare(from, _.get(mapping, "to.memberSet[0]")) || to && jskos.compare(to, _.get(mapping, "from.memberSet[0]"))) {
|
|
62
64
|
[mapping.from, mapping.fromScheme, mapping.to, mapping.toScheme] = [mapping.to, mapping.toScheme, mapping.from, mapping.fromScheme];
|
|
63
65
|
}
|
|
64
66
|
if (!mapping.fromScheme && fromScheme) {
|
|
@@ -70,14 +72,12 @@ class OccurrencesApiProvider extends BaseProvider {
|
|
|
70
72
|
mapping.type = [jskos.defaultMappingType.uri];
|
|
71
73
|
mapping._occurrence = occurrence;
|
|
72
74
|
mapping = jskos.addMappingIdentifiers(mapping);
|
|
73
|
-
if (occurrence.database) {
|
|
74
|
-
mapping.creator = [occurrence.database];
|
|
75
|
-
}
|
|
76
75
|
mappings.push(mapping);
|
|
77
76
|
}
|
|
77
|
+
mappings._url = occurrences._url;
|
|
78
78
|
return mappings;
|
|
79
79
|
}
|
|
80
|
-
async getOccurrences({ from, to, concepts, ...config }) {
|
|
80
|
+
async getOccurrences({ from, to, concepts, threshold = 0, ...config }) {
|
|
81
81
|
let promises = [];
|
|
82
82
|
concepts = (concepts || []).concat([from, to]).filter((c) => !!c);
|
|
83
83
|
for (let concept of concepts) {
|
|
@@ -101,17 +101,7 @@ class OccurrencesApiProvider extends BaseProvider {
|
|
|
101
101
|
params: {
|
|
102
102
|
member: uri,
|
|
103
103
|
scheme: "*",
|
|
104
|
-
threshold
|
|
105
|
-
}
|
|
106
|
-
}));
|
|
107
|
-
}
|
|
108
|
-
if (uris.length > 1) {
|
|
109
|
-
let urisString = uris.join(" ");
|
|
110
|
-
promises.push(this._getOccurrences({
|
|
111
|
-
...config,
|
|
112
|
-
params: {
|
|
113
|
-
member: urisString,
|
|
114
|
-
threshold: 5
|
|
104
|
+
threshold
|
|
115
105
|
}
|
|
116
106
|
}));
|
|
117
107
|
}
|
|
@@ -135,7 +125,9 @@ class OccurrencesApiProvider extends BaseProvider {
|
|
|
135
125
|
delete occurrences[value];
|
|
136
126
|
});
|
|
137
127
|
occurrences = occurrences.filter((o) => o != null);
|
|
138
|
-
|
|
128
|
+
occurrences = occurrences.sort((a, b) => parseInt(b.count || 0) - parseInt(a.count || 0));
|
|
129
|
+
occurrences._url = results.map((result) => result._url);
|
|
130
|
+
return occurrences;
|
|
139
131
|
}
|
|
140
132
|
async _getOccurrences(config) {
|
|
141
133
|
let resultsFromCache = this._cache.find((item) => {
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import BaseProvider from "./base-provider.js";
|
|
2
|
+
import * as _ from "../utils/lodash.js";
|
|
3
|
+
import * as errors from "../errors/index.js";
|
|
4
|
+
import { listOfCapabilities } from "../utils/index.js";
|
|
5
|
+
import jskos from "jskos-tools";
|
|
6
|
+
import FlexSearch from "flexsearch";
|
|
7
|
+
function decodeUnicode(text) {
|
|
8
|
+
return text.replace(/\\u[\dA-F]{4}/gi, function(match) {
|
|
9
|
+
return String.fromCharCode(parseInt(match.replace(/\\u/g, ""), 16));
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
class SkohubProvider extends BaseProvider {
|
|
13
|
+
_prepare() {
|
|
14
|
+
this.has.schemes = true;
|
|
15
|
+
this.has.top = true;
|
|
16
|
+
this.has.data = true;
|
|
17
|
+
this.has.concepts = true;
|
|
18
|
+
this.has.narrower = true;
|
|
19
|
+
this.has.ancestors = true;
|
|
20
|
+
this.has.suggest = true;
|
|
21
|
+
this.has.search = true;
|
|
22
|
+
listOfCapabilities.filter((c) => !this.has[c]).forEach((c) => {
|
|
23
|
+
this.has[c] = false;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
_setup() {
|
|
27
|
+
this._index = {};
|
|
28
|
+
this._conceptCache = {};
|
|
29
|
+
this._schemeCache = {};
|
|
30
|
+
}
|
|
31
|
+
static _registryConfigForBartocApiConfig({ url, scheme } = {}) {
|
|
32
|
+
if (!url || !scheme) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const newScheme = { uri: url, identifier: jskos.getAllUris(scheme).filter((uri) => uri !== url) };
|
|
36
|
+
return { schemes: [newScheme] };
|
|
37
|
+
}
|
|
38
|
+
async _loadScheme({ scheme, ...config }) {
|
|
39
|
+
let uris = jskos.getAllUris(scheme);
|
|
40
|
+
for (let uri2 of uris) {
|
|
41
|
+
if (this._schemeCache[uri2]) {
|
|
42
|
+
return this._schemeCache[uri2];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const schemeFromList = this.schemes.find((s) => jskos.compare(s, scheme));
|
|
46
|
+
if (!schemeFromList || !schemeFromList.uri) {
|
|
47
|
+
throw new errors.InvalidRequestError({ message: `Tried to load unsupported scheme (${scheme && scheme.uri})` });
|
|
48
|
+
}
|
|
49
|
+
const uri = schemeFromList.uri;
|
|
50
|
+
uris = _.uniq(uris.concat(jskos.getAllUris(schemeFromList)));
|
|
51
|
+
let postfix = ".json";
|
|
52
|
+
if (uri.endsWith("/")) {
|
|
53
|
+
postfix = "index.json";
|
|
54
|
+
}
|
|
55
|
+
const data = await this.axios({ ...config, url: `${uri}${postfix}`, _skipAdditionalParameters: true });
|
|
56
|
+
if (data.id !== uri) {
|
|
57
|
+
throw new errors.InvalidRequestError({ message: "Skohub URL did not return expected concept scheme" });
|
|
58
|
+
}
|
|
59
|
+
const { title, preferredNamespaceUri, hasTopConcept, description } = data;
|
|
60
|
+
scheme = { uri, identifier: uris.filter((u) => u !== uri) };
|
|
61
|
+
scheme.prefLabel = title;
|
|
62
|
+
Object.keys(scheme.prefLabel || {}).forEach((key) => {
|
|
63
|
+
scheme.prefLabel[key] = decodeUnicode(scheme.prefLabel[key]);
|
|
64
|
+
});
|
|
65
|
+
scheme.namespace = preferredNamespaceUri;
|
|
66
|
+
scheme.topConcepts = (hasTopConcept || []).map((c) => this._toJskosConcept(c));
|
|
67
|
+
scheme.concepts = [null];
|
|
68
|
+
if (description) {
|
|
69
|
+
scheme.definition = description;
|
|
70
|
+
Object.keys(scheme.definition).forEach((key) => {
|
|
71
|
+
scheme.definition[key] = [decodeUnicode(scheme.definition[key])];
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
for (let key of Object.keys(scheme).filter((key2) => !scheme[key2])) {
|
|
75
|
+
delete scheme[key];
|
|
76
|
+
}
|
|
77
|
+
for (let uri2 of uris) {
|
|
78
|
+
this._schemeCache[uri2] = scheme;
|
|
79
|
+
}
|
|
80
|
+
return scheme;
|
|
81
|
+
}
|
|
82
|
+
async _loadConcept({ uri, ...config }) {
|
|
83
|
+
if (this._conceptCache[uri]) {
|
|
84
|
+
return this._conceptCache[uri];
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const data = await this.axios({ ...config, url: `${uri}.json`, _skipAdditionalParameters: true });
|
|
88
|
+
if (data.id !== uri) {
|
|
89
|
+
throw new errors.InvalidRequestError({ message: "Skohub URL did not return expected concept URI" });
|
|
90
|
+
}
|
|
91
|
+
const concept = this._toJskosConcept(data);
|
|
92
|
+
this._conceptCache[uri] = concept;
|
|
93
|
+
return concept;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
_toJskosConcept(data) {
|
|
99
|
+
const concept = { uri: data.id };
|
|
100
|
+
concept.prefLabel = data.prefLabel;
|
|
101
|
+
Object.keys(concept.prefLabel || {}).forEach((key) => {
|
|
102
|
+
concept.prefLabel[key] = decodeUnicode(concept.prefLabel[key]);
|
|
103
|
+
});
|
|
104
|
+
concept.narrower = (data.narrower || []).map((c) => this._toJskosConcept(c));
|
|
105
|
+
concept.notation = data.notation || [];
|
|
106
|
+
if (data.broader && data.broader.id) {
|
|
107
|
+
concept.broader = [{ uri: data.broader.id }];
|
|
108
|
+
}
|
|
109
|
+
if (data.inScheme && data.inScheme.id) {
|
|
110
|
+
concept.inScheme = [{ uri: data.inScheme.id }];
|
|
111
|
+
}
|
|
112
|
+
if (data.scopeNote) {
|
|
113
|
+
concept.scopeNote = data.scopeNote;
|
|
114
|
+
Object.keys(concept.scopeNote).forEach((key) => {
|
|
115
|
+
concept.scopeNote[key] = [decodeUnicode(concept.scopeNote[key])];
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return concept;
|
|
119
|
+
}
|
|
120
|
+
async getSchemes({ ...config }) {
|
|
121
|
+
return Promise.all(this.schemes.map((scheme) => this._loadScheme({ ...config, scheme })));
|
|
122
|
+
}
|
|
123
|
+
async getTop({ scheme, ...config }) {
|
|
124
|
+
if (!scheme || !scheme.uri) {
|
|
125
|
+
throw new errors.InvalidOrMissingParameterError({ parameter: "scheme", message: "Missing scheme URI" });
|
|
126
|
+
}
|
|
127
|
+
scheme = await this._loadScheme({ scheme, ...config });
|
|
128
|
+
return scheme.topConcepts || [];
|
|
129
|
+
}
|
|
130
|
+
async getConcepts({ concepts, ...config }) {
|
|
131
|
+
if (!_.isArray(concepts)) {
|
|
132
|
+
concepts = [concepts];
|
|
133
|
+
}
|
|
134
|
+
return (await Promise.all(concepts.map(({ uri }) => this._loadConcept({ ...config, uri })))).filter(Boolean);
|
|
135
|
+
}
|
|
136
|
+
async getAncestors({ concept, ...config }) {
|
|
137
|
+
if (!concept || !concept.uri) {
|
|
138
|
+
throw new errors.InvalidOrMissingParameterError({ parameter: "concept" });
|
|
139
|
+
}
|
|
140
|
+
if (concept.ancestors && concept.ancestors[0] !== null) {
|
|
141
|
+
return concept.ancestors;
|
|
142
|
+
}
|
|
143
|
+
concept = await this._loadConcept({ ...config, uri: concept.uri });
|
|
144
|
+
if (!concept || !concept.broader || !concept.broader.length) {
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
const broader = concept.broader[0];
|
|
148
|
+
return [broader].concat(await this.getAncestors({ concept: broader, ...config })).map((c) => ({ uri: c.uri }));
|
|
149
|
+
}
|
|
150
|
+
async getNarrower({ concept, ...config }) {
|
|
151
|
+
if (!concept || !concept.uri) {
|
|
152
|
+
throw new errors.InvalidOrMissingParameterError({ parameter: "concept" });
|
|
153
|
+
}
|
|
154
|
+
if (concept.narrower && concept.narrower[0] !== null) {
|
|
155
|
+
return concept.narrower;
|
|
156
|
+
}
|
|
157
|
+
concept = await this._loadConcept({ ...config, uri: concept.uri });
|
|
158
|
+
return concept.narrower;
|
|
159
|
+
}
|
|
160
|
+
async search({ search, scheme, limit = 100 }) {
|
|
161
|
+
scheme = await this._loadScheme({ scheme });
|
|
162
|
+
if (!scheme || !scheme.uri) {
|
|
163
|
+
throw new errors.InvalidOrMissingParameterError({ parameter: "scheme" });
|
|
164
|
+
}
|
|
165
|
+
if (!search) {
|
|
166
|
+
throw new errors.InvalidOrMissingParameterError({ parameter: "search" });
|
|
167
|
+
}
|
|
168
|
+
let index;
|
|
169
|
+
if (!this._index[scheme.uri]) {
|
|
170
|
+
this._index[scheme.uri] = {};
|
|
171
|
+
}
|
|
172
|
+
for (const lang of [""].concat(this.languages)) {
|
|
173
|
+
if (this._index[scheme.uri][lang]) {
|
|
174
|
+
index = this._index[scheme.uri][lang];
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
if (this._index[scheme.uri][lang] === null) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
let postfix = lang ? `.${lang}.index` : ".index";
|
|
182
|
+
if (scheme.uri.endsWith("/")) {
|
|
183
|
+
postfix = `index${postfix}`;
|
|
184
|
+
}
|
|
185
|
+
const data = await this.axios({ url: `${scheme.uri}${postfix}`, _skipAdditionalParameters: true });
|
|
186
|
+
if (data.length < 100) {
|
|
187
|
+
this._index[scheme.uri][lang] = null;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
index = FlexSearch.create();
|
|
191
|
+
index.import(data);
|
|
192
|
+
this._index[scheme.uri][lang] = index;
|
|
193
|
+
break;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
this._index[scheme.uri][lang] = null;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (!index) {
|
|
199
|
+
throw new errors.InvalidRequestError({ message: "Could not find search index for any of the available languages " + this.languages.join(",") });
|
|
200
|
+
}
|
|
201
|
+
const result = index.search(search);
|
|
202
|
+
const concepts = await this.getConcepts({ concepts: result.map((uri) => ({ uri })) });
|
|
203
|
+
return concepts.slice(0, limit);
|
|
204
|
+
}
|
|
205
|
+
async suggest(config) {
|
|
206
|
+
config._raw = true;
|
|
207
|
+
const concepts = await this.search(config);
|
|
208
|
+
const result = [config.search, [], [], []];
|
|
209
|
+
for (let concept of concepts) {
|
|
210
|
+
const notation = jskos.notation(concept);
|
|
211
|
+
const label = jskos.prefLabel(concept);
|
|
212
|
+
result[1].push((notation ? notation + " " : "") + label);
|
|
213
|
+
result[2].push("");
|
|
214
|
+
result[3].push(concept.uri);
|
|
215
|
+
}
|
|
216
|
+
if (concepts._totalCount != void 0) {
|
|
217
|
+
result._totalCount = concepts._totalCount;
|
|
218
|
+
} else {
|
|
219
|
+
result._totalCount = concepts.length;
|
|
220
|
+
}
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
SkohubProvider.providerName = "Skohub";
|
|
225
|
+
SkohubProvider.providerType = "http://bartoc.org/api-type/skohub";
|
|
226
|
+
export {
|
|
227
|
+
SkohubProvider as default
|
|
228
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cocoda-sdk",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.2",
|
|
4
4
|
"description": "SDK for Cocoda",
|
|
5
5
|
"main": "dist/cjs/index.cjs",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -52,22 +52,23 @@
|
|
|
52
52
|
},
|
|
53
53
|
"homepage": "https://github.com/gbv/cocoda-sdk#readme",
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"axios-mock-adapter": "^1.
|
|
55
|
+
"axios-mock-adapter": "^1.21.1",
|
|
56
56
|
"better-docs": "^2.7.2",
|
|
57
|
-
"esbuild": "~0.14.
|
|
57
|
+
"esbuild": "~0.14.43",
|
|
58
58
|
"esbuild-plugin-ifdef": "^1.0.1",
|
|
59
|
-
"eslint": "^8.
|
|
59
|
+
"eslint": "^8.17.0",
|
|
60
60
|
"eslint-config-gbv": "^1.0.3",
|
|
61
|
-
"glob": "^8.0.
|
|
61
|
+
"glob": "^8.0.3",
|
|
62
62
|
"jsdoc": "^3.6.10",
|
|
63
63
|
"license-checker": "^25.0.1",
|
|
64
|
-
"lint-staged": "^12.
|
|
64
|
+
"lint-staged": "^12.5.0",
|
|
65
65
|
"mocha": "^9.2.2",
|
|
66
66
|
"mocha-eslint": "^7.0.0",
|
|
67
67
|
"pre-commit": "^1.2.2"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"axios": "
|
|
70
|
+
"axios": "~0.26.1",
|
|
71
|
+
"flexsearch": "~0.6.32",
|
|
71
72
|
"jskos-tools": "^1.0.26",
|
|
72
73
|
"localforage": "^1.10.0",
|
|
73
74
|
"lodash": "^4.17.21",
|