cocoda-sdk 1.0.13 → 2.0.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.
Files changed (36) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +74 -28
  3. package/dist/cjs/index.cjs +2673 -0
  4. package/dist/cocoda-sdk.js +33 -17423
  5. package/dist/cocoda-sdk.js.LICENSES.txt +105 -86
  6. package/dist/cocoda-sdk.js.map +7 -0
  7. package/dist/esm/errors/index.js +46 -0
  8. package/dist/esm/index.js +9 -0
  9. package/dist/esm/lib/CocodaSDK.js +269 -0
  10. package/dist/esm/providers/base-provider.js +368 -0
  11. package/dist/esm/providers/concept-api-provider.js +278 -0
  12. package/dist/esm/providers/index.js +20 -0
  13. package/dist/esm/providers/label-search-suggestion-provider.js +101 -0
  14. package/dist/esm/providers/loc-api-provider.js +185 -0
  15. package/dist/esm/providers/local-mappings-provider.js +337 -0
  16. package/dist/esm/providers/mappings-api-provider.js +264 -0
  17. package/dist/esm/providers/occurrences-api-provider.js +163 -0
  18. package/dist/esm/providers/reconciliation-api-provider.js +140 -0
  19. package/dist/esm/providers/skosmos-api-provider.js +345 -0
  20. package/{utils → dist/esm/utils}/index.js +40 -53
  21. package/dist/esm/utils/lodash.js +34 -0
  22. package/package.json +16 -17
  23. package/errors/index.js +0 -119
  24. package/index.js +0 -5
  25. package/lib/CocodaSDK.js +0 -360
  26. package/providers/base-provider.js +0 -581
  27. package/providers/concept-api-provider.js +0 -377
  28. package/providers/index.js +0 -34
  29. package/providers/label-search-suggestion-provider.js +0 -219
  30. package/providers/loc-api-provider.js +0 -275
  31. package/providers/local-mappings-provider.js +0 -459
  32. package/providers/mappings-api-provider.js +0 -396
  33. package/providers/occurrences-api-provider.js +0 -234
  34. package/providers/reconciliation-api-provider.js +0 -211
  35. package/providers/skosmos-api-provider.js +0 -441
  36. package/utils/lodash.js +0 -21
@@ -1,377 +0,0 @@
1
- const BaseProvider = require("./base-provider")
2
- const _ = require("../utils/lodash")
3
- const errors = require("../errors")
4
- const utils = require("../utils")
5
- const jskos = require("jskos-tools")
6
-
7
- /**
8
- * JSKOS Concept API.
9
- *
10
- * This class provides access to concept schemes and their concepts via JSKOS API in [JSKOS format](https://gbv.github.io/jskos/).
11
- * See [jskos-server](https://github.com/gbv/jskos-server) for a JSKOS API reference implementation and [DANTE](https://api.dante.gbv.de/) for another API endpoint.
12
- *
13
- * To use it in a registry, specify `provider` as "ConceptApi" and provide the API base URL as `api`:
14
- * ```json
15
- * {
16
- * "uri": "http://coli-conc.gbv.de/registry/coli-conc-concepts",
17
- * "provider": "ConceptApi",
18
- * "api": "https://coli-conc.gbv.de/api/"
19
- * }
20
- * ```
21
- *
22
- * If the `/status` endpoint can be queried, the remaining API methods will be taken from that. As a fallback, the default endpoints will be appended to `api`.
23
- *
24
- * Alternatively, you can provide the endpoints separately: `status`, `schemes`, `top`, `concepts`, `data`, `narrower`, `ancestors`, `types`, `suggest`, `search`
25
- * Note that `schemes`, `top`, and `types` can also be provided as arrays.
26
- *
27
- * Additionally, the following JSKOS properties can be provided: `prefLabel`, `notation`, `definition`
28
- *
29
- * @extends BaseProvider
30
- * @category Providers
31
- */
32
- class ConceptApiProvider extends BaseProvider {
33
-
34
- /**
35
- * @private
36
- */
37
- _prepare() {
38
- // Set status endpoint only
39
- if (this._api.api && this._api.status === undefined) {
40
- this._api.status = utils.concatUrl(this._api.api, "/status")
41
- }
42
- }
43
-
44
- /**
45
- * @private
46
- */
47
- _setup() {
48
- // Implicitly fill `this._api` if necessary
49
- if (this._api.api) {
50
- const endpoints = {
51
- schemes: "/voc",
52
- top: "/voc/top",
53
- concepts: "/voc/concepts",
54
- data: "/data",
55
- narrower: "/narrower",
56
- ancestors: "/ancestors",
57
- types: "/types",
58
- suggest: "/suggest",
59
- search: "/search",
60
- }
61
- for (let key of Object.keys(endpoints)) {
62
- // Only override if undefined
63
- if (this._api[key] === undefined) {
64
- this._api[key] = utils.concatUrl(this._api.api, endpoints[key])
65
- }
66
- }
67
- }
68
- this.has.schemes = !!this._api.schemes
69
- this.has.top = !!this._api.top
70
- this.has.data = !!this._api.data
71
- this.has.concepts = !!this._api.concepts || this.has.data
72
- this.has.narrower = !!this._api.narrower
73
- this.has.ancestors = !!this._api.ancestors
74
- this.has.types = !!this._api.types
75
- this.has.suggest = !!this._api.suggest
76
- this.has.search = !!this._api.search
77
- this.has.auth = _.get(this._config, "auth.key") != null
78
- this._defaultParams = {
79
- properties: "uri,prefLabel,notation,inScheme",
80
- }
81
- }
82
-
83
- /**
84
- * Returns all concept schemes.
85
- *
86
- * @param {Object} config
87
- * @returns {Object[]} array of JSKOS concept scheme objects
88
- */
89
- async getSchemes(config) {
90
- if (!this._api.schemes) {
91
- throw new errors.MissingApiUrlError()
92
- }
93
- if (Array.isArray(this._api.schemes)) {
94
- return this._api.schemes
95
- }
96
- const schemes = await this.axios({
97
- ...config,
98
- method: "get",
99
- url: this._api.schemes,
100
- params: {
101
- ...this._defaultParams,
102
- // ? What should the default limit be?
103
- limit: 500,
104
- ...(config.params || {}),
105
- },
106
- })
107
- // If schemes were given in registry object, only request those schemes from API
108
- if (Array.isArray(this._jskos.schemes)) {
109
- return utils.withCustomProps(schemes.filter(s => jskos.isContainedIn(s, this._jskos.schemes)), schemes)
110
- } else {
111
- return schemes
112
- }
113
- }
114
-
115
- /**
116
- * Returns top concepts for a concept scheme.
117
- *
118
- * @param {Object} config
119
- * @param {Object} config.scheme concept scheme object
120
- * @returns {Object[]} array of JSKOS concept objects
121
- */
122
- async getTop({ scheme, ...config }) {
123
- if (!this._api.top) {
124
- throw new errors.MissingApiUrlError()
125
- }
126
- if (!scheme) {
127
- throw new errors.InvalidOrMissingParameterError({ parameter: "scheme" })
128
- }
129
- if (Array.isArray(this._api.top)) {
130
- return this._api.top
131
- }
132
- return this.axios({
133
- ...config,
134
- method: "get",
135
- url: this._api.top,
136
- params: {
137
- ...this._defaultParams,
138
- // ? What should the default limit be?
139
- limit: 10000,
140
- ...(config.params || {}),
141
- uri: scheme.uri,
142
- },
143
- })
144
- }
145
-
146
- /**
147
- * Returns details for a list of concepts.
148
- *
149
- * @param {Object} config
150
- * @param {Object[]} config.concepts list of concept objects to load
151
- * @returns {Object[]} array of JSKOS concept objects
152
- */
153
- async getConcepts({ concepts, ...config }) {
154
- if (!this.has.data) {
155
- throw new errors.MissingApiUrlError()
156
- }
157
- if (!concepts) {
158
- throw new errors.InvalidOrMissingParameterError({ parameter: "concepts" })
159
- }
160
- if (!Array.isArray(concepts)) {
161
- concepts = [concepts]
162
- }
163
- let uris = concepts.map(concept => concept.uri).filter(uri => uri != null)
164
- return this.axios({
165
- ...config,
166
- method: "get",
167
- url: this._api.data,
168
- params: {
169
- ...this._defaultParams,
170
- // ? What should the default limit be?
171
- limit: 500,
172
- ...(config.params || {}),
173
- uri: uris.join("|"),
174
- },
175
- })
176
- }
177
-
178
- /**
179
- * Returns narrower concepts for a concept.
180
- *
181
- * @param {Object} config
182
- * @param {Object} config.concept concept object
183
- * @returns {Object[]} array of JSKOS concept objects
184
- */
185
- async getNarrower({ concept, ...config }) {
186
- if (!this._api.narrower) {
187
- throw new errors.MissingApiUrlError()
188
- }
189
- if (!concept || !concept.uri) {
190
- throw new errors.InvalidOrMissingParameterError({ parameter: "concept" })
191
- }
192
- return this.axios({
193
- ...config,
194
- method: "get",
195
- url: this._api.narrower,
196
- params: {
197
- ...this._defaultParams,
198
- // ? What should the default limit be?
199
- limit: 10000,
200
- ...(config.params || {}),
201
- uri: concept.uri,
202
- },
203
- })
204
- }
205
-
206
- /**
207
- * Returns ancestor concepts for a concept.
208
- *
209
- * @param {Object} config
210
- * @param {Object} config.concept concept object
211
- * @returns {Object[]} array of JSKOS concept objects
212
- */
213
- async getAncestors({ concept, ...config }) {
214
- if (!this._api.ancestors) {
215
- throw new errors.MissingApiUrlError()
216
- }
217
- if (!concept || !concept.uri) {
218
- throw new errors.InvalidOrMissingParameterError({ parameter: "concept" })
219
- }
220
- return this.axios({
221
- ...config,
222
- method: "get",
223
- url: this._api.ancestors,
224
- params: {
225
- ...this._defaultParams,
226
- // ? What should the default limit be?
227
- limit: 10000,
228
- ...(config.params || {}),
229
- uri: concept.uri,
230
- },
231
- })
232
- }
233
-
234
- /**
235
- * Returns suggestion result in OpenSearch Suggest Format.
236
- *
237
- * @param {Object} config
238
- * @param {string} config.search search string
239
- * @param {Object} [config.scheme] concept scheme to search in
240
- * @param {number} [config.limit=100] maximum number of search results (default might be overridden by registry)
241
- * @param {string} [config.use=notation,label] which fields to search ("notation", "label" or "notation,label")
242
- * @param {string[]} [config.types=[]] list of type URIs
243
- * @param {string} [config.sort=score] sorting parameter
244
- * @returns {Array} result in OpenSearch Suggest Format
245
- */
246
- async suggest({ scheme, use = "notation,label", types = [], sort = "score", ...config }) {
247
- return this._search({
248
- ...config,
249
- endpoint: "suggest",
250
- params: {
251
- ...config.params,
252
- voc: _.get(scheme, "uri", ""),
253
- type: types.join("|"),
254
- use,
255
- sort,
256
- },
257
- })
258
- }
259
-
260
- /**
261
- * Returns search results in JSKOS Format.
262
- *
263
- * @param {Object} config
264
- * @param {string} config.search search string
265
- * @param {Object} [config.scheme] concept scheme to search in
266
- * @param {number} [config.limit=100] maximum number of search results (default might be overridden by registry)
267
- * @param {number} [config.offset=0] offset
268
- * @param {string[]} [config.types=[]] list of type URIs
269
- * @returns {Array} result in JSKOS Format
270
- */
271
- async search({ scheme, types = [], ...config }) {
272
- return this._search({
273
- ...config,
274
- endpoint: "search",
275
- params: {
276
- ...config.params,
277
- voc: _.get(scheme, "uri", ""),
278
- type: types.join("|"),
279
- },
280
- })
281
- }
282
-
283
- /**
284
- * Returns concept scheme suggestion result in OpenSearch Suggest Format.
285
- *
286
- * @param {Object} config
287
- * @param {string} config.search search string
288
- * @param {number} [config.limit=100] maximum number of search results (default might be overridden by registry)
289
- * @param {string} [config.use=notation,label] which fields to search ("notation", "label" or "notation,label")
290
- * @param {string} [config.sort=score] sorting parameter
291
- * @returns {Array} result in OpenSearch Suggest Format
292
- */
293
- async vocSuggest({ use = "notation,label", sort = "score", ...config }) {
294
- return this._search({
295
- ...config,
296
- endpoint: "voc-suggest",
297
- params: {
298
- ...config.params,
299
- use,
300
- sort,
301
- },
302
- })
303
- }
304
-
305
- /**
306
- * Returns concept scheme search results in JSKOS Format.
307
- *
308
- * @param {Object} config
309
- * @param {string} config.search search string
310
- * @param {number} [config.limit=100] maximum number of search results (default might be overridden by registry)
311
- * @param {number} [config.offset=0] offset
312
- * @returns {Array} result in JSKOS Format
313
- */
314
- async vocSearch(config) {
315
- return this._search({
316
- ...config,
317
- endpoint: "voc-search",
318
- })
319
- }
320
-
321
- async _search({ endpoint, search, limit, offset, params, ...config }) {
322
- let url = this._api[endpoint]
323
- if (!url) {
324
- throw new errors.MissingApiUrlError()
325
- }
326
- if (!search) {
327
- throw new errors.InvalidOrMissingParameterError({ parameter: "search" })
328
- }
329
- limit = limit || this._jskos.suggestResultLimit || 100
330
- offset = offset || 0
331
- // Some registries use URL templates with {searchTerms}
332
- url = url.replace("{searchTerms}", search)
333
- return this.axios({
334
- ...config,
335
- params: {
336
- ...this._defaultParams,
337
- ...params,
338
- limit: limit,
339
- count: limit, // Some endpoints use count instead of limit
340
- offset,
341
- search,
342
- query: search,
343
- },
344
- method: "get",
345
- url,
346
- })
347
- }
348
-
349
- /**
350
- * Returns a list of types.
351
- *
352
- * @param {Object} config
353
- * @param {Object} [config.scheme] concept scheme to load types for
354
- * @returns {Object[]} array of JSKOS type objects
355
- */
356
- async getTypes({ scheme, ...config }) {
357
- if (!this._api.types) {
358
- throw new errors.MissingApiUrlError()
359
- }
360
- if (Array.isArray(this._api.types)) {
361
- return this._api.types
362
- }
363
- if (scheme && scheme.uri) {
364
- _.set(config, "params.uri", scheme.uri)
365
- }
366
- return this.axios({
367
- ...config,
368
- method: "get",
369
- url: this._api.types,
370
- })
371
- }
372
-
373
- }
374
-
375
- ConceptApiProvider.providerName = "ConceptApi"
376
-
377
- module.exports = ConceptApiProvider
@@ -1,34 +0,0 @@
1
- const errors = require("../errors")
2
- const BaseProvider = require("./base-provider")
3
-
4
- let providers = {
5
- [BaseProvider.providerName]: BaseProvider,
6
- init(registry) {
7
- if (this[registry.provider]) {
8
- return new this[registry.provider](registry)
9
- }
10
- throw new errors.InvalidProviderError()
11
- },
12
- addProvider(provider) {
13
- if (provider.prototype instanceof providers[BaseProvider.providerName]) {
14
- this[provider.providerName] = provider
15
- } else {
16
- throw new errors.InvalidProviderError()
17
- }
18
- },
19
- }
20
-
21
- for (let provider of [
22
- require("./local-mappings-provider"),
23
- require("./mappings-api-provider"),
24
- require("./occurrences-api-provider"),
25
- require("./concept-api-provider"),
26
- require("./reconciliation-api-provider"),
27
- require("./label-search-suggestion-provider"),
28
- require("./skosmos-api-provider"),
29
- require("./loc-api-provider"),
30
- ]) {
31
- providers.addProvider(provider)
32
- }
33
-
34
- module.exports = providers
@@ -1,219 +0,0 @@
1
- const BaseProvider = require("./base-provider")
2
- const jskos = require("jskos-tools")
3
- const _ = require("../utils/lodash")
4
- const errors = require("../errors")
5
-
6
- // TODO: Only keep the last 20 results in cache.
7
- // TODO: Try to remove dependencies on `selected`, `scheme._registry.registry.uri`, etc.
8
-
9
- /**
10
- * Label search suggestion provider.
11
- *
12
- * This provider offers mapping recommendations based on label match via the `/search` endpoint of JSKOS APIs.
13
- *
14
- * The provider requires that a list of initialized registries with search endpoints is provided via `setRegistries`. Note that it has further dependencies on Cocoda and might be adjusted to reduce these dependencies.
15
- *
16
- * To use it in a registry, specify `provider` as "LabelSearchSuggestion":
17
- * ```json
18
- * {
19
- * "uri": "http://coli-conc.gbv.de/registry/coli-conc-recommendations"
20
- * "provider": "LabelSearchSuggestion"
21
- * }
22
- * ```
23
- *
24
- * You can provide a list of excluded schemes as JSKOS objects in `excludedSchemes`.
25
- *
26
- * Additionally, the following JSKOS properties can be provided: `prefLabel`, `notation`, `definition`
27
- *
28
- * @extends BaseProvider
29
- * @category Providers
30
- */
31
- class LabelSearchSuggestionProvider extends BaseProvider {
32
-
33
- /**
34
- * @private
35
- */
36
- _setup() {
37
- this._cache = []
38
- this.has.mappings = true
39
- }
40
-
41
- /**
42
- * Sets a local list of registries where the search providers are taken from.
43
- *
44
- * @param {Object[]} registries list of registries
45
- */
46
- setRegistries(registries) {
47
- this._registries = registries
48
- }
49
-
50
- /**
51
- * List of search provider URIs.
52
- *
53
- * @private
54
- */
55
- get _searchUris() {
56
- const _searchUris = {}
57
- for (let registry of this._registries) {
58
- const search = _.get(registry, "_api.search") || _.get(registry, "_jskos.search") || registry.search
59
- if (search && _.isString(search)) {
60
- _searchUris[registry.uri] = search
61
- }
62
- }
63
- return _searchUris
64
- }
65
-
66
- /**
67
- * Override `supportsScheme` to check whether a search URI is available for the scheme's registry.
68
- *
69
- * @param {Object} scheme - target scheme to check for support
70
- * @returns {boolean}
71
- */
72
- supportsScheme(scheme) {
73
- let targetRegistry = _.get(scheme, "_registry.uri")
74
- return super.supportsScheme(scheme) && targetRegistry != null && this._searchUris && this._searchUris[targetRegistry]
75
- }
76
-
77
- /**
78
- * Returns a list of mappings.
79
- *
80
- * @param {Object} config
81
- * @param {Object} config.from JSKOS concept on from side
82
- * @param {Object} config.to JSKOS concept on to side
83
- * @param {Object} config.mode mappings mode
84
- * @param {Object} config.selected selected mappings in Cocoda
85
- * @returns {Object[]} array of JSKOS mapping objects
86
- */
87
- async getMappings({ from, to, mode, selected, limit = 10, ...config }) {
88
- // TODO: Why mode?
89
- if (mode != "or") {
90
- return []
91
- }
92
- if (!this._searchUris) {
93
- throw new errors.MissingApiUrlError({ message: "No registries available to search" })
94
- }
95
- if (!selected) {
96
- throw new errors.InvalidOrMissingParameterError({ parameter: "selected" })
97
- }
98
- let promises = []
99
- if (from && this.supportsScheme(selected.scheme[false])) {
100
- promises.push(this._getMappings({ ...config, concept: from, sourceScheme: selected.scheme[true], targetScheme: selected.scheme[false], limit }))
101
- } else {
102
- promises.push(Promise.resolve([]))
103
- }
104
- if (to && this.supportsScheme(selected.scheme[true])) {
105
- promises.push(this._getMappings({ ...config, concept: to, sourceScheme: selected.scheme[false], targetScheme: selected.scheme[true], limit, swap: true }))
106
- } else {
107
- promises.push(Promise.resolve([]))
108
- }
109
- let [fromResult, toResult] = await Promise.all(promises)
110
- // Filter all duplicates from toResult
111
- toResult = toResult.filter(m => !fromResult.find(n => jskos.compareMappingMembers(m, n)))
112
- // Reduce number of results until limit is reached
113
- while (fromResult.length + toResult.length > limit) {
114
- if (toResult.length >= fromResult.length) {
115
- toResult = toResult.slice(0, -1)
116
- } else {
117
- fromResult = fromResult.slice(0, -1)
118
- }
119
- }
120
- return _.union(fromResult, toResult)
121
- }
122
-
123
- /**
124
- * Internal function to get mapping recommendations for a certain concept with sourceScheme and targetScheme.
125
- *
126
- * @private
127
- *
128
- * @param {Object} config
129
- * @param {Object} config.concept
130
- * @param {Object} config.sourceScheme
131
- * @param {Pbject} config.targetScheme
132
- * @param {boolean} config.swap - whether to reverse the direction of the mappings
133
- */
134
- async _getMappings({ concept, sourceScheme, targetScheme, limit, swap = false, ...config }) {
135
- if (!concept || !sourceScheme || !targetScheme) {
136
- return []
137
- }
138
- // If source scheme is the same as target scheme, skip
139
- if (jskos.compare(sourceScheme, targetScheme)) {
140
- return []
141
- }
142
- // Prepare label
143
- // TODO: Can we use a language prioritiy list like for requests?
144
- let label = jskos.prefLabel(concept, {
145
- fallbackToUri: false,
146
- language: this.languages[0] || this._defaultLanguages[0],
147
- })
148
- if (!label) {
149
- return []
150
- }
151
- // Get results from API or cache
152
- const results = await this._getResults({ ...config, label, targetScheme, limit })
153
- // Map results to actual mappings
154
- let mappings = results.map(result => ({
155
- fromScheme: sourceScheme,
156
- from: { memberSet: [concept] },
157
- toScheme: targetScheme,
158
- to: { memberSet: [result] },
159
- type: ["http://www.w3.org/2004/02/skos/core#mappingRelation"],
160
- }))
161
- if (swap) {
162
- // Swap mapping sides if only `to` was set
163
- mappings = mappings.map(mapping => Object.assign(mapping, {
164
- fromScheme: mapping.toScheme,
165
- from: mapping.to,
166
- toScheme: mapping.fromScheme,
167
- to: mapping.from,
168
- }))
169
- }
170
- return mappings
171
- }
172
-
173
- /**
174
- * Internal function that either makes an API request or uses a local cache.
175
- *
176
- * @private
177
- *
178
- * @param {Object} config
179
- * @param {string} config.label
180
- * @param {Object} config.targetScheme
181
- */
182
- async _getResults({ label, targetScheme, limit, ...config }) {
183
- // Use local cache.
184
- let resultsFromCache = (this._cache[targetScheme.uri] || {})[label]
185
- if (resultsFromCache && resultsFromCache._limit >= limit) {
186
- return resultsFromCache
187
- }
188
- // Determine search URI for target scheme's registry
189
- const targetRegistry = _.get(targetScheme, "_registry.uri")
190
- const url = targetRegistry != null && this._searchUris && this._searchUris[targetRegistry]
191
- if (!url) {
192
- return []
193
- }
194
- // API request
195
- const data = await this.axios({
196
- ...config,
197
- method: "get",
198
- url,
199
- params: {
200
- query: label,
201
- limit,
202
- voc: targetScheme.uri,
203
- },
204
- })
205
- // Save result in cache
206
- if (!this._cache[targetScheme.uri]) {
207
- this._cache[targetScheme.uri] = {}
208
- }
209
- this._cache[targetScheme.uri][label] = data
210
- this._cache[targetScheme.uri][label]._limit = limit
211
- return data
212
- }
213
-
214
- }
215
-
216
- LabelSearchSuggestionProvider.providerName = "LabelSearchSuggestion"
217
- LabelSearchSuggestionProvider.stored = false
218
-
219
- module.exports = LabelSearchSuggestionProvider