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