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