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/bin/reset.js ADDED
@@ -0,0 +1,257 @@
1
+ #!/usr/bin/env node
2
+
3
+ import * as db from "../utils/db.js"
4
+ import yesno from "yesno"
5
+ import jskos from "jskos-tools"
6
+ import _ from "lodash"
7
+
8
+
9
+ import meow from "meow"
10
+ const cli = meow(`
11
+ Usage
12
+ $ npm run reset -- [options] [URIs]
13
+
14
+ type is required unless you want to reset the whole database.
15
+
16
+ Options
17
+ GNU long option Option Meaning
18
+ --type -t Object type to be deleted. If omitted, type will be "concepts" if -s is given, "mappings" if -c is given, all types otherwise.
19
+ --scheme -s Only for concepts. Deletes only concepts from a certain scheme (inScheme). (Not applicable when URIs are specified.)
20
+ --concordance -c Only for mappings. Deletes only mappings from a certain concordance (partOf). (Not applicable when URIs are specified.)
21
+ --set-api EXPERIMENTAL. Onlt for concepts. Will update the scheme's \`API\` property after deleting concepts.
22
+
23
+ Examples
24
+ $ npm run reset -- -t concepts -s http://uri.gbv.de/terminology/rvk/
25
+ `, {
26
+ importMeta: import.meta,
27
+ flags: {
28
+ type: {
29
+ type: "string",
30
+ shortFlag: "t",
31
+ default: "",
32
+ },
33
+ scheme: {
34
+ type: "string",
35
+ shortFlag: "s",
36
+ default: "",
37
+ },
38
+ setApi: {
39
+ type: "boolean",
40
+ default: false,
41
+ },
42
+ concordance: {
43
+ type: "string",
44
+ shortFlag: "c",
45
+ default: "",
46
+ },
47
+ help: {
48
+ type: "boolean",
49
+ shortFlag: "h",
50
+ default: false,
51
+ },
52
+ },
53
+ })
54
+
55
+ if (cli.flags.help) {
56
+ cli.showHelp()
57
+ process.exit(0)
58
+ }
59
+
60
+ const log = (...args) => {
61
+ console.log(...args)
62
+ }
63
+ const logError = ({ message, showHelp = false, exit = false }) => {
64
+ console.error(` Error: ${message}`)
65
+ if (showHelp) {
66
+ cli.showHelp()
67
+ }
68
+ if (exit) {
69
+ db.disconnect()
70
+ process.exit(1)
71
+ }
72
+ }
73
+
74
+ const uris = cli.input
75
+
76
+ // Parse type
77
+ let type = jskos.guessObjectType(cli.flags.type, true)
78
+ let typeInferred = false
79
+ if (cli.flags.type && !type) {
80
+ logError({
81
+ message: `Invalid <type> argument: ${cli.flags.type || ""}`,
82
+ showHelp: true,
83
+ exit: true,
84
+ })
85
+ }
86
+
87
+ // Consistentcy checks and inferrence of parameters
88
+ if (cli.flags.scheme && cli.flags.concordance) {
89
+ logError({
90
+ message: "Options -s and -c are not compatible with each other.",
91
+ showHelp: true,
92
+ exit: true,
93
+ })
94
+ } else if ((cli.flags.scheme || cli.flags.concordance) && uris.length) {
95
+ logError({
96
+ message: "Options -s/-c can't be used when URIs are specified.",
97
+ showHelp: true,
98
+ exit: true,
99
+ })
100
+ } else if (cli.flags.scheme && !type) {
101
+ type = "concept"
102
+ typeInferred = true
103
+ } else if (cli.flags.concordance && !type) {
104
+ type = "mapping"
105
+ typeInferred = true
106
+ } else if (cli.flags.scheme && type != "concept") {
107
+ logError({
108
+ message: `Option -s is not compatible with type ${type}.`,
109
+ showHelp: true,
110
+ exit: true,
111
+ })
112
+ } else if (cli.flags.setApi && type != "concept") {
113
+ logError({
114
+ message: `Option --set-api is not compatible with type ${type}.`,
115
+ showHelp: true,
116
+ exit: true,
117
+ })
118
+ } else if (cli.flags.concordance && type != "mapping") {
119
+ logError({
120
+ message: `Option -c is not compatible with type ${type}.`,
121
+ showHelp: true,
122
+ exit: true,
123
+ })
124
+ }
125
+
126
+ import config from "../config/index.js"
127
+ import { createServices } from "../services/index.js"
128
+ const services = createServices(config)
129
+ import { models } from "../models/index.js"
130
+
131
+ ;
132
+ (async () => {
133
+ // 1. Connect to database.
134
+ try {
135
+ await db.connect()
136
+ } catch (error) {
137
+ logError({
138
+ message: error,
139
+ exit: true,
140
+ })
141
+ }
142
+ log("Connection to database established.")
143
+ log()
144
+
145
+ // 2. Figure out what will get deleted (default: all types).
146
+ const types = type ? [type] : Object.keys(models)
147
+ // Prepare scheme/concordance URIs
148
+ let filterUris
149
+ if (cli.flags.scheme) {
150
+ const scheme = await services.scheme.getScheme(cli.flags.scheme)
151
+ if (scheme) {
152
+ filterUris = [scheme.uri].concat(scheme.identifier || [])
153
+ } else {
154
+ filterUris = [cli.flags.scheme]
155
+ }
156
+ } else if (cli.flags.concordance) {
157
+ const concordance = await services.concordance.getItem(cli.flags.concordance)
158
+ if (concordance) {
159
+ filterUris = [concordance.uri].concat(concordance.identifier || [])
160
+ } else {
161
+ filterUris = [cli.flags.concordance]
162
+ }
163
+ }
164
+ const toBeDeleted = {}
165
+ for (let type of types) {
166
+ const query = {}
167
+ if (!uris.length) {
168
+ // Get list of all URIs from database
169
+ // Apply filters if necessary
170
+ if (cli.flags.scheme) {
171
+ query["inScheme.uri"] = { $in: filterUris }
172
+ } else if (cli.flags.concordance) {
173
+ query["partOf.uri"] = { $in: filterUris }
174
+ }
175
+ } else {
176
+ query[type === "annotation" ? "id" : "uri"] = { $in: uris }
177
+ }
178
+ toBeDeleted[type] = (await models[type].find(query, { _id: 1 }).lean()).map(r => r._id)
179
+ }
180
+
181
+ let question = `Deleting ${uris.length ? "" : "all "}${type ? `${type}s` : "data"}${typeInferred ? " (inferred)" : ""}`
182
+ if (uris.length) {
183
+ question += " with URIs " + uris.join(", ")
184
+ }
185
+ if (cli.flags.scheme) {
186
+ question += ` from scheme ${cli.flags.scheme}`
187
+ } else if (cli.flags.concordance) {
188
+ question += ` from concordance ${cli.flags.concordance}`
189
+ }
190
+ question += " from database.\n"
191
+ let totalCount = 0
192
+ for (let type of types) {
193
+ const count = toBeDeleted[type] && toBeDeleted[type].length
194
+ if (count) {
195
+ question += `- ${count} ${type}s will be deleted.\n`
196
+ }
197
+ totalCount += count
198
+ }
199
+ if (totalCount == 0) {
200
+ logError({
201
+ message: "Did not find any entities to be deleted, aborting...",
202
+ exit: true,
203
+ })
204
+ }
205
+ if (totalCount > 50000) {
206
+ question += "This will take a while.\n"
207
+ }
208
+ question += "Is that okay?"
209
+ const ok = await yesno({
210
+ question,
211
+ defaultValue: false,
212
+ })
213
+ if (!ok) {
214
+ logError({
215
+ message: "Aborting...",
216
+ exit: true,
217
+ })
218
+ }
219
+ log()
220
+
221
+ for (let type of types) {
222
+ if (!toBeDeleted[type] || !toBeDeleted[type].length) {
223
+ continue
224
+ }
225
+ log(`Dealing with ${type}s...`)
226
+ for (let chunk of _.chunk(toBeDeleted[type], 50000)) {
227
+ const query = { _id: { $in: chunk } }
228
+ // For concepts, get all schemes to be adjusted later
229
+ let schemeUrisToAdjust = []
230
+ if (type == "concept") {
231
+ schemeUrisToAdjust = await models.concept.distinct("inScheme.uri", query).lean()
232
+ }
233
+ // For mappings, get all concordances to be adjusted later
234
+ let concordanceUrisToAdjust = []
235
+ if (type == "mapping") {
236
+ concordanceUrisToAdjust = await models.mapping.distinct("partOf.uri", query).lean()
237
+ }
238
+ // Delete entities...
239
+ const result = await models[type].deleteMany(query)
240
+ log(`- ${result.deletedCount} ${type}s deleted.`)
241
+ // Adjust schemes
242
+ if (schemeUrisToAdjust.length) {
243
+ log(`- adjusting ${schemeUrisToAdjust.length} schemes...`)
244
+ await services.scheme.postAdjustmentsForItems(schemeUrisToAdjust.map(uri => ({ uri })), { setApi: cli.flags.setApi })
245
+ }
246
+ // Adjust concordances
247
+ if (concordanceUrisToAdjust.length) {
248
+ log(`- adjusting extent for ${concordanceUrisToAdjust.length} concordances...`)
249
+ await Promise.all(concordanceUrisToAdjust.map(uri => services.concordance.postAdjustmentForConcordance(uri)))
250
+ }
251
+ }
252
+ log()
253
+ }
254
+
255
+ db.disconnect()
256
+ log(`End of import script: ${new Date()}`)
257
+ })()
package/bin/upgrade.js ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Upgrader } from "../utils/version.js"
4
+ import config from "../config/index.js"
5
+
6
+ const upgrader = new Upgrader(config)
7
+
8
+ import * as db from "../utils/db.js"
9
+ import { Meta } from "../models/meta.js"
10
+
11
+ ;(async () => {
12
+ await db.connect()
13
+ const meta = await Meta.findOne()
14
+ const list = upgrader.getUpgrades(meta.version, { forceLatest: process.argv.includes("-f") || process.argv.includes("--force-latest") })
15
+ console.log()
16
+ for (const version of list) {
17
+ console.log(`Performing necessary upgrades for version ${version}...`)
18
+ try {
19
+ await upgrader[version]()
20
+ meta.version = version
21
+ await meta.save()
22
+ console.log(`... upgrades for version ${version} done.`)
23
+ } catch (error) {
24
+ console.error("Error:", error)
25
+ console.error("aborting...")
26
+ break
27
+ }
28
+ console.log()
29
+ }
30
+ if (!list.length) {
31
+ console.log("No upgrades necessary.")
32
+ }
33
+ db.disconnect()
34
+ })()
@@ -0,0 +1,88 @@
1
+ {
2
+ "verbosity": "warn",
3
+ "baseUrl": null,
4
+ "title": "JSKOS Server",
5
+ "version": null,
6
+ "closedWorldAssumption": true,
7
+ "port": 3000,
8
+ "proxies": [],
9
+ "mongo": {
10
+ "user": "",
11
+ "pass": "",
12
+ "host": "127.0.0.1",
13
+ "port": 27017,
14
+ "db": "jskos-server",
15
+ "options": {
16
+ "connectTimeoutMS": 360000,
17
+ "socketTimeoutMS": 360000,
18
+ "heartbeatFrequencyMS": 10000
19
+ }
20
+ },
21
+ "auth": {
22
+ "algorithm": "RS256",
23
+ "key": null
24
+ },
25
+ "schemes": true,
26
+ "concepts": true,
27
+ "mappings": {
28
+ "read": {
29
+ "auth": false
30
+ },
31
+ "create": {
32
+ "auth": true
33
+ },
34
+ "update": {
35
+ "auth": true,
36
+ "crossUser": false
37
+ },
38
+ "delete": {
39
+ "auth": true,
40
+ "crossUser": false
41
+ },
42
+ "fromSchemeWhitelist": null,
43
+ "toSchemeWhitelist": null,
44
+ "cardinality": "1-to-n"
45
+ },
46
+ "concordances": true,
47
+ "annotations": {
48
+ "read": {
49
+ "auth": false
50
+ },
51
+ "create": {
52
+ "auth": true
53
+ },
54
+ "update": {
55
+ "auth": true,
56
+ "crossUser": false
57
+ },
58
+ "delete": {
59
+ "auth": true,
60
+ "crossUser": false
61
+ },
62
+ "moderatingIdentities": [],
63
+ "mismatchTagVocabulary": null
64
+ },
65
+ "registries": {
66
+ "read": {
67
+ "auth": false
68
+ },
69
+ "create": {
70
+ "auth": true
71
+ },
72
+ "update": {
73
+ "auth": true,
74
+ "crossUser": false
75
+ },
76
+ "delete": {
77
+ "auth": true,
78
+ "crossUser": false
79
+ },
80
+ "mixedTypes": false
81
+ },
82
+ "changes": false,
83
+ "anonymous": false,
84
+ "identityProviders": null,
85
+ "identityGroups": {},
86
+ "identities": null,
87
+ "ips": null
88
+ }