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.
- package/.dockerignore +20 -0
- package/.editorconfig +9 -0
- package/.github/workflows/docker.yml +59 -0
- package/.github/workflows/gh-pages.yml +23 -0
- package/.github/workflows/gh-release.yml +19 -0
- package/.github/workflows/test.yml +39 -0
- package/.husky/pre-commit +1 -0
- package/CHANGELOG.md +18 -0
- package/LICENSE +21 -0
- package/README.md +2710 -0
- package/bin/extra.js +81 -0
- package/bin/import.js +438 -0
- package/bin/mongodb.js +21 -0
- package/bin/reset.js +257 -0
- package/bin/upgrade.js +34 -0
- package/config/config.default.json +88 -0
- package/config/config.schema.json +877 -0
- package/config/config.test.json +107 -0
- package/config/index.js +77 -0
- package/config/setup.js +212 -0
- package/depdendencies.png +0 -0
- package/docker/.env +1 -0
- package/docker/Dockerfile +20 -0
- package/docker/README.md +175 -0
- package/docker/docker-compose.yml +29 -0
- package/docker/docker-entrypoint.sh +8 -0
- package/docker/mongo-initdb.d/mongo_setup.sh +22 -0
- package/ecosystem.example.json +7 -0
- package/errors/index.js +94 -0
- package/eslint.config.js +17 -0
- package/index.js +10 -0
- package/models/annotations.js +13 -0
- package/models/concepts.js +12 -0
- package/models/concordances.js +12 -0
- package/models/index.js +33 -0
- package/models/mappings.js +20 -0
- package/models/meta.js +21 -0
- package/models/registries.js +12 -0
- package/models/schemes.js +12 -0
- package/package.json +91 -0
- package/routes/annotations.js +83 -0
- package/routes/common.js +86 -0
- package/routes/concepts.js +64 -0
- package/routes/concordances.js +86 -0
- package/routes/data.js +19 -0
- package/routes/mappings.js +108 -0
- package/routes/registries.js +24 -0
- package/routes/schemes.js +72 -0
- package/routes/validate.js +37 -0
- package/server.js +190 -0
- package/services/abstract.js +328 -0
- package/services/annotations.js +237 -0
- package/services/concepts.js +459 -0
- package/services/concordances.js +264 -0
- package/services/data.js +30 -0
- package/services/index.js +34 -0
- package/services/mappings.js +978 -0
- package/services/registries.js +319 -0
- package/services/schemes.js +318 -0
- package/services/validate.js +39 -0
- package/status.schema.json +145 -0
- package/test/abstract-service.js +36 -0
- package/test/annotations/annotation.json +13 -0
- package/test/api.js +2481 -0
- package/test/chai.js +14 -0
- package/test/changes.js +179 -0
- package/test/concepts/conceptNoFileEnding +4 -0
- package/test/concepts/concepts-ddc-6-60-61-62.json +123 -0
- package/test/concordances/concordances.ndjson +2 -0
- package/test/config.js +26 -0
- package/test/configs/complex-config.json +90 -0
- package/test/configs/empty-object.json +1 -0
- package/test/configs/fail-array.json +1 -0
- package/test/configs/fail-empty.json +0 -0
- package/test/configs/fail-mapping-only-props1.json +5 -0
- package/test/configs/fail-mapping-only-props2.json +5 -0
- package/test/configs/fail-mapping-only-props3.json +5 -0
- package/test/configs/fail-mapping-only-props4.json +5 -0
- package/test/configs/fail-nonexisting-prop.json +3 -0
- package/test/configs/fail-port-string.json +3 -0
- package/test/configs/fail-registry-types.json +16 -0
- package/test/configs/registry-types.json +16 -0
- package/test/data-write.js +784 -0
- package/test/eslint.js +22 -0
- package/test/import-reset.js +287 -0
- package/test/infer-mappings.js +340 -0
- package/test/ipcheck.js +287 -0
- package/test/mappings/README.md +1 -0
- package/test/mappings/ddc-gnd-1.mapping.json +33 -0
- package/test/mappings/ddc-gnd-2.mapping.json +67 -0
- package/test/mappings/mapping-ddc-gnd-noScheme.json +145 -0
- package/test/mappings/mapping-ddc-gnd.json +175 -0
- package/test/mappings/mappings-ddc.json +214 -0
- package/test/registries/registries.ndjson +2 -0
- package/test/services.js +557 -0
- package/test/terminologies/terminologies.json +94 -0
- package/test/test-utils.js +182 -0
- package/test/utils.js +425 -0
- package/test/validate.js +226 -0
- package/utils/adjust.js +206 -0
- package/utils/auth.js +154 -0
- package/utils/changes.js +88 -0
- package/utils/db.js +106 -0
- package/utils/ipcheck.js +76 -0
- package/utils/middleware.js +636 -0
- package/utils/searchHelper.js +153 -0
- package/utils/status.js +77 -0
- package/utils/users.js +7 -0
- package/utils/utils.js +114 -0
- package/utils/uuid.js +6 -0
- package/utils/version.js +324 -0
- package/views/base.ejs +172 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import assert from "node:assert"
|
|
2
|
+
import mongoose from "mongoose"
|
|
3
|
+
import { MongoMemoryServer, MongoMemoryReplSet } from "mongodb-memory-server"
|
|
4
|
+
import { createServices } from "../index.js"
|
|
5
|
+
|
|
6
|
+
import config from "../config/config.test.json" with { type: "json" }
|
|
7
|
+
const services = createServices(config)
|
|
8
|
+
|
|
9
|
+
let mongod
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Starts an in-memory MongoDB server (standalone or replica set).
|
|
13
|
+
*/
|
|
14
|
+
export async function setupInMemoryMongo(opts = { replSet: false }) {
|
|
15
|
+
if (opts.replSet) {
|
|
16
|
+
mongod = new MongoMemoryReplSet({ replSet: { count: 1 } })
|
|
17
|
+
} else {
|
|
18
|
+
mongod = new MongoMemoryServer()
|
|
19
|
+
}
|
|
20
|
+
await mongod.start()
|
|
21
|
+
const uri = mongod.getUri()
|
|
22
|
+
|
|
23
|
+
await mongoose.disconnect().catch(() => {})
|
|
24
|
+
await mongoose.connect(uri, {
|
|
25
|
+
serverSelectionTimeoutMS: 30000,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
return uri
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Stops Mongoose and in-memory server.
|
|
34
|
+
*/
|
|
35
|
+
export async function teardownInMemoryMongo() {
|
|
36
|
+
if (mongoose.connection.readyState !== 0) {
|
|
37
|
+
await mongoose.disconnect()
|
|
38
|
+
}
|
|
39
|
+
if (mongod) {
|
|
40
|
+
await mongod.stop()
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Drop the database.
|
|
47
|
+
*/
|
|
48
|
+
export async function dropDatabase() {
|
|
49
|
+
const conn = mongoose.connection
|
|
50
|
+
// If not connected at all, skip drop
|
|
51
|
+
if (conn.readyState === 0) {
|
|
52
|
+
console.log(" ⚠ No connection, skipping drop")
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
// If connecting, wait until connected or timeout
|
|
56
|
+
if (conn.readyState !== 1) {
|
|
57
|
+
await new Promise((resolve, reject) => {
|
|
58
|
+
const timeout = setTimeout(() => reject(new Error("Timeout waiting for DB connection")), 5000)
|
|
59
|
+
conn.once("connected", () => {
|
|
60
|
+
clearTimeout(timeout); resolve()
|
|
61
|
+
})
|
|
62
|
+
conn.once("error", (err) => {
|
|
63
|
+
clearTimeout(timeout); reject(err)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
await conn.db.dropDatabase()
|
|
69
|
+
console.log(" ✓ Dropped in-memory database")
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.error(" x Error: Dropping database failed.", err)
|
|
72
|
+
throw err
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
/* process.on("SIGINT", () => {
|
|
78
|
+
dropDatabase(() => process.exit(1))
|
|
79
|
+
}) */
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Mocha hooks: drop DB before and after each suite.
|
|
83
|
+
*/
|
|
84
|
+
export function dropDatabaseBeforeAndAfter() {
|
|
85
|
+
before(async () => {
|
|
86
|
+
await dropDatabase()
|
|
87
|
+
})
|
|
88
|
+
after(async () => {
|
|
89
|
+
await dropDatabase()
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Asserts that Mongoose is connected.
|
|
95
|
+
*/
|
|
96
|
+
export function assertMongoDB() {
|
|
97
|
+
describe("MongoDB Connection", () => {
|
|
98
|
+
it("should connect successfully", (done) => {
|
|
99
|
+
const conn = mongoose.connection
|
|
100
|
+
if (conn.readyState === 1) {
|
|
101
|
+
return done()
|
|
102
|
+
}
|
|
103
|
+
conn.once("connected", () => done())
|
|
104
|
+
conn.once("error", (err) => done(err))
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Ensure that all JSKOS collections exist and have their indexes.
|
|
112
|
+
* This replaces running `import.js --indexes`.
|
|
113
|
+
*/
|
|
114
|
+
export async function createCollectionsAndIndexes() {
|
|
115
|
+
const collNames = ["terminologies","concepts","concordances","mappings","annotations","registries"]
|
|
116
|
+
// 1. Create an empty collection if it doesn't exist
|
|
117
|
+
for (const name of collNames) {
|
|
118
|
+
const exists = await mongoose.connection.db
|
|
119
|
+
.listCollections({ name })
|
|
120
|
+
.hasNext()
|
|
121
|
+
if (!exists) {
|
|
122
|
+
await mongoose.connection.db.createCollection(name)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// 2. Call each service's createIndexes() method
|
|
126
|
+
for (const type of Object.keys(services)) {
|
|
127
|
+
// services[type].createIndexes() should invoke e.g. Model.createIndexes()
|
|
128
|
+
await services[type].createIndexes()
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Verifies that indexes exist on all relevant collections.
|
|
135
|
+
*/
|
|
136
|
+
export async function assertIndexes() {
|
|
137
|
+
it("should have at least default indexes", async () => {
|
|
138
|
+
const collections = ["terminologies", "concepts", "concordances", "mappings", "annotations", "registries"]
|
|
139
|
+
for (const name of collections) {
|
|
140
|
+
// ensure collection exists
|
|
141
|
+
const exists = await mongoose.connection.db.listCollections({ name }).hasNext()
|
|
142
|
+
if (!exists) {
|
|
143
|
+
await mongoose.connection.db.createCollection(name)
|
|
144
|
+
}
|
|
145
|
+
const info = await mongoose.connection.db.collection(name).indexInformation()
|
|
146
|
+
// Expect at least the _id index
|
|
147
|
+
assert.ok(info._id_, `Missing _id index on ${name}`)
|
|
148
|
+
// Optionally check other indexes here
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
import { exec as cpexec } from "node:child_process"
|
|
155
|
+
/**
|
|
156
|
+
* A wrapper around child_process' exec function for async/await.
|
|
157
|
+
*
|
|
158
|
+
* @param {*} command
|
|
159
|
+
* @param {*} options
|
|
160
|
+
*/
|
|
161
|
+
export async function exec(command, options) {
|
|
162
|
+
return new Promise((resolve, reject) => {
|
|
163
|
+
cpexec(command, options || {}, (error, stdout, stderr) => {
|
|
164
|
+
if (error) {
|
|
165
|
+
error.stdout = stdout
|
|
166
|
+
error.stderr = stderr
|
|
167
|
+
return reject(error)
|
|
168
|
+
}
|
|
169
|
+
resolve(stdout)
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
import Stream from "node:stream"
|
|
175
|
+
import * as anystream from "json-anystream"
|
|
176
|
+
|
|
177
|
+
export async function arrayToStream(array) {
|
|
178
|
+
const readable = new Stream.Readable({ objectMode: true })
|
|
179
|
+
array.forEach(item => readable.push(JSON.stringify(item) + "\n"))
|
|
180
|
+
readable.push(null)
|
|
181
|
+
return anystream.make(readable, "ndjson")
|
|
182
|
+
}
|
package/test/utils.js
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
// Tests for utilities
|
|
2
|
+
|
|
3
|
+
import assert from "node:assert"
|
|
4
|
+
import { getCreator, handleCreatorForObject } from "../utils/middleware.js"
|
|
5
|
+
import { cleanJSON } from "../utils/utils.js"
|
|
6
|
+
import config from "../config/index.js"
|
|
7
|
+
|
|
8
|
+
describe("utils", () => {
|
|
9
|
+
|
|
10
|
+
describe("getCreator", () => {
|
|
11
|
+
const tests = [
|
|
12
|
+
{
|
|
13
|
+
req: {},
|
|
14
|
+
creator: null,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
req: {
|
|
18
|
+
user: {
|
|
19
|
+
uri: "test",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
creator: {
|
|
23
|
+
uri: "test",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
req: {
|
|
28
|
+
user: {
|
|
29
|
+
uri: "test",
|
|
30
|
+
},
|
|
31
|
+
query: {
|
|
32
|
+
identity: "test2",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
creator: {
|
|
36
|
+
uri: "test",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
req: {
|
|
41
|
+
user: {
|
|
42
|
+
uri: "test",
|
|
43
|
+
identities: {
|
|
44
|
+
test2: { uri: "test2" },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
query: {
|
|
48
|
+
identity: "test2",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
creator: {
|
|
52
|
+
uri: "test2",
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
req: {
|
|
57
|
+
user: {
|
|
58
|
+
uri: "test",
|
|
59
|
+
identities: {
|
|
60
|
+
test2: {
|
|
61
|
+
uri: "test2",
|
|
62
|
+
name: "name2",
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
query: {
|
|
67
|
+
identity: "test2",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
creator: {
|
|
71
|
+
uri: "test2",
|
|
72
|
+
prefLabel: { en: "name2" },
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
req: {
|
|
77
|
+
type: "annotations",
|
|
78
|
+
user: {
|
|
79
|
+
uri: "test",
|
|
80
|
+
identities: {
|
|
81
|
+
test2: {
|
|
82
|
+
uri: "test2",
|
|
83
|
+
name: "name2",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
query: {
|
|
88
|
+
identity: "test2",
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
creator: {
|
|
92
|
+
id: "test2",
|
|
93
|
+
name: "name2",
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
req: {
|
|
98
|
+
user: {
|
|
99
|
+
uri: "test",
|
|
100
|
+
identities: {
|
|
101
|
+
test2: {
|
|
102
|
+
uri: "test2",
|
|
103
|
+
name: "name2",
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
query: {
|
|
108
|
+
identity: "test2",
|
|
109
|
+
identityName: "",
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
creator: {
|
|
113
|
+
uri: "test2",
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
req: {
|
|
118
|
+
query: {
|
|
119
|
+
identityName: "name",
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
creator: {
|
|
123
|
+
prefLabel: { en: "name" },
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
req: {
|
|
128
|
+
user: {
|
|
129
|
+
uri: "",
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
creator: null,
|
|
133
|
+
},
|
|
134
|
+
]
|
|
135
|
+
let index = 0
|
|
136
|
+
for (let { req, creator: expected } of tests) {
|
|
137
|
+
it(`should pass test[${index}]`, async () => {
|
|
138
|
+
const actual = getCreator(Object.assign({ query: {} }, req))
|
|
139
|
+
// For non-annotations, creator should be an array if defined
|
|
140
|
+
assert.deepStrictEqual(actual, expected)
|
|
141
|
+
})
|
|
142
|
+
index += 1
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
it("should fail if req is undefined", async () => {
|
|
146
|
+
assert.throws(() => {
|
|
147
|
+
getCreator()
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it("should fail if req.query is undefined", async () => {
|
|
152
|
+
assert.throws(() => {
|
|
153
|
+
getCreator({})
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
describe("handleCreatorForObject", () => {
|
|
160
|
+
const req = {
|
|
161
|
+
anonymous: false,
|
|
162
|
+
auth: true,
|
|
163
|
+
}
|
|
164
|
+
const reqWithMethod = (method) => Object.assign({ method }, req)
|
|
165
|
+
const tests = [
|
|
166
|
+
// Everything undefined
|
|
167
|
+
{
|
|
168
|
+
req,
|
|
169
|
+
},
|
|
170
|
+
// For null-ish values, keep the valye
|
|
171
|
+
{
|
|
172
|
+
object: null,
|
|
173
|
+
expected: null,
|
|
174
|
+
req,
|
|
175
|
+
},
|
|
176
|
+
// Everything empty
|
|
177
|
+
{
|
|
178
|
+
object: {},
|
|
179
|
+
expected: {},
|
|
180
|
+
req,
|
|
181
|
+
},
|
|
182
|
+
// No modifications without method/type
|
|
183
|
+
{
|
|
184
|
+
object: {
|
|
185
|
+
creator: "value doesn't matter",
|
|
186
|
+
},
|
|
187
|
+
expected: {
|
|
188
|
+
creator: "value doesn't matter",
|
|
189
|
+
},
|
|
190
|
+
req,
|
|
191
|
+
},
|
|
192
|
+
// Always remove contributor for annotations
|
|
193
|
+
{
|
|
194
|
+
object: {
|
|
195
|
+
contributor: "value doesn't matter",
|
|
196
|
+
},
|
|
197
|
+
expected: {},
|
|
198
|
+
req: Object.assign({
|
|
199
|
+
type: "annotations",
|
|
200
|
+
}, req),
|
|
201
|
+
},
|
|
202
|
+
// Set creator for POST
|
|
203
|
+
{
|
|
204
|
+
object: {},
|
|
205
|
+
creator: { uri: "test" },
|
|
206
|
+
req: reqWithMethod("POST"),
|
|
207
|
+
expected: {
|
|
208
|
+
creator: [{ uri: "test" }],
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
// Set creator for annotation
|
|
212
|
+
{
|
|
213
|
+
object: { creator: {} },
|
|
214
|
+
creator: { uri: "test" },
|
|
215
|
+
req: Object.assign({
|
|
216
|
+
type: "annotations",
|
|
217
|
+
}, reqWithMethod("PUT")),
|
|
218
|
+
expected: {
|
|
219
|
+
creator: { uri: "test" },
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
// Keep existing creator for PUT
|
|
223
|
+
{
|
|
224
|
+
object: {
|
|
225
|
+
creator: "some creator",
|
|
226
|
+
},
|
|
227
|
+
req: reqWithMethod("PUT"),
|
|
228
|
+
existing: {
|
|
229
|
+
creator: "creator",
|
|
230
|
+
contributor: "contributor",
|
|
231
|
+
},
|
|
232
|
+
expected: {
|
|
233
|
+
creator: "creator",
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
// Remove creator for PATCH
|
|
237
|
+
{
|
|
238
|
+
object: {
|
|
239
|
+
creator: "some creator",
|
|
240
|
+
},
|
|
241
|
+
req: reqWithMethod("PATCH"),
|
|
242
|
+
existing: {
|
|
243
|
+
creator: "creator",
|
|
244
|
+
},
|
|
245
|
+
expected: {},
|
|
246
|
+
},
|
|
247
|
+
// Add to contributor for PUT/PATCH
|
|
248
|
+
{
|
|
249
|
+
object: {},
|
|
250
|
+
req: reqWithMethod("PATCH"),
|
|
251
|
+
creator: {
|
|
252
|
+
uri: "test",
|
|
253
|
+
},
|
|
254
|
+
existing: {},
|
|
255
|
+
expected: {
|
|
256
|
+
contributor: [{ uri: "test" }],
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
object: {},
|
|
261
|
+
req: reqWithMethod("PUT"),
|
|
262
|
+
creator: {
|
|
263
|
+
uri: "test",
|
|
264
|
+
},
|
|
265
|
+
existing: {
|
|
266
|
+
creator: [{ uri: "other" }],
|
|
267
|
+
},
|
|
268
|
+
expected: {
|
|
269
|
+
creator: [{ uri: "other" }],
|
|
270
|
+
contributor: [{ uri: "test" }],
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
// Adjust existing creator entry
|
|
274
|
+
{
|
|
275
|
+
object: {},
|
|
276
|
+
req: Object.assign({
|
|
277
|
+
user: {
|
|
278
|
+
uri: "test",
|
|
279
|
+
identities: {
|
|
280
|
+
test: {
|
|
281
|
+
uri: "testAlternative",
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
}, reqWithMethod("PUT")),
|
|
286
|
+
creator: {
|
|
287
|
+
uri: "test",
|
|
288
|
+
prefLabel: { en: "name" },
|
|
289
|
+
},
|
|
290
|
+
existing: {
|
|
291
|
+
creator: [{ uri: "testAlternative" }],
|
|
292
|
+
},
|
|
293
|
+
expected: {
|
|
294
|
+
creator: [{
|
|
295
|
+
uri: "test",
|
|
296
|
+
prefLabel: { en: "name" },
|
|
297
|
+
}],
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
// Adjust existing contributor entry, push to end
|
|
301
|
+
{
|
|
302
|
+
object: {},
|
|
303
|
+
req: Object.assign({
|
|
304
|
+
user: {
|
|
305
|
+
uri: "test",
|
|
306
|
+
identities: {
|
|
307
|
+
test: {
|
|
308
|
+
uri: "testAlternative",
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
}, reqWithMethod("PUT")),
|
|
313
|
+
creator: { uri: "test" },
|
|
314
|
+
existing: {
|
|
315
|
+
creator: [{}],
|
|
316
|
+
contributor: [{ uri: "testAlternative" }, {}],
|
|
317
|
+
},
|
|
318
|
+
expected: {
|
|
319
|
+
creator: [{}],
|
|
320
|
+
contributor: [{}, { uri: "test" }],
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
// Allow any kind of values if auth is false
|
|
324
|
+
{
|
|
325
|
+
object: { creator: [{ uri: "abc " }], contributor: [] },
|
|
326
|
+
expected: { creator: [{ uri: "abc " }], contributor: [] },
|
|
327
|
+
req: Object.assign(reqWithMethod("POST"), { auth: false }),
|
|
328
|
+
// Should be ignored
|
|
329
|
+
creator: { uri: "test" },
|
|
330
|
+
},
|
|
331
|
+
// Always remove creator/contributor from payload when anonymous is true, but don't change existing values
|
|
332
|
+
{
|
|
333
|
+
object: { creator: [{ uri: "abc " }], contributor: [] },
|
|
334
|
+
existing: { creator: [{ uri: "def" }], contributor: [{ uri: "ghj" }] },
|
|
335
|
+
expected: { creator: [{ uri: "def" }], contributor: [{ uri: "ghj" }] },
|
|
336
|
+
req: Object.assign(reqWithMethod("PUT"), { anonymous: true }),
|
|
337
|
+
},
|
|
338
|
+
]
|
|
339
|
+
let index = 0
|
|
340
|
+
for (let { expected, ...options } of tests) {
|
|
341
|
+
it(`should pass test[${index}]`, async () => {
|
|
342
|
+
const actual = handleCreatorForObject(Object.assign({ req: {} }, options))
|
|
343
|
+
// Should return object reference
|
|
344
|
+
assert.strictEqual(actual, options.object)
|
|
345
|
+
// Check if content is correct as well
|
|
346
|
+
assert.deepStrictEqual(actual, expected)
|
|
347
|
+
})
|
|
348
|
+
index += 1
|
|
349
|
+
}
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
describe("cleanJSON", () => {
|
|
353
|
+
const prevClosedWorldAssumption = config.closedWorldAssumption
|
|
354
|
+
|
|
355
|
+
const tests = [
|
|
356
|
+
{
|
|
357
|
+
closedWorldAssumption: false,
|
|
358
|
+
input: {
|
|
359
|
+
_a: 1,
|
|
360
|
+
b: {},
|
|
361
|
+
c: [],
|
|
362
|
+
d: 2,
|
|
363
|
+
},
|
|
364
|
+
output: {
|
|
365
|
+
d: 2,
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
closedWorldAssumption: true,
|
|
370
|
+
input: {
|
|
371
|
+
b: {},
|
|
372
|
+
c: [],
|
|
373
|
+
},
|
|
374
|
+
output: {
|
|
375
|
+
b: {},
|
|
376
|
+
c: [],
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
closedWorldAssumption: false,
|
|
381
|
+
input: [
|
|
382
|
+
{
|
|
383
|
+
_a: 1,
|
|
384
|
+
b: {},
|
|
385
|
+
c: [],
|
|
386
|
+
d: null,
|
|
387
|
+
},
|
|
388
|
+
],
|
|
389
|
+
output: [
|
|
390
|
+
{
|
|
391
|
+
d: null,
|
|
392
|
+
},
|
|
393
|
+
],
|
|
394
|
+
},
|
|
395
|
+
// Currently, only top-level properties are affected by closedWorldAssumption = false.
|
|
396
|
+
// See: https://github.com/gbv/jskos-server/commit/123dc9da09f1e41f2263ee8a0f7faeefe67fa9ed#r67204606
|
|
397
|
+
{
|
|
398
|
+
closedWorldAssumption: false,
|
|
399
|
+
input: {
|
|
400
|
+
a: {
|
|
401
|
+
b: {},
|
|
402
|
+
_b: 1,
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
output: {
|
|
406
|
+
a: {
|
|
407
|
+
b: {},
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
]
|
|
412
|
+
|
|
413
|
+
let index = 0
|
|
414
|
+
for (let { closedWorldAssumption, input, output } of tests) {
|
|
415
|
+
it(`should pass test[${index}]`, async () => {
|
|
416
|
+
cleanJSON(input, 0, closedWorldAssumption)
|
|
417
|
+
assert.deepEqual(input, output)
|
|
418
|
+
})
|
|
419
|
+
index += 1
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
config.closedWorldAssumption = prevClosedWorldAssumption
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
})
|