adapt-authoring-adaptframework 2.3.4 → 2.5.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.
@@ -1,5 +1,8 @@
1
1
  {
2
2
  "documentation": {
3
3
  "enable": true
4
+ },
5
+ "framework": {
6
+ "targetVersion": 5
4
7
  }
5
8
  }
@@ -126,5 +126,14 @@
126
126
  },
127
127
  "description": "Plugin is missing a dependency",
128
128
  "statusCode": 500
129
+ },
130
+ "FW_VERSION_NOT_ALLOWED": {
131
+ "data": {
132
+ "version": "The version that is not allowed",
133
+ "targetMajorVersion": "The allowed major version number",
134
+ "allowedRange": "The allowed semver range"
135
+ },
136
+ "description": "Framework version is outside the allowed major version",
137
+ "statusCode": 400
129
138
  }
130
139
  }
@@ -40,11 +40,12 @@ class AdaptFrameworkBuild {
40
40
  * @property {String} userId The user executing the build
41
41
  * @property {String} expiresAt When the build expires
42
42
  * @property {Boolean} compress Whether output files should be compressed into an archive file
43
+ * @property {String} outputDir If set, uses this as the build root. If the directory already exists, only content data and assets are written (framework copy and compilation are skipped)
43
44
  *
44
45
  * @constructor
45
46
  * @param {AdaptFrameworkBuildOptions} options
46
47
  */
47
- constructor ({ action, courseId, userId, expiresAt, compress }) {
48
+ constructor ({ action, courseId, userId, expiresAt, compress, outputDir }) {
48
49
  /**
49
50
  * The MongoDB collection name
50
51
  * @type {String}
@@ -150,6 +151,11 @@ class AdaptFrameworkBuild {
150
151
  * @type {Hook}
151
152
  */
152
153
  this.postBuildHook = new Hook({ mutable: true })
154
+ /**
155
+ * Custom output directory. If the directory already exists, only content and assets are written
156
+ * @type {String}
157
+ */
158
+ this.outputDir = outputDir ?? null
153
159
  }
154
160
 
155
161
  /**
@@ -164,17 +170,26 @@ class AdaptFrameworkBuild {
164
170
  if (!this.expiresAt) {
165
171
  this.expiresAt = await AdaptFrameworkBuild.getBuildExpiry()
166
172
  }
167
- // random suffix to account for parallel builds executed at exactly the same millisecond
168
- const randomSuffix = `_${Math.floor(Math.random() * 1000).toString().padStart(4, '0')}`
169
- this.dir = path.resolve(framework.getConfig('buildDir'), Date.now() + randomSuffix)
173
+ if (this.outputDir) {
174
+ this.dir = this.outputDir
175
+ } else {
176
+ // random suffix to account for parallel builds executed at exactly the same millisecond
177
+ const randomSuffix = `_${Math.floor(Math.random() * 1000).toString().padStart(4, '0')}`
178
+ this.dir = path.resolve(framework.getConfig('buildDir'), Date.now() + randomSuffix)
179
+ }
170
180
  this.buildDir = path.join(this.dir, 'build')
171
181
  this.courseDir = path.join(this.buildDir, 'course')
172
182
 
183
+ const dirExists = await fs.access(this.dir).then(() => true, () => false)
184
+ const contentOnly = this.outputDir && dirExists
185
+
173
186
  const cacheDir = path.join(framework.getConfig('buildDir'), 'cache')
174
187
 
175
188
  await ensureDir(this.dir)
176
189
  await ensureDir(this.buildDir)
177
- await ensureDir(cacheDir)
190
+ if (!contentOnly) {
191
+ await ensureDir(cacheDir)
192
+ }
178
193
 
179
194
  logDir('dir', this.dir)
180
195
  logDir('buildDir', this.buildDir)
@@ -182,21 +197,23 @@ class AdaptFrameworkBuild {
182
197
 
183
198
  await this.loadCourseData()
184
199
 
185
- await Promise.all([
186
- this.copyAssets(),
187
- copyFrameworkSource({
200
+ const tasks = [this.copyAssets()]
201
+ if (!contentOnly) {
202
+ tasks.push(copyFrameworkSource({
188
203
  destDir: this.dir,
189
204
  enabledPlugins: this.enabledPlugins.map(p => p.name),
190
205
  linkNodeModules: !this.isExport
191
- })
192
- ])
206
+ }))
207
+ }
208
+ await Promise.all(tasks)
209
+
193
210
  await this.preBuildHook.invoke(this)
194
211
 
195
212
  await this.writeContentJson()
196
213
 
197
214
  logDir('courseDir', this.courseDir)
198
215
 
199
- if (!this.isExport) {
216
+ if (!contentOnly && !this.isExport) {
200
217
  try {
201
218
  logMemory()
202
219
  await AdaptCli.buildCourse({
@@ -70,6 +70,13 @@ class AdaptFrameworkModule extends AbstractModule {
70
70
  */
71
71
  this.contentMigrations = []
72
72
 
73
+ const meta = await readJson(path.resolve(this.rootDir, 'adapt-authoring.json'))
74
+ /**
75
+ * The major version of the Adapt framework this module is designed to work with
76
+ * @type {Number}
77
+ */
78
+ this._targetFrameworkVersion = meta.framework?.targetVersion
79
+
73
80
  this.app.waitForModule('content').then(content => {
74
81
  content.accessCheckHook.tap(this.checkContentAccess.bind(this))
75
82
  })
@@ -103,6 +110,36 @@ class AdaptFrameworkModule extends AbstractModule {
103
110
  return this._version
104
111
  }
105
112
 
113
+ /**
114
+ * The major version of the Adapt framework this module is designed to work with
115
+ * @type {Number|undefined}
116
+ */
117
+ get targetFrameworkVersion () {
118
+ return this._targetFrameworkVersion
119
+ }
120
+
121
+ /**
122
+ * Returns a semver range string constrained to the target major version, or undefined if no target is set
123
+ * @type {String|undefined}
124
+ */
125
+ get targetVersionRange () {
126
+ if (this._targetFrameworkVersion === undefined) return undefined
127
+ return `>=${this._targetFrameworkVersion}.0.0 <${this._targetFrameworkVersion + 1}.0.0`
128
+ }
129
+
130
+ /**
131
+ * Checks whether the given version is compatible with the configured target major version
132
+ * @param {string} version Semver version string to check
133
+ * @throws If the version's major does not match the target major version
134
+ */
135
+ checkVersionCompatibility (version) {
136
+ if (this._targetFrameworkVersion === undefined) return
137
+ const major = semver.major(version)
138
+ if (major !== this._targetFrameworkVersion) {
139
+ throw this.app.errors.FW_VERSION_NOT_ALLOWED.setData({ version, targetMajorVersion: this._targetFrameworkVersion, allowedRange: this.targetVersionRange })
140
+ }
141
+ }
142
+
106
143
  /**
107
144
  * Installs a local copy of the Adapt framework
108
145
  * @return {Promise}
@@ -121,10 +158,13 @@ class AdaptFrameworkModule extends AbstractModule {
121
158
  } catch (e) {
122
159
  // package is missing, an install is required
123
160
  }
124
- await this.runCliCommand('installFramework', { version })
161
+ if (version) {
162
+ this.checkVersionCompatibility(version)
163
+ }
164
+ await this.runCliCommand('installFramework', { version: version ?? this.targetVersionRange })
125
165
  } catch (e) {
126
166
  this.log('error', `failed to install framework, ${e.message}`)
127
- throw this.app.errors.FW_INSTALL_FAILED.setData({ reason: e.message })
167
+ throw e.statusCode ? e : this.app.errors.FW_INSTALL_FAILED.setData({ reason: e.message })
128
168
  }
129
169
  this.log('verbose', 'INSTALL hook invoke')
130
170
  await this.postInstallHook.invoke()
@@ -136,7 +176,7 @@ class AdaptFrameworkModule extends AbstractModule {
136
176
  */
137
177
  async getLatestVersion () {
138
178
  try {
139
- return semver.clean(await this.runCliCommand('getLatestFrameworkVersion'))
179
+ return semver.clean(await this.runCliCommand('getLatestFrameworkVersion', { version: this.targetVersionRange }))
140
180
  } catch (e) {
141
181
  this.log('error', `failed to retrieve framework update data, ${e.message}`)
142
182
  throw this.app.errors.FW_LATEST_VERSION_FAILED.setData({ reason: e.message })
@@ -167,11 +207,14 @@ class AdaptFrameworkModule extends AbstractModule {
167
207
  */
168
208
  async updateFramework (version) {
169
209
  try {
170
- await this.runCliCommand('updateFramework', { version })
210
+ if (version) {
211
+ this.checkVersionCompatibility(version)
212
+ }
213
+ await this.runCliCommand('updateFramework', { version: version ?? this.targetVersionRange })
171
214
  this._version = await this.runCliCommand('getCurrentFrameworkVersion')
172
215
  } catch (e) {
173
216
  this.log('error', `failed to update framework, ${e.message}`)
174
- throw this.app.errors.FW_UPDATE_FAILED.setData({ reason: e.message })
217
+ throw e.statusCode ? e : this.app.errors.FW_UPDATE_FAILED.setData({ reason: e.message })
175
218
  }
176
219
  this.postUpdateHook.invoke()
177
220
  }
@@ -181,7 +224,7 @@ class AdaptFrameworkModule extends AbstractModule {
181
224
  */
182
225
  async logStatus () {
183
226
  const current = this.version
184
- const latest = await this.runCliCommand('getLatestFrameworkVersion')
227
+ const latest = await this.getLatestVersion()
185
228
 
186
229
  this.log('info', `local adapt_framework v${current} installed`)
187
230
  if (semver.lt(current, latest)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adapt-authoring-adaptframework",
3
- "version": "2.3.4",
3
+ "version": "2.5.0",
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",