adapt-authoring-contentplugin 1.6.1 → 1.7.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.
@@ -10,7 +10,8 @@ permissions:
10
10
  issues: write
11
11
  pull-requests: write
12
12
  id-token: write
13
+ packages: write
13
14
 
14
15
  jobs:
15
16
  release:
16
- uses: adaptlearning/semantic-release-config/.github/workflows/release.yml@master
17
+ uses: adaptlearning/semantic-release-config/.github/workflows/release.yml@master
@@ -24,8 +24,10 @@ class ContentPluginModule extends AbstractApiModule {
24
24
  /** @ignore */ this.root = 'contentplugins'
25
25
  /** @ignore */ this.schemaName = 'contentplugin'
26
26
  /**
27
- * Reference to all content plugin schemas, grouped by plugin
28
- * @type {Object}
27
+ * Maps plugin name to a map of schema $anchor file path. The file path
28
+ * lets us re-register schemas after JsonSchemaModule resets the registry
29
+ * on app ready (it only re-registers schemas owned by app.dependencies).
30
+ * @type {Object<string, Object<string, string>>}
29
31
  */
30
32
  this.pluginSchemas = {}
31
33
  /**
@@ -45,7 +47,7 @@ class ContentPluginModule extends AbstractApiModule {
45
47
  if (!process.env.ADAPT_ALLOW_PRERELEASE) {
46
48
  process.env.ADAPT_ALLOW_PRERELEASE = 'true'
47
49
  }
48
- const [framework, mongodb] = await this.app.waitForModule('adaptframework', 'mongodb')
50
+ const [framework, jsonschema, mongodb] = await this.app.waitForModule('adaptframework', 'jsonschema', 'mongodb')
49
51
 
50
52
  await mongodb.setIndex(this.collectionName, 'name', { unique: true })
51
53
  await mongodb.setIndex(this.collectionName, 'displayName', { unique: true })
@@ -56,6 +58,10 @@ class ContentPluginModule extends AbstractApiModule {
56
58
  */
57
59
  this.framework = framework
58
60
 
61
+ // JsonSchemaModule resets the registry at app ready and only re-registers
62
+ // schemas from app.dependencies — plugin schemas would otherwise be lost.
63
+ jsonschema.registerSchemasHook.tap(() => this.reregisterPluginSchemas())
64
+
59
65
  try {
60
66
  await this.initPlugins()
61
67
  } catch (e) {
@@ -114,8 +120,8 @@ class ContentPluginModule extends AbstractApiModule {
114
120
  const pluginData = await this.findOne({ _id })
115
121
  // unregister any schemas
116
122
  const jsonschema = await this.app.waitForModule('jsonschema')
117
- const schemas = this.pluginSchemas[pluginData.name] ?? []
118
- schemas.forEach(s => jsonschema.deregisterSchema(s))
123
+ const schemas = this.pluginSchemas[pluginData.name] ?? {}
124
+ Object.keys(schemas).forEach(s => jsonschema.deregisterSchema(s))
119
125
  delete this.pluginSchemas[pluginData.name]
120
126
 
121
127
  await this.framework.runCliCommand('uninstallPlugins', { plugins: [pluginData.name] })
@@ -213,9 +219,9 @@ class ContentPluginModule extends AbstractApiModule {
213
219
  const jsonschema = await this.app.waitForModule('jsonschema')
214
220
  return Promise.all(pluginInfo.map(async plugin => {
215
221
  const name = plugin.name
216
- const oldSchemaPaths = this.pluginSchemas[name]
217
- if (oldSchemaPaths) {
218
- Object.values(oldSchemaPaths).forEach(s => jsonschema.deregisterSchema(s))
222
+ const existing = this.pluginSchemas[name]
223
+ if (existing) {
224
+ Object.keys(existing).forEach(s => jsonschema.deregisterSchema(s))
219
225
  delete this.pluginSchemas[name]
220
226
  }
221
227
  const schemaPaths = await plugin.getSchemaPaths()
@@ -223,15 +229,31 @@ class ContentPluginModule extends AbstractApiModule {
223
229
  const schema = await this.readJson(schemaPath)
224
230
  const source = schema?.$patch?.source?.$ref
225
231
  if (source) {
226
- if (!this.pluginSchemas[name]) this.pluginSchemas[name] = []
227
- if (this.pluginSchemas[name].includes(schema.$anchor)) jsonschema.deregisterSchema(this.pluginSchemas[name][source])
228
- this.pluginSchemas[name].push(schema.$anchor)
232
+ if (!this.pluginSchemas[name]) this.pluginSchemas[name] = {}
233
+ this.pluginSchemas[name][schema.$anchor] = schemaPath
229
234
  }
230
235
  return jsonschema.registerSchema(schemaPath, { replace: true })
231
236
  }))
232
237
  }))
233
238
  }
234
239
 
240
+ /**
241
+ * Re-registers tracked plugin schemas (called via JsonSchemaModule.registerSchemasHook)
242
+ * @return {Promise}
243
+ */
244
+ async reregisterPluginSchemas () {
245
+ const jsonschema = await this.app.waitForModule('jsonschema')
246
+ for (const schemas of Object.values(this.pluginSchemas)) {
247
+ for (const schemaPath of Object.values(schemas)) {
248
+ try {
249
+ jsonschema.registerSchema(schemaPath, { replace: true })
250
+ } catch (e) {
251
+ this.log('warn', `failed to re-register plugin schema ${schemaPath}`, e)
252
+ }
253
+ }
254
+ }
255
+ }
256
+
235
257
  /**
236
258
  * Returns whether a schema is registered by a plugin
237
259
  * @param {String} schemaName Name of the schema to check
@@ -239,17 +261,17 @@ class ContentPluginModule extends AbstractApiModule {
239
261
  */
240
262
  isPluginSchema (schemaName) {
241
263
  for (const p in this.pluginSchemas) {
242
- if (this.pluginSchemas[p].includes(schemaName)) return true
264
+ if (schemaName in this.pluginSchemas[p]) return true
243
265
  }
244
266
  }
245
267
 
246
268
  /**
247
269
  * Returns all schemas registered by a plugin
248
270
  * @param {String} pluginName Plugin name
249
- * @return {Array} List of the plugin's registered schemas
271
+ * @return {Array} List of the plugin's registered schema $anchors
250
272
  */
251
273
  getPluginSchemas (pluginName) {
252
- return this.pluginSchemas[pluginName] ?? []
274
+ return Object.keys(this.pluginSchemas[pluginName] ?? {})
253
275
  }
254
276
 
255
277
  /**
@@ -393,9 +415,20 @@ class ContentPluginModule extends AbstractApiModule {
393
415
  */
394
416
  async updatePlugin (_id) {
395
417
  const [{ name }] = await this.find({ _id })
418
+ const { readFrameworkPluginVersions } = await import('adapt-authoring-adaptframework')
419
+ const fromPlugins = await readFrameworkPluginVersions(this.framework.path)
396
420
  const [pluginData] = await this.framework.runCliCommand('updatePlugins', { plugins: [name] })
397
421
  const p = await this.update({ name }, pluginData._sourceInfo)
398
422
  await this.processPluginSchemas(pluginData)
423
+ const toPlugins = await readFrameworkPluginVersions(this.framework.path)
424
+ const courses = await this.getPluginUses(_id)
425
+ if (courses.length) {
426
+ await this.framework.migrateCourses({
427
+ fromPlugins,
428
+ toPlugins,
429
+ courseIds: courses.map(c => c._id)
430
+ })
431
+ }
399
432
  this.log('info', `successfully updated plugin ${p.name}@${p.version}`)
400
433
  return p
401
434
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adapt-authoring-contentplugin",
3
- "version": "1.6.1",
3
+ "version": "1.7.1",
4
4
  "description": "Module for managing framework plugins",
5
5
  "homepage": "https://github.com/adapt-security/adapt-authoring-contentplugin",
6
6
  "repository": "github:adapt-security/adapt-authoring-contentplugin",
@@ -11,13 +11,13 @@
11
11
  "type": "module",
12
12
  "dependencies": {
13
13
  "adapt-authoring-api": "^3.0.0",
14
- "adapt-authoring-core": "^2.0.0",
14
+ "adapt-authoring-core": "^3.0.0",
15
15
  "adapt-cli": "^3.3.3",
16
16
  "semver": "^7.6.0"
17
17
  },
18
18
  "peerDependencies": {
19
- "adapt-authoring-adaptframework": "^2.0.0",
20
- "adapt-authoring-content": "^2.0.0",
19
+ "adapt-authoring-adaptframework": "^3.0.0",
20
+ "adapt-authoring-content": "^3.0.0",
21
21
  "adapt-authoring-jsonschema": "^1.2.0",
22
22
  "adapt-authoring-middleware": "^1.0.2",
23
23
  "adapt-authoring-mongodb": "^3.0.0",