@trojs/openapi-server 2.1.1 → 3.0.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/package.json +24 -18
- package/src/api.js +27 -26
- package/src/error-status.js +6 -6
- package/src/express-callback.js +57 -57
- package/src/handlers/not-found.js +6 -6
- package/src/handlers/request-validation.js +7 -7
- package/src/handlers/response-validation.js +24 -24
- package/src/handlers/unauthorized.js +6 -6
- package/src/openapi.js +5 -5
- package/src/operation-ids.js +2 -2
- package/src/params.js +16 -16
- package/src/router.js +26 -26
- package/src/server.js +39 -37
- package/src/types.js +3 -3
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trojs/openapi-server",
|
|
3
3
|
"description": "OpenAPI Server",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Pieter Wigboldus",
|
|
7
7
|
"url": "https://trojs.org/"
|
|
8
8
|
},
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"scripts": {
|
|
11
|
-
"lint": "eslint
|
|
12
|
-
"lint:report": "eslint src/*.js
|
|
13
|
-
"lint:fix": "eslint
|
|
11
|
+
"lint": "eslint",
|
|
12
|
+
"lint:report": "eslint src/*.js -f json -o report.json",
|
|
13
|
+
"lint:fix": "eslint --fix",
|
|
14
14
|
"test": "c8 node --test src/*.test.js",
|
|
15
15
|
"cpd": "node_modules/jscpd/bin/jscpd src",
|
|
16
16
|
"vulnerabilities": "npm audit --omit=dev"
|
|
@@ -33,22 +33,21 @@
|
|
|
33
33
|
],
|
|
34
34
|
"main": "src/server.js",
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@
|
|
37
|
-
"@
|
|
36
|
+
"@eslint/js": "^9.15.0",
|
|
37
|
+
"@stylistic/eslint-plugin": "^2.11.0",
|
|
38
|
+
"@stylistic/eslint-plugin-js": "^2.11.0",
|
|
38
39
|
"@types/express-serve-static-core": "^4.17.41",
|
|
39
40
|
"@types/node": "^22.0.0",
|
|
40
41
|
"c8": "^10.0.0",
|
|
41
|
-
"eslint": "^
|
|
42
|
-
"eslint-config-airbnb-base": "^15.0.0",
|
|
42
|
+
"eslint": "^9.15.0",
|
|
43
43
|
"eslint-config-prettier": "^9.1.0",
|
|
44
|
-
"eslint-plugin-
|
|
45
|
-
"eslint-plugin-
|
|
46
|
-
"eslint-plugin-
|
|
47
|
-
"eslint-plugin-
|
|
48
|
-
"eslint-plugin-
|
|
49
|
-
"eslint-plugin-
|
|
50
|
-
"
|
|
51
|
-
"eslint-plugin-sonarjs": "^0.25.1",
|
|
44
|
+
"eslint-plugin-import": "^2.31.0",
|
|
45
|
+
"eslint-plugin-jsdoc": "^50.5.0",
|
|
46
|
+
"eslint-plugin-n": "^17.14.0",
|
|
47
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
48
|
+
"eslint-plugin-promise": "^7.1.0",
|
|
49
|
+
"eslint-plugin-sonarjs": "^2.0.4",
|
|
50
|
+
"globals": "^15.12.0",
|
|
52
51
|
"jscpd": "^4.0.0",
|
|
53
52
|
"prettier": "^3.3.3",
|
|
54
53
|
"supertest": "^7.0.0"
|
|
@@ -58,7 +57,7 @@
|
|
|
58
57
|
"url": "https://github.com/trojs/openapi-server"
|
|
59
58
|
},
|
|
60
59
|
"engines": {
|
|
61
|
-
"node": ">=
|
|
60
|
+
"node": ">= 20 < 21 || >= 22 < 23"
|
|
62
61
|
},
|
|
63
62
|
"keywords": [
|
|
64
63
|
"openapi",
|
|
@@ -83,6 +82,13 @@
|
|
|
83
82
|
"overrides": {
|
|
84
83
|
"semver": "^7.5.3",
|
|
85
84
|
"send@<=0.19.0": "^0.19.0",
|
|
86
|
-
"cookie@<=0.7.0": "0.7.0"
|
|
85
|
+
"cookie@<=0.7.0": "0.7.0",
|
|
86
|
+
"eslint-plugin-sonarjs": {
|
|
87
|
+
"eslint": "^9.15.0",
|
|
88
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
89
|
+
"@typescript-eslint/utils": "^8.0.0",
|
|
90
|
+
"eslint-plugin-import": "^2.31.0",
|
|
91
|
+
"eslint-plugin-react-hooks": "^5.0.0"
|
|
92
|
+
}
|
|
87
93
|
}
|
|
88
94
|
}
|
package/src/api.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import express from 'express'
|
|
2
|
-
import swaggerUi from 'swagger-ui-express'
|
|
3
|
-
import { setupRouter } from './router.js'
|
|
1
|
+
import express from 'express'
|
|
2
|
+
import swaggerUi from 'swagger-ui-express'
|
|
3
|
+
import { setupRouter } from './router.js'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @typedef {import('openapi-backend').Handler} Handler
|
|
@@ -29,13 +29,14 @@ import { setupRouter } from './router.js';
|
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* Setup the server for a specific API, so every server can run multiple instances of the API,
|
|
32
|
+
* Setup the server for a specific API, so every server can run multiple instances of the API,
|
|
33
|
+
* like different versions, for e.g. different clients
|
|
33
34
|
*/
|
|
34
35
|
|
|
35
36
|
export class Api {
|
|
36
37
|
/**
|
|
37
38
|
* Create a new instance of the API
|
|
38
|
-
* @
|
|
39
|
+
* @class
|
|
39
40
|
* @param {ApiSchema} params
|
|
40
41
|
*/
|
|
41
42
|
constructor({
|
|
@@ -50,36 +51,36 @@ export class Api {
|
|
|
50
51
|
securityHandlers,
|
|
51
52
|
swagger,
|
|
52
53
|
apiDocs,
|
|
53
|
-
ajvOptions
|
|
54
|
+
ajvOptions
|
|
54
55
|
}) {
|
|
55
|
-
this.version = version
|
|
56
|
-
this.specification = specification
|
|
57
|
-
this.controllers = controllers
|
|
58
|
-
this.apiRoot = apiRoot
|
|
59
|
-
this.strictSpecification = strictSpecification
|
|
60
|
-
this.errorDetails = errorDetails || false
|
|
61
|
-
this.logger = logger || console
|
|
62
|
-
this.meta = meta || {}
|
|
63
|
-
this.securityHandlers = securityHandlers || []
|
|
64
|
-
this.swagger = swagger ?? true
|
|
65
|
-
this.apiDocs = apiDocs ?? true
|
|
66
|
-
this.ajvOptions = ajvOptions ?? { allErrors: false }
|
|
56
|
+
this.version = version
|
|
57
|
+
this.specification = specification
|
|
58
|
+
this.controllers = controllers
|
|
59
|
+
this.apiRoot = apiRoot
|
|
60
|
+
this.strictSpecification = strictSpecification
|
|
61
|
+
this.errorDetails = errorDetails || false
|
|
62
|
+
this.logger = logger || console
|
|
63
|
+
this.meta = meta || {}
|
|
64
|
+
this.securityHandlers = securityHandlers || []
|
|
65
|
+
this.swagger = swagger ?? true
|
|
66
|
+
this.apiDocs = apiDocs ?? true
|
|
67
|
+
this.ajvOptions = ajvOptions ?? { allErrors: false }
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
setup() {
|
|
70
|
-
const router = express.Router()
|
|
71
|
+
const router = express.Router()
|
|
71
72
|
|
|
72
73
|
if (this.swagger) {
|
|
73
74
|
router.use(
|
|
74
75
|
'/swagger',
|
|
75
76
|
swaggerUi.serveFiles(this.specification, {}),
|
|
76
77
|
swaggerUi.setup(this.specification)
|
|
77
|
-
)
|
|
78
|
+
)
|
|
78
79
|
}
|
|
79
80
|
if (this.apiDocs) {
|
|
80
81
|
router.get('/api-docs', (_request, response) =>
|
|
81
82
|
response.json(this.specification)
|
|
82
|
-
)
|
|
83
|
+
)
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
const { api } = setupRouter({
|
|
@@ -91,14 +92,14 @@ export class Api {
|
|
|
91
92
|
logger: this.logger,
|
|
92
93
|
meta: this.meta,
|
|
93
94
|
securityHandlers: this.securityHandlers,
|
|
94
|
-
ajvOptions: this.ajvOptions
|
|
95
|
-
})
|
|
96
|
-
api.init()
|
|
95
|
+
ajvOptions: this.ajvOptions
|
|
96
|
+
})
|
|
97
|
+
api.init()
|
|
97
98
|
|
|
98
99
|
router.use((request, response) =>
|
|
99
100
|
api.handleRequest(request, request, response)
|
|
100
|
-
)
|
|
101
|
+
)
|
|
101
102
|
|
|
102
|
-
return router
|
|
103
|
+
return router
|
|
103
104
|
}
|
|
104
105
|
}
|
package/src/error-status.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
const errorCodesStatus = [
|
|
2
2
|
{
|
|
3
3
|
type: TypeError,
|
|
4
|
-
status: 422
|
|
4
|
+
status: 422
|
|
5
5
|
},
|
|
6
6
|
{
|
|
7
7
|
type: RangeError,
|
|
8
|
-
status: 404
|
|
8
|
+
status: 404
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
type: Error,
|
|
12
|
-
status: 500
|
|
13
|
-
}
|
|
14
|
-
]
|
|
12
|
+
status: 500
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Get a http status when you send an error.
|
|
@@ -21,4 +21,4 @@ const errorCodesStatus = [
|
|
|
21
21
|
*/
|
|
22
22
|
export default (error) =>
|
|
23
23
|
errorCodesStatus.find((errorCode) => error instanceof errorCode.type)
|
|
24
|
-
.status
|
|
24
|
+
.status
|
package/src/express-callback.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import getStatusByError from './error-status.js'
|
|
2
|
-
import { parseParams } from './params.js'
|
|
1
|
+
import getStatusByError from './error-status.js'
|
|
2
|
+
import { parseParams } from './params.js'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @typedef {import('express-serve-static-core').Request} Request
|
|
@@ -29,68 +29,68 @@ export const makeExpressCallback =
|
|
|
29
29
|
* @param {Response} response
|
|
30
30
|
* @returns {Promise<any>}
|
|
31
31
|
*/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
32
|
+
async (context, request, response) => {
|
|
33
|
+
try {
|
|
34
|
+
const allParameters = {
|
|
35
|
+
...(context.request?.params || {}),
|
|
36
|
+
...(context.request?.query || {})
|
|
37
|
+
}
|
|
38
|
+
const parameters = parseParams({
|
|
39
|
+
query: allParameters,
|
|
40
|
+
spec: context.operation.parameters,
|
|
41
|
+
mock
|
|
42
|
+
})
|
|
43
|
+
const url = `${request.protocol}://${request.get('Host')}${request.originalUrl}`
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
45
|
+
const responseBody = await controller({
|
|
46
|
+
context,
|
|
47
|
+
request,
|
|
48
|
+
response,
|
|
49
|
+
parameters,
|
|
50
|
+
specification,
|
|
51
|
+
post: request.body,
|
|
52
|
+
url,
|
|
53
|
+
logger,
|
|
54
|
+
meta
|
|
55
|
+
})
|
|
56
|
+
logger.debug({
|
|
57
|
+
url,
|
|
58
|
+
parameters,
|
|
59
|
+
post: request.body,
|
|
60
|
+
response: responseBody
|
|
61
|
+
})
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
return responseBody
|
|
64
|
+
} catch (error) {
|
|
65
|
+
const errorCodeStatus = getStatusByError(error)
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
if (errorCodeStatus >= 500) {
|
|
68
|
+
logger.error(error)
|
|
69
|
+
} else {
|
|
70
|
+
logger.warn(error)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
response.status(errorCodeStatus)
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
if (errorDetails) {
|
|
76
|
+
return {
|
|
77
|
+
errors: [
|
|
78
|
+
{
|
|
79
|
+
message: error.message,
|
|
80
|
+
value: error.valueOf(),
|
|
81
|
+
type: error.constructor.name
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
status: errorCodeStatus,
|
|
85
|
+
timestamp: new Date(),
|
|
86
|
+
message: error.message
|
|
87
|
+
}
|
|
88
|
+
}
|
|
74
89
|
|
|
75
|
-
if (errorDetails) {
|
|
76
90
|
return {
|
|
77
|
-
errors: [
|
|
78
|
-
{
|
|
79
|
-
message: error.message,
|
|
80
|
-
value: error.valueOf(),
|
|
81
|
-
type: error.constructor.name,
|
|
82
|
-
},
|
|
83
|
-
],
|
|
84
91
|
status: errorCodeStatus,
|
|
85
92
|
timestamp: new Date(),
|
|
86
|
-
message: error.message
|
|
87
|
-
}
|
|
93
|
+
message: error.message
|
|
94
|
+
}
|
|
88
95
|
}
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
status: errorCodeStatus,
|
|
92
|
-
timestamp: new Date(),
|
|
93
|
-
message: error.message,
|
|
94
|
-
};
|
|
95
96
|
}
|
|
96
|
-
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export const notFound = (_context, request, response) => {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
response.status(404)
|
|
3
|
+
return {
|
|
4
|
+
status: 404,
|
|
5
|
+
timestamp: new Date(),
|
|
6
|
+
message: 'Not found'
|
|
7
|
+
}
|
|
8
8
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export const requestValidation = (context, request, response) => {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
response.status(400)
|
|
3
|
+
return {
|
|
4
|
+
errors: context.validation.errors,
|
|
5
|
+
status: 400,
|
|
6
|
+
timestamp: new Date(),
|
|
7
|
+
message: 'Bad Request'
|
|
8
|
+
}
|
|
9
9
|
}
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
export const responseValidation = (context, request, response) => {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
const responseDoesntNeedValidation = response.statusCode >= 400
|
|
3
|
+
if (responseDoesntNeedValidation) {
|
|
4
|
+
return response.json(context.response)
|
|
5
|
+
}
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
7
|
+
const valid = context.api.validateResponse(
|
|
8
|
+
context.response,
|
|
9
|
+
context.operation
|
|
10
|
+
)
|
|
11
|
+
if (valid?.errors) {
|
|
12
|
+
return response.status(502).json({
|
|
13
|
+
errors: valid.errors,
|
|
14
|
+
status: 502,
|
|
15
|
+
timestamp: new Date(),
|
|
16
|
+
message: 'Bad response'
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
if (!context.response) {
|
|
21
|
+
return response.end()
|
|
22
|
+
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
const contentType = request?.headers?.accept ?? 'application/json'
|
|
25
|
+
if (contentType === 'application/json') {
|
|
26
|
+
return response.json(context.response)
|
|
27
|
+
}
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
return response.send(context.response)
|
|
30
30
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export const unauthorized = async (context, request, response) => {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
response.status(401)
|
|
3
|
+
return {
|
|
4
|
+
status: 401,
|
|
5
|
+
timestamp: new Date(),
|
|
6
|
+
message: 'Unauthorized'
|
|
7
|
+
}
|
|
8
8
|
}
|
package/src/openapi.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises'
|
|
1
|
+
import { readFile } from 'node:fs/promises'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Get the OpenAPI specification from the file.
|
|
@@ -9,7 +9,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
9
9
|
* @returns {Promise<{ openAPISpecification: object; }>}
|
|
10
10
|
*/
|
|
11
11
|
export const openAPI = async ({ file, base = import.meta.url }) => {
|
|
12
|
-
const fileUrl = new URL(file, base)
|
|
13
|
-
const openAPISpecification = JSON.parse(await readFile(fileUrl, 'utf8'))
|
|
14
|
-
return { openAPISpecification }
|
|
15
|
-
}
|
|
12
|
+
const fileUrl = new URL(file, base)
|
|
13
|
+
const openAPISpecification = JSON.parse(await readFile(fileUrl, 'utf8'))
|
|
14
|
+
return { openAPISpecification }
|
|
15
|
+
}
|
package/src/operation-ids.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const operations = ['get', 'put', 'patch', 'post', 'delete']
|
|
1
|
+
const operations = ['get', 'put', 'patch', 'post', 'delete']
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Get all operation ID's from the specification.
|
|
@@ -13,4 +13,4 @@ export const operationIds = ({ specification }) =>
|
|
|
13
13
|
operations.includes(operation) ? data.operationId : null
|
|
14
14
|
)
|
|
15
15
|
)
|
|
16
|
-
.flat()
|
|
16
|
+
.flat()
|
package/src/params.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { types } from './types.js'
|
|
1
|
+
import { types } from './types.js'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Parse params to the types defined in the spec
|
|
@@ -11,39 +11,39 @@ import { types } from './types.js';
|
|
|
11
11
|
export const parseParams = ({ query, spec, mock = false }) =>
|
|
12
12
|
spec
|
|
13
13
|
.map((parameter) => {
|
|
14
|
-
const { name, schema } = parameter
|
|
14
|
+
const { name, schema } = parameter
|
|
15
15
|
const {
|
|
16
16
|
type,
|
|
17
17
|
default: defaultValue,
|
|
18
|
-
example: exampleValue
|
|
19
|
-
} = schema
|
|
20
|
-
const Type = types[type]
|
|
21
|
-
const paramName = query?.[name]
|
|
18
|
+
example: exampleValue
|
|
19
|
+
} = schema
|
|
20
|
+
const Type = types[type]
|
|
21
|
+
const paramName = query?.[name]
|
|
22
22
|
|
|
23
23
|
if (!paramName && defaultValue !== undefined) {
|
|
24
|
-
return { name, value: defaultValue }
|
|
24
|
+
return { name, value: defaultValue }
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
if (!paramName && mock && exampleValue !== undefined) {
|
|
28
|
-
return { name, value: exampleValue }
|
|
28
|
+
return { name, value: exampleValue }
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
if (!paramName) {
|
|
32
|
-
return undefined
|
|
32
|
+
return undefined
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
if (Type === Boolean) {
|
|
36
36
|
return {
|
|
37
37
|
name,
|
|
38
|
-
value: JSON.parse(paramName.toLowerCase())
|
|
39
|
-
}
|
|
38
|
+
value: JSON.parse(paramName.toLowerCase())
|
|
39
|
+
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
const value = new Type(paramName).valueOf()
|
|
43
|
-
return { name, value }
|
|
42
|
+
const value = new Type(paramName).valueOf()
|
|
43
|
+
return { name, value }
|
|
44
44
|
})
|
|
45
45
|
.filter(Boolean)
|
|
46
46
|
.reduce((acc, { name, value }) => {
|
|
47
|
-
acc[name] = value
|
|
48
|
-
return acc
|
|
49
|
-
}, {})
|
|
47
|
+
acc[name] = value
|
|
48
|
+
return acc
|
|
49
|
+
}, {})
|
package/src/router.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { OpenAPIBackend } from 'openapi-backend'
|
|
2
|
-
import addFormats from 'ajv-formats'
|
|
3
|
-
import { makeExpressCallback } from './express-callback.js'
|
|
4
|
-
import { operationIds } from './operation-ids.js'
|
|
5
|
-
import { notFound } from './handlers/not-found.js'
|
|
6
|
-
import { requestValidation } from './handlers/request-validation.js'
|
|
7
|
-
import { responseValidation } from './handlers/response-validation.js'
|
|
8
|
-
import { unauthorized } from './handlers/unauthorized.js'
|
|
1
|
+
import { OpenAPIBackend } from 'openapi-backend'
|
|
2
|
+
import addFormats from 'ajv-formats'
|
|
3
|
+
import { makeExpressCallback } from './express-callback.js'
|
|
4
|
+
import { operationIds } from './operation-ids.js'
|
|
5
|
+
import { notFound } from './handlers/not-found.js'
|
|
6
|
+
import { requestValidation } from './handlers/request-validation.js'
|
|
7
|
+
import { responseValidation } from './handlers/response-validation.js'
|
|
8
|
+
import { unauthorized } from './handlers/unauthorized.js'
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* @typedef {import('./api.js').Logger} Logger
|
|
@@ -38,7 +38,7 @@ export const setupRouter = ({
|
|
|
38
38
|
meta,
|
|
39
39
|
securityHandlers = [],
|
|
40
40
|
ajvOptions = {},
|
|
41
|
-
mock
|
|
41
|
+
mock
|
|
42
42
|
}) => {
|
|
43
43
|
const api = new OpenAPIBackend({
|
|
44
44
|
definition: openAPISpecification,
|
|
@@ -46,22 +46,22 @@ export const setupRouter = ({
|
|
|
46
46
|
strict: strictSpecification,
|
|
47
47
|
ajvOpts: ajvOptions,
|
|
48
48
|
customizeAjv: (originalAjv) => {
|
|
49
|
-
addFormats(originalAjv)
|
|
50
|
-
return originalAjv
|
|
51
|
-
}
|
|
52
|
-
})
|
|
49
|
+
addFormats(originalAjv)
|
|
50
|
+
return originalAjv
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
53
|
|
|
54
54
|
api.register({
|
|
55
55
|
unauthorizedHandler: unauthorized,
|
|
56
56
|
validationFail: requestValidation,
|
|
57
57
|
notFound,
|
|
58
|
-
postResponseHandler: responseValidation
|
|
59
|
-
})
|
|
58
|
+
postResponseHandler: responseValidation
|
|
59
|
+
})
|
|
60
60
|
|
|
61
61
|
operationIds({ specification: openAPISpecification }).forEach(
|
|
62
62
|
(operationId) => {
|
|
63
63
|
if (!Object.hasOwn(controllers, operationId)) {
|
|
64
|
-
return
|
|
64
|
+
return
|
|
65
65
|
}
|
|
66
66
|
api.register(
|
|
67
67
|
operationId,
|
|
@@ -71,24 +71,24 @@ export const setupRouter = ({
|
|
|
71
71
|
errorDetails,
|
|
72
72
|
logger,
|
|
73
73
|
meta,
|
|
74
|
-
mock
|
|
74
|
+
mock
|
|
75
75
|
})
|
|
76
|
-
)
|
|
76
|
+
)
|
|
77
77
|
}
|
|
78
|
-
)
|
|
78
|
+
)
|
|
79
79
|
|
|
80
80
|
api.register('notImplemented', (context) => {
|
|
81
81
|
const { mock: mockImplementation } =
|
|
82
|
-
context.api.mockResponseForOperation(context.operation.operationId)
|
|
83
|
-
return mockImplementation
|
|
84
|
-
})
|
|
82
|
+
context.api.mockResponseForOperation(context.operation.operationId)
|
|
83
|
+
return mockImplementation
|
|
84
|
+
})
|
|
85
85
|
|
|
86
86
|
securityHandlers.forEach((securityHandler) => {
|
|
87
87
|
api.registerSecurityHandler(
|
|
88
88
|
securityHandler.name,
|
|
89
89
|
securityHandler.handler
|
|
90
|
-
)
|
|
91
|
-
})
|
|
90
|
+
)
|
|
91
|
+
})
|
|
92
92
|
|
|
93
|
-
return { api, openAPISpecification }
|
|
94
|
-
}
|
|
93
|
+
return { api, openAPISpecification }
|
|
94
|
+
}
|
package/src/server.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import express from 'express'
|
|
2
|
-
import cors from 'cors'
|
|
3
|
-
import compression from 'compression'
|
|
4
|
-
import helmet from 'helmet'
|
|
5
|
-
import * as Sentry from '@sentry/node'
|
|
6
|
-
import bodyParser from 'body-parser'
|
|
7
|
-
import { openAPI } from './openapi.js'
|
|
8
|
-
import { Api } from './api.js'
|
|
1
|
+
import express from 'express'
|
|
2
|
+
import cors from 'cors'
|
|
3
|
+
import compression from 'compression'
|
|
4
|
+
import helmet from 'helmet'
|
|
5
|
+
import * as Sentry from '@sentry/node'
|
|
6
|
+
import bodyParser from 'body-parser'
|
|
7
|
+
import { openAPI } from './openapi.js'
|
|
8
|
+
import { Api } from './api.js'
|
|
9
|
+
|
|
10
|
+
/* eslint-disable sonarjs/cors */
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Get the origin resource policy
|
|
@@ -17,10 +19,10 @@ const getOriginResourcePolicy = (origin) => ({
|
|
|
17
19
|
policy: origin === '*' ? 'cross-origin' : 'same-origin',
|
|
18
20
|
directives: {
|
|
19
21
|
// ...
|
|
20
|
-
'require-trusted-types-for': ["'script'"]
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
})
|
|
22
|
+
'require-trusted-types-for': ["'script'"]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
})
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
28
|
* @typedef {import('express-serve-static-core').Request} Request
|
|
@@ -77,13 +79,13 @@ export const setupServer = async ({
|
|
|
77
79
|
poweredBy = 'TroJS',
|
|
78
80
|
version = '1.0.0',
|
|
79
81
|
middleware = [],
|
|
80
|
-
maximumBodySize = undefined
|
|
82
|
+
maximumBodySize = undefined
|
|
81
83
|
}) => {
|
|
82
84
|
const corsOptions = {
|
|
83
|
-
origin
|
|
84
|
-
}
|
|
85
|
+
origin
|
|
86
|
+
}
|
|
85
87
|
|
|
86
|
-
const app = express()
|
|
88
|
+
const app = express()
|
|
87
89
|
|
|
88
90
|
if (sentry) {
|
|
89
91
|
Sentry.init({
|
|
@@ -91,37 +93,37 @@ export const setupServer = async ({
|
|
|
91
93
|
tracesSampleRate: sentry.tracesSampleRate || 1.0,
|
|
92
94
|
profilesSampleRate: sentry.profilesSampleRate || 1.0,
|
|
93
95
|
integrations: sentry.integrations || [],
|
|
94
|
-
release: sentry.release
|
|
95
|
-
})
|
|
96
|
+
release: sentry.release
|
|
97
|
+
})
|
|
96
98
|
}
|
|
97
99
|
|
|
98
|
-
app.use(cors(corsOptions))
|
|
99
|
-
app.use(compression())
|
|
100
|
-
app.use(helmet(getOriginResourcePolicy(origin)))
|
|
101
|
-
app.use(express.json({ limit: maximumBodySize }))
|
|
102
|
-
middleware.forEach((fn) => app.use(fn))
|
|
103
|
-
app.use(bodyParser.urlencoded({ extended: false, limit: maximumBodySize }))
|
|
100
|
+
app.use(cors(corsOptions))
|
|
101
|
+
app.use(compression())
|
|
102
|
+
app.use(helmet(getOriginResourcePolicy(origin)))
|
|
103
|
+
app.use(express.json({ limit: maximumBodySize }))
|
|
104
|
+
middleware.forEach((fn) => app.use(fn))
|
|
105
|
+
app.use(bodyParser.urlencoded({ extended: false, limit: maximumBodySize }))
|
|
104
106
|
app.use((_request, response, next) => {
|
|
105
|
-
response.setHeader('X-Powered-By', poweredBy)
|
|
106
|
-
response.setHeader('X-Version', version)
|
|
107
|
-
next()
|
|
108
|
-
})
|
|
107
|
+
response.setHeader('X-Powered-By', poweredBy)
|
|
108
|
+
response.setHeader('X-Version', version)
|
|
109
|
+
next()
|
|
110
|
+
})
|
|
109
111
|
|
|
110
112
|
if (staticFolder) {
|
|
111
|
-
app.use(express.static(staticFolder))
|
|
113
|
+
app.use(express.static(staticFolder))
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
apis.forEach((api) => {
|
|
115
|
-
const apiRoutes = new Api(api)
|
|
116
|
-
const routes = apiRoutes.setup()
|
|
117
|
-
app.use(`/${api.version}`, routes)
|
|
118
|
-
})
|
|
117
|
+
const apiRoutes = new Api(api)
|
|
118
|
+
const routes = apiRoutes.setup()
|
|
119
|
+
app.use(`/${api.version}`, routes)
|
|
120
|
+
})
|
|
119
121
|
|
|
120
122
|
if (sentry) {
|
|
121
|
-
Sentry.setupExpressErrorHandler(app)
|
|
123
|
+
Sentry.setupExpressErrorHandler(app)
|
|
122
124
|
}
|
|
123
125
|
|
|
124
|
-
return { app }
|
|
125
|
-
}
|
|
126
|
+
return { app }
|
|
127
|
+
}
|
|
126
128
|
|
|
127
|
-
export { openAPI, Api }
|
|
129
|
+
export { openAPI, Api }
|
package/src/types.js
CHANGED