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