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