adapt-authoring-mongodb 2.0.1 → 3.0.1

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:
@@ -9,4 +9,4 @@ jobs:
9
9
  with:
10
10
  node-version: 'lts/*'
11
11
  - run: npm install
12
- - run: npx standard
12
+ - run: npx standard
package/index.js CHANGED
@@ -2,6 +2,5 @@
2
2
  * MongoDB integration
3
3
  * @namespace mongodb
4
4
  */
5
- export { default as ObjectIdUtils } from './lib/ObjectIdUtils.js'
6
- export { create, isObjectId, isValid, parse, parseIds } from './lib/utils.js'
5
+ export { convertObjectIds, createObjectId, isObjectId, isValidObjectId, parseObjectId } from './lib/utils.js'
7
6
  export { default } from './lib/MongoDBModule.js'
@@ -1,7 +1,8 @@
1
1
  import { AbstractModule } from 'adapt-authoring-core'
2
2
  import { MongoClient } from 'mongodb'
3
- import ObjectIdUtils from './ObjectIdUtils.js'
4
- import { parseIds } from './utils/parseIds.js'
3
+ import { convertObjectIds } from './utils/convertObjectIds.js'
4
+ import { isValidObjectId } from './utils/isValidObjectId.js'
5
+ import { parseObjectId } from './utils/parseObjectId.js'
5
6
  /**
6
7
  * Represents a single MongoDB server instance
7
8
  * @memberof mongodb
@@ -17,8 +18,7 @@ class MongoDBModule extends AbstractModule {
17
18
  */
18
19
  this.client = new MongoClient(this.getConfig('connectionUri'), { ignoreUndefined: true })
19
20
  await this.connect()
20
- // add custom keywords to JSON Schema validator
21
- await ObjectIdUtils.addSchemaKeyword()
21
+ await this.addSchemaKeyword()
22
22
  }
23
23
 
24
24
  /**
@@ -99,7 +99,7 @@ class MongoDBModule extends AbstractModule {
99
99
  * @see https://mongodb.github.io/node-mongodb-native/4.2/classes/Collection.html#insertOne
100
100
  */
101
101
  async insert (collectionName, data, options) {
102
- parseIds(data)
102
+ convertObjectIds(data)
103
103
  this.parseOptions(options)
104
104
  // MongoDB doesn't like the explicit setting of _id
105
105
  delete data._id
@@ -123,7 +123,7 @@ class MongoDBModule extends AbstractModule {
123
123
  * @see https://mongodb.github.io/node-mongodb-native/4.2/classes/Collection.html#find
124
124
  */
125
125
  async find (collectionName, query, options) {
126
- parseIds(query)
126
+ convertObjectIds(query)
127
127
  this.parseOptions(options)
128
128
  try {
129
129
  const cursor = this.getCollection(collectionName).find(query, options)
@@ -146,8 +146,8 @@ class MongoDBModule extends AbstractModule {
146
146
  async update (collectionName, query, data, options) {
147
147
  const opts = Object.assign({ includeResultMetadata: false, returnDocument: 'after' }, options)
148
148
  this.parseOptions(opts)
149
- parseIds(query)
150
- parseIds(data)
149
+ convertObjectIds(query)
150
+ convertObjectIds(data)
151
151
  // MongoDB doesn't like the explicit setting of _id
152
152
  delete data._id
153
153
  if (data.$set) delete data.$set._id
@@ -169,8 +169,8 @@ class MongoDBModule extends AbstractModule {
169
169
  */
170
170
  async updateMany (collectionName, query, data, options) {
171
171
  this.parseOptions(options)
172
- parseIds(query)
173
- parseIds(data)
172
+ convertObjectIds(query)
173
+ convertObjectIds(data)
174
174
  // MongoDB doesn't like the explicit setting of _id
175
175
  delete data._id
176
176
  if (data.$set) delete data.$set._id
@@ -194,8 +194,8 @@ class MongoDBModule extends AbstractModule {
194
194
  */
195
195
  async replace (collectionName, query, data, options) {
196
196
  const opts = Object.assign({ includeResultMetadata: false, returnDocument: 'after' }, options)
197
- parseIds(query)
198
- parseIds(data)
197
+ convertObjectIds(query)
198
+ convertObjectIds(data)
199
199
  this.parseOptions(options)
200
200
  // MongoDB doesn't like the explicit setting of _id
201
201
  delete data._id
@@ -217,7 +217,7 @@ class MongoDBModule extends AbstractModule {
217
217
  * @see https://mongodb.github.io/node-mongodb-native/4.2/classes/Collection.html#deleteOne
218
218
  */
219
219
  async delete (collectionName, query, options) {
220
- parseIds(query)
220
+ convertObjectIds(query)
221
221
  this.parseOptions(options)
222
222
  try {
223
223
  await this.getCollection(collectionName).deleteOne(query, options)
@@ -236,7 +236,7 @@ class MongoDBModule extends AbstractModule {
236
236
  * @see https://mongodb.github.io/node-mongodb-native/4.2/classes/Collection.html#deleteMany
237
237
  */
238
238
  async deleteMany (collectionName, query, options) {
239
- parseIds(query)
239
+ convertObjectIds(query)
240
240
  this.parseOptions(options)
241
241
  try {
242
242
  await this.getCollection(collectionName).deleteMany(query, options)
@@ -246,6 +246,32 @@ class MongoDBModule extends AbstractModule {
246
246
  }
247
247
  }
248
248
 
249
+ /**
250
+ * Registers the isObjectId JSON schema keyword
251
+ */
252
+ async addSchemaKeyword () {
253
+ const jsonschema = await this.app.waitForModule('jsonschema')
254
+ jsonschema.addKeyword({
255
+ keyword: 'isObjectId',
256
+ type: 'string',
257
+ modifying: true,
258
+ schemaType: 'boolean',
259
+ compile: () => {
260
+ return (value, { parentData, parentDataProperty }) => {
261
+ if (!isValidObjectId(value)) {
262
+ return false
263
+ }
264
+ try {
265
+ parentData[parentDataProperty] = parseObjectId(value)
266
+ } catch (e) {
267
+ return false
268
+ }
269
+ return true
270
+ }
271
+ }
272
+ }, { override: true })
273
+ }
274
+
249
275
  /**
250
276
  * Returns the relevant AdaptError instance to match the MongoError
251
277
  * @param {String} collectionName DB collection being processed
@@ -1,29 +1,29 @@
1
1
  import { isObject } from 'adapt-authoring-core'
2
- import { isValid } from './isValid.js'
3
- import { parse } from './parse.js'
2
+ import { isValidObjectId } from './isValidObjectId.js'
3
+ import { parseObjectId } from './parseObjectId.js'
4
4
 
5
5
  /**
6
6
  * Checks an input object for any strings which pass the parse check and convert matches to ObjectId instances
7
7
  * @param {Object} o Object to be checked
8
8
  * @memberof mongodb
9
9
  */
10
- export function parseIds (o) {
10
+ export function convertObjectIds (o) {
11
11
  if (o === undefined) {
12
12
  return
13
13
  }
14
14
  Object.entries(o).forEach(([k, v]) => {
15
15
  if (isObject(v)) {
16
- parseIds(v)
16
+ convertObjectIds(v)
17
17
  } else if (Array.isArray(v)) {
18
18
  v.forEach((v2, i) => {
19
19
  try {
20
- if (typeof v2 === 'string') v[i] = parse(v2)
20
+ if (typeof v2 === 'string') v[i] = parseObjectId(v2)
21
21
  } catch (e) {} // ignore failures
22
- parseIds(v2)
22
+ convertObjectIds(v2)
23
23
  })
24
- } else if (typeof v === 'string' && isValid(v)) {
24
+ } else if (typeof v === 'string' && isValidObjectId(v)) {
25
25
  try {
26
- o[k] = parse(v)
26
+ o[k] = parseObjectId(v)
27
27
  } catch (e) {} // ignore failures
28
28
  }
29
29
  })
@@ -5,6 +5,6 @@ import { ObjectId } from 'mongodb'
5
5
  * @return {external:MongoDBObjectId}
6
6
  * @memberof mongodb
7
7
  */
8
- export function create () {
8
+ export function createObjectId () {
9
9
  return new ObjectId()
10
10
  }
@@ -1,4 +1,4 @@
1
- import { parse } from './parse.js'
1
+ import { parseObjectId } from './parseObjectId.js'
2
2
 
3
3
  /**
4
4
  * Checks a string is a valid ObjectId
@@ -6,9 +6,9 @@ import { parse } from './parse.js'
6
6
  * @return {Boolean}
7
7
  * @memberof mongodb
8
8
  */
9
- export function isValid (s) {
9
+ export function isValidObjectId (s) {
10
10
  try {
11
- return parse(s).equals(s)
11
+ return parseObjectId(s).equals(s)
12
12
  } catch (e) {
13
13
  return false
14
14
  }
@@ -9,7 +9,7 @@ import { isObjectId } from './isObjectId.js'
9
9
  * @throws {Error}
10
10
  * @memberof mongodb
11
11
  */
12
- export function parse (s) {
12
+ export function parseObjectId (s) {
13
13
  if (isObjectId(s)) {
14
14
  return s
15
15
  }
package/lib/utils.js CHANGED
@@ -1,5 +1,5 @@
1
- export { create } from './utils/create.js'
1
+ export { createObjectId } from './utils/createObjectId.js'
2
2
  export { isObjectId } from './utils/isObjectId.js'
3
- export { isValid } from './utils/isValid.js'
4
- export { parse } from './utils/parse.js'
5
- export { parseIds } from './utils/parseIds.js'
3
+ export { isValidObjectId } from './utils/isValidObjectId.js'
4
+ export { parseObjectId } from './utils/parseObjectId.js'
5
+ export { convertObjectIds } from './utils/convertObjectIds.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adapt-authoring-mongodb",
3
- "version": "2.0.1",
3
+ "version": "3.0.1",
4
4
  "description": "Module for saving to a MongoDB instance",
5
5
  "homepage": "https://github.com/adapt-security/adapt-authoring-mongodb",
6
6
  "license": "GPL-3.0",
@@ -16,17 +16,6 @@
16
16
  "adapt-authoring-core": "^2.0.0",
17
17
  "adapt-authoring-jsonschema": "^1.2.2"
18
18
  },
19
- "peerDependenciesMeta": {
20
- "adapt-authoring-config": {
21
- "optional": true
22
- },
23
- "adapt-authoring-core": {
24
- "optional": true
25
- },
26
- "adapt-authoring-jsonschema": {
27
- "optional": true
28
- }
29
- },
30
19
  "devDependencies": {
31
20
  "@semantic-release/git": "^10.0.1",
32
21
  "conventional-changelog-eslint": "^6.0.0",
@@ -15,24 +15,24 @@ mock.getter(App, 'instance', () => ({
15
15
  }
16
16
  }))
17
17
 
18
- const { parseIds } = await import('../lib/utils/parseIds.js')
18
+ const { convertObjectIds } = await import('../lib/utils/convertObjectIds.js')
19
19
 
20
- describe('parseIds()', () => {
20
+ describe('convertObjectIds()', () => {
21
21
  it('should handle undefined input gracefully', () => {
22
- assert.equal(parseIds(undefined), undefined)
22
+ assert.equal(convertObjectIds(undefined), undefined)
23
23
  })
24
24
 
25
25
  it('should convert valid ObjectId strings in a flat object', () => {
26
26
  const idStr = new ObjectId().toString()
27
27
  const obj = { _id: idStr }
28
- parseIds(obj)
28
+ convertObjectIds(obj)
29
29
  assert.ok(obj._id instanceof ObjectId)
30
30
  assert.equal(obj._id.toString(), idStr)
31
31
  })
32
32
 
33
33
  it('should leave non-ObjectId strings unchanged', () => {
34
34
  const obj = { name: 'test', value: 'hello' }
35
- parseIds(obj)
35
+ convertObjectIds(obj)
36
36
  assert.equal(obj.name, 'test')
37
37
  assert.equal(obj.value, 'hello')
38
38
  })
@@ -40,7 +40,7 @@ describe('parseIds()', () => {
40
40
  it('should recurse into nested objects', () => {
41
41
  const idStr = new ObjectId().toString()
42
42
  const obj = { nested: { _id: idStr } }
43
- parseIds(obj)
43
+ convertObjectIds(obj)
44
44
  assert.ok(obj.nested._id instanceof ObjectId)
45
45
  assert.equal(obj.nested._id.toString(), idStr)
46
46
  })
@@ -48,7 +48,7 @@ describe('parseIds()', () => {
48
48
  it('should convert ObjectId strings in arrays', () => {
49
49
  const idStr = new ObjectId().toString()
50
50
  const obj = { ids: [idStr] }
51
- parseIds(obj)
51
+ convertObjectIds(obj)
52
52
  assert.ok(obj.ids[0] instanceof ObjectId)
53
53
  assert.equal(obj.ids[0].toString(), idStr)
54
54
  })
@@ -56,20 +56,20 @@ describe('parseIds()', () => {
56
56
  it('should recurse into objects within arrays', () => {
57
57
  const idStr = new ObjectId().toString()
58
58
  const obj = { items: [{ _id: idStr }] }
59
- parseIds(obj)
59
+ convertObjectIds(obj)
60
60
  assert.ok(obj.items[0]._id instanceof ObjectId)
61
61
  })
62
62
 
63
63
  it('should leave non-ObjectId strings in arrays unchanged', () => {
64
64
  const obj = { tags: ['hello', 'world'] }
65
- parseIds(obj)
65
+ convertObjectIds(obj)
66
66
  assert.equal(obj.tags[0], 'hello')
67
67
  assert.equal(obj.tags[1], 'world')
68
68
  })
69
69
 
70
70
  it('should leave numbers and booleans unchanged', () => {
71
71
  const obj = { count: 42, active: true }
72
- parseIds(obj)
72
+ convertObjectIds(obj)
73
73
  assert.equal(obj.count, 42)
74
74
  assert.equal(obj.active, true)
75
75
  })
@@ -77,7 +77,7 @@ describe('parseIds()', () => {
77
77
  it('should handle deeply nested structures', () => {
78
78
  const idStr = new ObjectId().toString()
79
79
  const obj = { a: { b: { c: { _id: idStr } } } }
80
- parseIds(obj)
80
+ convertObjectIds(obj)
81
81
  assert.ok(obj.a.b.c._id instanceof ObjectId)
82
82
  })
83
83
 
@@ -89,7 +89,7 @@ describe('parseIds()', () => {
89
89
  count: 5,
90
90
  nested: { value: 'keep' }
91
91
  }
92
- parseIds(obj)
92
+ convertObjectIds(obj)
93
93
  assert.ok(obj._id instanceof ObjectId)
94
94
  assert.equal(obj.name, 'test')
95
95
  assert.equal(obj.count, 5)
@@ -98,13 +98,13 @@ describe('parseIds()', () => {
98
98
 
99
99
  it('should handle empty objects', () => {
100
100
  const obj = {}
101
- parseIds(obj)
101
+ convertObjectIds(obj)
102
102
  assert.deepEqual(obj, {})
103
103
  })
104
104
 
105
105
  it('should handle objects with empty arrays', () => {
106
106
  const obj = { items: [] }
107
- parseIds(obj)
107
+ convertObjectIds(obj)
108
108
  assert.deepEqual(obj.items, [])
109
109
  })
110
110
  })
@@ -1,22 +1,22 @@
1
1
  import { describe, it } from 'node:test'
2
2
  import assert from 'node:assert/strict'
3
3
  import { ObjectId } from 'mongodb'
4
- import { create } from '../lib/utils/create.js'
4
+ import { createObjectId } from '../lib/utils/createObjectId.js'
5
5
 
6
- describe('create()', () => {
6
+ describe('createObjectId()', () => {
7
7
  it('should return an ObjectId instance', () => {
8
- const id = create()
8
+ const id = createObjectId()
9
9
  assert.ok(id instanceof ObjectId)
10
10
  })
11
11
 
12
12
  it('should create unique IDs each time', () => {
13
- const id1 = create()
14
- const id2 = create()
13
+ const id1 = createObjectId()
14
+ const id2 = createObjectId()
15
15
  assert.notEqual(id1.toString(), id2.toString())
16
16
  })
17
17
 
18
18
  it('should return a 24-character hex string representation', () => {
19
- const id = create()
19
+ const id = createObjectId()
20
20
  assert.match(id.toString(), /^[0-9a-f]{24}$/)
21
21
  })
22
22
  })
@@ -15,39 +15,39 @@ mock.getter(App, 'instance', () => ({
15
15
  }
16
16
  }))
17
17
 
18
- const { isValid } = await import('../lib/utils/isValid.js')
18
+ const { isValidObjectId } = await import('../lib/utils/isValidObjectId.js')
19
19
 
20
- describe('isValid()', () => {
20
+ describe('isValidObjectId()', () => {
21
21
  it('should return true for a valid ObjectId string', () => {
22
22
  const id = new ObjectId()
23
- assert.equal(isValid(id.toString()), true)
23
+ assert.equal(isValidObjectId(id.toString()), true)
24
24
  })
25
25
 
26
26
  it('should return true for a 24-char hex string', () => {
27
- assert.equal(isValid('507f1f77bcf86cd799439011'), true)
27
+ assert.equal(isValidObjectId('507f1f77bcf86cd799439011'), true)
28
28
  })
29
29
 
30
30
  it('should return false for a random string', () => {
31
- assert.equal(isValid('not-an-objectid'), false)
31
+ assert.equal(isValidObjectId('not-an-objectid'), false)
32
32
  })
33
33
 
34
34
  it('should return false for an empty string', () => {
35
- assert.equal(isValid(''), false)
35
+ assert.equal(isValidObjectId(''), false)
36
36
  })
37
37
 
38
38
  it('should return false for a number', () => {
39
- assert.equal(isValid(12345), false)
39
+ assert.equal(isValidObjectId(12345), false)
40
40
  })
41
41
 
42
42
  it('should return false for a 23-char hex string', () => {
43
- assert.equal(isValid('507f1f77bcf86cd79943901'), false)
43
+ assert.equal(isValidObjectId('507f1f77bcf86cd79943901'), false)
44
44
  })
45
45
 
46
46
  it('should return false for null', () => {
47
- assert.equal(isValid(null), false)
47
+ assert.equal(isValidObjectId(null), false)
48
48
  })
49
49
 
50
50
  it('should return false for undefined', () => {
51
- assert.equal(isValid(undefined), false)
51
+ assert.equal(isValidObjectId(undefined), false)
52
52
  })
53
53
  })
@@ -15,24 +15,24 @@ mock.getter(App, 'instance', () => ({
15
15
  }
16
16
  }))
17
17
 
18
- const { parse } = await import('../lib/utils/parse.js')
18
+ const { parseObjectId } = await import('../lib/utils/parseObjectId.js')
19
19
 
20
- describe('parse()', () => {
20
+ describe('parseObjectId()', () => {
21
21
  it('should return same ObjectId if already an ObjectId instance', () => {
22
22
  const id = new ObjectId()
23
- assert.equal(parse(id), id)
23
+ assert.equal(parseObjectId(id), id)
24
24
  })
25
25
 
26
26
  it('should convert a valid ObjectId string to an ObjectId', () => {
27
27
  const str = '507f1f77bcf86cd799439011'
28
- const result = parse(str)
28
+ const result = parseObjectId(str)
29
29
  assert.ok(result instanceof ObjectId)
30
30
  assert.equal(result.toString(), str)
31
31
  })
32
32
 
33
33
  it('should throw INVALID_OBJECTID for an invalid string', () => {
34
34
  assert.throws(
35
- () => parse('not-a-valid-id'),
35
+ () => parseObjectId('not-a-valid-id'),
36
36
  (err) => {
37
37
  assert.equal(err.message, 'INVALID_OBJECTID')
38
38
  assert.deepEqual(err.data, { value: 'not-a-valid-id' })
@@ -43,7 +43,7 @@ describe('parse()', () => {
43
43
 
44
44
  it('should throw INVALID_OBJECTID for an empty string', () => {
45
45
  assert.throws(
46
- () => parse(''),
46
+ () => parseObjectId(''),
47
47
  (err) => {
48
48
  assert.equal(err.message, 'INVALID_OBJECTID')
49
49
  return true
@@ -53,14 +53,14 @@ describe('parse()', () => {
53
53
 
54
54
  it('should convert a 24-char hex string', () => {
55
55
  const hex = 'aabbccddeeff00112233aabb'
56
- const result = parse(hex)
56
+ const result = parseObjectId(hex)
57
57
  assert.ok(result instanceof ObjectId)
58
58
  assert.equal(result.toString(), hex)
59
59
  })
60
60
 
61
61
  it('should throw for a 23-char string', () => {
62
62
  assert.throws(
63
- () => parse('507f1f77bcf86cd79943901'),
63
+ () => parseObjectId('507f1f77bcf86cd79943901'),
64
64
  (err) => {
65
65
  assert.equal(err.message, 'INVALID_OBJECTID')
66
66
  return true
@@ -1,37 +0,0 @@
1
- import { App } from 'adapt-authoring-core'
2
- import { isValid } from './utils/isValid.js'
3
- import { parse } from './utils/parse.js'
4
-
5
- /**
6
- * JSON schema integration for MongoDB ObjectIds
7
- * @memberof mongodb
8
- */
9
- class ObjectIdUtils {
10
- /**
11
- * Registers the isObjectId JSON schema keyword
12
- */
13
- static async addSchemaKeyword () {
14
- const jsonschema = await App.instance.waitForModule('jsonschema')
15
- jsonschema.addKeyword({
16
- keyword: 'isObjectId',
17
- type: 'string',
18
- modifying: true,
19
- schemaType: 'boolean',
20
- compile: () => {
21
- return (value, { parentData, parentDataProperty }) => {
22
- if (!isValid(value)) {
23
- return false
24
- }
25
- try {
26
- parentData[parentDataProperty] = parse(value)
27
- } catch (e) {
28
- return false
29
- }
30
- return true
31
- }
32
- }
33
- })
34
- }
35
- }
36
-
37
- export default ObjectIdUtils