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