jskos-server 2.4.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 (112) hide show
  1. package/.dockerignore +20 -0
  2. package/.editorconfig +9 -0
  3. package/.github/workflows/docker.yml +59 -0
  4. package/.github/workflows/gh-pages.yml +23 -0
  5. package/.github/workflows/gh-release.yml +19 -0
  6. package/.github/workflows/test.yml +39 -0
  7. package/.husky/pre-commit +1 -0
  8. package/CHANGELOG.md +18 -0
  9. package/LICENSE +21 -0
  10. package/README.md +2710 -0
  11. package/bin/extra.js +81 -0
  12. package/bin/import.js +438 -0
  13. package/bin/mongodb.js +21 -0
  14. package/bin/reset.js +257 -0
  15. package/bin/upgrade.js +34 -0
  16. package/config/config.default.json +88 -0
  17. package/config/config.schema.json +877 -0
  18. package/config/config.test.json +107 -0
  19. package/config/index.js +77 -0
  20. package/config/setup.js +212 -0
  21. package/depdendencies.png +0 -0
  22. package/docker/.env +1 -0
  23. package/docker/Dockerfile +20 -0
  24. package/docker/README.md +175 -0
  25. package/docker/docker-compose.yml +29 -0
  26. package/docker/docker-entrypoint.sh +8 -0
  27. package/docker/mongo-initdb.d/mongo_setup.sh +22 -0
  28. package/ecosystem.example.json +7 -0
  29. package/errors/index.js +94 -0
  30. package/eslint.config.js +17 -0
  31. package/index.js +10 -0
  32. package/models/annotations.js +13 -0
  33. package/models/concepts.js +12 -0
  34. package/models/concordances.js +12 -0
  35. package/models/index.js +33 -0
  36. package/models/mappings.js +20 -0
  37. package/models/meta.js +21 -0
  38. package/models/registries.js +12 -0
  39. package/models/schemes.js +12 -0
  40. package/package.json +91 -0
  41. package/routes/annotations.js +83 -0
  42. package/routes/common.js +86 -0
  43. package/routes/concepts.js +64 -0
  44. package/routes/concordances.js +86 -0
  45. package/routes/data.js +19 -0
  46. package/routes/mappings.js +108 -0
  47. package/routes/registries.js +24 -0
  48. package/routes/schemes.js +72 -0
  49. package/routes/validate.js +37 -0
  50. package/server.js +190 -0
  51. package/services/abstract.js +328 -0
  52. package/services/annotations.js +237 -0
  53. package/services/concepts.js +459 -0
  54. package/services/concordances.js +264 -0
  55. package/services/data.js +30 -0
  56. package/services/index.js +34 -0
  57. package/services/mappings.js +978 -0
  58. package/services/registries.js +319 -0
  59. package/services/schemes.js +318 -0
  60. package/services/validate.js +39 -0
  61. package/status.schema.json +145 -0
  62. package/test/abstract-service.js +36 -0
  63. package/test/annotations/annotation.json +13 -0
  64. package/test/api.js +2481 -0
  65. package/test/chai.js +14 -0
  66. package/test/changes.js +179 -0
  67. package/test/concepts/conceptNoFileEnding +4 -0
  68. package/test/concepts/concepts-ddc-6-60-61-62.json +123 -0
  69. package/test/concordances/concordances.ndjson +2 -0
  70. package/test/config.js +26 -0
  71. package/test/configs/complex-config.json +90 -0
  72. package/test/configs/empty-object.json +1 -0
  73. package/test/configs/fail-array.json +1 -0
  74. package/test/configs/fail-empty.json +0 -0
  75. package/test/configs/fail-mapping-only-props1.json +5 -0
  76. package/test/configs/fail-mapping-only-props2.json +5 -0
  77. package/test/configs/fail-mapping-only-props3.json +5 -0
  78. package/test/configs/fail-mapping-only-props4.json +5 -0
  79. package/test/configs/fail-nonexisting-prop.json +3 -0
  80. package/test/configs/fail-port-string.json +3 -0
  81. package/test/configs/fail-registry-types.json +16 -0
  82. package/test/configs/registry-types.json +16 -0
  83. package/test/data-write.js +784 -0
  84. package/test/eslint.js +22 -0
  85. package/test/import-reset.js +287 -0
  86. package/test/infer-mappings.js +340 -0
  87. package/test/ipcheck.js +287 -0
  88. package/test/mappings/README.md +1 -0
  89. package/test/mappings/ddc-gnd-1.mapping.json +33 -0
  90. package/test/mappings/ddc-gnd-2.mapping.json +67 -0
  91. package/test/mappings/mapping-ddc-gnd-noScheme.json +145 -0
  92. package/test/mappings/mapping-ddc-gnd.json +175 -0
  93. package/test/mappings/mappings-ddc.json +214 -0
  94. package/test/registries/registries.ndjson +2 -0
  95. package/test/services.js +557 -0
  96. package/test/terminologies/terminologies.json +94 -0
  97. package/test/test-utils.js +182 -0
  98. package/test/utils.js +425 -0
  99. package/test/validate.js +226 -0
  100. package/utils/adjust.js +206 -0
  101. package/utils/auth.js +154 -0
  102. package/utils/changes.js +88 -0
  103. package/utils/db.js +106 -0
  104. package/utils/ipcheck.js +76 -0
  105. package/utils/middleware.js +636 -0
  106. package/utils/searchHelper.js +153 -0
  107. package/utils/status.js +77 -0
  108. package/utils/users.js +7 -0
  109. package/utils/utils.js +114 -0
  110. package/utils/uuid.js +6 -0
  111. package/utils/version.js +324 -0
  112. package/views/base.ejs +172 -0
package/test/eslint.js ADDED
@@ -0,0 +1,22 @@
1
+ import assert from "node:assert"
2
+ import { loadESLint } from "eslint"
3
+
4
+ const DefaultESLint = await loadESLint()
5
+ const eslint = new DefaultESLint()
6
+ const results = await eslint.lintFiles([
7
+ "**/*.js",
8
+ ])
9
+
10
+ describe("ESLint Errors", () => {
11
+
12
+ results.forEach(result => {
13
+ it(result.filePath, (done) => {
14
+ assert(
15
+ result.errorCount === 0,
16
+ "\n" + result.messages.map(m => `\t\t${m.line}:${m.column}\terror\t${m.message}\t${m.ruleId}`).join("\n"),
17
+ )
18
+ done()
19
+ })
20
+ })
21
+
22
+ })
@@ -0,0 +1,287 @@
1
+ import * as server from "../server.js"
2
+ import assert from "node:assert"
3
+
4
+ import { teardownInMemoryMongo, createCollectionsAndIndexes, setupInMemoryMongo, assertIndexes, assertMongoDB, dropDatabaseBeforeAndAfter, exec } from "./test-utils.js"
5
+
6
+ describe("Import and Reset Script", () => {
7
+
8
+ before(async () => {
9
+ const mongoUri = await setupInMemoryMongo({ replSet: false })
10
+ process.env.MONGO_URI = mongoUri
11
+ await createCollectionsAndIndexes()
12
+ })
13
+
14
+ after(async () => {
15
+ // close server if you started one
16
+ await teardownInMemoryMongo()
17
+ })
18
+
19
+ // 🗑 Drop DB before *and* after every single `it()` in this file
20
+ dropDatabaseBeforeAndAfter()
21
+
22
+ // 🔌 Sanity‐check that mongoose really is connected
23
+ assertMongoDB()
24
+
25
+ describe("Import Script", () => {
26
+
27
+ it("should create index on one type", async () => {
28
+ const type = "mappings"
29
+ await exec("NODE_ENV=test ./bin/import.js --indexes " + type)
30
+ for (let collection of ["terminologies", "concepts", "concordances", "mappings", "annotations"]) {
31
+ try {
32
+ const result = await server.db.collection(collection).indexInformation()
33
+ if (collection == type) {
34
+ assert(Object.keys(result).length >= 3)
35
+ } else {
36
+ assert.fail(`Expected index information to fail for ${collection}.`)
37
+ }
38
+ } catch (error) {
39
+ if (collection == type) {
40
+ assert.fail(`Expected index information to return result for ${collection}.`)
41
+ }
42
+ }
43
+ }
44
+ })
45
+
46
+ assertIndexes()
47
+
48
+ it("should import terminologies", async () => {
49
+ // Add vocabularies to database
50
+ await exec("NODE_ENV=test ./bin/import.js schemes ./test/terminologies/terminologies.json")
51
+ const results = await server.db.collection("terminologies").find({}).toArray()
52
+ assert.strictEqual(results.length, 3)
53
+ })
54
+
55
+ it("should import concepts", async () => {
56
+ // Add concepts to database
57
+ await exec("NODE_ENV=test ./bin/import.js -q concepts ./test/concepts/concepts-ddc-6-60-61-62.json")
58
+ const results = await server.db.collection("concepts").find({}).toArray()
59
+ assert.strictEqual(results.length, 4)
60
+ })
61
+
62
+ it("should import concepts from file with --format option", async () => {
63
+ const filename = "conceptNoFileEnding"
64
+ const command = `NODE_ENV=test ./bin/import.js concepts ./test/concepts/${filename}`
65
+ // First, without -f
66
+ try {
67
+ await exec(command)
68
+ assert.fail("Expected import without file ending and without format option to fail.")
69
+ } catch (error) {
70
+ // Ignore error
71
+ }
72
+ // Then with -f
73
+ await exec(`${command} --format json`)
74
+ const results = await server.db.collection("concepts").find({ uri: `uri:${filename}` }).toArray()
75
+ assert.strictEqual(results.length, 1)
76
+ })
77
+
78
+ it("should import concordances", async () => {
79
+ // Add concordances to database
80
+ await exec("NODE_ENV=test ./bin/import.js concordances ./test/concordances/concordances.ndjson")
81
+ const results = await server.db.collection("concordances").find({}).toArray()
82
+ assert.strictEqual(results.length, 2)
83
+ })
84
+
85
+ it("should not import mappings without scheme", async () => {
86
+ await exec("NODE_ENV=test ./bin/import.js mappings ./test/mappings/mapping-ddc-gnd-noScheme.json")
87
+ const results = await server.db.collection("mappings").find({}).toArray()
88
+ assert.strictEqual(results.length, 0)
89
+ })
90
+
91
+ it("should import mappings", async () => {
92
+ // Add mappings to database
93
+ await exec("NODE_ENV=test ./bin/import.js mappings ./test/mappings/mapping-ddc-gnd.json")
94
+ const results = await server.db.collection("mappings").find({}).toArray()
95
+ assert.strictEqual(results.length, 3)
96
+ })
97
+
98
+ it("should import mappings into a concordance (and set fromScheme/toScheme for the mappings)", async () => {
99
+ let results
100
+ const concordance = "http://coli-conc.gbv.de/concordances/ddc_rvk_medizin"
101
+ const query = { "partOf.uri": concordance }
102
+ // Before, it should have no mappings
103
+ results = await server.db.collection("mappings").find(query).toArray()
104
+ assert.strictEqual(results.length, 0)
105
+ // Add mappings to database
106
+ await exec(`NODE_ENV=test ./bin/import.js mappings ./test/mappings/mapping-ddc-gnd-noScheme.json -c ${concordance}`)
107
+ results = await server.db.collection("mappings").find(query).toArray()
108
+ assert.strictEqual(results.length, 3)
109
+ results.forEach(mapping => {
110
+ assert.ok(mapping.fromScheme?.uri, "fromScheme field is missing from mapping")
111
+ assert.ok(mapping.toScheme?.uri, "toScheme field is missing from mapping")
112
+ })
113
+ })
114
+
115
+ it("should import annotations", async () => {
116
+ // Import one annotation
117
+ let results
118
+ results = await server.db.collection("annotations").find({}).toArray()
119
+ assert.strictEqual(results.length, 0)
120
+ await exec("NODE_ENV=test ./bin/import.js annotations ./test/annotations/annotation.json")
121
+ results = await server.db.collection("annotations").find({}).toArray()
122
+ assert.strictEqual(results.length, 1)
123
+ })
124
+
125
+ it("should fail on --reset (was removed)", async () => {
126
+ try {
127
+ await exec("NODE_ENV=test ./bin/import.js --reset")
128
+ assert.fail("Expected import script with option --reset to fail.")
129
+ } catch (error) {
130
+ // Ignore error
131
+ }
132
+ })
133
+
134
+ it("should fail without parameters", async () => {
135
+ try {
136
+ await exec("NODE_ENV=test ./bin/import.js")
137
+ assert.fail("Expected import script to fail without parameters.")
138
+ } catch (error) {
139
+ // Ignore error
140
+ }
141
+ })
142
+
143
+ it("should fail on with invalid type option", async () => {
144
+ try {
145
+ await exec("NODE_ENV=test ./bin/import.js asbfamshbfa")
146
+ assert.fail("Expected import script with invalid type option to fail.")
147
+ } catch (error) {
148
+ // Ignore error
149
+ }
150
+ })
151
+
152
+ it("should fail when no file is given", async () => {
153
+ try {
154
+ await exec("NODE_ENV=test ./bin/import.js concepts")
155
+ assert.fail("Expected import script with no file to fail.")
156
+ } catch (error) {
157
+ // Ignore error
158
+ }
159
+ })
160
+
161
+ it("should fail when -c is used with type other than mapping", async () => {
162
+ try {
163
+ await exec("NODE_ENV=test ./bin/import.js -c some:uri concepts ./some/file/doesntmatter")
164
+ assert.fail("Expected import script with option -c to fail when type is not mapping.")
165
+ } catch (error) {
166
+ // Ignore error
167
+ }
168
+ })
169
+
170
+ })
171
+
172
+ describe("Reset Script", () => {
173
+
174
+ it("should abort reset script when given no as input", async () => {
175
+ try {
176
+ await exec("echo no | NODE_ENV=test ./bin/reset.js")
177
+ assert.fail("Expected reset script to abort.")
178
+ } catch (error) {
179
+ assert(error.stdout.includes("Is that okay?"))
180
+ assert(error.stderr.includes("Error: Aborting"))
181
+ }
182
+ })
183
+
184
+ const type = "annotations"
185
+ it(`should delete ${type}`, async () => {
186
+ await exec(`yes | NODE_ENV=test ./bin/reset.js -t ${type}`)
187
+ for (let collection of ["concepts", "mappings", "terminologies", "annotations", "concordances"]) {
188
+ const result = await server.db.collection(collection).find({}).toArray()
189
+ if (collection == type) {
190
+ assert.strictEqual(result.length, 0)
191
+ } else {
192
+ assert.notStrictEqual(result.length, 0)
193
+ }
194
+ }
195
+ })
196
+
197
+ it("should delete concepts from certain scheme", async () => {
198
+ const scheme = "http://dewey.info/scheme/edition/e23/"
199
+ try {
200
+ await exec(`echo no | NODE_ENV=test ./bin/reset.js -s ${scheme}`)
201
+ assert.fail("Expected exec to fail.")
202
+ } catch (error) {
203
+ assert(error.stdout.includes(`from scheme ${scheme}`))
204
+ assert(error.stdout.includes("concepts will be deleted"))
205
+ }
206
+ })
207
+
208
+ it("should delete mappings from certain concordance", async () => {
209
+ const concordance = "http://coli-conc.gbv.de/concordances/ddc_rvk_medizin"
210
+ try {
211
+ await exec(`echo no | NODE_ENV=test ./bin/reset.js -c ${concordance}`)
212
+ assert.fail("Expected exec to fail.")
213
+ } catch (error) {
214
+ assert(error.stdout.includes(`from concordance ${concordance}`))
215
+ assert(error.stdout.includes("mappings will be deleted"))
216
+ }
217
+ })
218
+
219
+ it("should delete entities with specific URIs", async () => {
220
+ let results
221
+ results = await server.db.collection("concepts").find({}).toArray()
222
+ const lengthBefore = results.length
223
+ const uris = results.map(r => r.uri).slice(0, 2)
224
+ await exec(`yes | NODE_ENV=test ./bin/reset.js ${uris.join(" ")}`)
225
+ results = await server.db.collection("concepts").find({}).toArray()
226
+ assert.strictEqual(results.length, lengthBefore - uris.length)
227
+ })
228
+
229
+ const abortMessageForNoEntities = "Did not find any entities to be deleted, aborting"
230
+
231
+ it("should fail when -s and -c are given", async () => {
232
+ try {
233
+ await exec("yes | NODE_ENV=test ./bin/reset.js -s a:b -c c:d")
234
+ assert.fail("Expected exec to fail.")
235
+ } catch (error) {
236
+ assert(!error.stderr.includes(abortMessageForNoEntities))
237
+ }
238
+ })
239
+
240
+ it("should fail when -s is used with type other than concepts", async () => {
241
+ try {
242
+ await exec("yes | NODE_ENV=test ./bin/reset.js -s a:b -t mappings")
243
+ assert.fail("Expected exec to fail.")
244
+ } catch (error) {
245
+ assert(!error.stderr.includes(abortMessageForNoEntities))
246
+ }
247
+ })
248
+
249
+ it("should fail when -c is used with type other than mappings", async () => {
250
+ try {
251
+ await exec("yes | NODE_ENV=test ./bin/reset.js -c c:d -t concepts")
252
+ assert.fail("Expected exec to fail.")
253
+ } catch (error) {
254
+ assert(!error.stderr.includes(abortMessageForNoEntities))
255
+ }
256
+ })
257
+
258
+ it("should fail when -s is used with URI", async () => {
259
+ try {
260
+ await exec("yes | NODE_ENV=test ./bin/reset.js -s a:b c:d")
261
+ assert.fail("Expected exec to fail.")
262
+ } catch (error) {
263
+ assert(!error.stderr.includes(abortMessageForNoEntities))
264
+ }
265
+ })
266
+
267
+ it("should clear the whole database", async () => {
268
+ // Clear database
269
+ await exec("yes | NODE_ENV=test ./bin/reset.js")
270
+ for (let collection of ["concepts", "mappings", "terminologies"]) {
271
+ const result = await server.db.collection(collection).find({}).toArray()
272
+ assert.strictEqual(result.length, 0)
273
+ }
274
+ })
275
+
276
+ it("should fail if trying to clear the database a second time", async () => {
277
+ try {
278
+ await exec("yes | NODE_ENV=test ./bin/reset.js")
279
+ assert.fail("Expected reset script to fail if there are no entities to delete.")
280
+ } catch (error) {
281
+ assert(error.stderr.includes(abortMessageForNoEntities))
282
+ }
283
+ })
284
+
285
+ })
286
+
287
+ })
@@ -0,0 +1,340 @@
1
+ import chai from "./chai.js"
2
+
3
+ import * as server from "../server.js"
4
+ import assert from "node:assert"
5
+ import { assertIndexes, assertMongoDB, dropDatabaseBeforeAndAfter, setupInMemoryMongo, createCollectionsAndIndexes, teardownInMemoryMongo } from "./test-utils.js"
6
+
7
+ import _ from "lodash"
8
+ import config from "../config/index.js"
9
+
10
+ // Prepare jwt
11
+ import jwt from "jsonwebtoken"
12
+ const user = {
13
+ uri: "http://test.user",
14
+ name: "Test User",
15
+ identities: {
16
+ test: {},
17
+ },
18
+ }
19
+ const token = jwt.sign({ user }, "test")
20
+
21
+ const scheme = {
22
+ uri: "urn:test:source-scheme",
23
+ }
24
+ scheme.namespace = `${scheme.uri}:`
25
+
26
+ const inScheme = [scheme]
27
+
28
+ const targetScheme = {
29
+ uri: "urn:test:target-scheme",
30
+ }
31
+
32
+ const concepts = [
33
+ {
34
+ inScheme,
35
+ topConceptOf: [scheme],
36
+ uri: `${scheme.uri}:1`,
37
+ },
38
+ {
39
+ inScheme,
40
+ uri: `${scheme.uri}:1.1`,
41
+ broader: [{ uri: `${scheme.uri}:1` }],
42
+ },
43
+ {
44
+ inScheme,
45
+ uri: `${scheme.uri}:1.1.1`,
46
+ broader: [{ uri: `${scheme.uri}:1.1` }],
47
+ },
48
+ ]
49
+
50
+ describe("/mappings/infer", () => {
51
+ before(async () => {
52
+ const mongoUri = await setupInMemoryMongo({ replSet: false })
53
+ process.env.MONGO_URI = mongoUri
54
+ await createCollectionsAndIndexes()
55
+ await assertIndexes()
56
+ })
57
+
58
+ after(async () => {
59
+ // close server if you started one
60
+ await teardownInMemoryMongo()
61
+ })
62
+
63
+ // 🗑 Drop DB before *and* after every single `it()` in this file
64
+ dropDatabaseBeforeAndAfter()
65
+
66
+ // 🔌 Sanity‐check that mongoose really is connected
67
+ assertMongoDB()
68
+
69
+ const schemeTargetScheme = [scheme, targetScheme]
70
+ schemeTargetScheme.forEach(s => {
71
+ it("should POST test scheme", done => {
72
+ scheme.API = [
73
+ {
74
+ type: "http://bartoc.org/api-type/jskos",
75
+ url: `http://localhost:${config.port}`,
76
+ },
77
+ ],
78
+ chai.request.execute(server.app)
79
+ .post("/voc")
80
+ .send(s)
81
+ .end((error, res) => {
82
+ assert.equal(error, null)
83
+ res.should.have.status(201)
84
+ res.body.should.be.an("object")
85
+ assert.equal(res.body.uri, s.uri)
86
+ // Should have no concepts or top concepts
87
+ assert.deepEqual(res.body.concepts, [])
88
+ assert.deepEqual(res.body.topConcepts, [])
89
+ done()
90
+ })
91
+ })
92
+ })
93
+
94
+ it("should POST test concepts", done => {
95
+ chai.request.execute(server.app)
96
+ .post("/concepts")
97
+ .send(concepts)
98
+ .end((error, res) => {
99
+ res.should.have.status(201)
100
+ res.body.should.be.an("array")
101
+ assert.equal(res.body.length, concepts.length)
102
+ done()
103
+ })
104
+ })
105
+
106
+ it("should return empty result for /mappings/infer", done => {
107
+ chai.request.execute(server.app)
108
+ .get("/mappings/infer")
109
+ .query({
110
+ from: _.last(concepts).uri,
111
+ fromScheme: scheme.uri,
112
+ toScheme: targetScheme.uri,
113
+ })
114
+ .end((error, res) => {
115
+ res.should.have.status(200)
116
+ res.body.should.be.an("array")
117
+ assert.equal(res.body.length, 0)
118
+ done()
119
+ })
120
+ })
121
+
122
+ it("should add a mapping for an ancestor concept and return it as inferred mapping", done => {
123
+ const mapping = {
124
+ from: { memberSet: [concepts[0]] },
125
+ fromScheme: scheme,
126
+ to: { memberSet: [{ uri: `${targetScheme.uri}:5` }] },
127
+ toScheme: targetScheme,
128
+ }
129
+ chai.request.execute(server.app)
130
+ .post("/mappings")
131
+ .set("Authorization", `Bearer ${token}`)
132
+ .send(mapping)
133
+ .end((err, res) => {
134
+ assert.equal(res.status, 201)
135
+ const mappingUri = res.body.uri
136
+ // Request /mappings/infer again
137
+ chai.request.execute(server.app)
138
+ .get("/mappings/infer")
139
+ .query({
140
+ from: _.last(concepts).uri,
141
+ fromScheme: scheme.uri,
142
+ toScheme: targetScheme.uri,
143
+ })
144
+ .end((error, res) => {
145
+ res.should.have.status(200)
146
+ res.body.should.be.an("array")
147
+ assert.equal(res.body.length, 1)
148
+ assert.equal(res.body[0].source[0].uri, mappingUri)
149
+ // Since the mapping has no type, expect mappingRelation
150
+ assert.equal(res.body[0].type[0], "http://www.w3.org/2004/02/skos/core#mappingRelation")
151
+ done()
152
+ })
153
+ })
154
+ })
155
+
156
+ it("should also work with notation instead of URI", done => {
157
+ chai.request.execute(server.app)
158
+ .get("/mappings/infer")
159
+ .query({
160
+ from: _.last(concepts).uri.replace(scheme.namespace, ""),
161
+ fromScheme: scheme.uri,
162
+ toScheme: targetScheme.uri,
163
+ })
164
+ .end((error, res) => {
165
+ res.should.have.status(200)
166
+ res.body.should.be.an("array")
167
+ assert.equal(res.body.length, 1)
168
+ done()
169
+ })
170
+ })
171
+
172
+ it("should return nothing for previous request if depth is set to 1", done => {
173
+ chai.request.execute(server.app)
174
+ .get("/mappings/infer")
175
+ .query({
176
+ from: _.last(concepts).uri,
177
+ fromScheme: scheme.uri,
178
+ toScheme: targetScheme.uri,
179
+ depth: 1,
180
+ })
181
+ .end((error, res) => {
182
+ res.should.have.status(200)
183
+ res.body.should.be.an("array")
184
+ assert.equal(res.body.length, 0)
185
+ done()
186
+ })
187
+ })
188
+
189
+ it("should add a mapping for a deeper ancestor concept and return it instead as inferred mapping; also adjust mapping type", done => {
190
+ const mapping = {
191
+ from: { memberSet: [concepts[1]] },
192
+ fromScheme: scheme,
193
+ to: { memberSet: [{ uri: `${targetScheme.uri}:6` }] },
194
+ toScheme: targetScheme,
195
+ type: ["http://www.w3.org/2004/02/skos/core#closeMatch"],
196
+ }
197
+ chai.request.execute(server.app)
198
+ .post("/mappings")
199
+ .set("Authorization", `Bearer ${token}`)
200
+ .send(mapping)
201
+ .end((err, res) => {
202
+ assert.equal(res.status, 201)
203
+ const mappingUri = res.body.uri
204
+ // Request /mappings/infer again
205
+ chai.request.execute(server.app)
206
+ .get("/mappings/infer")
207
+ .query({
208
+ from: _.last(concepts).uri,
209
+ fromScheme: scheme.uri,
210
+ toScheme: targetScheme.uri,
211
+ })
212
+ .end((error, res) => {
213
+ res.should.have.status(200)
214
+ res.body.should.be.an("array")
215
+ assert.equal(res.body.length, 1)
216
+ assert.equal(res.body[0].source[0].uri, mappingUri)
217
+ // Since the mapping has type closeMatch and `strict` is false by default, expect narrowMatch
218
+ assert.equal(res.body[0].type[0], "http://www.w3.org/2004/02/skos/core#narrowMatch")
219
+ done()
220
+ })
221
+ })
222
+ })
223
+
224
+ it("should return nothing for previous request if depth is set to 0", done => {
225
+ chai.request.execute(server.app)
226
+ .get("/mappings/infer")
227
+ .query({
228
+ from: _.last(concepts).uri,
229
+ fromScheme: scheme.uri,
230
+ toScheme: targetScheme.uri,
231
+ depth: 0,
232
+ })
233
+ .end((error, res) => {
234
+ res.should.have.status(200)
235
+ res.body.should.be.an("array")
236
+ assert.equal(res.body.length, 0)
237
+ done()
238
+ })
239
+ })
240
+
241
+ it("should not use mapping of type `closeMatch` for inference if parameter `strict` is set", done => {
242
+ chai.request.execute(server.app)
243
+ .get("/mappings/infer")
244
+ .query({
245
+ from: _.last(concepts).uri,
246
+ fromScheme: scheme.uri,
247
+ toScheme: targetScheme.uri,
248
+ strict: "true",
249
+ })
250
+ .end((error, res) => {
251
+ res.should.have.status(200)
252
+ res.body.should.be.an("array")
253
+ assert.equal(res.body.length, 1)
254
+ assert.notEqual(res.body[0].to.memberSet[0].uri, `${targetScheme.uri}:6`)
255
+ assert.equal(res.body[0].to.memberSet[0].uri, `${targetScheme.uri}:5`)
256
+ done()
257
+ })
258
+ })
259
+
260
+ it("should add a mapping for the requested concept and return it instead", done => {
261
+ const mapping = {
262
+ from: { memberSet: [_.last(concepts)] },
263
+ fromScheme: scheme,
264
+ to: { memberSet: [{ uri: `${targetScheme.uri}:7` }] },
265
+ toScheme: targetScheme,
266
+ }
267
+ chai.request.execute(server.app)
268
+ .post("/mappings")
269
+ .set("Authorization", `Bearer ${token}`)
270
+ .send(mapping)
271
+ .end((err, res) => {
272
+ assert.equal(res.status, 201)
273
+ const mappingUri = res.body.uri
274
+ // Request /mappings/infer again
275
+ chai.request.execute(server.app)
276
+ .get("/mappings/infer")
277
+ .query({
278
+ from: _.last(concepts).uri,
279
+ fromScheme: scheme.uri,
280
+ toScheme: targetScheme.uri,
281
+ depth: 0,
282
+ })
283
+ .end((error, res) => {
284
+ res.should.have.status(200)
285
+ res.body.should.be.an("array")
286
+ assert.equal(res.body.length, 1)
287
+ assert.equal(res.body[0].uri, mappingUri)
288
+ done()
289
+ })
290
+ })
291
+ })
292
+
293
+ it("should return empty result when mappings of type `broadMatch` are requested", done => {
294
+ chai.request.execute(server.app)
295
+ .get("/mappings/infer")
296
+ .query({
297
+ from: _.last(concepts).uri,
298
+ fromScheme: scheme.uri,
299
+ toScheme: targetScheme.uri,
300
+ type: "http://www.w3.org/2004/02/skos/core#broadMatch",
301
+ })
302
+ .end((error, res) => {
303
+ res.should.have.status(200)
304
+ res.body.should.be.an("array")
305
+ assert.equal(res.body.length, 0)
306
+ done()
307
+ })
308
+ })
309
+
310
+ it("should throw error when parameter `to` is given", done => {
311
+ chai.request.execute(server.app)
312
+ .get("/mappings/infer")
313
+ .query({
314
+ from: _.last(concepts).uri,
315
+ fromScheme: scheme.uri,
316
+ to: "test",
317
+ toScheme: targetScheme.uri,
318
+ })
319
+ .end((error, res) => {
320
+ res.should.have.status(400)
321
+ done()
322
+ })
323
+ })
324
+
325
+ it("should throw error when parameter `direction` is given with value `backward`", done => {
326
+ chai.request.execute(server.app)
327
+ .get("/mappings/infer")
328
+ .query({
329
+ from: _.last(concepts).uri,
330
+ fromScheme: scheme.uri,
331
+ toScheme: targetScheme.uri,
332
+ direction: "backward",
333
+ })
334
+ .end((error, res) => {
335
+ res.should.have.status(400)
336
+ done()
337
+ })
338
+ })
339
+
340
+ })