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.
@@ -1,4 +1,4 @@
1
- name: Standard.js formatting check
1
+ name: Lint
2
2
  on: push
3
3
  jobs:
4
4
  default:
@@ -10,3 +10,4 @@ jobs:
10
10
  node-version: 'lts/*'
11
11
  - run: npm install
12
12
  - run: npx standard
13
+
@@ -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
- /** @ignore */ this.root = this.collectionName = this.schemaName = 'content'
14
- this.useDefaultRouteConfig()
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",
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": "^2.0.0",
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 root, collectionName and schemaName to "content"', async () => {
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 before cloning', async () => {
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