adapt-authoring-server 2.2.3 → 2.2.5

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.
@@ -1,32 +1,16 @@
1
1
  name: Release
2
+
2
3
  on:
3
4
  push:
4
5
  branches:
5
6
  - master
6
7
 
8
+ permissions:
9
+ contents: write
10
+ issues: write
11
+ pull-requests: write
12
+ id-token: write
13
+
7
14
  jobs:
8
15
  release:
9
- name: Release
10
- runs-on: ubuntu-latest
11
- permissions:
12
- contents: write # to be able to publish a GitHub release
13
- issues: write # to be able to comment on released issues
14
- pull-requests: write # to be able to comment on released pull requests
15
- id-token: write # to enable use of OIDC for trusted publishing and npm provenance
16
- steps:
17
- - name: Checkout
18
- uses: actions/checkout@v3
19
- with:
20
- fetch-depth: 0
21
- - name: Setup Node.js
22
- uses: actions/setup-node@v3
23
- with:
24
- node-version: 'lts/*'
25
- - name: Update npm
26
- run: npm install -g npm@latest
27
- - name: Install dependencies
28
- run: npm install
29
- - name: Release
30
- env:
31
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32
- run: npx semantic-release
16
+ uses: adaptlearning/semantic-release-config/.github/workflows/release.yml@master
@@ -86,6 +86,7 @@ class ServerUtils {
86
86
  const e = App.instance.errors[error.code]
87
87
  if (e) {
88
88
  if (error.statusCode) e.statusCode = error.statusCode
89
+ if (error.data) e.setData(error.data)
89
90
  e.error = error.message
90
91
  error = e
91
92
  } else {
@@ -66,7 +66,7 @@ export async function loadRouteConfig (rootDir, target, options = {}) {
66
66
  ? resolveHandlers(config.routes, target, aliases)
67
67
  : []
68
68
 
69
- // Prepend default routes from template if provided (unless useDefaultRoutes is false)
69
+ // Prepend default routes from template if provided
70
70
  if (options.defaults && config.useDefaultRoutes !== false) {
71
71
  const template = await readJson(options.defaults)
72
72
  const defaultRoutes = resolveHandlers(template.routes || [], target, aliases)
@@ -83,7 +83,8 @@ export async function loadRouteConfig (rootDir, target, options = {}) {
83
83
  return { ...d, ...rest, handlers: { ...d.handlers, ...rest.handlers } }
84
84
  })
85
85
  const remaining = customRoutes.filter(r => !r.override || !matched.has(r.route))
86
- config.routes = [...mergedDefaults, ...remaining]
86
+ // Custom routes must come before defaults so they take priority in Express's first-match routing
87
+ config.routes = [...remaining, ...mergedDefaults]
87
88
  } else {
88
89
  config.routes = customRoutes
89
90
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adapt-authoring-server",
3
- "version": "2.2.3",
3
+ "version": "2.2.5",
4
4
  "description": "Provides an Express application routing and more",
5
5
  "homepage": "https://github.com/adapt-security/adapt-authoring-server",
6
6
  "license": "GPL-3.0",
@@ -17,37 +17,11 @@
17
17
  "lodash": "^4.17.21"
18
18
  },
19
19
  "devDependencies": {
20
- "@semantic-release/git": "^10.0.1",
20
+ "@adaptlearning/semantic-release-config": "^1.0.0",
21
21
  "adapt-schemas": "^1.1.0",
22
- "conventional-changelog-eslint": "^6.0.0",
23
- "semantic-release": "^25.0.2",
24
22
  "standard": "^17.1.0"
25
23
  },
26
24
  "release": {
27
- "plugins": [
28
- [
29
- "@semantic-release/commit-analyzer",
30
- {
31
- "preset": "eslint"
32
- }
33
- ],
34
- [
35
- "@semantic-release/release-notes-generator",
36
- {
37
- "preset": "eslint"
38
- }
39
- ],
40
- "@semantic-release/npm",
41
- "@semantic-release/github",
42
- [
43
- "@semantic-release/git",
44
- {
45
- "assets": [
46
- "package.json"
47
- ],
48
- "message": "Chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
49
- }
50
- ]
51
- ]
25
+ "extends": "@adaptlearning/semantic-release-config"
52
26
  }
53
27
  }
@@ -17,6 +17,18 @@ describe('ServerUtils', () => {
17
17
  })
18
18
 
19
19
  describe('#addErrorHandler()', () => {
20
+ /** Set up a res with addErrorHandler + status/json capture */
21
+ function createSendErrorSetup (reqOverrides = {}) {
22
+ const req = { translate: (error) => error.message, ...reqOverrides }
23
+ const res = {}
24
+ ServerUtils.addErrorHandler(req, res, () => {})
25
+ let statusCode
26
+ let jsonData
27
+ res.status = (code) => { statusCode = code; return res }
28
+ res.json = (data) => { jsonData = data }
29
+ return { req, res, getStatus: () => statusCode, getJson: () => jsonData }
30
+ }
31
+
20
32
  it('should add sendError method to response object', () => {
21
33
  const req = {}
22
34
  const res = {}
@@ -30,28 +42,18 @@ describe('ServerUtils', () => {
30
42
  })
31
43
 
32
44
  it('sendError should send AdaptError as JSON with status code', () => {
33
- const req = { translate: (error) => error.message }
34
- const res = {}
35
- const next = () => {}
36
-
37
- ServerUtils.addErrorHandler(req, res, next)
38
-
39
- let statusCode
40
- let jsonData
41
- res.status = (code) => { statusCode = code; return res }
42
- res.json = (data) => { jsonData = data }
45
+ const { res, getStatus, getJson } = createSendErrorSetup()
43
46
 
44
- const adaptError = {
47
+ res.sendError({
45
48
  constructor: { name: 'AdaptError' },
46
49
  statusCode: 404,
47
50
  code: 'NOT_FOUND',
48
51
  message: 'Not found'
49
- }
50
- res.sendError(adaptError)
52
+ })
51
53
 
52
- assert.equal(statusCode, 404)
53
- assert.equal(jsonData.code, 'NOT_FOUND')
54
- assert.equal(jsonData.message, 'Not found')
54
+ assert.equal(getStatus(), 404)
55
+ assert.equal(getJson().code, 'NOT_FOUND')
56
+ assert.equal(getJson().message, 'Not found')
55
57
  })
56
58
 
57
59
  it('sendError should fall back to SERVER_ERROR for unknown errors', () => {
@@ -63,21 +65,12 @@ describe('ServerUtils', () => {
63
65
  message: 'Internal server error'
64
66
  }
65
67
 
66
- const req = { translate: (error) => error.message }
67
- const res = {}
68
- const next = () => {}
69
-
70
- ServerUtils.addErrorHandler(req, res, next)
71
-
72
- let statusCode
73
- let jsonData
74
- res.status = (code) => { statusCode = code; return res }
75
- res.json = (data) => { jsonData = data }
68
+ const { res, getStatus, getJson } = createSendErrorSetup()
76
69
 
77
70
  res.sendError(new Error('something broke'))
78
71
 
79
- assert.equal(statusCode, 500)
80
- assert.equal(jsonData.code, 'SERVER_ERROR')
72
+ assert.equal(getStatus(), 500)
73
+ assert.equal(getJson().code, 'SERVER_ERROR')
81
74
  })
82
75
 
83
76
  it('sendError should look up known error codes on non-AdaptError', () => {
@@ -88,68 +81,60 @@ describe('ServerUtils', () => {
88
81
  message: 'Custom error'
89
82
  }
90
83
 
91
- const req = { translate: (error) => error.message }
92
- const res = {}
93
- const next = () => {}
94
-
95
- ServerUtils.addErrorHandler(req, res, next)
96
-
97
- let statusCode
98
- let jsonData
99
- res.status = (code) => { statusCode = code; return res }
100
- res.json = (data) => { jsonData = data }
84
+ const { res, getStatus, getJson } = createSendErrorSetup()
101
85
 
102
86
  const error = new Error('details')
103
87
  error.code = 'CUSTOM_CODE'
104
88
  res.sendError(error)
105
89
 
106
- assert.equal(statusCode, 422)
107
- assert.equal(jsonData.code, 'CUSTOM_CODE')
90
+ assert.equal(getStatus(), 422)
91
+ assert.equal(getJson().code, 'CUSTOM_CODE')
108
92
  })
109
93
 
110
- it('sendError should include data field in response', () => {
111
- const req = { translate: (error) => error.message }
112
- const res = {}
113
- const next = () => {}
94
+ it('sendError should copy data from non-AdaptError to looked-up error', () => {
95
+ App.instance.errors = App.instance.errors || {}
96
+ App.instance.errors.VALIDATION_FAILED = {
97
+ statusCode: 400,
98
+ code: 'VALIDATION_FAILED',
99
+ message: 'Validation failed',
100
+ setData (data) { this.data = data; return this }
101
+ }
114
102
 
115
- ServerUtils.addErrorHandler(req, res, next)
103
+ const { res, getJson } = createSendErrorSetup()
116
104
 
117
- let jsonData
118
- res.status = () => res
119
- res.json = (data) => { jsonData = data }
105
+ const error = new Error('Validation failed')
106
+ error.code = 'VALIDATION_FAILED'
107
+ error.data = { errors: '/title is required', schemaName: 'config' }
108
+ res.sendError(error)
120
109
 
121
- const adaptError = {
110
+ assert.deepEqual(getJson().data, { errors: '/title is required', schemaName: 'config' })
111
+ })
112
+
113
+ it('sendError should include data field in response', () => {
114
+ const { res, getJson } = createSendErrorSetup()
115
+
116
+ res.sendError({
122
117
  constructor: { name: 'AdaptError' },
123
118
  statusCode: 400,
124
119
  code: 'BAD_REQUEST',
125
120
  message: 'Bad request',
126
121
  data: { field: 'email' }
127
- }
128
- res.sendError(adaptError)
122
+ })
129
123
 
130
- assert.deepEqual(jsonData.data, { field: 'email' })
124
+ assert.deepEqual(getJson().data, { field: 'email' })
131
125
  })
132
126
 
133
127
  it('sendError should use error.message when req.translate is not available', () => {
134
- const req = {}
135
- const res = {}
136
- const next = () => {}
128
+ const { res, getJson } = createSendErrorSetup({ translate: undefined })
137
129
 
138
- ServerUtils.addErrorHandler(req, res, next)
139
-
140
- let jsonData
141
- res.status = () => res
142
- res.json = (data) => { jsonData = data }
143
-
144
- const adaptError = {
130
+ res.sendError({
145
131
  constructor: { name: 'AdaptError' },
146
132
  statusCode: 500,
147
133
  code: 'ERR',
148
134
  message: 'fallback message'
149
- }
150
- res.sendError(adaptError)
135
+ })
151
136
 
152
- assert.equal(jsonData.message, 'fallback message')
137
+ assert.equal(getJson().message, 'fallback message')
153
138
  })
154
139
  })
155
140
 
@@ -301,7 +301,7 @@ describe('loadRouteConfig()', () => {
301
301
  })
302
302
 
303
303
  describe('defaults option', () => {
304
- it('should prepend default routes from template when defaults path is provided', async () => {
304
+ it('should place custom routes before default routes for correct Express matching', async () => {
305
305
  const dir = path.join(tmpDir, 'with-defaults')
306
306
  await mkdir(dir, { recursive: true })
307
307
  await writeJson(path.join(dir, 'routes.json'), {
@@ -318,8 +318,8 @@ describe('loadRouteConfig()', () => {
318
318
  }
319
319
  const config = await loadRouteConfig(dir, target, { defaults: defaultsPath })
320
320
  assert.equal(config.routes.length, 2)
321
- assert.equal(config.routes[0].route, '/')
322
- assert.equal(config.routes[1].route, '/custom')
321
+ assert.equal(config.routes[0].route, '/custom')
322
+ assert.equal(config.routes[1].route, '/')
323
323
  })
324
324
 
325
325
  it('should resolve handler strings in default routes using handlerAliases', async () => {
@@ -450,8 +450,8 @@ describe('loadRouteConfig()', () => {
450
450
  })
451
451
  const config = await loadRouteConfig(dir, { myHandler: () => {} }, { defaults: defaultsPath })
452
452
  assert.equal(config.routes.length, 2)
453
- assert.equal(config.routes[0].route, '/')
454
- assert.equal(config.routes[1].route, '/nomatch')
453
+ assert.equal(config.routes[0].route, '/nomatch')
454
+ assert.equal(config.routes[1].route, '/')
455
455
  })
456
456
 
457
457
  it('should strip the override flag from merged route', async () => {