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