| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| import {HIDE_INTERSTITIAL_SPINNER_EVENT} from 'brooklyn-ui-utils/interstitial-spinner/interstitial-spinner'; |
| import template from './main.template.html'; |
| import {EntityFamily} from '../../components/util/model/entity.model'; |
| import {graphicalState} from './graphical/graphical.state'; |
| import {yamlState} from './yaml/yaml.state'; |
| import {graphicalEditEntityState} from './graphical/edit/entity/edit.entity.controller'; |
| import {graphicalEditPolicyState} from './graphical/edit/policy/edit.policy.controller'; |
| import {graphicalEditEnricherState} from './graphical/edit/enricher/edit.enricher.controller'; |
| import {graphicalEditSpecState} from './graphical/edit/spec/edit.spec.controller'; |
| import bottomSheetTemplate from './bottom-sheet.template.html'; |
| import {ISSUE_LEVEL} from '../../components/util/model/issue.model'; |
| |
| const layers = [ |
| { |
| id: 'locations', |
| label: 'Locations', |
| selector: '.node-location', |
| active: true |
| }, |
| { |
| id: 'adjuncts', |
| label: 'Adjuncts', |
| selector: '.node-adjuncts', |
| active: true |
| }, |
| { |
| id: 'memberspecs', |
| label: 'Member specs', |
| selector: '.spec-node-group', |
| active: true |
| }, |
| { |
| id: 'relationships', |
| label: 'Relationships', |
| selector: '.relation-group', |
| active: true |
| } |
| ]; |
| const layerCacheKey = 'blueprint-composer.layers'; |
| |
| export function MainController($scope, $element, $log, $state, $stateParams, brBrandInfo, blueprintService, actionService, catalogApi, applicationApi, brSnackbar, brBottomSheet, edit, yaml, composerOverrides) { |
| $scope.$emit(HIDE_INTERSTITIAL_SPINNER_EVENT); |
| let vm = this; |
| |
| if (edit && yaml) { |
| throw 'Cannot supply both YAML source and a catalog item to edit'; |
| } |
| |
| vm.mode = $state.current; |
| $scope.$on('$stateChangeSuccess', (event, toState)=>{ |
| vm.mode = toState; |
| }); |
| |
| vm.layers = localStorage && localStorage.getItem(layerCacheKey) !== null ? |
| JSON.parse(localStorage.getItem(layerCacheKey)) : |
| layers; |
| $scope.$watch('vm.layers', ()=> { |
| vm.layers.forEach(layer => { |
| document.querySelectorAll(layer.selector).forEach(node => { |
| angular.element(node).css('display', layer.active ? 'block' : 'none'); |
| }); |
| }); |
| if (localStorage) { |
| try { |
| localStorage.setItem(layerCacheKey, JSON.stringify(vm.layers)); |
| } catch (ex) { |
| $log.error('Cannot save layers preferences: ' + ex.message); |
| } |
| } |
| }, true); |
| |
| vm.parseError = false; |
| $scope.$on('yaml.lint', (event, error)=>{ |
| $scope.$applyAsync(()=> { |
| vm.parseError = error; |
| }); |
| }); |
| |
| $scope.$on('blueprint.reset', () => { |
| vm.saveToCatalogConfig = {}; |
| blueprintService.reset(); |
| $state.go($state.includes(yamlState) ? yamlState : graphicalState, {}, {inherit: false, reload: true}); |
| }); |
| $scope.$on('blueprint.deploy', () => { |
| vm.deployApplication(); |
| }); |
| |
| vm.saveToCatalogConfig = {}; |
| if (edit) { |
| vm.saveToCatalogConfig = Object.assign(vm.saveToCatalogConfig, { |
| version: edit.type.version, |
| 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.iconUrlSource || edit.type.iconUrl, |
| 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; |
| } |
| |
| vm.isGraphicalMode = () => { |
| return $state.includes(graphicalState.name); |
| }; |
| vm.isYamlMode = () => { |
| return $state.includes(yamlState.name); |
| }; |
| |
| if (yaml) { |
| if (vm.isYamlMode()) { |
| // don't set blueprint; yaml mode will take from "initial yaml" |
| blueprintService.reset(); |
| $scope.initialYaml = yaml; |
| } else { |
| try { |
| blueprintService.setFromYaml(yaml); |
| } catch (e) { |
| console.warn("YAML supplied for editing is not valid for a blueprint. It will be ignored unless opened in the YAML editor:", e); |
| blueprintService.reset(); |
| $scope.initialYaml = yaml; |
| } |
| } |
| } else { |
| blueprintService.reset(); |
| } |
| |
| vm.deployApplication = function () { |
| if (blueprintService.hasIssues()) { |
| vm.displayIssues(); |
| return; |
| } |
| |
| deployApplication(); |
| }; |
| |
| vm.getAllActions = () => { |
| return actionService.getActions(); |
| } |
| |
| vm.displayIssues = () => { |
| let isOnlyWarnings = blueprintService.getIssues().length === blueprintService.getIssues().filter(issue => issue.level === ISSUE_LEVEL.WARN).length; |
| let message = ''; |
| let options = {}; |
| if (isOnlyWarnings) { |
| message = 'Blueprint has some warnings, deployment might fail. Do you wish to proceed anyway?'; |
| options = { |
| label: 'Deploy', |
| callback: deployApplication |
| } |
| } else { |
| message = 'Blueprint is not valid: there ' + ( |
| blueprintService.getIssues().filter(issue => issue.level === ISSUE_LEVEL.ERROR).length > 1 ? |
| 'are ' + blueprintService.getIssues().filter(issue => issue.level === ISSUE_LEVEL.ERROR).length + ' problems' : |
| 'is 1 problem' |
| ) + ' to fix'; |
| } |
| |
| brSnackbar.create(message, options); |
| |
| let firstEntityWithIssue = blueprintService.getIssues().filter(issue => { |
| if (isOnlyWarnings) { |
| return issue.level === ISSUE_LEVEL.WARN; |
| } else { |
| return issue.level === ISSUE_LEVEL.ERROR; |
| } |
| })[0].entity; |
| switch (firstEntityWithIssue.family) { |
| case EntityFamily.ENTITY: |
| $state.go(graphicalEditEntityState, {entityId: firstEntityWithIssue._id}); |
| return; |
| case EntityFamily.POLICY: |
| $state.go(graphicalEditPolicyState, {entityId: firstEntityWithIssue.parent._id, policyId: firstEntityWithIssue._id}); |
| return; |
| case EntityFamily.ENRICHER: |
| $state.go(graphicalEditEnricherState, {entityId: firstEntityWithIssue.parent._id, enricherId: firstEntityWithIssue._id}); |
| return; |
| case EntityFamily.SPEC: |
| $state.go(graphicalEditSpecState, {entityId: firstEntityWithIssue.parent._id, specId: firstEntityWithIssue._id}); |
| return; |
| } |
| } |
| |
| function deployApplication() { |
| let bottomSheetOpts = { |
| controller: ['$log', 'brBrandInfo', 'brBottomSheetInstance', 'blueprintService', 'applicationApi', bottomSheetController], |
| controllerAs: 'ctrl', |
| template: bottomSheetTemplate |
| }; |
| |
| brBottomSheet.open(bottomSheetOpts); |
| |
| function bottomSheetController($log, brBrandInfo, brBottomSheetInstance, blueprintService, applicationApi) { |
| this.loading = true; |
| this.vendors = brBrandInfo.getVendorPackages(); |
| |
| this.close = ()=> { |
| brBottomSheetInstance.close('Closing deployment error reporting'); |
| }; |
| this.investigate = ()=> { |
| brBottomSheetInstance.updateMode('inset'); |
| }; |
| this.onClipboardSuccess = (e)=> { |
| angular.element(e.trigger).triggerHandler('copied'); |
| e.clearSelection(); |
| }; |
| this.onClipboardError = (e)=> { |
| let message = ''; |
| let actionKey = e.action === 'cut' ? 'X' : 'C'; |
| if(/iPhone|iPad/i.test(navigator.userAgent)) { |
| message = 'No support :('; |
| } |
| else if(/Mac/i.test(navigator.userAgent)) { |
| message = 'Press ⌘-' + actionKey + ' to ' + e.action; |
| } |
| else { |
| message = 'Press Ctrl-' + actionKey + ' to ' + e.action; |
| } |
| brSnackbar.create(message); |
| }; |
| |
| let applicationToDeploy = blueprintService.getAsYaml(); |
| applicationApi.createApplication(applicationToDeploy).then((success)=> { |
| $log.info('Application deployment ... success', success); |
| window.location.href = brBrandInfo.getAppDeployedUrl(success.entityId, success.entityId); |
| }).catch((error)=> { |
| $log.error('Application deployment ... failed', error); |
| this.error = { |
| title: 'Application deployment failed', |
| message: error.error.message, |
| details: error.error.details |
| }; |
| }).finally(()=> { |
| this.loading = false; |
| }); |
| } |
| } |
| |
| // allow downstream to configure controller and scope here |
| (composerOverrides.configureMainController || function() {})(vm, $scope, $element); |
| } |
| |
| export const mainState = { |
| name: 'main', |
| url: '/?bundleSymbolicName&bundleVersion&typeSymbolicName&typeVersion&yaml', |
| abstract: true, |
| template: template, |
| controller: ['$scope', '$element', '$log', '$state', '$stateParams', 'brBrandInfo', 'blueprintService', 'actionService', 'catalogApi', 'applicationApi', 'brSnackbar', 'brBottomSheet', 'edit', 'yaml', 'composerOverrides', MainController], |
| controllerAs: 'vm', |
| resolve: { |
| edit: ['$stateParams', 'blueprintLoaderApi', ($stateParams, blueprintLoaderApi) => blueprintLoaderApi.loadBlueprint($stateParams)], |
| yaml: ['$stateParams', 'blueprintLoaderApi', ($stateParams, blueprintLoaderApi) => blueprintLoaderApi.loadYaml($stateParams)] |
| } |
| }; |