adapt-authoring-content 2.1.3 → 2.1.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.
- package/.github/workflows/standardjs.yml +2 -1
- package/lib/ContentModule.js +5 -16
- package/package.json +3 -2
- package/routes.json +63 -0
- package/tests/ContentModule.spec.js +6 -40
package/lib/ContentModule.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { AbstractApiModule } from 'adapt-authoring-api'
|
|
2
2
|
import { getDescendants } from './utils.js'
|
|
3
3
|
import { Hook, stringifyValues } from 'adapt-authoring-core'
|
|
4
|
-
import apidefs from './apidefs.js'
|
|
5
4
|
/**
|
|
6
5
|
* Module which handles course content
|
|
7
6
|
* @memberof content
|
|
@@ -10,19 +9,8 @@ import apidefs from './apidefs.js'
|
|
|
10
9
|
class ContentModule extends AbstractApiModule {
|
|
11
10
|
/** @override */
|
|
12
11
|
async setValues () {
|
|
13
|
-
|
|
14
|
-
this.
|
|
15
|
-
this.routes.push({
|
|
16
|
-
route: '/insertrecursive',
|
|
17
|
-
handlers: { post: this.handleInsertRecursive.bind(this) },
|
|
18
|
-
permissions: { post: ['write:content'] },
|
|
19
|
-
meta: apidefs.insertrecursive
|
|
20
|
-
}, {
|
|
21
|
-
route: '/clone',
|
|
22
|
-
handlers: { post: this.handleClone.bind(this) },
|
|
23
|
-
permissions: { post: ['write:content'] },
|
|
24
|
-
meta: apidefs.clone
|
|
25
|
-
})
|
|
12
|
+
await super.setValues()
|
|
13
|
+
/** @ignore */ this.collectionName = this.schemaName = 'content'
|
|
26
14
|
}
|
|
27
15
|
|
|
28
16
|
/** @override */
|
|
@@ -52,7 +40,7 @@ class ContentModule extends AbstractApiModule {
|
|
|
52
40
|
await mongodb.setIndex(this.collectionName, { _courseId: 1, _parentId: 1, _type: 1 })
|
|
53
41
|
await mongodb.setIndex(this.collectionName, { _courseId: 1, _friendlyId: 1 }, {
|
|
54
42
|
unique: true,
|
|
55
|
-
partialFilterExpression: { _friendlyId: { $type: 'string' } }
|
|
43
|
+
partialFilterExpression: { _friendlyId: { $type: 'string', $gt: '' } }
|
|
56
44
|
})
|
|
57
45
|
}
|
|
58
46
|
|
|
@@ -266,7 +254,7 @@ class ContentModule extends AbstractApiModule {
|
|
|
266
254
|
await this.clone(userId, config._id, undefined, { _courseId: newData._id.toString() })
|
|
267
255
|
delete payload._id
|
|
268
256
|
delete payload._courseId
|
|
269
|
-
await this.update({ _id: newData._id }, payload)
|
|
257
|
+
await this.update({ _id: newData._id }, payload, { validate: false })
|
|
270
258
|
}
|
|
271
259
|
}
|
|
272
260
|
const children = await this.find({ _parentId: _id })
|
|
@@ -374,6 +362,7 @@ class ContentModule extends AbstractApiModule {
|
|
|
374
362
|
*/
|
|
375
363
|
async handleClone (req, res, next) {
|
|
376
364
|
try {
|
|
365
|
+
await this.requestHook.invoke(req)
|
|
377
366
|
const { _id, _parentId } = req.body
|
|
378
367
|
const source = await this.findOne({ _id: req.body._id })
|
|
379
368
|
await this.checkAccess(req, source)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adapt-authoring-content",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"description": "Module for managing Adapt content",
|
|
5
5
|
"homepage": "https://github.com/adapt-security/adapt-authoring-content",
|
|
6
6
|
"license": "GPL-3.0",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"test": "node --test 'tests/**/*.spec.js'"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"adapt-authoring-api": "^
|
|
14
|
+
"adapt-authoring-api": "^3.0.0",
|
|
15
15
|
"adapt-authoring-core": "^2.0.0"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"adapt-authoring-tags": "^1.0.2"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
+
"adapt-authoring-server": "^2.1.0",
|
|
25
26
|
"@semantic-release/git": "^10.0.1",
|
|
26
27
|
"conventional-changelog-eslint": "^6.0.0",
|
|
27
28
|
"semantic-release": "^25.0.2",
|
package/routes.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"root": "content",
|
|
3
|
+
"routes": [
|
|
4
|
+
{
|
|
5
|
+
"route": "/insertrecursive",
|
|
6
|
+
"handlers": { "post": "handleInsertRecursive" },
|
|
7
|
+
"permissions": { "post": ["write:${scope}"] },
|
|
8
|
+
"meta": {
|
|
9
|
+
"post": {
|
|
10
|
+
"summary": "Insert hierarchical content data",
|
|
11
|
+
"description": "Recursively inserts content data",
|
|
12
|
+
"parameters": [{ "name": "rootId", "in": "path", "description": "The parent content item _id", "required": true }],
|
|
13
|
+
"requestBody": {
|
|
14
|
+
"content": {
|
|
15
|
+
"application/json": {
|
|
16
|
+
"schema": { "$ref": "#components/schemas/content" }
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"responses": {
|
|
21
|
+
"201": {
|
|
22
|
+
"description": "The newly inserted data",
|
|
23
|
+
"content": {
|
|
24
|
+
"application/json": {
|
|
25
|
+
"schema": {
|
|
26
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
27
|
+
"type": "array",
|
|
28
|
+
"items": { "$ref": "#components/schemas/content" }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"route": "/clone",
|
|
39
|
+
"handlers": { "post": "handleClone" },
|
|
40
|
+
"permissions": { "post": ["write:${scope}"] },
|
|
41
|
+
"meta": {
|
|
42
|
+
"post": {
|
|
43
|
+
"summary": "Clones a content item",
|
|
44
|
+
"description": "Duplicates a content item as well as all its children.",
|
|
45
|
+
"responses": {
|
|
46
|
+
"201": {
|
|
47
|
+
"description": "The newly cloned data",
|
|
48
|
+
"content": {
|
|
49
|
+
"application/json": {
|
|
50
|
+
"schema": {
|
|
51
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
52
|
+
"type": "array",
|
|
53
|
+
"items": { "$ref": "#components/schemas/content" }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
@@ -68,15 +68,14 @@ function createInstance (overrides = {}) {
|
|
|
68
68
|
update: mock.fn(async (q, d) => ({ ...q, ...d })),
|
|
69
69
|
delete: mock.fn(async () => ({})),
|
|
70
70
|
|
|
71
|
-
useDefaultRouteConfig: mock.fn(),
|
|
72
71
|
setDefaultOptions: mock.fn((opts) => opts),
|
|
73
72
|
checkAccess: mock.fn(async (req, data) => data),
|
|
74
73
|
log: mock.fn(),
|
|
75
74
|
|
|
75
|
+
requestHook: createMockHook(),
|
|
76
76
|
preCloneHook: createMockHook(),
|
|
77
77
|
postCloneHook: createMockHook(),
|
|
78
78
|
|
|
79
|
-
// Methods that setValues tries to .bind(this)
|
|
80
79
|
handleInsertRecursive: mock.fn(),
|
|
81
80
|
handleClone: mock.fn(),
|
|
82
81
|
|
|
@@ -97,48 +96,14 @@ describe('ContentModule', () => {
|
|
|
97
96
|
// setValues
|
|
98
97
|
// -----------------------------------------------------------------------
|
|
99
98
|
describe('setValues', () => {
|
|
100
|
-
it('should set
|
|
99
|
+
it('should set collectionName and schemaName to "content"', async () => {
|
|
101
100
|
const inst = createInstance()
|
|
101
|
+
Object.getPrototypeOf(ContentModule.prototype).setValues = mock.fn(async function () {})
|
|
102
102
|
await ContentModule.prototype.setValues.call(inst)
|
|
103
103
|
|
|
104
|
-
assert.equal(inst.root, 'content')
|
|
105
104
|
assert.equal(inst.collectionName, 'content')
|
|
106
105
|
assert.equal(inst.schemaName, 'content')
|
|
107
106
|
})
|
|
108
|
-
|
|
109
|
-
it('should call useDefaultRouteConfig', async () => {
|
|
110
|
-
const inst = createInstance()
|
|
111
|
-
await ContentModule.prototype.setValues.call(inst)
|
|
112
|
-
|
|
113
|
-
assert.equal(inst.useDefaultRouteConfig.mock.callCount(), 1)
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it('should push insertrecursive and clone routes', async () => {
|
|
117
|
-
const inst = createInstance()
|
|
118
|
-
await ContentModule.prototype.setValues.call(inst)
|
|
119
|
-
|
|
120
|
-
assert.equal(inst.routes.length, 2)
|
|
121
|
-
assert.equal(inst.routes[0].route, '/insertrecursive')
|
|
122
|
-
assert.equal(inst.routes[1].route, '/clone')
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
it('should assign correct HTTP methods for insertrecursive route', async () => {
|
|
126
|
-
const inst = createInstance()
|
|
127
|
-
await ContentModule.prototype.setValues.call(inst)
|
|
128
|
-
|
|
129
|
-
const route = inst.routes[0]
|
|
130
|
-
assert.ok(route.handlers.post)
|
|
131
|
-
assert.deepEqual(route.permissions, { post: ['write:content'] })
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
it('should assign correct HTTP methods for clone route', async () => {
|
|
135
|
-
const inst = createInstance()
|
|
136
|
-
await ContentModule.prototype.setValues.call(inst)
|
|
137
|
-
|
|
138
|
-
const route = inst.routes[1]
|
|
139
|
-
assert.ok(route.handlers.post)
|
|
140
|
-
assert.deepEqual(route.permissions, { post: ['write:content'] })
|
|
141
|
-
})
|
|
142
107
|
})
|
|
143
108
|
|
|
144
109
|
// -----------------------------------------------------------------------
|
|
@@ -906,9 +871,10 @@ describe('ContentModule', () => {
|
|
|
906
871
|
assert.equal(next.mock.calls[0].arguments[0], error)
|
|
907
872
|
})
|
|
908
873
|
|
|
909
|
-
it('should call checkAccess
|
|
874
|
+
it('should call requestHook, checkAccess, then clone in order', async () => {
|
|
910
875
|
const callOrder = []
|
|
911
876
|
const inst = createInstance()
|
|
877
|
+
inst.requestHook = { invoke: mock.fn(async () => { callOrder.push('requestHook') }) }
|
|
912
878
|
inst.findOne = mock.fn(async () => {
|
|
913
879
|
callOrder.push('findOne')
|
|
914
880
|
return { _id: 'orig' }
|
|
@@ -931,7 +897,7 @@ describe('ContentModule', () => {
|
|
|
931
897
|
|
|
932
898
|
await ContentModule.prototype.handleClone.call(inst, req, res, mock.fn())
|
|
933
899
|
|
|
934
|
-
assert.deepEqual(callOrder, ['findOne', 'checkAccess', 'clone'])
|
|
900
|
+
assert.deepEqual(callOrder, ['requestHook', 'findOne', 'checkAccess', 'clone'])
|
|
935
901
|
})
|
|
936
902
|
})
|
|
937
903
|
|