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