Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions lib/ContentPluginModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,15 +348,159 @@ class ContentPluginModule extends AbstractApiModule {
return pkg
}

/**
* Loads JSON data into relevant directories
*/
async writeCourseData () {
return Promise.all(Object.keys(this.courseData).map(async (type) => {
const { dir, fileName, data } = this.courseData[type]
if (data) {
const filePath = path.join(dir, fileName)
const pathDir = await fs.access(dir).then(() => true).catch(() => false)
if (!pathDir) await fs.mkdir(dir, { recursive: true })
await fs.writeFile(filePath, JSON.stringify(data, null, 2))
}
}));
}

/**
* Run grunt task
* @return {Promise}
*/
runGruntTask (subTask, { outputDir, captureDir, file }) {
const args = [
'npx grunt migration:' + subTask,
outputDir ? `--outputdir=${outputDir}` : '',
captureDir ? `--capturedir=${captureDir}` : '',
file ? `--file=${file}` : ''
].filter(Boolean).join(' ');
return this.execPromise(args);
}

async execPromise (task) {
this.log('debug', 'EXEC', task)
return new Promise((resolve, reject) => {
exec(task, { cwd: this.framework.path }, (error, stdout, stderr) => {
if (stdout) this.log('debug', 'EXEC_STDOUT', task, stdout)
if (stderr) this.log('debug', 'EXEC_STDERR', task, stderr)
error ? reject(error) : resolve(stdout)
})
})
}

/**
* Finds all courses using a plugin, saves data to the course directory and runs grunt:migration capture
* @param {String} _id + name of the plugin being updated
* @return Returns promise to find/save course data and run grunt:migration capture
*/
async capturePluginCourses (_id, name) {
const content = await this.app.waitForModule('content')
const courses = await this.getPluginUses(_id)
if (!courses.length) return

await Promise.all(courses.map(async c => {
const contentItems = await content.find({ _courseId: c._id })
if (!contentItems.length) return

const outputDir = path.join(this.framework.path, 'src/course', c._id.toString())
const courseDir = path.join(outputDir, 'course')
const langDir = path.join(courseDir, 'en')

this.courseData = {
course: { dir: langDir, fileName: 'course.json', data: undefined },
config: { dir: courseDir, fileName: 'config.json', data: undefined },
contentObject: { dir: langDir, fileName: 'contentObjects.json', data: [] },
article: { dir: langDir, fileName: 'articles.json', data: [] },
block: { dir: langDir, fileName: 'blocks.json', data: [] },
component: { dir: langDir, fileName: 'components.json', data: [] }
}

contentItems.forEach(item => {
const type = item._type === 'page' || item._type === 'menu' ? 'contentObject' : item._type;
if (typeof this.courseData[type].data === 'object') {
this.courseData[type].data.push(item);
return
}
this.courseData[type].data = item
})

await this.writeCourseData()
const folderName = `${c._id}-migrations`
const captureDir = path.join('./', folderName)
const opts = { outputDir: outputDir, captureDir: captureDir, file: name }

this.capturedCourses.push({
outputDir: outputDir,
captureDir: captureDir,
courseDir,
courseId: c._id,
})

await this.runGruntTask('capture', opts)
}));
}

/**
* Takes an array of filePaths and extracts the JSON data from each file
* @param {String} filePaths array of file paths to JSON files
* @return Returns all course data in an array
*/
async loadJsonData (filePaths) {
const data = [];
for (const filePath of filePaths) {
const content = await fs.readFile(filePath, 'utf8')
const data = JSON.parse(content)
if (Array.isArray(data)) {
data.push(...data)
} else {
data.push(data)
}
}
return data;
}

/**
* Migrates each course within this.capturedCourses
* @param {String} _id The _id for the plugin to update and name of the plugin
* @return Resolves with logging confirmation
*/
async migratePluginCourses (_id, name) {
if (!this.capturedCourses.length) return
const content = await this.app.waitForModule('content')
await Promise.all(this.capturedCourses.map(async c => {
const opts = { outputDir: c.outputDir, captureDir: c.captureDir, file: name }
await this.runGruntTask('migrate', opts)

const filePaths = Array.from(await new Promise(resolve => {
globs([
'**/*.json',
'*.json'
], { cwd: path.join(c.outputDir, 'course'), absolute: true }, (err, files) => resolve(err ? null : files))
})).filter((file, index, self) => self.indexOf(file) === index)
const contentItems = await this.loadJsonData(filePaths);

contentItems.forEach(async item => {
await content.update({ _id: item._id }, item)
});

const captureDir = path.join(this.framework.path, c.captureDir)
await fs.rm(c.outputDir, { recursive: true, force: true })
await fs.rm(captureDir, { recursive: true, force: true })
return this.log('info', `successfully updated course ${c.courseId} with plugin ${name}`)
}));
}

/**
* Updates a single plugin
* @param {String} _id The _id for the plugin to update
* @return Resolves with update data
*/
async updatePlugin (_id) {
const [{ name }] = await this.find({ _id })
await this.capturePluginCourses(_id, name)
const [pluginData] = await this.framework.runCliCommand({ plugins: [name] })
const p = await this.update({ name }, pluginData._sourceInfo)
await this.migratePluginCourses(_id, name)
this.log('info', `successfully updated plugin ${p.name}@${p.version}`)
return p
}
Expand Down