adapt-authoring-content 2.1.7 → 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/errors/errors.json +7 -8
- package/index.js +1 -0
- package/lib/ContentModule.js +432 -110
- package/lib/ContentTree.js +128 -0
- package/lib/utils/computeSortOrderOps.js +21 -0
- package/lib/utils/contentTypeToSchemaName.js +9 -0
- package/lib/utils/extractAssetIds.js +18 -0
- package/lib/utils/formatFriendlyId.js +12 -0
- package/lib/utils/parseMaxSeq.js +16 -0
- package/lib/utils.js +6 -1
- package/migrations/3.0.0.js +123 -0
- package/package.json +4 -3
- package/routes.json +51 -0
- package/schema/contentassets.schema.json +18 -0
- package/tests/ContentModule.spec.js +512 -1634
- package/tests/ContentTree.spec.js +230 -0
- package/tests/_ht.js +116 -0
- package/tests/utils-computeSortOrderOps.spec.js +94 -0
- package/tests/utils-contentTypeToSchemaName.spec.js +21 -0
- package/tests/utils-extractAssetIds.spec.js +118 -0
- package/tests/utils-formatFriendlyId.spec.js +40 -0
- package/tests/utils-parseMaxSeq.spec.js +49 -0
- package/lib/utils/getDescendants.js +0 -22
- package/tests/utils-getDescendants.spec.js +0 -117
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Finds all descendant content items for a given root using BFS traversal
|
|
3
|
-
* @param {Function} findFn Function to query content items (receives query object, returns array)
|
|
4
|
-
* @param {Object} rootItem The root item document
|
|
5
|
-
* @returns {Promise<Array<Object>>} Array of descendant content items
|
|
6
|
-
* @memberof content
|
|
7
|
-
*/
|
|
8
|
-
export async function getDescendants (findFn, rootItem) {
|
|
9
|
-
const courseItems = await findFn({ _courseId: rootItem._courseId })
|
|
10
|
-
const descendants = []
|
|
11
|
-
let items = [rootItem]
|
|
12
|
-
do {
|
|
13
|
-
items = items.reduce((m, i) => [...m, ...courseItems.filter(c => c._parentId?.toString() === i._id.toString())], [])
|
|
14
|
-
descendants.push(...items)
|
|
15
|
-
} while (items.length)
|
|
16
|
-
|
|
17
|
-
if (rootItem._type === 'course') {
|
|
18
|
-
const config = courseItems.find(c => c._type === 'config')
|
|
19
|
-
if (config) descendants.push(config)
|
|
20
|
-
}
|
|
21
|
-
return descendants
|
|
22
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { describe, it } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import { getDescendants } from '../lib/utils/getDescendants.js'
|
|
4
|
-
|
|
5
|
-
describe('getDescendants()', () => {
|
|
6
|
-
const makeId = (n) => ({ toString: () => `id${n}` })
|
|
7
|
-
|
|
8
|
-
const courseItems = [
|
|
9
|
-
{ _id: makeId(1), _courseId: 'course1', _type: 'course' },
|
|
10
|
-
{ _id: makeId(2), _courseId: 'course1', _parentId: makeId(1), _type: 'page' },
|
|
11
|
-
{ _id: makeId(3), _courseId: 'course1', _parentId: makeId(2), _type: 'article' },
|
|
12
|
-
{ _id: makeId(4), _courseId: 'course1', _parentId: makeId(3), _type: 'block' },
|
|
13
|
-
{ _id: makeId(5), _courseId: 'course1', _parentId: makeId(4), _type: 'component' },
|
|
14
|
-
{ _id: makeId(6), _courseId: 'course1', _type: 'config' }
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
const findFn = async (query) => {
|
|
18
|
-
return courseItems.filter(c => {
|
|
19
|
-
return Object.entries(query).every(([k, v]) => {
|
|
20
|
-
const itemVal = c[k]
|
|
21
|
-
if (itemVal && typeof itemVal.toString === 'function' && typeof v === 'string') {
|
|
22
|
-
return itemVal.toString() === v
|
|
23
|
-
}
|
|
24
|
-
return itemVal === v
|
|
25
|
-
})
|
|
26
|
-
})
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
it('should return all descendants of the course (including config)', async () => {
|
|
30
|
-
const root = courseItems[0]
|
|
31
|
-
const result = await getDescendants(findFn, root)
|
|
32
|
-
// Should include page, article, block, component, config = 5 items
|
|
33
|
-
assert.equal(result.length, 5)
|
|
34
|
-
assert.ok(result.some(r => r._type === 'page'))
|
|
35
|
-
assert.ok(result.some(r => r._type === 'article'))
|
|
36
|
-
assert.ok(result.some(r => r._type === 'block'))
|
|
37
|
-
assert.ok(result.some(r => r._type === 'component'))
|
|
38
|
-
assert.ok(result.some(r => r._type === 'config'))
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('should return descendants of a page', async () => {
|
|
42
|
-
const root = courseItems[1] // page
|
|
43
|
-
const result = await getDescendants(findFn, root)
|
|
44
|
-
// article, block, component = 3
|
|
45
|
-
assert.equal(result.length, 3)
|
|
46
|
-
assert.ok(result.some(r => r._type === 'article'))
|
|
47
|
-
assert.ok(result.some(r => r._type === 'block'))
|
|
48
|
-
assert.ok(result.some(r => r._type === 'component'))
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('should return descendants of an article', async () => {
|
|
52
|
-
const root = courseItems[2] // article
|
|
53
|
-
const result = await getDescendants(findFn, root)
|
|
54
|
-
// block, component = 2
|
|
55
|
-
assert.equal(result.length, 2)
|
|
56
|
-
assert.ok(result.some(r => r._type === 'block'))
|
|
57
|
-
assert.ok(result.some(r => r._type === 'component'))
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('should return empty array for a leaf node', async () => {
|
|
61
|
-
const root = courseItems[4] // component (leaf)
|
|
62
|
-
const result = await getDescendants(findFn, root)
|
|
63
|
-
assert.equal(result.length, 0)
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('should not include config for non-course roots', async () => {
|
|
67
|
-
const root = courseItems[1] // page
|
|
68
|
-
const result = await getDescendants(findFn, root)
|
|
69
|
-
assert.ok(!result.some(r => r._type === 'config'))
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it('should include config for course root', async () => {
|
|
73
|
-
const root = courseItems[0]
|
|
74
|
-
const result = await getDescendants(findFn, root)
|
|
75
|
-
assert.ok(result.some(r => r._type === 'config'))
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('should handle course with no config', async () => {
|
|
79
|
-
const itemsNoConfig = courseItems.filter(c => c._type !== 'config')
|
|
80
|
-
const findNoConfig = async (query) => {
|
|
81
|
-
return itemsNoConfig.filter(c => {
|
|
82
|
-
return Object.entries(query).every(([k, v]) => {
|
|
83
|
-
const itemVal = c[k]
|
|
84
|
-
if (itemVal && typeof itemVal.toString === 'function' && typeof v === 'string') {
|
|
85
|
-
return itemVal.toString() === v
|
|
86
|
-
}
|
|
87
|
-
return itemVal === v
|
|
88
|
-
})
|
|
89
|
-
})
|
|
90
|
-
}
|
|
91
|
-
const root = itemsNoConfig[0] // course
|
|
92
|
-
const result = await getDescendants(findNoConfig, root)
|
|
93
|
-
// page, article, block, component = 4 (no config)
|
|
94
|
-
assert.equal(result.length, 4)
|
|
95
|
-
assert.ok(!result.some(r => r._type === 'config'))
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
it('should handle empty course (no children)', async () => {
|
|
99
|
-
const emptyItems = [{ _id: makeId(10), _courseId: 'course10', _type: 'course' }]
|
|
100
|
-
const emptyFind = async () => emptyItems
|
|
101
|
-
const root = emptyItems[0]
|
|
102
|
-
const result = await getDescendants(emptyFind, root)
|
|
103
|
-
assert.equal(result.length, 0)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it('should use findFn with correct query', async () => {
|
|
107
|
-
const queries = []
|
|
108
|
-
const trackingFind = async (query) => {
|
|
109
|
-
queries.push(query)
|
|
110
|
-
return []
|
|
111
|
-
}
|
|
112
|
-
const root = { _id: makeId(99), _courseId: 'courseX', _type: 'page' }
|
|
113
|
-
await getDescendants(trackingFind, root)
|
|
114
|
-
assert.equal(queries.length, 1)
|
|
115
|
-
assert.deepEqual(queries[0], { _courseId: 'courseX' })
|
|
116
|
-
})
|
|
117
|
-
})
|