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