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
package/errors/index.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Errors.
|
|
3
|
+
*
|
|
4
|
+
* If possible, provide a more detailed error message when one of these errors is used.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export class EntityNotFoundError extends Error {
|
|
8
|
+
constructor(message, id) {
|
|
9
|
+
message = message || `The requested entity ${id} could not be found.`
|
|
10
|
+
super(message)
|
|
11
|
+
this.statusCode = 404
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class MalformedBodyError extends Error {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
message = message || "The body of the request is malformed."
|
|
18
|
+
super(message)
|
|
19
|
+
this.statusCode = 400
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class MalformedRequestError extends Error {
|
|
24
|
+
constructor(message) {
|
|
25
|
+
message = message || "The request is malformed (missing parameter etc.)."
|
|
26
|
+
super(message)
|
|
27
|
+
this.statusCode = 400
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class DuplicateEntityError extends Error {
|
|
32
|
+
constructor(message, id) {
|
|
33
|
+
message = message || `The entity ${id} already exists in the database.`
|
|
34
|
+
super(message)
|
|
35
|
+
this.statusCode = 422
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class InvalidBodyError extends Error {
|
|
40
|
+
constructor(message) {
|
|
41
|
+
message = message || "The body of the request is well formed, but could not be validated."
|
|
42
|
+
super(message)
|
|
43
|
+
this.statusCode = 422
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class CreatorDoesNotMatchError extends Error {
|
|
48
|
+
constructor(message) {
|
|
49
|
+
message = message || "Access to this ressource is not allowed for you (but might be for other users)."
|
|
50
|
+
super(message)
|
|
51
|
+
this.statusCode = 403
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export class BackendError extends Error {
|
|
56
|
+
constructor(message) {
|
|
57
|
+
message = message || "There was an error with the backend. Please try again later."
|
|
58
|
+
super(message)
|
|
59
|
+
this.statusCode = 500
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class DatabaseAccessError extends BackendError {
|
|
64
|
+
constructor(message) {
|
|
65
|
+
message = message || "There was an error accessing the database. Please try again later."
|
|
66
|
+
super(message)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export class DatabaseInconsistencyError extends BackendError {
|
|
71
|
+
constructor(message) {
|
|
72
|
+
if (message) {
|
|
73
|
+
message += " Please contact us with this error message at coli-conc@gbv.de or open an issue on GitHub. Thanks!"
|
|
74
|
+
} else {
|
|
75
|
+
message = "There was an inconsistency error with the database. Please try again later."
|
|
76
|
+
}
|
|
77
|
+
super(message)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export class ConfigurationError extends BackendError {
|
|
82
|
+
constructor(message) {
|
|
83
|
+
message = message || "There was an error with the server configuration. Please contact the server administrator and try again later."
|
|
84
|
+
super(message)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export class ForbiddenAccessError extends Error {
|
|
89
|
+
constructor(message) {
|
|
90
|
+
message = message || "Access is forbidden."
|
|
91
|
+
super(message)
|
|
92
|
+
this.statusCode = 403
|
|
93
|
+
}
|
|
94
|
+
}
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import gbv from "eslint-config-gbv"
|
|
2
|
+
|
|
3
|
+
// Extend base config and add ignores for Docker data
|
|
4
|
+
export default [
|
|
5
|
+
...gbv,
|
|
6
|
+
{
|
|
7
|
+
ignores: [
|
|
8
|
+
"docker/data/**",
|
|
9
|
+
],
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
languageOptions: {
|
|
13
|
+
globals: { mocha: "readable" },
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
]
|
|
17
|
+
|
package/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { validateConfig, setupConfig } from "./config/setup.js"
|
|
2
|
+
import { createServices as _createServices } from "./services/index.js"
|
|
3
|
+
|
|
4
|
+
// TODO: move this into createServices
|
|
5
|
+
function createServices(config) {
|
|
6
|
+
setupConfig(config)
|
|
7
|
+
return _createServices(config)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { validateConfig, createServices }
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import mongoose from "mongoose"
|
|
2
|
+
const Schema = mongoose.Schema
|
|
3
|
+
|
|
4
|
+
const annotationSchema = new Schema({
|
|
5
|
+
_id: String,
|
|
6
|
+
id: String,
|
|
7
|
+
}, {
|
|
8
|
+
versionKey: false,
|
|
9
|
+
strict: false,
|
|
10
|
+
autoIndex: false,
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
export const Annotation = mongoose.model("Annotation", annotationSchema)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import mongoose from "mongoose"
|
|
2
|
+
const Schema = mongoose.Schema
|
|
3
|
+
|
|
4
|
+
const conceptSchema = new Schema({
|
|
5
|
+
_id: String,
|
|
6
|
+
}, {
|
|
7
|
+
versionKey: false,
|
|
8
|
+
strict: false,
|
|
9
|
+
autoIndex: false,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
export const Concept = mongoose.model("Concept", conceptSchema)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import mongoose from "mongoose"
|
|
2
|
+
const Schema = mongoose.Schema
|
|
3
|
+
|
|
4
|
+
const concordanceSchema = new Schema({
|
|
5
|
+
_id: String,
|
|
6
|
+
}, {
|
|
7
|
+
versionKey: false,
|
|
8
|
+
strict: false,
|
|
9
|
+
autoIndex: false,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
export const Concordance = mongoose.model("Concordance", concordanceSchema)
|
package/models/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Annotation } from "./annotations.js"
|
|
2
|
+
import { Concept } from "./concepts.js"
|
|
3
|
+
import { Concordance } from "./concordances.js"
|
|
4
|
+
import { Mapping } from "./mappings.js"
|
|
5
|
+
import { Meta } from "./meta.js"
|
|
6
|
+
import { Scheme } from "./schemes.js"
|
|
7
|
+
import { Registry } from "./registries.js"
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
Annotation,
|
|
11
|
+
Concept,
|
|
12
|
+
Concordance,
|
|
13
|
+
Mapping,
|
|
14
|
+
Meta,
|
|
15
|
+
Scheme,
|
|
16
|
+
Registry,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const models = {
|
|
20
|
+
scheme: Scheme,
|
|
21
|
+
concept: Concept,
|
|
22
|
+
concordance: Concordance,
|
|
23
|
+
mapping: Mapping,
|
|
24
|
+
annotation: Annotation,
|
|
25
|
+
registry: Registry,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
Object.keys(models).forEach(type => {
|
|
29
|
+
const plural = type === "registry" ? "registries" : `${type}s`
|
|
30
|
+
Object.defineProperty(models, plural, {
|
|
31
|
+
get: () => models[type],
|
|
32
|
+
})
|
|
33
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import mongoose from "mongoose"
|
|
2
|
+
import jskos from "jskos-tools"
|
|
3
|
+
|
|
4
|
+
const Schema = mongoose.Schema
|
|
5
|
+
|
|
6
|
+
const mappingSchema = new Schema({
|
|
7
|
+
_id: String,
|
|
8
|
+
}, {
|
|
9
|
+
versionKey: false,
|
|
10
|
+
strict: false,
|
|
11
|
+
autoIndex: false,
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
mappingSchema.pre("save", function(next) {
|
|
15
|
+
// Add mapping identifier
|
|
16
|
+
this.set("identifier", jskos.addMappingIdentifiers(this).identifier)
|
|
17
|
+
next()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
export const Mapping = mongoose.model("Mapping", mappingSchema)
|
package/models/meta.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import mongoose from "mongoose"
|
|
2
|
+
const Schema = mongoose.Schema
|
|
3
|
+
|
|
4
|
+
const metaSchema = new Schema({
|
|
5
|
+
// makes sure that there's only one entry in the collection
|
|
6
|
+
_default: {
|
|
7
|
+
type: Boolean,
|
|
8
|
+
default: true,
|
|
9
|
+
required: true,
|
|
10
|
+
unique: true,
|
|
11
|
+
immutable: true,
|
|
12
|
+
},
|
|
13
|
+
version: String,
|
|
14
|
+
}, {
|
|
15
|
+
versionKey: false,
|
|
16
|
+
strict: false,
|
|
17
|
+
collection: "meta",
|
|
18
|
+
autoIndex: false,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
export const Meta = mongoose.model("meta", metaSchema)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import mongoose from "mongoose"
|
|
2
|
+
const Schema = mongoose.Schema
|
|
3
|
+
|
|
4
|
+
const registrySchema = new Schema({
|
|
5
|
+
_id: String,
|
|
6
|
+
}, {
|
|
7
|
+
versionKey: false,
|
|
8
|
+
strict: false,
|
|
9
|
+
autoIndex: false,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
export const Registry = mongoose.model("Registry", registrySchema)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import mongoose from "mongoose"
|
|
2
|
+
const Schema = mongoose.Schema
|
|
3
|
+
|
|
4
|
+
const terminologySchema = new Schema({
|
|
5
|
+
_id: String,
|
|
6
|
+
}, {
|
|
7
|
+
versionKey: false,
|
|
8
|
+
strict: false,
|
|
9
|
+
autoIndex: false,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
export const Scheme = mongoose.model("Terminology", terminologySchema)
|
package/package.json
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jskos-server",
|
|
3
|
+
"version": "2.4.0",
|
|
4
|
+
"apiVersion": "2.2",
|
|
5
|
+
"description": "JSKOS database and web service",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/gbv/jskos-server.git"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"exports": "./index.js",
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=22"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "NODE_ENV=test mocha --exit --timeout 60000 --slow 2000",
|
|
17
|
+
"coverage": "NODE_ENV=test c8 --reporter=text --skip-full mocha --exit --timeout 60000 --slow 2000",
|
|
18
|
+
"start": "nodemon server.js",
|
|
19
|
+
"mongodb": "./bin/mongodb.js",
|
|
20
|
+
"lint": "eslint",
|
|
21
|
+
"fix": "eslint --fix",
|
|
22
|
+
"lint-staged": "lint-staged",
|
|
23
|
+
"prepare": "husky || true",
|
|
24
|
+
"import": "./bin/import.js",
|
|
25
|
+
"import-batch": "func() { tr '\\n' '\\0' < \"$2\" | xargs -0 -n1 ./bin/import.js $1; }; func",
|
|
26
|
+
"reset": "./bin/reset.js",
|
|
27
|
+
"upgrade": "./bin/upgrade.js",
|
|
28
|
+
"extra": "./bin/extra.js",
|
|
29
|
+
"yesno": "node -e \"const yesno = require('yesno'); yesno({ question: 'Are you sure you want to continue?' }).then(ok => process.exit(ok ? 0 : 1));\"",
|
|
30
|
+
"release": "test $(git rev-parse --abbrev-ref HEAD) = dev && git pull && npm test && npm version $SEMVER && npm run --silent yesno && (git push && git checkout main && git merge dev && git push --follow-tags && git checkout dev) || (git tag -d $(git describe --tags) && git reset --hard HEAD~1)",
|
|
31
|
+
"release:patch": "SEMVER=patch npm run release",
|
|
32
|
+
"release:minor": "SEMVER=minor npm run release",
|
|
33
|
+
"release:major": "SEMVER=major npm run release"
|
|
34
|
+
},
|
|
35
|
+
"lint-staged": {
|
|
36
|
+
"**/*.js": [
|
|
37
|
+
"eslint --fix"
|
|
38
|
+
],
|
|
39
|
+
"*.js": [
|
|
40
|
+
"eslint --fix"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"author": "",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"c8": "^10.1.3",
|
|
47
|
+
"chai": "^6.2.2",
|
|
48
|
+
"chai-as-promised": "^8.0.2",
|
|
49
|
+
"chai-http": "^5.1.1",
|
|
50
|
+
"eslint": "~9.39",
|
|
51
|
+
"eslint-config-gbv": "~2.7",
|
|
52
|
+
"esmock": "^2.6.9",
|
|
53
|
+
"express-ws": "^5.0.2",
|
|
54
|
+
"glob": "^13.0.0",
|
|
55
|
+
"husky": "^9.1.7",
|
|
56
|
+
"jsonwebtoken": "^9.0.2",
|
|
57
|
+
"lint-staged": "^16.2.7",
|
|
58
|
+
"mocha": "^11.0.1",
|
|
59
|
+
"mongodb-memory-server": "^11.0.1",
|
|
60
|
+
"ws": "^8.18.2"
|
|
61
|
+
},
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"ajv": "^8.17.1",
|
|
64
|
+
"ajv-formats": "^3.0.1",
|
|
65
|
+
"axios": "^1.7.9",
|
|
66
|
+
"cocoda-sdk": "^3.4.12",
|
|
67
|
+
"dotenv": "^16.4.7",
|
|
68
|
+
"ejs": "^3.1.10",
|
|
69
|
+
"express": "^5.2.1",
|
|
70
|
+
"express-basic-auth": "^1.2.1",
|
|
71
|
+
"express-ws": "^5.0.2",
|
|
72
|
+
"ipaddr.js": "^2.2.0",
|
|
73
|
+
"jskos-tools": "^1.0.43",
|
|
74
|
+
"jskos-validate": "1.2.1",
|
|
75
|
+
"json-anystream": "^2.0.1",
|
|
76
|
+
"JSONStream": "^1.3.5",
|
|
77
|
+
"lodash": "^4.17.21",
|
|
78
|
+
"meow": "^14.0.0",
|
|
79
|
+
"mongodb": "^7.0.0",
|
|
80
|
+
"mongoose": "^9.1.2",
|
|
81
|
+
"morgan": "^1.10.0",
|
|
82
|
+
"nocache": "^4.0.0",
|
|
83
|
+
"nodemon": "^3.1.7",
|
|
84
|
+
"passport": "~0.7.0",
|
|
85
|
+
"passport-anonymous": "^1.0.1",
|
|
86
|
+
"passport-jwt": "^4.0.1",
|
|
87
|
+
"portfinder": "^1.0.32",
|
|
88
|
+
"uuid": "^13.0.0",
|
|
89
|
+
"yesno": "^0.4.0"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import express from "express"
|
|
2
|
+
import { AnnotationService } from "../services/annotations.js"
|
|
3
|
+
import * as utils from "../utils/middleware.js"
|
|
4
|
+
import { wrapAsync } from "../utils/middleware.js"
|
|
5
|
+
import { useAuth } from "../utils/auth.js"
|
|
6
|
+
import { readRoute, createRoute, updateRoute, deleteRoute } from "./common.js"
|
|
7
|
+
|
|
8
|
+
export default config => {
|
|
9
|
+
const router = express.Router()
|
|
10
|
+
const { annotations } = config
|
|
11
|
+
if (!annotations) {
|
|
12
|
+
return router
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const service = new AnnotationService(config)
|
|
16
|
+
|
|
17
|
+
readRoute(router, "/", annotations.read, service, "annotations")
|
|
18
|
+
createRoute(router, "/", annotations.create, service)
|
|
19
|
+
updateRoute(router, "/", annotations.update, service)
|
|
20
|
+
deleteRoute(router, "/", annotations.delete, service)
|
|
21
|
+
|
|
22
|
+
if (annotations.read) {
|
|
23
|
+
router.get(
|
|
24
|
+
"/:_id",
|
|
25
|
+
useAuth(annotations.read.auth),
|
|
26
|
+
wrapAsync(async req => service.getItem(req.params._id)),
|
|
27
|
+
utils.adjust,
|
|
28
|
+
utils.returnJSON,
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (annotations.update) {
|
|
33
|
+
router.put(
|
|
34
|
+
"/:_id",
|
|
35
|
+
useAuth(annotations.update.auth),
|
|
36
|
+
utils.bodyParser,
|
|
37
|
+
wrapAsync(async (req) => {
|
|
38
|
+
return await service.updateItem({
|
|
39
|
+
_id: req.params._id,
|
|
40
|
+
body: req.body,
|
|
41
|
+
user: req.user,
|
|
42
|
+
existing: req.existing,
|
|
43
|
+
})
|
|
44
|
+
}),
|
|
45
|
+
utils.adjust,
|
|
46
|
+
utils.returnJSON,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
router.patch(
|
|
50
|
+
"/:_id",
|
|
51
|
+
useAuth(annotations.update.auth),
|
|
52
|
+
utils.bodyParser,
|
|
53
|
+
wrapAsync(async (req) => {
|
|
54
|
+
return await service.patchAnnotation({
|
|
55
|
+
_id: req.params._id,
|
|
56
|
+
body: req.body,
|
|
57
|
+
user: req.user,
|
|
58
|
+
existing: req.existing,
|
|
59
|
+
})
|
|
60
|
+
}),
|
|
61
|
+
utils.adjust,
|
|
62
|
+
utils.returnJSON,
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (annotations.delete) {
|
|
67
|
+
router.delete(
|
|
68
|
+
"/:_id",
|
|
69
|
+
useAuth(annotations.delete.auth),
|
|
70
|
+
utils.bodyParser,
|
|
71
|
+
wrapAsync(async (req) => {
|
|
72
|
+
return await service.deleteItem({
|
|
73
|
+
uri: req.params._id,
|
|
74
|
+
user: req.user,
|
|
75
|
+
existing: req.existing,
|
|
76
|
+
})
|
|
77
|
+
}),
|
|
78
|
+
(req, res) => res.sendStatus(204),
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return router
|
|
83
|
+
}
|
package/routes/common.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { useAuth } from "../utils/auth.js"
|
|
2
|
+
import { bodyParser, wrapAsync, supportDownloadFormats, returnJSON, addPaginationHeaders, handleDownload, wrapDownload, adjust } from "../utils/middleware.js"
|
|
3
|
+
|
|
4
|
+
export function readRoute(router, path, config, service, name, formats=[]) {
|
|
5
|
+
if (config) {
|
|
6
|
+
router.get(
|
|
7
|
+
path,
|
|
8
|
+
useAuth(config.auth),
|
|
9
|
+
supportDownloadFormats(formats),
|
|
10
|
+
wrapAsync(async req => service.queryItems(req.query)),
|
|
11
|
+
wrapDownload(addPaginationHeaders, false),
|
|
12
|
+
wrapDownload(adjust, false),
|
|
13
|
+
wrapDownload(returnJSON, false),
|
|
14
|
+
wrapDownload(handleDownload(name), true),
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createRoute(router, path, config, service) {
|
|
20
|
+
if (config) {
|
|
21
|
+
router.post(
|
|
22
|
+
path,
|
|
23
|
+
useAuth(config.auth),
|
|
24
|
+
bodyParser,
|
|
25
|
+
wrapAsync(async (req) => {
|
|
26
|
+
return await service.createItem({
|
|
27
|
+
bodyStream: req.anystream,
|
|
28
|
+
user: req.user,
|
|
29
|
+
bulk: req.query?.bulk,
|
|
30
|
+
scheme: req.query?.scheme,
|
|
31
|
+
setApi: req.query?.setApi, // TODO: this is not documented
|
|
32
|
+
})
|
|
33
|
+
}),
|
|
34
|
+
adjust,
|
|
35
|
+
returnJSON,
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function updateRoute(router, path, config, service) {
|
|
41
|
+
if (config) {
|
|
42
|
+
router.put(
|
|
43
|
+
path,
|
|
44
|
+
useAuth(config.auth),
|
|
45
|
+
bodyParser,
|
|
46
|
+
wrapAsync(async req => service.updateItems({
|
|
47
|
+
body: req.body,
|
|
48
|
+
existing: req.existing,
|
|
49
|
+
setApi: req.query?.setApi, // TODO: this is not documented
|
|
50
|
+
})),
|
|
51
|
+
adjust,
|
|
52
|
+
returnJSON,
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function deleteRoute(router, path, config, service) {
|
|
58
|
+
if (config) {
|
|
59
|
+
router.delete(
|
|
60
|
+
path,
|
|
61
|
+
useAuth(config.auth),
|
|
62
|
+
bodyParser,
|
|
63
|
+
wrapAsync(async (req) => {
|
|
64
|
+
return await service.deleteItem({
|
|
65
|
+
uri: req.query.uri,
|
|
66
|
+
existing: req.existing,
|
|
67
|
+
setApi: req.query?.setApi, // TODO: this is not documented
|
|
68
|
+
})
|
|
69
|
+
}),
|
|
70
|
+
(req, res) => res.sendStatus(204),
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function suggestRoute(router, path, config, service) {
|
|
76
|
+
if (config) {
|
|
77
|
+
router.get(
|
|
78
|
+
path,
|
|
79
|
+
useAuth(config.auth),
|
|
80
|
+
supportDownloadFormats([]),
|
|
81
|
+
wrapAsync(async req => service.getSuggestions(req.query)),
|
|
82
|
+
addPaginationHeaders,
|
|
83
|
+
returnJSON,
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import express from "express"
|
|
2
|
+
import { ConceptService } from "../services/concepts.js"
|
|
3
|
+
import * as utils from "../utils/middleware.js"
|
|
4
|
+
import { wrapAsync } from "../utils/middleware.js"
|
|
5
|
+
import { useAuth } from "../utils/auth.js"
|
|
6
|
+
import { readRoute, createRoute, updateRoute, deleteRoute, suggestRoute } from "./common.js"
|
|
7
|
+
|
|
8
|
+
export default config => {
|
|
9
|
+
const router = express.Router()
|
|
10
|
+
const { concepts } = config
|
|
11
|
+
if (!concepts) {
|
|
12
|
+
return concepts
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const service = new ConceptService(config)
|
|
16
|
+
|
|
17
|
+
readRoute(router, "/concepts", concepts.read, service, "concepts", ["json", "ndjson"])
|
|
18
|
+
createRoute(router, "/concepts", concepts.create, service)
|
|
19
|
+
updateRoute(router, "/concepts", concepts.update, service)
|
|
20
|
+
deleteRoute(router, "/concepts", concepts.delete, service)
|
|
21
|
+
|
|
22
|
+
if (concepts.read) {
|
|
23
|
+
// Add these routes both with and without the /concepts prefix.
|
|
24
|
+
// TODO for 3.0: The routes without should be deprecated in the next major release.
|
|
25
|
+
// See also: https://github.com/gbv/jskos-server/issues/193#issuecomment-1508038432
|
|
26
|
+
|
|
27
|
+
for (const prefix of ["", "/concepts"]) {
|
|
28
|
+
|
|
29
|
+
router.get(
|
|
30
|
+
prefix + "/narrower",
|
|
31
|
+
useAuth(concepts.read.auth),
|
|
32
|
+
utils.supportDownloadFormats([]),
|
|
33
|
+
wrapAsync(async req => service.getNarrower(req.query)),
|
|
34
|
+
utils.addPaginationHeaders,
|
|
35
|
+
utils.adjust,
|
|
36
|
+
utils.returnJSON,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
router.get(
|
|
40
|
+
prefix + "/ancestors",
|
|
41
|
+
useAuth(concepts.read.auth),
|
|
42
|
+
utils.supportDownloadFormats([]),
|
|
43
|
+
wrapAsync(async req => service.getAncestors(req.query)),
|
|
44
|
+
utils.addPaginationHeaders,
|
|
45
|
+
utils.adjust,
|
|
46
|
+
utils.returnJSON,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
suggestRoute(router, prefix + "/suggest", concepts.read, service)
|
|
50
|
+
|
|
51
|
+
router.get(
|
|
52
|
+
prefix + "/search",
|
|
53
|
+
useAuth(concepts.read.auth),
|
|
54
|
+
utils.supportDownloadFormats([]),
|
|
55
|
+
wrapAsync(async req => await service.search(req.query)),
|
|
56
|
+
utils.addPaginationHeaders,
|
|
57
|
+
utils.adjust,
|
|
58
|
+
utils.returnJSON,
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return router
|
|
64
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import express from "express"
|
|
2
|
+
import { ConcordanceService } from "../services/concordances.js"
|
|
3
|
+
import * as utils from "../utils/middleware.js"
|
|
4
|
+
import { wrapAsync, wrapDownload } from "../utils/middleware.js"
|
|
5
|
+
import { useAuth } from "../utils/auth.js"
|
|
6
|
+
import { readRoute, createRoute, updateRoute, deleteRoute } from "./common.js"
|
|
7
|
+
|
|
8
|
+
export default config => {
|
|
9
|
+
const router = express.Router()
|
|
10
|
+
const { concordances } = config
|
|
11
|
+
if (!concordances) {
|
|
12
|
+
return router
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const service = new ConcordanceService(config)
|
|
16
|
+
|
|
17
|
+
readRoute(router, "/", concordances.read, service, "concordances", ["json", "ndjson"])
|
|
18
|
+
|
|
19
|
+
if (concordances.read) {
|
|
20
|
+
router.get(
|
|
21
|
+
"/:_id",
|
|
22
|
+
useAuth(concordances.read.auth),
|
|
23
|
+
utils.supportDownloadFormats(["json", "ndjson"]),
|
|
24
|
+
wrapAsync(async req => service.getItem(req.params._id)),
|
|
25
|
+
wrapDownload(utils.adjust, false),
|
|
26
|
+
wrapDownload(utils.returnJSON, false),
|
|
27
|
+
wrapDownload(utils.handleDownload("concordance"), true),
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
createRoute(router, "/", concordances.create, service)
|
|
32
|
+
updateRoute(router, "/", concordances.update, service)
|
|
33
|
+
deleteRoute(router, "/", concordances.delete, service)
|
|
34
|
+
|
|
35
|
+
if (concordances.update) {
|
|
36
|
+
router.put(
|
|
37
|
+
"/:_id",
|
|
38
|
+
useAuth(concordances.update.auth),
|
|
39
|
+
utils.bodyParser,
|
|
40
|
+
wrapAsync(async (req) => {
|
|
41
|
+
return await service.putConcordance({
|
|
42
|
+
_id: req.params._id,
|
|
43
|
+
body: req.body,
|
|
44
|
+
user: req.user,
|
|
45
|
+
existing: req.existing,
|
|
46
|
+
})
|
|
47
|
+
}),
|
|
48
|
+
utils.adjust,
|
|
49
|
+
utils.returnJSON,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
router.patch(
|
|
53
|
+
"/:_id",
|
|
54
|
+
useAuth(concordances.update.auth),
|
|
55
|
+
utils.bodyParser,
|
|
56
|
+
wrapAsync(async (req) => {
|
|
57
|
+
return await service.patchConcordance({
|
|
58
|
+
_id: req.params._id,
|
|
59
|
+
body: req.body,
|
|
60
|
+
user: req.user,
|
|
61
|
+
existing: req.existing,
|
|
62
|
+
})
|
|
63
|
+
}),
|
|
64
|
+
utils.adjust,
|
|
65
|
+
utils.returnJSON,
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (concordances.delete) {
|
|
70
|
+
router.delete(
|
|
71
|
+
"/:_id",
|
|
72
|
+
useAuth(concordances.delete.auth),
|
|
73
|
+
utils.bodyParser,
|
|
74
|
+
wrapAsync(async (req) => {
|
|
75
|
+
return await service.deleteItem({
|
|
76
|
+
_id: req.params._id,
|
|
77
|
+
user: req.user,
|
|
78
|
+
existing: req.existing,
|
|
79
|
+
})
|
|
80
|
+
}),
|
|
81
|
+
(req, res) => res.sendStatus(204),
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return router
|
|
86
|
+
}
|