Fix issue on tosca button enable/disable functionality
[sdc.git] / catalog-ui / src / app / ng2 / pages / properties-assignment / properties-assignment.page.component.ts
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
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
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
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=========================================================
19  */
20
21 import * as _ from "lodash";
22 import {Component, Inject, ViewChild} from "@angular/core";
23 import {PropertiesService} from "../../services/properties.service";
24 import {
25     ButtonModel,
26     Component as ComponentData,
27     ComponentInstance,
28     DerivedFEProperty,
29     FilterPropertiesAssignmentData,
30     GroupInstance,
31     InputBEModel,
32     InputFEModel,
33     InstanceBePropertiesMap,
34     InstanceFePropertiesMap,
35     InstancePropertiesAPIMap,
36     ModalModel,
37     PolicyInstance,
38     PropertyBEModel,
39     PropertyFEModel,
40     Service,
41     SimpleFlatProperty,
42     PropertyDeclareAPIModel,
43     PropertiesGroup
44 } from "app/models";
45 import {ResourceType} from "app/utils";
46 import {ComponentServiceNg2} from "../../services/component-services/component.service";
47 import {TopologyTemplateService} from "../../services/component-services/topology-template.service";
48 import {ComponentInstanceServiceNg2} from "../../services/component-instance-services/component-instance.service"
49 import {KeysPipe} from 'app/ng2/pipes/keys.pipe';
50 import {EVENTS, PROPERTY_TYPES, WorkspaceMode, PROPERTY_DATA} from "../../../utils/constants";
51 import {EventListenerService} from "app/services/event-listener-service"
52 import {HierarchyDisplayOptions} from "../../components/logic/hierarchy-navigtion/hierarchy-display-options";
53 import {FilterPropertiesAssignmentComponent} from "../../components/logic/filter-properties-assignment/filter-properties-assignment.component";
54 import {PropertyRowSelectedEvent} from "../../components/logic/properties-table/properties-table.component";
55 import {HierarchyNavService} from "./services/hierarchy-nav.service";
56 import {PropertiesUtils} from "./services/properties.utils";
57 import {ComponentModeService} from "../../services/component-services/component-mode.service";
58 import {Tab, Tabs} from "../../components/ui/tabs/tabs.component";
59 import {InputsUtils} from "./services/inputs.utils";
60 import {InstanceFeDetails} from "../../../models/instance-fe-details";
61 import {SdcUiCommon, SdcUiServices} from "onap-ui-angular";
62 import {UnsavedChangesComponent} from "app/ng2/components/ui/forms/unsaved-changes/unsaved-changes.component";
63 import {PropertyCreatorComponent} from "./property-creator/property-creator.component";
64 import {ModalService} from "../../services/modal.service";
65 import {DeclareListComponent} from "./declare-list/declare-list.component";
66 import {ToscaFunctionComponent, ToscaFunctionValidationEvent} from "./tosca-function/tosca-function.component";
67 import {CapabilitiesGroup, Capability} from "../../../models/capability";
68 import {ToscaPresentationData} from "../../../models/tosca-presentation";
69 import {Observable} from "rxjs";
70 import {TranslateService} from "../../shared/translator/translate.service";
71 import {ToscaFunction} from "../../../models/tosca-function";
72 import {SubPropertyToscaFunction} from "../../../models/sub-property-tosca-function";
73
74 const SERVICE_SELF_TITLE = "SELF";
75 @Component({
76     templateUrl: './properties-assignment.page.component.html',
77     styleUrls: ['./properties-assignment.page.component.less']
78 })
79 export class PropertiesAssignmentComponent {
80     title = "Properties & Inputs";
81
82     component: ComponentData;
83     componentInstanceNamesMap: { [key: string]: InstanceFeDetails } = {}; //key is the instance uniqueId
84     componentInstanceMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>(); //key is the instance uniqueId
85
86     propertiesNavigationData = [];
87     instancesNavigationData = [];
88
89     instanceFePropertiesMap: InstanceFePropertiesMap;
90     inputs: Array<InputFEModel> = [];
91     policies: Array<PolicyInstance> = [];
92     instances: Array<ComponentInstance | GroupInstance | PolicyInstance> = [];
93     searchQuery: string;
94     propertyStructureHeader: string;
95
96     selectedFlatProperty: SimpleFlatProperty = new SimpleFlatProperty();
97     selectedInstanceData: ComponentInstance | GroupInstance | PolicyInstance = null;
98     checkedPropertiesCount: number = 0;
99     checkedChildPropertiesCount: number = 0;
100     enableToscaFunction: boolean = false;
101     checkedToscaCount: number = 0;
102
103     hierarchyPropertiesDisplayOptions: HierarchyDisplayOptions = new HierarchyDisplayOptions('path', 'name', 'childrens');
104     hierarchyInstancesDisplayOptions: HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name', 'archived', null, 'iconClass');
105     displayClearSearch = false;
106     searchPropertyName: string;
107     currentMainTab: Tab;
108     isInputsTabSelected: boolean;
109     isPropertiesTabSelected: boolean;
110     isPoliciesTabSelected: boolean;
111     isReadonly: boolean;
112     resourceIsReadonly: boolean;
113     loadingInstances: boolean = false;
114     loadingInputs: boolean = false;
115     loadingPolicies: boolean = false;
116     loadingProperties: boolean = false;
117     changedData: Array<PropertyFEModel | InputFEModel>;
118     hasChangedData: boolean;
119     isValidChangedData: boolean;
120     savingChangedData: boolean;
121     stateChangeStartUnregister: Function;
122     serviceBePropertiesMap: InstanceBePropertiesMap;
123     serviceBeCapabilitiesPropertiesMap: InstanceBePropertiesMap;
124     selectedInstance_FlattenCapabilitiesList: Capability[];
125     componentInstancePropertyMap : PropertiesGroup;
126
127     @ViewChild('hierarchyNavTabs') hierarchyNavTabs: Tabs;
128     @ViewChild('propertyInputTabs') propertyInputTabs: Tabs;
129     @ViewChild('advanceSearch') advanceSearch: FilterPropertiesAssignmentComponent;
130
131     constructor(private propertiesService: PropertiesService,
132                 private hierarchyNavService: HierarchyNavService,
133                 private propertiesUtils: PropertiesUtils,
134                 private inputsUtils: InputsUtils,
135                 private componentServiceNg2: ComponentServiceNg2,
136                 private componentInstanceServiceNg2: ComponentInstanceServiceNg2,
137                 private propertyCreatorComponent: PropertyCreatorComponent,
138                 @Inject("$stateParams") _stateParams,
139                 @Inject("$scope") private $scope: ng.IScope,
140                 @Inject("$state") private $state: ng.ui.IStateService,
141                 @Inject("Notification") private notification: any,
142                 private componentModeService: ComponentModeService,
143                 private eventListenerService: EventListenerService,
144                 private ModalServiceSdcUI: SdcUiServices.ModalService,
145                 private modalService: ModalService,
146                 private keysPipe: KeysPipe,
147                 private topologyTemplateService: TopologyTemplateService,
148                 private translateService: TranslateService) {
149
150         this.instanceFePropertiesMap = new InstanceFePropertiesMap();
151         /* This is the way you can access the component data, please do not use any data except metadata, all other data should be received from the new api calls on the first time
152         than if the data is already exist, no need to call the api again - Ask orit if you have any questions*/
153         this.component = _stateParams.component;
154         this.eventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE, this.onCheckout);
155         this.updateViewMode();
156         this.changedData = [];
157         this.updateHasChangedData();
158         this.isValidChangedData = true;
159     }
160
161     ngOnInit() {
162         console.debug("==>" + this.constructor.name + ": ngOnInit");
163         this.loadingInputs = true;
164         this.loadingPolicies = true;
165         this.loadingInstances = true;
166         this.loadingProperties = true;
167         this.topologyTemplateService
168         .getComponentInputsWithProperties(this.component.componentType, this.component.uniqueId)
169         .subscribe(response => {
170             _.forEach(response.inputs, (input: InputBEModel) => {
171                 const newInput: InputFEModel = new InputFEModel(input);
172                 this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
173                 this.inputs.push(newInput); //only push items that were declared via SDC
174             });
175             this.componentInstancePropertyMap = response.componentInstancesProperties;
176             this.loadingInputs = false;
177
178         }, error => {
179         }); //ignore error
180         this.componentServiceNg2
181         .getComponentResourcePropertiesData(this.component)
182         .subscribe(response => {
183             this.loadingPolicies = false;
184             this.instances = [];
185             this.instances.push(...response.componentInstances);
186             this.instances.push(...response.groupInstances);
187             this.instances.push(...response.policies);
188
189             if (response.componentInstances) {
190                 response.componentInstances.forEach(instance => {
191                     this.componentInstanceMap.set(instance.uniqueId, <InstanceFeDetails>{
192                         name: instance.name,
193                         iconClass: instance.iconClass,
194                         originArchived: instance.originArchived
195                     });
196                 });
197             }
198
199             _.forEach(response.policies, (policy: any) => {
200                 const newPolicy: InputFEModel = new InputFEModel(policy);
201                 this.inputsUtils.resetInputDefaultValue(newPolicy, policy.defaultValue);
202                 this.policies.push(policy);
203             });
204
205             // add the service self instance to the top of the list.
206             const serviceInstance = new ComponentInstance();
207             serviceInstance.name = SERVICE_SELF_TITLE;
208             serviceInstance.uniqueId = this.component.uniqueId;
209             this.instances.unshift(serviceInstance);
210
211             _.forEach(this.instances, (instance) => {
212                 this.instancesNavigationData.push(instance);
213                 this.componentInstanceNamesMap[instance.uniqueId] = <InstanceFeDetails>{
214                     name: instance.name,
215                     iconClass: instance.iconClass,
216                     originArchived: instance.originArchived
217                 };
218             });
219             this.loadingInstances = false;
220             if (this.instancesNavigationData[0] == undefined) {
221                 this.loadingProperties = false;
222             }
223             this.selectFirstInstanceByDefault();
224         }, error => {
225             this.loadingInstances = false;
226         }); //ignore error
227
228         this.stateChangeStartUnregister = this.$scope.$on('$stateChangeStart', (event, toState, toParams) => {
229             // stop if has changed properties
230             if (this.hasChangedData) {
231                 event.preventDefault();
232                 this.showUnsavedChangesAlert().then(() => {
233                     this.$state.go(toState, toParams);
234                 }, () => {
235                 });
236             }
237         });
238
239       this.loadDataTypesByComponentModel(this.component.model);
240     }
241
242     ngOnDestroy() {
243         this.eventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE);
244         this.stateChangeStartUnregister();
245     }
246
247     selectFirstInstanceByDefault = () => {
248         if (this.instancesNavigationData[0] !== undefined) {
249             this.onInstanceSelectedUpdate(this.instancesNavigationData[0]);
250         }
251     };
252
253     updateViewMode = () => {
254         this.isReadonly = this.componentModeService.getComponentMode(this.component) === WorkspaceMode.VIEW;
255     }
256
257     onCheckout = (component: ComponentData) => {
258         this.component = component;
259         this.updateViewMode();
260     }
261
262     isSelf = (): boolean => {
263         return this.selectedInstanceData && this.selectedInstanceData.uniqueId == this.component.uniqueId;
264     }
265
266     showAddProperties = (): boolean => {
267         if (this.component.isService() && !(<Service>this.component).isSubstituteCandidate()) {
268             return false;
269         }
270         return this.isSelf();
271     }
272
273     getServiceProperties() {
274         this.loadingProperties = true;
275         this.topologyTemplateService
276         .getServiceProperties(this.component.uniqueId)
277         .subscribe((response) => {
278             this.serviceBePropertiesMap = new InstanceBePropertiesMap();
279             this.serviceBePropertiesMap[this.component.uniqueId] = response;
280             this.processInstancePropertiesResponse(this.serviceBePropertiesMap, false);
281             this.loadingProperties = false;
282         }, (error) => {
283             this.loadingProperties = false;
284         });
285     }
286
287     onInstanceSelectedUpdate = (instance: ComponentInstance | GroupInstance | PolicyInstance) => {
288         // stop if has changed properties
289         if (this.hasChangedData) {
290             this.showUnsavedChangesAlert().then((resolve) => {
291                 this.changeSelectedInstance(instance)
292             }, (reject) => {
293             });
294             return;
295         }
296         this.changeSelectedInstance(instance);
297     };
298
299     changeSelectedInstance = (instance: ComponentInstance | GroupInstance | PolicyInstance) => {
300         this.selectedInstanceData = instance;
301         this.loadingProperties = true;
302         if (instance instanceof ComponentInstance) {
303             let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
304             if (this.isInput(instance.originType)) {
305                 this.componentInstanceServiceNg2
306                 .getComponentInstanceInputs(this.component, instance)
307                 .subscribe(response => {
308                     instanceBePropertiesMap[instance.uniqueId] = response;
309                     this.processInstancePropertiesResponse(instanceBePropertiesMap, true);
310                 }, () => {
311                     //ignore error
312                 }, () => {
313                     this.loadingProperties = false;
314                 });
315             } else if (this.isSelf()) {
316                 this.getServiceProperties();
317             } else {
318                 this.componentInstanceServiceNg2
319                 .getComponentInstanceProperties(this.component, instance.uniqueId)
320                 .subscribe(response => {
321                     instanceBePropertiesMap[instance.uniqueId] = response;
322                     this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
323                 }, () => {
324                     //ignore error
325                 }, () => {
326                     this.loadingProperties = false;
327                 });
328             }
329             this.loadingProperties = false;
330             this.resourceIsReadonly = (instance.componentName === "vnfConfiguration");
331         } else if (instance instanceof GroupInstance) {
332             let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
333             this.componentInstanceServiceNg2
334             .getComponentGroupInstanceProperties(this.component, this.selectedInstanceData.uniqueId)
335             .subscribe((response) => {
336                 instanceBePropertiesMap[instance.uniqueId] = response;
337                 this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
338             }, () => {
339                 //ignore error
340             }, () => {
341                 this.loadingProperties = false;
342             });
343         } else if (instance instanceof PolicyInstance) {
344             let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
345             this.componentInstanceServiceNg2
346             .getComponentPolicyInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId)
347             .subscribe((response) => {
348                 instanceBePropertiesMap[instance.uniqueId] = response;
349                 this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
350             }, () => {
351                 //ignore error
352             }, () => {
353                 this.loadingProperties = false;
354             });
355         } else {
356             this.loadingProperties = false;
357         }
358
359         if (this.searchPropertyName) {
360             this.clearSearch();
361         }
362         //clear selected property from the navigation
363         this.selectedFlatProperty = new SimpleFlatProperty();
364         this.propertiesNavigationData = [];
365     };
366
367     /**
368      * Entry point handling response from server
369      */
370     processInstancePropertiesResponse = (instanceBePropertiesMap: InstanceBePropertiesMap, originTypeIsVF: boolean) => {
371         this.instanceFePropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(instanceBePropertiesMap, originTypeIsVF, this.inputs, this.component.model); //create flattened children, disable declared props, and init values
372         this.checkedPropertiesCount = 0;
373         this.checkedChildPropertiesCount = 0;
374     };
375
376     processInstanceCapabilitiesPropertiesResponse = (originTypeIsVF: boolean) => {
377         let selectedComponentInstanceData = <ComponentInstance>(this.selectedInstanceData);
378         let currentUniqueId = this.selectedInstanceData.uniqueId;
379         this.serviceBeCapabilitiesPropertiesMap = new InstanceBePropertiesMap();
380         let isCapabilityOwnedByInstance: boolean;
381         this.serviceBeCapabilitiesPropertiesMap[currentUniqueId] = _.reduce(
382             this.selectedInstance_FlattenCapabilitiesList,
383             (result, cap: Capability) => {
384                 isCapabilityOwnedByInstance = cap.ownerId === currentUniqueId ||
385                     selectedComponentInstanceData.isServiceProxy() || selectedComponentInstanceData.isServiceSubstitution() &&
386                     cap.ownerId === selectedComponentInstanceData.sourceModelUid;
387                 if (cap.properties && isCapabilityOwnedByInstance) {
388                     _.forEach(cap.properties, prop => {
389                         if (!prop.origName) {
390                             prop.origName = prop.name;
391                             prop.name = cap.name + '_' + prop.name;//for display. (before save - the name returns to its orig value: prop.name)
392                         }
393                     });
394                     return result.concat(cap.properties);
395                 }
396                 return result;
397             }, []);
398         let instanceFECapabilitiesPropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(this.serviceBeCapabilitiesPropertiesMap, originTypeIsVF, this.inputs); //create flattened children, disable declared props, and init values
399         //update FECapabilitiesProperties with their origName according to BeCapabilitiesProperties
400         _.forEach(instanceFECapabilitiesPropertiesMap[currentUniqueId], prop => {
401             prop.origName = _.find(this.serviceBeCapabilitiesPropertiesMap[currentUniqueId], p => p.uniqueId === prop.uniqueId).origName;
402         });
403         //concatenate capabilitiesProps to all props list
404         this.instanceFePropertiesMap[currentUniqueId] = (this.instanceFePropertiesMap[currentUniqueId] || []).concat(instanceFECapabilitiesPropertiesMap[currentUniqueId]);
405         this.checkedPropertiesCount = 0;
406     };
407
408     isCapabilityProperty = (prop: PropertyBEModel) => {
409         return _.find(this.selectedInstance_FlattenCapabilitiesList, cap => cap.uniqueId === prop.parentUniqueId);
410     };
411
412     /*** VALUE CHANGE EVENTS ***/
413     dataChanged = (item: PropertyFEModel | InputFEModel) => {
414         let itemHasChanged;
415         if (this.isPropertiesTabSelected && item instanceof PropertyFEModel) {
416             itemHasChanged = item.hasValueObjChanged();
417         } else if (this.isInputsTabSelected && item instanceof InputFEModel) {
418             itemHasChanged = item.hasChanged();
419         } else if (this.isPoliciesTabSelected && item instanceof InputFEModel) {
420             itemHasChanged = item.hasDefaultValueChanged();
421         }
422
423         const dataChangedIdx = this.changedData.findIndex((changedItem) => changedItem === item);
424         if (itemHasChanged) {
425             if (dataChangedIdx === -1) {
426                 this.changedData.push(item);
427             }
428         } else {
429             if (dataChangedIdx !== -1) {
430                 this.changedData.splice(dataChangedIdx, 1);
431             }
432         }
433
434         if (this.isPropertiesTabSelected) {
435             this.isValidChangedData = this.changedData.every((changedItem) => (<PropertyFEModel>changedItem).valueObjIsValid);
436         } else if (this.isInputsTabSelected) {
437             this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid && (<InputFEModel>changedItem).metadataIsValid);
438         } else if (this.isPoliciesTabSelected) {
439             this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid);
440         }
441         this.updateHasChangedData();
442     };
443
444
445     /*** HEIRARCHY/NAV RELATED FUNCTIONS ***/
446
447     /**
448      * Handle select node in navigation area, and select the row in table
449      */
450     onPropertySelectedUpdate = ($event) => {
451         console.debug("==>" + this.constructor.name + ": onPropertySelectedUpdate");
452         this.selectedFlatProperty = $event;
453         let parentProperty: PropertyFEModel = this.propertiesService.getParentPropertyFEModelFromPath(this.instanceFePropertiesMap[this.selectedFlatProperty.instanceName], this.selectedFlatProperty.path);
454         parentProperty.expandedChildPropertyId = this.selectedFlatProperty.path;
455     };
456
457     /**
458      * When user select row in table, this will prepare the hirarchy object for the tree.
459      */
460     selectPropertyRow = (propertyRowSelectedEvent: PropertyRowSelectedEvent) => {
461         console.debug("==>" + this.constructor.name + ": selectPropertyRow " + propertyRowSelectedEvent.propertyModel.name);
462         let property = propertyRowSelectedEvent.propertyModel;
463         let instanceName = propertyRowSelectedEvent.instanceName;
464         this.propertyStructureHeader = null;
465
466         // Build hirarchy tree for the navigation and update propertiesNavigationData with it.
467         if (!(this.selectedInstanceData instanceof ComponentInstance) || this.selectedInstanceData.originType !== ResourceType.VF) {
468             let simpleFlatProperty: Array<SimpleFlatProperty>;
469             if (property instanceof PropertyFEModel) {
470                 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(property, instanceName);
471             } else if (property instanceof DerivedFEProperty) {
472                 // Need to find parent PropertyFEModel
473                 let parentPropertyFEModel: PropertyFEModel = _.find(this.instanceFePropertiesMap[instanceName], (tmpFeProperty): boolean => {
474                     return property.propertiesName.indexOf(tmpFeProperty.name) === 0;
475                 });
476                 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(parentPropertyFEModel, instanceName);
477             }
478             this.propertiesNavigationData = simpleFlatProperty;
479         }
480
481         // Update the header in the navigation tree with property name.
482         this.propertyStructureHeader = (property.propertiesName.split('#'))[0];
483
484         // Set selected property in table
485         this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName);
486         this.hierarchyNavTabs.triggerTabChange('Property Structure');
487     };
488
489
490     selectInstanceRow = ($event) => {//get instance name
491         this.selectedInstanceData = _.find(this.instancesNavigationData, (instance: ComponentInstance) => {
492             return instance.name == $event;
493         });
494         this.hierarchyNavTabs.triggerTabChange('Composition');
495     };
496
497     tabChanged = (event) => {
498         // stop if has changed properties
499         if (this.hasChangedData) {
500             this.propertyInputTabs.triggerTabChange(this.currentMainTab.title);
501             this.showUnsavedChangesAlert().then((proceed) => {
502                 this.propertyInputTabs.selectTab(this.propertyInputTabs.tabs.find((tab) => tab.title === event.title));
503             }, () => {
504             });
505             return;
506         }
507
508         console.debug("==>" + this.constructor.name + ": tabChanged " + event);
509         this.currentMainTab = this.propertyInputTabs.tabs.find((tab) => tab.title === event.title);
510         this.isPropertiesTabSelected = this.currentMainTab.title === "Properties";
511         this.isInputsTabSelected = this.currentMainTab.title === "Inputs";
512         this.isPoliciesTabSelected = this.currentMainTab.title === "Policies";
513         this.propertyStructureHeader = null;
514         this.searchQuery = '';
515     };
516
517     /**
518      * Select Tosca function value from defined values
519      */
520     selectToscaFunctionAndValues = (): void => {
521         const selectedInstanceData: ComponentInstance | GroupInstance | PolicyInstance = this.getSelectedInstance();
522         if (!selectedInstanceData) {
523             return;
524         }
525         this.openToscaGetFunctionModal();
526     }
527
528     private getSelectedInstance(): ComponentInstance | GroupInstance | PolicyInstance {
529         const instancesIds = this.keysPipe.transform(this.instanceFePropertiesMap, []);
530         const instanceId: string = instancesIds[0];
531         return <ComponentInstance | GroupInstance | PolicyInstance> this.instances.find(instance => 
532             instance.uniqueId == instanceId && (instance instanceof ComponentInstance || instance instanceof GroupInstance || instance instanceof PolicyInstance));
533     }
534
535     private buildCheckedInstanceProperty(): PropertyBEModel {
536         return this.buildCheckedInstanceProperties()[0];
537     }
538
539     private buildCheckedInstanceProperties(): PropertyBEModel[] {
540         const instancesIds = this.keysPipe.transform(this.instanceFePropertiesMap, []);
541         const instanceId: string = instancesIds[0];
542         return this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
543     }
544
545     private openToscaGetFunctionModal() {
546         const modalTitle = this.translateService.translate('TOSCA_FUNCTION_MODAL_TITLE');
547         const modalButtons = [];
548         let disableSaveButtonFlag = true;
549         const modal = this.modalService.createCustomModal(new ModalModel(
550             'sm',
551             modalTitle,
552             null,
553             modalButtons,
554             null /* type */
555         ));
556         modalButtons.push(new ButtonModel(this.translateService.translate('MODAL_SAVE'), 'blue',
557             () => {
558                 const toscaGetFunction: ToscaFunction = modal.instance.dynamicContent.instance.toscaFunctionForm.value;
559                 if (toscaGetFunction) {
560                     this.updateCheckedInstancePropertyFunctionValue(toscaGetFunction);
561                 } else {
562                     this.clearCheckedInstancePropertyValue();
563                 }
564                 this.modalService.closeCurrentModal();
565             },
566             (): boolean => { return disableSaveButtonFlag }
567         ));
568         const checkedInstanceProperty = this.buildCheckedInstanceProperty();
569         modalButtons.push(new ButtonModel(this.translateService.translate('MODAL_CANCEL'), 'outline grey', () => {
570             this.modalService.closeCurrentModal();
571         }));
572
573
574         this.modalService.addDynamicContentToModalAndBindInputs(modal, ToscaFunctionComponent, {
575             'property': checkedInstanceProperty,
576             'componentInstanceMap': this.componentInstanceMap
577         });
578         modal.instance.dynamicContent.instance.onValidityChange.subscribe((validationEvent: ToscaFunctionValidationEvent) => {
579             disableSaveButtonFlag = !validationEvent.isValid;
580         });
581         modal.instance.open();
582     }
583
584     private clearCheckedInstancePropertyValue() {
585         const checkedInstanceProperty: PropertyBEModel = this.buildCheckedInstanceProperty();
586         const currentValue : any = checkedInstanceProperty.value;
587         checkedInstanceProperty.getInputValues = null;
588         checkedInstanceProperty.value = null;
589         checkedInstanceProperty.toscaFunction = null;
590         if (checkedInstanceProperty instanceof PropertyDeclareAPIModel && (<PropertyDeclareAPIModel>checkedInstanceProperty).propertiesName){
591             const propertiesNameArray = (<PropertyDeclareAPIModel>checkedInstanceProperty).propertiesName;
592             const parts = propertiesNameArray.split("#");
593             let currentKey = [];
594             if (this.isListOrMap(checkedInstanceProperty.type)) {
595                 if (checkedInstanceProperty.schemaType == PROPERTY_TYPES.MAP) {
596                     currentKey.push((<DerivedFEProperty>checkedInstanceProperty.input).parentMapKey);
597                 }
598                 currentKey.push((<DerivedFEProperty>checkedInstanceProperty.input).mapKey);
599                 if (checkedInstanceProperty.schemaType != PROPERTY_TYPES.MAP && this.isComplexSchemaType(checkedInstanceProperty.schemaType)) {
600                     currentKey.push(parts.reverse()[0]);
601                 }
602             }
603             if (propertiesNameArray.length > 1){
604                 const index = checkedInstanceProperty.subPropertyToscaFunctions.findIndex(existingSubPropertyToscaFunction => this.areEqual(existingSubPropertyToscaFunction.subPropertyPath, currentKey.length > 0 ? currentKey : parts.slice(1)));
605                 checkedInstanceProperty.subPropertyToscaFunctions.splice(index, 1);
606             }
607             if(currentValue !== null && currentKey.length > 0){
608                 let valueJson = JSON.parse(currentValue);
609                 if(currentKey.length >1){
610                     let innerObj = valueJson[currentKey[0]];
611                     delete innerObj[currentKey[1]];
612                     valueJson[currentKey[0]] = innerObj;
613                 }else{
614                     delete valueJson[currentKey[0]];
615                 }
616                 if (checkedInstanceProperty.type == PROPERTY_TYPES.LIST && currentKey.length == 1) {
617                     let listValue = valueJson.filter(function (item) {
618                         return item != null && item != '';
619                     });
620                     checkedInstanceProperty.value = JSON.stringify(listValue);
621                 } else {
622                     checkedInstanceProperty.value = JSON.stringify(valueJson);
623                 }
624             }
625         }
626         if (this.selectedInstanceData instanceof ComponentInstance) {
627             this.updateInstanceProperty(checkedInstanceProperty);
628         } else if (this.selectedInstanceData instanceof GroupInstance) {
629             this.updateGroupInstanceProperty(checkedInstanceProperty);
630         } else if (this.selectedInstanceData instanceof PolicyInstance) {
631             this.updatePolicyInstanceProperty(checkedInstanceProperty);
632         }
633     }
634
635     private updateCheckedInstancePropertyFunctionValue(toscaFunction: ToscaFunction) {
636         const checkedProperty: PropertyBEModel = this.buildCheckedInstanceProperty();
637         if (checkedProperty instanceof PropertyDeclareAPIModel && (<PropertyDeclareAPIModel>checkedProperty).propertiesName){
638             const propertiesName = (<PropertyDeclareAPIModel>checkedProperty).propertiesName;
639             const parts = propertiesName.split("#");
640             let currentKey = [];
641             if (this.isListOrMap(checkedProperty.type)) {
642                 if (checkedProperty.schemaType == PROPERTY_TYPES.MAP) {
643                     currentKey.push((<DerivedFEProperty>checkedProperty.input).parentMapKey);
644                 }
645                 currentKey.push((<DerivedFEProperty>checkedProperty.input).mapKey);
646                 if (checkedProperty.schemaType != PROPERTY_TYPES.MAP && this.isComplexSchemaType(checkedProperty.schemaType)) {
647                     currentKey.push(parts.reverse()[0]);
648                 }
649             }
650             if (checkedProperty.subPropertyToscaFunctions == null){
651                 checkedProperty.subPropertyToscaFunctions = [];
652             }
653             let subPropertyToscaFunction = checkedProperty.subPropertyToscaFunctions.find(existingSubPropertyToscaFunction => this.areEqual(existingSubPropertyToscaFunction.subPropertyPath, currentKey.length > 0 ? currentKey : parts.slice(1)));
654             if (!subPropertyToscaFunction){
655                  subPropertyToscaFunction = new SubPropertyToscaFunction();
656                  checkedProperty.subPropertyToscaFunctions.push(subPropertyToscaFunction);
657             }
658             subPropertyToscaFunction.toscaFunction = toscaFunction;
659             subPropertyToscaFunction.subPropertyPath = currentKey.length > 0 ? currentKey : parts.slice(1);
660    
661         } else {
662             checkedProperty.subPropertyToscaFunctions = null;
663             checkedProperty.toscaFunction = toscaFunction;
664         }
665         if (this.selectedInstanceData instanceof ComponentInstance) {
666             this.updateInstanceProperty(checkedProperty);
667         } else if (this.selectedInstanceData instanceof GroupInstance) {
668             this.updateGroupInstanceProperty(checkedProperty);
669         } else if (this.selectedInstanceData instanceof PolicyInstance) {
670             this.updatePolicyInstanceProperty(checkedProperty);
671         }
672     }
673
674     private isComplexSchemaType(propertyType: string): boolean {
675         return PROPERTY_DATA.SIMPLE_TYPES.indexOf(propertyType) === -1;
676     }
677
678     private isListOrMap(propertyType: string): boolean {
679         return PROPERTY_TYPES.MAP === propertyType || PROPERTY_TYPES.LIST === propertyType;
680     }
681
682     private areEqual(array1: string[], array2: string[]): boolean {
683         return array1.length === array2.length && array1.every(function(value, index) { return value === array2[index]})
684     }
685
686     updateInstanceProperty(instanceProperty: PropertyBEModel) {
687         this.loadingProperties = true;
688         this.enableToscaFunction = false;
689         this.checkedToscaCount = 0;
690         this.componentInstanceServiceNg2.updateInstanceProperties(this.component.componentType, this.component.uniqueId,
691             this.selectedInstanceData.uniqueId, [instanceProperty])
692         .subscribe(() => {
693             this.changeSelectedInstance(this.getSelectedInstance());
694         }, (error) => {
695             this.loadingProperties = false;
696             console.error(error);
697         }, () => {
698             this.loadingProperties = false;
699         });
700     }
701
702     updateGroupInstanceProperty(instanceProperty: PropertyBEModel) {
703         this.loadingProperties = true;
704         this.componentInstanceServiceNg2.updateComponentGroupInstanceProperties(this.component.componentType, this.component.uniqueId,
705             this.selectedInstanceData.uniqueId, [instanceProperty])
706         .subscribe(() => {
707             this.changeSelectedInstance(this.getSelectedInstance());
708         }, (error) => {
709             this.loadingProperties = false;
710             console.error(error);
711         }, () => {
712             this.loadingProperties = false;
713         });
714     }
715
716     updatePolicyInstanceProperty(instanceProperty: PropertyBEModel) {
717         this.loadingProperties = true;
718         this.componentInstanceServiceNg2.updateComponentPolicyInstanceProperties(this.component.componentType, this.component.uniqueId,
719             this.selectedInstanceData.uniqueId, [instanceProperty])
720         .subscribe(() => {
721             this.changeSelectedInstance(this.getSelectedInstance());
722         }, (error) => {
723             this.loadingProperties = false;
724             console.error(error);
725         }, () => {
726             this.loadingProperties = false;
727         });
728     }
729
730     /*** DECLARE PROPERTIES/INPUTS ***/
731     declareProperties = (): void => {
732         console.debug("==>" + this.constructor.name + ": declareProperties");
733
734         let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
735         let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
736         let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
737         let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
738         let instancesIds = this.keysPipe.transform(this.instanceFePropertiesMap, []);
739
740         angular.forEach(instancesIds, (instanceId: string): void => {
741             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
742             if (selectedInstanceData instanceof ComponentInstance) {
743                 if (!this.isInput(selectedInstanceData.originType)) {
744                     // convert Property FE model -> Property BE model, extract only checked
745                     selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
746                 } else {
747                     selectedComponentInstancesInputs[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
748                 }
749             } else if (selectedInstanceData instanceof GroupInstance) {
750                 selectedGroupInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
751             } else if (selectedInstanceData instanceof PolicyInstance) {
752                 selectedPolicyInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
753             }
754         });
755
756         let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
757
758         //move changed capabilities properties from componentInstanceInputsMap obj to componentInstanceProperties
759         inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] =
760             (inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] || []).concat(
761                 _.filter(
762                     inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId],
763                     (prop: PropertyBEModel) => this.isCapabilityProperty(prop)
764                 )
765             );
766         inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId] = _.filter(
767             inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId],
768             prop => !this.isCapabilityProperty(prop)
769         );
770         if (inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId].length === 0) {
771             delete inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId];
772         }
773
774         let isCapabilityPropertyChanged = false;
775         _.forEach(
776             inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId],
777             (prop: PropertyBEModel) => {
778                 prop.name = prop.origName || prop.name;
779                 if (this.isCapabilityProperty(prop)) {
780                     isCapabilityPropertyChanged = true;
781                 }
782             }
783         );
784         this.topologyTemplateService
785             .createInput(this.component, inputsToCreate, this.isSelf())
786             .subscribe((response) => {
787                 this.selectInstanceRow(SERVICE_SELF_TITLE);
788                 this.onInstanceSelectedUpdate(this.instances[0]);
789                 this.setInputTabIndication(response.length);
790                 this.checkedPropertiesCount = 0;
791                 this.checkedChildPropertiesCount = 0;
792                 _.forEach(response, (input: InputBEModel) => {
793                     const newInput: InputFEModel = new InputFEModel(input);
794                     this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
795                     this.inputs.push(newInput);
796                     this.updatePropertyValueAfterDeclare(newInput);
797                 });
798                 if (isCapabilityPropertyChanged) {
799                     this.reloadInstanceCapabilities();
800                 }
801             }, error => {}); //ignore error
802     };
803
804     declareListProperties = (): void => {
805         // get selected properties
806         let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
807         let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
808         let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
809         let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
810         let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
811         let propertyNameList: Array<string> = [];
812         let insId :string;
813
814         angular.forEach(instancesIds, (instanceId: string): void => {
815             insId = instanceId;
816             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
817             let checkedProperties: PropertyBEModel[] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
818
819             if (selectedInstanceData instanceof ComponentInstance) {
820                 if (!this.isInput(selectedInstanceData.originType)) {
821                     // convert Property FE model -> Property BE model, extract only checked
822                     selectedComponentInstancesProperties[instanceId] = checkedProperties;
823                 } else {
824                     selectedComponentInstancesInputs[instanceId] = checkedProperties;
825                 }
826             } else if (selectedInstanceData instanceof GroupInstance) {
827                 selectedGroupInstancesProperties[instanceId] = checkedProperties;
828             } else if (selectedInstanceData instanceof PolicyInstance) {
829                 selectedPolicyInstancesProperties[instanceId] = checkedProperties;
830             }
831
832             angular.forEach(checkedProperties, (property: PropertyBEModel) => {
833                 propertyNameList.push(property.name);
834             });
835         });
836
837         let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
838
839         let modalTitle = 'Declare Properties as List Input';
840         const modal = this.modalService.createCustomModal(new ModalModel(
841             'sm', /* size */
842             modalTitle, /* title */
843             null, /* content */
844             [ /* buttons */
845                 new ButtonModel(
846                     'Save', /* text */
847                     'blue', /* css class */
848                     () => { /* callback */
849                         let content:any = modal.instance.dynamicContent.instance;
850
851                         /* listInput */
852                         let reglistInput: InstanceBePropertiesMap = new InstanceBePropertiesMap();
853                         let typelist: any = PROPERTY_TYPES.LIST;
854                         let uniID: any = insId;
855                         let boolfalse: any = false;
856                         let required: any = content.propertyModel.required;
857                         let schem :any = {
858                             "empty": boolfalse,
859                             "property": {
860                                 "type": content.propertyModel.simpleType,
861                                 "required": required
862                             }
863                         }
864                         let schemaProp :any = {
865                             "type": content.propertyModel.simpleType,
866                             "required": required
867                         }
868
869                         reglistInput.description = content.propertyModel.description;
870                         reglistInput.name = content.propertyModel.name;
871                         reglistInput.type = typelist;
872                         reglistInput.schemaType = content.propertyModel.simpleType;
873                         reglistInput.instanceUniqueId = uniID;
874                         reglistInput.uniqueId = uniID;
875                         reglistInput.required = required;
876                         reglistInput.schema = schem;
877                         reglistInput.schemaProperty = schemaProp;
878
879                         let input = {
880                             componentInstInputsMap: content.inputsToCreate,
881                             listInput: reglistInput
882                         };
883
884                         this.topologyTemplateService
885                         .createListInput(this.component, input, this.isSelf())
886                         .subscribe(response => {
887                             this.setInputTabIndication(response.length);
888                             this.checkedPropertiesCount = 0;
889                             this.checkedChildPropertiesCount = 0;
890                             _.forEach(response, (input: InputBEModel) => {
891                                 let newInput: InputFEModel = new InputFEModel(input);
892                                 this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
893                                 this.inputs.push(newInput);
894                                 // create list input does not return updated properties info, so need to reload
895                                 //this.updatePropertyValueAfterDeclare(newInput);
896                                 // Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
897                                 this.changeSelectedInstance(this.selectedInstanceData);
898
899                                 modal.instance.close();
900                             });
901                         }, error => {}); //ignore error
902             
903                     }
904                     /*, getDisabled: function */
905                 ),
906                 new ButtonModel('Cancel', 'outline grey', () => {
907                     modal.instance.close();
908                 }),
909             ],
910             null /* type */
911         ));
912         // 3rd arg is passed to DeclareListComponent instance
913         this.modalService.addDynamicContentToModal(modal, DeclareListComponent, {properties: inputsToCreate, propertyNameList: propertyNameList});
914         modal.instance.open();
915     };
916
917      /*** DECLARE PROPERTIES/POLICIES ***/
918      declarePropertiesToPolicies = (): void => {
919         let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
920         let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
921
922         angular.forEach(instancesIds, (instanceId: string): void => {
923             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
924             if (selectedInstanceData instanceof ComponentInstance) {
925                 if (!this.isInput(selectedInstanceData.originType)) {
926                     selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
927                 }
928             }
929         });
930
931         let policiesToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(null, selectedComponentInstancesProperties, null, null);
932         this.loadingPolicies = true;
933
934         this.topologyTemplateService
935             .createPolicy(this.component, policiesToCreate, this.isSelf())
936             .subscribe(response => {
937                 this.setPolicyTabIndication(response.length);
938                 this.checkedPropertiesCount = 0;
939                 this.displayPoliciesAsDeclared(response);
940                 this.loadingPolicies = false;
941             }); //ignore error
942
943     }
944
945     displayPoliciesAsDeclared = (policies) => {
946         _.forEach(policies, (policy: any) => {
947             let newPolicy: InputFEModel = new InputFEModel(policy);
948             this.inputsUtils.resetInputDefaultValue(newPolicy, policy.defaultValue);
949             newPolicy.relatedPropertyName = policy.name;
950             newPolicy.relatedPropertyValue = policy.value;
951             this.updatePropertyValueAfterDeclare(newPolicy);
952             this.policies.push(policy);
953         });
954     }
955
956     saveChangedData = ():Promise<(PropertyBEModel|InputBEModel)[]> => {
957         return new Promise((resolve, reject) => {
958             if (!this.isValidChangedData) {
959                 reject('Changed data is invalid - cannot save!');
960                 return;
961             }
962             if (!this.changedData.length) {
963                 resolve([]);
964                 return;
965             }
966
967             // make request and its handlers
968             let request;
969             let handleSuccess, handleError;
970             let changedInputsProperties = [], changedCapabilitiesProperties = [];
971             if (this.isPropertiesTabSelected) {
972                 const changedProperties: PropertyBEModel[] = this.changedData.map((changedProp) => {
973                     changedProp = <PropertyFEModel>changedProp;
974                     const propBE = new PropertyBEModel(changedProp);
975                     propBE.toscaPresentation = new ToscaPresentationData();
976                     propBE.toscaPresentation.ownerId = changedProp.parentUniqueId;
977                     propBE.value = changedProp.getJSONValue();
978                     propBE.name = changedProp.origName || changedProp.name;
979                     delete propBE.origName;
980                     return propBE;
981                 });
982                 changedCapabilitiesProperties = _.filter(changedProperties, prop => this.isCapabilityProperty(prop));
983
984                 if (this.selectedInstanceData instanceof ComponentInstance) {
985                     if (this.isInput(this.selectedInstanceData.originType)) {
986                         changedInputsProperties = _.filter(changedProperties, prop => !this.isCapabilityProperty(prop));
987                         if (changedInputsProperties.length && changedCapabilitiesProperties.length) {
988                             request = Observable.forkJoin(
989                                 this.componentInstanceServiceNg2.updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties),
990                                 this.componentInstanceServiceNg2.updateInstanceProperties(this.component.componentType, this.component.uniqueId,
991                                     this.selectedInstanceData.uniqueId, changedCapabilitiesProperties)
992                             );
993                         }
994                         else if (changedInputsProperties.length) {
995                             request = this.componentInstanceServiceNg2
996                                 .updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties);
997                         }
998                         else if (changedCapabilitiesProperties.length) {
999                             request = this.componentInstanceServiceNg2
1000                                 .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedCapabilitiesProperties);
1001                         }
1002                         handleSuccess = (response) => {
1003                             // reset each changed property with new value and remove it from changed properties list
1004                             response.forEach((resInput) => {
1005                                 const changedProp = <PropertyFEModel>this.changedData.shift();
1006                                 this.propertiesUtils.resetPropertyValue(changedProp, resInput.value);
1007                             });
1008                         };
1009                     } else {
1010                         if (this.isSelf()) {
1011                             request = this.topologyTemplateService.updateServiceProperties(this.component.uniqueId,  _.map(changedProperties, cp => {
1012                                 delete cp.constraints;
1013                                 return cp;
1014                             }));
1015                         } else {
1016                             request = this.componentInstanceServiceNg2
1017                                 .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
1018                         }
1019                         handleSuccess = (response) => {
1020                             // reset each changed property with new value and remove it from changed properties list
1021                             response.forEach((resProp) => {
1022                                 const changedProp = <PropertyFEModel>this.changedData.shift();
1023                                 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
1024                             });
1025                             resolve(response);
1026                         };
1027                     }
1028                 } else if (this.selectedInstanceData instanceof GroupInstance) {
1029                     request = this.componentInstanceServiceNg2
1030                         .updateComponentGroupInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
1031                     handleSuccess = (response) => {
1032                         // reset each changed property with new value and remove it from changed properties list
1033                         response.forEach((resProp) => {
1034                             const changedProp = <PropertyFEModel>this.changedData.shift();
1035                             this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
1036                         });
1037                         resolve(response);
1038                     };
1039                 } else if (this.selectedInstanceData instanceof PolicyInstance) {
1040                     request = this.componentInstanceServiceNg2
1041                         .updateComponentPolicyInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
1042                     handleSuccess = (response) => {
1043                         // reset each changed property with new value and remove it from changed properties list
1044                         response.forEach((resProp) => {
1045                             const changedProp = <PropertyFEModel>this.changedData.shift();
1046                             this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
1047                         });
1048                         resolve(response);
1049                     };
1050                 }
1051             } else if (this.isInputsTabSelected) {
1052             
1053                 const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => {
1054                     changedInput = <InputFEModel>changedInput;
1055                     const inputBE = new InputBEModel(changedInput);
1056                     inputBE.defaultValue = changedInput.getJSONDefaultValue();
1057                     return inputBE;
1058                 });
1059                 request = this.componentServiceNg2
1060                     .updateComponentInputs(this.component, changedInputs);
1061                 handleSuccess = (response) => {
1062                     // reset each changed property with new value and remove it from changed properties list
1063                     response.forEach((resInput) => {
1064                         const changedInput = <InputFEModel>this.changedData.shift();
1065                         this.inputsUtils.resetInputDefaultValue(changedInput, resInput.defaultValue);
1066                         changedInput.required = resInput.required;
1067                         changedInput.requiredOrig = resInput.required;
1068                     });
1069                 }
1070             }
1071
1072             this.savingChangedData = true;
1073             request.subscribe(
1074                 (response) => {
1075                     this.savingChangedData = false;
1076                     if (changedCapabilitiesProperties.length) {
1077                         this.reloadInstanceCapabilities();
1078                     }
1079                     handleSuccess && handleSuccess(response);
1080                     this.updateHasChangedData();
1081                     resolve(response);
1082                 },
1083                 (error) => {
1084                     this.savingChangedData = false;
1085                     handleError && handleError(error);
1086                     this.updateHasChangedData();
1087                     reject(error);
1088                 }
1089             );
1090         });
1091     };
1092
1093     reloadInstanceCapabilities = (): void => {
1094         let currentInstanceIndex = _.findIndex(this.instances, instance => instance.uniqueId == this.selectedInstanceData.uniqueId);
1095         this.componentServiceNg2.getComponentResourceInstances(this.component).subscribe(result => {
1096             let instanceCapabilitiesData: CapabilitiesGroup = _.reduce(result.componentInstances, (res, instance) => {
1097                 if (instance.uniqueId === this.selectedInstanceData.uniqueId) {
1098                     return instance.capabilities;
1099                 }
1100                 return res;
1101             }, new CapabilitiesGroup());
1102             (<ComponentInstance>this.instances[currentInstanceIndex]).capabilities = instanceCapabilitiesData;
1103         });
1104     };
1105
1106     reverseChangedData = ():void => {
1107         // make reverse item handler
1108         let handleReverseItem;
1109         if (this.isPropertiesTabSelected) {
1110             handleReverseItem = (changedItem) => {
1111                 changedItem = <PropertyFEModel>changedItem;
1112                 this.propertiesUtils.resetPropertyValue(changedItem, changedItem.value);
1113             };
1114         } else if (this.isInputsTabSelected) {
1115             handleReverseItem = (changedItem) => {
1116                 changedItem = <InputFEModel>changedItem;
1117                 this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue);
1118                 changedItem.resetMetadata();
1119                 changedItem.required = changedItem.requiredOrig;
1120             };
1121         }
1122
1123         this.changedData.forEach(handleReverseItem);
1124         this.changedData = [];
1125         this.updateHasChangedData();
1126     };
1127
1128     updateHasChangedData = ():boolean => {
1129         const curHasChangedData:boolean = (this.changedData.length > 0);
1130         if (curHasChangedData !== this.hasChangedData) {
1131             this.hasChangedData = curHasChangedData;
1132             if(this.hasChangedData) {
1133                 this.eventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert);
1134             } else {
1135                 this.eventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
1136             }
1137         } 
1138         return this.hasChangedData;
1139     };
1140
1141     doSaveChangedData = (onSuccessFunction?:Function, onError?:Function):void => {
1142         this.saveChangedData().then(
1143             () => {
1144                 this.notification.success({
1145                     message: 'Successfully saved changes',
1146                     title: 'Saved'
1147                 });
1148                 if(onSuccessFunction) onSuccessFunction();
1149             },
1150             () => {
1151                 this.notification.error({
1152                     message: 'Failed to save changes!',
1153                     title: 'Failure'
1154                 });
1155                 if(onError) onError();
1156             }
1157         );
1158     };
1159
1160     showUnsavedChangesAlert = ():Promise<any> => {
1161         let modalTitle:string;
1162         if (this.isPropertiesTabSelected) {
1163             modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`;
1164         } else if (this.isInputsTabSelected) {
1165             modalTitle = `Unsaved inputs for ${this.component.name}`;
1166         }
1167
1168         return new Promise<any>((resolve, reject) => {
1169             const modal = this.ModalServiceSdcUI.openCustomModal(
1170                 {
1171                     title: modalTitle,
1172                     size: 'sm',
1173                     type: SdcUiCommon.ModalType.custom,
1174                     testId: "navigate-modal",
1175
1176                     buttons: [
1177                         {id: 'cancelButton', text: 'Cancel', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => reject()},
1178                         {id: 'discardButton', text: 'Discard', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => { this.reverseChangedData(); resolve()}},
1179                         {id: 'saveButton', text: 'Save', type: SdcUiCommon.ButtonType.primary, size: 'xsm', closeModal: true, disabled: !this.isValidChangedData, callback: () => this.doSaveChangedData(resolve, reject)}
1180                     ] as SdcUiCommon.IModalButtonComponent[]
1181                 } as SdcUiCommon.IModalConfig, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData});
1182         });
1183
1184     }
1185
1186     updatePropertyValueAfterDeclare = (input: InputFEModel) => {
1187         if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
1188             const instanceName = input.instanceUniqueId.slice(input.instanceUniqueId.lastIndexOf('.') + 1);
1189             const propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => {
1190                 return feProperty.name == input.relatedPropertyName &&
1191                     (feProperty.name == input.relatedPropertyName || input.name === instanceName.concat('_').concat(feProperty.name.replace(/[.]/g, '_')));
1192             });
1193             const inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined;
1194             propertyForUpdatindVal.setAsDeclared(inputPath); //set prop as declared before assigning value
1195             this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, inputPath);
1196             this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, input.relatedPropertyValue, inputPath);
1197         }
1198     }
1199
1200     //used for declare button, to keep count of newly checked properties (and ignore declared properties)
1201     updateCheckedPropertyCount = (increment: boolean): void => {
1202         this.checkedPropertiesCount += (increment) ? 1 : -1;
1203         this.checkedToscaCount = 0;
1204         this.enableToscaFunction = false;
1205         console.debug("CheckedProperties count is now.... " + this.checkedPropertiesCount);
1206     };
1207
1208     updateCheckedChildPropertyCount = (increment: boolean): void => {
1209         this.checkedChildPropertiesCount += (increment) ? 1 : -1;
1210     };
1211
1212     togggleToscaBtn = (toscaFlag: boolean) : void => {
1213         this.checkedToscaCount += toscaFlag ? 1 : -1;
1214         if(this.checkedToscaCount == 1){
1215             this.enableToscaFunction = true;
1216         }else{
1217             this.enableToscaFunction = false;
1218         }
1219     };
1220
1221     setInputTabIndication = (numInputs: number): void => {
1222         this.propertyInputTabs.setTabIndication('Inputs', numInputs);
1223     };
1224
1225     setPolicyTabIndication = (numPolicies: number): void => {
1226         this.propertyInputTabs.setTabIndication('Policies', numPolicies);
1227     }
1228
1229     resetUnsavedChangesForInput = (input:InputFEModel) => {
1230         this.inputsUtils.resetInputDefaultValue(input, input.defaultValue);
1231         this.changedData = this.changedData.filter((changedItem) => changedItem.uniqueId !== input.uniqueId);
1232         this.updateHasChangedData();
1233     }
1234
1235     deleteInput = (input: InputFEModel) => {
1236         //reset any unsaved changes to the input before deleting it
1237         this.resetUnsavedChangesForInput(input);
1238
1239         console.debug("==>" + this.constructor.name + ": deleteInput");
1240         let inputToDelete = new InputBEModel(input);
1241
1242         this.componentServiceNg2
1243             .deleteInput(this.component, inputToDelete)
1244             .subscribe(response => {
1245                 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
1246
1247                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
1248                 this.changeSelectedInstance(this.selectedInstanceData);
1249                 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
1250
1251                 // if (instanceFeProperties) {
1252                 //     let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
1253                 //         return prop.name == input.propertyName;
1254                 //     });
1255
1256                 //     if (propToEnable) {
1257                 //         if (propToEnable.name == response.inputPath) response.inputPath = null;
1258                 //         propToEnable.setNonDeclared(response.inputPath);
1259                 //         //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
1260                 //         this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
1261                 //     }
1262                 // }
1263             }, error => {}); //ignore error
1264     };
1265
1266     deletePolicy = (policy: PolicyInstance) => {
1267         this.loadingPolicies = true;
1268         this.topologyTemplateService
1269             .deletePolicy(this.component, policy)
1270             .subscribe((response) => {
1271                 this.policies = this.policies.filter(policy => policy.uniqueId !== response.uniqueId);
1272                 this.changeSelectedInstance(this.selectedInstanceData);
1273                 this.loadingPolicies = false;
1274             });
1275     };
1276
1277     deleteProperty = (property: PropertyFEModel) => {
1278         const propertyToDelete = new PropertyFEModel(property);
1279         this.loadingProperties = true;
1280         const feMap = this.instanceFePropertiesMap;
1281         this.topologyTemplateService
1282             .deleteServiceProperty(this.component.uniqueId, propertyToDelete)
1283             .subscribe((response) => {
1284                 const props = feMap[this.component.uniqueId];
1285                 props.splice(props.findIndex(p => p.uniqueId === response),1);
1286                 this.loadingProperties = false;
1287             }, (error) => {
1288                 this.loadingProperties = false;
1289                 console.error(error);
1290             });
1291     }
1292
1293     /*** addProperty ***/
1294     addProperty = (model: string) => {
1295         this.loadDataTypesByComponentModel(model)
1296         let modalTitle = 'Add Property';
1297         let modal = this.modalService.createCustomModal(new ModalModel(
1298             'sm',
1299             modalTitle,
1300             null,
1301             [
1302                 new ButtonModel('Save', 'blue', () => {
1303                     modal.instance.dynamicContent.instance.isLoading = true;
1304                     const newProperty: PropertyBEModel = modal.instance.dynamicContent.instance.propertyModel;
1305                     this.topologyTemplateService.createServiceProperty(this.component.uniqueId, newProperty)
1306                         .subscribe((response) => {
1307                             modal.instance.dynamicContent.instance.isLoading = false;
1308                             const newProp: PropertyFEModel = this.propertiesUtils.convertAddPropertyBAToPropertyFE(response);
1309                             this.instanceFePropertiesMap[this.component.uniqueId].push(newProp);
1310                             modal.instance.close();
1311                         }, (error) => {
1312                             modal.instance.dynamicContent.instance.isLoading = false;
1313                             this.notification.error({
1314                                 message: 'Failed to add property:' + error,
1315                                 title: 'Failure'
1316                             });
1317                         });
1318                 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
1319                 new ButtonModel('Cancel', 'outline grey', () => {
1320                     modal.instance.close();
1321                 }),
1322             ],
1323             null
1324         ));
1325         modal.instance.open();
1326         this.modalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
1327     }
1328
1329     /*** addInput ***/
1330     addInput = () => {
1331         let modalTitle = 'Add Input';
1332         let modal = this.modalService.createCustomModal(new ModalModel(
1333             'sm',
1334             modalTitle,
1335             null,
1336             [
1337                 new ButtonModel('Save', 'blue', () => {
1338                     modal.instance.dynamicContent.instance.isLoading = true;
1339                     const newInput: InputBEModel = modal.instance.dynamicContent.instance.propertyModel;
1340                     this.topologyTemplateService.createServiceInput(this.component.uniqueId, newInput)
1341                         .subscribe((response) => {
1342                             modal.instance.dynamicContent.instance.isLoading = false;
1343                             const newInputProp: InputFEModel = this.inputsUtils.convertInputBEToInputFE(response);
1344                             this.inputs.push(newInputProp);
1345                             modal.instance.close();
1346                         }, (error) => {
1347                             modal.instance.dynamicContent.instance.isLoading = false;
1348                             this.notification.error({
1349                                 message: 'Failed to add input:' + error,
1350                                 title: 'Failure'
1351                             });
1352                         });
1353                 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
1354                 new ButtonModel('Cancel', 'outline grey', () => {
1355                     modal.instance.close();
1356                 }),
1357             ],
1358             null
1359         ));
1360         this.modalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
1361         modal.instance.open();
1362     }
1363
1364     /*** SEARCH RELATED FUNCTIONS ***/
1365     searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
1366         let instanceBePropertiesMap:InstanceBePropertiesMap;
1367         this.componentServiceNg2
1368             .filterComponentInstanceProperties(this.component, filterData)
1369             .subscribe((response) => {
1370                 this.processInstancePropertiesResponse(response, false);
1371                 this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
1372                 this.searchPropertyName = filterData.propertyName;//mark in table
1373                 this.hierarchyNavTabs.triggerTabChange('Composition');
1374                 this.propertiesNavigationData = [];
1375                 this.displayClearSearch = true;
1376             }, (error) => {}); //ignore error
1377
1378     }
1379
1380     clearSearch = () => {
1381         this.instancesNavigationData = this.instances;
1382         this.searchPropertyName = "";
1383         this.hierarchyPropertiesDisplayOptions.searchText = "";
1384         this.displayClearSearch = false;
1385         this.advanceSearch.clearAll();
1386         this.searchQuery = '';
1387     };
1388
1389     clickOnClearSearch = () => {
1390         this.clearSearch();
1391         this.selectFirstInstanceByDefault();
1392         this.hierarchyNavTabs.triggerTabChange('Composition');
1393     };
1394
1395     private isInput = (instanceType:string):boolean =>{
1396         return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;
1397     }
1398
1399     loadDataTypesByComponentModel(model:string) {
1400         this.propertyCreatorComponent.filterDataTypesByModel(model);
1401     }
1402
1403 }