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