CONFD = XML_ONLY
BUILD_TYPE = Debug
-NOT_DEVELOPER_TYPE = FALSE
+NOT_DEVELOPER_BUILD ?= FALSE
COVERAGE_BUILD = FALSE
RIFT_AGENT_BUILD = $(CONFD)
PROJECT_TOP_DIR = $(TOP_ROOT_PATH)
mkdir -p $(RIFT_BUILD)
mkdir -p $(RIFT_ARTIFACTS)
mkdir -p $(RIFT_INSTALL)
- cd $(RIFT_BUILD) && $(RIFT_SHELL_EXE) cmake $(TOP_SRC_PATH) -DCMAKE_INSTALL_PREFIX=$(TOP_ROOT_PATH) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) -DNOT_DEVELOPER_BUILD=$(NOT_DEVELOPER_TYPE) -DCOVERAGE_BUILD=$(COVERAGE_TYPE) -DRIFT_AGENT_BUILD=$(RIFT_AGENT_BUILD) -DPROJECT_TOP_DIR=$(PROJECT_TOP_DIR) -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} -DRIFT_SUBMODULE_NAME=$(PROJECT_TOP_DIR) -DRIFT_PACKAGE_GENERATOR=DEB -DRELEASE_NUMBER=$(RELEASE_NUMBER) -DBUILD_NUMBER=$(BUILD_NUMBER)
+ cd $(RIFT_BUILD) && $(RIFT_SHELL_EXE) cmake $(TOP_SRC_PATH) -DCMAKE_INSTALL_PREFIX=$(TOP_ROOT_PATH) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) -DNOT_DEVELOPER_BUILD=$(NOT_DEVELOPER_BUILD) -DCOVERAGE_BUILD=$(COVERAGE_TYPE) -DRIFT_AGENT_BUILD=$(RIFT_AGENT_BUILD) -DPROJECT_TOP_DIR=$(PROJECT_TOP_DIR) -DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} -DRIFT_SUBMODULE_NAME=$(PROJECT_TOP_DIR) -DRIFT_PACKAGE_GENERATOR=DEB -DRELEASE_NUMBER=$(RELEASE_NUMBER) -DBUILD_NUMBER=$(BUILD_NUMBER)
rw: cmake
$(RIFT_SHELL_EXE) $(MAKE) -C $(RIFT_BUILD)
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)
// 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>
label: 'Management Network',
ref: 'mgmt-network'
}, {
- label: 'Floating IP Pool',
+ label: 'Floating IP Pool Network Name',
ref: 'floating-ip-pool',
optional: true
}, {
})
}
+function makeAssetTypeParamName (type) {
+ return type.toLowerCase() + '-file-type';
+}
FileManager.addFile = function(req) {
console.log(' Uploading file', req.file.originalname, 'as', req.file.filename);
var api_server = req.query['api_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) {
+ 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
+ 'package-path': package_path ? package_path + '/' + req.file.filename : req.file.filename
}
+ var assetType = req.query['asset_type'].toUpperCase();
+ input[makeAssetTypeParamName(package_type)] = assetType;
return new Promise(function(resolve, reject) {
Promise.all([
rp({
var id = req.query['package_id'];
var downloadUrl = req.query['url'];
var path = req.query['package_path'];
- var payload = {
- "input": {
- "package-type": type,
- "package-id": id
- }
+ var assetType = req.query['asset_type'];
+ var input = {
+ "package-type": type,
+ "package-id": id
}
+ var payload = {input: input};
if(req.method == 'GET') {
if(downloadUrl && path) {
payload.input['external-url'] = downloadUrl;
payload.input['package-path'] = path;
+ payload.input[makeAssetTypeParamName(type)] = assetType;
return download(payload);
} else {
return list(payload);
}
if(req.method == 'DELETE') {
payload.input['package-path'] = path;
+ payload.input[makeAssetTypeParamName(type)] = assetType;
return deleteFile(payload)
}
import _isArray from 'lodash/isArray'
import _cloneDeep from 'lodash/cloneDeep'
import _debounce from 'lodash/debounce';
+import _uniqueId from 'lodash/uniqueId';
+import _set from 'lodash/set';
+import _get from 'lodash/get';
+import _has from 'lodash/has';
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 resolveReactKey(value) {
+ const keyPath = ['uiState', 'fieldKey'];
+ if (!_has(value, keyPath)) {
+ _set(value, keyPath, _uniqueId());
+ }
+ return _get(value, keyPath);
+}
+
function getDescriptorMetaBasicForType(type) {
const basicPropertiesFilter = d => _includes(DESCRIPTOR_MODEL_FIELDS[type], d.name);
return DescriptorModelMetaFactory.getModelMetaForType(type, basicPropertiesFilter) || {properties: []};
key={fieldKey}
id={fieldKey}
className={ClassNames({'-value-not-set': !isValueSet})}
- defaultValue={val && val.toUpperCase()} title={pathToProperty}
+ defaultValue={val && val.toUpperCase()}
+ title={pathToProperty}
+ onChange={onSelectChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
+ readOnly={!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}
}
- function buildElement(container, property, valuePath, value) {
- return property.properties.map((property, index) => {
- let childValue;
- const childPath = valuePath.slice();
- if (typeof value === 'object') {
- childValue = value[property.name];
+ /**
+ * buiid and return an array of components representing an editor for each property.
+ *
+ * @param {any} container the master document being edited
+ * @param {[property]} properties
+ * @param {string} pathToProperties path within the container to the properties
+ * @param {Object} data source for each property
+ * @param {any} props object containing main data panel information, e.g. panel width {width: 375}
+ * which may be useful/necessary to a components rendering.
+ * @returns an array of react components
+ */
+ function buildComponentsForProperties(container, properties, pathToProperties, data, props) {
+ return properties.map((property) => {
+ let value;
+ let propertyPath = pathToProperties.slice();
+ if (data && typeof data === 'object') {
+ value = data[property.name];
}
if(property.type != 'choice'){
- childPath.push(property.name);
+ propertyPath.push(property.name);
}
- return build(container, property, childPath, childValue);
-
+ return build(container, property, propertyPath, value, props);
});
}
+ function buildElement(container, property, valuePath, value) {
+ return buildComponentsForProperties(container, property.properties, valuePath, value);
+ }
+
function buildChoice(container, property, path, value, key) {
function processChoiceChange(name, value) {
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])) {
+ if(c.optionValue && fieldProperties.hasOwnProperty(c.optionValue.split('.')[1])) {
utils.assignPathValue(container.model, ['uiState.choice', pathToChoice, 'selected'].join('.'), c.optionValue);
}
});
selectedOptionValue = utils.resolvePath(container.model, ['uiState.choice', pathToChoice, 'selected'].join('.'));
} else {
property.properties.map(function(p) {
- let pname = p.properties[0].name;
+ let pname = p.properties[0] && p.properties[0].name;
if(container.model.hasOwnProperty(pname)) {
utils.assignPathValue(container.model, ['uiState.choice', pathToChoice, 'selected'].join('.'), [p.name, pname].join('.'));
}
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 && 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);
+ let valueResponse = null;
+ if (valueProperty.properties && valueProperty.properties.length) {
+ valueResponse = valueProperty.properties.map(valuePropertyFn);
+ } else if (!isMissingDescriptorMeta) {
+ let value = utils.resolvePath(container.model, path.concat(valueProperty.name).join('.')) || container.model[valueProperty.name];
+ valueResponse = build(container, valueProperty, path.concat(valueProperty.name), value)
+ } else {
+ valueResponse = 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('.'));
if (isArray) {
valuePath.push(index);
- fieldId += index;
+ fieldId = isLeafList ? fieldId + index + value : resolveReactKey(value);
}
if (isMetaField) {
value = utils.resolvePath(container.model, ['uiState.choice'].concat(path, 'selected').join('.'));
if(!value) {
property.properties.map(function(p) {
- let pname = p.properties[0].name;
+ let pname = p.properties[0] && p.properties[0].name;
if(container.model.hasOwnProperty(pname)) {
value = container.model[pname];
}
<div className="basic-properties-group">
<h2>Basic</h2>
<div>
- {basicProperties.map(property => {
- const path = [property.name];
- const value = container.model[property.name];
- return build(container, property, path, value);
- })}
+ {buildComponentsForProperties(container, basicProperties, [], container.model)}
</div>
</div>
);
<a className="toggle-show-less" href="#show-more-properties">less…</a>
</h1>
<div className="toggleable">
- {properties.map(property => {
- const path = [property.name];
- const value = container.model[property.name];
- return build(container, property, path, value, {toggle: true, width: props.width});
- })}
+ {buildComponentsForProperties(container, properties, [], container.model, {toggle: true, width: props.width})}
</div>
<div className="toggle-bottom-spacer" style={{visibility: 'hidden', 'height': '50%', position: 'absolute'}}>We need this so when the user closes the panel it won't shift away and scare the bj out of them!</div>
</div>
//https://raw.githubusercontent.com/RIFTIO/RIFT.ware/master/rift-shell
import _cloneDeep from 'lodash/cloneDeep'
import _findIndex from 'lodash/findIndex'
+import _uniqueId from 'lodash/uniqueId'
import React from 'react';
import ReactDOM from 'react-dom';
import TreeView from 'react-treeview';
import './FileMananger.scss';
import FileManagerActions from './FileManagerActions.js';
import imgSave from '../../../../node_modules/open-iconic/svg/data-transfer-upload.svg'
-import {Panel, PanelWrapper} from 'widgets/panel/panel';
+import { Panel, PanelWrapper } from 'widgets/panel/panel';
import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx';
import LoadingIndicator from 'widgets/loading-indicator/loadingIndicator.jsx';
import FileManagerUploadDropZone from '../../libraries/FileManagerUploadDropZone';
let API_SERVER = require('utils/rw.js').getSearchParams(window.location).api_server;
-const createDropZone = function (action, clickable, type, id, path, dropTarget) {
- const dropZone = new FileManagerUploadDropZone(ReactDOM.findDOMNode(dropTarget), [clickable], action, type, id, path);
+const ASSET_TYPE = {
+ 'nsd': [
+ { id: 'ICONS', folder: 'icons', title: "Icons", allowFolders: false },
+ { id: 'SCRIPTS', folder: 'scripts', title: "scripts", allowFolders: true },
+ { id: 'NS_CONFIG', folder: 'ns_config', title: "NS Config", allowFolders: false },
+ { id: 'VNF_CONFIG', folder: 'vnf_config', title: "VNF Config", allowFolders: false }
+ ],
+ 'vnfd': [
+ { id: 'ICONS', folder: 'icons', title: "Icons", allowFolders: false },
+ { id: 'CHARMS', folder: 'charms', title: "charms", allowFolders: true },
+ { id: 'SCRIPTS', folder: 'scripts', title: "scripts", allowFolders: true },
+ { id: 'IMAGES', folder: 'images', title: "images", allowFolders: false },
+ { id: 'CLOUD_INIT', folder: 'cloud_init', title: "cloud_init", allowFolders: false },
+ { id: 'README', folder: '.', title: ".", allowFolders: false }
+ ]
+}
+
+const createDropZone = function (action, clickable, getUploadPropsCallback, dropTarget) {
+ const dropZone = new FileManagerUploadDropZone(ReactDOM.findDOMNode(dropTarget), [clickable], action, getUploadPropsCallback);
// dropZone.on('dragover', this.onDragOver);
// dropZone.on('dragend', this.onDragEnd);
// dropZone.on('addedfile', this.onFileAdded);
return dropZone;
};
-//updateFileLocationInput
+
+function normalizeAssets(packageType, assetInfo, filesStatus) {
+ let assets = {};
+ let assetTypes = ASSET_TYPE[packageType];
+ assetTypes.forEach(assetGroup => {
+ const typeFolder = assetGroup.folder;
+ let folders = assetInfo.id.filter(name => name.startsWith(typeFolder));
+ if (folders.length) {
+ folders.reverse();
+ assets[assetGroup.id] = folders.map(fullName => {
+ let path = fullName.slice(typeFolder.length + 1);
+ let files = assetInfo.data[fullName].map(info => ({
+ name: info.name.startsWith(fullName) ? info.name.slice(fullName.length + 1) : info.name,
+ status: filesStatus[info.name]
+ }));
+ return { path, files };
+ });
+ }
+ });
+ return assets;
+}
+
+function sendDeleteFileRequest(assetType, path, name) {
+ path = path ? path + '/' + name : name;
+ FileManagerActions.deletePackageFile({ assetType, path });
+}
+
class FileManager extends React.Component {
constructor(props) {
super(props)
+ let assests = props.files;
}
componentWillMount() {
// FileManagerActions.openFileManagerSockets()
}
generateFolder(data, nesting) {
let nestingLevel = nesting || 1;
+ }
+ render() {
+ let { files, filesState, type, item, actions } = this.props;
+ let assets = normalizeAssets(type, files, filesState);
+ let children = [];
+ let assetTypes = ASSET_TYPE[type];
+ assetTypes.forEach(assetGroup => {
+ const typeFolder = assetGroup.folder;
+ let folders = assets[assetGroup.id];
+ let rootAssets = { path: '', files: [] };
+ let subFolders = null;
+ if (folders && folders.length) {
+ rootAssets = folders[0];
+ subFolders = folders.slice(1);
+ }
+ children.push(
+ <AssetGroup
+ key={typeFolder}
+ packageId={item.id}
+ packageType={type}
+ title={assetGroup.title}
+ assetGroup={assetGroup}
+ path={rootAssets.path}
+ files={rootAssets.files}
+ allowsFolders={assetGroup.allowFolders}
+ folders={subFolders}
+ showNotification={actions.showNotification}
+ />
+ )
+ }, this);
+ let html = (
+ <div className="FileManager">
+ <PanelWrapper style={{ flexDirection: 'column' }}>
+ {children}
+ </PanelWrapper>
+ </div>
+ )
+ return html;
}
- deleteFile(name) {
- return function(e) {
- FileManagerActions.deletePackageFile(name);
- }
+}
+
+function NewHierachy(props) {
+ return (
+ <Panel className="addFileSection" style={{ backgroundColor: 'transparent' }} no-corners>
+ <div className="inputSection">
+ <TextInput placeholder="some/path" label="create a folder hierarchy" onChange={FileManagerActions.newPathNameUpdated} />
+ <Button label="Create" onClick={e => FileManagerActions.createDirectory(props.assetGroup.id)} />
+ </div>
+ </Panel>
+ );
+}
+
+class AssetGroup extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { downloadPath: "" };
}
- updateFileLocationInput(name) {
- return function(e) {
- FileManagerActions.updateFileLocationInput({
- name: name,
- value: e.target.value
+
+ render() {
+ let { title, packageType, packageId, assetGroup, files, allowsFolders, folders, path, inputState, showNotification } = this.props;
+ let children = [];
+ if (folders && folders.length) {
+ folders.map(function (folder, i) {
+ children.push(
+ <AssetGroup
+ key={folder.path}
+ packageId={packageId}
+ title={folder.path}
+ packageType={packageType}
+ files={folder.files}
+ assetGroup={assetGroup}
+ path={folder.path}
+ showNotification={showNotification}
+ />
+ )
});
}
- }
- sendDownloadFileRequst = (url, path) => {
- let self = this;
- return function(e) {
- if(!url || url == "") {
- return self.props.actions.showNotification.defer({type: 'error', msg: 'Value missing in download request'});;
+ let folderCreateComponent = allowsFolders ? <NewHierachy assetGroup={assetGroup} /> : null;
+ let entries = null
+
+ function uploadFileFromUrl(url) {
+ if (!url || url == "") {
+ return;
}
- let files = self.props.files.data;
- let folder = path.split('/');
let splitUrl = url.split('/');
let fileName = splitUrl[splitUrl.length - 1];
- folder.pop;
- let fullPath = _cloneDeep(folder);
- fullPath.push(fileName);
- fullPath = fullPath.join('/');
- folder = folder.join('/');
- let fileIndex = _findIndex(files[folder], function(f) {
- return f.name == fullPath;
- })
- if (fileIndex == -1) {
- FileManagerActions.sendDownloadFileRequst({
- url: url,
- path: path
- });
+ if (files.findIndex(f => f.name === fileName) > -1) {
+ showNotification('It seems you\'re attempting to upload a file with a duplicate file name');
} else {
- self.props.actions.showNotification('It seems you\'re attempting to upload a file with a duplicate file name');
+ FileManagerActions.sendDownloadFileRequest({ url, assetType: assetGroup.id, path: path });
}
}
- }
- render() {
- let self = this;
- let html = (
- <div className="FileManager">
- <PanelWrapper style={{flexDirection: 'column'}}>
- <Panel className="addFileSection" style={{backgroundColor: 'transparent'}} no-corners>
- <div className="inputSection">
- <TextInput placeholder="some/path" value={this.props.newPathName} label="create a new directory" onChange={FileManagerActions.newPathNameUpdated} />
- <Button label="Create" onClick={FileManagerActions.createDirectory} />
+
+ return (
+ <Panel title={title} itemClassName="nested" no-corners>
+ {folderCreateComponent}
+ <div className="folder">
+ <FileAssetList files={files} path={path} packageId={packageId} packageType={packageType} assetGroup={assetGroup} />
+ <Panel className="addFileSection" no-corners>
+ <ItemUpload packageType={packageType} packageId={packageId} path={path} assetGroup={assetGroup} />
+ <div style={{ marginLeft: '0.5rem' }}>
+ OR
</div>
- </Panel>
- {self.props.files && self.props.files.id && buildList(self, self.props.files) }
- </PanelWrapper>
- </div>
- )
- return html;
+ <div className="inputSection">
+ <TextInput placeholder="URL" className="" label="External URL" value={this.state.downloadPath} onChange={e => this.setState({ downloadPath: e.target.value })} />
+ <Button className='ComposerAppSave' label="DOWNLOAD" onClick={e => uploadFileFromUrl(this.state.downloadPath)} />
+ </div>
+ </Panel>
+ <div>
+ {children}
+ </div>
+ </div>
+ </Panel>
+ );
}
-
}
-function buildList(self, data) {
- let toReturn = [];
- data.id.map(function(k,i) {
- toReturn.push (contentFolder(self, data.data[k], k, k+i, self.props.filesState, self.updateFileLocationInput, self.sendDownloadFileRequst, self.deleteFile));
- });
- return toReturn.reverse();
+function FileAssetList(props) {
+ let { packageType, packageId, assetGroup, files, path } = props;
+ let children = null;
+ if (files) {
+ children = files.map(function (file, i) {
+ if (!file.hasOwnProperty('contents')) {
+ return <FileAsset key={file.name} file={file} path={path} id={packageId} type={packageType} assetGroup={assetGroup} />
+ }
+ })
+ }
+ return (
+ <div className='file-list'>
+ {children}
+ </div>
+ );
+
}
-function contentFolder(context, folder, path, key, inputState, updateFn, sendDownloadFileRequst, deleteFn) {
- let type = context.props.type;
- let id = context.props.item.id;
- let classId = `DZ-${path.replace(/\/|\s+/g, '-')}`;
- const onboardDropZone = createDropZone.bind(this, FileManagerUploadDropZone.ACTIONS.onboard, '.ComposerAppAddFile.' + classId, type, id, path);
+function FileAsset(props) {
+ let { file, path, type, assetGroup, id } = props;
+ const name = file.name;
+ const downloadHost = API_SERVER.match('localhost') || API_SERVER.match('127.0.0.1') ? `${window.location.protocol}//${window.location.hostname}` : API_SERVER;
+ //{`${window.location.protocol}//${API_SERVER}:4567/api/package${type}/${id}/${path}/${name}`}
return (
- <Panel title={path} key={key} itemClassName="nested" no-corners>
- <div className="folder">
- {
- folder.map(function(f, i) {
- if( !f.hasOwnProperty('contents') ){
- return contentFile(context, f, path, i, deleteFn);
- }
- })
- }
- <Panel className="addFileSection" no-corners>
- <ItemUpload type={type} id={id} path={path} key={key} dropZone={onboardDropZone} />
- <div style={{marginLeft: '0.5rem'}}>
- OR
+ <div className="file">
+ <div className="file-section">
+ <div className="file-info">
+ <div className="file-status"
+ style={{ display: (file.status && file.status.toLowerCase() != 'completed') ? 'inherit' : 'none', color: (file.status == 'FAILED' ? 'red' : 'inherit') }}>
+ {file.status && (file.status == 'IN_PROGRESS' || file.status == 'DOWNLOADING') ? <LoadingIndicator size={2} /> : file.status}
+ </div>
+ <div className="file-name">
+ <a target="_blank" href={`${downloadHost}:4567/api/package/${type}/${id}/${assetGroup.folder}${path}/${name}`}>{name}</a>
+ </div>
</div>
- <div className="inputSection">
- <TextInput placeholder="URL" className="" label="External URL" value={inputState[path]} onChange={updateFn(path)} />
- <Button className='ComposerAppSave' label="DOWNLOAD" onClick={sendDownloadFileRequst(inputState[path], path)}/>
+ <div className="file-action"
+ style={{ display: (!file.status || (file && file.status.toLowerCase() != 'loading...')) ? 'inherit' : 'none', cursor: 'pointer' }}
+ onClick={e => sendDeleteFileRequest(assetGroup.id, path, name)}>
+ X
</div>
- </Panel>
-
</div>
- </Panel>
- );
+ </div>
+ )
}
+
class ItemUpload extends React.Component {
constructor(props) {
super(props);
+ this.state = { dropzoneIdClass: 'DZ-' + _uniqueId() };
}
componentDidMount() {
- if (this.props.dropZone) {
- const dropTarget = this;
- const dropZone = this.props.dropZone(dropTarget);
- }
+ createDropZone(
+ FileManagerUploadDropZone.ACTIONS.onboard,
+ '.ComposerAppAddFile.' + this.state.dropzoneIdClass,
+ () => {
+ let theCode = 'crap';
+ return ({
+ packageType: this.props.packageType,
+ packageId: this.props.packageId,
+ assetGroup: this.props.assetGroup,
+ path: this.props.path
+ })},
+ this);
}
+
render() {
- let {type, id, path, key, ...props} = this.props;
- let classId = `DZ-${path.replace(/\/|\s+/g, '-')}`;
+ let { dropzoneIdClass } = this.props;
return (
<div className="inputSection">
- <label className="sqTextInput" style={{flexDirection: 'row', alignItems:'center'}}>
+ <label className="sqTextInput" style={{ flexDirection: 'row', alignItems: 'center' }}>
<span>Upload File</span>
- <Button className={'ComposerAppAddFile ' + classId} label="BROWSE"/>
+ <Button className={'ComposerAppAddFile ' + this.state.dropzoneIdClass} label="BROWSE" />
</label>
</div>
)
}
}
-function contentFile(context, file, path, key, deleteFn) {
- const name = stripPath(file.name, path);
- const id = context.props.item.id;
- const type = context.props.type;
- const downloadHost = API_SERVER.match('localhost') || API_SERVER.match('127.0.0.1') ? `${window.location.protocol}//${window.location.hostname}` : API_SERVER;
- //{`${window.location.protocol}//${API_SERVER}:4567/api/package${type}/${id}/${path}/${name}`}
- return (
- <div className="file" key={key}>
- <div className="file-section">
- <div className="file-info">
- <div className="file-status" style={{display: (file.status && file.status.toLowerCase() != 'completed') ? 'inherit' : 'none', color: (file.status == 'FAILED' ? 'red' : 'inherit')}}>
- {file.status && (file.status == 'IN_PROGRESS' || file.status == 'DOWNLOADING' ) ? <LoadingIndicator size={2} /> : file.status }
- </div>
- <div className="file-name">
- <a target="_blank" href={`${downloadHost}:4567/api/package/${type}/${id}/${path}/${name}`}>{name}</a>
- </div>
- </div>
- <div className="file-action" style={{display: (!file.status || (file && file.status.toLowerCase() != 'loading...')) ? 'inherit' : 'none', cursor: 'pointer'}} onClick={deleteFn(file.name)}>X</div>
- </div>
- </div>
- )
-}
function stripPath(name, path, returnPath) {
let stripSlash = (name.indexOf('/') > -1) ? '/' : '';
class FileManagerActions {
constructor() {
- this.generateActions('getFilelistSuccess', 'getFilelistError', 'updateFileLocationInput','sendDownloadFileRequst', 'addFileSuccess', 'addFileError','deletePackageFile','deleteFileSuccess','deleteFileError','openDownloadMonitoringSocketSuccess', 'openDownloadMonitoringSocketError',
- 'getFilelistSocketSuccess',
- 'openFileManagerSockets', 'closeFileManagerSockets','newPathNameUpdated', 'createDirectory');
+ this.generateActions(
+ 'getFilelistSuccess',
+ 'getFilelistError',
+ 'updateFileLocationInput',
+ 'sendDownloadFileRequest',
+ 'addFileSuccess',
+ 'addFileError',
+ 'deletePackageFile',
+ 'deleteFileSuccess',
+ 'deleteFileError',
+ 'openDownloadMonitoringSocketSuccess',
+ 'openDownloadMonitoringSocketError',
+ 'getFilelistSocketSuccess',
+ 'openFileManagerSockets',
+ 'closeFileManagerSockets',
+ 'newPathNameUpdated',
+ 'createDirectory'
+ );
}
}
-export default alt.createActions(FileManagerActions);
+export default alt.createActions(FileManagerActions);
\ No newline at end of file
},
addFile: function() {
return {
- remote: function(state, id, type, path, url, refresh) {
+ remote: function(state, id, type, assetType, path, url, refresh) {
return new Promise(function(resolve, reject) {
console.log('Adding file');
console.log(id, type, path, url);
let splitUrl = url.split('/');
let fileName = splitUrl[splitUrl.length -1];
- let packagePath = refresh ? path + ((path[path.length - 1] == '/') ? '' : '/') : path + '/' + fileName;
+ let packagePath = refresh ? path + ((path[path.length - 1] == '/') ? '' : '/') : (path ? path + '/' + fileName : fileName);
+ let assetFolder = assetType.toLowerCase();
$.ajax({
- beforeSend: Utils.addAuthorizationStub,
- url: 'api/file-manager?api_server=' + utils.getSearchParams(window.location).api_server +'&package_type=' + type + '&package_id=' + id + '&package_path=' + packagePath + '&url=' + url,
+ beforeSend: (xhr) => {
+ Utils.addAuthorizationStub(xhr);
+ // lets get the buzy graphic rolling
+ FileManagerActions.addFileSuccess.defer({
+ path: assetFolder + (path ? '/' + path: ''),
+ fileName: fileName,
+ refresh: refresh
+ });
+ },
+ url: 'api/file-manager?api_server=' + utils.getSearchParams(window.location).api_server +'&package_type=' + type + '&package_id=' + id + '&package_path=' + packagePath + '&asset_type=' + assetType + '&url=' + url,
success: function(data) {
resolve({
- data:data,
- path: path,
+ data: data,
+ path: assetFolder + (path ? '/' + path: ''),
fileName: fileName,
refresh: refresh
});
},
deleteFile: function() {
return {
- remote: function(state, id, type, path) {
+ remote: function(state, id, type, assetType, path) {
+ let assetFolder = assetType.toLowerCase();
return new Promise(function(resolve, reject) {
$.ajax({
method: 'DELETE',
beforeSend: Utils.addAuthorizationStub,
- url: 'api/file-manager?api_server=' + utils.getSearchParams(window.location).api_server +'&package_type=' + type + '&package_id=' + id + '&package_path=' + path ,
+ url: 'api/file-manager?api_server=' + utils.getSearchParams(window.location).api_server +'&package_type=' + type + '&package_id=' + id + '&asset_type=' + assetType + '&package_path=' + path ,
success: function(data) {
if (data.output.status == 'True') {
- resolve({
- data: data,
- path: path
- });
+ resolve({data, assetFolder, path});
} else {
- reject({
- data: data,
- path: path
- })
+ reject({data, assetFolder, path})
}
},
error: function(error) {
if (typeof error == 'string') {
error = JSON.parse(error);
}
- reject({
- path: path,
- data: error
- });
+ reject({data, assetFolder, path});
}
}).fail(function(xhr){
//Authentication and the handling of fail states should be wrapped up into a connection class.
return window.location.origin;
}
-function initializeDropZone(element = '#dropzone', button = false, action = ACTIONS.onboard, type, id, path) {
+function initializeDropZone(element = '#dropzone', button = false, action = ACTIONS.onboard, getUploadProps) {
let Auth = 'Basic ' + window.sessionStorage.getItem("auth");
let dev_download_server = Utils.getSearchParams(window.location).dev_download_server;
DropZone.autoDiscover = false;
return new DropZone(element, {
paramName: 'package',
url() {
+ let {packageType, packageId, assetGroup, path} = getUploadProps();
if (action === ACTIONS.update) {
return getCatalogPackageManagerServerOrigin() + '/api/update';
}
- return getCatalogPackageManagerServerOrigin() + '/composer/api/file-manager?api_server=' + Utils.getSearchParams(window.location).api_server + '&package_type=' + type + '&package_id=' + id + '&package_path=' + path + ( dev_download_server ? '&dev_download_server=' + dev_download_server : '');
+ let url = getCatalogPackageManagerServerOrigin()
+ + '/composer/api/file-manager?api_server='
+ + Utils.getSearchParams(window.location).api_server
+ + '&package_type=' + packageType
+ + '&package_id=' + packageId
+ + '&package_path=' + path
+ + '&asset_type=' + assetGroup.id
+ + ( dev_download_server ? '&dev_download_server=' + dev_download_server : '');
+ return url;
},
headers: {
'Authorization': Auth
previewTemplate: '',
sending(file, xhr, formData) {
// NOTE ie11 does not get this form data
+ let {packageType, packageId, assetGroup, path} = getUploadProps();
formData.append('id', file.id);
FileManagerActions.addFileSuccess({
- fileName: file.name,
- path: path
- })
+ path: assetGroup.folder + (path ? '/' + path: ''),
+ fileName: file.name
+ });
},
error(file, errorMessage) {
const response = {
});
}
-export default class CatalogPackageManagerUploadDropZone {
+export default class FileManagerUploadDropZone {
- constructor(element, button, action, type, id, path) {
- this.dropZone = initializeDropZone(element, button, action, type, id, path);
+ constructor(element, button, action, getUploadProps) {
+ this.dropZone = initializeDropZone(element, button, action, getUploadProps);
}
static get ACTIONS() {
* This class provides methods to get the metadata about descriptor models.
*/
-'use strict';
-
import _cloneDeep from 'lodash/cloneDeep'
-import utils from './../utils'
+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) {
+ data = data.reduce((result, value) => {
+ if (value !== '' && value !== null && value !== undefined && typeof value !== 'object') {
+ result.push(value);
+ }
+ return result;
+ }, []);
+ if (data.length){
+ let obj = {};
+ obj[this.name] = data;
+ 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;
})
})
},
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']);
uiState.properties = uiState.properties.filter(filterProperties);
},
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);
}
})
* 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') {
+ 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;
}
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);
+ keyValue = keyValue + '-' + (i + 1);
makeUniqueName(); // not worried about recursing too deep (chances ??)
break;
}
return keyValue;
}
-}
+}
\ No newline at end of file
* This class provides utility methods for interrogating an instance of model uiState object.
*/
-'use strict';
-
import _includes from 'lodash/includes'
import _isArray from 'lodash/isArray'
import guid from './../guid'
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);
},
-
/*
*
* Copyright 2016 RIFT.IO Inc
* Created by onvelocity on 10/20/15.
*/
-import _isNumber from 'lodash/isNumber'
-import _cloneDeep from 'lodash/cloneDeep'
-import _isEmpty from 'lodash/isEmpty'
-import _omit from 'lodash/omit'
-import _pick from 'lodash/pick'
-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
showDescriptor: ComposerAppActions.showDescriptor,
getFilelistSuccess: FileManagerActions.getFilelistSuccess,
updateFileLocationInput: FileManagerActions.updateFileLocationInput,
- sendDownloadFileRequst: FileManagerActions.sendDownloadFileRequst,
+ sendDownloadFileRequest: FileManagerActions.sendDownloadFileRequest,
addFileSuccess: FileManagerActions.addFileSuccess,
deletePackageFile: FileManagerActions.deletePackageFile,
deleteFileSuccess: FileManagerActions.deleteFileSuccess,
if (self.fileMonitoringSocketID) {
let newState = {};
if(data.hasOwnProperty('contents')) {
- filesState = addInputState( _cloneDeep(this.filesState),data);
+ filesState = updateFileState( _cloneDeep(this.filesState),data);
let normalizedData = normalizeTree(data);
newState = {
files: {
id:[],
data:{}
};
- data.contents.map(getContents);
function getContents(d) {
if(d.hasOwnProperty('contents')) {
let contents = [];
f.data[d.name] = contents;
}
}
+ getContents(data);
return f;
}
- function addInputState(obj, d) {
+ function updateFileState(obj, d) {
d.newFile = '';
if(d.hasOwnProperty('contents')) {
- d.contents.map(addInputState.bind(null, obj))
- }
- if(!obj[d.name]) {
- obj[d.name] = '';
+ d.contents.map(updateFileState.bind(null, obj))
}
+ // override any "pending" state we may have initialized
+ obj[d.name] = '';
return obj;
}
}
- sendDownloadFileRequst(data) {
+ sendDownloadFileRequest(data) {
let id = data.id || this.item.id;
let type = data.type || this.item.uiState.type;
+ let assetType = data.assetType;
let path = data.path;
let url = data.url;
- this.getInstance().addFile(id, type, path, url, data.refresh);
+ this.getInstance().addFile(id, type, assetType, path, url, data.refresh);
}
updateFileLocationInput = (data) => {
let name = data.name;
addFileSuccess = (data) => {
if(!data.refresh) {
let path = data.path;
+ if (path.startsWith('readme')) {
+ // this asset type stuff should be in a more common location
+ // this is a wee bit of a hack till it is
+ path = '.' + path.slice(6);
+ }
let fileName = data.fileName;
let files = _cloneDeep(this.files);
- let loadingIndex = files.data[path].push({
- status: 'DOWNLOADING',
- name: path + '/' + fileName
- }) - 1;
- this.setState({files: files});
+ let assetGroup = files.data[path] || [];
+ if (fileName) {
+ let name = path + '/' + fileName;
+ if (assetGroup.findIndex(f => f.name === name) == -1){
+ assetGroup.push({name});
+ }
+ }
+ files.data[path] = assetGroup;
+ if (files.id.indexOf(path) == -1){
+ files.id.push(path);
+ }
+ let filesState = _cloneDeep(this.filesState);
+ filesState[name] = "DOWNLOADING";
+ this.setState({files, filesState});
}
}
}
this.setState({
+ filesState: [],
+ files: {
+ id:[],
+ data:{}
+ },
fileMonitoringSocketID: id,
fileMonitoringSocket: ws
})
endWatchingJob(id) {
}
- deletePackageFile(name) {
+ deletePackageFile(asset) {
+ let {assetType, path} = asset;
let id = this.item.id;
let type = this.item.uiState.type;
- this.getInstance().deleteFile(id, type, name);
+ this.getInstance().deleteFile(id, type, assetType, path);
}
deleteFileSuccess = (data) => {
- let path = data.path.split('/')
+ let name = null;
+ let path = null;
+ if (data.assetFolder === 'readme'){
+ // freak'n root folder is special
+ name = data.path;
+ path = ['.'];
+ } else {
+ name = data.assetFolder + '/' + data.path;
+ path = name.split('/');
+ path.pop();
+ }
let files = _cloneDeep(this.files);
- path.pop();
- path = path.join('/');
- let pathFiles = files.data[path]
- _remove(pathFiles, function(c) {
- return c.name == data.path;
+ let filesForPath = files.data[path.join('/')]
+ _remove(filesForPath, function(c) {
+ return c.name == name;
});
this.setState({
newPathName: value
})
}
- createDirectory = () => {
+ createDirectory = (assetType) => {
console.log(this.newPathName);
- this.sendDownloadFileRequst({
+ this.sendDownloadFileRequest({
id: this.item.id,
type: this.item.uiState.type,
- path: this.item.name + '/' + this.newPathName,
+ assetType: assetType,
+ path: this.newPathName,
url: utils.getSearchParams(window.location).dev_download_server || window.location.protocol + '//' + window.location.host,
refresh: true
});
}
inputParametersHTML = (props) => {
let inputParameters = props.inputParameters;
+ const handleChange = (i, event) => props.updateInputParam(i, event.target.value);
return inputParameters && inputParameters.map(function(input, i) {
return (
<div className="configure-nsd_section" key={i}>
<h3 className="launchpadCard_title">Input Parameters</h3>
<div className="inputControls">
- <TextInput label={ input.label || input.xpath } type="text" onChange={props.updateInputParam.bind(self, i)} />
+ <TextInput label={ input.label || input.xpath } type="text" onChange={handleChange.bind(this, i)} />
</div>
</div>
)
});
return window.location.hash = 'launchpad/' + tokenizedHash[2];
}
- launchNSRError(error) {
+ launchNSRError(data) {
var msg = 'Something went wrong while trying to instantiate. Check the error logs for more information';
- if(error) {
- msg = error;
+ if (data.error) {
+ msg = data.error;
}
Alt.actions.global.showNotification.defer(msg);
Alt.actions.global.hideScreenLoader.defer();
(<li key="nsr"><h3>NSR: {this.props.nsr['cloud-account']}</h3></li>)
)
}
- this.props.nsr['vnfrs'].map(function(v,i) {
+ this.props.nsr['vnfrs'] && this.props.nsr['vnfrs'].map(function(v,i) {
if(v.hasOwnProperty('cloud-account')) {
status.push(
(<li key={i}><h3>VNFR {v['short-name']}: {v['cloud-account']}</h3></li>)
*/
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 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
);
}
}) : trows.push(
- <tr key={sgrInstanceIndex}>
+ <tr key={sgri}>
<td colSpan="5" style={{textAlign: 'center'}}>No network services scaled in this group</td>
</tr>
);
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);
consoleUrlsTabTitle = 'VDU Console Links';
tabList.push(
- <Tab key={cardData.id + '-cp'}>{consoleUrlsTabTitle}</Tab>
+ <Tab key={cardData.id + '-vducl'}>{consoleUrlsTabTitle}</Tab>
);
tabPanels.push(
- <TabPanel key={cardData.id + '-cp-panel'}>
+ <TabPanel key={cardData.id + '-vducl-panel'}>
<div className="consoleUrls">
{consoleUrlsComponent}
</div>