diff --git a/src/common/updates/Updates.js b/src/common/updates/Updates.js new file mode 100644 index 000000000..39b11b882 --- /dev/null +++ b/src/common/updates/Updates.js @@ -0,0 +1,71 @@ +/* globals define */ +define([ + 'q' +], function( + Q +) { + + const allUpdates = [ + { + name: 'CustomUtilities', + isNeeded: function(core, rootNode) { + // Check the root directory for a MyUtilities node + return core.loadChildren(rootNode) + .then(children => { + const names = children.map(node => core.getAttribute(node, 'name')); + return !names.includes('MyUtilities'); + }); + }, + apply: function(core, rootNode, META) { + // Create 'MyUtilities' node + const utils = core.createNode({ + parent: rootNode, + base: META.FCO + }); + core.setAttribute(utils, 'name', 'MyUtilities'); + + // Add 'MyUtilities' to the META + const META_ASPECT_SET_NAME = 'MetaAspectSet'; + const META_SHEETS = 'MetaSheets'; + const tabId = core.getRegistry(rootNode, META_SHEETS) + .find(desc => desc.order === 0) + .SetID; + + core.addMember(rootNode, META_ASPECT_SET_NAME, utils); + core.addMember(rootNode, tabId, utils); + + // Add 'Code' from 'pipelines' as a valid child + core.setChildMeta(utils, META['pipeline.Code']); + + // Set the default visualizer to TabbedTextEditor + core.setRegistry(utils, 'validVisualizers', 'TabbedTextEditor'); + } + } + ]; + + const Updates = {}; + + Updates.getAvailableUpdates = function(core, rootNode) { + return Q.all(allUpdates.map(update => update.isNeeded(core, rootNode))) + .then(isNeeded => { + const updates = allUpdates.filter((update, i) => isNeeded[i]); + return updates; + }); + }; + + Updates.getUpdates = function(names) { + if (names) { + return allUpdates.filter(update => names.includes(update.name)); + } + return allUpdates; + }; + + Updates.getUpdate = function(name) { + return Updates.getUpdates([name])[0]; + }; + + // Constants + Updates.MIGRATION = 'Migration'; + Updates.SEED = 'SeedUpdate'; + return Updates; +}); diff --git a/src/plugins/ApplyUpdates/ApplyUpdates.js b/src/plugins/ApplyUpdates/ApplyUpdates.js new file mode 100644 index 000000000..e95e56de4 --- /dev/null +++ b/src/plugins/ApplyUpdates/ApplyUpdates.js @@ -0,0 +1,77 @@ +/*globals define*/ +/*eslint-env node, browser*/ + +define([ + 'deepforge/updates/Updates', + 'text!./metadata.json', + 'plugin/PluginBase' +], function ( + Updates, + pluginMetadata, + PluginBase +) { + 'use strict'; + + pluginMetadata = JSON.parse(pluginMetadata); + + /** + * Initializes a new instance of ApplyUpdates. + * @class + * @augments {PluginBase} + * @classdesc This class represents the plugin ApplyUpdates. + * @constructor + */ + var ApplyUpdates = function () { + // Call base class' constructor. + PluginBase.call(this); + this.pluginMetadata = pluginMetadata; + }; + + /** + * Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc. + * This is also available at the instance at this.pluginMetadata. + * @type {object} + */ + ApplyUpdates.metadata = pluginMetadata; + + // Prototypical inheritance from PluginBase. + ApplyUpdates.prototype = Object.create(PluginBase.prototype); + ApplyUpdates.prototype.constructor = ApplyUpdates; + + /** + * Main function for the plugin to execute. This will perform the execution. + * Notes: + * - Always log with the provided logger.[error,warning,info,debug]. + * - Do NOT put any user interaction logic UI, etc. inside this method. + * - callback always has to be called even if error happened. + * + * @param {function(string, plugin.PluginResult)} callback - the result callback + */ + ApplyUpdates.prototype.main = async function (callback) { + // Retrieve the updates to apply + const config = this.getCurrentConfig(); + const updateNames = config.updates || []; + + if (!updateNames.length) { + this.result.setSuccess(true); + return callback(null, this.result); + } + + // Apply each of the updates + const updates = Updates.getUpdates(updateNames); + + for (let i = 0, len = updates.length; i < len; i++) { + const update = updates[i]; + this.logger.info(`Applying update: ${update.name} to ${this.projectId}`); + await update.apply(this.core, this.rootNode, this.META); + } + + // Save the project + await this.save(`Applied project updates: ${updateNames.join(",")}`); + + this.result.setSuccess(true); + callback(null, this.result); + }; + + return ApplyUpdates; +}); diff --git a/src/plugins/ApplyUpdates/metadata.json b/src/plugins/ApplyUpdates/metadata.json new file mode 100644 index 000000000..10ceff7b7 --- /dev/null +++ b/src/plugins/ApplyUpdates/metadata.json @@ -0,0 +1,14 @@ +{ + "id": "ApplyUpdates", + "name": "ApplyUpdates", + "version": "0.1.0", + "description": "", + "icon": { + "class": "glyphicon glyphicon-cog", + "src": "" + }, + "disableServerSideExecution": false, + "disableBrowserSideExecution": false, + "writeAccessRequired": false, + "configStructure": [] +} \ No newline at end of file diff --git a/src/plugins/CheckLibraries/CheckLibraries.js b/src/plugins/CheckLibraries/CheckLibraries.js deleted file mode 100644 index ebacc1d45..000000000 --- a/src/plugins/CheckLibraries/CheckLibraries.js +++ /dev/null @@ -1,178 +0,0 @@ -/*globals define*/ -/*jshint node:true, browser:true*/ - -define([ - 'plugin/UploadSeedToBlob/UploadSeedToBlob/UploadSeedToBlob', - './metadata.json', - 'module', - 'path', - 'fs', - 'q' -], function ( - PluginBase, - pluginMetadata, - module, - path, - fs, - Q -) { - 'use strict'; - - /** - * Initializes a new instance of CheckLibraries. - * @class - * @augments {PluginBase} - * @classdesc This class represents the plugin CheckLibraries. - * @constructor - */ - var CheckLibraries = function () { - // Call base class' constructor. - PluginBase.call(this); - this.pluginMetadata = pluginMetadata; - this.libraries = {}; - }; - - /** - * Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc. - * This is also available at the instance at this.pluginMetadata. - * @type {object} - */ - CheckLibraries.metadata = pluginMetadata; - - // Prototypical inheritance from PluginBase. - CheckLibraries.prototype = Object.create(PluginBase.prototype); - CheckLibraries.prototype.constructor = CheckLibraries; - - /** - * Main function for the plugin to execute. This will perform the execution. - * Notes: - * - Always log with the provided logger.[error,warning,info,debug]. - * - Do NOT put any user interaction logic UI, etc. inside this method. - * - callback always has to be called even if error happened. - * - * @param {function(string, plugin.PluginResult)} callback - the result callback - */ - CheckLibraries.prototype.main = function (callback) { - var tuples; - - return this.getAllLibraries() - .then(libs => { - tuples = libs - .map(lib => { // map to [name, version, dir] - var version, - hash, - data, - versionPath = this.getSeedVersionPath(lib); - - try { - this.logger.info(`Checking for version info at ${versionPath}`); - version = fs.readFileSync(versionPath, 'utf8'); - this.logger.debug(`${lib} version is ${version}`); - data = fs.readFileSync(this.getSeedHashPath(lib), 'utf8').split(' '); - if (data[1] === version) { - hash = data[0]; - this.logger.debug(`${lib} hash is ${hash}`); - } - } catch (e) { - if (!version) { - this.logger.warn(`Could not find library version for ${lib}`); - } else { - this.logger.warn(`Could not find library hash for ${lib}`); - } - } - - return [lib, version, hash]; - }) - .filter(tuple => { // get only the libs w/ updates available - let [lib, version] = tuple; - - if (!version) return false; - - let projVersion = this.getLoadedVersion(lib); - let latest = version.replace(/\s+/g, ''); - - this.logger.info(`${lib} version info:\n${projVersion} ` + - `(project)\n${latest} (latest)`); - return projVersion < latest; - }); - - return Q.all(tuples.map(tuple => this.uploadSeed.apply(this, tuple))); - }) - .then(hashes => { - var name; - - for (var i = hashes.length; i--;) { - name = tuples[i][0]; - this.createMessage(this.libraries[name], `${name} ${hashes[i]}`); - } - - this.logger.info(`Found ${hashes.length} out of date libraries`); - this.result.setSuccess(true); - callback(null, this.result); - }) - .fail(err => { - this.logger.error(`Could not check the libraries: ${err}`); - callback(err, this.result); - }); - }; - - CheckLibraries.prototype.getSeedHashPath = function (name) { - return path.join(this.getSeedDir(name), 'hash.txt'); - }; - - CheckLibraries.prototype.getSeedVersionPath = function (name) { - return path.join(this.getSeedDir(name), 'version.txt'); - }; - - CheckLibraries.prototype.upgradeSeedToVersion = function (name, version, hash) { - if (!hash) { // Upload the seed - // Get the data - this.logger.info(`Uploading new version of ${name} (${version})`); - return this.uploadSeed(name) - .then(newHash => { // Store the new hash - this.logger.info(`Upload of ${name} finished!`); - hash = newHash; - return Q.nfcall( - fs.writeFile, - this.getSeedHashPath(name), - `${hash} ${version}` - ); - }).then(() => hash); - } - return hash; - }; - - CheckLibraries.prototype.getAllLibraries = function () { - const DEFAULT_LIBRARIES = ['pipeline']; - var name, - names = []; - - return this.core.loadChildren(this.rootNode) - .then(children => { - for (var i = children.length; i--;) { - if (this.core.isLibraryRoot(children[i])) { - name = this.core.getAttribute(children[i], 'name'); - this.libraries[name] = children[i]; - if (DEFAULT_LIBRARIES.includes(name)) { - names.push(name); - } - } - } - if (names.length) { - this.logger.debug(`Found libraries: ${names.join(', ')}`); - } else { - this.logger.debug('Found no libraries!'); - } - return names; - }); - }; - - CheckLibraries.prototype.getLoadedVersion = function (libName) { - var node = this.libraries[libName], - version = this.core.getAttribute(node, 'version'); // using library root hash - - return version; - }; - - return CheckLibraries; -}); diff --git a/src/plugins/CheckUpdates/CheckUpdates.js b/src/plugins/CheckUpdates/CheckUpdates.js new file mode 100644 index 000000000..5e7994a2e --- /dev/null +++ b/src/plugins/CheckUpdates/CheckUpdates.js @@ -0,0 +1,203 @@ +/*globals define*/ +/*jshint node:true, browser:true*/ + +define([ + 'deepforge/updates/Updates', + 'plugin/UploadSeedToBlob/UploadSeedToBlob/UploadSeedToBlob', + './metadata.json', + 'module', + 'path', + 'fs', + 'q' +], function ( + Updates, + PluginBase, + pluginMetadata, + module, + path, + fs, + Q +) { + 'use strict'; + + /** + * Initializes a new instance of CheckUpdates. + * @class + * @augments {PluginBase} + * @classdesc This class represents the plugin CheckUpdates. + * @constructor + */ + var CheckUpdates = function () { + // Call base class' constructor. + PluginBase.call(this); + this.pluginMetadata = pluginMetadata; + this.libraries = {}; + }; + + /** + * Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc. + * This is also available at the instance at this.pluginMetadata. + * @type {object} + */ + CheckUpdates.metadata = pluginMetadata; + + // Prototypical inheritance from PluginBase. + CheckUpdates.prototype = Object.create(PluginBase.prototype); + CheckUpdates.prototype.constructor = CheckUpdates; + + /** + * Main function for the plugin to execute. This will perform the execution. + * Notes: + * - Always log with the provided logger.[error,warning,info,debug]. + * - Do NOT put any user interaction logic UI, etc. inside this method. + * - callback always has to be called even if error happened. + * + * @param {function(string, plugin.PluginResult)} callback - the result callback + */ + CheckUpdates.prototype.main = async function (callback) { + let seedUpdates = []; + try { + seedUpdates = await this.checkMainLibraries(); + } catch (err) { + this.logger.error(`Could not check the libraries: ${err}`); + return callback(err, this.result); + } + + // Check for migrations + const updates = await Updates.getAvailableUpdates(this.core, this.rootNode); + const updateNames = updates.map(u => u.name).join(', ') || ''; + this.logger.info(`Updates available for ${this.projectId}: ${updateNames}`); + + // Combine and report the result + const msgs = seedUpdates + .concat( + updates.map(update => { + return { + type: Updates.MIGRATION, + nodeId: null, + name: update.name + }; + }) + ); + + msgs.forEach(msg => this.createMessage(msg.nodeId, JSON.stringify(msg))); + this.result.setSuccess(true); + return callback(null, this.result); + }; + + CheckUpdates.prototype.checkMainLibraries = async function () { + const libs = await this.getAllLibraries(); + const tuples = libs + .map(lib => { // map to [name, version, dir] + var version, + hash, + data, + versionPath = this.getSeedVersionPath(lib); + + try { + this.logger.info(`Checking for version info at ${versionPath}`); + version = fs.readFileSync(versionPath, 'utf8'); + this.logger.debug(`${lib} version is ${version}`); + data = fs.readFileSync(this.getSeedHashPath(lib), 'utf8').split(' '); + if (data[1] === version) { + hash = data[0]; + this.logger.debug(`${lib} hash is ${hash}`); + } + } catch (e) { + if (!version) { + this.logger.warn(`Could not find library version for ${lib}`); + } else { + this.logger.warn(`Could not find library hash for ${lib}`); + } + } + + return [lib, version, hash]; + }) + .filter(tuple => { // get only the libs w/ updates available + let [lib, version] = tuple; + + if (!version) return false; + + let projVersion = this.getLoadedVersion(lib); + let latest = version.replace(/\s+/g, ''); + + this.logger.info(`${lib} version info:\n${projVersion} ` + + `(project)\n${latest} (latest)`); + return projVersion < latest; + }); + + const hashes = await Q.all(tuples.map(tuple => this.uploadSeed.apply(this, tuple))); + + this.logger.info(`Found ${hashes.length} out of date libraries`); + + return hashes.map((hash, i) => { + const [name] = tuples[i]; + return { + type: Updates.SEED, + name: name, + nodeId: this.core.getPath(this.libraries[name]), + hash: hash + }; + }); + }; + + CheckUpdates.prototype.getSeedHashPath = function (name) { + return path.join(this.getSeedDir(name), 'hash.txt'); + }; + + CheckUpdates.prototype.getSeedVersionPath = function (name) { + return path.join(this.getSeedDir(name), 'version.txt'); + }; + + CheckUpdates.prototype.upgradeSeedToVersion = function (name, version, hash) { + if (!hash) { // Upload the seed + // Get the data + this.logger.info(`Uploading new version of ${name} (${version})`); + return this.uploadSeed(name) + .then(newHash => { // Store the new hash + this.logger.info(`Upload of ${name} finished!`); + hash = newHash; + return Q.nfcall( + fs.writeFile, + this.getSeedHashPath(name), + `${hash} ${version}` + ); + }).then(() => hash); + } + return hash; + }; + + CheckUpdates.prototype.getAllLibraries = function () { + const DEFAULT_LIBRARIES = ['pipeline']; + var name, + names = []; + + return this.core.loadChildren(this.rootNode) + .then(children => { + for (var i = children.length; i--;) { + if (this.core.isLibraryRoot(children[i])) { + name = this.core.getAttribute(children[i], 'name'); + this.libraries[name] = children[i]; + if (DEFAULT_LIBRARIES.includes(name)) { + names.push(name); + } + } + } + if (names.length) { + this.logger.debug(`Found libraries: ${names.join(', ')}`); + } else { + this.logger.debug('Found no libraries!'); + } + return names; + }); + }; + + CheckUpdates.prototype.getLoadedVersion = function (libName) { + var node = this.libraries[libName], + version = this.core.getAttribute(node, 'version'); // using library root hash + + return version; + }; + + return CheckUpdates; +}); diff --git a/src/plugins/CheckLibraries/metadata.json b/src/plugins/CheckUpdates/metadata.json similarity index 82% rename from src/plugins/CheckLibraries/metadata.json rename to src/plugins/CheckUpdates/metadata.json index be9bafc8f..d9ca4bfc7 100644 --- a/src/plugins/CheckLibraries/metadata.json +++ b/src/plugins/CheckUpdates/metadata.json @@ -1,6 +1,6 @@ { - "id": "CheckLibraries", - "name": "CheckLibraries", + "id": "CheckUpdates", + "name": "CheckUpdates", "version": "0.1.0", "description": "", "icon": { diff --git a/src/visualizers/panels/Sidebar/SidebarPanel.js b/src/visualizers/panels/Sidebar/SidebarPanel.js index 2a33211de..523610670 100644 --- a/src/visualizers/panels/Sidebar/SidebarPanel.js +++ b/src/visualizers/panels/Sidebar/SidebarPanel.js @@ -2,6 +2,7 @@ /*jshint browser: true*/ define([ + 'deepforge/updates/Updates', 'js/Constants', 'js/PanelBase/PanelBase', 'panels/AutoViz/AutoVizPanel', @@ -9,6 +10,7 @@ define([ 'deepforge/globals', 'q' ], function ( + Updates, CONSTANTS, PanelBase, AutoVizPanel, @@ -48,8 +50,8 @@ define([ SidebarPanel.prototype._initialize = function () { this.widget = new SidebarWidget(this.logger, this.$el); this.widget.getProjectName = this.getProjectName.bind(this); - this.widget.updateLibraries = this.updateLibraries.bind(this); - this.widget.checkLibUpdates = this.checkLibUpdates.bind(this); + this.widget.applyUpdates = this.applyUpdates.bind(this); + this.widget.checkUpdates = this.checkUpdates.bind(this); this.widget.setEmbeddedPanel = this.setEmbeddedPanel.bind(this); this.onActivate(); @@ -75,7 +77,7 @@ define([ if (typeof nodeId === 'string') { categories = Object.keys(CATEGORY_TO_PLACE); - + Q.all(categories.map(category => { place = CATEGORY_TO_PLACE[category]; return DeepForge.places[place](); @@ -144,26 +146,45 @@ define([ WebGMEGlobal.Toolbar.refresh(); }; - /* * * * * * * * Library Updates * * * * * * * */ + /* * * * * * * * Project Updates * * * * * * * */ SidebarPanel.prototype.getProjectName = function () { var projectId = this._client.getActiveProjectId(); return projectId && projectId.split('+')[1]; }; - SidebarPanel.prototype.checkLibUpdates = function () { - var pluginId = 'CheckLibraries', - context = this._client.getCurrentPluginContext(pluginId); + SidebarPanel.prototype.checkUpdates = function () { + const pluginId = 'CheckUpdates'; + const context = this._client.getCurrentPluginContext(pluginId); return Q.ninvoke(this._client, 'runServerPlugin', pluginId, context) .then(res => { - return res.messages.map(msg => msg.message.split(' ')); + return res.messages.map(msg => JSON.parse(msg.message)); }); }; - SidebarPanel.prototype.updateLibraries = function (libraries) { - var promises = libraries - .map(lib => Q.ninvoke(this._client, 'updateLibrary', lib[0], lib[1])); + SidebarPanel.prototype.applyUpdates = function (updates) { + // Seed Updates should apply the + const seedUpdates = updates.filter(update => update.type === Updates.SEED); + const promises = seedUpdates.map(update => { + const {name, hash} = update; + return Q.ninvoke(this._client, 'updateLibrary', name, hash); + }); + + // Apply the migrations + const pluginId = 'ApplyUpdates'; + const migrations = updates + .filter(update => update.type === Updates.MIGRATION) + .map(update => update.name); + + const context = this._client.getCurrentPluginContext(pluginId); + context.pluginConfig = { + updates: migrations + }; + + promises.push( + Q.ninvoke(this._client, 'runServerPlugin', pluginId, context) + ); return Q.all(promises); }; diff --git a/src/visualizers/widgets/Sidebar/SidebarWidget.js b/src/visualizers/widgets/Sidebar/SidebarWidget.js index b6851d4ff..e7db38f28 100644 --- a/src/visualizers/widgets/Sidebar/SidebarWidget.js +++ b/src/visualizers/widgets/Sidebar/SidebarWidget.js @@ -66,21 +66,15 @@ define([ return; } - this.checkLibUpdates() + return this.checkUpdates() .then(updates => { if (updates.length) { // prompt about updates - var names = updates.map(update => update[0]), + const msg = `${updates.length} update(s) available. Click to update.`; + var names = updates.map(update => update.name), projName = this.getProjectName(), - content = $(''), - msg = `${projName} is out of date. Click to update.`; + content = $(''); - this.logger.info(`Updates available for ${names.join(', ')}`); - - if (names.indexOf('nn') !== -1) { - msg = 'Newer nn library available. Click to update'; - } else if (names.indexOf('pipeline') !== -1) { - msg = 'Execution updates available. Click to update'; - } + this.logger.info(`Updates available for ${projName}: ${names.join(', ')}`); content.text(msg); content.on('click', () => { @@ -88,14 +82,9 @@ define([ content.parent().fadeOut(); // Create updating notification - msg = 'Updating execution library...'; - if (names.indexOf('nn') !== -1) { - msg = 'Updating nn library...'; - } - - content.text(msg); + content.text('Applying Updates...'); Materialize.toast(content, 8000); - this.updateLibraries(updates).then(() => { + this.applyUpdates(updates).then(() => { content.parent().remove(); Materialize.toast('Update complete!', 2000); }); @@ -104,7 +93,7 @@ define([ Materialize.toast(content, 8000); } }) - .fail(err => Materialize.toast(`Library update check failed: ${err}`, 2000)); + .catch(err => Materialize.toast(`Update check failed: ${err}`, 2000)); }; SidebarWidget.prototype.width = function () { diff --git a/test/unit/plugins/ApplyUpdates/ApplyUpdates.spec.js b/test/unit/plugins/ApplyUpdates/ApplyUpdates.spec.js new file mode 100644 index 000000000..bcbaa49b5 --- /dev/null +++ b/test/unit/plugins/ApplyUpdates/ApplyUpdates.spec.js @@ -0,0 +1,110 @@ +'use strict'; +const testFixture = require('../../../globals'); + +describe('ApplyUpdates', function () { + const Updates = testFixture.requirejs('deepforge/updates/Updates'); + var gmeConfig = testFixture.getGmeConfig(), + expect = testFixture.expect, + Q = testFixture.Q, + logger = testFixture.logger.fork('ApplyUpdates'), + PluginCliManager = testFixture.WebGME.PluginCliManager, + projectName = 'testProject', + pluginName = 'ApplyUpdates', + project, + gmeAuth, + storage, + commitHash; + + const manager = new PluginCliManager(null, logger, gmeConfig); + + before(function (done) { + testFixture.clearDBAndGetGMEAuth(gmeConfig, projectName) + .then(function (gmeAuth_) { + gmeAuth = gmeAuth_; + // This uses in memory storage. Use testFixture.getMongoStorage to persist test to database. + storage = testFixture.getMemoryStorage(logger, gmeConfig, gmeAuth); + return storage.openDatabase(); + }) + .then(function () { + var importParam = { + projectSeed: testFixture.path.join(testFixture.DF_SEED_DIR, 'devProject', 'devProject.webgmex'), + projectName: projectName, + branchName: 'master', + logger: logger, + gmeConfig: gmeConfig + }; + + return testFixture.importProject(storage, importParam); + }) + .then(function (importResult) { + project = importResult.project; + commitHash = importResult.commitHash; + return project.createBranch('test', commitHash); + }) + .nodeify(done); + }); + + after(function (done) { + storage.closeDatabase() + .then(function () { + return gmeAuth.unload(); + }) + .nodeify(done); + }); + + it('should run plugin and not update the branch if no updates', function (done) { + const pluginConfig = {}; + const context = { + project: project, + commitHash: commitHash, + branchName: 'test', + activeNode: '/', + }; + + manager.executePlugin(pluginName, pluginConfig, context, function (err, pluginResult) { + try { + expect(err).to.equal(null); + expect(typeof pluginResult).to.equal('object'); + expect(pluginResult.success).to.equal(true); + } catch (e) { + done(e); + return; + } + + project.getBranchHash('test') + .then(function (branchHash) { + expect(branchHash).to.equal(commitHash); + }) + .nodeify(done); + }); + }); + + describe('CustomUtilities', function() { + let plugin = null; + let context = null; + + before(async () => { + context = { + project: project, + commitHash: commitHash, + branchName: 'test', + activeNode: '/', + }; + + plugin = await manager.initializePlugin(pluginName); + await manager.configurePlugin(plugin, {}, context); + }); + + it('should add MyUtilities on update', async function() { + const pluginConfig = { + updates: ['CustomUtilities'] + }; + + const result = await Q.ninvoke(manager, 'executePlugin', pluginName, pluginConfig, context); + // Check that it now has MyUtilities + const update = await Updates.getUpdate('CustomUtilities'); + const {core, rootNode} = plugin; + const isNeeded = await update.isNeeded(core, rootNode); + }); + }); +}); diff --git a/test/unit/plugins/CheckLibraries/CheckLibraries.spec.js b/test/unit/plugins/CheckUpdates/CheckLibraries.spec.js similarity index 94% rename from test/unit/plugins/CheckLibraries/CheckLibraries.spec.js rename to test/unit/plugins/CheckUpdates/CheckLibraries.spec.js index 3abebae74..e6291feae 100644 --- a/test/unit/plugins/CheckLibraries/CheckLibraries.spec.js +++ b/test/unit/plugins/CheckUpdates/CheckLibraries.spec.js @@ -1,13 +1,13 @@ /*jshint node:true, mocha:true*/ -describe('CheckLibraries', function () { +describe('CheckUpdates', function () { const testFixture = require('../../../globals'); var gmeConfig = testFixture.getGmeConfig(), expect = testFixture.expect, - logger = testFixture.logger.fork('CheckLibraries'), + logger = testFixture.logger.fork('CheckUpdates'), PluginCliManager = testFixture.WebGME.PluginCliManager, projectName = 'testProject', - pluginName = 'CheckLibraries', + pluginName = 'CheckUpdates', project, gmeAuth, storage, diff --git a/webgme-setup.json b/webgme-setup.json index d7f625a32..342aaf460 100644 --- a/webgme-setup.json +++ b/webgme-setup.json @@ -29,9 +29,9 @@ "src": "src/plugins/ExecuteJob", "test": "test/plugins/ExecuteJob" }, - "CheckLibraries": { - "src": "src/plugins/CheckLibraries", - "test": "test/plugins/CheckLibraries" + "CheckUpdates": { + "src": "src/plugins/CheckUpdates", + "test": "test/plugins/CheckUpdates" }, "UpdateLibrarySeed": { "src": "src/plugins/UpdateLibrarySeed", @@ -48,6 +48,10 @@ "ImportLibrary": { "src": "src/plugins/ImportLibrary", "test": "test/plugins/ImportLibrary" + }, + "ApplyUpdates": { + "src": "src/plugins/ApplyUpdates", + "test": "test/plugins/ApplyUpdates" } }, "layouts": { @@ -366,4 +370,4 @@ "seeds": {}, "routers": {} } -} +} \ No newline at end of file