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.
- package/LICENSE +1 -1
- package/README.md +74 -28
- package/dist/cjs/index.cjs +2673 -0
- package/dist/cocoda-sdk.js +33 -17423
- package/dist/cocoda-sdk.js.LICENSES.txt +105 -86
- package/dist/cocoda-sdk.js.map +7 -0
- package/dist/esm/errors/index.js +46 -0
- package/dist/esm/index.js +9 -0
- package/dist/esm/lib/CocodaSDK.js +269 -0
- package/dist/esm/providers/base-provider.js +368 -0
- package/dist/esm/providers/concept-api-provider.js +278 -0
- package/dist/esm/providers/index.js +20 -0
- package/dist/esm/providers/label-search-suggestion-provider.js +101 -0
- package/dist/esm/providers/loc-api-provider.js +185 -0
- package/dist/esm/providers/local-mappings-provider.js +337 -0
- package/dist/esm/providers/mappings-api-provider.js +264 -0
- package/dist/esm/providers/occurrences-api-provider.js +163 -0
- package/dist/esm/providers/reconciliation-api-provider.js +140 -0
- package/dist/esm/providers/skosmos-api-provider.js +345 -0
- package/{utils → dist/esm/utils}/index.js +40 -53
- package/dist/esm/utils/lodash.js +34 -0
- package/package.json +16 -17
- package/errors/index.js +0 -119
- package/index.js +0 -5
- package/lib/CocodaSDK.js +0 -360
- package/providers/base-provider.js +0 -581
- package/providers/concept-api-provider.js +0 -377
- package/providers/index.js +0 -34
- package/providers/label-search-suggestion-provider.js +0 -219
- package/providers/loc-api-provider.js +0 -275
- package/providers/local-mappings-provider.js +0 -459
- package/providers/mappings-api-provider.js +0 -396
- package/providers/occurrences-api-provider.js +0 -234
- package/providers/reconciliation-api-provider.js +0 -211
- package/providers/skosmos-api-provider.js +0 -441
- package/utils/lodash.js +0 -21
|
@@ -1,581 +0,0 @@
|
|
|
1
|
-
const jskos = require("jskos-tools")
|
|
2
|
-
const _ = require("../utils/lodash")
|
|
3
|
-
const axios = require("axios")
|
|
4
|
-
const utils = require("../utils")
|
|
5
|
-
const errors = require("../errors")
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* BaseProvider to be subclassed to implement specific providers. Do not initialize a registry directly with this!
|
|
9
|
-
*
|
|
10
|
-
* Prefix all internal method and properties with underscore (e.g. `this._cache`)!
|
|
11
|
-
*
|
|
12
|
-
* Methods that can be overridden:
|
|
13
|
-
* - Do not override the constructor! Use _prepare or _setup instead.
|
|
14
|
-
* - _prepare: will be called before the registry is initialized (i.e. it's `/status` endpoint is queries if necessasry)
|
|
15
|
-
* - _setup: will be called after registry is initialized (i.e. it's `/status` endpoint is queries if necessasry), should be used to set properties on this.has and custom preparations
|
|
16
|
-
* - isAuthorizedFor: override if you want to customize
|
|
17
|
-
* - supportsScheme: override if you want to customize
|
|
18
|
-
* - setRegistries: implement this method if the provider needs access to other registries in cocoda-sdk (takes one parameter `registries`)
|
|
19
|
-
* - getRegistries
|
|
20
|
-
* - getSchemes
|
|
21
|
-
* - getTypes
|
|
22
|
-
* - suggest
|
|
23
|
-
* - getConcordances
|
|
24
|
-
* - getOccurrences
|
|
25
|
-
* - getTop
|
|
26
|
-
* - getConcepts
|
|
27
|
-
* - getNarrower
|
|
28
|
-
* - getAncestors
|
|
29
|
-
* - search
|
|
30
|
-
* - getMapping
|
|
31
|
-
* - getMappings
|
|
32
|
-
* - postMapping
|
|
33
|
-
* - postMappings
|
|
34
|
-
* - putMapping
|
|
35
|
-
* - patchMapping
|
|
36
|
-
* - deleteMapping
|
|
37
|
-
* - deleteMappings
|
|
38
|
-
* - getAnnotations
|
|
39
|
-
* - postAnnotation
|
|
40
|
-
* - putAnnotation
|
|
41
|
-
* - patchAnnotation
|
|
42
|
-
* - deleteAnnotation
|
|
43
|
-
*
|
|
44
|
-
* Internal (starting with underscore) and external properties that can be used:
|
|
45
|
-
* - `this.has`: an object of functionality of the registry (needs to be set by subclasses)
|
|
46
|
-
* - `this.languages`: an array of language tags provided by the user in order of priority
|
|
47
|
-
* - `this._jskos`: the raw JSKOS object used to initialize this registry
|
|
48
|
-
* - `this._path`: if available, the path of the current browser window
|
|
49
|
-
* - `this._defaultLanguages`: an array of default language tags
|
|
50
|
-
* - `this._auth`: authentication key and token
|
|
51
|
-
* - `this._api`: object of API endpoints for the registry
|
|
52
|
-
* - `this._config`: configuration of the registry as provided by the `/status` endpoint if available
|
|
53
|
-
*
|
|
54
|
-
* All of the request methods take ONE parameter which is a config object. Actual parameters should be properties on this object. The config object should be destructured to remove the properties your method needs, and the remaining config object should be given to the axios request.
|
|
55
|
-
* Example:
|
|
56
|
-
* ```js
|
|
57
|
-
* getConcept({ concept, ...config }) {
|
|
58
|
-
* return this.axios({
|
|
59
|
-
* ...config,
|
|
60
|
-
* method: "get",
|
|
61
|
-
* params: {
|
|
62
|
-
* uri: concept.uri,
|
|
63
|
-
* },
|
|
64
|
-
* })
|
|
65
|
-
* }
|
|
66
|
-
* ```
|
|
67
|
-
*
|
|
68
|
-
* Always use `this.axios` like in the example for http requests!
|
|
69
|
-
*
|
|
70
|
-
* @category Providers
|
|
71
|
-
*/
|
|
72
|
-
class BaseProvider {
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Provider constructor.
|
|
76
|
-
*
|
|
77
|
-
* @param {Object} registry the registry for this provider
|
|
78
|
-
*/
|
|
79
|
-
constructor(registry = {}) {
|
|
80
|
-
this._jskos = registry
|
|
81
|
-
|
|
82
|
-
this.axios = axios.create({
|
|
83
|
-
// TODO: Decide on timeout value
|
|
84
|
-
timeout: 20000,
|
|
85
|
-
})
|
|
86
|
-
// Path is used for https check and local mappings
|
|
87
|
-
this._path = typeof window !== "undefined" && window.location.pathname
|
|
88
|
-
/**
|
|
89
|
-
* A dictionary with functionality of the registry (e.g. `registry.has.schemes`).
|
|
90
|
-
* @type {Object}
|
|
91
|
-
* @readonly
|
|
92
|
-
*/
|
|
93
|
-
this.has = {}
|
|
94
|
-
// Set default language priority list
|
|
95
|
-
this._defaultLanguages = "en,de,fr,es,nl,it,fi,pl,ru,cs,jp".split(",")
|
|
96
|
-
/**
|
|
97
|
-
* A list of RFC 3066 language tags in lowercase in order of priority.
|
|
98
|
-
* @type {string[]}
|
|
99
|
-
*/
|
|
100
|
-
this.languages = []
|
|
101
|
-
// Set auth details to null
|
|
102
|
-
this._auth = {
|
|
103
|
-
key: null,
|
|
104
|
-
bearerToken: null,
|
|
105
|
-
}
|
|
106
|
-
// Set repeating requests array
|
|
107
|
-
this._repeating = []
|
|
108
|
-
|
|
109
|
-
// Set API URLs from registry object
|
|
110
|
-
this._api = {
|
|
111
|
-
status: registry.status,
|
|
112
|
-
schemes: registry.schemes,
|
|
113
|
-
top: registry.top,
|
|
114
|
-
data: registry.data,
|
|
115
|
-
concepts: registry.concepts,
|
|
116
|
-
narrower: registry.narrower,
|
|
117
|
-
ancestors: registry.ancestors,
|
|
118
|
-
types: registry.types,
|
|
119
|
-
suggest: registry.suggest,
|
|
120
|
-
search: registry.search,
|
|
121
|
-
"voc-suggest": registry["voc-suggest"],
|
|
122
|
-
"voc-search": registry["voc-search"],
|
|
123
|
-
mappings: registry.mappings,
|
|
124
|
-
concordances: registry.concordances,
|
|
125
|
-
annotations: registry.annotations,
|
|
126
|
-
occurrences: registry.occurrences,
|
|
127
|
-
reconcile: registry.reconcile,
|
|
128
|
-
api: registry.api,
|
|
129
|
-
}
|
|
130
|
-
this._config = {}
|
|
131
|
-
|
|
132
|
-
// Set default retry config
|
|
133
|
-
this.setRetryConfig()
|
|
134
|
-
|
|
135
|
-
// Add a request interceptor
|
|
136
|
-
this.axios.interceptors.request.use((config) => {
|
|
137
|
-
// Add language parameter to request
|
|
138
|
-
const language = _.uniq([].concat(_.get(config, "params.language", "").split(","), this.languages, this._defaultLanguages).filter(lang => lang != "")).join(",")
|
|
139
|
-
_.set(config, "params.language", language)
|
|
140
|
-
// Set auth
|
|
141
|
-
if (this.has.auth && this._auth.bearerToken && !_.get(config, "headers.Authorization")) {
|
|
142
|
-
_.set(config, "headers.Authorization", `Bearer ${this._auth.bearerToken}`)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Don't perform http requests if site is used via https
|
|
146
|
-
if (config.url.startsWith("http:") && typeof window !== "undefined" && window.location.protocol == "https:") {
|
|
147
|
-
// TODO: Return proper error object.
|
|
148
|
-
throw new axios.Cancel("Can't call http API from https.")
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return config
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
// Add a response interceptor
|
|
155
|
-
this.axios.interceptors.response.use(({ data, headers = {}, config = {} }) => {
|
|
156
|
-
// Apply unicode normalization
|
|
157
|
-
data = jskos.normalize(data)
|
|
158
|
-
|
|
159
|
-
// Add URL to array as prop
|
|
160
|
-
let url = config.url
|
|
161
|
-
if (!url.endsWith("?")) {
|
|
162
|
-
url += "?"
|
|
163
|
-
}
|
|
164
|
-
_.forOwn(config.params || {}, (value, key) => {
|
|
165
|
-
url += `${key}=${encodeURIComponent(value)}&`
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
if (_.isArray(data) || _.isObject(data)) {
|
|
169
|
-
// Add total count to array as prop
|
|
170
|
-
let totalCount = parseInt(headers["x-total-count"])
|
|
171
|
-
if (!isNaN(totalCount)) {
|
|
172
|
-
data._totalCount = totalCount
|
|
173
|
-
}
|
|
174
|
-
data._url = url
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// TODO: Return data or whole response here?
|
|
178
|
-
return data
|
|
179
|
-
}, error => {
|
|
180
|
-
const count = _.get(error, "config._retryCount", 0)
|
|
181
|
-
const method = _.get(error, "config.method")
|
|
182
|
-
const statusCode = _.get(error, "response.status")
|
|
183
|
-
if (
|
|
184
|
-
this._retryConfig.methods.includes(method)
|
|
185
|
-
&& this._retryConfig.statusCodes.includes(statusCode)
|
|
186
|
-
&& count < this._retryConfig.count
|
|
187
|
-
) {
|
|
188
|
-
error.config._retryCount = count + 1
|
|
189
|
-
// from: https://github.com/axios/axios/issues/934#issuecomment-531463172
|
|
190
|
-
if(error.config.data) error.config.data = JSON.parse(error.config.data)
|
|
191
|
-
return new Promise((resolve, reject) => {
|
|
192
|
-
setTimeout(() => {
|
|
193
|
-
this.axios(error.config).then(resolve).catch(reject)
|
|
194
|
-
}, (() => {
|
|
195
|
-
const delay = this._retryConfig.delay
|
|
196
|
-
if (typeof delay === "function") {
|
|
197
|
-
return delay(count)
|
|
198
|
-
}
|
|
199
|
-
return delay
|
|
200
|
-
})())
|
|
201
|
-
})
|
|
202
|
-
} else {
|
|
203
|
-
return Promise.reject(error)
|
|
204
|
-
}
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
const currentRequests = []
|
|
208
|
-
for (let { method, type } of utils.requestMethods) {
|
|
209
|
-
// Make sure all methods exist, but thrown an error if they are not implemented
|
|
210
|
-
const existingMethod = this[method] && this[method].bind(this)
|
|
211
|
-
if (!existingMethod) {
|
|
212
|
-
this[method] = () => { throw new errors.MethodNotImplementedError({ method }) }
|
|
213
|
-
continue
|
|
214
|
-
}
|
|
215
|
-
this[method] = (options = {}) => {
|
|
216
|
-
// Allow calling the "raw" method without adjustments
|
|
217
|
-
if (options._raw) {
|
|
218
|
-
delete options._raw
|
|
219
|
-
return existingMethod(options)
|
|
220
|
-
}
|
|
221
|
-
// Return from existing requests if one exists
|
|
222
|
-
const existingRequest = currentRequests.find(r => r.method == method && _.isEqual(r.options, options))
|
|
223
|
-
if (existingRequest) {
|
|
224
|
-
return existingRequest.promise
|
|
225
|
-
}
|
|
226
|
-
// Add an axios cancel token to each request
|
|
227
|
-
let source
|
|
228
|
-
if (!options.cancelToken) {
|
|
229
|
-
source = this.getCancelTokenSource()
|
|
230
|
-
options.cancelToken = source.token
|
|
231
|
-
}
|
|
232
|
-
// Make sure a registry is initialized (see `init` method) before any request
|
|
233
|
-
// TODO: Is this a good solution?
|
|
234
|
-
const promise = this.init()
|
|
235
|
-
.then(() => existingMethod(options))
|
|
236
|
-
// Add totalCount to arrays
|
|
237
|
-
.then(result => {
|
|
238
|
-
if (_.isArray(result) && result._totalCount === undefined) {
|
|
239
|
-
result._totalCount = result.length
|
|
240
|
-
} else if (_.isObject(result) && result._totalCount === undefined) {
|
|
241
|
-
result._totalCount = 1
|
|
242
|
-
}
|
|
243
|
-
if (result && type && this[`adjust${type}`]) {
|
|
244
|
-
result = this[`adjust${type}`](result)
|
|
245
|
-
}
|
|
246
|
-
return result
|
|
247
|
-
}).catch(error => {
|
|
248
|
-
if (error instanceof errors.CDKError) {
|
|
249
|
-
throw error
|
|
250
|
-
} else {
|
|
251
|
-
if (error.response) {
|
|
252
|
-
// 4xx = invalid request
|
|
253
|
-
if (error.response.status.toString().startsWith(4)) {
|
|
254
|
-
throw new errors.InvalidRequestError({ relatedError: error, code: error.response.status })
|
|
255
|
-
} else {
|
|
256
|
-
throw new errors.BackendError({ relatedError: error, code: error.response.status })
|
|
257
|
-
}
|
|
258
|
-
} else if (error.request) {
|
|
259
|
-
if (typeof navigator !== "undefined") {
|
|
260
|
-
// If connected, it should be a backend problem
|
|
261
|
-
if (navigator.connection || navigator.mozConnection || navigator.webkitConnection) {
|
|
262
|
-
throw new errors.BackendUnavailableError({ relatedError: error })
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
// Otherwise, assume a network error
|
|
266
|
-
throw new errors.NetworkError({ relatedError: error })
|
|
267
|
-
} else {
|
|
268
|
-
// Otherwise, throw generic CDKError
|
|
269
|
-
throw new errors.CDKError({ relatedError: error })
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
})
|
|
273
|
-
// Attach cancel method to Promise
|
|
274
|
-
if (source) {
|
|
275
|
-
promise.cancel = (...args) => {
|
|
276
|
-
return source.cancel(...args)
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
// Save to list of existing requests
|
|
280
|
-
const request = {
|
|
281
|
-
method,
|
|
282
|
-
options: _.omit(options, ["cancelToken"]),
|
|
283
|
-
promise,
|
|
284
|
-
}
|
|
285
|
-
currentRequests.push(request)
|
|
286
|
-
// Remove from list of current requests after promise is done
|
|
287
|
-
promise.catch(() => {}).then(() => currentRequests.splice(currentRequests.indexOf(request), 1))
|
|
288
|
-
// Add adjustment methods
|
|
289
|
-
return promise
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Expose some properties from original registry object as getters
|
|
295
|
-
get uri() { return this._jskos.uri }
|
|
296
|
-
get notation() { return this._jskos.notation }
|
|
297
|
-
get prefLabel() { return this._jskos.prefLabel }
|
|
298
|
-
get definition() { return this._jskos.definition }
|
|
299
|
-
get schemes() { return this._jskos.schemes }
|
|
300
|
-
get excludedSchemes() { return this._jskos.excludedSchemes }
|
|
301
|
-
get stored() { return this._jskos.stored !== undefined ? this._jskos.stored : this.constructor.stored }
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Load data about registry via the status endpoint.
|
|
305
|
-
*
|
|
306
|
-
* @returns {Promise} Promise that resolves when initialization is complete.
|
|
307
|
-
*/
|
|
308
|
-
async init() {
|
|
309
|
-
// Save the actual Promise in _init and return it immediately on a second call
|
|
310
|
-
if (this._init) {
|
|
311
|
-
return this._init
|
|
312
|
-
}
|
|
313
|
-
this._init = (async () => {
|
|
314
|
-
// Call preparation method
|
|
315
|
-
this._prepare()
|
|
316
|
-
let status
|
|
317
|
-
if (_.isString(this._api.status)) {
|
|
318
|
-
// Request status endpoint
|
|
319
|
-
try {
|
|
320
|
-
status = await this.axios({
|
|
321
|
-
method: "get",
|
|
322
|
-
url: this._api.status,
|
|
323
|
-
})
|
|
324
|
-
} catch(error) {
|
|
325
|
-
if (_.get(error, "response.status") === 404) {
|
|
326
|
-
// If /status is not available, remove from _api
|
|
327
|
-
this._api.status = null
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
} else {
|
|
331
|
-
// Assume object
|
|
332
|
-
status = this._api.status
|
|
333
|
-
}
|
|
334
|
-
if (_.isObject(status) && !_.isEmpty(status)) {
|
|
335
|
-
// Set config
|
|
336
|
-
this._config = status.config || {}
|
|
337
|
-
// Merge status result and existing API URLs
|
|
338
|
-
for (let key of Object.keys(this._api)) {
|
|
339
|
-
// Only override if undefined
|
|
340
|
-
if (this._api[key] === undefined) {
|
|
341
|
-
// Fall back to null, i.e. if /status was successful, no endpoints are implied by the provider
|
|
342
|
-
// See also: https://github.com/gbv/cocoda-sdk/issues/21
|
|
343
|
-
this._api[key] = status[key] || null
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
this._setup()
|
|
348
|
-
})()
|
|
349
|
-
return this._init
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Preparation to be executed before init. Should be overwritten by subclasses.
|
|
354
|
-
*
|
|
355
|
-
* @private
|
|
356
|
-
*/
|
|
357
|
-
_prepare() {}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Setup to be executed after init. Should be overwritten by subclasses.
|
|
361
|
-
*
|
|
362
|
-
* @private
|
|
363
|
-
*/
|
|
364
|
-
_setup() {}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Returns a source for a axios cancel token.
|
|
368
|
-
*
|
|
369
|
-
* @returns {Object} axios cancel token source
|
|
370
|
-
*/
|
|
371
|
-
getCancelTokenSource() {
|
|
372
|
-
return axios.CancelToken.source()
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Sets authentication credentials.
|
|
377
|
-
*
|
|
378
|
-
* @param {Object} options
|
|
379
|
-
* @param {string} options.key public key of login-server instance the user is authorized for
|
|
380
|
-
* @param {string} options.bearerToken token that is sent with each request
|
|
381
|
-
*/
|
|
382
|
-
setAuth({ key = this._auth.key, bearerToken = this._auth.bearerToken }) {
|
|
383
|
-
this._auth.key = key
|
|
384
|
-
this._auth.bearerToken = bearerToken
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Sets retry configuration.
|
|
389
|
-
*
|
|
390
|
-
* @param {Object} config
|
|
391
|
-
* @param {string[]} [config.methods=["get", "head", "options"]] HTTP methods to retry (lowercase)
|
|
392
|
-
* @param {number[]} [config.statusCodes=[401, 403]] status codes to retry
|
|
393
|
-
* @param {number} [config.count=3] maximum number of retries
|
|
394
|
-
* @param {number|Function} [config.delay=300*count] a delay in ms or a function that takes the number of current retries and returns a delay in ms
|
|
395
|
-
*/
|
|
396
|
-
setRetryConfig(config = {}) {
|
|
397
|
-
this._retryConfig = Object.assign({
|
|
398
|
-
methods: ["get", "head", "options"],
|
|
399
|
-
statusCodes: [401, 403],
|
|
400
|
-
count: 3,
|
|
401
|
-
delay: (count) => {
|
|
402
|
-
return 300 * count
|
|
403
|
-
},
|
|
404
|
-
}, config)
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/**
|
|
408
|
-
* Returns whether a user is authorized for a certain request.
|
|
409
|
-
*
|
|
410
|
-
* @param {Object} options
|
|
411
|
-
* @param {string} options.type type of item (e.g. mappings)
|
|
412
|
-
* @param {string} options.action action to be performed (read/create/update/delete)
|
|
413
|
-
* @param {Object} options.user user object
|
|
414
|
-
* @param {boolean} [options.crossUser] whether the request is a crossUser request (i.e. updading/deleting another user's item)
|
|
415
|
-
* @returns {boolean}
|
|
416
|
-
*/
|
|
417
|
-
isAuthorizedFor({ type, action, user, crossUser }) {
|
|
418
|
-
if (action == "read" && this.has[type] === true) {
|
|
419
|
-
return true
|
|
420
|
-
}
|
|
421
|
-
if (!this.has[type]) {
|
|
422
|
-
return false
|
|
423
|
-
}
|
|
424
|
-
const options = _.get(this._config, `${type}.${action}`)
|
|
425
|
-
if (!options) {
|
|
426
|
-
return !!this.has[type][action]
|
|
427
|
-
}
|
|
428
|
-
if (options.auth && (!user || !this._auth.key)) {
|
|
429
|
-
return false
|
|
430
|
-
}
|
|
431
|
-
// Public key mismatch
|
|
432
|
-
if (options.auth && this._auth.key != _.get(this._config, "auth.key")) {
|
|
433
|
-
return false
|
|
434
|
-
}
|
|
435
|
-
if (options.auth && options.identities) {
|
|
436
|
-
// Check if one of the user's identities matches
|
|
437
|
-
const uris = [user.uri].concat(Object.values(user.identities || {}).map(id => id.uri)).filter(uri => uri != null)
|
|
438
|
-
if (_.intersection(uris, options.identities).length == 0) {
|
|
439
|
-
return false
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
if (options.auth && options.identityProviders) {
|
|
443
|
-
// Check if user has the required provider
|
|
444
|
-
const providers = Object.keys((user && user.identities) || {})
|
|
445
|
-
if (_.intersection(providers, options.identityProviders).length == 0) {
|
|
446
|
-
return false
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
if (crossUser) {
|
|
450
|
-
return !!options.crossUser
|
|
451
|
-
}
|
|
452
|
-
return !!this.has[type][action]
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
/**
|
|
456
|
-
* Returns a boolean whether a certain target scheme is supported or not.
|
|
457
|
-
*
|
|
458
|
-
* @param {Object} scheme
|
|
459
|
-
* @returns {boolean}
|
|
460
|
-
*/
|
|
461
|
-
supportsScheme(scheme) {
|
|
462
|
-
if (!scheme) {
|
|
463
|
-
return false
|
|
464
|
-
}
|
|
465
|
-
let schemes = _.isArray(this.schemes) ? this.schemes : null
|
|
466
|
-
if (schemes == null && !jskos.isContainedIn(scheme, this.excludedSchemes || [])) {
|
|
467
|
-
return true
|
|
468
|
-
}
|
|
469
|
-
return jskos.isContainedIn(scheme, schemes)
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
adjustConcept(concept) {
|
|
473
|
-
// Add _getNarrower function to concepts
|
|
474
|
-
concept._getNarrower = (config) => {
|
|
475
|
-
return this.getNarrower({ ...config, concept })
|
|
476
|
-
}
|
|
477
|
-
// Add _getAncestors function to concepts
|
|
478
|
-
concept._getAncestors = (config) => {
|
|
479
|
-
return this.getAncestors({ ...config, concept })
|
|
480
|
-
}
|
|
481
|
-
// Add _getDetails function to concepts
|
|
482
|
-
concept._getDetails = async (config) => {
|
|
483
|
-
return (await this.getConcepts({ ...config, concepts: [concept] }))[0]
|
|
484
|
-
}
|
|
485
|
-
// Adjust broader/narrower/ancestors if necessary
|
|
486
|
-
for (let type of ["broader", "narrower", "ancestors"]) {
|
|
487
|
-
if (Array.isArray(concept[type]) && !concept[type].includes(null)) {
|
|
488
|
-
concept[type] = this.adjustConcepts(concept[type])
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
// Add _registry to concepts
|
|
492
|
-
concept._registry = this
|
|
493
|
-
return concept
|
|
494
|
-
}
|
|
495
|
-
adjustConcepts(concepts) {
|
|
496
|
-
return utils.withCustomProps(concepts.map(concept => this.adjustConcept(concept)), concepts)
|
|
497
|
-
}
|
|
498
|
-
adjustRegistries(registries) {
|
|
499
|
-
return registries
|
|
500
|
-
}
|
|
501
|
-
adjustSchemes(schemes) {
|
|
502
|
-
for (let scheme of schemes) {
|
|
503
|
-
// Add _getTop function to schemes
|
|
504
|
-
scheme._getTop = (config) => {
|
|
505
|
-
return this.getTop({ ...config, scheme })
|
|
506
|
-
}
|
|
507
|
-
// Add _getTypes function to schemes
|
|
508
|
-
scheme._getTypes = (config) => {
|
|
509
|
-
return this.getTypes({ ...config, scheme })
|
|
510
|
-
}
|
|
511
|
-
// Add _registry to schemes
|
|
512
|
-
scheme._registry = this
|
|
513
|
-
// Add _suggest function to schemes
|
|
514
|
-
scheme._suggest = ({ search, ...config }) => {
|
|
515
|
-
return this.suggest({ ...config, search, scheme })
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
return schemes
|
|
519
|
-
}
|
|
520
|
-
adjustConcordances(concordances) {
|
|
521
|
-
for (let concordance of concordances) {
|
|
522
|
-
// Add _registry to concordance
|
|
523
|
-
concordance._registry = this
|
|
524
|
-
}
|
|
525
|
-
return concordances
|
|
526
|
-
}
|
|
527
|
-
adjustMapping(mapping) {
|
|
528
|
-
// TODO: Add default type
|
|
529
|
-
// Add fromScheme and toScheme if missing
|
|
530
|
-
for (let side of ["from", "to"]) {
|
|
531
|
-
let sideScheme = `${side}Scheme`
|
|
532
|
-
if (!mapping[sideScheme]) {
|
|
533
|
-
mapping[sideScheme] = _.get(jskos.conceptsOfMapping(mapping, side), "[0].inScheme[0]", null)
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
mapping._registry = this
|
|
537
|
-
if (!mapping.identifier) {
|
|
538
|
-
// Add mapping identifiers for this mapping
|
|
539
|
-
let identifier = _.get(jskos.addMappingIdentifiers(mapping), "identifier")
|
|
540
|
-
if (identifier) {
|
|
541
|
-
mapping.identifier = identifier
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
return mapping
|
|
545
|
-
}
|
|
546
|
-
adjustMappings(mappings) {
|
|
547
|
-
return utils.withCustomProps(mappings.map(mapping => this.adjustMapping(mapping)), mappings)
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
/**
|
|
551
|
-
* POSTs multiple mappings. Do not override in subclass!
|
|
552
|
-
*
|
|
553
|
-
* @param {Object} config
|
|
554
|
-
* @param {Array} config.mappings array of mapping objects
|
|
555
|
-
* @returns {Object[]} array of created mapping objects
|
|
556
|
-
*/
|
|
557
|
-
async postMappings({ mappings, ...config } = {}) {
|
|
558
|
-
if (!mappings || !mappings.length) {
|
|
559
|
-
throw new errors.InvalidOrMissingParameterError({ parameter: "mappings" })
|
|
560
|
-
}
|
|
561
|
-
return Promise.all(mappings.map(mapping => this.postMapping({ mapping, ...config, _raw: true })))
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
/**
|
|
565
|
-
* DELETEs multiple mappings. Do not override in subclass!
|
|
566
|
-
*
|
|
567
|
-
* @param {Object} config
|
|
568
|
-
* @param {Array} config.mappings array of mapping objects
|
|
569
|
-
*/
|
|
570
|
-
async deleteMappings({ mappings, ...config } = {}) {
|
|
571
|
-
if (!mappings || !mappings.length) {
|
|
572
|
-
throw new errors.InvalidOrMissingParameterError({ parameter: "mappings" })
|
|
573
|
-
}
|
|
574
|
-
return Promise.all(mappings.map(mapping => this.deleteMapping({ mapping, ...config, _raw: true })))
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
BaseProvider.providerName = "Base"
|
|
580
|
-
|
|
581
|
-
module.exports = BaseProvider
|