From a552f04640459669cabdd5882b9e8964998b746e Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 16 Nov 2018 13:42:17 +0000 Subject: [PATCH 1/4] 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 --- .../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(-) 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 252f6da3f..adc3f4286 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; - }; + }; } } 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 c3a6cb7b9..4b5f626b3 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 @@
- +

- You must specify a name for this item + You must specify a name for this item or supply explicit bundle ID and blueprint symbolic name

@@ -60,7 +60,7 @@
catalog-bom- - +

The bundle ID can contains only letters, numbers as well a the following characters: ., - and _ @@ -69,7 +69,7 @@

- +

The blueprint symbolic name can contains only letters, numbers as well a the following characters: ., - and _

@@ -77,26 +77,34 @@
+
+ +
- +
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 d4204f891..bd0180fcf 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; } From abd5b435cd65b509fca801edf01142b6e6b9b97e Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 16 Nov 2018 14:37:57 +0000 Subject: [PATCH 2/4] use new icon url source --- ui-modules/blueprint-composer/app/views/main/main.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 bd0180fcf..87bf803fc 100644 --- a/ui-modules/blueprint-composer/app/views/main/main.controller.js +++ b/ui-modules/blueprint-composer/app/views/main/main.controller.js @@ -113,7 +113,7 @@ export function MainController($scope, $element, $log, $state, $stateParams, brB ? 'application' : 'entity', description: edit.type.description, - iconUrl: edit.type.iconUrl, + iconUrl: edit.type.iconUrlSource || edit.type.iconUrl, original: { bundle: edit.bundle.symbolicName.replace(/^catalog-bom-/, ''), symbolicName: edit.type.symbolicName, From 176ab914c1c2da475f8ca8ebfd87756a6d1e13a1 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Tue, 20 Nov 2018 10:04:08 +0000 Subject: [PATCH 3/4] address PR comments --- .../components/catalog-saver/catalog-saver.directive.js | 9 +++++---- .../catalog-saver/catalog-saver.modal.template.html | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) 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 adc3f4286..435d75813 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 @@ -44,7 +44,7 @@ const TYPES = [ angular.module(MODULE_NAME, [angularAnimate, uibModal, brUtils]) .directive('catalogSaver', ['$rootScope', '$uibModal', '$injector', '$filter', 'composerOverrides', 'blueprintService', saveToCatalogModalDirective]) .directive('catalogVersion', ['$parse', catalogVersionDirective]) - .directive('blueprintNameOrSymbolicNameAndBundleIdRequired', blueprintNameOrSymbolicNameAndBundleIdRequiredDirective) + .directive('composerBlueprintNameValidator', composerBlueprintNameValidatorDirective) .filter('bundlize', bundlizeProvider) .run(['$templateCache', templateCache]); @@ -210,7 +210,7 @@ export function CatalogItemModalController($scope, $filter, blueprintService, pa $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.$watchGroup(['config.name', 'config.itemType', 'config.bundle', 'config.symbolicName'], (newVals) => { $scope.updateDefaults(newVals[0]); $scope.form.name.$validate(); $scope.buttonText = $scope.buttonTextFn(); @@ -218,13 +218,14 @@ export function CatalogItemModalController($scope, $filter, blueprintService, pa }); } -function blueprintNameOrSymbolicNameAndBundleIdRequiredDirective() { +function composerBlueprintNameValidatorDirective() { return { restrict: 'A', require: 'ngModel', link: function(scope, element, attr, ngModel) { - ngModel.$validators.blueprintNameOrSymbolicNameAndBundleIdRequired = function(modelValue, viewValue) { + ngModel.$validators.composerBlueprintNameValidator = function(modelValue, viewValue) { scope.updateDefaults(modelValue); + console.log("valildating", scope.config.bundle, scope.config.symbolicName); if (!ngModel.$isEmpty(modelValue)) { // anything set is valid return true; 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 4b5f626b3..a0c9c2ff5 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,7 +26,7 @@
- +

You must specify a name for this item or supply explicit bundle ID and blueprint symbolic name

@@ -82,7 +82,7 @@ Application entity + uib-popover="Save as an application entity which can be deployed on its own, or configured and used in blueprints but only config and sensors declared at the root are accessible in the Composer ('application' item type)">
From 959ea3ac0e9e0a386b7dc427e21a7fe648d124b2 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Tue, 20 Nov 2018 10:17:18 +0000 Subject: [PATCH 4/4] more PR comments addressed --- .../components/catalog-saver/catalog-saver.modal.template.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a0c9c2ff5..1fc7de90b 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 @@ -90,7 +90,7 @@ Application template + uib-popover="Save as a blueprint template which can be used as an editable starting point for blueprints or used as an application entity, and in some contexts this prioritizes the blueprint for inclusion in quick-selection views ('template' item type)">