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