adapt-authoring-adaptframework 1.10.1 → 1.10.3

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.
@@ -138,6 +138,11 @@ class AdaptFrameworkImport {
138
138
  * @type {Object}
139
139
  */
140
140
  this.newContentPlugins = {}
141
+ /**
142
+ * Plugins that were updated during import with their original metadata for rollback
143
+ * @type {Object}
144
+ */
145
+ this.updatedContentPlugins = {}
141
146
  /**
142
147
  * Key/value store mapping old component keys to component names
143
148
  * @type {Object}
@@ -158,6 +163,11 @@ class AdaptFrameworkImport {
158
163
  * @type {String}
159
164
  */
160
165
  this.userId = userId
166
+ /**
167
+ * Array of tag IDs created during import for rollback
168
+ * @type {Array<String>}
169
+ */
170
+ this.newTagIds = []
161
171
  /**
162
172
  * Contains non-fatal infomation messages regarding import status which can be return as response data. Fatal errors are thrown in the usual way.
163
173
  * @type {Object}
@@ -548,6 +558,7 @@ class AdaptFrameworkImport {
548
558
  await Promise.all(Array.from(newTags).map(async n => {
549
559
  const { _id } = await tags.insert({ title: n })
550
560
  existingTagMap[n] = _id.toString()
561
+ this.newTagIds.push(_id.toString())
551
562
  }))
552
563
  // map tags from titles to new _ids
553
564
  this.tags = this.tags.map(t => existingTagMap[t])
@@ -652,6 +663,11 @@ class AdaptFrameworkImport {
652
663
  const errors = []
653
664
  await Promise.all([...pluginsToInstall, ...pluginsToUpdate].map(async p => {
654
665
  try {
666
+ // Store original plugin metadata for updates before overwriting
667
+ const isUpdate = pluginsToUpdate.includes(p)
668
+ if (isUpdate && this.installedPlugins[p]) {
669
+ this.updatedContentPlugins[p] = this.installedPlugins[p]
670
+ }
655
671
  // try and infer a targetAttribute if there isn't one
656
672
  const pluginBowerPath = path.join(this.usedContentPlugins[p].path, 'bower.json')
657
673
  const bowerJson = await FWUtils.readJson(pluginBowerPath)
@@ -661,7 +677,9 @@ class AdaptFrameworkImport {
661
677
  }
662
678
  if (!this.settings.isDryRun) {
663
679
  const [pluginData] = await this.contentplugin.installPlugins([[p, this.usedContentPlugins[p].path]], { strict: true })
664
- this.newContentPlugins[p] = pluginData
680
+ if (!isUpdate) {
681
+ this.newContentPlugins[p] = pluginData
682
+ }
665
683
  }
666
684
  this.statusReport.info.push({ code: 'INSTALL_PLUGIN', data: { name: p, version: bowerJson.version } })
667
685
  } catch (e) {
@@ -690,8 +708,10 @@ class AdaptFrameworkImport {
690
708
  */
691
709
  async importCourseData () {
692
710
  const formatError = e => {
693
- if (e?.data?.schemaName) return { schemaName: e.data.schemaName, id: e.data.data?._id, errors: e.data.errors }
694
- return { message: App.instance.lang.translate(undefined, e) }
711
+ if (e?.data?.schemaName) {
712
+ return `${e.data.schemaName}${e.data.data?._id ? ` (${e.data.data._id})` : ''}: ${e.data.errors ?? e.message}`
713
+ }
714
+ return e?.toString() ?? String(e)
695
715
  }
696
716
  /**
697
717
  * Note: the execution order is important here
@@ -840,14 +860,21 @@ class AdaptFrameworkImport {
840
860
  return
841
861
  }
842
862
  try {
843
- const tasks = [
844
- fs.rm(this.path, { recursive: true })
845
- ]
863
+ const tasks = [fs.rm(this.path, { recursive: true })]
846
864
  if (error) {
847
- tasks.push(
848
- Promise.all(Object.values(this.newContentPlugins).map(p => this.contentplugin.uninstallPlugin(p._id))),
849
- Promise.all(Object.values(this.assetMap).map(a => this.assets.delete({ _id: a })))
850
- )
865
+ // Uninstall newly installed plugins
866
+ tasks.push(Promise.all(Object.values(this.newContentPlugins).map(p => this.contentplugin.uninstallPlugin(p._id))))
867
+ // Restore updated plugins to their original versions
868
+ if (Object.keys(this.updatedContentPlugins).length > 0) {
869
+ tasks.push(this.restoreUpdatedPlugins())
870
+ }
871
+ // Delete imported assets
872
+ tasks.push(Promise.all(Object.values(this.assetMap).map(a => this.assets.delete({ _id: a }))))
873
+ // Delete newly created tags
874
+ if (this.newTagIds.length > 0) {
875
+ const tags = await App.instance.waitForModule('tags')
876
+ tasks.push(Promise.all(this.newTagIds.map(id => tags.delete({ _id: id }))))
877
+ }
851
878
  let _courseId
852
879
  try {
853
880
  const { ObjectId } = await App.instance.waitForModule('mongodb')
@@ -863,6 +890,26 @@ class AdaptFrameworkImport {
863
890
  await Promise.allSettled(tasks)
864
891
  } catch (e) {} // ignore any thrown errors
865
892
  }
893
+
894
+ /**
895
+ * Restores plugins that were updated during import to their original versions
896
+ * Uses ContentPluginModule's restorePluginFromBackup to restore from cached backups
897
+ * @return {Promise}
898
+ */
899
+ async restoreUpdatedPlugins () {
900
+ const pluginNames = Object.keys(this.updatedContentPlugins)
901
+ if (pluginNames.length === 0) return Promise.resolve()
902
+
903
+ const restoreTasks = []
904
+ for (const [pluginName, originalMetadata] of Object.entries(this.updatedContentPlugins)) {
905
+ FWUtils.log('info', `restoring plugin '${pluginName}' to previous version ${originalMetadata.version}`)
906
+ restoreTasks.push(
907
+ this.contentplugin.restorePluginFromBackup(pluginName)
908
+ .catch(e => FWUtils.log('error', `failed to restore plugin '${pluginName}' from backup, ${e.message}`))
909
+ )
910
+ }
911
+ return Promise.allSettled(restoreTasks)
912
+ }
866
913
  }
867
914
 
868
915
  export default AdaptFrameworkImport
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adapt-authoring-adaptframework",
3
- "version": "1.10.1",
3
+ "version": "1.10.3",
4
4
  "description": "Adapt framework integration for the Adapt authoring tool",
5
5
  "homepage": "https://github.com/adapt-security/adapt-authoring-adaptframework",
6
6
  "license": "GPL-3.0",