--- /dev/null
+FROM ubuntu:16.04
+
+RUN apt-get update && apt-get -y install python3 curl build-essential
+RUN curl http://repos.riftio.com/public/xenial-riftware-public-key | apt-key add - && \
+ curl -o /etc/apt/sources.list.d/OSM.list http://buildtracker.riftio.com/repo_file/ub16/OSM/ && \
+ apt-get update && \
+ apt-get -y install rw.toolchain-rwbase \
+ rw.toolchain-rwtoolchain \
+ rw.core.mgmt-mgmt \
+ rw.core.util-util \
+ rw.core.rwvx-rwvx \
+ rw.core.rwvx-rwdts \
+ rw.automation.core-RWAUTO \
+ rw.tools-container-tools \
+ rw.tools-scripts \
+ python-cinderclient \
+ libxml2-dev \
+ libxslt-dev
+
+RUN /usr/rift/container_tools/mkcontainer --modes build --modes ext --repo OSM
+
+RUN chmod 777 /usr/rift /usr/rift/usr/share
+
+RUN rm -rf /tmp/npm-cache
--- /dev/null
+pipeline {
+ agent any
+ stages {
+ stage("Build") {
+ agent {
+ dockerfile true
+ }
+ steps {
+ sh 'make NOT_DEVELOPER_BUILD=TRUE -j16 package'
+ stash name: "deb-files", includes: ".build/*.deb"
+ }
+ }
+ stage("Repo Component") {
+ steps {
+ unstash "deb-files"
+ sh '''
+ mkdir -p pool/UI
+ mv .build/*.deb pool/UI/
+ mkdir -p dists/$RELEASE/UI/binary-amd64
+ apt-ftparchive packages pool/UI > dists/$RELEASE/UI/binary-amd64/Packages
+ gzip -9fk dists/$RELEASE/UI/binary-amd64/Packages
+ '''
+ archiveArtifacts artifacts: "dists/**,pool/UI/*.deb"
+ }
+ }
+ }
+}
* ['c', 'd']
*/
+import _isNumber from 'lodash/isNumber';
+import _each from 'lodash/each';
+import _flatten from 'lodash/flatten';
+import _union from 'lodash/union';
+
var rw = rw || {
// Angular specific for now but can be modified in one location if need ever be
BaseController: function() {
},
sum : function(total, i, key, value) {
- if (_.isNumber(value)) {
+ if (_isNumber(value)) {
total[key] = (i === 0 ? value : (total[key] + value));
}
},
sum2 : function(key) {
return function(prev, cur, i) {
var value = cur[key];
- if (_.isNumber(value)) {
+ if (_isNumber(value)) {
if (typeof(prev) === 'undefined') {
return value;
} else {
max : function(key) {
return function(prev, cur, i) {
var value = cur[key];
- if (_.isNumber(value)) {
+ if (_isNumber(value)) {
if (typeof(prev) === 'undefined') {
return value;
} else if (prev < value) {
operation(total, i, key, value);
};
for (i = 0; i < rows.length; i++) {
- _.each(rows[i], f);
+ _each(rows[i], f);
}
}
};
rw.vnf = {
ports: function(service) {
- return _.flatten(jsonPath.eval(service, '$.connector[*].interface[*].port'));
+ return _flatten(jsonPath.eval(service, '$.connector[*].interface[*].port'));
},
fabricPorts: function(service) {
- return _.flatten(jsonPath.eval(service, '$.vm[*].fabric.port'));
+ return _flatten(jsonPath.eval(service, '$.vm[*].fabric.port'));
}
};
}
var i = 0;
var self = this;
- _.each(children, function(child) {
+ _each(children, function(child) {
self.visit.call(self, child, parent, i, listType);
i += 1;
});
rw.vcs = {
allVms : function() {
- return _.flatten([this.jpath('$.collection[*].vm'), this.jpath('$.collection[*].collection[*].vm')], true);
+ return _flatten([this.jpath('$.collection[*].vm'), this.jpath('$.collection[*].collection[*].vm')], true);
},
vms: function(n) {
getChildren: function(n) {
switch (rw.vcs.nodeType(n)) {
case 'rwcolony':
- return 'vm' in n ? _.union(n.collection, n.vm) : n.collection;
+ return 'vm' in n ? _union(n.collection, n.vm) : n.collection;
case 'rwcluster':
return n.vm;
case 'RWVM':
},
jpath : function(jpath, n) {
- return _.flatten(jsonPath.eval(n || this, jpath), true);
+ return _flatten(jsonPath.eval(n || this, jpath), true);
}
};
//Login needs to be refactored. Too many cross dependencies
var AuthActions = require('../widgets/login/loginAuthActions.js');
var $ = require('jquery');
-var rw = require('utils/rw.js');
+import rw from './rw.js';
var API_SERVER = rw.getSearchParams(window.location).api_server;
-let NODE_PORT = require('utils/rw.js').getSearchParams(window.location).api_port || ((window.location.protocol == 'https:') ? 8443 : 8000);
+let NODE_PORT = rw.getSearchParams(window.location).api_port || ((window.location.protocol == 'https:') ? 8443 : 8000);
var SockJS = require('sockjs-client');
var Utils = {};
onKeyPress: this.onKeyPress,
onKeyUp: this.onKeyUp,
onFocus: this.onFocus,
- onBlur: this.onBlur
+ onBlur: this.onBlur,
+ disabled: this.isDisabled,
},
button_icon,
React.createElement("span", {className: "rw-button__label"}, display)
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
type={"error"}
hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
onDismiss={HeaderStore.validateReset}
+ timeout= {5000}
/>
// html = (
}
}
-export default Alt.createStore(HeaderStoreConstructor)
+export default Alt.createStore(HeaderStoreConstructor, 'HeaderStoreConstructor')
*
*/
import React from 'react';
-import _ from 'lodash';
+import _isArrayLike from 'lodash/isArrayLike';
+import _isObjectLike from 'lodash/isObjectLike';
+import _isEmpty from 'lodash/isEmpty';
/**
*
var listNode = null;
var self = this;
- if (_.isArrayLike(data) && _.isObjectLike(data)) {
+ if (_isArrayLike(data) && _isObjectLike(data)) {
var children = [];
data.forEach(function(element, index, array) {
- if ( _.isArrayLike(element) || _.isObjectLike(element)) {
+ if ( _isArrayLike(element) || _isObjectLike(element)) {
children.push(self.createList(element, iter+1));
} else {
children.push(React.createElement(itemTag, {
key: iter,
className: groupClass }, children);
}
- else if (_.isObjectLike(data)) {
+ else if (_isObjectLike(data)) {
var children = [];
for (var key in data) {
- if ( _.isArrayLike(data[key]) || _.isObjectLike(data[key])) {
+ if ( _isArrayLike(data[key]) || _isObjectLike(data[key])) {
children.push(
React.createElement(listHeaderTag, {
key: key + '_header',
return React.createElement("div", {
className: "listy" },
- _.isEmpty(data) ?
+ _isEmpty(data) ?
this.noDataMessage() :
this.createList(data)
)
if (!obj)
return new Error('Validation failed. "%" is undefined', fullAttr);
- if (!obj.hasOwnProperty("tag") || _.isEmpty(obj.tag))
+ if (!obj.hasOwnProperty("tag") || _isEmpty(obj.tag))
return new Error('Validation failed. "%s" missing attribute "tag"', fullAttr);
if (!obj.hasOwnProperty("className") || obj.className == undefined)
return new Error('Validation failed. "%s" missing attribute "className"', fullAttr);
import Utils from 'utils/utils.js';
import Button from 'widgets/button/rw.button.js';
import './login.scss'
-let rw = require('utils/rw.js');
+import rw from 'utils/rw.js';
+
class LoginScreen extends React.Component{
constructor(props) {
super(props);
import Utils from 'utils/utils.js';
import Crouton from 'react-crouton';
import TreeView from 'react-treeview';
-import _ from 'lodash';
+import _isEqual from 'lodash/isEqual';
+import _merge from 'lodash/merge';
+import _indexOf from 'lodash/indexOf';
import '../../../node_modules/react-treeview/react-treeview.css';
import './eventCenter.scss';
if (props.newNotificationEvent && props.newNotificationMsg) {
if (latestNotification) {
latestNotification = JSON.parse(latestNotification);
- if (!_.isEqual(props.newNotificationMsg, latestNotification)) {
+ if (!_isEqual(props.newNotificationMsg, latestNotification)) {
stateObject.newNotificationEvent = props.newNotificationEvent;
stateObject.newNotificationMsg = props.newNotificationMsg;
sessionStorage.setItem('latestNotification', JSON.stringify(stateObject.newNotificationMsg));
}
if (notificationList) {
- stateObject.notifications = _.merge(notificationList, props.notifications);
+ stateObject.notifications = _merge(notificationList, props.notifications);
} else {
stateObject.notifications = props.notifications;
}
notificationFields.eventTime = notification.eventTime;
Object.keys(notification).map((notificationKey) => {
- if (_.indexOf(['source', 'eventTime'], notificationKey) == -1) {
+ if (_indexOf(['source', 'eventTime'], notificationKey) == -1) {
notificationFields.eventKey = notificationKey;
notificationFields.details = notification[notificationFields.eventKey];
}
import SkyquakeContainerStore from './skyquakeContainerStore.js';
// import Breadcrumbs from 'react-breadcrumbs';
import Utils from 'utils/utils.js';
-import _ from 'lodash';
import Crouton from 'react-crouton';
import ScreenLoader from 'widgets/screen-loader/screenLoader.jsx';
import './skyquakeApp.scss';
type={notificationType}
hidden={!(displayNotification && notificationMessage)}
onDismiss={SkyquakeContainerActions.hideNotification}
+ timeout= {5000}
/>
<ScreenLoader show={displayScreenLoader}/>
<SkyquakeNav nav={nav}
import Alt from './skyquakeAltInstance.js';
import $ from 'jquery';
import SkyquakeContainerActions from './skyquakeContainerActions'
+import rw from 'utils/rw.js';
+import Utils from 'utils/utils.js';
-let Utils = require('utils/utils.js');
-let API_SERVER = require('utils/rw.js').getSearchParams(window.location).api_server;
+let API_SERVER = rw.getSearchParams(window.location).api_server;
let HOST = API_SERVER;
-let NODE_PORT = require('utils/rw.js').getSearchParams(window.location).api_port || ((window.location.protocol == 'https:') ? 8443 : 8000);
-let DEV_MODE = require('utils/rw.js').getSearchParams(window.location).dev_mode || false;
-let RW_REST_API_PORT = require('utils/rw.js').getSearchParams(window.location).rw_rest_api_port || 8008;
+let NODE_PORT = rw.getSearchParams(window.location).api_port || ((window.location.protocol == 'https:') ? 8443 : 8000);
+let DEV_MODE = rw.getSearchParams(window.location).dev_mode || false;
+let RW_REST_API_PORT = rw.getSearchParams(window.location).rw_rest_api_port || 8008;
if (DEV_MODE) {
HOST = window.location.protocol + '//' + window.location.hostname;
remote: function(state, location, streamSource) {
return new Promise((resolve, reject) => {
$.ajax({
- url: '//' + window.location.hostname + ':' + window.location.port + '/socket-polling',
+ url: '//' + window.location.hostname + ':' + window.location.port + '/socket-polling?api_server=' + API_SERVER,
type: 'POST',
beforeSend: Utils.addAuthorizationStub,
data: {
import SkyquakeContainerSource from './skyquakeContainerSource.js';
import SkyquakeContainerActions from './skyquakeContainerActions';
let Utils = require('utils/utils.js');
-import _ from 'lodash';
+import _indexOf from 'lodash/indexOf';
+import _isEqual from 'lodash/isEqual';
//Temporary, until api server is on same port as webserver
-var rw = require('utils/rw.js');
+import rw from 'utils/rw.js';
+
var API_SERVER = rw.getSearchParams(window.location).api_server;
var UPLOAD_SERVER = rw.getSearchParams(window.location).upload_server;
} else {
// Temp to test before adding multi-sources
data.notification.source = streamSource;
- if (_.indexOf(self.notifications, data.notification) == -1) {
+ if (_indexOf(self.notifications, data.notification) == -1) {
// newly appreared event.
// Add to the notifications list and setState
self.notifications.unshift(data.notification);
Utils.checkAuthentication(data.statusCode, function() {
self.closeSocket();
});
- if (!_.isEqual(data.project, self.projects)) {
+ if (!_isEqual(data.project, self.projects)) {
let user = self.user;
user.projects = data.project;
self.setState({
}
}
-export default Alt.createStore(SkyquakeContainerStore);
+export default Alt.createStore(SkyquakeContainerStore, 'SkyquakeContainerStore');
import {isRBACValid, SkyquakeRBAC} from 'widgets/skyquake_rbac/skyquakeRBAC.jsx';
//Temporary, until api server is on same port as webserver
-var rw = require('utils/rw.js');
+import rw from 'utils/rw.js';
+
var API_SERVER = rw.getSearchParams(window.location).api_server;
var UPLOAD_SERVER = rw.getSearchParams(window.location).upload_server;
import ReactDOM from 'react-dom';
import d3 from 'd3';
import DashboardCard from '../dashboard_card/dashboard_card.jsx';
-import _ from 'lodash';
+import _cloneDeep from 'lodash/cloneDeep';
import $ from 'jquery';
import './topologyTree.scss';
//this.props.selectNode(props.data);
}
if(this.svg) {
- this.update(_.cloneDeep(props.data));
+ this.update(_cloneDeep(props.data));
// this.selectedID = props.data.id;
}
}
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.3.13",
"babel-runtime": "^6.3.19",
+ "compression-webpack-plugin": "^0.3.2",
"cors": "^2.7.1",
"css-loader": "^0.23.1",
"file-loader": "^0.8.5",
npm install
echo 'Fetching third-party node_modules for '$PLUGIN_NAME'...done'
echo 'Packaging '$PLUGIN_NAME' using webpack'
-ui_plugin_cmake_build=true ./node_modules/.bin/webpack --progress --config webpack.production.config.js
+ui_plugin_cmake_build=true ./node_modules/.bin/webpack --optimize-minimize --progress --config webpack.production.config.js
echo 'Packaging '$PLUGIN_NAME' using webpack... done'
echo 'Building plugin '$PLUGIN_NAME'... done'
// If in the mission control, create an uptime table;
var uptime = this.state.createTime && this.state.createTime;
+ var fossInfoComponent = (
+ <div className="table-container">
+ <h2> FOSS Info </h2>
+ <table>
+ <thead>
+ <tr>
+ <th>
+ <a target="_blank" href='https://open.riftio.com/open-source-software-usage/'>
+ Click here for FOSS Info (requires Internet connection)
+ </a>
+ </th>
+ </tr>
+ </thead>
+ </table>
+ </div>
+ );
+
var uptimeComponent = (
<div className="table-container">
<h2> Uptime Info </h2>
if (this.state != null) {
var html = (
<div className="table-container-wrapper">
+ {fossInfoComponent}
{uptimeComponent}
<div className="table-container">
<h2> Version Info </h2>
console.log('uptime success', time)
}
-module.exports = Alt.createStore(aboutStore);;
+module.exports = Alt.createStore(aboutStore, 'aboutStore');;
* limitations under the License.
*
*/
-var Webpack = require('webpack');
+var webpack = require('webpack');
var path = require('path');
var nodeModulesPath = path.resolve(__dirname, 'node_modules');
var buildPath = path.resolve(__dirname, 'public', 'build');
var uiPluginCmakeBuild = process.env.ui_plugin_cmake_build || false;
var frameworkPath = uiPluginCmakeBuild?'../../../../skyquake/skyquake-build/framework':'../../framework';
var HtmlWebpackPlugin = require('html-webpack-plugin');
+var CompressionPlugin = require("compression-webpack-plugin");
+
// Added to overcome node-sass bug https://github.com/iam4x/isomorphic-flux-boilerplate/issues/62
process.env.UV_THREADPOOL_SIZE=64;
var config = {
},
plugins: [
new HtmlWebpackPlugin({
- filename: '../index.html'
- , templateContent: '<div id="app"></div>'
- }),
- new Webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js", Infinity)
+ filename: '../index.html',
+ templateContent: '<div id="app"></div>'
+ })
]
};
+
+if (process.argv.indexOf('--optimize-minimize') !== -1) {
+ // we are going to output a gzip file in the production process
+ config.output.filename = "gzip-" + config.output.filename;
+ config.plugins.push(new webpack.DefinePlugin({ // <-- key to reducing React's size
+ 'process.env': {
+ 'NODE_ENV': JSON.stringify('production')
+ }
+ }));
+ config.plugins.push(new CompressionPlugin({
+ asset: "[path]", // overwrite js file with gz file
+ algorithm: "gzip",
+ test: /\.(js)$/
+ }));
+}
module.exports = config;
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.3.13",
"babel-runtime": "^6.3.19",
+ "compression-webpack-plugin": "^0.3.2",
"cors": "^2.7.1",
"css-loader": "^0.23.1",
"file-loader": "^0.8.5",
npm install
echo 'Fetching third-party node_modules for '$PLUGIN_NAME'...done'
echo 'Packaging '$PLUGIN_NAME' using webpack'
-ui_plugin_cmake_build=true ./node_modules/.bin/webpack --progress --config webpack.production.config.js
+ui_plugin_cmake_build=true ./node_modules/.bin/webpack --optimize-minimize --progress --config webpack.production.config.js
echo 'Packaging '$PLUGIN_NAME' using webpack... done'
echo 'Building plugin '$PLUGIN_NAME'... done'
import React from 'react';
import Button from 'widgets/button/rw.button.js';
-import _ from 'lodash';
+import _cloneDeep from 'lodash/cloneDeep';
import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx';
import Crouton from 'react-crouton';
import TextInput from 'widgets/form_controls/textInput.jsx';
}
}
- let newAccount = _.cloneDeep(removeTrailingWhitespace(Account));
+ let newAccount = _cloneDeep(removeTrailingWhitespace(Account));
delete newAccount.params;
newAccount.nestedParams &&
newAccount.nestedParams['container-name'] &&
self.props.router.push({pathname:'accounts'});
self.props.flux.actions.global.hideScreenLoader.defer();
},
- function() {
- self.props.flux.actions.global.showNotification("There was an error creating your account. Please contact your system administrator.");
+ function(error) {
+ self.props.flux.actions.global.showNotification(error);
self.props.flux.actions.global.hideScreenLoader.defer();
});
}
}
evaluateSubmit = (e) => {
if (e.keyCode == 13) {
- if (this.props.edit) {
+ if (this.props.params.name != 'create') {
this.update(e);
} else {
this.create(e);
return resolve(false);
}
$.ajax({
- url: '/socket-polling',
+ url: '/socket-polling?api_server=' + API_SERVER,
type: 'POST',
beforeSend: Utils.addAuthorizationStub,
data: {
}).fail(function(xhr){
//Authentication and the handling of fail states should be wrapped up into a connection class.
Utils.checkAuthentication(xhr.status);
+ reject(xhr.responseText || 'An error occurred. Check your logs for more information');
});;
});
},
}).fail(function(xhr){
//Authentication and the handling of fail states should be wrapped up into a connection class.
Utils.checkAuthentication(xhr.status);
- reject();
+ reject(xhr.responseText || 'An error occurred. Check your logs for more information');
});
});
}).fail(function(xhr){
//Authentication and the handling of fail states should be wrapped up into a connection class.
Utils.checkAuthentication(xhr.status);
- reject('error');
+ reject(xhr.responseText || 'An error occurred. Check your logs for more information');
});
});
}).fail(function(xhr){
//Authentication and the handling of fail states should be wrapped up into a connection class.
Utils.checkAuthentication(xhr.status);
- reject('error');
+ reject(xhr.responseText || 'An error occurred. Check your logs for more information');
});
})
},
* limitations under the License.
*
*/
-var Webpack = require('webpack');
+var webpack = require('webpack');
var path = require('path');
var nodeModulesPath = path.resolve(__dirname, 'node_modules');
var buildPath = path.resolve(__dirname, 'public', 'build');
var uiPluginCmakeBuild = process.env.ui_plugin_cmake_build || false;
var frameworkPath = uiPluginCmakeBuild?'../../../../skyquake/skyquake-build/framework':'../../framework';
var HtmlWebpackPlugin = require('html-webpack-plugin');
-var CommonsPlugin = new require("webpack/lib/optimize/CommonsChunkPlugin")
+var CompressionPlugin = require("compression-webpack-plugin");
// Added to overcome node-sass bug https://github.com/iam4x/isomorphic-flux-boilerplate/issues/62
process.env.UV_THREADPOOL_SIZE=64;
var config = {
},
plugins: [
new HtmlWebpackPlugin({
- filename: '../index.html'
- , templateContent: '<div id="app"></div>'
- }),
- new Webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js", Infinity)
+ filename: '../index.html',
+ templateContent: '<div id="app"></div>'
+ })
]
};
+
+if (process.argv.indexOf('--optimize-minimize') !== -1) {
+ // we are going to output a gzip file in the production process
+ config.output.filename = "gzip-" + config.output.filename;
+ config.plugins.push(new webpack.DefinePlugin({ // <-- key to reducing React's size
+ 'process.env': {
+ 'NODE_ENV': JSON.stringify('production')
+ }
+ }));
+ config.plugins.push(new CompressionPlugin({
+ asset: "[path]", // overwrite js file with gz file
+ algorithm: "gzip",
+ test: /\.(js)$/
+ }));
+}
module.exports = config;
var Composer = {};
var FileManager = {};
+var PackageManager = {};
var DataCenters = {};
// Catalog module methods
Composer.get = function(req) {
});
}
-Composer.update = function(req) {
- console.log(' Updating file', req.file.originalname, 'as', req.file.filename);
+PackageManager.upload = function(req) {
+ console.log(' Uploading file', req.file.originalname, 'as', req.file.filename);
var api_server = req.query['api_server'];
// dev_download_server is for testing purposes.
// It is the direct IP address of the Node server where the
var download_host = req.query['dev_download_server'];
if (!download_host) {
- download_host = req.protocol + '://' + req.get('host');//api_server + ':' + utils.getPortForProtocol(req.protocol);
+ download_host = req.protocol + '://' + req.get('host');//req.api_server + ':' + utils.getPortForProtocol(req.protocol);
}
+
var input = {
- 'external-url': download_host + '/composer/update/' + req.file.filename,
+ 'external-url': download_host + '/composer/upload/' + req.file.filename,
'package-type': 'VNFD',
'package-id': uuid()
}
- var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-update');
+ var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-create');
input = utils.addProjectContextToRPCPayload(req, uri, input);
});
}).catch(function(error) {
var res = {};
- console.log('Problem with Composer.upload', error);
+ console.log('Problem with PackageManager.upload', error);
res.statusCode = error.statusCode || 500;
res.errorMessage = {
error: 'Failed to upload package ' + req.file.originalname + '. Error: ' + error
});
};
-Composer.upload = function(req) {
- console.log(' Uploading file', req.file.originalname, 'as', req.file.filename);
+PackageManager.update = function(req) {
+ console.log(' Updating file', req.file.originalname, 'as', req.file.filename);
var api_server = req.query['api_server'];
// dev_download_server is for testing purposes.
// It is the direct IP address of the Node server where the
var download_host = req.query['dev_download_server'];
if (!download_host) {
- download_host = req.protocol + '://' + req.get('host');//req.api_server + ':' + utils.getPortForProtocol(req.protocol);
+ download_host = req.protocol + '://' + req.get('host');//api_server + ':' + utils.getPortForProtocol(req.protocol);
}
-
var input = {
- 'external-url': download_host + '/composer/upload/' + req.file.filename,
+ 'external-url': download_host + '/composer/update/' + req.file.filename,
'package-type': 'VNFD',
'package-id': uuid()
};
- var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-create');
+ var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-update');
input = utils.addProjectContextToRPCPayload(req, uri, input);
data['transaction_id'] = result[0].body['output']['transaction-id'];
// Add a status checker on the transaction and then to delete the file later
- PackageFileHandler.checkCreatePackageStatusAndHandleFile(req, data['transaction_id']);
+ PackageFileHandler.checkCreatePackageStatusAndHandleFile(req, data['transaction_id'], true);
// Return status to composer UI to update the status.
resolve({
});
};
-
-
-Composer.addFile = function(req) {
- console.log(' Uploading file', req.file.originalname, 'as', req.file.filename);
+PackageManager.export = function(req) {
+ // /api/operations/package-export
var api_server = req.query['api_server'];
- var download_host = req.query['dev_download_server'];
- var package_id = req.query['package_id'];
- var package_type = req.query['package_type'].toUpperCase();
- var package_path = req.query['package_path'];
- if (!download_host) {
- download_host = req.protocol + '://' + req.get('host');//api_server + ':' + utils.getPortForProtocol(req.protocol);
- }
- var input = {
- 'external-url': download_host + '/composer/upload/' + req.query['package_id'] + '/' + req.file.filename,
- 'package-type': package_type,
- 'package-id': package_id,
- 'package-path': package_path + '/' + req.file.filename
- }
-
- var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-file-add');
-
+ var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-export');
+ var input = req.body;
input = utils.addProjectContextToRPCPayload(req, uri, input);
-
return new Promise(function(resolve, reject) {
Promise.all([
rp({
- uri: uri,
+ uri: utils.confdPort(api_server) + '/api/operations/package-export',
method: 'POST',
headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
'Authorization': req.session && req.session.authorization
rejectUnauthorized: false,
resolveWithFullResponse: true,
json: true,
- body: {
- input: input
- }
+ body: { "input": input }
})
]).then(function(result) {
var data = {};
- data['transaction_id'] = result[0].body['output']['task-id'];
resolve({
statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK,
- data: data
+ data: result[0].body
});
}).catch(function(error) {
var res = {};
- console.log('Problem with Composer.upload', error);
+ console.log('Problem with PackageManager.export', error);
res.statusCode = error.statusCode || 500;
res.errorMessage = {
- error: 'Failed to upload package ' + req.file.originalname + '. Error: ' + error
+ error: error
};
reject(res);
});
});
}
-Composer.exportPackage = function(req) {
+PackageManager.copy = function(req) {
+ // /api/operations/package-copy
var api_server = req.query['api_server'];
- var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-export');
+ var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-copy');
var input = req.body;
input = utils.addProjectContextToRPCPayload(req, uri, input);
+
return new Promise(function(resolve, reject) {
Promise.all([
rp({
uri: uri,
method: 'POST',
headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
- 'Authorization': req.session && req.session.authorization
+ 'Authorization': req.get('Authorization')
}),
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
resolveWithFullResponse: true,
json: true,
- body: { "input": input }
+ body: { "input": input}
})
]).then(function(result) {
var data = {};
});
}).catch(function(error) {
var res = {};
- console.log('Problem with Composer.exportPackage', error);
+ console.log('Problem with PackageManager.copy', error);
res.statusCode = error.statusCode || 500;
res.errorMessage = {
error: error
});
}
+/**
+ * This methods retrieves the status of package operations. It takes an optional
+ * transaction id (id) this if present will return only that status otherwise
+ * an array of status' will be response.
+ */
+PackageManager.getJobStatus = function(req) {
+ var api_server = req.query["api_server"];
+ var uri = utils.confdPort(api_server);
+ var id = req.params['id'];
+ var url = utils.projectContextUrl(req, uri + '/api/operational/copy-jobs' + (id ? '/job/' + id : ''));
+ return new Promise(function(resolve, reject) {
+ request({
+ url: url,
+ method: 'GET',
+ headers: _.extend({}, constants.HTTP_HEADERS.accept.data, {
+ 'Authorization': req.get('Authorization')
+ }),
+ forever: constants.FOREVER_ON,
+ rejectUnauthorized: false
+ }, function(error, response, body) {
+ if (utils.validateResponse('restconfAPI.streams', error, response, body, resolve, reject)) {
+ var returnData;
+ if (id) {
+ returnData = JSON.parse(response.body)['rw-pkg-mgmt:job'];
+ } else {
+ var data = JSON.parse(response.body)['rw-pkg-mgmt:copy-jobs'];
+ returnData = (data && data.job) || [];
+ }
+ resolve({
+ statusCode: response.statusCode,
+ data: returnData
+ })
+ };
+ })
+ })
+}
+
+FileManager.addFile = function(req) {
+ console.log(' Uploading file', req.file.originalname, 'as', req.file.filename);
+ var api_server = req.query['api_server'];
+ var download_host = req.query['dev_download_server'];
+ var package_id = req.query['package_id'];
+ var package_type = req.query['package_type'].toUpperCase();
+ var package_path = req.query['package_path'];
+ if (!download_host) {
+ download_host = req.protocol + '://' + req.get('host');//api_server + ':' + utils.getPortForProtocol(req.protocol);
+ }
+ var input = {
+ 'external-url': download_host + '/composer/upload/' + req.query['package_id'] + '/' + req.file.filename,
+ 'package-type': package_type,
+ 'package-id': package_id,
+ 'package-path': package_path + '/' + req.file.filename
+ };
+
+ var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-file-add');
+
+ input = utils.addProjectContextToRPCPayload(req, uri, input);
+
+
+ return new Promise(function(resolve, reject) {
+ Promise.all([
+ rp({
+ uri: uri,
+ method: 'POST',
+ headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
+ 'Authorization': req.get('Authorization')
+ }),
+ forever: constants.FOREVER_ON,
+ rejectUnauthorized: false,
+ resolveWithFullResponse: true,
+ json: true,
+ body: {
+ input: input
+ }
+ })
+ ]).then(function(result) {
+ var data = {};
+ data['transaction_id'] = result[0].body['output']['task-id'];
+ resolve({
+ statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK,
+ data: data
+ });
+ }).catch(function(error) {
+ var res = {};
+ console.log('Problem with Composer.upload', error);
+ res.statusCode = error.statusCode || 500;
+ res.errorMessage = {
+ error: 'Failed to upload package ' + req.file.originalname + '. Error: ' + error
+ };
+ reject(res);
+ });
+ });
+}
+
FileManager.get = function(req) {
var api_server = req.query['api_server'];
var type = req.query['package_type'] && req.query['package_type'].toUpperCase();
}
module.exports = {
Composer:Composer,
- FileManager: FileManager
+ FileManager: FileManager,
+ PackageManager: PackageManager
};
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.3.13",
"babel-runtime": "^6.3.19",
+ "compression-webpack-plugin": "^0.3.2",
"css-loader": "^0.23.0",
"eslint": "^1.10.2",
"eslint-loader": "^1.1.1",
var C = require('./api/composer.js');
var Composer = C.Composer;
var FileManager = C.FileManager;
+var PackageManager = C.PackageManager;
var multer = require('multer');
var fs = require('fs');
var path = require('path');
res.send(error.errorMessage);
});
});
-router.post('/upload', cors(), upload.single('package'), function (req, res, next) {
- Composer.upload(req).then(function(data) {
+
+router.post('/api/file-manager', cors(), upload.single('package'), function (req, res, next) {
+ FileManager.addFile(req).then(function(data) {
utils.sendSuccessResponse(data, res);
}, function(error) {
utils.sendErrorResponse(error, res);
});
});
-router.use('/upload', cors(), express.static('upload/packages'));
-router.post('/update', cors(), upload.single('package'), function (req, res, next) {
- Composer.update(req).then(function(data) {
+router.get('/api/file-manager', cors(), function(req, res) {
+ FileManager.get(req).then(function(data) {
+ utils.sendSuccessResponse(data, res);
+ }, function(error) {
+ utils.sendErrorResponse(error, res);
+ });
+})
+router.get('/api/file-manager/jobs/:id', cors(), function(req, res) {
+ FileManager.job(req).then(function(data) {
+ utils.sendSuccessResponse(data, res);
+ }, function(error) {
+ utils.sendErrorResponse(error, res);
+ });
+});
+router.delete('/api/file-manager', cors(), function(req, res) {
+ FileManager.get(req).then(function(data) {
utils.sendSuccessResponse(data, res);
}, function(error) {
utils.sendErrorResponse(error, res);
});
});
-router.use('/update', cors(), express.static('upload/packages'));
-
+// Catalog operations via package manager
-router.post('/api/file-manager', cors(), upload.single('package'), function (req, res, next) {
- Composer.addFile(req).then(function(data) {
+router.post('/upload', cors(), upload.single('package'), function (req, res, next) {
+ PackageManager.upload(req).then(function(data) {
utils.sendSuccessResponse(data, res);
}, function(error) {
utils.sendErrorResponse(error, res);
});
});
+router.use('/upload', cors(), express.static('upload/packages'));
-router.get('/api/file-manager', cors(), function(req, res) {
- FileManager.get(req).then(function(data) {
+router.post('/update', cors(), upload.single('package'), function (req, res, next) {
+ PackageManager.update(req).then(function(data) {
utils.sendSuccessResponse(data, res);
}, function(error) {
utils.sendErrorResponse(error, res);
});
-})
-router.get('/api/file-manager/jobs/:id', cors(), function(req, res) {
- FileManager.job(req).then(function(data) {
+});
+router.use('/update', cors(), express.static('upload/packages'));
+
+router.post('/api/package-export', cors(), function (req, res, next) {
+ PackageManager.export(req).then(function(data) {
utils.sendSuccessResponse(data, res);
}, function(error) {
utils.sendErrorResponse(error, res);
});
});
-router.delete('/api/file-manager', cors(), function(req, res) {
- FileManager.get(req).then(function(data) {
+router.post('/api/package-copy', cors(), function (req, res, next) {
+ PackageManager.copy(req).then(function(data) {
utils.sendSuccessResponse(data, res);
}, function(error) {
utils.sendErrorResponse(error, res);
});
});
-
-router.post('/api/package-export', cors(), function (req, res, next) {
- Composer.exportPackage(req).then(function(data) {
+router.get('/api/package-manager/jobs/:id', cors(), function (req, res, next) {
+ PackageManager.getJobStatus(req).then(function(data) {
utils.sendSuccessResponse(data, res);
}, function(error) {
utils.sendErrorResponse(error, res);
npm install
echo 'Fetching third-party node_modules for '$PLUGIN_NAME'...done'
echo 'Packaging '$PLUGIN_NAME' using webpack'
-ui_plugin_cmake_build=true ./node_modules/.bin/webpack --progress --config webpack.production.config.js
+ui_plugin_cmake_build=true ./node_modules/.bin/webpack --optimize-minimize --progress --config webpack.production.config.js
echo 'Packaging '$PLUGIN_NAME' using webpack... done'
echo 'Building plugin '$PLUGIN_NAME'... done'
class CatalogPackageManagerActions {
constructor() {
- this.generateActions('downloadCatalogPackage', 'downloadCatalogPackageStatusUpdated', 'downloadCatalogPackageError', 'uploadCatalogPackage', 'uploadCatalogPackageStatusUpdated', 'uploadCatalogPackageError', 'removeCatalogPackage');
+ this.generateActions(
+ 'downloadCatalogPackage',
+ 'downloadCatalogPackageStatusUpdated',
+ 'downloadCatalogPackageError',
+ 'uploadCatalogPackage',
+ 'uploadCatalogPackageStatusUpdated',
+ 'uploadCatalogPackageError',
+ 'copyCatalogPackage',
+ 'updateStatus',
+ 'removeCatalogOperation'
+ );
}
}
*/
'use strict';
-import _ from 'lodash'
+import _includes from 'lodash/includes'
import cc from 'change-case'
import React from 'react'
import PureRenderMixin from 'react-addons-pure-render-mixin'
);
},
onDragOver(event) {
- const isDraggingFiles = _.includes(event.dataTransfer.types, 'Files');
+ const isDraggingFiles = _includes(event.dataTransfer.types, 'Files');
if (!isDraggingFiles) {
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
var createItem = function (catalogPackage) {
const onClickRemove = function () {
- CatalogPackageManagerActions.removeCatalogPackage(catalogPackage);
+ CatalogPackageManagerActions.removeCatalogOperation(catalogPackage);
};
const classNames = ClassNames('item', {'-error': catalogPackage.error, '-success': catalogPackage.success});
return (
);
};
- const packages = this.state.packages || [];
+ const operations = this.state.operations || [];
return (
<div className="CatalogPackageManager">
<div className="items">
- {packages.map(createItem)}
+ {operations.map(createItem)}
</div>
</div>
);
*/
'use strict';
-import _ from 'lodash'
+import _includes from 'lodash/includes'
import React from 'react'
import ReactDOM from 'react-dom'
import messages from './messages'
uiTransientState.isDrop = false;
uiTransientState.isDragging = true;
uiTransientState.wasTrayOpen = this.state.isTrayOpen;
- uiTransientState.isDraggingFiles = _.includes(e.dataTransfer.types, 'Files');
+ uiTransientState.isDraggingFiles = _includes(e.dataTransfer.types, 'Files');
const dragState = ComposerAppStore.getState().drag || {};
if (uiTransientState.isDraggingFiles || (dragState.type === 'catalog-item')) {
CatalogPanelTrayActions.open();
CatalogItemsActions.createCatalogItem(type);
},
onClickDuplicateCatalogItem() {
+ CatalogPanelTrayActions.open();
CatalogItemsActions.duplicateSelectedCatalogItem();
},
onClickExportCatalogItems() {
*/
'use strict';
-import _ from 'lodash'
+import _cloneDeep from 'lodash/cloneDeep'
import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin'
import messages from './messages'
const selectedContainer = selected[0];
if (selectedContainer) {
bodyComponent = <CatalogItemDetailsEditor container={selectedContainer} width={this.props.layout.right} />;
- const edit = _.cloneDeep(selectedContainer.model);
+ const edit = _cloneDeep(selectedContainer.model);
json = serializers.serialize(edit) || edit;
}
const jsonViewerTitle = selectedContainer ? selectedContainer.model.name : 'nothing selected';
*
* This class generates the form fields used to edit the CONFD JSON model.
*/
-'use strict';
-import _ from 'lodash'
+import _includes from 'lodash/includes'
+import _isArray from 'lodash/isArray'
+import _cloneDeep from 'lodash/cloneDeep'
+import _debounce from 'lodash/debounce';
import utils from '../libraries/utils'
import React from 'react'
import ClassNames from 'classnames'
import '../styles/EditDescriptorModelProperties.scss'
+const EMPTY_LEAF_PRESENT = '--empty-leaf-set--';
+
function getDescriptorMetaBasicForType(type) {
- const basicPropertiesFilter = d => _.includes(DESCRIPTOR_MODEL_FIELDS[type], d.name);
+ const basicPropertiesFilter = d => _includes(DESCRIPTOR_MODEL_FIELDS[type], d.name);
return DescriptorModelMetaFactory.getModelMetaForType(type, basicPropertiesFilter) || {properties: []};
}
function getDescriptorMetaAdvancedForType(type) {
- const advPropertiesFilter = d => !_.includes(DESCRIPTOR_MODEL_FIELDS[type], d.name);
+ const advPropertiesFilter = d => !_includes(DESCRIPTOR_MODEL_FIELDS[type], d.name);
return DescriptorModelMetaFactory.getModelMetaForType(type, advPropertiesFilter) || {properties: []};
}
create(model, path, property);
} else {
const name = path.join('.');
- const value = Property.createModelInstance(property);
+ // get a unique name for the new list item based on the current list content
+ // some lists, based on the key, may not get a uniqueName generated here
+ const uniqueName = DescriptorModelMetaFactory.generateItemUniqueName(container.model[property.name], property);
+ const value = Property.createModelInstance(property, uniqueName);
utils.assignPathValue(this.model, name, value);
}
CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
);
}
- function onFormFieldValueChanged(event) {
- if (DescriptorModelFactory.isContainer(this)) {
- event.preventDefault();
- const name = event.target.name;
- const value = event.target.value;
- utils.assignPathValue(this.model, name, value);
- CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
- }
- }
-
function buildField(container, property, path, value, fieldKey) {
let cds = CatalogDataStore;
let catalogs = cds.getTransientCatalogs();
- const name = path.join('.');
+ const pathToProperty = path.join('.');
+ const isEditable = true;
const isGuid = Property.isGuid(property);
const isBoolean = Property.isBoolean(property);
- const onChange = onFormFieldValueChanged.bind(container);
const isEnumeration = Property.isEnumeration(property);
const isLeafRef = Property.isLeafRef(property);
const onFocus = onFocusPropertyFormInputElement.bind(container, property, path, value);
const placeholder = changeCase.title(property.name);
const className = ClassNames(property.name + '-input', {'-is-guid': isGuid});
- const fieldValue = value ? (value.constructor.name != "Object") ? value : '' : undefined;
+ const fieldValue = value ? (value.constructor.name != "Object") ? value : '' : (isNaN(value) ? undefined : value);
+
+ // process the named field value change
+ function processFieldValueChange(name, value) {
+ console.debug('processed change for -- ' + name + ' -- with value -- ' + value);
+ // this = the container being edited
+ if (DescriptorModelFactory.isContainer(this)) {
+ utils.assignPathValue(this.model, name, value);
+ CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+ }
+ }
+
+ // change handler used for onChange event
+ const changeHandler = (handleValueChange, event) => {
+ event.preventDefault();
+ console.debug(event.target.value);
+ handleValueChange(event.target.value);
+ };
+ // create an onChange event handler for a text field for the specified field path (debounced to accumulate chars)
+ const onTextChange = changeHandler.bind(null, _debounce(
+ processFieldValueChange.bind(container, pathToProperty), 2000, {maxWait: 5000})); // max wait for short-name
+ // create an onChange event handler for a select field for the specified field path
+ const onSelectChange = changeHandler.bind(null, processFieldValueChange.bind(container, pathToProperty));
+
if (isEnumeration) {
const enumeration = Property.getEnumeration(property, value);
const options = enumeration.map((d, i) => {
// so we categorically ignore them
// https://trello.com/c/uzEwVx6W/230-bug-enum-should-not-use-index-only-name
//return <option key={fieldKey + ':' + i} value={d.value}>{d.name}</option>;
- return <option key={fieldKey.toString() + ':' + i} value={d.name}>{d.name}</option>;
+ return <option key={':' + i} value={d.name}>{d.name}</option>;
});
const isValueSet = enumeration.filter(d => d.isSelected).length > 0;
if (!isValueSet || property.cardinality === '0..1') {
const noValueDisplayText = changeCase.title(property.name);
- options.unshift(<option key={'(value-not-in-enum)' + fieldKey.toString()} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
+ options.unshift(<option key={'(value-not-in-enum)'} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
}
- return <select key={fieldKey.toString()} id={fieldKey.toString()} className={ClassNames({'-value-not-set': !isValueSet})} name={name} value={value} title={name} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} disabled={!isEditable}>{options}</select>;
+ return (
+ <select
+ key={fieldKey}
+ id={fieldKey}
+ className={ClassNames({'-value-not-set': !isValueSet})}
+ defaultValue={value}
+ title={pathToProperty}
+ onChange={onSelectChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
+ disabled={!isEditable}>
+ {options}
+ </select>
+ );
}
if (isLeafRef) {
- let fullFieldKey = _.isArray(fieldKey) ? fieldKey.join(':') : fieldKey;
+ let fullPathString = container.key + ':' + path.join(':');
let containerRef = container;
while (containerRef.parent) {
- fullFieldKey = containerRef.parent.key + ':' + fullFieldKey;
+ fullPathString = containerRef.parent.key + ':' + fullPathString;
containerRef = containerRef.parent;
}
- const leafRefPathValues = Property.getLeafRef(property, path, value, fullFieldKey, catalogs, container);
+ const leafRefPathValues = Property.getLeafRef(property, path, value, fullPathString, catalogs, container);
const options = leafRefPathValues && leafRefPathValues.map((d, i) => {
- return <option key={fieldKey.toString() + ':' + i} value={d.value}>{d.value}</option>;
+ return <option key={':' + i} value={d.value}>{d.value}</option>;
});
const isValueSet = leafRefPathValues.filter(d => d.isSelected).length > 0;
if (!isValueSet || property.cardinality === '0..1') {
const noValueDisplayText = changeCase.title(property.name);
- options.unshift(<option key={'(value-not-in-leafref)' + fieldKey.toString()} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
+ options.unshift(<option key={'(value-not-in-leafref)'} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
}
- return <select key={fieldKey.toString()} id={fieldKey.toString()} className={ClassNames({'-value-not-set': !isValueSet})} name={name} value={value} title={name} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} disabled={!isEditable}>{options}</select>;
+ return (
+ <select
+ key={fieldKey}
+ id={fieldKey}
+ className={ClassNames({'-value-not-set': !isValueSet})}
+ defaultValue={value}
+ title={pathToProperty}
+ onChange={onSelectChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
+ disabled={!isEditable}>
+ {options}
+ </select>
+ );
}
if (isBoolean) {
- let fullFieldKey = _.isArray(fieldKey) ? fieldKey.join(':') : fieldKey;
- let containerRef = container;
- while (containerRef.parent) {
- fullFieldKey = containerRef.parent.key + ':' + fullFieldKey;
- containerRef = containerRef.parent;
- }
-
const options = [
- <option key={fieldKey.toString() + '-true'} value="TRUE">TRUE</option>,
- <option key={fieldKey.toString() + '-false'} value="FALSE">FALSE</option>
+ <option key={'true'} value="TRUE">TRUE</option>,
+ <option key={'false'} value="FALSE">FALSE</option>
]
// if (!isValueSet) {
const noValueDisplayText = changeCase.title(property.name);
- options.unshift(<option key={'(value-not-in-leafref)' + fieldKey.toString()} value="" placeholder={placeholder}></option>);
+ options.unshift(<option key={'(value-not-in-leafref)'} value="" placeholder={placeholder}></option>);
// }
let val = value;
if(typeof(val) == 'number') {
val = value ? "TRUE" : "FALSE"
}
const isValueSet = (val != '' && val)
- return <select key={fieldKey.toString()} id={fieldKey.toString()} className={ClassNames({'-value-not-set': !isValueSet})} name={name} value={val && val.toUpperCase()} title={name} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} disabled={!isEditable}>{options}</select>;
+ return (
+ <select
+ key={fieldKey}
+ id={fieldKey}
+ className={ClassNames({'-value-not-set': !isValueSet})}
+ defaultValue={val && val.toUpperCase()}
+ title={pathToProperty}
+ onChange={onSelectChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
+ disabled={!isEditable}>
+ {options}
+ </select>
+ );
+ }
+
+ if (Property.isLeafEmpty(property)) {
+ // A null value indicates the leaf exists (as opposed to undefined).
+ // We stick in a string when the user actually sets it to simplify things
+ // but the correct thing happens when we serialize to user data
+ let isEmptyLeafPresent = (value === EMPTY_LEAF_PRESENT || value === null);
+ let present = isEmptyLeafPresent ? EMPTY_LEAF_PRESENT : "";
+ const options = [
+ <option key={'true'} value={EMPTY_LEAF_PRESENT}>Enabled</option>,
+ <option key={'false'} value="">Not Enabled</option>
+ ]
+
+ return (
+ <select
+ key={fieldKey}
+ id={fieldKey}
+ className={ClassNames({'-value-not-set': !isEmptyLeafPresent})}
+ defaultValue={present}
+ title={pathToProperty}
+ onChange={onSelectChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
+ disabled={!isEditable}>
+ {options}
+ </select>
+ );
}
if (property['preserve-line-breaks']) {
- return <textarea key={fieldKey.toString()} cols="5" id={fieldKey.toString()} name={name} value={value} placeholder={placeholder} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} onMouseOut={endEditing} onMouseLeave={endEditing} readOnly={!!isEditable} />;
+ return (
+ <textarea
+ key={fieldKey}
+ cols="5"
+ id={fieldKey}
+ defaultValue={value}
+ placeholder={placeholder}
+ onChange={onTextChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
+ onMouseOut={endEditing}
+ onMouseLeave={endEditing}
+ disabled={!isEditable} />
+ );
}
- return <input
- key={fieldKey.toString()}
- id={fieldKey.toString()}
- type="text"
- name={name}
- value={fieldValue}
- className={className}
- placeholder={placeholder}
- onChange={onChange}
- onFocus={onFocus}
- onBlur={endEditing}
- onMouseDown={startEditing}
- onMouseOver={startEditing}
- onMouseOut={endEditing}
- onMouseLeave={endEditing}
- readOnly={!isEditable}
- />;
+ return (
+ <input
+ key={fieldKey}
+ id={fieldKey}
+ type="text"
+ defaultValue={fieldValue}
+ className={className}
+ placeholder={placeholder}
+ onChange={onTextChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
+ onMouseOut={endEditing}
+ onMouseLeave={endEditing}
+ disabled={!isEditable}
+ />
+ );
}
function buildChoice(container, property, path, value, key) {
- function onFormFieldValueChanged(event) {
+ function processChoiceChange(name, value) {
if (DescriptorModelFactory.isContainer(this)) {
- event.preventDefault();
-
- let name = event.target.name;
- const value = event.target.value;
-
-
/*
Transient State is stored for convenience in the uiState field.
The choice yang type uses case elements to describe the "options".
isTopCase = true;
choiceObject = utils.resolvePath(this.model, [selected].join('.'));
}
- utils.assignPathValue(stateObject, [selected].join('.'), _.cloneDeep(choiceObject));
+ utils.assignPathValue(stateObject, [selected].join('.'), _cloneDeep(choiceObject));
if(selected) {
if(this.model.uiState.choice.hasOwnProperty(name)) {
utils.assignPathValue(this.model, [value].join('.'), newChoiceObject)
}
-
// update the selected name
utils.assignPathValue(this.model, statePath.concat('selected').join('.'), value);
}
}
+ const pathToChoice = path.join('.');
const caseByNameMap = {};
- const onChange = onFormFieldValueChanged.bind(container);
+ const choiceChangeHandler = processChoiceChange.bind(container, pathToChoice);
+ const onChange = ((handleChoiceChange, event) => {
+ event.preventDefault();
+ handleChoiceChange(event.target.value);
+ }).bind(null, choiceChangeHandler);
+
const cases = property.properties.map(d => {
if (d.type === 'case') {
- caseByNameMap[d.name] = d.properties[0];
+ //Previous it was assumed that a case choice would have only one property. Now we pass on either the only item or the
+ caseByNameMap[d.name] = d.properties && (d.properties.length == 1 ? d.properties[0] : d.properties);
return {
optionName: d.name,
optionTitle: d.description,
);
});
- const selectName = path.join('.');
- let selectedOptionPath = ['uiState.choice', selectName, 'selected'].join('.');
+ let selectedOptionPath = ['uiState.choice', pathToChoice, 'selected'].join('.');
//Currently selected choice/case statement on UI model
let selectedOptionValue = utils.resolvePath(container.model, selectedOptionPath);
//If first time loaded, and none is selected, check if there is a value corresponding to a case statement in the container model
if(!selectedOptionValue) {
//get field properties for choice on container model
- let fieldProperties = utils.resolvePath(container.model, selectName);
+ let fieldProperties = utils.resolvePath(container.model, pathToChoice);
if(fieldProperties) {
//Check each case statement in model and see if it is present in container model.
cases.map(function(c){
if(fieldProperties.hasOwnProperty(c.optionValue.split('.')[1])) {
- utils.assignPathValue(container.model, ['uiState.choice', selectName, 'selected'].join('.'), c.optionValue);
+ utils.assignPathValue(container.model, ['uiState.choice', pathToChoice, 'selected'].join('.'), c.optionValue);
}
});
- selectedOptionValue = utils.resolvePath(container.model, ['uiState.choice', selectName, 'selected'].join('.'));
+ selectedOptionValue = utils.resolvePath(container.model, ['uiState.choice', pathToChoice, 'selected'].join('.'));
} else {
property.properties.map(function(p) {
let pname = p.properties[0].name;
if(container.model.hasOwnProperty(pname)) {
- utils.assignPathValue(container.model, ['uiState.choice', selectName, 'selected'].join('.'), [p.name, pname].join('.'));
+ utils.assignPathValue(container.model, ['uiState.choice', pathToChoice, 'selected'].join('.'), [p.name, pname].join('.'));
}
})
- selectedOptionValue = utils.resolvePath(container.model, ['uiState.choice', selectName, 'selected'].join('.'));
+ selectedOptionValue = utils.resolvePath(container.model, ['uiState.choice', pathToChoice, 'selected'].join('.'));
}
}
//If selectedOptionValue is present, take first item in string which represents the case name.
const valueProperty = caseByNameMap[selectedOptionValue ? selectedOptionValue.split('.')[0] : undefined] || {properties: []};
const isLeaf = Property.isLeaf(valueProperty);
- const hasProperties = _.isArray(valueProperty.properties) && valueProperty.properties.length;
+ const hasProperties = _isArray(valueProperty.properties) && valueProperty.properties.length;
const isMissingDescriptorMeta = !hasProperties && !Property.isLeaf(valueProperty);
//Some magic that prevents errors for arising
- const valueResponse = valueProperty.properties.length ? valueProperty.properties.map((d, i) => {
+ const valueResponse = valueProperty.properties && valueProperty.properties.length ? valueProperty.properties.map(valuePropertyFn) : (!isMissingDescriptorMeta) ? build(container, valueProperty, path.concat(valueProperty.name), utils.resolvePath(container.model, path.concat(valueProperty.name).join('.')) || container.model[valueProperty.name]) :
+ valueProperty.map && valueProperty.map(valuePropertyFn);
+ function valuePropertyFn(d, i) {
const childPath = path.concat(valueProperty.name, d.name);
const childValue = utils.resolvePath(container.model, childPath.join('.'));
return (
{build(container, d, childPath, childValue, props)}
</div>
);
- }) : (!isMissingDescriptorMeta) ? build(container, valueProperty, path.concat(valueProperty.name), utils.resolvePath(container.model, path.concat(valueProperty.name).join('.')) || container.model[valueProperty.name]) : null
+ }
// end magic
const onFocus = onFocusPropertyFormInputElement.bind(container, property, path, value);
return (
<div key={key} className="choice">
- <select key={Date.now()} className={ClassNames({'-value-not-set': !selectedOptionValue})} name={selectName} value={selectedOptionValue} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} onMouseOut={endEditing} onMouseLeave={endEditing} readOnly={!isEditable}>
+ <select
+ key={Date.now()}
+ className={ClassNames({'-value-not-set': !selectedOptionValue})}
+ defaultValue={selectedOptionValue}
+ onChange={onChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
+ onMouseOut={endEditing}
+ onMouseLeave={endEditing}
+ disabled={!isEditable}
+ >
{options}
</select>
{valueResponse}
}
- function buildSimpleListItem(container, property, path, value, key, index) {
+ function buildSimpleListItem(container, property, path, value, uniqueId, index) {
// todo need to abstract this better
const title = getTitle(value);
var req = require.context("../", true, /\.svg/);
return (
- <div>
- <a href="#select-list-item" key={Date.now()} className={property.name + '-list-item simple-list-item '} onClick={onClickSelectItem.bind(container, property, path, value)}>
+ <div key={uniqueId} >
+ <a href="#select-list-item" className={property.name + '-list-item simple-list-item '} onClick={onClickSelectItem.bind(container, property, path, value)}>
<img src={req('./' + DescriptorModelIconFactory.getUrlForType(property.name))} width="20px" />
<span>{title}</span>
</a>
);
}
- function buildRemoveListItem(container, property, valuePath, fieldKey, index) {
+ function buildRemoveListItem(container, property, valuePath, index) {
const className = ClassNames(property.name + '-remove actions');
return (
- <div key={fieldKey.concat(index).join(':')} className={className}>
+ <div className={className}>
<h3>
<span className={property.type + '-name name'}>{changeCase.title(property.name)}</span>
<span className="info">{index + 1}</span>
);
}
- function buildLeafListItem(container, property, valuePath, value, key, index) {
+ function buildLeafListItem(container, property, valuePath, value, uniqueId, index) {
// look at the type to determine how to parse the value
return (
- <div>
- {buildRemoveListItem(container, property, valuePath, key, index)}
- {buildField(container, property, valuePath, value, key)}
+ <div key={uniqueId}>
+ {buildRemoveListItem(container, property, valuePath, index)}
+ {buildField(container, property, valuePath, value, uniqueId)}
</div>
);
const isArray = Property.isArray(property);
const isObject = Property.isObject(property);
const isLeafList = Property.isLeafList(property);
- const fieldKey = [container.id].concat(path);
const isRequired = Property.isRequired(property);
const title = changeCase.titleCase(property.name);
const columnCount = property.properties.length || 1;
const isColumnar = isArray && (Math.round(props.width / columnCount) > 155);
const classNames = {'-is-required': isRequired, '-is-columnar': isColumnar};
+ // create a unique Id for use as react component keys and html element ids
+ // use uid (from ui info) instead of id property (which is not stable)
+ let uniqueId = container.uid;
+ let containerRef = container;
+ while (containerRef.parent) {
+ uniqueId = containerRef.parent.uid + ':' + uniqueId;
+ containerRef = containerRef.parent;
+ }
+ uniqueId += ':' + path.join(':')
+
if (!property.properties && isObject) {
const uiState = DescriptorModelMetaFactory.getModelMetaForType(property.name) || {};
property.properties = uiState.properties;
}
- const hasProperties = _.isArray(property.properties) && property.properties.length;
+ const hasProperties = _isArray(property.properties) && property.properties.length;
const isMissingDescriptorMeta = !hasProperties && !Property.isLeaf(property);
// ensure value is not undefined for non-leaf property types
value = isArray ? [] : {};
}
}
- const valueAsArray = _.isArray(value) ? value : isLeafList && typeof value === 'undefined' ? [] : [value];
+ const valueAsArray = _isArray(value) ? value : isLeafList && typeof value === 'undefined' ? [] : [value];
const isMetaField = property.name === 'meta';
const isCVNFD = property.name === 'constituent-vnfd';
valueAsArray.forEach((value, index) => {
let field;
- const key = fieldKey.slice();
const valuePath = path.slice();
+ // create a unique field Id for use as react component keys and html element ids
+ // notes:
+ // keys only need to be unique on components in the same array
+ // html element ids should be unique with the document (or form)
+ let fieldId = uniqueId;
if (isArray) {
valuePath.push(index);
- key.push(index);
+ fieldId += index;
}
if (isMetaField) {
}
if (isMissingDescriptorMeta) {
- field = <span key={key.concat('warning').join(':')} className="warning">No Descriptor Meta for {property.name}</span>;
+ field = <span key={fieldId} className="warning">No Descriptor Meta for {property.name}</span>;
} else if (property.type === 'choice') {
- field = buildChoice(container, property, valuePath, value, key.join(':'));
+ field = buildChoice(container, property, valuePath, value, fieldId);
} else if (isSimpleListView) {
- field = buildSimpleListItem(container, property, valuePath, value, key, index);
+ field = buildSimpleListItem(container, property, valuePath, value, fieldId, index);
} else if (isLeafList) {
- field = buildLeafListItem(container, property, valuePath, value, key, index);
+ field = buildLeafListItem(container, property, valuePath, value, fieldId, index);
} else if (hasProperties) {
- field = buildElement(container, property, valuePath, value, key.join(':'))
+ field = buildElement(container, property, valuePath, value, fieldId)
} else {
- field = buildField(container, property, valuePath, value, key.join(':'));
+ field = buildField(container, property, valuePath, value, fieldId);
}
function onClickLeaf(property, path, value, event) {
event.preventDefault();
event.stopPropagation();
this.getRoot().uiState.focusedPropertyPath = path.join('.');
- console.log('property selected', path.join('.'));
+ console.debug('property selected', path.join('.'));
ComposerAppActions.propertySelected([path.join('.')]);
}
const isContainerList = isArray && !(isSimpleListView || isLeafList);
fields.push(
- <div key={fieldKey.concat(['property-content', index]).join(':')}
+ <div key={fieldId}
className={ClassNames('property-content', {'simple-list': isSimpleListView})}
onClick={clickHandler.bind(container, property, valuePath, value)}>
- {isContainerList ? buildRemoveListItem(container, property, valuePath, fieldKey, index) : null}
+ {isContainerList ? buildRemoveListItem(container, property, valuePath, index) : null}
{field}
</div>
);
const onFocus = isLeaf ? event => event.target.classList.add('-is-focused') : false;
return (
- <div key={fieldKey.join(':')} className={ClassNames(property.type + '-property property', classNames)} onFocus={onFocus}>
+ <div key={uniqueId} className={ClassNames(property.type + '-property property', classNames)} onFocus={onFocus}>
<h3 className="property-label">
- <label htmlFor={fieldKey.join(':')}>
+ <label htmlFor={uniqueId}>
<span className={property.type + '-name name'}>{title}</span>
<small>
<span className={property.type + '-info info'}>{displayValueInfo}</span>
{buildAdvancedGroup()}
</div>
);
+};
-}
//https://raw.githubusercontent.com/RIFTIO/RIFT.ware/master/rift-shell
-import _ from 'lodash'
+import _cloneDeep from 'lodash/cloneDeep'
+import _findIndex from 'lodash/findIndex'
import React from 'react';
import ReactDOM from 'react-dom';
import TreeView from 'react-treeview';
let splitUrl = url.split('/');
let fileName = splitUrl[splitUrl.length - 1];
folder.pop;
- let fullPath = _.cloneDeep(folder);
+ let fullPath = _cloneDeep(folder);
fullPath.push(fileName);
fullPath = fullPath.join('/');
folder = folder.join('/');
- let fileIndex = _.findIndex(files[folder], function(f) {
+ let fileIndex = _findIndex(files[folder], function(f) {
return f.name == fullPath;
})
if (fileIndex == -1) {
function buildList(self, data) {
let toReturn = [];
data.id.map(function(k,i) {
- toReturn.push (contentFolder(self, data.data[k], k, i, self.props.filesState, self.updateFileLocationInput, self.sendDownloadFileRequst, self.deleteFile));
+ toReturn.push (contentFolder(self, data.data[k], k, k+i, self.props.filesState, self.updateFileLocationInput, self.sendDownloadFileRequst, self.deleteFile));
});
return toReturn.reverse();
}
function contentFolder(context, folder, path, key, inputState, updateFn, sendDownloadFileRequst, deleteFn) {
let type = context.props.type;
let id = context.props.item.id;
- const onboardDropZone = createDropZone.bind(this, FileManagerUploadDropZone.ACTIONS.onboard, '.ComposerAppAddFile.' + path.replace(/\//g, '-'), type, id, path);
+ let classId = `DZ-${path.replace(/\/|\s+/g, '-')}`;
+ const onboardDropZone = createDropZone.bind(this, FileManagerUploadDropZone.ACTIONS.onboard, '.ComposerAppAddFile.' + classId, type, id, path);
return (
<Panel title={path} key={key} itemClassName="nested" no-corners>
<div className="folder">
}
render() {
let {type, id, path, key, ...props} = this.props;
+ let classId = `DZ-${path.replace(/\/|\s+/g, '-')}`;
return (
<div className="inputSection">
<label className="sqTextInput" style={{flexDirection: 'row', alignItems:'center'}}>
<span>Upload File</span>
- <Button className={'ComposerAppAddFile ' + path.replace(/\//g, '-')} label="BROWSE"/>
+ <Button className={'ComposerAppAddFile ' + classId} label="BROWSE"/>
</label>
</div>
)
*/
'use strict';
-import _ from 'lodash'
import $ from 'jquery'
import alt from '../../alt'
import utils from '../../libraries/utils'
beforeSend: Utils.addAuthorizationStub,
url: 'api/file-manager?api_server=' + utils.getSearchParams(window.location).api_server +'&package_type=' + type + '&package_id=' + id + '&package_path=' + path ,
success: function(data) {
- resolve({
- data: data,
- path: path
- });
+ if (data.output.status == 'True') {
+ resolve({
+ data: data,
+ path: path
+ });
+ } else {
+ reject({
+ data: data,
+ path: path
+ })
+ }
},
error: function(error) {
if (typeof error == 'string') {
error = JSON.parse(error);
}
- reject(error);
+ reject({
+ path: path,
+ data: error
+ });
}
}).fail(function(xhr){
//Authentication and the handling of fail states should be wrapped up into a connection class.
return new Promise(function(resolve, reject) {
//api/operational/download-jobs/job/
$.ajax({
- url: '/socket-polling',
+ url: '/socket-polling?api_server=' + API_SERVER,
type: 'POST',
beforeSend: Utils.addAuthorizationStub,
data: {
- url: 'http://localhost:8000/composer/api/file-manager/jobs/' + packageID + '?api_server=' + API_SERVER,
+ url: 'composer/api/file-manager/jobs/' + packageID + '?api_server=' + API_SERVER,
},
success: function(data, textStatus, jqXHR) {
Utils.checkAndResolveSocketRequest(data, resolve, reject);
return new Promise(function(resolve, reject) {
//api/operational/download-jobs/job/
$.ajax({
- url: '/socket-polling',
+ url: '/socket-polling?api_server=' + API_SERVER,
type: 'POST',
beforeSend: Utils.addAuthorizationStub,
data: {
- url: 'http://localhost:8000/composer/api/file-manager?api_server=' + utils.getSearchParams(window.location).api_server +'&package_type=' + type + '&package_id=' + id
+ url: 'composer/api/file-manager?api_server=' + utils.getSearchParams(window.location).api_server +'&package_type=' + type + '&package_id=' + id
},
success: function(data, textStatus, jqXHR) {
Utils.checkAndResolveSocketRequest(data, resolve, reject);
* Created by onvelocity on 10/2/15.
*/
-import _ from 'lodash'
import d3 from 'd3'
import UID from './UniqueId'
import SelectionManager from './SelectionManager'
'use strict';
-import _ from 'lodash'
import d3 from 'd3'
import UID from './UniqueId'
import React from 'react'
*/
'use strict';
-import _ from 'lodash'
import d3 from 'd3'
import math from './math'
import ClassNames from 'classnames'
'use strict';
-import _ from 'lodash'
+import _isArray from 'lodash/isArray'
import d3 from 'd3'
/**
Array.from(document.querySelectorAll(`svg .forwarding-graph-paths`)).forEach(d => {
d3.select(d).classed('-is-highlighting', true);
});
- const list = _.isArray(rsp) ? rsp : [rsp];
+ const list = _isArray(rsp) ? rsp : [rsp];
list.forEach(rsp => {
Array.from(document.querySelectorAll(`[data-id="${rsp.id}"]`)).forEach(d => {
d.parentNode.appendChild(d);
* Created by onvelocity on 2/10/16.
*/
import alt from '../../../alt'
-import _ from 'lodash'
import d3 from 'd3'
import math from '../math'
import ClassNames from 'classnames'
* Created by onvelocity on 8/23/15.
*/
-import _ from 'lodash'
+import _isArray from 'lodash/isArray'
import guid from '../guid'
import Position from '../graph/Position'
import IconFactory from './IconFactory'
updateModelList(modelFieldName, modelFieldValue, descriptorClass = DescriptorModel, newItemAddedSuccessCallback = () => {}) {
// value can be Array of (DescriptorModel | json model), DescriptorModel, or json model
- if (_.isArray(modelFieldValue)) {
+ if (_isArray(modelFieldValue)) {
this.model[modelFieldName] = modelFieldValue.map(d => d instanceof descriptorClass ? d.model : d);
return true;
}
'use strict';
-import _ from 'lodash'
+import _isEmpty from 'lodash/isEmpty'
+import _cloneDeep from 'lodash/cloneDeep'
import d3 from 'd3'
import UID from './../UniqueId'
import guid from '../guid'
}
return (containerList, obj) => {
- if (_.isEmpty(obj)) {
+ if (_isEmpty(obj)) {
return containerList;
}
switch (obj.uiState.type) {
} else {
model = vnfdToWrap;
}
- return new VirtualNetworkFunctionReadOnlyWrapper(_.cloneDeep(model), parent);
+ return new VirtualNetworkFunctionReadOnlyWrapper(_cloneDeep(model), parent);
}
static newClassifier(model, parent) {
* This class provides methods to get the metadata about descriptor models.
*/
-'use strict';
-
-import _ from 'lodash'
-import utils from './../utils'
+import _cloneDeep from 'lodash/cloneDeep'
+import _isEmpty from 'lodash/isEmpty'
+import _pick from 'lodash/pick'
+import _get from 'lodash/get'
+import _set from 'lodash/set'
import DescriptorModelMetaProperty from './DescriptorModelMetaProperty'
import CommonUtils from 'utils/utils';
const assign = Object.assign;
return type;
}
+const uiStateToSave = ['containerPositionMap'];
+
+//////
+// Data serialization will be done on a meta model basis. That is,
+// given a schema and data, retrieve from the data only that which is
+// defined by the schema.
+//
+
+// serialize data for a list of properties
+function serializeAll(properties, data) {
+ if (data) {
+ return properties.reduce((obj, p) => {
+ return Object.assign(obj, p.serialize(data));
+ }, {});
+ }
+ return null;
+}
+
+function serialize_container(data) {
+ data = data[this.name];
+ if (_isEmpty(data)) {
+ return null;
+ }
+ let obj = {};
+ obj[this.name] = serializeAll(this.properties, data);
+ return obj;
+}
+
+function serialize_list(data) {
+ data = data[this.name];
+ if (data) {
+ if (!Array.isArray(data)) {
+ return serializeAll(this.properties, data);
+ } else if (data.length) {
+ let list = data.reduce((c, d) => {
+ let obj = serializeAll(this.properties, d);
+ if (!_isEmpty(obj)) {
+ c.push(obj);
+ }
+ return c;
+ }, []);
+ if (!_isEmpty(list)){
+ let obj = {};
+ obj[this.name] = list;
+ return obj;
+ }
+ }
+ }
+ return null;
+}
+
+function serialize_leaf(data) {
+ let value = data[this.name];
+ if (value === null || typeof value === 'undefined' || value === '') {
+ return null;
+ }
+ let obj = {};
+ if (this['data-type'] === 'empty') {
+ value = ''; // empty string does get sent as value
+ }
+ obj[this.name] = value;
+ return obj;
+}
+
+function serialize_leaf_empty(data) {
+ let value = data[this.name];
+ if (value) {
+ let obj = {};
+ obj[this.name] = "";
+ return obj;
+ }
+ return null;
+}
+
+function serialize_leaf_list(data) {
+ data = data[this.name];
+ if (data) {
+ commaSeparatedValues = data.reduce((d, v) => {
+ let leaf = Serializer.leaf.call(this, d);
+ let value = leaf & leaf[this.name];
+ if (value && value.length) {
+ if (v.length) {
+ v += ', ';
+ }
+ v += value;
+ }
+ }, "");
+ if (commaSeparatedValues.length) {
+ let obj = {};
+ obj[this.name] = commaSeparatedValues;
+ return obj;
+ }
+ }
+ return null;
+}
+
+function serialize_choice(data) {
+ let keys = Object.keys(data);
+ if (keys) {
+ const chosen = this.properties.find(
+ c => c.type === 'case' && c.properties && c.properties.some(p => keys.indexOf(p.name) > -1));
+ return chosen && serializeAll(chosen.properties, data);
+ }
+ return null;
+}
+
+function serialize_case(data) {
+ return Serializer.container.call(this, data);
+}
+
+// special ui data handler for leaf of type string named 'meta'
+function serialize_meta(data) {
+ let uiState = data['uiState'];
+ let meta = uiState && _pick(uiState, uiStateToSave);
+ // if there is no uiState to save perhaps this was not a ui state property
+ return _isEmpty(meta) ? null : {meta: JSON.stringify(meta)};
+}
+
+function serialize_unsupported(data) {
+ console.error('unsupported property', property);
+ return null;
+}
+
+function getSerializer(property) {
+ switch (property.name) {
+ case 'rw-nsd:meta':
+ case 'rw-vnfd:meta':
+ return serialize_meta.bind(property);
+ }
+ switch (property.type) {
+ case 'list':
+ return serialize_list.bind(property);
+ case 'container':
+ return serialize_container.bind(property);
+ case 'choice':
+ return serialize_choice.bind(property);
+ case 'case':
+ return serialize_case.bind(property);
+ case 'leaf_list':
+ return serialize_leaf_list.bind(property);
+ case 'leaf':
+ switch (property['data-type']){
+ case 'empty':
+ return serialize_leaf_empty.bind(property);
+ }
+ return serialize_leaf.bind(property);
+ }
+ return serialize_unsupported.bind(property);
+}
+
let modelMetaByPropertyNameMap = [];
let cachedDescriptorModelMetaRequest = null;
export default {
init() {
if (!cachedDescriptorModelMetaRequest) {
- cachedDescriptorModelMetaRequest = new Promise(function(resolve, reject) {
- CommonUtils.getDescriptorModelMeta().then(function(data) {
+ cachedDescriptorModelMetaRequest = new Promise(function (resolve, reject) {
+ CommonUtils.getDescriptorModelMeta().then(function (data) {
let DescriptorModelMetaJSON = data;
modelMetaByPropertyNameMap = Object.keys(DescriptorModelMetaJSON).reduce((map, key) => {
function mapProperties(parentMap, parentObj) {
+ // let's beef up the meta info with a helper (more to come?)
+ parentObj.serialize = getSerializer(parentObj);
parentMap[':meta'] = parentObj;
const properties = parentObj && parentObj.properties ? parentObj.properties : [];
properties.forEach(p => {
- parentMap[p.name] = mapProperties({}, assign(p, {':qualified-type': parentObj[':qualified-type'] + '.' + p.name}));
+ parentMap[p.name] = mapProperties({}, assign(p, {
+ ':qualified-type': parentObj[':qualified-type'] + '.' + p.name
+ }));
return map;
}, parentMap);
return parentMap;
}
- map[key] = mapProperties({}, assign(DescriptorModelMetaJSON[key], {':qualified-type': key}));
+ map[key] = mapProperties({}, assign(DescriptorModelMetaJSON[key], {
+ ':qualified-type': key
+ }));
return map;
}, {});
(() => {
// initialize the UI centric properties that CONFD could care less about
- utils.assignPathValue(modelMetaByPropertyNameMap, 'nsd.meta.:meta.preserve-line-breaks', true);
- utils.assignPathValue(modelMetaByPropertyNameMap, 'vnfd.meta.:meta.preserve-line-breaks', true);
- utils.assignPathValue(modelMetaByPropertyNameMap, 'vnfd.vdu.cloud-init.:meta.preserve-line-breaks', true);
- utils.assignPathValue(modelMetaByPropertyNameMap, 'nsd.constituent-vnfd.vnf-configuration.config-template.:meta.preserve-line-breaks', true);
+ _set(modelMetaByPropertyNameMap, 'nsd.meta.:meta.preserve-line-breaks', true);
+ _set(modelMetaByPropertyNameMap, 'vnfd.meta.:meta.preserve-line-breaks', true);
+ _set(modelMetaByPropertyNameMap, 'vnfd.vdu.cloud-init.:meta.preserve-line-breaks', true);
+ _set(modelMetaByPropertyNameMap, 'nsd.constituent-vnfd.vnf-configuration.config-template.:meta.preserve-line-breaks', true);
})();
resolve();
- }, function(error) {
+ }, function (error) {
cachedDescriptorModelMetaRequest = null;
})
})
return cachedDescriptorModelMetaRequest;
},
- createModelInstanceForType(typeOrPath) {
+ /**
+ * Create a new instance of the indicated property and, if relevent, use the given
+ * unique name for the instance's key (see generateItemUniqueName)
+ *
+ * @param {Object | string} typeOrPath a property definition object or a path to a property
+ * @param [{string}] uniqueName optional
+ * @returns
+ */
+ createModelInstanceForType(typeOrPath, uniqueName) {
const modelMeta = this.getModelMetaForType(typeOrPath);
- return DescriptorModelMetaProperty.createModelInstance(modelMeta);
+ return DescriptorModelMetaProperty.createModelInstance(modelMeta, uniqueName);
},
getModelMetaForType(typeOrPath, filterProperties = () => true) {
// resolve paths like 'nsd' or 'vnfd.vdu' or 'nsd.constituent-vnfd'
- const found = utils.resolvePath(modelMetaByPropertyNameMap, getPathForType(typeOrPath));
+ const found = _get(modelMetaByPropertyNameMap, getPathForType(typeOrPath));
if (found) {
- const uiState = _.cloneDeep(found[':meta']);
+ const uiState = _cloneDeep(found[':meta']);
uiState.properties = uiState.properties.filter(filterProperties);
return uiState;
}
},
getModelFieldNamesForType(typeOrPath) {
// resolve paths like 'nsd' or 'vnfd.vdu' or 'nsd.constituent-vnfd'
- const found = utils.resolvePath(modelMetaByPropertyNameMap, getPathForType(typeOrPath));
+ const found = _get(modelMetaByPropertyNameMap, getPathForType(typeOrPath));
if (found) {
let result = [];
found[':meta'].properties.map((p) => {
// if(false) {
- if(p.type == 'choice') {
+ if (p.type == 'choice') {
result.push(p.name)
- return p.properties.map(function(q){
+ return p.properties.map(function (q) {
result.push(q.properties[0].name);
})
- } else {
+ } else {
return result.push(p.name);
}
})
return result;
}
console.warn('no model uiState found for type', typeOrPath);
+ },
+ /**
+ * For a list with a single valued key that is of type string, generate a unique name
+ * for a new entry to be added to the indicated list. This name will use the provided
+ * prefix (or the list's name) followed by a number. The number will be based on the
+ * current length of the array but will insure there is no collision with an existing
+ * name.
+ *
+ * @param {Array} list the list model data
+ * @param {prooerty} property the schema definition of the list
+ * @param [{any} prefix] the perferred prefix for the name. If not provide property.name
+ * will be used.
+ * @returns {string}
+ */
+ generateItemUniqueName(list, property, prefix) {
+ if (property.type !== 'list' ||
+ property.key.length !== 1 ||
+ property.properties.find(prop => prop.name === property.key[0])['data-type'] !== 'string') {
+ // only support list with a single key of type string
+ return null;
+ }
+ if (!prefix) {
+ prefix = property.name;
+ }
+ let key = property.key[0];
+ let suffix = list ? list.length + 1 : 1
+ let keyValue = prefix + '-' + suffix;
+
+ function makeUniqueName() {
+ if (list) {
+ for (let i = 0; i < list.length; i = ++i) {
+ if (list[i][key] === keyValue) {
+ keyValue = keyValue + '-' + (i + 1);
+ makeUniqueName(); // not worried about recursing too deep (chances ??)
+ break;
+ }
+ }
+ }
+ }
+ makeUniqueName();
+ return keyValue;
}
-}
+
+}
\ No newline at end of file
* This class provides utility methods for interrogating an instance of model uiState object.
*/
-'use strict';
-
-import _ from 'lodash'
+import _includes from 'lodash/includes'
+import _isArray from 'lodash/isArray'
import guid from './../guid'
import changeCase from 'change-case'
import InstanceCounter from './../InstanceCounter'
isBoolean(property = {}) {
return (typeof(property['data-type']) == 'string') && (property['data-type'].toLowerCase() == 'boolean')
},
+ isLeafEmpty(property = {}) {
+ return (typeof(property['data-type']) == 'string') && (property['data-type'].toLowerCase() == 'empty')
+ },
isLeaf(property = {}) {
return /leaf|choice/.test(property.type);
},
return !/^(leaf|leaf_list)$/.test(property.type);
},
isSimpleList(property = {}) {
- return _.includes(DescriptorModelFields.simpleList, property.name);
+ return _includes(DescriptorModelFields.simpleList, property.name);
},
isPrimativeDataType(property = {}) {
const Property = this;
}
return /uuid/.test(property['data-type']);
},
- createModelInstance(property) {
+ /**
+ * Create a new instance of the indicated property and, if relevent, use the given
+ * unique name for the instance's key (see generateItemUniqueName)
+ *
+ * @param {Object} typeOrPath - property definition
+ * @param {any} uniqueName
+ * @returns
+ */
+ createModelInstance(property, uniqueName) {
const Property = this;
const defaultValue = Property.defaultValue.bind(this);
- function createModel(uiState, parentMeta) {
+ function createModel(uiState, parentMeta, uniqueName) {
const model = {};
if (Property.isLeaf(uiState)) {
if (uiState.name === 'name') {
- return changeCase.param(parentMeta.name) + '-' + InstanceCounter.count(parentMeta[':qualified-type']);
+ return uniqueName || (changeCase.param(parentMeta.name) + '-' + InstanceCounter.count(parentMeta[':qualified-type']));
}
- if (_.isArray(parentMeta.key) && _.includes(parentMeta.key, uiState.name)) {
+ if (_isArray(parentMeta.key) && _includes(parentMeta.key, uiState.name)) {
if (/uuid/.test(uiState['data-type'])) {
return guid();
}
if (uiState['data-type'] === 'string') {
- const prefix = uiState.name.replace('id', '');
- return (prefix ? changeCase.param(prefix) + '-' : '') + guid(5);
+ // if there is only one key property and we were given a
+ // unique name (probably because creating a list entry
+ // property) then use the unique name otherwise make one up.
+ if (parentMeta.key.length > 1 || !uniqueName) {
+ const prefix = uiState.name.replace('id', '');
+ uniqueName = (prefix ? changeCase.param(prefix) + '-' : '') + guid(5);
+ }
+ return uniqueName;
}
if (/int/.test(uiState['data-type'])) {
return InstanceCounter.count(uiState[':qualified-type']);
return [];
} else {
uiState.properties.forEach(p => {
- model[p.name] = createModel(p, uiState);
+ model[p.name] = createModel(p, uiState, uniqueName);
});
}
return model;
if (/list/.test(property.type)) {
property.type = 'container';
}
- const modelInstance = createModel(property, property);
+ const modelInstance = createModel(property, property, uniqueName);
modelInstance.uiState = {type: property.name};
const modelFragment = DescriptorTemplateFactory.createModelForType(property[':qualified-type'] || property.name) || {};
Object.assign(modelInstance, modelFragment);
-
/*
*
* Copyright 2016 RIFT.IO Inc
* Created by onvelocity on 10/20/15.
*/
-import _ from 'lodash'
-import utils from './../utils'
-import DescriptorModelFields from './DescriptorModelFields'
import DescriptorModelMetaFactory from './DescriptorModelMetaFactory'
-let nsdFields = null;
-let vldFields = null;
-let vnfdFields = null;
-let cvnfdFields = null;
-
-
-
-
-/**
- * Serialize DescriptorModel JSON into CONFD JSON. Also, cleans up the data as needed.
- *
- * @type {{serialize: (function(*=)), ':clean': (function(*=)), nsd: {serialize: (function(*=))}, vld: {serialize: (function(*=))}, vnfd-connection-point-ref: {serialize: (function(*=))}, constituent-vnfd: {serialize: (function(*=))}, vnfd: {serialize: (function(*=))}, vdu: {serialize: (function(*=))}}}
- */
const DescriptorModelSerializer = {
+ /**
+ * Create a json object that can be sent to the backend. I.e. CONFD JSON compliant to the schema definition.
+ *
+ * @param {any} model - the data blob from the editor. This is not modified.
+ * @returns cleansed data model
+ */
serialize(model) {
- const type = model.uiState && model.uiState.type;
- const serializer = this[type];
- if (serializer) {
- model = serializer.serialize(model);
- this[':clean'](model);
- return model;
- }
- return false;
- },
- ':clean'(model) {
- // remove uiState from all elements accept nsd and vnfd
- // remove empty / blank value fields
- function clean(m) {
- Object.keys(m).forEach(k => {
- const isEmptyObject = typeof m[k] === 'object' && _.isEmpty(m[k]);
- if (typeof m[k] === 'undefined' || isEmptyObject || m[k] === '') {
- delete m[k];
- }
- const isMetaAllowed = /^nsd|vnfd$/.test(m.uiState && m.uiState.type);
- if (k === 'uiState') {
- if (isMetaAllowed) {
- // remove any transient ui state properties
- const uiState = _.pick(m.uiState, DescriptorModelFields.meta);
- if (!_.isEmpty(uiState)) {
- // uiState field must be a string
- m['meta'] = JSON.stringify(uiState);
- }
- }
- delete m[k];
- }
- if (typeof m[k] === 'object') {
- clean(m[k]);
- }
- });
- }
- clean(model);
- return model;
- },
- nsd: {
- serialize(nsdModel) {
- if(!nsdFields) nsdFields = DescriptorModelMetaFactory.getModelFieldNamesForType('nsd').concat('uiState');
- const confd = _.pick(nsdModel, nsdFields);
-
- // vnfd is defined in the ETSI etsi_gs reference manual but RIFT does not use it
- delete confd.vnfd;
-
- // map the vnfd instances into the CONFD constituent-vnfd ref instances
- confd['constituent-vnfd'] = confd['constituent-vnfd'].map((d, index) => {
-
- const constituentVNFD = {
- 'member-vnf-index': d['member-vnf-index'],
- 'vnfd-id-ref': d['vnfd-id-ref']
- };
-
- if (d['vnf-configuration']) {
- const vnfConfig = _.cloneDeep(d['vnf-configuration']);
- const configType = vnfConfig['config-type'] || 'none';
- // make sure we send the correct values based on config type
- if (configType === 'none') {
- constituentVNFD['vnf-configuration'] = {'config-type': 'none'};
- const configPriority = utils.resolvePath(vnfConfig, 'input-params.config-priority');
- const configPriorityValue = _.isNumber(configPriority) ? configPriority : d.uiState['member-vnf-index'];
- utils.assignPathValue(constituentVNFD['vnf-configuration'], 'input-params.config-priority', configPriorityValue);
- } else {
- // remove any unused configuration options
- ['netconf', 'rest', 'script', 'juju'].forEach(type => {
- if (configType !== type) {
- delete vnfConfig[type];
- }
- });
- constituentVNFD['vnf-configuration'] = vnfConfig;
- }
- }
-
- if (d.hasOwnProperty('start-by-default')) {
- constituentVNFD['start-by-default'] = d['start-by-default'];
- }
-
- return constituentVNFD;
-
- });
- for (var key in confd) {
- checkForChoiceAndRemove(key, confd, nsdModel);
- }
- // serialize the VLD instances
- confd.vld = confd.vld.map(d => {
- return DescriptorModelSerializer.serialize(d);
- });
-
- return cleanEmptyTopKeys(confd);
-
- }
- },
- vld: {
- serialize(vldModel) {
- if(!vldFields) vldFields = DescriptorModelMetaFactory.getModelFieldNamesForType('nsd.vld');
- const confd = _.pick(vldModel, vldFields);
- const property = 'vnfd-connection-point-ref';
-
- // TODO: There is a bug in RIFT-REST that is not accepting empty
- // strings for string properties.
- // once that is fixed, remove this piece of code.
- // fix-start
- for (var key in confd) {
- if (confd.hasOwnProperty(key) && confd[key] === '') {
- delete confd[key];
- } else {
- //removes choice properties from top level object and copies immediate children onto it.
- checkForChoiceAndRemove(key, confd, vldModel);
- }
- }
-
-
- const deepProperty = 'provider-network';
- for (var key in confd[deepProperty]) {
- if (confd[deepProperty].hasOwnProperty(key) && confd[deepProperty][key] === '') {
- delete confd[deepProperty][key];
- }
- }
- // fix-end
- confd[property] = confd[property].map(d => DescriptorModelSerializer[property].serialize(d));
- return cleanEmptyTopKeys(confd);
- }
- },
- 'vnfd-connection-point-ref': {
- serialize(ref) {
- return _.pick(ref, ['member-vnf-index-ref', 'vnfd-id-ref', 'vnfd-connection-point-ref']);
- }
- },
- 'internal-connection-point': {
- serialize(ref) {
- return _.pick(ref, ['id-ref']);
- }
- },
- 'constituent-vnfd': {
- serialize(cvnfdModel) {
- if(!cvnfdFields) cvnfdFields = DescriptorModelMetaFactory.getModelFieldNamesForType('nsd.constituent-vnfd');
- return _.pick(cvnfdModel, cvnfdFields);
- }
- },
- vnfd: {
- serialize(vnfdModel) {
- if(!vnfdFields) vnfdFields = DescriptorModelMetaFactory.getModelFieldNamesForType('vnfd').concat('uiState');
- const confd = _.pick(vnfdModel, vnfdFields);
- confd.vdu = confd.vdu.map(d => DescriptorModelSerializer.serialize(d));
- return cleanEmptyTopKeys(confd);
- }
- },
- vdu: {
- serialize(vduModel) {
- const copy = _.cloneDeep(vduModel);
- for (let k in copy) {
- checkForChoiceAndRemove(k, copy, vduModel)
- }
- const confd = _.omit(copy, ['uiState']);
- return cleanEmptyTopKeys(confd);
- }
+ if (!model.uiState) {
+ console.error('model uiState null', model);
+ return {};
+ }
+ const path = model.uiState['qualified-type'] || model.uiState['type'];
+ const metaModel = DescriptorModelMetaFactory.getModelMetaForType(path);
+ const data = {};
+ const name = model.uiState['type'];
+ data[name] = model; // lets get the meta hierachy from the top
+ const result = metaModel.serialize(data);
+ console.debug(result);
+ return result;
}
-};
-
-
-function checkForChoiceAndRemove(k, confd, model) {
- let state = model.uiState;
- if (state.choice) {
- let choice = state.choice[k]
- if(choice) {
- if (choice.constructor.name == "Array") {
- for(let i = 0; i < choice.length; i++) {
- for (let key in confd[k][i]) {
- if(choice[i] && (choice[i].selected.indexOf(key) > -1)) {
- confd[k][i][key] = confd[k][i][key]
- }
- confd[key];
- };
- }
- } else {
- for (let key in confd[k]) {
- if(choice && (choice.selected.indexOf(key) > -1)) {
- confd[key] = confd[k][key]
- }
- };
- delete confd[k];
- }
-
- }
- }
- return confd;
-}
-
-function cleanEmptyTopKeys(m){
- Object.keys(m).forEach(k => {
- const isEmptyObject = typeof m[k] === 'object' && _.isEmpty(m[k]);
- if (typeof m[k] === 'undefined' || isEmptyObject || m[k] === '') {
- delete m[k];
- }
- });
- return m;
}
-
-export default DescriptorModelSerializer;
+export default DescriptorModelSerializer;
\ No newline at end of file
'use strict';
-import _ from 'lodash'
+import _isFunction from 'lodash/isFunction'
+import _isArray from 'lodash/isArray'
+import _isObject from 'lodash/isObject'
+import _cloneDeep from 'lodash/cloneDeep'
import DescriptorTemplates from './DescriptorTemplates'
import DescriptorModelMetaFactory from './DescriptorModelMetaFactory'
function init(m) {
keys(m).map(key => {
const value = m[key];
- if (_.isFunction(value)) {
+ if (_isFunction(value)) {
m[key] = value(m, key, model);
}
- if (_.isArray(value)) {
+ if (_isArray(value)) {
value.forEach(v => init(v));
}
- if (_.isObject(value)) {
+ if (_isObject(value)) {
init(value);
}
});
createModelForType(type) {
const template = DescriptorTemplates[type];
if (template) {
- const model = _.cloneDeep(template);
+ const model = _cloneDeep(template);
return resolveInitHandlers(model);
}
}
'use strict';
-import guid from './../guid'
-import InstanceCounter from './../InstanceCounter'
-
+//
+// note: values can be expressions. After the object is created the funtion will be
+// invoked. if you use the arrow function syntax the this pointer will reference
+// the created object.
+//
export default {
'vnfd': {
- 'id': '5b9af24e-2c8f-4792-9d6e-ff9eabb97f15',
- 'name': 'vnfd-1',
- 'short-name': 'vnfd-1',
'description': 'A simple VNF descriptor w/ one VDU',
'version': '1.0',
'connection-point': [
{
- 'name': 'cp1',
+ 'name': 'connection-point-1',
'type': 'VPORT'
}
],
'vdu': [
{
- 'id': 'abd6831e-f811-4580-9aad-1de9c6424180',
+ 'uiState': {
+ 'type': 'vdu'
+ },
+ 'id': 'vdu-1',
'name': 'vdu-1',
'vm-flavor': {
'vcpu-count': 4,
'external-interface': [
{
'name': 'eth0',
- 'vnfd-connection-point-ref': 'cp1',
+ 'vnfd-connection-point-ref': 'connection-point-1',
'virtual-interface': {
'type': 'VIRTIO'
}
]
},
'vnfd.internal-vld': {
- 'id': () => guid(),
- 'name': () => 'vld-' + InstanceCounter.count('new.vnfd.internal-vld'),
'description': 'Virtual link for internal fabric',
'type': 'ELAN'
}
'use strict';
-import _ from 'lodash'
import Classifier from './Classifier'
import DescriptorModel from '../DescriptorModel'
import RecordServicePath from './RecordServicePath'
}
createRsp() {
- const model = DescriptorModelMetaFactory.createModelInstanceForType('nsd.vnffgd.rsp');
+ const property = DescriptorModelMetaFactory.getModelMetaForType('nsd.vnffgd.rsp');
+ const uniqueName = DescriptorModelMetaFactory.generateItemUniqueName(this.rsp, property);
+ const model = DescriptorModelMetaFactory.createModelInstanceForType('nsd.vnffgd.rsp', uniqueName);
return this.rsp = new RecordServicePath(model, this);
}
'use strict';
-import _ from 'lodash'
import ColorGroups from '../../ColorGroups'
import DescriptorModel from '../DescriptorModel'
import ForwardingGraph from './ForwardingGraph'
}
createVld() {
- const model = DescriptorModelMetaFactory.createModelInstanceForType('nsd.vld');
+ const property = DescriptorModelMetaFactory.getModelMetaForType('nsd.vld');
+ const uniqueName = DescriptorModelMetaFactory.generateItemUniqueName(this.vld, property);
+ const model = DescriptorModelMetaFactory.createModelInstanceForType('nsd.vld', uniqueName);
return this.vld = DescriptorModelFactory.newVirtualLink(model, this);
}
}
set vnffgd(obj) {
- const onAddForwardingGraph = (fg) => {
- const index = this.vnffgd.map(suffixAsInteger('short-name')).reduce(toBiggestValue, this.vnffgd.length);
- fg.model['short-name'] = 'FG-' + index;
- };
- this.updateModelList('vnffgd', obj, ForwardingGraph, onAddForwardingGraph);
+ this.updateModelList('vnffgd', obj, ForwardingGraph);
}
createVnffgd(model) {
- model = model || DescriptorModelMetaFactory.createModelInstanceForType('nsd.vnffgd');
+ const property = DescriptorModelMetaFactory.getModelMetaForType('nsd.vnffgd');
+ const uniqueName = DescriptorModelMetaFactory.generateItemUniqueName(this.vnffgd, property, 'fg');
+ model = model || DescriptorModelMetaFactory.createModelInstanceForType('nsd.vnffgd', uniqueName);
return this.vnffgd = DescriptorModelFactory.newForwardingGraph(model, this);
}
'use strict';
-import _ from 'lodash'
import DescriptorModel from '../DescriptorModel'
import RspConnectionPointRef from './RspConnectionPointRef'
import DescriptorModelFactory from '../DescriptorModelFactory'
}
createVdu() {
- const model = DescriptorModelMetaFactory.createModelInstanceForType('vnfd.vdu');
+ const property = DescriptorModelMetaFactory.getModelMetaForType('vnfd.vdu');
+ const uniqueName = DescriptorModelMetaFactory.generateItemUniqueName(this.vdu, property);
+ const model = DescriptorModelMetaFactory.createModelInstanceForType('vnfd.vdu', uniqueName);
return this.vdu = DescriptorModelFactory.newVirtualDeploymentUnit(model, this);
}
}
createVld() {
- const model = DescriptorModelMetaFactory.createModelInstanceForType('vnfd.internal-vld');
+ const property = DescriptorModelMetaFactory.getModelMetaForType('vnfd.internal-vld');
+ const uniqueName = DescriptorModelMetaFactory.generateItemUniqueName(this['internal-vld'], property);
+ const model = DescriptorModelMetaFactory.createModelInstanceForType('vnfd.internal-vld', uniqueName);
return this.vld = DescriptorModelFactory.newInternalVirtualLink(model, this);
}
'use strict';
-import changeCase from 'change-case';
+import _cloneDeep from 'lodash/cloneDeep';
+import _isArray from 'lodash/isArray';
+import _map from 'lodash/map';
+import _flatten from 'lodash/flatten';
+import _find from 'lodash/find';
+import _toNumber from 'lodash/toNumber';
+import _isNumber from 'lodash/isNumber';
+import _clone from 'lodash/clone';
export default {
addAuthorizationStub(xhr) {
},
getResults (topLevelObject, pathArray) {
- let objectCopy = _.cloneDeep(topLevelObject);
+ let objectCopy = _cloneDeep(topLevelObject);
let i = pathArray.length;
let results = [];
while(pathArray[pathArray.length - i]) {
- if (_.isArray(objectCopy[pathArray[pathArray.length - i]])) {
+ if (_isArray(objectCopy[pathArray[pathArray.length - i]])) {
if (i == 2) {
- results = _.map(objectCopy[pathArray[pathArray.length - i]], pathArray[pathArray.length - 1]);
+ results = _map(objectCopy[pathArray[pathArray.length - i]], pathArray[pathArray.length - 1]);
} else {
objectCopy = objectCopy[pathArray[pathArray.length - i]];
}
- } else if (_.isArray(objectCopy)) {
+ } else if (_isArray(objectCopy)) {
objectCopy.map((object) => {
- if (_.isArray(object[pathArray[pathArray.length - i]])) {
+ if (_isArray(object[pathArray[pathArray.length - i]])) {
if (i == 2) {
- results = results.concat(_.map(object[pathArray[pathArray.length - i]], pathArray[pathArray.length - 1]));
+ results = results.concat(_map(object[pathArray[pathArray.length - i]], pathArray[pathArray.length - 1]));
}
}
})
getAbsoluteResults (topLevelObject, pathArray) {
let i = pathArray.length;
- let objectCopy = _.cloneDeep(topLevelObject);
+ let objectCopy = _cloneDeep(topLevelObject);
let results = [];
let fragment = pathArray[pathArray.length - i]
while (fragment) {
if (i == 1) {
// last fragment
- if (_.isArray(objectCopy)) {
+ if (_isArray(objectCopy)) {
// results will be obtained from a map
- results = _.map(objectCopy, fragment);
+ results = _map(objectCopy, fragment);
} else {
// object
if (fragment.match(/\[.*\]/g)) {
}
}
} else {
- if (_.isArray(objectCopy)) {
+ if (_isArray(objectCopy)) {
// is array
- objectCopy = _.map(objectCopy, fragment);
+ objectCopy = _map(objectCopy, fragment);
// If any of the deeper object is an array, flatten the entire list.
// This would usually be a bad leafref going out of its scope.
// Log it too
for (let i = 0; i < objectCopy.length; i++) {
- if (_.isArray(objectCopy[i])) {
- objectCopy = _.flatten(objectCopy);
+ if (_isArray(objectCopy[i])) {
+ objectCopy = _flatten(objectCopy);
console.log('This might be a bad leafref. Verify with backend team.')
break;
}
let key = fragment.split('[')[0];
let searchObject = {};
searchObject[predicateKey] = predicateValue;
- let found = _.find(objectCopy[key], searchObject);
+ let found = _find(objectCopy[key], searchObject);
if (found) {
objectCopy = found;
} else {
predicateValue != NaN &&
predicateValue != Infinity &&
predicateValue != -Infinity) {
- let numericalPredicateValue = _.toNumber(predicateValue);
- if (_.isNumber(numericalPredicateValue)) {
+ let numericalPredicateValue = _toNumber(predicateValue);
+ if (_isNumber(numericalPredicateValue)) {
searchObject[predicateKey] = numericalPredicateValue;
- found = _.find(objectCopy[key], searchObject);
+ found = _find(objectCopy[key], searchObject);
}
}
if (found) {
break;
}
objectCopy = objectCopy[fragment];
+ if (!objectCopy) {
+ // contains no value
+ break;
+ }
}
}
}
},
resolveLeafRefPath (catalogs, leafRefPath, fieldKey, path, container) {
- let pathCopy = _.clone(path);
+ let pathCopy = _clone(path);
// Strip any prefixes
let leafRefPathCopy = leafRefPath.replace(/[\w\d]*:/g, '');
// Strip any spaces
if (fieldKeyArray.length == 1) {
for (let key in catalogs) {
for (let subKey in catalogs[key]) {
- let found = _.find(catalogs[key][subKey], {id: fieldKeyArray[0]});
+ let found = _find(catalogs[key][subKey], {id: fieldKeyArray[0]});
if (found) {
results = this.getAbsoluteResults(found, pathArray.splice(-i, i));
return results;
} else if (fieldKeyArray.length == 2) {
for (let key in catalogs) {
for (let subKey in catalogs[key]) {
- let found = _.find(catalogs[key][subKey], {id: fieldKeyArray[0]});
+ console.log(key, subKey);
+ var found = _find(catalogs[key][subKey], {id: fieldKeyArray[0]});
+ if (found) {
+ for (let foundKey in found) {
+ if (_isArray(found[foundKey])) {
+ let topLevel = _find(found[foundKey], {id: fieldKeyArray[1]});
+ if (topLevel) {
+ results = this.getAbsoluteResults(topLevel, pathArray.splice(-i, i));
+ return results;
+ }
+ } else {
+ if (foundKey == fieldKeyArray[1]) {
+ results = this.getAbsoluteResults(found[foundKey], pathArray.splice(-i, i));
+ return results;
+ }
+ }
+ }
+ }
+ }
+ }
+ } else if (fieldKeyArray.length == 3) {
+ for (let key in catalogs) {
+ for (let subKey in catalogs[key]) {
+ let found = _find(catalogs[key][subKey], {id: fieldKeyArray[0]});
if (found) {
for (let foundKey in found) {
- let topLevel = _.find(found[foundKey], {id: fieldKeyArray[1]});
- if (topLevel) {
- results = this.getAbsoluteResults(topLevel, pathArray.splice(-i, i));
- return results;
+ if (_isArray(found[foundKey])) {
+ let topLevel = _find(found[foundKey], {id: fieldKeyArray[1]});
+ if (topLevel) {
+ results = this.getAbsoluteResults(topLevel, pathArray.splice(-i, i));
+ return results;
+ }
+ } else {
+ if (foundKey == fieldKeyArray[1]) {
+ results = this.getAbsoluteResults(found[foundKey], pathArray.splice(-i, i));
+ return results;
+ }
}
}
}
*/
'use strict';
-import _ from 'lodash'
import $ from 'jquery'
import alt from '../alt'
import utils from '../libraries/utils'
*/
'use strict';
-import $ from 'jquery'
import alt from '../alt'
-import utils from '../libraries/utils'
+import catalogUtils from '../libraries/utils'
import CatalogPackageManagerActions from '../actions/CatalogPackageManagerActions'
-let Utils = require('utils/utils.js');
-function getApiServerOrigin() {
- return utils.getSearchParams(window.location).upload_server + ':4567';
-}
+import Utils from 'utils/utils.js';
-function ajaxRequest(path, catalogPackage, resolve, reject, method = 'GET', input, urlOverride) {
- let options = {
- url: getApiServerOrigin() + path,
- type: method,
- beforeSend: Utils.addAuthorizationStub,
- dataType: 'json',
- success: function(data) {
- if (typeof data == 'string') {
- data = JSON.parse(data);
- }
- resolve({
- data: data,
- state: catalogPackage
- });
- },
- error: function(error) {
- if (typeof error == 'string') {
- error = JSON.parse(error);
- }
- reject({
- data: error,
- state: catalogPackage
- });
- }
+const getAuthorization = () => 'Basic ' + window.sessionStorage.getItem("auth");
+
+const getStateApiPath = (operation, id) =>
+ catalogUtils.getSearchParams(window.location).upload_server + ':4567/api/' + operation + '/' + id + '/state';
+
+const getComposerApiPath = (api) =>
+ window.location.origin + '/composer/api/' + api + '?api_server=' + catalogUtils.getSearchParams(window.location).api_server;
+
+const SUCCESS = {
+ pending: false,
+ success: true,
+ error: false,
+ message: "Completely successfully"
};
- if(input) {
- options.data = input;
+const FAILED = {
+ pending: false,
+ success: false,
+ error: true,
+ message: "Failed"
+ };
+const PENDING = {
+ pending: true,
+ success: false,
+ error: false,
+ message: "In progress"
+ };
+
+function ajaxFetch(url, operation, resolve, reject, method = 'GET', input, urlOverride) {
+ let credentials = 'same-origin';
+ let body = input ? JSON.stringify(input) : null;
+ let headers = new Headers();
+ headers.append('Authorization', getAuthorization());
+ headers.append('Accept', 'application/json');
+ if (input) {
+ headers.append('Content-Type', 'application/json');
}
- if (urlOverride) {
- options.url = window.location.origin + path;
+
+ fetch(url, {method, credentials, headers, body})
+ .then(checkStatusGetJson)
+ .then(handleSuccess)
+ .catch(handleError);
+
+ function checkStatusGetJson(response) {
+ if (response.status >= 200 && response.status < 300) {
+ return response.json();
+ } else {
+ var error = new Error(response.statusText)
+ error.status = response.status;
+ error.statusText = response.statusText;
+ throw error
+ }
}
- $.ajax(options).fail(function(xhr){
- //Authentication and the handling of fail states should be wrapped up into a connection class.
- Utils.checkAuthentication(xhr.status);
- });
-}
+ function handleSuccess (data) {
+ if (typeof data == 'string') {
+ data = JSON.parse(data);
+ }
+ resolve({
+ state: operation,
+ data,
+ operation
+ });
+ }
+ function handleError (data) {
+ if (typeof data == 'string') {
+ data = JSON.parse(data);
+ }
+ reject({
+ state: operation,
+ data,
+ operation
+ });
+ }
+}
const CatalogPackageManagerSource = {
- requestCatalogPackageDownload: function () {
+ requestCatalogPackageDownload: function () {
return {
remote: function (state, download, format, grammar, schema) {
return new Promise((resolve, reject) => {
- // the server does not add a status in the payload
- // so we add one so that the success handler will
- // be able to follow the flow of this download
- const setStatusBeforeResolve = (response = {}) => {
+ // we need an initial status for UI (server does not send)
+ const setStatusBeforeResolve = (response) => {
response.data.status = 'download-requested';
resolve(response);
};
- // RIFT-13485 requires to send type (nsd/vnfd) as a path element.
- // Backend no longer supports mixed multi-package download.
- // Probably does not even support multi-package download of same type.
- // Hence, pick the type from the first element.
const data = {
"package-type": download['catalogItems'][0]['uiState']['type'].toUpperCase(),
"package-id": download.ids,
"export-grammar": grammar && grammar.toUpperCase() || 'OSM',
"export-schema": schema && schema.toUpperCase() || "RIFT"
}
- const path = "/composer/api/package-export?api_server=" + utils.getSearchParams(window.location).api_server;
- ajaxRequest(path, download, setStatusBeforeResolve, reject, 'POST', data, true);
+ const path = getComposerApiPath('package-export');
+ ajaxFetch(path, download, setStatusBeforeResolve, reject, 'POST', data, true);
})
- //.then(function(data) {
- // let filename = data.data.output.filename;
- // window.open(getApiServerOrigin() + "/api/export/" + filename, "_blank")
- //});
},
success: CatalogPackageManagerActions.downloadCatalogPackageStatusUpdated,
error: CatalogPackageManagerActions.downloadCatalogPackageError
remote: function(state, download) {
const transactionId = download.transactionId;
return new Promise(function(resolve, reject) {
- const path = '/api/export/' + transactionId + '/state';
- ajaxRequest(path, download, resolve, reject);
+ const path = getStateApiPath('export', transactionId);
+ ajaxFetch(path, download, resolve, reject);
});
},
success: CatalogPackageManagerActions.downloadCatalogPackageStatusUpdated,
}
},
+ requestCatalogPackageCopy: function () {
+ return {
+ remote: function (state, operationInfo) {
+ return new Promise((resolve, reject) => {
+ // we need an initial status for UI (server does not send)
+ const successHandler = (response) => {
+ const status = response.data.output.status;
+ const state = status === "COMPLETED" ? SUCCESS : status === "FAILED" ? FAILED : PENDING;
+ state.progress = 25; // put something
+ let operation = Object.assign({}, operationInfo, state);
+ operation.transactionId = response.data.output['transaction-id'];
+ resolve(operation);
+ }
+ const failHandler = (response) => {
+ let operation = Object.assign({}, this, FAILED);
+ reject(operation);
+ };
+ const data = {
+ "package-type": operationInfo.args.packageType,
+ "package-id": operationInfo.args.id,
+ "package-name": operationInfo.args.name
+ }
+ const path = getComposerApiPath('package-copy');
+ ajaxFetch(path, operationInfo, successHandler, failHandler, 'POST', data, true);
+ })
+ },
+ success: CatalogPackageManagerActions.updateStatus,
+ error: CatalogPackageManagerActions.updateStatus
+ };
+ },
+
+ requestCatalogPackageCopyStatus: function() {
+ return {
+ remote: function(state, operation) {
+ return new Promise(function(resolve, reject) {
+ const successHandler = (response) => {
+ const status = response.data.status;
+ const state = status === "COMPLETED" ? SUCCESS : status === "FAILED" ? FAILED : PENDING;
+ state.progress = state.pending ? operation.progress + ((100 - operation.progress) / 2) : 100;
+ let newOp = Object.assign({}, operation, state);
+ resolve(newOp);
+ };
+ const failHandler = (response) => {
+ reject(Object.assign({}, operation, FAILED));
+ };
+ const path = getComposerApiPath('package-manager/jobs/' + operation.transactionId);
+ ajaxFetch(path, operation, successHandler, failHandler);
+ });
+ },
+ success: CatalogPackageManagerActions.updateStatus,
+ error: CatalogPackageManagerActions.updateStatus
+ }
+ },
+
requestCatalogPackageUploadStatus: function () {
return {
remote: function (state, upload) {
const transactionId = upload.transactionId;
return new Promise(function (resolve, reject) {
const action = upload.riftAction === 'onboard' ? 'upload' : 'update';
- const path = '/api/' + action + '/' + transactionId + '/state';
- ajaxRequest(path, upload, resolve, reject);
+ const path = getStateApiPath(action, transactionId);
+ ajaxFetch(path, upload, resolve, reject);
});
},
success: CatalogPackageManagerActions.uploadCatalogPackageStatusUpdated,
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
*/
'use strict';
-import _ from 'lodash'
+import _pick from 'lodash/pick'
+import _isEqual from 'lodash/isEqual'
+import _cloneDeep from 'lodash/cloneDeep'
import cc from 'change-case'
import alt from '../alt'
import UID from '../libraries/UniqueId'
import CatalogDataSource from '../sources/CatalogDataSource'
import ComposerAppStore from '../stores/ComposerAppStore'
import SelectionManager from '../libraries/SelectionManager'
-import ExportSelectorDialog from '../components/ExportSelectorDialog'
const defaults = {
catalogs: [],
const areCatalogItemsMetaDataEqual = function (a, b) {
const metaProps = ['id', 'name', 'short-name', 'description', 'vendor', 'version'];
- const aMetaData = _.pick(a, metaProps);
- const bMetaData = _.pick(b, metaProps);
- return _.isEqual(aMetaData, bMetaData);
+ const aMetaData = _pick(a, metaProps);
+ const bMetaData = _pick(b, metaProps);
+ return _isEqual(aMetaData, bMetaData);
};
+function createItem (type) {
+ let newItem = DescriptorModelMetaFactory.createModelInstanceForType(type);
+ if (newItem){
+ newItem.id = guid();
+ UID.assignUniqueId(newItem.uiState);
+ newItem.uiState.isNew = true;
+ newItem.uiState.modified = true;
+ newItem.uiState['instance-ref-count'] = 0;
+ }
+ return newItem;
+}
+
class CatalogDataStore {
constructor() {
}
addNewItemToCatalog(newItem) {
- const id = guid();
const type = newItem.uiState.type;
- newItem.id = id;
- UID.assignUniqueId(newItem.uiState);
this.getCatalogs().filter(d => d.type === type).forEach(catalog => {
catalog.descriptors.push(newItem);
});
// update indexes and integrate new model into catalog
this.updateCatalogIndexes(this.getCatalogs());
- return this.getCatalogItemById(id);
+ return this.getCatalogItemById(newItem.id);
}
updateCatalogIndexes(catalogs) {
vnfd.uiState['instance-ref-count'] = instanceRefCount;
}
// create an instance of this vnfd to carry transient ui state properties
- const instance = _.cloneDeep(vnfd);
+ const instance = _cloneDeep(vnfd);
instance.uiState['member-vnf-index'] = d['member-vnf-index'];
instance['vnf-configuration'] = d['vnf-configuration'];
instance['start-by-default'] = d['start-by-default'];
ComposerAppActions.showError.defer({
errorMessage: 'Cannot edit NSD/VNFD with references to instantiated Network Services'
});
- return _.cloneDeep(d);
+ return _cloneDeep(d);
} else {
item.uiState.modified = modified;
requiresSave = true;
ComposerAppActions.showError.defer({
errorMessage: 'Cannot edit NSD/VNFD with references to instantiated Network Services'
});
- return _.cloneDeep(d);
+ return _cloneDeep(d);
} else {
itemDescriptor.model.uiState.modified = true;
this.addSnapshot(itemDescriptor.model);
}
createCatalogItem(type = 'nsd') {
- const model = DescriptorModelMetaFactory.createModelInstanceForType(type);
- if (model) {
- const newItem = this.addNewItemToCatalog(model);
- newItem.uiState.isNew = true;
- newItem.uiState.modified = true;
- newItem.uiState['instance-ref-count'] = 0;
- // open the new model for editing in the canvas/details panels
- setTimeout(() => {
- this.selectCatalogItem(newItem);
- CatalogItemsActions.editCatalogItem.defer(newItem);
- }, 200);
- }
+ const newItem = createItem(type);
+ this.saveItem(newItem)
}
duplicateSelectedCatalogItem() {
- const item = this.getFirstSelectedCatalogItem();
- if (item) {
- const newItem = _.cloneDeep(item);
- newItem.name = newItem.name + ' Copy';
- const nsd = this.addNewItemToCatalog(newItem);
- this.selectCatalogItem(nsd);
- nsd.uiState.isNew = true;
- nsd.uiState.modified = true;
- nsd.uiState['instance-ref-count'] = 0;
- // note duplicated items get a new id, map the layout position
- // of the old id to the new id in order to preserve the layout
- if (nsd.uiState.containerPositionMap) {
- nsd.uiState.containerPositionMap[nsd.id] = nsd.uiState.containerPositionMap[item.id];
- delete nsd.uiState.containerPositionMap[item.id];
- }
- setTimeout(() => {
- this.selectCatalogItem(nsd);
- CatalogItemsActions.editCatalogItem.defer(nsd);
- }, 200);
+ // make request to backend to duplicate an item
+ const srcItem = this.getFirstSelectedCatalogItem();
+ if (srcItem) {
+ CatalogPackageManagerActions.copyCatalogPackage.defer(srcItem);
}
}
saveCatalogItem() {
const activeItem = ComposerAppStore.getState().item;
if (activeItem) {
- if (activeItem.uiState['instance-ref-count'] > 0) {
+ this.saveItem(activeItem);
+ }
+ }
+
+ saveItem(item) {
+ if (item) {
+ if (item.uiState['instance-ref-count'] > 0) {
console.log('cannot save NSD/VNFD with references to instantiated Network Services');
ComposerAppActions.showError.defer({
errorMessage: 'Cannot save NSD/VNFD with references to instantiated Network Services'
return;
}
const success = () => {
- delete activeItem.uiState.isNew;
- delete activeItem.uiState.modified;
- this.updateCatalogItem(activeItem);
+ delete item.uiState.modified;
+ if (item.uiState.isNew) {
+ this.addNewItemToCatalog(item);
+ delete item.uiState.isNew;
+ } else {
+ this.updateCatalogItem(item);
+ }
// TODO should the save action clear the undo/redo stack back to the beginning?
- this.resetSnapshots(activeItem);
+ this.resetSnapshots(item);
ModalOverlayActions.hideModalOverlay.defer();
- CatalogItemsActions.editCatalogItem.defer(activeItem);
+ CatalogItemsActions.editCatalogItem.defer(item);
};
const failure = () => {
ModalOverlayActions.hideModalOverlay.defer();
- CatalogItemsActions.editCatalogItem.defer(activeItem);
+ CatalogItemsActions.editCatalogItem.defer(item);
};
const exception = () => {
- console.warn('unable to save catalog item', activeItem);
+ console.warn('unable to save catalog item', item);
ModalOverlayActions.hideModalOverlay.defer();
- CatalogItemsActions.editCatalogItem.defer(activeItem);
+ CatalogItemsActions.editCatalogItem.defer(item);
};
ModalOverlayActions.showModalOverlay.defer();
- this.getInstance().saveCatalogItem(activeItem).then(success, failure).catch(exception);
+ this.getInstance().saveCatalogItem(item).then(success, failure).catch(exception);
}
}
exportSelectedCatalogItems(draggedItem) {
- const onSelectFormat = (selectedFormat, event) => {
- this.setState({
- selectedFormat: selectedFormat
- });
- };
-
- const onSelectGrammar = (selectedGrammar, event) => {
- this.setState({
- selectedGrammar: selectedGrammar
- });
- }
-
-
- const onCancel = () => {
- this.resetSelectionState();
- ModalOverlayActions.hideModalOverlay();
- };
-
- const onDownload = (event) => {
+ // collect the selected items and delegate to the catalog package manager action creator
+ const selectedItems = this.getAllSelectedCatalogItems();
+ if (selectedItems.length) {
CatalogPackageManagerActions.downloadCatalogPackage.defer({
selectedItems: selectedItems,
- selectedFormat: this.selectedFormat,
- selectedGrammar: this.selectedGrammar
+ selectedFormat: 'mano',
+ selectedGrammar: 'osm'
});
this.resetSelectionState();
- ModalOverlayActions.hideModalOverlay();
- return;
- }
-
- if (draggedItem) {
- // if item is given make sure it is also selected
- //draggedItem.uiState.selected = true;
- SelectionManager.addSelection(draggedItem);
- this.updateCatalogItem(draggedItem);
- }
- // collect the selected items and delegate to the catalog package manager action creator
- const selectedItems = this.getAllSelectedCatalogItems();
- if (selectedItems.length) {
- CatalogDataStore.chooseExportFormat(onSelectFormat, onSelectGrammar, onDownload, onCancel);
}
}
-
- static chooseExportFormat(onSelectFormat, onSelectGrammar, onDownload, onCancel) {
- ModalOverlayActions.showModalOverlay.defer(
- <ExportSelectorDialog
- onSelectFormat={onSelectFormat}
- onSelectGrammar={onSelectGrammar}
- onCancel={onCancel}
- onDownload={onDownload}
- currentlySelectedFormat='mano'
- currentlySelectedGrammar='osm'
- />
- );
+ saveCatalogItemError(data){
+ let error = JSON.parse(data.error.responseText);
+ const errorMsg = error && error.body && error.body['rpc-reply'] && JSON.stringify(error.body['rpc-reply']['rpc-error'], null, ' ')
+ ComposerAppActions.showError.defer({
+ errorMessage: 'Unable to save the descriptor.\n' + errorMsg
+ });
}
-
}
export default alt.createStore(CatalogDataStore, 'CatalogDataStore');
*/
'use strict';
-import _ from 'lodash'
+import _delay from 'lodash/delay'
+import _pick from 'lodash/pick'
import alt from '../alt'
import guid from '../libraries/guid'
import numeral from 'numeral'
import imgDownload from '../../../node_modules/open-iconic/svg/cloud-download.svg'
import imgOnboard from '../../../node_modules/open-iconic/svg/cloud-upload.svg'
import imgUpdate from '../../../node_modules/open-iconic/svg/data-transfer-upload.svg'
+import imgCopy from '../../../node_modules/open-iconic/svg/layers.svg'
const defaults = {
+ operation: {
+ id: '',
+ name: '',
+ icon: '',
+ transactionId: '',
+ progress: 0,
+ message: 'Requested',
+ args: {},
+ pending: false,
+ success: false,
+ error: false,
+ },
downloadPackage: {
id: '',
name: '',
return utils.getSearchParams(window.location).upload_server + ':4567';
}
-function delayStatusCheck(statusCheckFunction, catalogPackage) {
- if (!catalogPackage.checkStatusTimeoutId) {
+function delayStatusCheck(statusCheckFunction, operation) {
+ if (!operation.checkStatusTimeoutId) {
const delayCallback = function () {
- delete catalogPackage.checkStatusTimeoutId;
- statusCheckFunction(catalogPackage).catch(exception);
+ delete operation.checkStatusTimeoutId;
+ statusCheckFunction(operation).catch(exception);
};
- catalogPackage.checkStatusTimeoutId = _.delay(delayCallback, defaults.checkStatusDelayInSeconds * 1000);
+ operation.checkStatusTimeoutId = _delay(delayCallback, defaults.checkStatusDelayInSeconds * 1000);
}
}
constructor() {
- this.packages = [];
+ this.operations = [];
this.registerAsync(CatalogDataSource);
this.registerAsync(CatalogPackageManagerSource);
- this.bindAction(CatalogPackageManagerActions.REMOVE_CATALOG_PACKAGE, this.removeCatalogPackage);
+ this.bindAction(CatalogPackageManagerActions.REMOVE_CATALOG_OPERATION, this.removeCatalogOperation);
this.bindAction(CatalogPackageManagerActions.DOWNLOAD_CATALOG_PACKAGE, this.downloadCatalogPackage);
this.bindAction(CatalogPackageManagerActions.DOWNLOAD_CATALOG_PACKAGE_STATUS_UPDATED, this.onDownloadCatalogPackageStatusUpdated);
this.bindAction(CatalogPackageManagerActions.DOWNLOAD_CATALOG_PACKAGE_ERROR, this.onDownloadCatalogPackageError);
this.bindAction(CatalogPackageManagerActions.UPLOAD_CATALOG_PACKAGE, this.uploadCatalogPackage);
this.bindAction(CatalogPackageManagerActions.UPLOAD_CATALOG_PACKAGE_STATUS_UPDATED, this.onUploadCatalogPackageStatusUpdated);
this.bindAction(CatalogPackageManagerActions.UPLOAD_CATALOG_PACKAGE_ERROR, this.onUploadCatalogPackageError);
-
+ this.bindAction(CatalogPackageManagerActions.COPY_CATALOG_PACKAGE, this.copyCatalogPackage);
+ this.bindAction(CatalogPackageManagerActions.UPDATE_STATUS, this.updateOperationStatus);
}
- addPackage(catalogPackage) {
- const packages = [catalogPackage].concat(this.packages);
- this.setState({packages: packages});
+ addOperation(operation) {
+ const operations = [operation].concat(this.operations);
+ this.setState({operations});
}
- updatePackage(catalogPackage) {
- const packages = this.packages.map(d => {
- if (d.id === catalogPackage.id) {
- return Object.assign({}, d, catalogPackage);
+ updateOperation(operation) {
+ const operations = this.operations.map(d => {
+ if (d.id === operation.id) {
+ return Object.assign({}, d, operation);
}
return d;
});
- this.setState({packages: packages});
+ this.setState({operations});
}
- removeCatalogPackage(catalogPackage) {
- const packages = this.packages.filter(d => d.id !== catalogPackage.id);
- this.setState({packages: packages});
+ removeCatalogOperation(operation) {
+ const operations = this.operations.filter(d => d.id !== operation.id);
+ this.setState({operations});
+ }
+
+ copyCatalogPackage(sourcePackage) {
+ let operationInfo = Object.assign({}, defaults.operation);
+
+ operationInfo.args.packageType = sourcePackage['uiState']['type'].toUpperCase();
+ operationInfo.args.id = sourcePackage.id;
+ operationInfo.args.name = sourcePackage.name + ' copy';
+
+ operationInfo.id = guid();
+ operationInfo.icon = imgCopy;
+ operationInfo.type = 'copy';
+ operationInfo.name = "Creating " + operationInfo.args.name;
+ operationInfo.message = "Requesting duplication";
+
+ this.addOperation(operationInfo);
+ this.getInstance().requestCatalogPackageCopy(operationInfo, sourcePackage);
+ }
+
+ updateOperationStatus(operation) {
+ console.debug('package manager operation status update', operation);
+ this.updateOperation(operation);
+ if (operation.pending) {
+ delayStatusCheck(this.getInstance().requestCatalogPackageCopyStatus, operation);
+ }
}
uploadCatalogPackage(file) {
file.id = file.id || guid();
- const catalogPackage = _.pick(file, packagePropertyNames);
- catalogPackage.icon = file.riftAction === 'onboard' ? imgOnboard : imgUpdate;
- catalogPackage.type = 'upload';
- this.addPackage(catalogPackage);
+ const operation = _pick(file, packagePropertyNames);
+ operation.icon = file.riftAction === 'onboard' ? imgOnboard : imgUpdate;
+ operation.type = 'upload';
+ this.addOperation(operation);
// note DropZone.js handles the async upload so we don't have to invoke any async action creators
}
onUploadCatalogPackageStatusUpdated(response) {
const upload = updateStatusInfo(response);
- this.updatePackage(upload);
+ this.updateOperation(upload);
console.log('updating package upload')
// if pending with no transaction id - do nothing
// bc DropZone.js will notify upload progress
onUploadCatalogPackageError(response) {
console.warn('onUploadCatalogPackageError', response);
- const catalogPackage = updateStatusInfo(response);
- this.updatePackage(catalogPackage);
+ const operation = updateStatusInfo(response);
+ this.updateOperation(operation);
}
downloadCatalogPackage(data) {
let grammar = data['selectedGrammar'] || 'osm';
let format = "YAML";
if (catalogItems.length) {
- const catalogPackage = Object.assign({}, defaults.downloadPackage, {id: guid()});
- catalogPackage.name = catalogItems[0].name;
- catalogPackage.type = 'download';
+ const operation = Object.assign({}, defaults.downloadPackage, {id: guid()});
+ operation.name = catalogItems[0].name;
+ operation.type = 'download';
if (catalogItems.length > 1) {
- catalogPackage.name += ' (' + catalogItems.length + ' items)';
+ operation.name += ' (' + catalogItems.length + ' items)';
}
- catalogPackage.ids = catalogItems.map(d => d.id).sort().toString();
- catalogPackage.catalogItems = catalogItems;
- this.addPackage(catalogPackage);
- this.getInstance().requestCatalogPackageDownload(catalogPackage, format, grammar, schema).catch(exception);
+ operation.ids = catalogItems.map(d => d.id).sort().toString();
+ operation.catalogItems = catalogItems;
+ this.addOperation(operation);
+ this.getInstance().requestCatalogPackageDownload(operation, format, grammar, schema).catch(exception);
}
}
onDownloadCatalogPackageStatusUpdated(response) {
const download = updateStatusInfo(response);
- this.updatePackage(download);
+ this.updateOperation(download);
if (download.pending) {
delayStatusCheck(this.getInstance().requestCatalogPackageDownloadStatus, download);
}
onDownloadCatalogPackageError(response) {
console.warn('onDownloadCatalogPackageError', response);
- const catalogPackage = updateStatusInfo(response);
- this.updatePackage(catalogPackage);
+ const operation = updateStatusInfo(response);
+ this.updateOperation(operation);
}
}
}
function updateStatusInfo(response) {
- // returns the catalogPackage object with the status fields updated based on the server response
+ // returns the operation object with the status fields updated based on the server response
const statusInfo = {
pending: false,
success: false,
error: false
};
const responseData = (response.data.output) ? response.data.output : response.data;
- const catalogPackage = response.state;
- switch(response.data.status) {
- case 'upload-progress':
- statusInfo.pending = true;
- statusInfo.progress = parseFloat(responseData.progress) || 0;
- statusInfo.message = calculateUploadProgressMessage(catalogPackage.size, responseData.progress, responseData.bytesSent);
- break;
- case 'upload-success':
- statusInfo.pending = true;
- statusInfo.progress = 100;
- statusInfo.message = 'Upload completed.';
- statusInfo.transactionId = responseData['transaction_id'] || responseData['transaction-id'] || catalogPackage.transactionId;
- break;
- case 'upload-error':
- statusInfo.error = true;
- statusInfo.message = responseData.message;
- break;
- case 'download-requested':
- statusInfo.pending = true;
- statusInfo.progress = 25;
- statusInfo.transactionId = responseData['transaction_id'] || responseData['transaction-id'] || catalogPackage.transactionId;
- break;
- case 'pending':
- statusInfo.pending = true;
- statusInfo.progress = 50;
- statusInfo.message = responseData.events[responseData.events.length - 1].text;
- break;
- case 'success':
- statusInfo.success = true;
- statusInfo.progress = 100;
- statusInfo.message = responseData.events[responseData.events.length - 1].text;
- if (catalogPackage.type === 'download') {
- statusInfo.urlValidUntil = moment().add(defaults.downloadUrlTimeToLiveInMinutes, 'minutes').toISOString();
- if (responseData.filename) {
- statusInfo.url = getCatalogPackageManagerServerOrigin() + '/api/export/' + responseData.filename;
- } else {
- statusInfo.url = getCatalogPackageManagerServerOrigin() + '/api/export/' + catalogPackage.transactionId + '.tar.gz';
+ const operation = response.state;
+ if ( typeof response.data.status !== "number" ) {
+ switch(response.data.status) {
+ case 'upload-progress':
+ statusInfo.pending = true;
+ statusInfo.progress = parseFloat(responseData.progress) || 0;
+ statusInfo.message = calculateUploadProgressMessage(operation.size, responseData.progress, responseData.bytesSent);
+ break;
+ case 'upload-success':
+ statusInfo.pending = true;
+ statusInfo.progress = 100;
+ statusInfo.message = 'Upload completed.';
+ statusInfo.transactionId = responseData['transaction_id'] || responseData['transaction-id'] || operation.transactionId;
+ break;
+ case 'upload-error':
+ statusInfo.error = true;
+ statusInfo.message = responseData.message;
+ break;
+ case 'download-requested':
+ statusInfo.pending = true;
+ statusInfo.progress = 25;
+ statusInfo.transactionId = responseData['transaction_id'] || responseData['transaction-id'] || operation.transactionId;
+ break;
+ case 'pending':
+ statusInfo.pending = true;
+ statusInfo.progress = 50;
+ statusInfo.message = responseData.events[responseData.events.length - 1].text;
+ break;
+ case 'success':
+ statusInfo.success = true;
+ statusInfo.progress = 100;
+ statusInfo.message = responseData.events[responseData.events.length - 1].text;
+ if (operation.type === 'download') {
+ statusInfo.urlValidUntil = moment().add(defaults.downloadUrlTimeToLiveInMinutes, 'minutes').toISOString();
+ if (responseData.filename) {
+ statusInfo.url = getCatalogPackageManagerServerOrigin() + '/api/export/' + responseData.filename;
+ } else {
+ statusInfo.url = getCatalogPackageManagerServerOrigin() + '/api/export/' + operation.transactionId + '.tar.gz';
+ }
}
+ break;
+ case 'failure':
+ statusInfo.error = true;
+ statusInfo.message = responseData.errors[0].value;
+ break;
+ default:
+ throw new ReferenceError('a status of "request", "success", "failure", "pending", "upload-completed", "upload-error", "download-requested", "upload-progress", "upload-action" is required');
}
- break;
- case 'failure':
+ } else {
+ // typically get here due to unexpected development errors (backend exceptions, down/up load server access issues)
statusInfo.error = true;
- statusInfo.message = responseData.errors[0].value;
- break;
- default:
- throw new ReferenceError('a status of "request", "success", "failure", "pending", "upload-completed", "upload-error", "download-requested", "upload-progress", "upload-action" is required');
+ statusInfo.message = responseData.statusText || 'Error';
}
- return Object.assign({}, catalogPackage, statusInfo);
+ return Object.assign({}, operation, statusInfo);
}
export default alt.createStore(CatalogPackageManagerStore, 'CatalogPackageManagerStore');
*/
'use strict';
-import _ from 'lodash'
+import _isNumber from 'lodash/isNumber'
+import _cloneDeep from 'lodash/cloneDeep'
+import _isEmpty from 'lodash/isEmpty'
+import _mergeWith from 'lodash/mergeWith'
+import _uniqBy from 'lodash/uniqBy'
+import _isEqual from 'lodash/isEqual'
+import _findIndex from 'lodash/findIndex'
+import _remove from 'lodash/remove'
import d3 from 'd3'
import alt from '../alt'
import UID from '../libraries/UniqueId'
const getDefault = (name, defaultValue) => {
const val = window.localStorage.getItem('defaults-' + name);
if (val) {
- if (_.isNumber(val)) {
+ if (_isNumber(val)) {
if (val < 0) {
return setDefault(name, 0);
}
addFileSuccess: FileManagerActions.addFileSuccess,
deletePackageFile: FileManagerActions.deletePackageFile,
deleteFileSuccess: FileManagerActions.deleteFileSuccess,
+ deleteFileError: FileManagerActions.deleteFileError,
closeFileManagerSockets: FileManagerActions.closeFileManagerSockets,
openFileManagerSockets: FileManagerActions.openFileManagerSockets,
openDownloadMonitoringSocketSuccess: FileManagerActions.openDownloadMonitoringSocketSuccess,
d.cpNumber = ++cpNumber;
containers.filter(d => DescriptorModelFactory.isVnfdConnectionPointRef(d)).filter(ref => ref.key === d.key).forEach(ref => ref.cpNumber = d.cpNumber);
});
- this.setState({containers: containers, item: _.cloneDeep(item)});
+ this.setState({containers: containers, item: _cloneDeep(item)});
}
SelectionManager.refreshOutline();
}
}
SelectionManager.select(item);
this.updateItem(item);
- this.openFileManagerSockets(item)
+ if (item) {
+ this.openFileManagerSockets(item);
+ }
}
catalogItemMetaDataChanged(item) {
this.updateItem(item);
applyDefaultLayout() {
if (this.item && this.item.uiState && this.item.uiState.containerPositionMap) {
- if (!_.isEmpty(this.item.uiState.containerPositionMap)) {
+ if (!_isEmpty(this.item.uiState.containerPositionMap)) {
this.item.uiState.containerPositionMap = {};
CatalogItemsActions.catalogItemMetaDataChanged.defer(this.item);
}
if (isFullScreen()) {
const layout = comp.layout;
- const restoreLayout = _.cloneDeep(layout);
+ const restoreLayout = _cloneDeep(layout);
uiTransientState.restoreLayout = restoreLayout;
layout.left = 0;
layout.right = 0;
if (self.fileMonitoringSocketID) {
let newState = {};
if(data.hasOwnProperty('contents')) {
- filesState = addInputState( _.cloneDeep(this.filesState),data);
- // filesState = _.merge(self.filesState, addInputState({},data));
+ filesState = addInputState( _cloneDeep(this.filesState),data);
let normalizedData = normalizeTree(data);
newState = {
files: {
- data: _.mergeWith(normalizedData.data, self.files.data, function(obj, src) {
- return _.uniqBy(obj? obj.concat(src) : src, 'name');
+ data: _mergeWith(normalizedData.data, self.files.data, function(obj, src) {
+ return _uniqBy(obj? obj.concat(src) : src, 'name');
}),
id: normalizedData.id
},
files: false
}
}
+ if(!_isEqual(newState.files, this.files) || ! _isEqual(newState.fileState, this.fileState)) {
+ this.setState(newState);
+ }
- this.setState(newState);
}
function normalizeTree(data) {
let f = {
updateFileLocationInput = (data) => {
let name = data.name;
let value = data.value;
- var filesState = _.cloneDeep(this.filesState);
+ var filesState = _cloneDeep(this.filesState);
filesState[name] = value;
this.setState({
filesState: filesState
if(!data.refresh) {
let path = data.path;
let fileName = data.fileName;
- let files = _.cloneDeep(this.files);
+ let files = _cloneDeep(this.files);
let loadingIndex = files.data[path].push({
status: 'DOWNLOADING',
name: path + '/' + fileName
openDownloadMonitoringSocketSuccess = (id) => {
let self = this;
let ws = window.multiplexer.channel(id);
- let downloadJobs = _.cloneDeep(self.downloadJobs);
+ let downloadJobs = _cloneDeep(self.downloadJobs);
let newFiles = false;
ws.onmessage = (socket) => {
if (self.files && self.files.length > 0) {
try {
jobs = JSON.parse(socket.data);
} catch(e) {}
- newFiles = _.cloneDeep(self.files);
+ newFiles = _cloneDeep(self.files);
jobs.map(function(j) {
//check if not in completed state
let fullPath = j['package-path'];
let path = fullPath.split('/');
let fileName = path.pop();
path = path.join('/');
- let index = _.findIndex(self.files.data[path], function(o){
+ let index = _findIndex(self.files.data[path], function(o){
return fullPath == o.name
});
if((index > -1) && newFiles.data[path][index]) {
}
deleteFileSuccess = (data) => {
let path = data.path.split('/')
- let files = _.cloneDeep(this.files);
+ let files = _cloneDeep(this.files);
path.pop();
path = path.join('/');
let pathFiles = files.data[path]
- _.remove(pathFiles, function(c) {
+ _remove(pathFiles, function(c) {
return c.name == data.path;
});
files: files
})
}
+ deleteFileError = (error) => {
+ const filepath = error.path;
+ const message = error.data && error.data.output ? ' (' + error.data.output['error-trace'] + ')' : ' (server error)';
+ console.log('Unable to delete', filepath, 'Error:', message);
+ ComposerAppActions.showError.defer({
+ errorMessage: 'Unable to delete ' + filepath + message + '. '
+ });
+ }
+
newPathNameUpdated = (event) => {
const value = event.target.value;
this.setState({
/*global describe, beforeEach, it, expect, xit, xdescribe */
'use strict';
-import _ from 'lodash'
+import _cloneDeep from 'lodash/cloneDeep'
import DescriptorModelSerializer from '../../../src/libraries/model/DescriptorModelSerializer'
import DescriptorModelFactory from '../../../src/libraries/model/DescriptorModelFactory'
import SampleCatalogs from 'json!../../../src/assets/ping-pong-catalog.json'
describe('buildCatalogItemFactory', () => {
let containers;
beforeEach(() => {
- const nsdJson = _.cloneDeep(SampleCatalogs[0].descriptors[0]);
+ const nsdJson = _cloneDeep(SampleCatalogs[0].descriptors[0]);
// the CatalogItemsStore adds the type to the uiState field when the catalog is loaded
nsdJson.uiState = {type: 'nsd'};
// the user will open a catalog item by dbl clicking on it in the ui that is when we
expect(result).toEqual([]);
});
it('parses an NSD object', () => {
- const nsdJson = _.cloneDeep(SampleCatalogs[0].descriptors[0]);
+ const nsdJson = _cloneDeep(SampleCatalogs[0].descriptors[0]);
nsdJson.uiState = {type: 'nsd'};
const factory = DescriptorModelFactory.buildCatalogItemFactory(SampleCatalogs);
const result = [nsdJson].reduce(factory, [])[0];
});
describe('DescriptorModelSerializer', () => {
it('outputs the same JSON that was parsed by the .buildCatalogItemFactory method', () => {
- const inputJSON = _.cloneDeep(TestCatalogs[0].descriptors[0]);
+ const inputJSON = _cloneDeep(TestCatalogs[0].descriptors[0]);
inputJSON.uiState = {type: 'nsd'};
const factory = DescriptorModelFactory.buildCatalogItemFactory(TestCatalogs);
const parsedModel = [inputJSON].reduce(factory, []);
/*global describe, beforeEach, it, expect, xit */
'use strict';
-import _ from 'lodash'
import DescriptorModel from 'libraries/model/DescriptorModel'
class TestDescriptorModel extends DescriptorModel {
var uiPluginCmakeBuild = process.env.ui_plugin_cmake_build || false;
var frameworkPath = uiPluginCmakeBuild?'../../../../skyquake/skyquake-build/framework':'../../framework';
var HtmlWebpackPlugin = require('html-webpack-plugin');
+var CompressionPlugin = require("compression-webpack-plugin");
// Added to overcome node-sass bug https://github.com/iam4x/isomorphic-flux-boilerplate/issues/62
process.env.UV_THREADPOOL_SIZE=64;
-module.exports = {
+var config = {
devtool: 'source-map',
output: {
publicPath: 'assets/',
path: 'public/assets/',
- filename: 'src/main.js'
+ filename: 'bundle.js'
},
debug: false,
'helpers': path.join(process.cwd(), './test/helpers/')
}
},
- plugins: [
- // new webpack.optimize.DedupePlugin(),
- // new webpack.optimize.UglifyJsPlugin(),
- // new webpack.optimize.OccurenceOrderPlugin(),
- // new webpack.optimize.AggressiveMergingPlugin(),
- // new webpack.NoErrorsPlugin(),
- new HtmlWebpackPlugin({
- filename: '../index.html'
- , templateContent: '<div id="app"></div>'
- })
- ],
-
module: {
noParse: [/autoit.js/],
// preLoaders: [
},
{ test: /\.json$/, loader: "json-loader" },
]
- }
+ },
+ plugins: [
+ new HtmlWebpackPlugin({
+ filename: '../index.html',
+ templateContent: '<div id="app"></div>'
+ })
+ ]
};
+
+if (process.argv.indexOf('--optimize-minimize') !== -1) {
+ // we are going to output a gzip file in the production process
+ config.output.filename = "gzip-" + config.output.filename;
+ config.plugins.push(new webpack.DefinePlugin({ // <-- key to reducing React's size
+ 'process.env': {
+ 'NODE_ENV': JSON.stringify('production')
+ }
+ }));
+ config.plugins.push(new CompressionPlugin({
+ asset: "[path]", // overwrite js file with gz file
+ algorithm: "gzip",
+ test: /\.(js)$/
+ }));
+}
+
+module.exports = config;
\ No newline at end of file
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.3.13",
"babel-runtime": "^6.3.19",
+ "compression-webpack-plugin": "^0.3.2",
"cors": "^2.7.1",
"css-loader": "^0.23.1",
"file-loader": "^0.8.5",
npm install
echo 'Fetching third-party node_modules for '$PLUGIN_NAME'...done'
echo 'Packaging '$PLUGIN_NAME' using webpack'
-ui_plugin_cmake_build=true ./node_modules/.bin/webpack --progress --config webpack.production.config.js
+ui_plugin_cmake_build=true ./node_modules/.bin/webpack --optimize-minimize --progress --config webpack.production.config.js
echo 'Packaging '$PLUGIN_NAME' using webpack... done'
echo 'Building plugin '$PLUGIN_NAME'... done'
import React from 'react';
import Button from 'widgets/button/rw.button.js';
-import _ from 'lodash';
+import _cloneDeep from 'lodash/cloneDeep';
import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx';
import Crouton from 'react-crouton';
import TextInput from 'widgets/form_controls/textInput.jsx';
}
}
- let newAccount = _.cloneDeep(removeTrailingWhitespace(Account));
+ let newAccount = _cloneDeep(removeTrailingWhitespace(Account));
delete newAccount.params;
newAccount.nestedParams &&
newAccount.nestedParams['container-name'] &&
/*
* STANDARD_RIFT_IO_COPYRIGHT
*/
-var Webpack = require('webpack');
+var webpack = require('webpack');
var path = require('path');
var nodeModulesPath = path.resolve(__dirname, 'node_modules');
var buildPath = path.resolve(__dirname, 'public', 'build');
var uiPluginCmakeBuild = process.env.ui_plugin_cmake_build || false;
var frameworkPath = uiPluginCmakeBuild?'../../../../skyquake/skyquake-build/framework':'../../framework';
var HtmlWebpackPlugin = require('html-webpack-plugin');
-var CommonsPlugin = new require("webpack/lib/optimize/CommonsChunkPlugin")
+var CompressionPlugin = require("compression-webpack-plugin");
// Added to overcome node-sass bug https://github.com/iam4x/isomorphic-flux-boilerplate/issues/62
process.env.UV_THREADPOOL_SIZE=64;
var config = {
},
plugins: [
new HtmlWebpackPlugin({
- filename: '../index.html'
- , templateContent: '<div id="app"></div>'
- }),
- new Webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js", Infinity)
+ filename: '../index.html',
+ templateContent: '<div id="app"></div>'
+ })
]
};
+
+if (process.argv.indexOf('--optimize-minimize') !== -1) {
+ // we are going to output a gzip file in the production process
+ config.output.filename = "gzip-" + config.output.filename;
+ config.plugins.push(new webpack.DefinePlugin({ // <-- key to reducing React's size
+ 'process.env': {
+ 'NODE_ENV': JSON.stringify('production')
+ }
+ }));
+ config.plugins.push(new CompressionPlugin({
+ asset: "[path]", // overwrite js file with gz file
+ algorithm: "gzip",
+ test: /\.(js)$/
+ }));
+}
module.exports = config;
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.3.13",
"babel-runtime": "^6.3.19",
+ "compression-webpack-plugin": "^0.3.2",
"cors": "^2.7.1",
"css-loader": "^0.23.1",
"file-loader": "^0.8.5",
npm install
echo 'Fetching third-party node_modules for '$PLUGIN_NAME'...done'
echo 'Packaging '$PLUGIN_NAME' using webpack'
-ui_plugin_cmake_build=true ./node_modules/.bin/webpack --progress --config webpack.production.config.js
+ui_plugin_cmake_build=true ./node_modules/.bin/webpack --optimize-minimize --progress --config webpack.production.config.js
echo 'Packaging '$PLUGIN_NAME' using webpack... done'
echo 'Building plugin '$PLUGIN_NAME'... done'
import TreeView from 'react-treeview';
import '../node_modules/react-treeview/react-treeview.css';
import AppHeader from 'widgets/header/header.jsx';
+import ScreenLoader from 'widgets/screen-loader/screenLoader.jsx';
var crashActions = require('./crashActions.js');
var crashStore = require('./crashStore.js');
// var MissionControlStore = require('../missioncontrol/missionControlStore.js');
function openDashboard() {
- window.location.hash = "#/";
+ window.location.hash = "#/";
}
class CrashDetails extends React.Component {
- constructor(props) {
- super(props)
- var self = this;
- this.state = crashStore.getState();
- crashStore.listen(this.storeListener);
- }
- storeListener = (data) => {
- this.setState({
- list:data.crashList,
- noDebug:!this.hasDebugData(data.crashList)
- });
- }
- componentWillUnmount(){
- crashStore.unlisten(this.storeListener);
- }
- componentWillMount() {
- crashStore.get();
- }
- hasDebugData(list) {
- console.log(list);
- if (list && list.length > 0) {
- for (let i = 0; i < list.length; i++) {
- var trace = list[i].backtrace;
- for (let j = 0; j < trace.length; j++) {
- console.log(trace[j])
- if (trace[j].detail) {
- return true;
- }
+ constructor(props) {
+ super(props)
+ this.state = crashStore.getState();
+ crashStore.listen(this.storeListener);
+ }
+ storeListener = (data) => {
+ this.setState({
+ isLoading: data.isLoading,
+ list: data.crashList,
+ noDebug: !this.hasDebugData(data.crashList)
+ });
+ }
+ componentWillUnmount() {
+ crashStore.unlisten(this.storeListener);
+ }
+ componentWillMount() {
+ crashStore.get();
+ }
+ hasDebugData(list) {
+ console.log(list);
+ if (list && list.length > 0) {
+ for (let i = 0; i < list.length; i++) {
+ var trace = list[i].backtrace;
+ for (let j = 0; j < trace.length; j++) {
+ console.log(trace[j])
+ if (trace[j].detail) {
+ return true;
+ }
+ }
+ }
}
- }
+ return false;
}
- return false;
- }
- downloadFile(fileName, urlData) {
- var replacedNewLines = urlData.replace(/\\n/g, '\n');
- var replacedTabs = replacedNewLines.replace(/\\t/g, '\t');
- var replacedQuotes= replacedTabs.replace(/\\"/g, '"');
- var textFileBlob = new Blob([replacedQuotes], {type: 'text/plain;charset=UTF-8'});
- var aLink = document.createElement('a');
- var evt = document.createEvent("HTMLEvents");
- evt.initEvent("click");
- aLink.download = fileName;
- aLink.href = window.URL.createObjectURL(textFileBlob);
- aLink.dispatchEvent(evt);
- }
- render() {
- let html;
- var list = null;
- if (this.state != null) {
- var tree = <div style={{'marginLeft':'auto', 'marginRight':'auto', 'width':'230px', 'padding':'90px'}}> No Debug Information Available </div>;
- if (!this.state.noDebug)
- {
- var tree = this.state.list && this.state.list.map((node, i) => {
- var vm = node.name;
- var vm_label = <span>{vm}</span>;
- var backtrace = node.backtrace;
- return (
- <TreeView key={vm + '|' + i} nodeLabel={vm_label} defaultCollapsed={false}>
- {backtrace.map(details => {
-
- //Needed to decode HTML
- var text_temp = document.createElement("textarea")
- text_temp.innerHTML = details.detail;
- var text_temp = text_temp.value;
- var arr = text_temp.split(/\n/);
- var text = [];
- for (let i = 0; i < arr.length; i++) {
- text.push(arr[i]);
- text.push(<br/>)
- }
+ downloadFile(fileName) {
+ // wait till download selected to create text blob
+ let urlData = JSON.stringify(this.state.list, null, 2);
+ let replacedNewLines = urlData.replace(/\\n/g, '\n');
+ let replacedTabs = replacedNewLines.replace(/\\t/g, '\t');
+ let replacedQuotes = replacedTabs.replace(/\\"/g, '"');
+ let textFileBlob = new Blob([replacedQuotes], { type: 'text/plain;charset=UTF-8' });
+ let aLink = document.createElement('a');
+ aLink.download = fileName;
+ aLink.href = window.URL.createObjectURL(textFileBlob);
+ aLink.click(); // suprise, this works without being on the page
+ // it seems to cause no problems cleaning up the blob right away
+ window.URL.revokeObjectURL(aLink.href);
+ // assuming aLink just goes away when this function ends
+ }
+ render() {
+ let html;
+ if (this.state != null) {
+ if (!this.state.noDebug) {
+ let tree = this.state.list && this.state.list.map((node, i) => {
+ const vm = node.name;
+ const vm_label = <span>{vm}</span>;
+ const backtrace = node.backtrace;
+ return (
+ <TreeView key={vm + '|' + i} nodeLabel={vm_label} defaultCollapsed={false}>
+ {backtrace.map(details => {
+ // do some trickery to normalize details 'new line' char(s)
+ let textareaElement = document.createElement("textarea")
+ textareaElement.innerHTML = details.detail;
+ let detailsFormatted = textareaElement.value;
+ let arr = detailsFormatted.split(/\n/);
+ let text = [];
+ for (let i = 0; i < arr.length; i++) {
+ text.push(arr[i]);
+ text.push(<br key={'line-' + i} />); // react likes keys on array children
+ }
- return (
- <TreeView nodeLabel={<span>{details.id}</span>} key={vm + '||' + details.id} defaultCollapsed={false}>
- <p>{text}</p>
- </TreeView>
- );
- })}
- </TreeView>
- );
- })}
- html = (
- <div className="crash-details-wrapper">
- <div className="form-actions">
- <button role="button" className="dark" onClick={this.state.noDebug ? false : this.downloadFile.bind(this, 'crash.txt', 'data:text;charset=UTF-8,' + decodeURIComponent(JSON.stringify(this.state.list, null, 2)))}> Download Crash Details</button>
- </div>
- <div className="crash-container">
- <h2> Debug Information </h2>
- <div className="tree">{tree}</div>
- </div>
- </div>
- );
- } else {
- html = <div className="crash-container"></div>
- };
- let refPage = window.sessionStorage.getItem('refPage');
- refPage = JSON.parse(refPage);
- return (
+ return (
+ <TreeView nodeLabel={<span>{details.id}</span>} key={vm + '||' + details.id} defaultCollapsed={false}>
+ <p>{text}</p>
+ </TreeView>
+ );
+ })}
+ </TreeView>
+ );
+ });
+ let doDownload = this.downloadFile.bind(this, 'crash.txt');
+ html = (
+ <div className="crash-details-wrapper">
+ <div className="form-actions">
+ <button role="button" className="dark" onClick={this.state.noDebug ? false : doDownload}>Download Crash Details</button>
+ </div>
+ <div className="crash-container">
+ <h2> Debug Information </h2>
+ <div className="tree">{tree}</div>
+ </div>
+ </div>
+ );
+ } else {
+ let text = this.state.isLoading ? "Loading Debug Information" : "No Debug Information Available"
+ html = (
+ <div className="crash-details-wrapper">
+ <div className="crash-container">
+ <div style={{ 'marginLeft': 'auto', 'marginRight': 'auto', 'width': '230px', 'padding': '90px' }}>{text}</div>
+ </div>
+ </div>
+ );
+ }
+ } else {
+ html = <div className="crash-container"></div>
+ };
+ let refPage = window.sessionStorage.getItem('refPage');
+ refPage = JSON.parse(refPage);
+ return (
<div className="crash-app">
- {html}
+ {html}
+ <ScreenLoader show={this.state.isLoading}/>
</div>
- );
- }
+ );
+ }
}
export default CrashDetails;
function crashStore () {
this.exportAsync(require('./crashSource.js'));
this.bindActions(require('./crashActions.js'));
+ this.isLoading = false;
+ this.crashList = null;
}
crashStore.prototype.getCrashDetailsSuccess = function(list) {
this.setState({
- crashList:list
+ isLoading: false,
+ crashList: list
})
- console.log('success', list)
+ console.log('Crash details load success', list)
};
crashStore.prototype.getCrashDetailsLoading = function(info) {
+ this.setState({
+ isLoading: true,
+ crashList: null,
+ })
console.log('Loading crash details...', info)
};
crashStore.prototype.getCrashDetailsFailure = function(info) {
+ this.setState({
+ isLoading: false,
+ error: info
+ })
console.log('Failed to retrieve crash/debug details', info)
};
-module.exports = Alt.createStore(crashStore);;
+module.exports = Alt.createStore(crashStore, 'crashStore');;
* limitations under the License.
*
*/
-var Webpack = require('webpack');
+var webpack = require('webpack');
var path = require('path');
var nodeModulesPath = path.resolve(__dirname, 'node_modules');
var buildPath = path.resolve(__dirname, 'public', 'build');
var uiPluginCmakeBuild = process.env.ui_plugin_cmake_build || false;
var frameworkPath = uiPluginCmakeBuild?'../../../../skyquake/skyquake-build/framework':'../../framework';
var HtmlWebpackPlugin = require('html-webpack-plugin');
+var CompressionPlugin = require("compression-webpack-plugin");
// Added to overcome node-sass bug https://github.com/iam4x/isomorphic-flux-boilerplate/issues/62
process.env.UV_THREADPOOL_SIZE=64;
var config = {
},
plugins: [
new HtmlWebpackPlugin({
- filename: '../index.html'
- , templateContent: '<div id="app"></div>'
- }),
- new Webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js", Infinity)
+ filename: '../index.html',
+ templateContent: '<div id="app"></div>'
+ })
]
};
+
+if (process.argv.indexOf('--optimize-minimize') !== -1) {
+ // we are going to output a gzip file in the production process
+ config.output.filename = "gzip-" + config.output.filename;
+ config.plugins.push(new webpack.DefinePlugin({ // <-- key to reducing React's size
+ 'process.env': {
+ 'NODE_ENV': JSON.stringify('production')
+ }
+ }));
+ config.plugins.push(new CompressionPlugin({
+ asset: "[path]", // overwrite js file with gz file
+ algorithm: "gzip",
+ test: /\.(js)$/
+ }));
+}
module.exports = config;
"babel-preset-es2015": "^6.3.13",
"babel-preset-stage-0": "^6.3.13",
"babel-runtime": "^6.3.19",
+ "compression-webpack-plugin": "^0.3.2",
"express": "^4.13.3",
"history": "^1.17.0",
"json-loader": "^0.5.4",
npm install
echo 'Fetching third-party node_modules for '$PLUGIN_NAME'...done'
echo 'Packaging '$PLUGIN_NAME' using webpack'
-ui_plugin_cmake_build=true ./node_modules/.bin/webpack --progress --config webpack.production.config.js
+ui_plugin_cmake_build=true ./node_modules/.bin/webpack --optimize-minimize --progress --config webpack.production.config.js
echo 'Packaging '$PLUGIN_NAME' using webpack... done'
echo 'Building plugin '$PLUGIN_NAME'... done'
* limitations under the License.
*
*/
-var Webpack = require('webpack');
+var webpack = require('webpack');
var path = require('path');
var nodeModulesPath = path.resolve(__dirname, 'node_modules');
var buildPath = path.resolve(__dirname, 'public', 'build');
var uiPluginCmakeBuild = process.env.ui_plugin_cmake_build || false;
var frameworkPath = uiPluginCmakeBuild?'../../../../skyquake/skyquake-build/framework':'../../framework';
var HtmlWebpackPlugin = require('html-webpack-plugin');
+var CompressionPlugin = require("compression-webpack-plugin");
// Added to overcome node-sass bug https://github.com/iam4x/isomorphic-flux-boilerplate/issues/62
process.env.UV_THREADPOOL_SIZE=64;
var config = {
},
plugins: [
new HtmlWebpackPlugin({
- filename: '../index.html',
+ filename: '../index.html',
templateContent: '<div id="content"></div>'
- }),
- new Webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js", Infinity)
+ })
]
};
+
+if (process.argv.indexOf('--optimize-minimize') !== -1) {
+ // we are going to output a gzip file in the production process
+ config.output.filename = "gzip-" + config.output.filename;
+ config.plugins.push(new webpack.DefinePlugin({ // <-- key to reducing React's size
+ 'process.env': {
+ 'NODE_ENV': JSON.stringify('production')
+ }
+ }));
+ config.plugins.push(new CompressionPlugin({
+ asset: "[path]", // overwrite js file with gz file
+ algorithm: "gzip",
+ test: /\.(js)$/
+ }));
+}
module.exports = config;
"babel-preset-es2015": "^6.3.13",
"babel-preset-stage-0": "^6.3.13",
"babel-runtime": "^6.3.19",
+ "compression-webpack-plugin": "^0.3.2",
"express": "^4.13.3",
"history": "^1.17.0",
"json-loader": "^0.5.4",
npm install
echo 'Fetching third-party node_modules for '$PLUGIN_NAME'...done'
echo 'Packaging '$PLUGIN_NAME' using webpack'
-ui_plugin_cmake_build=true ./node_modules/.bin/webpack --progress --config webpack.production.config.js
+ui_plugin_cmake_build=true ./node_modules/.bin/webpack --optimize-minimize --progress --config webpack.production.config.js
echo 'Packaging '$PLUGIN_NAME' using webpack... done'
echo 'Building plugin '$PLUGIN_NAME'... done'
* limitations under the License.
*
*/
-var Webpack = require('webpack');
+var webpack = require('webpack');
var path = require('path');
var nodeModulesPath = path.resolve(__dirname, 'node_modules');
var buildPath = path.resolve(__dirname, 'public', 'build');
var uiPluginCmakeBuild = process.env.ui_plugin_cmake_build || false;
var frameworkPath = uiPluginCmakeBuild?'../../../../skyquake/skyquake-build/framework':'../../framework';
var HtmlWebpackPlugin = require('html-webpack-plugin');
+var CompressionPlugin = require("compression-webpack-plugin");
// Added to overcome node-sass bug https://github.com/iam4x/isomorphic-flux-boilerplate/issues/62
process.env.UV_THREADPOOL_SIZE=64;
var config = {
},
plugins: [
new HtmlWebpackPlugin({
- filename: '../index.html'
- , templateContent: '<div id="content"></div>'
- }),
- new Webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js", Infinity)
+ filename: '../index.html',
+ templateContent: '<div id="content"></div>'
+ })
]
};
+
+if (process.argv.indexOf('--optimize-minimize') !== -1) {
+ // we are going to output a gzip file in the production process
+ config.output.filename = "gzip-" + config.output.filename;
+ config.plugins.push(new webpack.DefinePlugin({ // <-- key to reducing React's size
+ 'process.env': {
+ 'NODE_ENV': JSON.stringify('production')
+ }
+ }));
+ config.plugins.push(new CompressionPlugin({
+ asset: "[path]", // overwrite js file with gz file
+ algorithm: "gzip",
+ test: /\.(js)$/
+ }));
+}
module.exports = config;
try {
if (nsr["monitoring-param"]) {
nsr["monitoring-param"].map(function(m) {
- var vnfr = vnfrs[m["vnfr-id"]] || {};
+ // var vnfr = vnfrs[m["vnfr-id"]] || {};
+ // m["vnfr-name"] = vnfr['name'] ? vnfr['name'] : (vnfr['short-name'] ? vnfr['short-name'] : 'VNFR');
+ var groupTag = m['group-tag'];
+ var vnfrId = m['vnfr-mon-param-ref'] && m['vnfr-mon-param-ref'][0] && m['vnfr-mon-param-ref'][0]['vnfr-id-ref'];
+ var vnfr = vnfrs[vnfrId] || {};
m["vnfr-name"] = vnfr['name'] ? vnfr['name'] : (vnfr['short-name'] ? vnfr['short-name'] : 'VNFR');
+ m['group-tag'] = (groupTag ? (groupTag + ' - ') : '') + m['vnfr-name'] + (vnfrId ? ' (' + vnfrId.substring(1,8) + '...)' : '');
+
});
}
} catch (e) {
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.3.13",
"babel-runtime": "^6.3.19",
+ "compression-webpack-plugin": "^0.3.2",
"cors": "^2.7.1",
"css-loader": "^0.23.1",
"file-loader": "^0.8.5",
"react-addons-css-transition-group": "^0.14.7",
"sass-loader": "^3.1.2",
"style-loader": "^0.13.0",
- "webpack": "^1.3.0",
+ "webpack": "~1.14.0",
"webpack-dev-server": "^1.10.1"
}
}
npm install
echo 'Fetching third-party node_modules for '$PLUGIN_NAME'...done'
echo 'Packaging '$PLUGIN_NAME' using webpack'
-ui_plugin_cmake_build=true ./node_modules/.bin/webpack --progress --config webpack.production.config.js
+ui_plugin_cmake_build=true ./node_modules/.bin/webpack --optimize-minimize --progress --config webpack.production.config.js
echo 'Packaging '$PLUGIN_NAME' using webpack... done'
echo 'Building plugin '$PLUGIN_NAME'... done'
});
};
-module.exports = alt.createStore(CreateFleet);
+module.exports = alt.createStore(CreateFleet, 'CreateFleet');
class InstantiateDashboard extends React.Component {
constructor(props) {
super(props);
- this.Store = this.props.flux.stores.hasOwnProperty('InstantiateStore') ? this.props.flux.stores.InstantiateStore : this.props.flux.createStore(InstantiateStore );
+ this.Store = this.props.flux.stores.hasOwnProperty('InstantiateStore') ? this.props.flux.stores.InstantiateStore : this.props.flux.createStore(InstantiateStore, 'InstantiateStore');
this.state = this.Store.getState();
}
componentDidMount() {
import GUID from 'utils/guid.js';
import AppHeaderActions from 'widgets/header/headerActions.js';
import Alt from '../alt';
-import _ from 'lodash';
+import _cloneDeep from 'lodash/cloneDeep';
+import _find from 'lodash/find';
class LaunchNetworkServiceStore {
},
updateSSHkeyRef: function(i, j, remove){
return function(e) {
- let usersList = _.cloneDeep(self.usersList)
+ let usersList = _cloneDeep(self.usersList)
let keys = usersList[i]['ssh-authorized-key'];
if(!remove) {
let keyRef = JSON.parse(e.target.value).name;
// Create a filtered NSD payload from the decorated one as RW.REST cannot handle extra parameters now
let nsdPayload = {};
- nsdPayload = _.cloneDeep(_.find(this.state.nsd[0].descriptors, {id: this.state.selectedNSDid}));
+ nsdPayload = _cloneDeep(_find(this.state.nsd[0].descriptors, {id: this.state.selectedNSDid}));
if (nsdPayload != {}) {
nsdPayload['meta'] && delete nsdPayload['meta'];
pnfd: data.pnfd
});
}
-// export default Alt.createStore(LaunchNetworkServiceStore);
export default LaunchNetworkServiceStore;
import NsListPanel from './nsListPanel/nsListPanel.jsx';
import Crouton from 'react-crouton'
import AppHeader from 'widgets/header/header.jsx';
-import Utils from 'utils/utils.js';
import './launchpad.scss';
import {SkyquakeRBAC, isRBACValid} from 'widgets/skyquake_rbac/skyquakeRBAC.jsx';
}
&-body {
- -ms-flex-flow: col wrap;
- flex-flow: col wrap;
+ -ms-flex-flow: column wrap;
+ flex-flow: column wrap;
}
.nsrSummary {
return resolve(false);
}
$.ajax({
- url: '/socket-polling',
+ url: '/socket-polling?api_server=' + API_SERVER,
type: 'POST',
beforeSend: Utils.addAuthorizationStub,
data: {
*
*/
import Alt from './alt';
-
+import _filter from 'lodash/filter';
+import _extend from 'lodash/extend';
+import _debounce from 'lodash/debounce';
var FleetSource = require('./launchpadFleetSource.js');
var FleetActions = require('./launchpadFleetActions.js');
import CardActions from './launchpad_card/launchpadCardActions.js';
var FleetStore;
-var _ = require('underscore');
-// _.debounce(function(){});
function FleetStoreConstructor() {
var self = this;
this.fleets = [];
let deletingNSRs = [];
if (self.nsrs) {
- deletingNSRs = _.filter(self.nsrs, function(nsr) {
+ deletingNSRs = _filter(self.nsrs, function(nsr) {
return nsr.deleting == true;
});
};
deletingNSRs.forEach(function(deletingNSR) {
data.nsrs.map(nsr => {
if (nsr.id == deletingNSR.id) {
- _.extend(nsr, deletingNSR);
+ _extend(nsr, deletingNSR);
}
});
});
FleetStoreConstructor.prototype.getNsrInstancesError = function(data) {
console.log('ERROR', data)
};
-FleetStoreConstructor.prototype.handleUpdateControlInput = _.debounce(function(data) {
+FleetStoreConstructor.prototype.handleUpdateControlInput = _debounce(function(data) {
var opt = data[0];
FleetStore.nsrControl(opt.operation, opt.url, data[1])
}, 500).bind(null);
data['console-url'] && window.open(data['console-url']);
}
-FleetStore = Alt.createStore(FleetStoreConstructor);
+FleetStore = Alt.createStore(FleetStoreConstructor, 'FleetStore');
module.exports = FleetStore;
import React from 'react';
import './jobListCard.scss'
+import TreeView from 'react-treeview';
import Uptime from 'widgets/uptime/uptime.jsx';
import Modal from 'react-awesome-modal';
getJobDetails(job) {
let jobDetails = null;
if (job['job-status-details']) {
+ let jobDetailsArray = job['job-status-details'].split(/\\n/);
+ let jobDetailsText = [];
+ jobDetailsArray && jobDetailsArray.map((jobDetail) => {
+ jobDetailsText.push(jobDetail);
+ jobDetailsText.push(<br/>);
+ });
jobDetails = (
<section className='jobListCard--details'>
<h4 onClick={this.openModal.bind(this)}>Job Details</h4>
<Modal
+ className='jobListCard--details--modal'
visible={this.state.modalVisible}
width="600"
height="400"
effect="fadeInUp">
- <div>
- <div className='jobListCard--details--content'>{job['job-status-details']}</div>
+ <div className='jobListCard--details--tree'>
+ <TreeView nodeLabel={<span>Job Details</span>} key={'job-details'} defaultCollapsed={false}>
+ <p>{jobDetailsText}</p>
+ </TreeView>
<h4 className='jobListCard--details--close' onClick={this.closeModal.bind(this)}>Close</h4>
</div>
</Modal>
}
&--details {
font-size:0.75rem;
+
+ >div {
+ display: flex;
+ >div {
+ display: flex;
+ padding: 1em 0 0 1em;
+ }
+ }
+
+ &--tree {
+ display: flex;
+ flex-direction: column;
+
+ .tree-view {
+ min-height: 300px;
+ min-width: 580px;
+ overflow-y: scroll;
+ }
+ }
+
h4 {
padding-bottom:0.5rem;
text-decoration:underline;
vnfrConfigPrimitive.name = vnfr['short-name'];
vnfrConfigPrimitive['vnfr-id-ref'] = vnfr['id'];
//input references
- let configPrimitives = vnfr['vnf-configuration']['service-primitive'];
+ let configPrimitives = vnfr['vnf-configuration'] && vnfr['vnf-configuration']['service-primitive'];
//input references by key
let vnfrConfigPrimitives = {}
*/
import React from 'react';
import RecordViewStore from '../recordViewer/recordViewStore.js';
+import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx';
import Button from 'widgets/button/rw.button.js';
import Utils from 'utils/utils.js';
-import _ from 'underscore';
import UpTime from 'widgets/uptime/uptime.jsx';
import './nsrScalingGroups.scss';
-export default class NsrScalingGroups extends React.Component {
+class NsrScalingGroups extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
- handleExecuteClick = (nsr_id, scaling_group_id, event) => {
+ handleExecuteClick = (nsr_id, scaling_group_id, max_instance_count, event) => {
+ let self = this;
+ if (this.getInstancesForScalingGroup(scaling_group_id) == max_instance_count) {
+ self.props.flux.actions.global.showNotification("Maximum allowed scaling instances created for this group. Cannot create any more");
+ return;
+ }
RecordViewStore.createScalingGroupInstance({
nsr_id: nsr_id,
scaling_group_id: scaling_group_id
let sgInstanceTable = this.createScalingGroupTable(sgd.name);
- let sgCreateInstanceButton = <Button label='Create Scaling Group Instance' className="dark" isDisabled={this.getInstancesForScalingGroup(sgd.name) == sgd["max-instance-count"]} isLoading={false} onClick={this.handleExecuteClick.bind(this, this.props.data.id, sgd.name)} />
+ let sgCreateInstanceButton = <Button label='Create Scaling Group Instance' className="dark" isDisabled={this.getInstancesForScalingGroup(sgd.name) == sgd["max-instance-count"]} isLoading={false} onClick={this.handleExecuteClick.bind(this, this.props.data.id, sgd.name, sgd['max-instance-count'])} />
let scalingGroup =
<div>
}
}
+
+export default SkyquakeComponent(NsrScalingGroups);
import JobListCard from '../launchpad_card/jobListCard.jsx';
import NSVirtualLinks from '../virtual_links/nsVirtualLinks.jsx';
import LaunchpadFleetStore from '../launchpadFleetStore.js';
-
+import _forEach from 'lodash/forEach';
import Prism from 'prismjs';
import 'prismjs/themes/prism.css';
// </pre>
function buildProperties(obj) {
let p = [];
- _.forEach(obj, function(v, k) {
+ _forEach(obj, function(v, k) {
p.push(
- <div style={{margin: '0.5rem 0.5rem'}} key={k + vi}>
+ <div style={{margin: '0.5rem 0.5rem'}} key={k + '-' + vi}>
<div style={{margin: '0 0.5rem',
fontWeight: 'bold', textTransform: 'uppercase', color: '#5b5b5b'}}>{k}</div>
- <div style={{margin: '0 0.5rem'}}>{v.constructor.name != 'Object' ? v : buildProperties(v)}</div>
+ <div style={{margin: '0 0.5rem'}}>{(v.constructor.name == 'String' || v.constructor.name == 'Number') ? v : buildProperties(v)}</div>
</div>
)
});
if(this.props.isLoading) {
html = <DashboardCard className="loading" showHeader={true} title={cardData["short-name"]}><LoadingIndicator size={10} show={true} /></DashboardCard>
} else {
- let glyphValue = (this.props.mmmrecordDetailsToggleValue) ? "chevron-left" : "chevron-right";
+ let glyphValue = (!this.props.recordDetailsToggleValue) ? "chevron-left" : "chevron-right";
if (this.props.type == 'nsr') {
tabList.push(
return new Promise(function(resolve, reject) {
console.log('Getting NSR Socket');
$.ajax({
- url: '/socket-polling',
+ url: '/socket-polling?api_server=' + API_SERVER,
type: 'POST',
beforeSend: Utils.addAuthorizationStub,
data: {
return new Promise(function(resolve, reject) {
console.log('Getting Job Socket');
$.ajax({
- url: '/socket-polling',
+ url: '/socket-polling?api_server=' + API_SERVER,
type: 'POST',
beforeSend: Utils.addAuthorizationStub,
data: {
return new Promise(function(resolve, reject) {
console.log('Getting VNFR Socket for: ' + state.recordID);
$.ajax({
- url: '/socket-polling',
+ url: '/socket-polling?api_server=' + API_SERVER,
type: 'POST',
beforeSend: Utils.addAuthorizationStub,
data: {
// import source
// import AppHeaderActions from 'widgets/header/headerActions.js';
import Alt from '../alt';
-import _ from 'underscore';
+import _find from 'lodash/find';
+import _indexOf from 'lodash/indexOf';
+import _extend from 'lodash/extend';
class RecordViewStore {
constructor() {
sgInstance['vnfrs'] && sgInstance['vnfrs'].map((vnfr, vnfrIndex) => {
scaledVnfrs.push(vnfr);
- let vnfrObj = _.findWhere(nsrs.vnfrs, {id: vnfr});
+ let vnfrObj = _find(nsrs.vnfrs, {id: vnfr});
scaledVnfNav.vnfr.push({
name: vnfrObj['short-name'],
id: vnfr,
// Non-scaled VNFRs
nsrs.vnfrs.map(function(vnfr, vnfrIndex) {
- if (_.indexOf(scaledVnfrs, vnfr.id) == -1) {
+ if (_indexOf(scaledVnfrs, vnfr.id) == -1) {
nav.push({
name: vnfr["short-name"],
id: vnfr.id,
};
}
- navigatorState = _.extend(navigatorState, {
+ navigatorState = _extend(navigatorState, {
recordData: recordData,
recordType: type,
cardLoading: false,
};
}
-export default Alt.createStore(RecordViewStore);
+export default Alt.createStore(RecordViewStore, 'RecordViewStore');
import GUID from 'utils/guid.js';
import AppHeaderActions from 'widgets/header/headerActions.js';
import Alt from '../alt';
-import _ from 'lodash';
+import _cloneDeep from 'lodash/cloneDeep';
+import _merge from 'lodash/merge';
export default class SshKeyStore {
keys: [],
entities: {}
};
- this.dataCache = _.cloneDeep(this.data);
+ this.dataCache = _cloneDeep(this.data);
this.newKey = {
name: '',
key: ''
cancelEditSshKeyPair = (k) => {
let self = this;
return function(e) {
- let data = _.cloneDeep(self.data);
+ let data = _cloneDeep(self.data);
data.entities[k].key = self.dataCache.entities[k].key;
data.entities[k].isEditable = false;
self.setState({data:data});
isEditable: false
};
this.setState({
- dataCache: _.cloneDeep(keys),
+ dataCache: _cloneDeep(keys),
data: keys,
newKey: {
name: '',
isEditable: false
};
this.setState({
- dataCache: _.cloneDeep(keys),
+ dataCache: _cloneDeep(keys),
data: keys,
newKey: {
name: '',
keys.keys.splice(keys.keys.indexOf(data.name), 1);
delete keys.entities[data.name];
this.setState({
- dataCache: _.cloneDeep(keys),
+ dataCache: _cloneDeep(keys),
data: keys
})
}
let flattened = this.flattenKeys(data);
this.setState({
data: flattened,
- dataCache: _.cloneDeep(flattened)
+ dataCache: _cloneDeep(flattened)
})
}
updateSshKeyPair = (k, field) => {
};
data && data.map(function(d){
fd.keys.push(d.name);
- fd.entities[d.name] = _.merge({isEditable: false}, d)
+ fd.entities[d.name] = _merge({isEditable: false}, d)
});
return fd;
}
class SshKeys extends Component {
constructor(props) {
super(props);
- this.Store = this.props.flux.stores.hasOwnProperty('SshKeyStore') ? this.props.flux.stores.SshKeyStore : this.props.flux.createStore(SshKeyStore);
+ this.Store = this.props.flux.stores.hasOwnProperty('SshKeyStore') ? this.props.flux.stores.SshKeyStore : this.props.flux.createStore(SshKeyStore, 'SshKeyStore');
this.state = this.Store.getState();
this.Store.listen(this.handleUpdate);
}
import DashboardCard from 'widgets/dashboard_card/dashboard_card.jsx';
import Listy from 'widgets/listy/listy.js';
-import _ from 'underscore';
+import _isEmpty from 'lodash/isEmpty';
export default class TopologyDetail extends React.Component {
constructor(props) {
}
detailData(data) {
- if (_.isEmpty(data)) {
+ if (_isEmpty(data)) {
return {};
} else {
return {
return resolve(false);
}
$.ajax({
- url: '/socket-polling',
+ url: '/socket-polling?api_server=' + API_SERVER,
type: 'POST',
beforeSend: Utils.addAuthorizationStub,
data: {
});
}
}
- export default Alt.createStore(TopologyL2Store);
+ export default Alt.createStore(TopologyL2Store, 'TopologyL2Store');
return resolve(false);
}
$.ajax({
- url: '/socket-polling',
+ url: '/socket-polling?api_server=' + API_SERVER,
type: 'POST',
beforeSend: Utils.addAuthorizationStub,
data: {
}
}
-export default Alt.createStore(TopologyStore);
+export default Alt.createStore(TopologyStore, 'TopologyStore');
*/
import React from 'react';
import Utils from 'utils/utils.js';
-import _ from 'lodash';
import './nsVirtualLinks.scss';
import NSVirtualLinkCreateStore from './nsVirtualLinkCreateStore.js';
import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx';
class NsVirtualLinkCreate extends React.Component {
constructor(props) {
super(props);
- this.Store = this.props.flux.stores.hasOwnProperty('NSVirtualLinkCreateStore') ? this.props.flux.stores.NSVirtualLinkCreateStore : this.props.flux.createStore(NSVirtualLinkCreateStore);
+ this.Store = this.props.flux.stores.hasOwnProperty('NSVirtualLinkCreateStore') ?
+ this.props.flux.stores.NSVirtualLinkCreateStore : this.props.flux.createStore(NSVirtualLinkCreateStore, 'NSVirtualLinkCreateStore');
this.state = this.Store.getState();
this.Store.listen(this.handleUpdate);
}
import NSVirtualLinkCreateActions from './nsVirtualLinkCreateActions.js';
import NSVirtualLinkCreateSource from './nsVirtualLinkCreateSource.js';
import Alt from '../alt';
-import _ from 'lodash';
+import _cloneDeep from 'lodash/cloneDeep';
+import _pickBy from 'lodash/pickBy';
+import _identity from 'lodash/identity';
class NSVirtualLinkCreateStore {
constructor() {
}
updateFirstLevelKey = (key, e) => {
- let vld = _.cloneDeep(this.vld);
+ let vld = _cloneDeep(this.vld);
let value = e.target.nodeName == "SELECT" ? JSON.parse(e.target.value) : e.target.value;
vld[key] = value;
this.setState({
}
updateSecondLevelKey = (firstLevelKey, secondLevelKey, e) => {
- let vld = _.cloneDeep(this.vld);
+ let vld = _cloneDeep(this.vld);
if (!vld[firstLevelKey]) {
vld[firstLevelKey] = {};
}
}
updateVLDInitParamsValue = (currentVLDInitParamsType, e) => {
- let vld = _.cloneDeep(this.vld);
+ let vld = _cloneDeep(this.vld);
this.vldInitParamsTypes.map((vldInitParamType) => {
if (currentVLDInitParamsType == vldInitParamType) {
let value = e.target.nodeName == "SELECT" ? JSON.parse(e.target.value) : e.target.value;
}
updateFirstLevelListKeyChange = (listName, index, keyName, e) => {
- let vld = _.cloneDeep(this.vld);
+ let vld = _cloneDeep(this.vld);
!vld[listName] && (vld[listName] = []);
addConnectionPointRef = () => {
let vld = {};
if (this.vld) {
- vld = _.cloneDeep(this.vld);
+ vld = _cloneDeep(this.vld);
if (!vld['vnfd-connection-point-ref']) {
vld['vnfd-connection-point-ref'] = [];
}
}
removeConnectionPointRef = (vnfdConnectionPointRefIndex) => {
- let vld = _.cloneDeep(this.vld);
+ let vld = _cloneDeep(this.vld);
vld['vnfd-connection-point-ref'].splice(vnfdConnectionPointRefIndex, 1);
this.setState({
vld: vld
cleanupPayload = (mode, vld) => {
// Do necessary cleanup here
- let cleanVld = _.pickBy(vld, _.identity);
+ let cleanVld = _pickBy(vld, _identity);
return cleanVld;
}
import React from 'react';
import RecordViewStore from '../recordViewer/recordViewStore.js';
import Utils from 'utils/utils.js';
-import _ from 'lodash';
+import _isArray from 'lodash/isArray';
import './nsVirtualLinks.scss';
import UpTime from 'widgets/uptime/uptime.jsx';
import NSVirtualLinksStore from './nsVirtualLinksStore.js';
let value = this.resolvePath(this.props.virtualLink, field.key);
let textFields = [];
- if (_.isArray(value)) {
+ if (_isArray(value)) {
value.map((v, idx) => {
let transformedValue = this.transformValue(field, v);
textFields.push(
import NSVirtualLinkCreateStore from './nsVirtualLinkCreateStore.js';
import Button from 'widgets/button/rw.button.js';
import Utils from 'utils/utils.js';
-import _ from 'lodash';
+import _find from 'lodash/find';
import './nsVirtualLinks.scss';
import UpTime from 'widgets/uptime/uptime.jsx';
import NSVirtualLinkDetails from './nsVirtualLinkDetails.jsx';
class NsVirtualLinks extends React.Component {
constructor(props) {
super(props);
- this.Store = this.props.flux.stores.hasOwnProperty('NSVirtualLinkCreateStore') ? this.props.flux.stores.NSVirtualLinkCreateStore : this.props.flux.createStore(NSVirtualLinkCreateStore);
+ this.Store = this.props.flux.stores.hasOwnProperty('NSVirtualLinkCreateStore') ?
+ this.props.flux.stores.NSVirtualLinkCreateStore : this.props.flux.createStore(NSVirtualLinkCreateStore, 'NSVirtualLinkCreateStore');
this.state = {};
this.state.mode = 'viewing'; // Can be 'viewing'/'creating'/'editing'/'deleting'. Default is 'viewing'
this.selectedVirtualLink = null;
handleSelectVirtualLinkClick = (virtualLinkId, event) => {
this.setState({
mode: 'viewing',
- selectedVirtualLink: this.props.data && this.props.data['decorated-vlrs'] && _.find(this.props.data['decorated-vlrs'], {id: virtualLinkId}),
+ selectedVirtualLink: this.props.data && this.props.data['decorated-vlrs'] && _find(this.props.data['decorated-vlrs'], {id: virtualLinkId}),
editingVirtualLink: null
});
}
event.stopPropagation();
this.setState({
mode: 'editing',
- editingVirtualLink: this.props.data && this.props.data['nsd'] && this.props.data['nsd']['vld'] && _.find(this.props.data['nsd']['vld'], {id: vldId}),
- selectedVirtualLink: this.props.data && this.props.data['decorated-vlrs'] && _.find(this.props.data['decorated-vlrs'], {id: vlrId})
+ editingVirtualLink: this.props.data && this.props.data['nsd'] && this.props.data['nsd']['vld'] && _find(this.props.data['nsd']['vld'], {id: vldId}),
+ selectedVirtualLink: this.props.data && this.props.data['decorated-vlrs'] && _find(this.props.data['decorated-vlrs'], {id: vlrId})
});
}
}
console.log(nsr_id)
$.ajax({
- url: '//' + window.location.hostname + ':' + window.location.port + '/socket-polling',
+ url: '//' + window.location.hostname + ':' + window.location.port + '/socket-polling?api_server=' + API_SERVER,
type: 'POST',
beforeSend: Utils.addAuthorizationStub,
data: {
}
};
-export default alt.createStore(VnfrStore)
+export default alt.createStore(VnfrStore, 'VnfrStore')
* limitations under the License.
*
*/
-var Webpack = require('webpack');
+var webpack = require('webpack');
var path = require('path');
var nodeModulesPath = path.resolve(__dirname, 'node_modules');
var buildPath = path.resolve(__dirname, 'public', 'build');
var uiPluginCmakeBuild = process.env.ui_plugin_cmake_build || false;
var frameworkPath = uiPluginCmakeBuild?'../../../../skyquake/skyquake-build/framework':'../../framework';
var HtmlWebpackPlugin = require('html-webpack-plugin');
-var CommonsPlugin = new require("webpack/lib/optimize/CommonsChunkPlugin")
+var CompressionPlugin = require("compression-webpack-plugin");
+
// Added to overcome node-sass bug https://github.com/iam4x/isomorphic-flux-boilerplate/issues/62
process.env.UV_THREADPOOL_SIZE=64;
var config = {
},
plugins: [
new HtmlWebpackPlugin({
- filename: '../index.html'
- , templateContent: '<div id="app"></div>'
- }),
- new Webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js", Infinity)
+ filename: '../index.html',
+ templateContent: '<div id="app"></div>'
+ })
]
};
+
+if (process.argv.indexOf('--optimize-minimize') !== -1) {
+ // we are going to output a gzip file in the production process
+ config.output.filename = "gzip-" + config.output.filename;
+ config.plugins.push(new webpack.DefinePlugin({ // <-- key to reducing React's size
+ 'process.env': {
+ 'NODE_ENV': JSON.stringify('production')
+ }
+ }));
+ config.plugins.push(new CompressionPlugin({
+ asset: "[path]", // overwrite js file with gz file
+ algorithm: "gzip",
+ test: /\.(js)$/
+ }));
+}
module.exports = config;
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.3.13",
"babel-runtime": "^6.3.19",
+ "compression-webpack-plugin": "^0.3.2",
"cors": "^2.7.1",
"css-loader": "^0.23.1",
"file-loader": "^0.8.5",
npm install
echo 'Fetching third-party node_modules for '$PLUGIN_NAME'...done'
echo 'Packaging '$PLUGIN_NAME' using webpack'
-ui_plugin_cmake_build=true ./node_modules/.bin/webpack --progress --config webpack.production.config.js
+ui_plugin_cmake_build=true ./node_modules/.bin/webpack --optimize-minimize --progress --config webpack.production.config.js
echo 'Packaging '$PLUGIN_NAME' using webpack... done'
echo 'Building plugin '$PLUGIN_NAME'... done'
*
*/
import React from 'react';
-import _ from 'lodash';
+import _isEmpty from 'lodash/isEmpty';
+import _find from 'lodash/find';
+import _cloneDeep from 'lodash/cloneDeep';
import './logging.scss';
import Button from 'widgets/button/rw.button.js';
getData() {
LoggingStore.getLoggingConfig();
this.setState({
- isLoading: _.isEmpty(this.state.loggingConfig)
+ isLoading: _isEmpty(this.state.loggingConfig)
});
}
componentWillUnmount = () => {
this.context.router.push({pathname: ''});
}
// removeCategoryNulls(config) {
- // let cleanConfig = _.cloneDeep(config);
+ // let cleanConfig = _cloneDeep(config);
// let cleanSeverities = [];
// config.defaultSeverities.map(function(d) {
// if (d.severity) {
// return cleanConfig;
// }
cleanupConfig(config) {
- let cleanConfig = _.cloneDeep(config);
+ let cleanConfig = _cloneDeep(config);
let cleanSeverities = [];
cleanConfig.defaultSeverities && cleanConfig.defaultSeverities.map((defSev) => {
if (defSev.severity) {
// for RIFT-14856 so that default severities map to syslog sink
// Find first syslog sink with (WTF - no type on sinks!) name syslog.
- let syslogSink = this.state.loggingConfig.sinks && _.find(this.state.loggingConfig.sinks, {
+ let syslogSink = this.state.loggingConfig.sinks && _find(this.state.loggingConfig.sinks, {
name: 'syslog'
});
let defaultSyslogSeverities = [];
this.state.loggingConfig && this.state.loggingConfig.defaultSeverities && this.state.loggingConfig.defaultSeverities.map((defaultSeverity) => {
// Mapping between default categories and names inside a sink
- let syslogFilterCategory = (syslogSink.filter && syslogSink.filter.category && _.find(syslogSink.filter.category, {
+ let syslogFilterCategory = (syslogSink.filter && syslogSink.filter.category && _find(syslogSink.filter.category, {
name: defaultSeverity.category
})) || {
name: defaultSeverity.category,
* limitations under the License.
*
*/
-import _ from 'lodash';
+import _cloneDeep from 'lodash/cloneDeep';
+import _findIndex from 'lodash/findIndex';
+import _remove from 'lodash/remove';
import LoggingActions from './loggingActions.js';
import LoggingSource from './loggingSource.js';
getLoggingConfigSuccess = (data) => {
console.log("LoggingStore.getLoggingConfigSuccess called. data=", data);
// Do we need to do a deep clone?
- const initialLoggingConfig = _.cloneDeep(data);
+ const initialLoggingConfig = _cloneDeep(data);
console.log("initialLoggingConfig=", initialLoggingConfig);
this.setState({
loggingConfig: data,
putLoggingConfigSuccess = (data) => {
console.log("LoggingStore.putLoggingConfigSuccess called. data=", data);
- const initialLoggingConfig = _.cloneDeep(this.loggingConfig);
+ const initialLoggingConfig = _cloneDeep(this.loggingConfig);
this.setState({
isLoading: false,
initialLoggingConfig: initialLoggingConfig
resetLoggingConfigData = (data) => {
console.log('LoggingStore.resetLoggingConfigData called. data=', data);
// Do we need to do a deep clone?
- const loggingConfig = _.cloneDeep(this.initialLoggingConfig);
+ const loggingConfig = _cloneDeep(this.initialLoggingConfig);
this.setState({
loggingConfig: loggingConfig
});
console.log("LoggingStore.updateCategoryDefaultSeverity:", catsev);
// find the category
- let catIndex = _.findIndex(this.loggingConfig.defaultSeverities, function(o) {
+ let catIndex = _findIndex(this.loggingConfig.defaultSeverities, function(o) {
return o.category == catsev.category;
});
console.log("catIndex=", catIndex);
// find the category (name) in the syslog sink
let self = this;
- let loggingConfig = _.cloneDeep(this.loggingConfig);
+ let loggingConfig = _cloneDeep(this.loggingConfig);
let syslogSinkIndex = -1;
let nulledCategories = this.nulledCategories;
if (sink.name == 'syslog') {
if (sink.filter) {
if (sink.filter.category) {
- let catIndex = _.findIndex(sink.filter.category, {
+ let catIndex = _findIndex(sink.filter.category, {
name: catsev.name
});
if (catIndex != -1) {
// missing elements is not allowed!
// When backend supports it, in loggingSource change the order of operations
// // Delete first followed by save/put.
- _.remove(loggingConfig.sinks[sinkIndex].filter.category, {
+ _remove(loggingConfig.sinks[sinkIndex].filter.category, {
name: catsev.name
});
} else {
sink.filter.category[catIndex] = catsev;
}
} else {
- _.remove(nulledCategories, (v) => v == catsev.name);
+ _remove(nulledCategories, (v) => v == catsev.name);
this.setState({
nulledCategories: nulledCategories
});
}
}
-export default alt.createStore(LoggingStore);
+export default alt.createStore(LoggingStore, 'LoggingStore');
* limitations under the License.
*
*/
-var Webpack = require('webpack');
+var webpack = require('webpack');
var path = require('path');
var nodeModulesPath = path.resolve(__dirname, 'node_modules');
var buildPath = path.resolve(__dirname, 'public', 'build');
var uiPluginCmakeBuild = process.env.ui_plugin_cmake_build || false;
var frameworkPath = uiPluginCmakeBuild?'../../../../skyquake/skyquake-build/framework':'../../framework';
var HtmlWebpackPlugin = require('html-webpack-plugin');
+var CompressionPlugin = require("compression-webpack-plugin");
// Added to overcome node-sass bug https://github.com/iam4x/isomorphic-flux-boilerplate/issues/62
process.env.UV_THREADPOOL_SIZE=64;
var config = {
},
plugins: [
new HtmlWebpackPlugin({
- filename: '../index.html'
- , templateContent: '<div id="app"></div>'
- }),
- new Webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js", Infinity)
+ filename: '../index.html',
+ templateContent: '<div id="app"></div>'
+ })
]
};
+
+if (process.argv.indexOf('--optimize-minimize') !== -1) {
+ // we are going to output a gzip file in the production process
+ config.output.filename = "gzip-" + config.output.filename;
+ config.plugins.push(new webpack.DefinePlugin({ // <-- key to reducing React's size
+ 'process.env': {
+ 'NODE_ENV': JSON.stringify('production')
+ }
+ }));
+ config.plugins.push(new CompressionPlugin({
+ asset: "[path]", // overwrite js file with gz file
+ algorithm: "gzip",
+ test: /\.(js)$/
+ }));
+}
module.exports = config;
npm install
echo 'Fetching third-party node_modules for '$f'...done'
echo 'Packaging '$f' using webpack'
- ./node_modules/.bin/webpack --progress --config webpack.production.config.js
+ ./node_modules/.bin/webpack --optimize-minimize --progress --config webpack.production.config.js
echo 'Packaging '$f' using webpack... done'
cd ..
echo 'Building plugin '$f'... done'
app.get('/multiplex-client', function(req, res) {
res.sendFile(__dirname + '/node_modules/websocket-multiplex/multiplex_client.js');
});
+
+ // handle requests for gzip'd files
+ app.get('*gzip*', function (req, res, next) {
+ res.set('Content-Encoding', 'gzip');
+ next();
+ });
+
}
/**
import {Panel, PanelWrapper} from '../../framework/widgets/panel/panel.jsx'
import '../../node_modules/open-iconic/font/css/open-iconic.css';
import 'style/base.scss';
-const Store = Alt.createStore(InstantiateStore)
+const Store = Alt.createStore(InstantiateStore, 'InstantiateStore');
// import StyleGuideItem from 'react-style-guide';
// import '../../node_modules/react-style-guide/node_modules/highlight.js/styles/github.css';
let SampleNSD = {
// import StyleGuideItem from 'react-style-guide';
// import '../../node_modules/react-style-guide/node_modules/highlight.js/styles/github.css';
-const Store = Alt.createStore(SshKeyStore)
+const Store = Alt.createStore(SshKeyStore, 'SshKeyStore');
storiesOf('CatalogCard', module)
// .add('page', () => (<SshKeys />))