Merge branch 'master' into projects
authorKIRAN KASHALKAR <kiran.kashalkar@riftio.com>
Fri, 5 May 2017 15:37:55 +0000 (11:37 -0400)
committerKIRAN KASHALKAR <kiran.kashalkar@riftio.com>
Fri, 5 May 2017 15:37:55 +0000 (11:37 -0400)
1  2 
skyquake/plugins/composer/api/composer.js
skyquake/plugins/composer/src/src/components/EditDescriptorModelProperties.js
skyquake/plugins/composer/src/src/components/filemanager/FileManagerSource.js
skyquake/plugins/launchpad/src/recordViewer/recordCard.jsx

@@@ -35,34 -35,33 +35,34 @@@ var DataCenters = {}
  Composer.get = function(req) {
      var api_server = req.query['api_server'];
      var results = {}
 +    var projectPrefix = req.session.projectId ? "project-" : "";
      return new Promise(function(resolve, reject) {
          Promise.all([
              rp({
 -                uri: utils.confdPort(api_server) + APIVersion + '/api/config/nsd-catalog/nsd?deep',
 +                uri: utils.projectContextUrl(req, utils.confdPort(api_server) + APIVersion + '/api/config/nsd-catalog/nsd?deep'),
                  method: 'GET',
                  headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
 -                    'Authorization': req.get('Authorization')
 +                    'Authorization': req.session && req.session.authorization
                  }),
                  forever: constants.FOREVER_ON,
                  rejectUnauthorized: false,
                  resolveWithFullResponse: true
              }),
              rp({
 -                uri: utils.confdPort(api_server) + APIVersion + '/api/config/vnfd-catalog/vnfd?deep',
 +                uri: utils.projectContextUrl(req, utils.confdPort(api_server) + APIVersion + '/api/config/vnfd-catalog/vnfd?deep'),
                  method: 'GET',
                  headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
 -                    'Authorization': req.get('Authorization')
 +                    'Authorization': req.session && req.session.authorization
                  }),
                  forever: constants.FOREVER_ON,
                  rejectUnauthorized: false,
                  resolveWithFullResponse: true
              }),
              rp({
 -                uri: utils.confdPort(api_server) + APIVersion + '/api/operational/ns-instance-opdata?deep',
 +                uri: utils.projectContextUrl(req, utils.confdPort(api_server) + APIVersion + '/api/operational/ns-instance-opdata?deep'),
                  method: 'GET',
                  headers: _.extend({}, constants.HTTP_HEADERS.accept.data, {
 -                    'Authorization': req.get('Authorization')
 +                    'Authorization': req.session && req.session.authorization
                  }),
                  forever: constants.FOREVER_ON,
                  rejectUnauthorized: false,
@@@ -75,7 -74,7 +75,7 @@@
              //   headers: _.extend({},
              //     constants.HTTP_HEADERS.accept.collection,
              //     {
 -            //       'Authorization': req.get('Authorization')
 +            //       'Authorization': req.session && req.session.authorization
              //     }),
              //   forever: constants.FOREVER_ON,
              // rejectUnauthorized: false,
                  "descriptors": []
              }];
              if (result[0].body) {
 -                response[0].descriptors = JSON.parse(result[0].body).collection['nsd:nsd'];
 +                response[0].descriptors = JSON.parse(result[0].body).collection[projectPrefix + 'nsd:nsd'];
                  if (result[2].body) {
                      var data = JSON.parse(result[2].body);
                      if (data && data["nsr:ns-instance-opdata"] && data["nsr:ns-instance-opdata"]["rw-nsr:nsd-ref-count"]) {
                  }
              };
              if (result[1].body) {
 -                response[1].descriptors = JSON.parse(result[1].body).collection['vnfd:vnfd'];
 +                response[1].descriptors = JSON.parse(result[1].body).collection[projectPrefix + 'vnfd:vnfd'];
              };
              // if (result[2].body) {
 -            //   response[2].descriptors = JSON.parse(result[2].body).collection['pnfd:pnfd'];
 +            //   response[2].descriptors = JSON.parse(result[2].body).collection[projectPrefix + 'pnfd:pnfd'];
              // };
              resolve({
                  statusCode: response.statusCode || 200,
@@@ -172,10 -171,10 +172,10 @@@ Composer.delete = function(req) 
      console.log('Deleting', catalogType, id, 'from', api_server);
      return new Promise(function(resolve, reject) {
          request({
 -            uri: utils.confdPort(api_server) + APIVersion + '/api/config/' + catalogType + '-catalog/' + catalogType + '/' + id,
 +            uri: utils.projectContextUrl(req, utils.confdPort(api_server) + APIVersion + '/api/config/' + catalogType + '-catalog/' + catalogType + '/' + id),
              method: 'DELETE',
              headers: _.extend({}, constants.HTTP_HEADERS.accept.data, {
 -                'Authorization': req.get('Authorization')
 +                'Authorization': req.session && req.session.authorization
              }),
              forever: constants.FOREVER_ON,
              rejectUnauthorized: false,
  Composer.getVNFD = function(req) {
      var api_server = req.query['api_server'];
      var vnfdID = req.body.data;
 -    var authorization = req.get('Authorization');
 +    var authorization = req.session && req.session.authorization;
      var VNFDs = [];
      if (typeof(vnfdID) == "object" && vnfdID.constructor.name == "Array") {
          vnfdID.map(function(id) {
          return new Promise(function(resolve, reject) {
              var url = utils.confdPort(api_server) + APIVersion + '/api/config/vnfd-catalog/vnfd' + (id ? '/' + id : '') + '?deep';
              request({
 -                uri: url,
 +                uri: utils.projectContextUrl(req, url),
                  method: 'GET',
                  headers: _.extend({}, constants.HTTP_HEADERS.accept.data, {
                      'Authorization': authorization
@@@ -256,10 -255,10 +256,10 @@@ Composer.create = function(req) 
      return new Promise(function(resolve, reject) {
          var requestHeaders = {};
          _.extend(requestHeaders, constants.HTTP_HEADERS.accept.data, constants.HTTP_HEADERS.content_type.data, {
 -            'Authorization': req.get('Authorization')
 +            'Authorization': req.session && req.session.authorization
          });
          request({
 -            uri: utils.confdPort(api_server) + '/api/config/' + catalogType + '-catalog',
 +            uri: utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/config/' + catalogType + '-catalog'),
              method: 'POST',
              headers: requestHeaders,
              forever: constants.FOREVER_ON,
@@@ -286,10 -285,10 +286,10 @@@ Composer.updateSave = function(req) 
      return new Promise(function(resolve, reject) {
          var requestHeaders = {};
          _.extend(requestHeaders, constants.HTTP_HEADERS.accept.data, constants.HTTP_HEADERS.content_type.data, {
 -            'Authorization': req.get('Authorization')
 +            'Authorization': req.session && req.session.authorization
          });
          request({
 -            uri: utils.confdPort(api_server) + APIVersion + '/api/config/' + catalogType + '-catalog' + '/' + catalogType + '/' + id,
 +            uri: utils.projectContextUrl(req, utils.confdPort(api_server) + APIVersion + '/api/config/' + catalogType + '-catalog' + '/' + catalogType + '/' + id),
              method: 'PUT',
              headers: requestHeaders,
              forever: constants.FOREVER_ON,
@@@ -317,30 -316,24 +317,30 @@@ PackageManager.upload = function(req) 
          download_host = req.protocol + '://' + req.get('host');//req.api_server + ':' + utils.getPortForProtocol(req.protocol);
      }
  
 +    var input = {
 +        '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-create');
 +
 +    input = utils.addProjectContextToRPCPayload(req, uri, input);
 +
      return new Promise(function(resolve, reject) {
          Promise.all([
              rp({
 -                uri: utils.confdPort(api_server) + '/api/operations/package-create',
 +                uri: uri,
                  method: 'POST',
                  headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
 -                    'Authorization': req.get('Authorization')
 +                    'Authorization': req.session && req.session.authorization
                  }),
                  forever: constants.FOREVER_ON,
                  rejectUnauthorized: false,
                  resolveWithFullResponse: true,
                  json: true,
                  body: {
 -                    input: {
 -                        'external-url': download_host + '/composer/upload/' + req.file.filename,
 -                        'package-type': 'VNFD',
 -                        'package-id': uuid()
 -                    }
 +                    input: input
                  }
              })
          ]).then(function(result) {
              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'], false);
  
              // Return status to composer UI to update the status.
              resolve({
@@@ -382,19 -375,14 +382,19 @@@ PackageManager.update = function(req) 
          '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-update');
 +
 +    input = utils.addProjectContextToRPCPayload(req, uri, input);
 +
      return new Promise(function(resolve, reject) {
          Promise.all([
              rp({
 -                uri: utils.confdPort(api_server) + '/api/operations/package-update',
 +                uri: uri,
                  method: 'POST',
                  headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
 -                    'Authorization': req.get('Authorization')
 +                    'Authorization': req.session && req.session.authorization
                  }),
                  forever: constants.FOREVER_ON,
                  rejectUnauthorized: false,
  PackageManager.export = function(req) {
      // /api/operations/package-export
      var api_server = req.query['api_server'];
 +    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: utils.confdPort(api_server) + '/api/operations/package-export',
                  method: 'POST',
                  headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
 -                    'Authorization': req.get('Authorization')
 +                    'Authorization': req.session && req.session.authorization
                  }),
                  forever: constants.FOREVER_ON,
                  rejectUnauthorized: false,
                  resolveWithFullResponse: true,
                  json: true,
 -                body: { "input": req.body}
 +                body: { "input": input }
              })
          ]).then(function(result) {
              var data = {};
  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-copy');
 +    var input = req.body;
 +    input = utils.addProjectContextToRPCPayload(req, uri, input);
 +
      return new Promise(function(resolve, reject) {
          Promise.all([
              rp({
 -                uri: utils.confdPort(api_server) + '/api/operations/package-copy',
 +                uri: uri,
                  method: 'POST',
                  headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
 -                    'Authorization': req.get('Authorization')
 +                    'Authorization': req.session && req.session.authorization
                  }),
                  forever: constants.FOREVER_ON,
                  rejectUnauthorized: false,
                  resolveWithFullResponse: true,
                  json: true,
 -                body: { "input": req.body}
 +                body: { "input": input}
              })
          ]).then(function(result) {
              var data = {};
  }
  
  /**
 - * This methods retrieves the status of package operations. It takes an optional 
 + * 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.
   */
@@@ -514,18 -495,18 +514,18 @@@ PackageManager.getJobStatus = function(
      var api_server = req.query["api_server"];
      var uri = utils.confdPort(api_server);
      var id = req.params['id'];
 -    var url = uri + '/api/operational/copy-jobs' + (id ? '/job/' + 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')
 +                'Authorization': req.session && req.session.authorization
              }),
              forever: constants.FOREVER_ON,
              rejectUnauthorized: false
          }, function(error, response, body) {
 -            if (utils.validateResponse('restconfAPI.streams', error, response, body, resolve, reject)) {
 +            if (utils.validateResponse('PackageManager.getJobStatus', error, response, body, resolve, reject)) {
                  var returnData;
                  if (id) {
                      returnData = JSON.parse(response.body)['rw-pkg-mgmt:job'];
      })
  }
  
+ 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 uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/package-file-add');
 +
 +    input = utils.addProjectContextToRPCPayload(req, uri, input);
 +
+     var assetType = req.query['asset_type'].toUpperCase();
+     input[makeAssetTypeParamName(package_type)] = assetType;
      return new Promise(function(resolve, reject) {
          Promise.all([
              rp({
 -                uri: utils.confdPort(api_server) + '/api/operations/package-file-add',
 +                uri: uri,
                  method: 'POST',
                  headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
 -                    'Authorization': req.get('Authorization')
 +                    'Authorization': req.session && req.session.authorization
                  }),
                  forever: constants.FOREVER_ON,
                  rejectUnauthorized: false,
@@@ -605,16 -585,17 +609,17 @@@ FileManager.get = function(req) 
      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)
      }
  
      function deleteFile(payload) {
 +        var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/rw-pkg-mgmt:package-file-delete');
 +        payload.input = utils.addProjectContextToRPCPayload(req, uri, payload.input);
          return new Promise(function(resolve, reject) {
              rp({
 -                uri: utils.confdPort(api_server) + '/api/operations/rw-pkg-mgmt:package-file-delete',
 +                uri: uri,
                  method: 'POST',
                  headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
 -                    'Authorization': req.get('Authorization')
 +                    'Authorization': req.session && req.session.authorization
                  }),
                  json: payload,
                  forever: constants.FOREVER_ON,
          })
      }
      function download(payload) {
 +        var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/rw-pkg-mgmt:package-file-add');
 +        payload.input = utils.addProjectContextToRPCPayload(req, uri, payload.input);
          return new Promise(function(resolve, reject) {
              rp({
 -                uri: utils.confdPort(api_server) + '/api/operations/rw-pkg-mgmt:package-file-add',
 +                uri: uri,
                  method: 'POST',
                  headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
 -                    'Authorization': req.get('Authorization')
 +                    'Authorization': req.session && req.session.authorization
                  }),
                  json: payload,
                  forever: constants.FOREVER_ON,
          })
      }
      function list(payload) {
 +        var uri = utils.projectContextUrl(req, utils.confdPort(api_server) + '/api/operations/get-package-endpoint');
 +        payload.input = utils.addProjectContextToRPCPayload(req, uri, payload.input);
          return new Promise(function(resolve, reject) {
              rp({
 -                uri: utils.confdPort(api_server) + '/api/operations/get-package-endpoint',
 +                uri: uri,
                  method: 'POST',
                  headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
 -                    'Authorization': req.get('Authorization')
 +                    'Authorization': req.session && req.session.authorization
                  }),
                  json: payload,
                  forever: constants.FOREVER_ON,
                      }
                      parsedEndpoint = URL.parse(endpoint);
                      rp({
 -                        uri: api_server + ':' + parsedEndpoint.port + parsedEndpoint.path,
 +                        uri: utils.projectContextUrl(req, api_server + ':' + parsedEndpoint.port + parsedEndpoint.path),
                          method: 'GET',
                          headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
 -                            'Authorization': req.get('Authorization')
 +                            'Authorization': req.session && req.session.authorization
                          }),
                          forever: constants.FOREVER_ON,
                          rejectUnauthorized: false,
@@@ -730,10 -706,10 +736,10 @@@ FileManager.job = function(req) 
      var id = req.params['id'];
      return new Promise(function(resolve, reject) {
          request({
 -            url: uri + url + '?deep',
 +            url: utils.projectContextUrl(req, uri + url + '?deep'),
              method: 'GET',
              headers: _.extend({}, constants.HTTP_HEADERS.accept.data, {
 -                'Authorization': req.get('Authorization')
 +                'Authorization': req.session && req.session.authorization
              }),
              forever: constants.FOREVER_ON,
              rejectUnauthorized: false,
@@@ -25,6 -25,10 +25,10 @@@ import _includes from 'lodash/includes
  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'
@@@ -50,6 -54,14 +54,14 @@@ import '../styles/EditDescriptorModelPr
  
  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: []};
@@@ -78,8 -90,6 +90,8 @@@ function getTitle(model = {}) 
  export default function EditDescriptorModelProperties(props) {
  
        const container = props.container;
 +      const readonly = props.readonly;
 +      const isEditable = !readonly; //true
  
        if (!(DescriptorModelFactory.isContainer(container))) {
                return
                        }
                        CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
                }
 +              if(readonly) {
 +                      return null;
 +              }
                return (
                                <Button className="inline-hint" onClick={onClickAddProperty.bind(container, property, path)} label="Add" src={imgAdd} />
                );
                        }
                        CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
                }
 +              if(readonly) {
 +                      return null;
 +              }
                return (
                        <Button className="remove-property-action inline-hint" title="Remove" onClick={onClickRemoveProperty.bind(container, property, path)} label="Remove" src={imgRemove}/>
                );
                let catalogs = cds.getTransientCatalogs();
  
                const pathToProperty = path.join('.');
 -              const isEditable = true;
                const isGuid = Property.isGuid(property);
                const isBoolean = Property.isBoolean(property);
                const isEnumeration = Property.isEnumeration(property);
                        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) => {
                                options.unshift(<option key={'(value-not-in-enum)'} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
                        }
                        return (
 -                              <select 
 -                                      key={fieldKey} 
 +                              <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} 
 -                                      readOnly={!isEditable}>
 +                                      className={ClassNames({'-value-not-set': !isValueSet})}
 +                                      defaultValue={value}
 +                                      title={pathToProperty}
 +                                      onChange={onSelectChange}
 +                                      onFocus={onFocus}
 +                                      onBlur={endEditing}
 +                                      onMouseDown={startEditing}
 +                                      onMouseOver={startEditing}
 +                                      disabled={!isEditable}>
                                                {options}
                                </select>
                        );
                                options.unshift(<option key={'(value-not-in-leafref)'} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
                        }
                        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} 
 -                                      readOnly={!isEditable}>
 +                              <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>
                        );
                        }
                        const isValueSet = (val != '' && val)
                        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} 
 -                                      readOnly={!isEditable}>
 +                              <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 isEmptyLeafPresent = (value === EMPTY_LEAF_PRESENT || value === null);
                        let present = isEmptyLeafPresent ? EMPTY_LEAF_PRESENT : "";
                        const options = [
                                <option key={'true'} value={EMPTY_LEAF_PRESENT}>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} 
 -                                      readOnly={!isEditable}>
 +                              <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} 
 -                                      cols="5" 
 -                                      id={fieldKey} 
 -                                      defaultValue={value} 
 -                                      placeholder={placeholder} 
 -                                      onChange={onTextChange} 
 -                                      onFocus={onFocus} 
 -                                      onBlur={endEditing} 
 -                                      onMouseDown={startEditing} 
 -                                      onMouseOver={startEditing} 
 -                                      onMouseOut={endEditing} 
 -                                      onMouseLeave={endEditing} 
 -                                      readOnly={!isEditable} />
 +                              <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 
 +                      <input
                                key={fieldKey}
                                id={fieldKey}
                                type="text"
                                onMouseOver={startEditing}
                                onMouseOut={endEditing}
                                onMouseLeave={endEditing}
 -                              readOnly={!isEditable}
 +                              disabled={!isEditable}
                        />
                );
  
        }
  
-       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) {
                                // write the current choice value into the state
                                let choiceObject = utils.resolvePath(this.model, [name, selected].join('.'));
                                let isTopCase = false;
 -                              if (!choiceObject) {
 +                              if (choiceObject) {
                                        isTopCase = true;
                                        choiceObject = utils.resolvePath(this.model, [selected].join('.'));
                                }
                                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('.'));
  
                return (
                        <div key={key} className="choice">
 -                              <select 
 -                                      key={Date.now()} 
 -                                      className={ClassNames({'-value-not-set': !selectedOptionValue})} 
 -                                      defaultValue={selectedOptionValue} 
 -                                      onChange={onChange} 
 -                                      onFocus={onFocus} 
 -                                      onBlur={endEditing} 
 -                                      onMouseDown={startEditing} 
 -                                      onMouseOver={startEditing} 
 -                                      onMouseOut={endEditing} 
 +                              <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>
                        let field;
                        const valuePath = path.slice();
                        // create a unique field Id for use as react component keys and html element ids
 -                      // notes: 
 +                      // 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);
-                               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&hellip;</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>
                </div>
        );
  };
 -
@@@ -57,20 -57,29 +57,29 @@@ const FileManagerSource = 
      },
      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 new Promise(function(resolve, reject) {
                      //api/operational/download-jobs/job/
                     $.ajax({
 -                    url: '/socket-polling?api_server=' + API_SERVER,
 +                    url: '/socket-polling',
                      type: 'POST',
                      beforeSend: Utils.addAuthorizationStub,
                      data: {
                  return new Promise(function(resolve, reject) {
                      //api/operational/download-jobs/job/
                     $.ajax({
 -                    url: '/socket-polling?api_server=' + API_SERVER,
 +                    url: '/socket-polling',
                      type: 'POST',
                      beforeSend: Utils.addAuthorizationStub,
                      data: {
@@@ -36,10 -36,6 +36,10 @@@ import _forEach from 'lodash/forEach'
  import Prism from 'prismjs';
  import 'prismjs/themes/prism.css';
  
 +import {SkyquakeRBAC, isRBACValid} from 'widgets/skyquake_rbac/skyquakeRBAC.jsx';
 +import ROLES from 'utils/roleConstants.js';
 +
 +const PROJECT_ROLES = ROLES.PROJECT;
  
  export default class RecordCard extends React.Component {
    constructor(props) {
@@@ -81,8 -77,6 +81,8 @@@
  
      let notice = null;
  
 +    let hasAccess = isRBACValid(this.context.userProfile, [PROJECT_ROLES.LCM_ADMIN]);
 +
      switch(this.props.type) {
        case 'vnfr' :
          cardData = this.props.data[0];
@@@ -92,7 -86,7 +92,7 @@@
          if (displayConfigPrimitives) {
            configPrimitiveComponent = (
              <div className="flex vnfrConfigPrimitiveContainer">
 -              <VnfrConfigPrimitives data={configPrimitivesProps} />
 +              <VnfrConfigPrimitives data={configPrimitivesProps} hasAccess={hasAccess} />
              {/* <NsrPrimitiveJobList jobs={cardData['config-agent-job']}/> */}
              <div style={{display:'flex', flexDirection: 'column',     flex: '1 1 40%'}}>
                  <div className="launchpadCard_title">
          if (displayConfigPrimitives) {
            configPrimitiveComponent = (
              <div className="flex nsConfigPrimitiveContainer">
 -              <NsrConfigPrimitives data={configPrimitivesProps} />
 +              <NsrConfigPrimitives data={configPrimitivesProps} hasAccess={hasAccess}  />
                <div style={{display:'flex', flexDirection: 'column',     flex: '1 1 40%'}}>
                  <div className="launchpadCard_title">
                    JOB LIST
            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>
@@@ -425,7 -419,3 +425,7 @@@ RecordCard.defaultProps = 
    isLoading: true,
    jobData: []
  }
 +RecordCard.contextTypes = {
 +    router: React.PropTypes.object,
 +    userProfile: React.PropTypes.object
 +};