2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 import * as _ from "lodash";
22 import { Component, ComponentInstance, IAppMenu, Requirement, Capability, ButtonModel } from "app/models";
23 import { SharingService, CacheService, EventListenerService, LeftPaletteLoaderService } from "app/services";
24 import { ModalsHandler, GRAPH_EVENTS, ComponentFactory, ChangeLifecycleStateHandler, MenuHandler, EVENTS, ComponentInstanceFactory } from "app/utils";
25 import { IWorkspaceViewModelScope } from "../../workspace-view-model";
26 import { ComponentGenericResponse } from "app/ng2/services/responses/component-generic-response";
27 import { Resource } from "app/models/components/resource";
28 import { ResourceType, ComponentType } from "app/utils/constants";
29 import { ComponentServiceFactoryNg2 } from "app/ng2/services/component-services/component.service.factory";
30 import { ServiceGenericResponse } from "app/ng2/services/responses/service-generic-response";
31 import { Service } from "app/models/components/service";
32 import { ZoneInstance } from "app/models/graph/zones/zone-instance";
33 import { ComponentServiceNg2 } from "app/ng2/services/component-services/component.service";
34 import { ModalService as ModalServiceSdcUI} from "sdc-ui/lib/angular/modals/modal.service"
35 import { IModalConfig, IModalButtonComponent } from "sdc-ui/lib/angular/modals/models/modal-config";
36 import { ValueEditComponent } from "app/ng2/components/ui/forms/value-edit/value-edit.component";
37 import { UnsavedChangesComponent } from "../../../../ng2/components/ui/forms/unsaved-changes/unsaved-changes.component";
38 import { ModalButtonComponent } from "sdc-ui/lib/angular/components";
42 export interface ICompositionViewModelScope extends IWorkspaceViewModelScope {
44 currentComponent:Component;
46 //Added for now, in the future need to remove and use only id and type to pass to tabs.
47 selectedComponent: Component;
48 selectedZoneInstance: ZoneInstance;
50 componentInstanceNames: Array<string>;
53 sharingService:SharingService;
57 isCanvasTagging:boolean;
58 isLoadingRightPanel:boolean;
60 openVersionChangeModal(pathsToDelete:string[]):ng.IPromise<any>;
61 onComponentInstanceVersionChange(component:Component);
62 isComponentInstanceSelected():boolean;
63 updateSelectedComponent():void;
65 deleteSelectedComponentInstance():void;
66 onBackgroundClick():void;
67 setSelectedInstance(componentInstance:ComponentInstance):void;
68 setSelectedZoneInstance(zoneInstance: ZoneInstance):void;
69 changeZoneInstanceName(newName:string):void;
72 isConfiguration():boolean;
73 preventMoveTab(state: boolean):void;
74 registerCreateInstanceEvent(callback: Function):void;
75 unregisterCreateInstanceEvent():void;
76 registerChangeComponentInstanceNameEvent(callback: Function):void;
77 unregisterChangeComponentInstanceNameEvent():void;
79 ComponentServiceNg2:ComponentServiceNg2,
80 cacheComponentsInstancesFullData:Component;
83 export class CompositionViewModel {
92 'Sdc.Services.SharingService',
94 'Sdc.Services.CacheService',
96 'ChangeLifecycleStateHandler',
97 'LeftPaletteLoaderService',
100 'EventListenerService',
101 'ComponentServiceFactoryNg2',
102 'ComponentServiceNg2',
106 constructor(private $scope:ICompositionViewModelScope,
107 private $log:ng.ILogService,
108 private sdcMenu:IAppMenu,
109 private MenuHandler:MenuHandler,
110 private $uibModal:ng.ui.bootstrap.IModalService,
111 private $state:ng.ui.IStateService,
112 private sharingService:SharingService,
113 private $filter:ng.IFilterService,
114 private cacheService:CacheService,
115 private ComponentFactory:ComponentFactory,
116 private ChangeLifecycleStateHandler:ChangeLifecycleStateHandler,
117 private LeftPaletteLoaderService:LeftPaletteLoaderService,
118 private ModalsHandler:ModalsHandler,
119 private ModalServiceSdcUI: ModalServiceSdcUI,
120 private eventListenerService:EventListenerService,
121 private ComponentServiceFactoryNg2: ComponentServiceFactoryNg2,
122 private ComponentServiceNg2:ComponentServiceNg2,
123 private Notification:any
126 this.$scope.setValidState(true);
128 this.initGraphData();
129 this.registerGraphEvents(this.$scope);
133 private initGraphData = ():void => {
134 if(!this.hasCompositionGraphData(this.$scope.component)) {
135 this.$scope.isLoading = true;
136 let service = this.ComponentServiceFactoryNg2.getComponentService(this.$scope.component);
137 service.getComponentCompositionData(this.$scope.component).subscribe((response:ComponentGenericResponse) => {
138 if (this.$scope.component.isService()) {
139 (<Service> this.$scope.component).forwardingPaths = (<ServiceGenericResponse>response).forwardingPaths;
141 this.$scope.component.componentInstances = response.componentInstances || [];
142 this.$scope.component.componentInstancesRelations = response.componentInstancesRelations || [];
143 this.$scope.component.policies = response.policies || [];
144 this.$scope.component.groupInstances = response.groupInstances || [];
145 this.$scope.isLoading = false;
146 this.initComponent();
147 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_COMPOSITION_GRAPH_DATA_LOADED);
150 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_COMPOSITION_GRAPH_DATA_LOADED);
152 this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_COMPOSITION_GRAPH_DATA_LOADED);
155 private hasCompositionGraphData = (component:Component):boolean => {
156 return !!(component.componentInstances && component.componentInstancesRelations && component.policies && component.groupInstances);
159 private cacheComponentsInstancesFullData:Array<Component>;
161 private initComponent = ():void => {
162 this.$scope.currentComponent = this.$scope.component;
163 this.$scope.selectedComponent = this.$scope.currentComponent;
164 this.$scope.selectedZoneInstance = null;
165 this.updateUuidMap();
166 this.$scope.isViewOnly = this.$scope.isViewMode();
169 private registerGraphEvents = (scope:ICompositionViewModelScope):void => {
170 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_NODE_SELECTED, scope.setSelectedInstance);
171 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_ZONE_INSTANCE_SELECTED, scope.setSelectedZoneInstance);
172 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED, scope.onBackgroundClick);
173 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_CANVAS_TAG_START, () => {
174 scope.isCanvasTagging = true;
175 this.eventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, true, this.showUnsavedChangesAlert);
177 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_CANVAS_TAG_END, () => {
178 scope.isCanvasTagging = false;
179 this.resetUnsavedChanges();
181 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_ZONE_INSTANCE_NAME_CHANGED, scope.changeZoneInstanceName);
182 this.eventListenerService.registerObserverCallback(EVENTS.UPDATE_PANEL, this.removeSelectedZoneInstance);
185 private showUnsavedChangesAlert = (afterSave?:Function):Promise<any> => {
186 let deferred = new Promise<any>((resolve, reject)=> {
187 const modal = this.ModalServiceSdcUI.openCustomModal(
189 title: "Unsaved Changes",
194 {id: 'cancelButton', text: 'Cancel', type: 'secondary', size: 'xsm', closeModal: true, callback: () => reject()},
195 {id: 'discardButton', text: 'Discard', type: 'secondary', size: 'xsm', closeModal: true, callback: () => { this.resetUnsavedChanges(); resolve()}},
196 {id: 'saveButton', text: 'Save', type: 'primary', size: 'xsm', closeModal: true, callback: () => { reject(); this.saveUnsavedChanges(afterSave); }}
197 ] as IModalButtonComponent[]
198 }, UnsavedChangesComponent, { isValidChangedData: true});
204 private unRegisterGraphEvents = (scope: ICompositionViewModelScope):void => {
205 this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_NODE_SELECTED, scope.setSelectedInstance);
206 this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_ZONE_INSTANCE_SELECTED, scope.setSelectedZoneInstance);
207 this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED, scope.onBackgroundClick);
208 this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_CANVAS_TAG_START);
209 this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_CANVAS_TAG_END);
210 this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_ZONE_INSTANCE_NAME_CHANGED, scope.changeZoneInstanceName);
211 this.eventListenerService.unRegisterObserver(EVENTS.UPDATE_PANEL, this.removeSelectedZoneInstance);
215 private resetUnsavedChanges = () => {
216 this.eventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
219 private saveUnsavedChanges = (afterSaveFunction?:Function):void => {
220 this.$scope.selectedZoneInstance.forceSave.next(afterSaveFunction);
221 this.eventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
224 private openUpdateComponentInstanceNameModal = ():void => {
226 let modalConfig:IModalConfig = {
230 testId: "renameInstanceModal",
232 {id: 'saveButton', text: 'OK', size: 'xsm', callback: this.saveInstanceName, closeModal: false},
233 {id: 'cancelButton', text: 'Cancel', size: 'sm', closeModal: true}
237 this.ModalServiceSdcUI.openCustomModal(modalConfig, ValueEditComponent, {name: this.$scope.currentComponent.selectedInstance.name, validityChangedCallback: this.enableOrDisableSaveButton});
242 private enableOrDisableSaveButton = (shouldEnable: boolean): void => {
243 let saveButton: ModalButtonComponent = this.ModalServiceSdcUI.getCurrentInstance().getButtonById('saveButton');
244 saveButton.disabled = !shouldEnable;
247 private saveInstanceName = () => {
248 let currentModal = this.ModalServiceSdcUI.getCurrentInstance();
249 let nameFromModal:string = currentModal.innerModalContent.instance.name;
251 if(nameFromModal != this.$scope.currentComponent.selectedInstance.name){
252 currentModal.buttons[0].disabled = true;
253 let componentInstanceModel:ComponentInstance = ComponentInstanceFactory.createComponentInstance(this.$scope.currentComponent.selectedInstance);
254 componentInstanceModel.name = nameFromModal;
256 let onFailed = (error) => {
257 currentModal.buttons[0].disabled = false;
259 let onSuccess = (componentInstance:ComponentInstance) => {
261 this.$scope.currentComponent.selectedInstance.name = componentInstance.name;
262 //update requirements and capabilities owner name
263 _.forEach(this.$scope.currentComponent.selectedInstance.requirements, (requirementsArray:Array<Requirement>) => {
264 _.forEach(requirementsArray, (requirement:Requirement):void => {
265 requirement.ownerName = componentInstance.name;
269 _.forEach(this.$scope.currentComponent.selectedInstance.capabilities, (capabilitiesArray:Array<Capability>) => {
270 _.forEach(capabilitiesArray, (capability:Capability):void => {
271 capability.ownerName = componentInstance.name;
274 this.ModalServiceSdcUI.closeModal();
275 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED, this.$scope.currentComponent.selectedInstance);
278 this.$scope.currentComponent.updateComponentInstance(componentInstanceModel).then(onSuccess, onFailed);
280 this.ModalServiceSdcUI.closeModal();
285 private removeSelectedComponentInstance = ():void => {
286 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, this.$scope.currentComponent.selectedInstance)
287 this.$scope.currentComponent.selectedInstance = null;
288 this.$scope.selectedComponent = this.$scope.currentComponent;
291 private removeSelectedZoneInstance = ():void => {
292 this.$scope.currentComponent.selectedInstance = null;
293 this.$scope.selectedZoneInstance = null;
294 this.$scope.selectedComponent = this.$scope.currentComponent;
297 private updateUuidMap = ():void => {
299 * In case user press F5, the page is refreshed and this.sharingService.currentEntity will be undefined,
300 * but after loadService or loadResource this.sharingService.currentEntity will be defined.
301 * Need to update the uuidMap with the new resource or service.
303 this.sharingService.addUuidValue(this.$scope.currentComponent.uniqueId, this.$scope.currentComponent.uuid);
306 private initScope = ():void => {
307 this.$scope.sharingService = this.sharingService;
308 this.$scope.sdcMenu = this.sdcMenu;
309 this.$scope.isLoading = false;
310 this.$scope.isLoadingRightPanel = false;
311 this.$scope.isCanvasTagging = false;
312 this.$scope.graphApi = {};
313 this.$scope.version = this.cacheService.get('version');
314 this.initComponent();
316 this.cacheComponentsInstancesFullData = new Array<Component>();
318 this.$scope.isComponentInstanceSelected = ():boolean => {
319 return this.$scope.currentComponent && this.$scope.currentComponent.selectedInstance != undefined && this.$scope.currentComponent.selectedInstance != null;
322 this.$scope.$on('$destroy', () => {
323 this.unRegisterGraphEvents(this.$scope);
326 this.$scope.restoreComponent = ():void => {
327 this.ComponentServiceNg2.restoreComponent(this.$scope.selectedComponent.componentType, this.$scope.selectedComponent.uniqueId).subscribe(() => {
328 this.Notification.success({
329 message: '<' + this.$scope.component.name + '> ' + this.$filter('translate')("ARCHIVE_SUCCESS_MESSAGE_TEXT"),
330 title: this.$filter('translate')("ARCHIVE_SUCCESS_MESSAGE_TITLE")
332 this.$scope.selectedComponent.archived = false;
337 this.$scope.updateSelectedComponent = ():void => {
338 if (this.$scope.currentComponent.selectedInstance) {
339 let parentComponentUid = this.$scope.currentComponent.selectedInstance.componentUid
340 if(this.$scope.currentComponent.selectedInstance.originType === ComponentType.SERVICE_PROXY){
341 parentComponentUid = this.$scope.currentComponent.selectedInstance.sourceModelUid;
343 let componentParent = _.find(this.cacheComponentsInstancesFullData, (component) => {
344 return component.uniqueId === parentComponentUid;
346 if (componentParent) {
347 this.$scope.selectedComponent = componentParent;
351 let onSuccess = (component:Component) => {
352 this.$scope.isLoadingRightPanel = false;
353 this.$scope.selectedComponent = component;
354 this.cacheComponentsInstancesFullData.push(component);
356 let onError = (component:Component) => {
357 console.log("Error updating selected component");
358 this.$scope.isLoadingRightPanel = false;
360 this.ComponentFactory.getComponentFromServer(this.$scope.currentComponent.selectedInstance.originType, parentComponentUid).then(onSuccess, onError);
362 console.log("Error updating selected component", e);
363 this.$scope.isLoadingRightPanel = false;
369 this.$scope.selectedComponent = this.$scope.currentComponent;
373 this.$scope.setSelectedInstance = (selectedComponent:ComponentInstance):void => {
375 this.$log.debug('composition-view-model::onNodeSelected:: with id: ' + selectedComponent.uniqueId);
376 this.$scope.currentComponent.setSelectedInstance(selectedComponent);
377 this.$scope.selectedZoneInstance = null;
378 this.$scope.updateSelectedComponent();
380 if (this.$state.current.name === 'workspace.composition.api') {
381 this.$state.go('workspace.composition.details');
383 if(!selectedComponent.isServiceProxy() && (this.$state.current.name === 'workspace.composition.consumption' || this.$state.current.name === 'workspace.composition.dependencies')) {
384 this.$state.go('workspace.composition.details');
388 this.$scope.setSelectedZoneInstance = (zoneInstance: ZoneInstance): void => {
389 this.$scope.currentComponent.selectedInstance = null;
390 this.$scope.selectedZoneInstance = zoneInstance;
393 this.$scope.onBackgroundClick = ():void => {
394 this.$scope.currentComponent.selectedInstance = null;
395 this.$scope.selectedZoneInstance = null;
396 this.$scope.selectedComponent = this.$scope.currentComponent;
398 if (this.$state.current.name === 'workspace.composition.api' || this.$state.current.name === 'workspace.composition.consumption' || this.$state.current.name === 'workspace.composition.dependencies') {
399 this.$state.go('workspace.composition.details');
402 if(this.$scope.selectedComponent.isService() && this.$state.current.name === 'workspace.composition.relations'){
403 this.$state.go('workspace.composition.api');
407 this.$scope.openUpdateModal = ():void => {
408 this.openUpdateComponentInstanceNameModal();
411 this.$scope.changeZoneInstanceName = (newName:string):void => {
412 this.$scope.selectedZoneInstance.instanceData.name = newName;
415 this.$scope.deleteSelectedComponentInstance = ():void => {
416 const {currentComponent} = this.$scope;
417 const {title, message} = this.$scope.sdcMenu.alertMessages['deleteInstance'];
418 let modalText = message.format([currentComponent.selectedInstance.name]);
420 if (currentComponent.isService()) {
421 const {forwardingPaths} = (<Service>currentComponent);
422 const instanceId = currentComponent.selectedInstance.uniqueId;
424 const relatedPaths = _.filter(forwardingPaths, forwardingPath => {
425 const pathElements = forwardingPath.pathElements.listToscaDataDefinition;
426 return pathElements.find(path => path.fromNode === instanceId || path.toNode === instanceId);
429 if (relatedPaths.length) {
430 const pathNames = _.map(relatedPaths, path => path.name).join(', ');
431 modalText += `<p>The following service paths will be erased: ${pathNames}</p>`;
434 this.ModalServiceSdcUI.openAlertModal(title, modalText, "OK", this.removeSelectedComponentInstance, "deleteInstanceModal");
437 this.$scope.openVersionChangeModal = (pathsToDelete:string[]):ng.IPromise<any> => {
438 const {currentComponent} = this.$scope;
439 const {forwardingPaths} = <Service>currentComponent;
441 const relatedPaths = _.filter(forwardingPaths, path =>
442 _.find(pathsToDelete, id =>
445 ).map(path => path.name);
446 const pathNames = _.join(relatedPaths, ', ') || 'none';
448 const {title, message} = this.$scope.sdcMenu.alertMessages['upgradeInstance'];
449 return this.ModalsHandler.openConfirmationModal(title, message.format([pathNames]), false);
452 this.$scope.onComponentInstanceVersionChange = (component:Component):void => {
453 let onChange = () => {
454 this.$scope.currentComponent = component;
455 this.$scope.setComponent(this.$scope.currentComponent);
456 this.$scope.updateSelectedComponent();
457 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_VERSION_CHANGED, this.$scope.currentComponent);
460 if (component.isService()) {
461 const service = this.ComponentServiceFactoryNg2.getComponentService(component);
462 service.getComponentCompositionData(component).subscribe((response:ServiceGenericResponse) => {
463 (<Service>component).forwardingPaths = response.forwardingPaths;
471 this.$scope.isPNF = (): boolean => {
472 return this.$scope.selectedComponent.isResource() && (<Resource>this.$scope.selectedComponent).resourceType === ResourceType.PNF;
475 this.$scope.isConfiguration = (): boolean => {
476 return this.$scope.selectedComponent.isResource() && (<Resource>this.$scope.selectedComponent).resourceType === ResourceType.CONFIGURATION;
479 this.$scope.preventMoveTab = (state: boolean): void => {
480 this.$scope.disabledTabs = state;
483 this.eventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE, this.$scope.reload);
485 this.$scope.registerCreateInstanceEvent = (callback: Function): void => {
486 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_CREATE_COMPONENT_INSTANCE, callback);
489 this.$scope.unregisterCreateInstanceEvent = (): void => {
490 this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_CREATE_COMPONENT_INSTANCE);
493 this.$scope.registerChangeComponentInstanceNameEvent = (callback: Function): void => {
494 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED, callback);
497 this.$scope.unregisterChangeComponentInstanceNameEvent = (): void => {
498 this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED);