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