Repository: brooklyn-ui
Updated Branches:
  refs/heads/master 60ddc6213 -> a9fcd5276


catalog saver to support 'application', 'template', and 'entity'

in line with https://github.com/apache/brooklyn-server/pull/1015
also when using a template the metadata is not populated as the intention is 
probably not to overwrite,
unless the user selects to save it as a template


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/commit/a552f046
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/tree/a552f046
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/diff/a552f046

Branch: refs/heads/master
Commit: a552f04640459669cabdd5882b9e8964998b746e
Parents: 4093d1f
Author: Alex Heneveld <alex.henev...@cloudsoftcorp.com>
Authored: Fri Nov 16 13:42:17 2018 +0000
Committer: Alex Heneveld <alex.henev...@cloudsoftcorp.com>
Committed: Fri Nov 16 13:43:29 2018 +0000

----------------------------------------------------------------------
 .../catalog-saver/catalog-saver.directive.js    | 121 +++++++++++++------
 .../catalog-saver.modal.template.html           |  26 ++--
 .../app/views/main/main.controller.js           |  25 +++-
 3 files changed, 124 insertions(+), 48 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/a552f046/ui-modules/blueprint-composer/app/components/catalog-saver/catalog-saver.directive.js
----------------------------------------------------------------------
diff --git 
a/ui-modules/blueprint-composer/app/components/catalog-saver/catalog-saver.directive.js
 
b/ui-modules/blueprint-composer/app/components/catalog-saver/catalog-saver.directive.js
index 252f6da..adc3f42 100644
--- 
a/ui-modules/blueprint-composer/app/components/catalog-saver/catalog-saver.directive.js
+++ 
b/ui-modules/blueprint-composer/app/components/catalog-saver/catalog-saver.directive.js
@@ -42,14 +42,15 @@ const TYPES = [
 ];
 
 angular.module(MODULE_NAME, [angularAnimate, uibModal, brUtils])
-    .directive('catalogSaver', ['$rootScope', '$uibModal', '$injector', 
'composerOverrides', 'blueprintService', saveToCatalogModalDirective])
+    .directive('catalogSaver', ['$rootScope', '$uibModal', '$injector', 
'$filter', 'composerOverrides', 'blueprintService', 
saveToCatalogModalDirective])
     .directive('catalogVersion', ['$parse', catalogVersionDirective])
+    .directive('blueprintNameOrSymbolicNameAndBundleIdRequired', 
blueprintNameOrSymbolicNameAndBundleIdRequiredDirective)
     .filter('bundlize', bundlizeProvider)
     .run(['$templateCache', templateCache]);
 
 export default MODULE_NAME;
 
-export function saveToCatalogModalDirective($rootScope, $uibModal, $injector, 
composerOverrides, blueprintService) {
+export function saveToCatalogModalDirective($rootScope, $uibModal, $injector, 
$filter, composerOverrides, blueprintService) {
     return {
         restrict: 'E',
         templateUrl: function (tElement, tAttrs) {
@@ -62,38 +63,50 @@ export function saveToCatalogModalDirective($rootScope, 
$uibModal, $injector, co
     };
 
     function link($scope, $element) {
-        $scope.buttonText = $scope.config.label || ($scope.config.itemType ? 
`Update ${$scope.config.name || $scope.config.symbolicName}` : 'Add to 
catalog');
-
+        if (!$scope.config.original) {
+            // original if provided contains the original metadata, e.g. for 
use if coming from a template and switching between template and non-template
+            $scope.config.original = {}
+        }
+        $scope.isNewFromTemplate = () => ($scope.config.itemType !== 
'template' && $scope.config.original.itemType === 'template');
+        $scope.isUpdate = () => !$scope.isNewFromTemplate() && 
Object.keys($scope.config.original).length>0;
+        $scope.buttonTextFn = () => $scope.config.label || ($scope.isUpdate() 
&& ($scope.config.name || $scope.config.original.name || 
$scope.config.symbolicName || $scope.config.original.symbolicName)) || 'Add to 
catalog';
+        $scope.buttonText = $scope.buttonTextFn(); 
+        
         $scope.activateModal = () => {
             let entity = blueprintService.get();
             let metadata = blueprintService.entityHasMetadata(entity) ? 
blueprintService.getEntityMetadata(entity) : new Map();
-
-            // Reset the config values if this is not an update
-            $scope.isUpdate = Object.keys($scope.config).length > 
($scope.config.label ? 1 : 0);
-            if (!$scope.isUpdate) {
-                $scope.config.itemType = 'template';
-            }
-
-            // Set various properties from the blueprint entity data
-            if (!$scope.config.version && (entity.hasVersion() || 
metadata.has('version'))) {
-                $scope.config.version = entity.version || 
metadata.get('version');
+              
+            if (!$scope.config.itemType) {
+                // This is the default item type
+                $scope.config.itemType = 'application';
             }
+            
+            // Set various properties from the blueprint entity data if not 
already set
             if (!$scope.config.iconUrl && (entity.hasIcon() || 
metadata.has('iconUrl'))) {
                 $scope.config.iconUrl = entity.icon || metadata.get('iconUrl');
             }
-            if (!$scope.config.name && entity.hasName()) {
-                $scope.config.name = entity.name;
-            }
-            if (!$scope.config.symbolicName && (entity.hasId() || 
metadata.has('id'))) {
-                $scope.config.symbolicName = entity.id || metadata.get('id');
-            }
-            if (!$scope.config.bundle) {
-                // NB: when editing a bundle this will already be set
-                if ($scope.config.symbolicName) {
-                    $scope.config.bundle = $scope.config.symbolicName;
+            if (!$scope.isNewFromTemplate()) {
+                // (these should only be set if not making something new from 
a template, as the entity items will refer to the template)
+                
+                // the name and the ID can be set in the UI, 
+                // or all can be inherited if root node is a known application 
type we are editting 
+                // (normally in those cases $scope.config will already be set 
by caller, but maybe not always) 
+                if (!$scope.config.name && entity.hasName()) {
+                    $scope.config.name = entity.name;
+                }
+                if (!$scope.config.symbolicName && (entity.hasId() || 
metadata.has('id'))) {
+                    $scope.config.symbolicName = entity.id || 
metadata.get('id');
+                }
+                if (!$scope.config.version && (entity.hasVersion() || 
metadata.has('version'))) {
+                    $scope.config.version = entity.version || 
metadata.get('version');
+                }
+                if (!$scope.config.bundle) {
+                    if ($scope.config.symbolicName) {
+                        $scope.config.bundle = $scope.config.symbolicName;
+                    }
                 }
             }
-
+            
             // Override this callback to update configuration data elsewhere
             $scope.config = (composerOverrides.updateCatalogConfig || 
((config, $element) => config))($scope.config, $element);
 
@@ -103,7 +116,7 @@ export function saveToCatalogModalDirective($rootScope, 
$uibModal, $injector, co
                 controller: ['$scope', '$filter', 'blueprintService', 
'paletteApi', 'brUtilsGeneral', CatalogItemModalController],
                 scope: $scope,
             });
-
+            
             // Promise is resolved when the modal is closed. We expect the 
modal to pass back the action to perform thereafter
             modalInstance.result.then(reason => {
                 switch (reason) {
@@ -133,11 +146,12 @@ export function CatalogItemModalController($scope, 
$filter, blueprintService, pa
     $scope.getTitle = () => {
         switch ($scope.state.view) {
             case VIEWS.form:
-                return $scope.isUpdate ? `Update ${$scope.config.name || 
$scope.config.symbolicName || 'blueprint'}` : 'Add to catalog';
+                return $scope.isUpdate() ? `Update ${$scope.config.name || 
$scope.config.symbolicName || 'blueprint'}` : 'Add to catalog';
             case VIEWS.saved:
-                return `${$scope.config.name || $scope.config.symbolicName || 
'Blueprint'} ${$scope.isUpdate ? 'updated' : 'saved'}`;
+                return `${$scope.config.name || $scope.config.symbolicName || 
'Blueprint'} ${$scope.isUpdate() ? 'updated' : 'saved'}`;
         }
     };
+    $scope.title = $scope.getTitle();
 
     $scope.save = () => {
         $scope.state.saving = true;
@@ -160,10 +174,10 @@ export function CatalogItemModalController($scope, 
$filter, blueprintService, pa
     function createBom() {
         let blueprint = blueprintService.getAsJson();
 
-        let bundleBase = $scope.config.bundle || 
$filter('bundlize')($scope.config.name);
-        let bundleId = $scope.config.symbolicName || 
$filter('bundlize')($scope.config.name);
+        let bundleBase = $scope.config.bundle || $scope.defaultBundle;
+        let bundleId = $scope.config.symbolicName || 
$scope.defaultSymbolicName;
         if (!bundleBase || !bundleId) {
-            throw "Either display name must be set of bundle and symbolic name 
explicitly set";  
+            throw "Either the display name must be set, or the bundle and 
symbolic name must be explicitly set";
         }
         
         let bomItem = {
@@ -176,8 +190,9 @@ export function CatalogItemModalController($scope, $filter, 
blueprintService, pa
             version: $scope.config.version,
             items: [ bomItem ]
         };
-        if (brUtilsGeneral.isNonEmpty($scope.config.name)) {
-            bomItem.name = $scope.config.name;
+        let bundleName = $scope.config.name || $scope.defaultName;
+        if (brUtilsGeneral.isNonEmpty(bundleName)) {
+            bomItem.name = bundleName;
         }
         if (brUtilsGeneral.isNonEmpty($scope.config.description)) {
             bomItem.description = $scope.config.description;
@@ -188,6 +203,44 @@ export function CatalogItemModalController($scope, 
$filter, blueprintService, pa
         
         return jsYaml.dump({ 'brooklyn.catalog': bomCatalogYaml });
     }
+
+    let bundlize = $filter('bundlize');
+    $scope.updateDefaults = (newName) => {
+        $scope.defaultName = ($scope.config.itemType==='template' && 
$scope.config.original.name) || null;
+        $scope.defaultSymbolicName = ($scope.config.itemType==='template' && 
$scope.config.original.symbolicName) || bundlize(newName) || null;
+        $scope.defaultBundle = ($scope.config.itemType==='template' && 
$scope.config.original.bundle) || bundlize(newName) || null;
+    };
+    $scope.$watchGroup(['config.name', 'config.itemType'], (newVals) => {
+        $scope.updateDefaults(newVals[0]);
+        $scope.form.name.$validate();
+        $scope.buttonText = $scope.buttonTextFn();
+        $scope.title = $scope.getTitle();
+    });
+}
+
+function blueprintNameOrSymbolicNameAndBundleIdRequiredDirective() {
+    return {
+        restrict: 'A',
+        require: 'ngModel',
+        link: function(scope, element, attr, ngModel) {
+            ngModel.$validators.blueprintNameOrSymbolicNameAndBundleIdRequired 
= function(modelValue, viewValue) {
+                scope.updateDefaults(modelValue);
+                if (!ngModel.$isEmpty(modelValue)) {
+                    // anything set is valid
+                    return true;
+                }
+                // if not set, we need a bundle and symbolic name
+                if (scope.config.bundle && scope.config.symbolicName) {
+                    return true;
+                }
+                // or if we have defaults for bundle and symbolic name we 
don't need this name
+                if (scope.defaultBundle && scope.defaultSymbolicName) {
+                    return true;
+                }
+                return false;
+            }
+        },
+    };
 }
 
 export function catalogVersionDirective($parse) {
@@ -220,7 +273,7 @@ export function catalogVersionDirective($parse) {
 
         ctrl.$validators.exist = (modelValue, viewValue) => {
             return !angular.isDefined(matches) || ctrl.$isEmpty(viewValue) || 
viewValue.endsWith('SNAPSHOT') || force === true || matches.indexOf(viewValue) 
=== -1;
-        };
+        };        
     }
 }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/a552f046/ui-modules/blueprint-composer/app/components/catalog-saver/catalog-saver.modal.template.html
----------------------------------------------------------------------
diff --git 
a/ui-modules/blueprint-composer/app/components/catalog-saver/catalog-saver.modal.template.html
 
b/ui-modules/blueprint-composer/app/components/catalog-saver/catalog-saver.modal.template.html
index c3a6cb7..4b5f626 100644
--- 
a/ui-modules/blueprint-composer/app/components/catalog-saver/catalog-saver.modal.template.html
+++ 
b/ui-modules/blueprint-composer/app/components/catalog-saver/catalog-saver.modal.template.html
@@ -26,9 +26,9 @@
 
         <div class="form-group" ng-class="{'has-error': form.name.$invalid}">
             <label class="control-label">Blueprint display name</label>
-            <input ng-model="config.name" ng-disabled="state.saving" 
class="form-control" name="name" type="text" required />
+            <input ng-model="config.name" ng-disabled="state.saving" 
class="form-control" name="name" type="text" placeholder="{{ defaultName }}" 
blueprint-name-or-symbolic-name-and-bundle-id-required/>
             <p class="help-block" ng-show="form.name.$invalid">
-                <span ng-if="form.name.$error.required">You must specify a 
name for this item</span>
+                <span ng-if="form.name.$invalid">You must specify a name for 
this item or supply explicit bundle ID and blueprint symbolic name</span>
             </p>
         </div>
 
@@ -60,7 +60,7 @@
                 <label class="control-label">Bundle ID</label>
                 <div class="input-group">
                     <span class="input-group-addon">catalog-bom-</span>
-                    <input ng-model="config.bundle" ng-disabled="state.saving" 
class="form-control" name="bundle" ng-pattern="state.pattern" autofocus 
placeholder="{{ (config.name | bundlize) || 'E.g. my-bundle-id' }}"/>
+                    <input ng-model="config.bundle" ng-disabled="state.saving" 
class="form-control" name="bundle" ng-pattern="state.pattern" autofocus 
placeholder="{{ defaultBundle || 'E.g. my-bundle-id' }}"/>
                 </div>
                 <p class="help-block" ng-show="form.bundle.$invalid">
                     <span ng-if="form.bundle.$error.pattern">The bundle ID can 
contains only letters, numbers as well a the following characters: 
<code>.</code>, <code>-</code> and <code>_</code></span>
@@ -69,7 +69,7 @@
 
             <div class="form-group" ng-class="{'has-error': 
form.symbolicName.$invalid}">
                 <label class="control-label">Blueprint symbolic name</label>
-                <input ng-model="config.symbolicName" 
ng-disabled="state.saving" class="form-control" name="symbolicName" 
ng-pattern="state.pattern" autofocus placeholder="{{ (config.name | bundlize) 
|| 'E.g. my-catalog-id' }}"/>
+                <input ng-model="config.symbolicName" 
ng-disabled="state.saving" class="form-control" name="symbolicName" 
ng-pattern="state.pattern" autofocus placeholder="{{ defaultSymbolicName || 
'E.g. my-catalog-id' }}"/>
                 <p class="help-block" ng-show="form.symbolicName.$invalid">
                     <span ng-if="form.symbolicName.$error.pattern">The 
blueprint symbolic name can contains only letters, numbers as well a the 
following characters: <code>.</code>, <code>-</code> and <code>_</code></span>
                 </p>
@@ -79,24 +79,32 @@
                 <label class="control-label">Blueprint type</label>
                 <div class="checkbox">
                     <label>
+                        <input ng-model="config.itemType" 
ng-disabled="state.saving" name="itemType" type="radio" value="application" />
+                        Application entity
+                        <i class="fa fa-fw fa-info-circle" 
popover-trigger="'mouseenter'"
+                           uib-popover="Save as an application entity which 
can be configured and used in blueprints or used on its own ('application' item 
type)"></i>
+                    </label>
+                </div>
+                <div class="checkbox">
+                    <label>
                         <input ng-model="config.itemType" 
ng-disabled="state.saving" name="itemType" type="radio" value="template" />
-                        Template
+                        Application template
                         <i class="fa fa-fw fa-info-circle" 
popover-trigger="'mouseenter'"
-                           uib-popover="Save as 'template' which can be used 
as an editable starting point for other blueprints or quick-launched 
directly"></i>
+                           uib-popover="Save as a blueprint template which can 
be used as an editable starting point for blueprints or used as an application 
entity ('template' item type)"></i>
                     </label>
                 </div>
                 <div class="checkbox">
                     <label>
                         <input ng-model="config.itemType" 
ng-disabled="state.saving" name="itemType" type="radio" value="entity" />
-                        Entity
+                        Extended entity
                         <i class="fa fa-fw fa-info-circle" 
popover-trigger="'mouseenter'"
-                           uib-popover="Save as a new 'entity' type which can 
be used in blueprints"></i>
+                           uib-popover="Save as an entity which can be 
configured and used in blueprints, exposing the config and adjuncts it inherits 
('entity' item type)"></i>
                     </label>
                 </div>
             </div>
 
             <div class="form-group">
-                <label class="control-label">Blueprint icon url</label>
+                <label class="control-label">Blueprint icon URL</label>
                 <input ng-model="config.iconUrl" ng-disabled="state.saving" 
class="form-control" name="iconUrl" type="text" />
             </div>
         </br-collapsible>

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/a552f046/ui-modules/blueprint-composer/app/views/main/main.controller.js
----------------------------------------------------------------------
diff --git a/ui-modules/blueprint-composer/app/views/main/main.controller.js 
b/ui-modules/blueprint-composer/app/views/main/main.controller.js
index d4204f8..bd0180f 100644
--- a/ui-modules/blueprint-composer/app/views/main/main.controller.js
+++ b/ui-modules/blueprint-composer/app/views/main/main.controller.js
@@ -105,16 +105,31 @@ export function MainController($scope, $element, $log, 
$state, $stateParams, brB
 
     vm.saveToCatalogConfig = {};
     if (edit) {
+        console.log("edit", edit);
         vm.saveToCatalogConfig = Object.assign(vm.saveToCatalogConfig, {
-            bundle: edit.bundle.symbolicName.replace(/^catalog-bom-/, ''),
             version: edit.type.version,
-            symbolicName: edit.type.symbolicName,
-            itemType: edit.type.template ? 'template' : 'entity',
-            name: edit.type.displayName,
+            template: edit.type.template || false,
+            itemType: edit.type.template || !edit.type.superTypes || 
edit.type.superTypes.contains("org.apache.brooklyn.api.entity.Application") 
+                ? 'application'
+                : 'entity',
             description: edit.type.description,
             iconUrl: edit.type.iconUrl,
-            versions: edit.versions,
+            original: {
+                bundle: edit.bundle.symbolicName.replace(/^catalog-bom-/, ''),
+                symbolicName: edit.type.symbolicName,
+                name: edit.type.displayName,
+                versions: edit.versions,
+                itemType: edit.type.template ? 'template'  
+                    : !edit.type.superTypes || 
edit.type.superTypes.contains("org.apache.brooklyn.api.entity.Application") ? 
'application'
+                    : 'entity', 
+            }
         });
+        if (!edit.type.template) {
+            // if loading a templates, do NOT set name, bundle, symbolicName, 
versions
+            // or in other words, for other items, DO set these
+            vm.saveToCatalogConfig = Object.assign(vm.saveToCatalogConfig, 
vm.saveToCatalogConfig.original); 
+        }
+        
         yaml = edit.type.plan.data;
     }
 

Reply via email to