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
@@ -0,0 +1,784 @@
1
+ import chai from "./chai.js"
2
+
3
+ import * as server from "../server.js"
4
+ import assert from "node:assert"
5
+ import { assertMongoDB, setupInMemoryMongo, createCollectionsAndIndexes, teardownInMemoryMongo } from "./test-utils.js"
6
+
7
+ const schemes = [
8
+ {
9
+ uri: "urn:test:scheme1",
10
+ notation: ["scheme1"],
11
+ },
12
+ {
13
+ uri: "urn:test:scheme2",
14
+ prefLabel: {
15
+ fr: "somelabel",
16
+ },
17
+ altLabel: {
18
+ de: [
19
+ "an altLabel",
20
+ ],
21
+ },
22
+ notation: ["scheme2"],
23
+ },
24
+ {
25
+ uri: "urn:test:scheme3",
26
+ definition: {
27
+ en: [
28
+ "this contains somelabel but shouldn't be found when leaving off the some",
29
+ ],
30
+ },
31
+ notation: ["scheme3"],
32
+ },
33
+ ]
34
+
35
+ describe("Data Writing features", () => {
36
+ before(async () => {
37
+ const mongoUri = await setupInMemoryMongo({ replSet: false })
38
+ process.env.MONGO_URI = mongoUri
39
+ await createCollectionsAndIndexes()
40
+ })
41
+
42
+ after(async () => {
43
+ // close server if you started one
44
+ await teardownInMemoryMongo()
45
+ })
46
+
47
+ // 🔌 Sanity‐check that mongoose really is connected
48
+ assertMongoDB()
49
+
50
+
51
+ describe("/voc write access", () => {
52
+
53
+ it("should POST a single scheme", done => {
54
+ chai.request.execute(server.app)
55
+ .post("/voc")
56
+ .send(schemes[0])
57
+ .end((error, res) => {
58
+ assert.equal(error, null)
59
+ res.should.have.status(201)
60
+ res.body.should.be.an("object")
61
+ assert.equal(res.body.uri, schemes[0].uri)
62
+ // Should have no concepts or top concepts
63
+ assert.deepEqual(res.body.concepts, [])
64
+ assert.deepEqual(res.body.topConcepts, [])
65
+ // Should have created and modified properties
66
+ assert.ok(!!res.body.created)
67
+ assert.ok(!!res.body.modified)
68
+ done()
69
+ })
70
+ })
71
+
72
+ it("should POST multiple schemes", done => {
73
+ chai.request.execute(server.app)
74
+ .post("/voc")
75
+ .send(schemes.slice(1))
76
+ .end((error, res) => {
77
+ assert.equal(error, null)
78
+ res.should.have.status(201)
79
+ res.body.should.be.an("array")
80
+ assert.deepEqual(res.body.map(c => c.uri), schemes.slice(1).map(c => c.uri))
81
+ for (let scheme of res.body) {
82
+ // Should have no concepts or top concepts
83
+ assert.deepEqual(scheme.concepts, [])
84
+ assert.deepEqual(scheme.topConcepts, [])
85
+ }
86
+ done()
87
+ })
88
+ })
89
+
90
+ // TODO: Maybe move somewhere else?
91
+ it("should GET correct results for notation", done => {
92
+ chai.request.execute(server.app)
93
+ .get("/voc/suggest")
94
+ .query({
95
+ search: "sche",
96
+ })
97
+ .end((err, res) => {
98
+ res.should.have.status(200)
99
+ res.should.have.header("Link")
100
+ res.should.have.header("X-Total-Count")
101
+ res.body.should.be.a("array")
102
+ res.body.length.should.be.eql(4) // OpenSearch Suggest Format
103
+ res.body[0].should.be.a("string")
104
+ res.body[1].should.be.a("array")
105
+ res.body[1].length.should.be.eql(schemes.length)
106
+ res.body[1][0].should.be.eql("scheme1")
107
+ res.body[3].should.be.a("array")
108
+ res.body[3].length.should.be.eql(schemes.length)
109
+ res.body[3][0].should.be.eql("urn:test:scheme1")
110
+ done()
111
+ })
112
+ })
113
+
114
+ // TODO: Maybe move somewhere else?
115
+ it("should GET correct results for term (1)", done => {
116
+ chai.request.execute(server.app)
117
+ .get("/voc/suggest")
118
+ .query({
119
+ search: "label",
120
+ })
121
+ .end((err, res) => {
122
+ res.should.have.status(200)
123
+ res.should.have.header("Link")
124
+ res.should.have.header("X-Total-Count")
125
+ res.body.should.be.a("array")
126
+ res.body.length.should.be.eql(4) // OpenSearch Suggest Format
127
+ res.body[0].should.be.a("string")
128
+ res.body[1].should.be.a("array")
129
+ res.body[1].length.should.be.eql(1)
130
+ res.body[3].should.be.a("array")
131
+ res.body[3].length.should.be.eql(1)
132
+ res.body[3][0].should.be.eql("urn:test:scheme2")
133
+ done()
134
+ })
135
+ })
136
+
137
+ /*
138
+ // TODO: Maybe move somewhere else?
139
+ it("should GET correct results for term (2)", done => {
140
+ chai.request.execute(server.app)
141
+ .get("/voc/suggest")
142
+ .query({
143
+ search: "somelabel",
144
+ })
145
+ .end((err, res) => {
146
+ res.should.have.status(200)
147
+ res.should.have.header("Link")
148
+ res.should.have.header("X-Total-Count")
149
+ res.body.should.be.a("array")
150
+ res.body.length.should.be.eql(4) // OpenSearch Suggest Format
151
+ res.body[0].should.be.a("string")
152
+ res.body[1].should.be.a("array")
153
+ res.body[1].length.should.be.eql(2)
154
+ res.body[3].should.be.a("array")
155
+ res.body[3].length.should.be.eql(2)
156
+ res.body[3][0].should.be.eql("urn:test:scheme2")
157
+ res.body[3][1].should.be.eql("urn:test:scheme3")
158
+ done()
159
+ })
160
+ })
161
+
162
+ it("should not POST an invalid scheme (1 - invalid prefLabel)", done => {
163
+ chai.request.execute(server.app)
164
+ .post("/voc")
165
+ .send({
166
+ uri: "uri:test",
167
+ prefLabel: "test",
168
+ })
169
+ .end((error, res) => {
170
+ assert.equal(error, null)
171
+ res.should.have.status(422)
172
+ res.body.should.be.an("object")
173
+ assert.equal(res.body.error, "InvalidBodyError")
174
+ done()
175
+ })
176
+ })
177
+
178
+ it("should not POST an invalid scheme (2 - missing URI)", done => {
179
+ chai.request.execute(server.app)
180
+ .post("/voc")
181
+ .send({
182
+ prefLabel: { en: "test" },
183
+ })
184
+ .end((error, res) => {
185
+ assert.equal(error, null)
186
+ res.should.have.status(422)
187
+ res.body.should.be.an("object")
188
+ assert.equal(res.body.error, "InvalidBodyError")
189
+ done()
190
+ })
191
+ })
192
+
193
+ it("should not POST a scheme that already exists", done => {
194
+ chai.request.execute(server.app)
195
+ .post("/voc")
196
+ .send(schemes[0])
197
+ .end((error, res) => {
198
+ assert.equal(error, null)
199
+ res.should.have.status(422)
200
+ res.body.should.be.an("object")
201
+ assert.equal(res.body.error, "DuplicateEntityError")
202
+ done()
203
+ })
204
+ })
205
+
206
+ it("should bulk POST schemes and ignore errors", done => {
207
+ const bulkSchemes = [
208
+ {
209
+ uri: "urn:test:scheme-bulk1",
210
+ },
211
+ {
212
+ uri: "urn:test:scheme-bulk2",
213
+ },
214
+ {
215
+ uri: "test-scheme-bulk-invalid",
216
+ },
217
+ {
218
+ uri: schemes[0].uri,
219
+ prefLabel: { en: "Bulk updated scheme" },
220
+ },
221
+ ]
222
+ chai.request.execute(server.app)
223
+ .post("/voc")
224
+ .query({
225
+ bulk: true,
226
+ })
227
+ .send(bulkSchemes)
228
+ .end((error, res) => {
229
+ assert.equal(error, null)
230
+ res.should.have.status(201)
231
+ res.body.should.be.an("array")
232
+ assert.equal(res.body.length, 3) // one invalid scheme removed
233
+ // Check updated scheme
234
+ chai.request.execute(server.app).get("/voc").query({ uri: schemes[0].uri }).end((error, res) => {
235
+ assert.equal(error, null)
236
+ res.should.have.status(200)
237
+ res.body.should.be.an("array")
238
+ assert.equal(res.body.length, 1)
239
+ assert.equal(res.body[0].uri, schemes[0].uri)
240
+ assert.deepEqual(res.body[0].prefLabel, bulkSchemes.find(s => s.uri == schemes[0].uri).prefLabel)
241
+ res.body[0].concepts.should.be.an("array")
242
+ res.body[0].topConcepts.should.be.an("array")
243
+ done()
244
+ })
245
+ })
246
+ })
247
+ */
248
+ /*
249
+ it("should PUT a scheme (created should be removed, modified should be updated)", async () => {
250
+ const patch = {
251
+ notation: ["A"],
252
+ created: "2012",
253
+ }
254
+ let scheme = schemes[0], res
255
+ // 1. Get current scheme from database
256
+ res = await chai.request.execute(server.app)
257
+ .get("/voc")
258
+ .query({
259
+ uri: scheme.uri,
260
+ })
261
+ assert.strictEqual(res.status, 200)
262
+ scheme = res.body[0]
263
+ assert.ok(!!scheme)
264
+ // 2. Make PUT request
265
+ res = await chai.request.execute(server.app)
266
+ .put("/voc")
267
+ .send(Object.assign({}, scheme, patch))
268
+ res.should.have.status(200)
269
+ res.body.should.be.an("object")
270
+ assert.deepStrictEqual(res.body.notation, patch.notation)
271
+ // Make sure created was NOT updated even though it was set on patch
272
+ assert.strictEqual(res.body.created, scheme.created)
273
+ assert.notStrictEqual(res.body.created, patch.created)
274
+ // Make sure modified was updated
275
+ assert.notStrictEqual(res.body.modified, scheme.modified)
276
+ })
277
+
278
+ it("should not PUT an invalid scheme", done => {
279
+ const patch = {
280
+ notation: "A",
281
+ }
282
+ chai.request.execute(server.app)
283
+ .put("/voc")
284
+ .send(Object.assign({}, schemes[0], patch))
285
+ .end((error, res) => {
286
+ assert.equal(error, null)
287
+ res.should.have.status(422)
288
+ res.body.should.be.an("object")
289
+ assert.equal(res.body.error, "InvalidBodyError")
290
+ done()
291
+ })
292
+ })
293
+
294
+ it("should not PUT a scheme that doesn't exist", done => {
295
+ chai.request.execute(server.app)
296
+ .put("/voc")
297
+ .send({
298
+ uri: "urn:test:scheme-that-does-not-exist",
299
+ notation: ["A"],
300
+ })
301
+ .end((error, res) => {
302
+ assert.equal(error, null)
303
+ res.should.have.status(404)
304
+ res.body.should.be.an("object")
305
+ assert.equal(res.body.error, "EntityNotFoundError")
306
+ done()
307
+ })
308
+ })
309
+
310
+ it("should DELETE a scheme", done => {
311
+ chai.request.execute(server.app)
312
+ .delete("/voc")
313
+ .query({
314
+ uri: schemes[2].uri,
315
+ })
316
+ .end((error, res) => {
317
+ assert.equal(error, null)
318
+ res.should.have.status(204)
319
+ done()
320
+ })
321
+ })
322
+
323
+ it("should not DELETE a scheme that doesn't exist", done => {
324
+ chai.request.execute(server.app)
325
+ .delete("/voc")
326
+ .query({
327
+ uri: "urn:test:scheme-that-does-not-exist",
328
+ })
329
+ .end((error, res) => {
330
+ assert.equal(error, null)
331
+ res.should.have.status(404)
332
+ res.body.should.be.an("object")
333
+ assert.equal(res.body.error, "EntityNotFoundError")
334
+ done()
335
+ })
336
+ })
337
+
338
+ })
339
+
340
+ describe("/concepts write access", () => {
341
+
342
+ const concept = {
343
+ uri: "urn:test:concept",
344
+ inScheme: [schemes[0]],
345
+ }
346
+
347
+ const concepts = [
348
+ {
349
+ uri: "urn:test:concept2",
350
+ topConceptOf: [schemes[1]],
351
+ },
352
+ {
353
+ uri: "urn:test:concept3",
354
+ inScheme: [schemes[0]],
355
+ broader: [concept],
356
+ },
357
+ ]
358
+
359
+ it("should POST a concept", done => {
360
+ chai.request.execute(server.app)
361
+ .post("/concepts")
362
+ .send(concept)
363
+ .end((error, res) => {
364
+ res.should.have.status(201)
365
+ res.body.should.be.a("object")
366
+ assert.equal(res.body.uri, concept.uri)
367
+ done()
368
+ })
369
+ })
370
+
371
+ it("should have refreshed the `concepts` property of the scheme after POSTing a concept", done => {
372
+ chai.request.execute(server.app)
373
+ .get("/voc")
374
+ .query({
375
+ uri: concept.inScheme[0].uri,
376
+ })
377
+ .end((error, res) => {
378
+ res.should.have.status(200)
379
+ res.body.should.be.a("array")
380
+ res.body[0].should.be.a("object")
381
+ assert.equal(res.body[0].uri, concept.inScheme[0].uri)
382
+ assert.deepEqual(res.body[0].concepts, [null])
383
+ done()
384
+ })
385
+ })
386
+
387
+ it("should not DELETE a scheme when it currently has concepts", done => {
388
+ chai.request.execute(server.app)
389
+ .delete("/voc")
390
+ .query({
391
+ uri: concept.inScheme[0].uri,
392
+ })
393
+ .end((error, res) => {
394
+ assert.equal(error, null)
395
+ res.should.have.status(400)
396
+ res.body.should.be.a("object")
397
+ assert.equal(res.body.error, "MalformedRequestError")
398
+ done()
399
+ })
400
+ })
401
+
402
+ it("should POST multiple concepts", done => {
403
+ chai.request.execute(server.app)
404
+ .post("/concepts")
405
+ .send(concepts)
406
+ .end((error, res) => {
407
+ res.should.have.status(201)
408
+ res.body.should.be.an("array")
409
+ assert.deepEqual(res.body.map(c => c.uri), concepts.map(c => c.uri))
410
+ done()
411
+ })
412
+ })
413
+
414
+ it("should have refreshed the `topConcepts` property of the scheme after POSTing a top concept", done => {
415
+ chai.request.execute(server.app)
416
+ .get("/voc")
417
+ .query({
418
+ uri: concepts[0].topConceptOf[0].uri,
419
+ })
420
+ .end((error, res) => {
421
+ res.should.have.status(200)
422
+ res.body.should.be.a("array")
423
+ res.body[0].should.be.a("object")
424
+ assert.equal(res.body[0].uri, concepts[0].topConceptOf[0].uri)
425
+ assert.deepEqual(res.body[0].topConcepts, [null])
426
+ done()
427
+ })
428
+ })
429
+
430
+ it("should not POST a concept without scheme", done => {
431
+ chai.request.execute(server.app)
432
+ .post("/concepts")
433
+ .send({
434
+ uri: "urn:test:concept-without-scheme",
435
+ })
436
+ .end((error, res) => {
437
+ res.should.have.status(400)
438
+ res.body.should.be.a("object")
439
+ assert.equal(res.body.error, "MalformedRequestError")
440
+ done()
441
+ })
442
+ })
443
+
444
+ it("should POST a concept without scheme when scheme param is given, then delete it", async () => {
445
+ const scheme = schemes[0]
446
+ const concept = {
447
+ uri: "urn:test:concept-without-scheme",
448
+ }
449
+ let res
450
+ // POST concept
451
+ res = await chai.request.execute(server.app)
452
+ .post("/concepts")
453
+ .query({
454
+ scheme: scheme.uri,
455
+ })
456
+ .send(concept)
457
+ res.should.have.status(201)
458
+ res.body.should.be.a("object")
459
+ assert.strictEqual(res.body.uri, concept.uri)
460
+ assert.strictEqual(res.body.inScheme[0].uri, scheme.uri)
461
+ // DELETE concept
462
+ res = await chai.request.execute(server.app)
463
+ .delete("/concepts")
464
+ .query(concept)
465
+ res.should.have.status(204)
466
+ })
467
+
468
+ it("should not POST a concept with invalid URI", done => {
469
+ chai.request.execute(server.app)
470
+ .post("/concepts")
471
+ .send({
472
+ uri: "concept-invalid-uri",
473
+ inScheme: [schemes[1]],
474
+ })
475
+ .end((error, res) => {
476
+ res.should.have.status(422)
477
+ res.body.should.be.a("object")
478
+ assert.equal(res.body.error, "InvalidBodyError")
479
+ done()
480
+ })
481
+ })
482
+
483
+ it("should not POST a concept with missing URI", done => {
484
+ chai.request.execute(server.app)
485
+ .post("/concepts")
486
+ .send({
487
+ inScheme: [schemes[1]],
488
+ })
489
+ .end((error, res) => {
490
+ res.should.have.status(422)
491
+ res.body.should.be.a("object")
492
+ assert.equal(res.body.error, "InvalidBodyError")
493
+ done()
494
+ })
495
+ })
496
+
497
+ it("should not POST a concept that already exists", done => {
498
+ chai.request.execute(server.app)
499
+ .post("/concepts")
500
+ .send(concept)
501
+ .end((error, res) => {
502
+ assert.equal(error, null)
503
+ res.should.have.status(422)
504
+ res.body.should.be.an("object")
505
+ assert.equal(res.body.error, "DuplicateEntityError")
506
+ done()
507
+ })
508
+ })
509
+
510
+ it("should POST a single concept that already exists if bulk is set", done => {
511
+ chai.request.execute(server.app)
512
+ .post("/concepts")
513
+ .query({
514
+ bulk: true,
515
+ })
516
+ .send(concept)
517
+ .end((error, res) => {
518
+ assert.equal(error, null)
519
+ res.should.have.status(201)
520
+ res.body.should.be.an("object")
521
+ done()
522
+ })
523
+ })
524
+
525
+ it("should POST upsert multiple concepts", done => {
526
+ chai.request.execute(server.app)
527
+ .post("/concepts")
528
+ .query({
529
+ bulk: true,
530
+ })
531
+ .send(concepts)
532
+ .end((error, res) => {
533
+ res.should.have.status(201)
534
+ res.body.should.be.an("array")
535
+ assert.deepEqual(res.body.map(c => c.uri), concepts.map(c => c.uri))
536
+ assert.deepEqual(Object.keys(res.body[0]), ["uri"])
537
+ done()
538
+ })
539
+ })
540
+
541
+ it("should ignore POST errors when bulk is set", done => {
542
+ chai.request.execute(server.app)
543
+ .post("/concepts")
544
+ .query({
545
+ bulk: true,
546
+ })
547
+ .send([{
548
+ uri: "concept-invalid-uri",
549
+ inScheme: [schemes[1]],
550
+ }])
551
+ .end((error, res) => {
552
+ res.should.have.status(201)
553
+ res.body.should.be.an("array")
554
+ assert.equal(res.body.length, 0)
555
+ done()
556
+ })
557
+ })
558
+
559
+ it("should not POST a concept with scheme that is not in database", done => {
560
+ chai.request.execute(server.app)
561
+ .post("/concepts")
562
+ .send({
563
+ uri: "urn:test:concept-with-missing-scheme",
564
+ inScheme: [
565
+ {
566
+ uri: "urn:test:scheme-that-does-not-exist",
567
+ },
568
+ ],
569
+ })
570
+ .end((error, res) => {
571
+ res.should.have.status(400)
572
+ res.body.should.be.a("object")
573
+ assert.equal(res.body.error, "MalformedRequestError")
574
+ done()
575
+ })
576
+ })
577
+
578
+ it("should PUT a concept", done => {
579
+ const patch = {
580
+ notation: ["A"],
581
+ }
582
+ chai.request.execute(server.app)
583
+ .put("/concepts")
584
+ .send(Object.assign({}, concept, patch))
585
+ .end((error, res) => {
586
+ assert.equal(error, null)
587
+ res.should.have.status(200)
588
+ res.body.should.be.an("object")
589
+ assert.deepEqual(res.body.notation, patch.notation)
590
+ done()
591
+ })
592
+ })
593
+
594
+ it("should not PUT an invalid concept", done => {
595
+ chai.request.execute(server.app)
596
+ .put("/concepts")
597
+ .send({
598
+ uri: "urn:test:concept2",
599
+ inScheme: [schemes[1]],
600
+ prefLabel: "should be an object",
601
+ })
602
+ .end((error, res) => {
603
+ res.should.have.status(422)
604
+ res.body.should.be.a("object")
605
+ assert.equal(res.body.error, "InvalidBodyError")
606
+ done()
607
+ })
608
+ })
609
+
610
+ it("should not PUT a concept that doesn't exist", done => {
611
+ chai.request.execute(server.app)
612
+ .put("/concepts")
613
+ .send({
614
+ uri: "urn:test:concept-that-does-not-exist",
615
+ inScheme: [schemes[1]],
616
+ prefLabel: { en: "should be an object" },
617
+ })
618
+ .end((error, res) => {
619
+ res.should.have.status(404)
620
+ res.body.should.be.a("object")
621
+ assert.equal(res.body.error, "EntityNotFoundError")
622
+ done()
623
+ })
624
+ })
625
+
626
+ it("should DELETE posted concepts", done => {
627
+ concepts.push(concept)
628
+ let count = 0
629
+ const maybeDone = () => {
630
+ count += 1
631
+ if (count == concepts.length) {
632
+ done()
633
+ }
634
+ }
635
+ for (let concept of concepts) {
636
+ chai.request.execute(server.app)
637
+ .delete("/concepts")
638
+ .query({
639
+ uri: concept.uri,
640
+ })
641
+ .end((err, res) => {
642
+ res.should.have.status(204)
643
+ maybeDone()
644
+ })
645
+ }
646
+ })
647
+
648
+ it("should have refreshed the `concepts` property of the scheme after DELETEing a concept", done => {
649
+ chai.request.execute(server.app)
650
+ .get("/voc")
651
+ .query({
652
+ uri: concept.inScheme[0].uri,
653
+ })
654
+ .end((error, res) => {
655
+ res.should.have.status(200)
656
+ res.body.should.be.a("array")
657
+ res.body[0].should.be.a("object")
658
+ assert.equal(res.body[0].uri, concept.inScheme[0].uri)
659
+ assert.deepEqual(res.body[0].concepts, [])
660
+ done()
661
+ })
662
+ })
663
+
664
+ it("should DELETE a scheme after its last concept was removed", done => {
665
+ chai.request.execute(server.app)
666
+ .delete("/voc")
667
+ .query({
668
+ uri: concept.inScheme[0].uri,
669
+ })
670
+ .end((error, res) => {
671
+ assert.equal(error, null)
672
+ res.should.have.status(204)
673
+ done()
674
+ })
675
+ })
676
+
677
+ it("should have refreshed the `topConcepts` property of the scheme after DELETEing a top concept", done => {
678
+ chai.request.execute(server.app)
679
+ .get("/voc")
680
+ .query({
681
+ uri: concepts[0].topConceptOf[0].uri,
682
+ })
683
+ .end((error, res) => {
684
+ res.should.have.status(200)
685
+ res.body.should.be.a("array")
686
+ res.body[0].should.be.a("object")
687
+ assert.equal(res.body[0].uri, concepts[0].topConceptOf[0].uri)
688
+ assert.deepEqual(res.body[0].topConcepts, [])
689
+ done()
690
+ })
691
+ })
692
+
693
+ it("should DELETE all concepts of a scheme", async () => {
694
+ const uri = concepts[0].topConceptOf[0].uri
695
+ let res
696
+ // Post concept
697
+ res = await chai.request.execute(server.app)
698
+ .post("/concepts")
699
+ .send(concepts[0])
700
+ res.should.have.status(201)
701
+ res.body.should.be.an("object")
702
+ res.body.uri.should.be.eql(concepts[0].uri)
703
+ // Delete from scheme
704
+ res = await chai.request.execute(server.app)
705
+ .delete("/voc/concepts")
706
+ .query({
707
+ uri,
708
+ })
709
+ res.should.have.status(204)
710
+ // Get from scheme
711
+ res = await chai.request.execute(server.app)
712
+ .get("/voc/concepts")
713
+ .query({ uri })
714
+ res.should.have.status(200)
715
+ res.body.should.be.a("array")
716
+ res.body.length.should.be.eql(0)
717
+ })
718
+
719
+ it("should not DELETE a concept that doesn't exist", done => {
720
+ chai.request.execute(server.app)
721
+ .delete("/concepts")
722
+ .query({
723
+ uri: "urn:test:concept-that-does-not-exist",
724
+ })
725
+ .end((error, res) => {
726
+ res.should.have.status(404)
727
+ res.body.should.be.a("object")
728
+ assert.equal(res.body.error, "EntityNotFoundError")
729
+ done()
730
+ })
731
+ })
732
+
733
+ it("should POST a concept, update its scheme's properties, then DELETE the concept", async () => {
734
+ const uri = concepts[0].topConceptOf[0].uri
735
+ const concept = {
736
+ uri: "urn:test:concept",
737
+ topConceptOf: [{ uri }],
738
+ }
739
+ const getScheme = async () => {
740
+ const res = await chai.request.execute(server.app)
741
+ .get("/voc")
742
+ .query({
743
+ uri,
744
+ })
745
+ assert.strictEqual(res.status, 200)
746
+ assert.strictEqual(res.body.length, 1)
747
+ return res
748
+ }
749
+ let res, scheme
750
+ // Get scheme before POSTing
751
+ res = await getScheme()
752
+ scheme = res.body[0]
753
+ assert.strictEqual(scheme.uri, uri)
754
+ // POST concept
755
+ res = await chai.request.execute(server.app)
756
+ .post("/concepts")
757
+ .send(concept)
758
+ assert.strictEqual(res.status, 201)
759
+ // Check scheme after POSTing
760
+ res = await getScheme()
761
+ // Check concepts, topConcepts, and modified properties
762
+ assert.notDeepStrictEqual(res.body.concepts, scheme.concepts)
763
+ assert.notDeepStrictEqual(res.body.topConcepts, scheme.topConcepts)
764
+ assert.notStrictEqual(res.body.modified, scheme.modified)
765
+ scheme = res.body[0]
766
+ // DELETE concept
767
+ res = await chai.request.execute(server.app)
768
+ .delete("/concepts")
769
+ .query({
770
+ uri: concept.uri,
771
+ })
772
+ assert.strictEqual(res.status, 204)
773
+ // Check scheme after DELETE
774
+ res = await getScheme()
775
+ // Check concepts, topConcepts, and modified properties
776
+ assert.notDeepStrictEqual(res.body.concepts, scheme.concepts)
777
+ assert.notDeepStrictEqual(res.body.topConcepts, scheme.topConcepts)
778
+ assert.notStrictEqual(res.body.modified, scheme.modified)
779
+ })
780
+ */
781
+
782
+ })
783
+
784
+ })