Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect and apply library updates. Closes #1884 #1888

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<%= JSON.stringify(extensions.map(function(ext) {
return {
name: ext.name,
version: ext.version,
description: ext.description,
nodeTypes: ext.nodeTypes,
initCode: ext.initCode,
Expand Down
54 changes: 46 additions & 8 deletions src/common/updates/Updates.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ define([
'deepforge/viz/PlotlyDescExtractor',
'deepforge/viz/FigureExtractor',
'./Version',
'q'
'text!deepforge/extensions/Libraries.json',
], function(
Storage,
PlotlyDescExtractor,
FigureExtractor,
Version,
Q
LibrariesTxt,
) {
const Libraries = JSON.parse(LibrariesTxt);
const GRAPH = 'Graph';
const getGraphNodes = async function(core, rootNode, graphNodes=[]) {
const children = await core.loadChildren(rootNode);
Expand Down Expand Up @@ -148,12 +149,48 @@ define([

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.getAvailableUpdates = async function(core, rootNode) {
const migrations = await this.getMigrationUpdates(core, rootNode);
const libUpdates = await this.getLibraryUpdates(core, rootNode);
return migrations.concat(libUpdates);
};

Updates.getMigrationUpdates = async function(core, rootNode) {
const isNeeded = await Promise.all(
allUpdates.map(update => update.isNeeded(core, rootNode))
);
const updates = allUpdates.filter((update, i) => isNeeded[i]);
return updates.map(update => ({
type: Updates.MIGRATION,
name: update.name,
}));
};

Updates.getLibraryUpdates = async function(core, rootNode) {
const children = await core.loadChildren(rootNode);
const libraryType = Object.values(core.getAllMetaNodes(rootNode))
.find(node => core.getAttribute(node, 'name') === 'LibraryCode');
const libraries = children.filter(child => core.isTypeOf(child, libraryType));
const libUpdates = Libraries.filter(libraryInfo => {
const {name, version} = libraryInfo;
const libNode = libraries
.find(library => {
const nodeName = core.getAttribute(library, 'name');
return nodeName === name || nodeName === `${name}InitCode`;
});

if (libNode) {
const nodeVersion = core.getAttribute(libNode, 'version');
const installedVersion = new Version(nodeVersion || '0.0.0');
const availableVersion = new Version(version || '0.0.0');
return installedVersion.lessThan(availableVersion);
}
});
return libUpdates.map(libInfo => ({
type: Updates.LIBRARY,
name: `Update ${libInfo.name} library`,
info: libInfo,
}));
};

Updates.getUpdates = function(names) {
Expand All @@ -170,5 +207,6 @@ define([
// Constants
Updates.MIGRATION = 'Migration';
Updates.SEED = 'SeedUpdate';
Updates.LIBRARY = 'Library';
return Updates;
});
27 changes: 19 additions & 8 deletions src/plugins/ApplyUpdates/ApplyUpdates.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
/*eslint-env node, browser*/

define([
'plugin/ImportLibrary/ImportLibrary/ImportLibrary',
'deepforge/updates/Updates',
'text!./metadata.json',
'plugin/PluginBase'
'underscore',
], function (
PluginBase,
Updates,
pluginMetadata,
PluginBase
_,
) {
'use strict';

Expand Down Expand Up @@ -50,24 +52,33 @@ define([
ApplyUpdates.prototype.main = async function (callback) {
// Retrieve the updates to apply
const config = this.getCurrentConfig();
const updateNames = config.updates || [];
const {updates=[]} = config;

if (!updateNames.length) {
if (!updates.length) {
this.result.setSuccess(true);
return callback(null, this.result);
}

// Apply each of the updates
const updates = Updates.getUpdates(updateNames);
const [migrations, libUpdates] = _.partition(
updates,
update => update.type === Updates.MIGRATION
);

for (let i = 0, len = updates.length; i < len; i++) {
const update = updates[i];
for (let i = 0, len = migrations.length; i < len; i++) {
const update = migrations[i];
this.logger.info(`Applying update: ${update.name} to ${this.projectId}`);
await update.apply(this.core, this.rootNode, this.META);
}

for (let i = libUpdates.length; i--;) {
const libraryInfo = libUpdates[i].info;
await this.importLibrary(libraryInfo);
}

// Save the project
await this.save(`Applied project updates: ${updateNames.join(",")}`);
const updateNames = updates.map(update => update.name);
await this.save(`Applied project updates: ${updateNames.join(',')}`);

this.result.setSuccess(true);
callback(null, this.result);
Expand Down
11 changes: 1 addition & 10 deletions src/plugins/CheckUpdates/CheckUpdates.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,7 @@ define([
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,
node: null,
name: update.name
};
})
);
const msgs = seedUpdates.concat(updates);

msgs.forEach(msg => {
const {node} = msg;
Expand Down
75 changes: 40 additions & 35 deletions src/plugins/ImportLibrary/ImportLibrary.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,24 +46,21 @@ define([
*
* @param {function(string, plugin.PluginResult)} callback - the result callback
*/
ImportLibrary.prototype.main = function (callback) {
ImportLibrary.prototype.main = async function () {
const config = this.getCurrentConfig();
const libraryInfo = config.libraryInfo;

return this.addSeedToBranch(libraryInfo.seed)
.then(branchName => this.createGMELibraryFromBranch(branchName, libraryInfo))
.then(branchInfo => this.removeTemporaryBranch(branchInfo))
.then(() => this.updateMetaForLibrary(libraryInfo))
.then(() => this.addLibraryInitCode(libraryInfo))
.then(() => this.save(`Imported ${libraryInfo.name} library`))
.then(() => {
this.result.setSuccess(true);
callback(null, this.result);
})
.catch(err => {
this.logger.error(`Could not check the libraries: ${err}`);
callback(err, this.result);
});
await this.importLibrary(libraryInfo);
await this.save(`Imported ${libraryInfo.name} library`);
this.result.setSuccess(true);
};

ImportLibrary.prototype.importLibrary = async function (libraryInfo) {
const branchName = await this.addSeedToBranch(libraryInfo.seed);
const branchInfo = await this.createGMELibraryFromBranch(branchName, libraryInfo);
await this.removeTemporaryBranch(branchInfo);
await this.updateMetaForLibrary(libraryInfo);
await this.addLibraryInitCode(libraryInfo);
};

ImportLibrary.prototype.getUniqueBranchName = function (basename) {
Expand Down Expand Up @@ -105,7 +102,12 @@ define([
let rootHash = commit.root;

libraryData.commitHash = commit._id;
await this.core.addLibrary(this.rootNode, name, rootHash, libraryData);
const alreadyExists = this.core.getLibraryNames(this.rootNode).includes(name);
if (alreadyExists) {
await this.core.updateLibrary(this.rootNode, name, rootHash, libraryData);
} else {
await this.core.addLibrary(this.rootNode, name, rootHash, libraryData);
}
return {
name: branchName,
hash: commit._id
Expand All @@ -116,7 +118,7 @@ define([
return this.project.deleteBranch(branch.name, branch.hash);
};

ImportLibrary.prototype.updateMetaForLibrary = function (libraryInfo) {
ImportLibrary.prototype.updateMetaForLibrary = async function (libraryInfo) {
const nodeNames = libraryInfo.nodeTypes;
const libraryNodes = this.getLibraryMetaNodes(libraryInfo.name);

Expand All @@ -132,34 +134,37 @@ define([
.filter(node => !!node);

// Add containment relationships to the meta
return this.core.loadChildren(this.rootNode)
.then(children => {
let parent = children.find(node => this.core.getAttribute(node, 'name') === 'MyResources');
if (!parent) throw new Error('Could not find resources location');
nodes.forEach(node => this.core.setChildMeta(parent, node));
});
const children = await this.core.loadChildren(this.rootNode);
let parent = children.find(node => this.core.getAttribute(node, 'name') === 'MyResources');
if (!parent) throw new Error('Could not find resources location');
nodes.forEach(node => this.core.setChildMeta(parent, node));
};

ImportLibrary.prototype.addLibraryInitCode = function (libraryInfo) {
// Get the library fco node
// Add the initialization code for this library;
ImportLibrary.prototype.addLibraryInitCode = async function (libraryInfo) {
const libraryNodes = this.getLibraryMetaNodes(libraryInfo.name);
const LibraryCode = this.getLibraryCodeNode();
const FCO = this.getFCONode();

// Make the LibraryCode node
const node = this.core.createNode({
parent: this.rootNode,
base: LibraryCode
});
const node = await this.createLibraryCodeNode(libraryInfo.name);

this.core.setAttribute(node, 'code', libraryInfo.initCode || '');
this.core.setAttribute(node, 'name', `${libraryInfo.name}InitCode`);
this.core.setAttribute(node, 'name', libraryInfo.name);
this.core.setAttribute(node, 'version', libraryInfo.version);

const FCO = this.getFCONode();
const libraryFCO = libraryNodes
.find(node => this.core.getPointerPath(node, 'base') === this.core.getPath(FCO));

this.core.setPointer(node, 'library', libraryFCO);
return node;
};

ImportLibrary.prototype.createLibraryCodeNode = async function (libName) {
const LibraryCode = this.getLibraryCodeNode();
const libraryCodeNodes = (await this.core.loadChildren(this.rootNode))
.filter(node => this.core.isTypeOf(node, LibraryCode));

return libraryCodeNodes.find(node => {
const name = this.core.getAttribute(node, 'name');
return name === libName || name === `${libName}InitCode`;
}) || this.core.createNode({parent: this.rootNode, base: LibraryCode});
};

ImportLibrary.prototype.getLibraryMetaNodes = function (libraryName) {
Expand Down
2 changes: 1 addition & 1 deletion src/visualizers/panels/ForgeActionButton/LibraryDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ define([
'common/storage/constants',
'q',
'underscore',
'text!./Libraries.json',
'text!deepforge/extensions/Libraries.json',
'text!./LibraryDialogModal.html',
'css!./LibraryDialog.css'
], function(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ define([
'deepforge/OperationCode',
'deepforge/Constants',
'./Colors',
'text!panels/ForgeActionButton/Libraries.json',
'text!deepforge/extensions/Libraries.json',
], function(
EasyDAGControlEventHandlers,
OperationCode,
Expand Down
9 changes: 3 additions & 6 deletions src/visualizers/panels/Sidebar/SidebarPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ define([
if (update.type === Updates.SEED) {
return 0;
}
if (Updates.getUpdate(update.name).beforeLibraryUpdates) {
const hasBeforeUpdates = update.type === Updates.MIGRATION && Updates.getUpdate(update.name).beforeLibraryUpdates;
if (hasBeforeUpdates) {
return 1;
}
return 2;
Expand All @@ -195,13 +196,9 @@ define([

SidebarPanel.prototype.applyMigrationUpdates = async function (updates) {
const pluginId = 'ApplyUpdates';
const names = updates.map(update => update.name);

const context = this._client.getCurrentPluginContext(pluginId);
context.pluginConfig = {
updates: names,
};

context.pluginConfig = {updates};
await Q.ninvoke(this._client, 'runServerPlugin', pluginId, context);
};

Expand Down
Loading