fix bug: [SDC-2275]Service properties - issue on save multiple properties
[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, ViewChild, Inject, TemplateRef} from "@angular/core";
23 import { PropertiesService } from "../../services/properties.service";
24 import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData, ModalModel, ButtonModel } from "app/models";
25 import { ResourceType } from "app/utils";
26 import {ComponentServiceNg2} from "../../services/component-services/component.service";
27 import {ComponentInstanceServiceNg2} from "../../services/component-instance-services/component-instance.service"
28 import { InputBEModel, InputFEModel, ComponentInstance, GroupInstance, PolicyInstance, PropertyBEModel, DerivedFEProperty, SimpleFlatProperty } from "app/models";
29 import { KeysPipe } from 'app/ng2/pipes/keys.pipe';
30 import {WorkspaceMode, EVENTS} from "../../../utils/constants";
31 import {EventListenerService} from "app/services/event-listener-service"
32 import {HierarchyDisplayOptions} from "../../components/logic/hierarchy-navigtion/hierarchy-display-options";
33 import {FilterPropertiesAssignmentComponent} from "../../components/logic/filter-properties-assignment/filter-properties-assignment.component";
34 import {PropertyRowSelectedEvent} from "../../components/logic/properties-table/properties-table.component";
35 import {HierarchyNavService} from "./services/hierarchy-nav.service";
36 import {PropertiesUtils} from "./services/properties.utils";
37 import {ComponentModeService} from "../../services/component-services/component-mode.service";
38 import {ModalService} from "../../services/modal.service";
39 import {Tabs, Tab} from "../../components/ui/tabs/tabs.component";
40 import {InputsUtils} from "./services/inputs.utils";
41 import {PropertyCreatorComponent} from "./property-creator/property-creator.component";
42 import { InstanceFeDetails } from "../../../models/instance-fe-details";
43 import { SdcUiComponents } from "sdc-ui/lib/angular";
44 //import { ModalService as ModalServiceSdcUI} from "sdc-ui/lib/angular/modals/modal.service";
45 import { IModalButtonComponent } from "sdc-ui/lib/angular/modals/models/modal-config";
46 import { UnsavedChangesComponent } from "app/ng2/components/ui/forms/unsaved-changes/unsaved-changes.component";
47
48 const SERVICE_SELF_TITLE = "SELF";
49 @Component({
50     templateUrl: './properties-assignment.page.component.html',
51     styleUrls: ['./properties-assignment.page.component.less']
52 })
53 export class PropertiesAssignmentComponent {
54     title = "Properties & Inputs";
55
56     component: ComponentData;
57     componentInstanceNamesMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>();//instanceUniqueId, {name, iconClass}
58
59     propertiesNavigationData = [];
60     instancesNavigationData = [];
61
62     instanceFePropertiesMap:InstanceFePropertiesMap;
63     inputs: Array<InputFEModel> = [];
64     policies: Array<PolicyInstance> = [];
65     instances: Array<ComponentInstance|GroupInstance|PolicyInstance> = [];
66     searchQuery: string;
67     propertyStructureHeader: string;
68
69     selectedFlatProperty: SimpleFlatProperty = new SimpleFlatProperty();
70     selectedInstanceData: ComponentInstance|GroupInstance|PolicyInstance = null;
71     checkedPropertiesCount: number = 0;
72
73     hierarchyPropertiesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('path', 'name', 'childrens');
74     hierarchyInstancesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name', 'archived', null, 'iconClass');
75     displayClearSearch = false;
76     searchPropertyName:string;
77     currentMainTab:Tab;
78     isInputsTabSelected:boolean;
79     isPropertiesTabSelected:boolean;
80     isPoliciesTabSelected:boolean;
81     isReadonly:boolean;
82     resourceIsReadonly:boolean;
83     loadingInstances:boolean = false;
84     loadingInputs:boolean = false;
85     loadingPolicies:boolean = false;
86     loadingProperties:boolean = false;
87     changedData:Array<PropertyFEModel|InputFEModel>;
88     hasChangedData:boolean;
89     isValidChangedData:boolean;
90     savingChangedData:boolean;
91     stateChangeStartUnregister:Function;
92     serviceBePropertiesMap: InstanceBePropertiesMap;
93
94     @ViewChild('hierarchyNavTabs') hierarchyNavTabs: Tabs;
95     @ViewChild('propertyInputTabs') propertyInputTabs: Tabs;
96     @ViewChild('advanceSearch') advanceSearch: FilterPropertiesAssignmentComponent;
97    
98     constructor(private propertiesService: PropertiesService,
99                 private hierarchyNavService: HierarchyNavService,
100                 private propertiesUtils:PropertiesUtils,
101                 private inputsUtils:InputsUtils,
102                 private componentServiceNg2:ComponentServiceNg2,
103                 private componentInstanceServiceNg2:ComponentInstanceServiceNg2,
104                 @Inject("$stateParams") _stateParams,
105                 @Inject("$scope") private $scope:ng.IScope,
106                 @Inject("$state") private $state:ng.ui.IStateService,
107                 @Inject("Notification") private Notification:any,
108                 private componentModeService:ComponentModeService,
109                 private ModalService:ModalService,
110                 private EventListenerService:EventListenerService,
111                 private ModalServiceSdcUI: SdcUiComponents.ModalService) {
112
113         this.instanceFePropertiesMap = new InstanceFePropertiesMap();
114
115         /* 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
116         than if the data is already exist, no need to call the api again - Ask orit if you have any questions*/
117         this.component = _stateParams.component;
118         this.EventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE, this.onCheckout);
119         this.updateViewMode();
120
121         this.changedData = [];
122         this.updateHasChangedData();
123         this.isValidChangedData = true;
124     }
125
126     ngOnInit() {
127         console.log("==>" + this.constructor.name + ": ngOnInit");
128         this.loadingInputs = true;
129         this.loadingPolicies = true;
130         this.loadingInstances = true;
131         this.loadingProperties = true;
132         this.componentServiceNg2
133             .getComponentInputsWithProperties(this.component)
134             .subscribe(response => {
135                 _.forEach(response.inputs, (input: InputBEModel) => {
136                     const newInput: InputFEModel = new InputFEModel(input);
137                     this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
138                     this.inputs.push(newInput); //only push items that were declared via SDC
139                 });
140                 this.loadingInputs = false;
141
142             });
143         this.componentServiceNg2
144             .getComponentResourcePropertiesData(this.component)
145             .subscribe(response => {
146                 this.loadingPolicies = false;
147                 this.instances = [];
148                 this.instances.push(...response.componentInstances);
149                 this.instances.push(...response.groupInstances);
150
151                 _.forEach(response.policies, (policy: any) => {
152                     const newPolicy: InputFEModel = new InputFEModel(policy);
153                     this.inputsUtils.resetInputDefaultValue(newPolicy, policy.defaultValue);
154                     this.policies.push(policy);
155                 });
156
157                 // add the service self instance to the top of the list.
158                 const serviceInstance = new ComponentInstance();
159                 serviceInstance.name = SERVICE_SELF_TITLE;
160                 serviceInstance.uniqueId = this.component.uniqueId;
161                 this.instances.unshift(serviceInstance);
162
163                 _.forEach(this.instances, (instance) => {
164                     this.instancesNavigationData.push(instance);
165                     this.componentInstanceNamesMap[instance.uniqueId] = <InstanceFeDetails>{name: instance.name, iconClass:instance.iconClass, originArchived:instance.originArchived};
166                 });
167                 this.loadingInstances = false;
168                 if (this.instancesNavigationData[0] == undefined) {
169                     this.loadingProperties = false;
170                 }
171                 this.selectFirstInstanceByDefault();
172             });
173
174         this.stateChangeStartUnregister = this.$scope.$on('$stateChangeStart', (event, toState, toParams) => {
175             // stop if has changed properties
176             if (this.hasChangedData) {
177                 event.preventDefault();
178                 this.showUnsavedChangesAlert().then(() => {
179                     this.$state.go(toState, toParams);
180                 }, () => {});
181             }
182         });
183     };
184
185     ngOnDestroy() {
186         this.EventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE);
187         this.stateChangeStartUnregister();
188     }
189
190     selectFirstInstanceByDefault = () => {
191         if (this.instancesNavigationData[0] !== undefined) {
192             this.onInstanceSelectedUpdate(this.instancesNavigationData[0]);
193         }
194     };
195
196     updateViewMode = () => {
197         this.isReadonly = this.componentModeService.getComponentMode(this.component) === WorkspaceMode.VIEW;
198     }
199
200     onCheckout = (component:ComponentData) => {
201         this.component = component;
202         this.updateViewMode();
203     }
204
205     isSelf = ():boolean => {
206         return this.selectedInstanceData && this.selectedInstanceData.uniqueId == this.component.uniqueId;
207     }
208
209     getServiceProperties(){
210         this.loadingProperties = false;
211         this.componentServiceNg2
212             .getServiceProperties(this.component)
213             .subscribe(response => {
214                 this.serviceBePropertiesMap = new InstanceBePropertiesMap();
215                 this.serviceBePropertiesMap[this.component.uniqueId] = response;
216                 this.processInstancePropertiesResponse(this.serviceBePropertiesMap, false);
217                 this.loadingProperties = false;
218             }, error => {
219                 this.loadingProperties = false;
220             });
221     }
222
223     onInstanceSelectedUpdate = (instance: ComponentInstance|GroupInstance|PolicyInstance) => {
224         // stop if has changed properties
225         if (this.hasChangedData) {
226             this.showUnsavedChangesAlert().then((resolve)=> {
227                 this.changeSelectedInstance(instance)
228             }, (reject) => {
229             });
230             return;
231         }
232         this.changeSelectedInstance(instance);
233     };
234
235     changeSelectedInstance =  (instance: ComponentInstance|GroupInstance|PolicyInstance) => {
236         this.selectedInstanceData = instance;
237         this.loadingProperties = true;
238         if (instance instanceof ComponentInstance) {
239             let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
240             if (this.isInput(instance.originType)) {
241                 this.componentInstanceServiceNg2
242                     .getComponentInstanceInputs(this.component, instance)
243                     .subscribe(response => {
244                         instanceBePropertiesMap[instance.uniqueId] = response;
245                         this.processInstancePropertiesResponse(instanceBePropertiesMap, true);
246                         this.loadingProperties = false;
247                     }, error => {
248                     }); //ignore error
249             } else if (this.isSelf()) {
250                 this.getServiceProperties();
251             } else {
252                 this.componentInstanceServiceNg2
253                     .getComponentInstanceProperties(this.component, instance.uniqueId)
254                     .subscribe(response => {
255                         instanceBePropertiesMap[instance.uniqueId] = response;
256                         this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
257                         this.loadingProperties = false;
258                     }, error => {
259                     }); //ignore error
260             }
261
262             this.resourceIsReadonly = (instance.componentName === "vnfConfiguration");
263         } else if (instance instanceof GroupInstance) {
264             let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
265             this.componentInstanceServiceNg2
266                 .getComponentGroupInstanceProperties(this.component, this.selectedInstanceData.uniqueId)
267                 .subscribe((response) => {
268                     instanceBePropertiesMap[instance.uniqueId] = response;
269                     this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
270                     this.loadingProperties = false;
271                 });
272         } else if (instance instanceof PolicyInstance) {
273             let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
274             this.componentInstanceServiceNg2
275                 .getComponentPolicyInstanceProperties(this.component, this.selectedInstanceData.uniqueId)
276                 .subscribe((response) => {
277                     instanceBePropertiesMap[instance.uniqueId] = response;
278                     this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
279                     this.loadingProperties = false;
280                 });
281         } else {
282             this.loadingProperties = false;
283         }
284
285         if (this.searchPropertyName) {
286             this.clearSearch();
287         }
288         //clear selected property from the navigation
289         this.selectedFlatProperty = new SimpleFlatProperty();
290         this.propertiesNavigationData = [];
291     };
292
293     /**
294      * Entry point handling response from server
295      */
296     processInstancePropertiesResponse = (instanceBePropertiesMap: InstanceBePropertiesMap, originTypeIsVF: boolean) => {
297         this.instanceFePropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(instanceBePropertiesMap, originTypeIsVF, this.inputs); //create flattened children, disable declared props, and init values
298         this.checkedPropertiesCount = 0;
299     };
300
301
302     /*** VALUE CHANGE EVENTS ***/
303     dataChanged = (item:PropertyFEModel|InputFEModel) => {
304         let itemHasChanged;
305         if (this.isPropertiesTabSelected && item instanceof PropertyFEModel) {
306             itemHasChanged = item.hasValueObjChanged();
307         } else if (this.isInputsTabSelected && item instanceof InputFEModel) {
308             itemHasChanged = item.hasDefaultValueChanged();
309         } else if (this.isPoliciesTabSelected && item instanceof InputFEModel) {
310             itemHasChanged = item.hasDefaultValueChanged();
311         }
312
313         const dataChangedIdx = this.changedData.findIndex((changedItem) => changedItem === item);
314         if (itemHasChanged) {
315             if (dataChangedIdx === -1) {
316                 this.changedData.push(item);
317             }
318         } else {
319             if (dataChangedIdx !== -1) {
320                 this.changedData.splice(dataChangedIdx, 1);
321             }
322         }
323
324         if (this.isPropertiesTabSelected) {
325             this.isValidChangedData = this.changedData.every((changedItem) => (<PropertyFEModel>changedItem).valueObjIsValid);
326         } else if (this.isInputsTabSelected || this.isPoliciesTabSelected) {
327             this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid);
328         }
329         this.updateHasChangedData();
330     };
331
332
333     /*** HEIRARCHY/NAV RELATED FUNCTIONS ***/
334
335     /**
336      * Handle select node in navigation area, and select the row in table
337      */
338     onPropertySelectedUpdate = ($event) => {
339         console.log("==>" + this.constructor.name + ": onPropertySelectedUpdate");
340         this.selectedFlatProperty = $event;
341         let parentProperty:PropertyFEModel = this.propertiesService.getParentPropertyFEModelFromPath(this.instanceFePropertiesMap[this.selectedFlatProperty.instanceName], this.selectedFlatProperty.path);
342         parentProperty.expandedChildPropertyId = this.selectedFlatProperty.path;
343     };
344
345     /**
346      * When user select row in table, this will prepare the hirarchy object for the tree.
347      */
348     selectPropertyRow = (propertyRowSelectedEvent:PropertyRowSelectedEvent) => {
349         console.log("==>" + this.constructor.name + ": selectPropertyRow " + propertyRowSelectedEvent.propertyModel.name);
350         let property = propertyRowSelectedEvent.propertyModel;
351         let instanceName = propertyRowSelectedEvent.instanceName;
352         this.propertyStructureHeader = null;
353
354         // Build hirarchy tree for the navigation and update propertiesNavigationData with it.
355         if (!(this.selectedInstanceData instanceof ComponentInstance) || this.selectedInstanceData.originType !== ResourceType.VF) {
356             let simpleFlatProperty:Array<SimpleFlatProperty>;
357             if (property instanceof PropertyFEModel) {
358                 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(property, instanceName);
359             } else if (property instanceof DerivedFEProperty) {
360                 // Need to find parent PropertyFEModel
361                 let parentPropertyFEModel:PropertyFEModel = _.find(this.instanceFePropertiesMap[instanceName], (tmpFeProperty):boolean => {
362                     return property.propertiesName.indexOf(tmpFeProperty.name)===0;
363                 });
364                 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(parentPropertyFEModel, instanceName);
365             }
366             this.propertiesNavigationData = simpleFlatProperty;
367         }
368
369         // Update the header in the navigation tree with property name.
370         this.propertyStructureHeader = (property.propertiesName.split('#'))[0];
371
372         // Set selected property in table
373         this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName);
374         this.hierarchyNavTabs.triggerTabChange('Property Structure');
375     };
376
377
378     selectInstanceRow = ($event) => {//get instance name
379         this.selectedInstanceData =  _.find(this.instancesNavigationData, (instance:ComponentInstance) => {
380             return instance.name == $event;
381         });
382         this.hierarchyNavTabs.triggerTabChange('Composition');
383     };
384
385     tabChanged = (event) => {
386         // stop if has changed properties
387         if (this.hasChangedData) {
388             this.propertyInputTabs.triggerTabChange(this.currentMainTab.title);
389             this.showUnsavedChangesAlert().then((proceed) => {
390                 this.propertyInputTabs.selectTab(this.propertyInputTabs.tabs.find((tab) => tab.title === event.title));
391             }, ()=> {
392             });
393             return;
394         }
395
396         console.log("==>" + this.constructor.name + ": tabChanged " + event);
397         this.currentMainTab = this.propertyInputTabs.tabs.find((tab) => tab.title === event.title);
398         this.isPropertiesTabSelected = this.currentMainTab.title === "Properties";
399         this.isInputsTabSelected = this.currentMainTab.title === "Inputs";
400         this.isPoliciesTabSelected = this.currentMainTab.title === "Policies";
401         this.propertyStructureHeader = null;
402         this.searchQuery = '';
403     };
404
405
406
407     /*** DECLARE PROPERTIES/INPUTS ***/
408     declareProperties = (): void => {
409         console.log("==>" + this.constructor.name + ": declareProperties");
410
411         let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
412         let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
413         let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
414         let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
415         let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
416
417         angular.forEach(instancesIds, (instanceId: string): void => {
418             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
419             if (selectedInstanceData instanceof ComponentInstance) {
420                 if (!this.isInput(selectedInstanceData.originType)) {
421                     selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
422                 } else {
423                     selectedComponentInstancesInputs[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
424                 }
425             } else if (selectedInstanceData instanceof GroupInstance) {
426                 selectedGroupInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
427             } else if (selectedInstanceData instanceof PolicyInstance) {
428                 selectedPolicyInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
429             }
430         });
431
432         let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
433
434         this.componentServiceNg2
435             .createInput(this.component, inputsToCreate, this.isSelf())
436             .subscribe(response => {
437                 this.setInputTabIndication(response.length);
438                 this.checkedPropertiesCount = 0;
439                 _.forEach(response, (input: InputBEModel) => {
440                     let newInput: InputFEModel = new InputFEModel(input);
441                     this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
442                     this.inputs.push(newInput);
443                     this.updatePropertyValueAfterDeclare(newInput);
444                 });
445             }, error => {}); //ignore error
446     };
447
448     /*** DECLARE PROPERTIES/POLICIES ***/
449     declarePropertiesToPolicies = (): void => {
450         let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
451         let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
452
453         angular.forEach(instancesIds, (instanceId: string): void => {
454             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
455             if (selectedInstanceData instanceof ComponentInstance) {
456                 if (!this.isInput(selectedInstanceData.originType)) {
457                     selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
458                 }
459             }
460         });
461
462         let policiesToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(null, selectedComponentInstancesProperties, null, null);
463         this.loadingPolicies = true;
464
465         this.componentServiceNg2
466             .createPolicy(this.component, policiesToCreate, this.isSelf())
467             .subscribe(response => {
468                 this.setPolicyTabIndication(response.length);
469                 this.checkedPropertiesCount = 0;
470                 this.displayPoliciesAsDeclared(response);
471                 this.loadingPolicies = false;
472             }); //ignore error
473
474     };
475
476     displayPoliciesAsDeclared = (policies) => {
477         _.forEach(policies, (policy: any) => {
478             let newPolicy: InputFEModel = new InputFEModel(policy);
479             this.inputsUtils.resetInputDefaultValue(newPolicy, policy.defaultValue);
480             newPolicy.relatedPropertyName = policy.name;
481             newPolicy.relatedPropertyValue = policy.value;
482             this.updatePropertyValueAfterDeclare(newPolicy);
483             this.policies.push(policy);
484         });
485     };
486
487
488     saveChangedData = ():Promise<(PropertyBEModel|InputBEModel)[]> => {
489         return new Promise((resolve, reject) => {
490             if (!this.isValidChangedData) {
491                 reject('Changed data is invalid - cannot save!');
492                 return;
493             }
494             if (!this.changedData.length) {
495                 resolve([]);
496                 return;
497             }
498
499             // make request and its handlers
500             let request;
501             let handleSuccess, handleError;
502             if (this.isPropertiesTabSelected) {
503                 const changedProperties: PropertyBEModel[] = this.changedData.map((changedProp) => {
504                     changedProp = <PropertyFEModel>changedProp;
505                     const propBE = new PropertyBEModel(changedProp);
506                     propBE.value = changedProp.getJSONValue();
507                     return propBE;
508                 });
509
510                 if (this.selectedInstanceData instanceof ComponentInstance) {
511                     if (this.isInput(this.selectedInstanceData.originType)) {
512                         request = this.componentInstanceServiceNg2
513                             .updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedProperties);
514                         handleSuccess = (response) => {
515                             // reset each changed property with new value and remove it from changed properties list
516                             response.forEach((resInput) => {
517                                 const changedProp = <PropertyFEModel>this.changedData.shift();
518                                 this.propertiesUtils.resetPropertyValue(changedProp, resInput.value);
519                             });
520                             console.log('updated instance inputs:', response);
521                         };
522                     } else {
523                         if (this.isSelf()) {
524                             request = this.componentServiceNg2.updateServiceProperties(this.component, _.map(changedProperties, cp => {
525                                 delete cp.constraints;
526                                 return cp;
527                             }));
528                         } else {
529                             request = this.componentInstanceServiceNg2
530                                 .updateInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties);
531                         }
532                         handleSuccess = (response) => {
533                             // reset each changed property with new value and remove it from changed properties list
534                             response.forEach((resProp) => {
535                                 const changedProp = <PropertyFEModel>_.find(this.changedData, changedDataObject => changedDataObject.uniqueId === resProp.uniqueId);
536                                 this.changedData = _.filter(this.changedData, changedDataObject => changedDataObject.uniqueId !== resProp.uniqueId);
537                                 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
538                             });
539                             resolve(response);
540                             console.log("updated instance properties: ", response);
541                         };
542                     }
543                 } else if (this.selectedInstanceData instanceof GroupInstance) {
544                     request = this.componentInstanceServiceNg2
545                         .updateComponentGroupInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties);
546                     handleSuccess = (response) => {
547                         // reset each changed property with new value and remove it from changed properties list
548                         response.forEach((resProp) => {
549                             const changedProp = <PropertyFEModel>this.changedData.shift();
550                             this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
551                         });
552                         resolve(response);
553                         console.log("updated group instance properties: ", response);
554                     };
555                 } else if (this.selectedInstanceData instanceof PolicyInstance) {
556                     request = this.componentInstanceServiceNg2
557                         .updateComponentPolicyInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties);
558                     handleSuccess = (response) => {
559                         // reset each changed property with new value and remove it from changed properties list
560                         response.forEach((resProp) => {
561                             const changedProp = <PropertyFEModel>this.changedData.shift();
562                             this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
563                         });
564                         resolve(response);
565                         console.log("updated policy instance properties: ", response);
566                     };
567                 }
568             } else if (this.isInputsTabSelected) {
569                 const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => {
570                     changedInput = <InputFEModel>changedInput;
571                     const inputBE = new InputBEModel(changedInput);
572                     inputBE.defaultValue = changedInput.getJSONDefaultValue();
573                     return inputBE;
574                 });
575                 request = this.componentServiceNg2
576                     .updateComponentInputs(this.component, changedInputs);
577                 handleSuccess = (response) => {
578                     // reset each changed property with new value and remove it from changed properties list
579                     response.forEach((resInput) => {
580                         const changedInput = <InputFEModel>this.changedData.shift();
581                         this.inputsUtils.resetInputDefaultValue(changedInput, resInput.defaultValue);
582                     });
583                     console.log("updated the component inputs and got this response: ", response);
584                 }
585             }
586
587             this.savingChangedData = true;
588             request.subscribe(
589                 (response) => {
590                     this.savingChangedData = false;
591                     handleSuccess && handleSuccess(response);
592                     this.updateHasChangedData();
593                     resolve(response);
594                 },
595                 (error) => {
596                     this.savingChangedData = false;
597                     handleError && handleError(error);
598                     this.updateHasChangedData();
599                     reject(error);
600                 }
601             );
602         });
603     };
604
605     reverseChangedData = ():void => {
606         // make reverse item handler
607         let handleReverseItem;
608         if (this.isPropertiesTabSelected) {
609             handleReverseItem = (changedItem) => {
610                 changedItem = <PropertyFEModel>changedItem;
611                 this.propertiesUtils.resetPropertyValue(changedItem, changedItem.value);
612             };
613         } else if (this.isInputsTabSelected) {
614             handleReverseItem = (changedItem) => {
615                 changedItem = <InputFEModel>changedItem;
616                 this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue);
617             };
618         }
619
620         this.changedData.forEach(handleReverseItem);
621         this.changedData = [];
622         this.updateHasChangedData();
623     };
624
625     updateHasChangedData = ():boolean => {
626         const curHasChangedData:boolean = (this.changedData.length > 0);
627         if (curHasChangedData !== this.hasChangedData) {
628             this.hasChangedData = curHasChangedData;
629             if(this.hasChangedData) {
630                 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert);
631             } else {
632                 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
633             }
634         } 
635         return this.hasChangedData;
636     };
637
638     doSaveChangedData = (onSuccessFunction?:Function, onError?:Function):void => {
639         this.saveChangedData().then(
640             () => {
641                 this.Notification.success({
642                     message: 'Successfully saved changes',
643                     title: 'Saved'
644                 });
645                 if(onSuccessFunction) onSuccessFunction();
646             },
647             () => {
648                 this.Notification.error({
649                     message: 'Failed to save changes!',
650                     title: 'Failure'
651                 });
652                 if(onError) onError();
653             }
654         );
655     };
656
657     showUnsavedChangesAlert = ():Promise<any> => {
658         let modalTitle:string;
659         if (this.isPropertiesTabSelected) {
660             modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`;
661         } else if (this.isInputsTabSelected) {
662             modalTitle = `Unsaved inputs for ${this.component.name}`;
663         }
664
665         return new Promise<any>((resolve, reject) => {
666             const modal = this.ModalServiceSdcUI.openCustomModal(
667                 {
668                     title: modalTitle,
669                     size: 'sm',
670                     type: 'custom',
671                     testId: "id",
672                     
673                     buttons: [
674                         {id: 'cancelButton', text: 'Cancel', type: 'secondary', size: 'xsm', closeModal: true, callback: () => reject()},
675                         {id: 'discardButton', text: 'Discard', type: 'secondary', size: 'xsm', closeModal: true, callback: () => { this.reverseChangedData(); resolve()}},
676                         {id: 'saveButton', text: 'Save', type: 'primary', size: 'xsm', closeModal: true, disabled: !this.isValidChangedData, callback: () => this.doSaveChangedData(resolve, reject)}
677                 ] as IModalButtonComponent[]
678             }, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData});
679         });
680
681     }
682
683     updatePropertyValueAfterDeclare = (input: InputFEModel) => {
684         if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
685             let propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => {
686                 return feProperty.name == input.relatedPropertyName;
687             });
688             let inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined;
689             propertyForUpdatindVal.setAsDeclared(inputPath); //set prop as declared before assigning value
690             this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, inputPath);
691             this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, input.relatedPropertyValue, inputPath);
692         }
693     }
694
695     //used for declare button, to keep count of newly checked properties (and ignore declared properties)
696     updateCheckedPropertyCount = (increment: boolean): void => {
697         this.checkedPropertiesCount += (increment) ? 1 : -1;
698         console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount);
699     };
700
701     setInputTabIndication = (numInputs: number): void => {
702         this.propertyInputTabs.setTabIndication('Inputs', numInputs);
703     };
704
705     setPolicyTabIndication = (numPolicies: number): void => {
706         this.propertyInputTabs.setTabIndication('Policies', numPolicies);
707     };
708
709     resetUnsavedChangesForInput = (input:InputFEModel) => {
710         this.inputsUtils.resetInputDefaultValue(input, input.defaultValue);
711         this.changedData = this.changedData.filter((changedItem) => changedItem.uniqueId !== input.uniqueId);
712         this.updateHasChangedData();
713     }
714
715     deleteInput = (input: InputFEModel) => {
716         //reset any unsaved changes to the input before deleting it
717         this.resetUnsavedChangesForInput(input);
718
719         console.log("==>" + this.constructor.name + ": deleteInput");
720         let inputToDelete = new InputBEModel(input);
721
722         this.componentServiceNg2
723             .deleteInput(this.component, inputToDelete)
724             .subscribe(response => {
725                 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
726
727                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
728                 this.changeSelectedInstance(this.selectedInstanceData);
729                 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
730
731                 // if (instanceFeProperties) {
732                 //     let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
733                 //         return prop.name == input.propertyName;
734                 //     });
735
736                 //     if (propToEnable) {
737                 //         if (propToEnable.name == response.inputPath) response.inputPath = null;
738                 //         propToEnable.setNonDeclared(response.inputPath);
739                 //         //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
740                 //         this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
741                 //     }
742                 // }
743             }, error => {}); //ignore error
744     };
745
746     deletePolicy = (policy: PolicyInstance) => {
747         this.loadingPolicies = true;
748         this.componentServiceNg2
749             .deletePolicy(this.component, policy)
750             .subscribe(response => {
751                 this.policies = this.policies.filter(policy => policy.uniqueId !== response.uniqueId);
752                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
753                 this.changeSelectedInstance(this.selectedInstanceData);
754                 this.loadingPolicies = false;
755             });
756     };
757
758     deleteProperty = (property: PropertyFEModel) => {
759         let propertyToDelete = new PropertyFEModel(property);
760         this.loadingProperties = true;
761         let feMap = this.instanceFePropertiesMap;
762         this.componentServiceNg2
763             .deleteServiceProperty(this.component, propertyToDelete)
764             .subscribe(response => {
765                 const props = feMap[this.component.uniqueId];
766                 props.splice(props.findIndex(p => p.uniqueId === response),1);
767                 this.loadingProperties = false;
768             }, error => {
769                 this.loadingProperties = false;
770                 console.error(error);
771             });
772     };
773
774     /*** addProperty ***/
775     addProperty = () => {
776         let modalTitle = 'Add Property';
777         const modal = this.ModalService.createCustomModal(new ModalModel(
778             'sm',
779             modalTitle,
780             null,
781             [
782                 new ButtonModel('Save', 'blue', () => {
783                     modal.instance.dynamicContent.instance.isLoading = true;
784                     const newProperty: PropertyBEModel = modal.instance.dynamicContent.instance.propertyModel;
785                     this.componentServiceNg2.createServiceProperty(this.component, newProperty)
786                         .subscribe(response => {
787                             modal.instance.dynamicContent.instance.isLoading = false;
788                             let newProp: PropertyFEModel = this.propertiesUtils.convertAddPropertyBAToPropertyFE(response);
789                             this.instanceFePropertiesMap[this.component.uniqueId].push(newProp);
790                             modal.instance.close();
791                         }, (error) => {
792                             modal.instance.dynamicContent.instance.isLoading = false;
793                             this.Notification.error({
794                                 message: 'Failed to add property:' + error,
795                                 title: 'Failure'
796                             });
797                         });
798
799                 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
800                 new ButtonModel('Cancel', 'outline grey', () => {
801                     modal.instance.close();
802                 }),
803             ],
804             null
805         ));
806         this.ModalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
807         modal.instance.open();
808     };
809
810     /*** SEARCH RELATED FUNCTIONS ***/
811     searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
812         let instanceBePropertiesMap:InstanceBePropertiesMap;
813         this.componentServiceNg2
814             .filterComponentInstanceProperties(this.component, filterData)
815             .subscribe(response => {
816
817                 this.processInstancePropertiesResponse(response, false);
818                 this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
819                 this.searchPropertyName = filterData.propertyName;//mark in table
820                 this.hierarchyNavTabs.triggerTabChange('Composition');
821                 this.propertiesNavigationData = [];
822                 this.displayClearSearch = true;
823             }, error => {}); //ignore error
824
825     };
826
827     clearSearch = () => {
828         this.instancesNavigationData = this.instances;
829         this.searchPropertyName = "";
830         this.hierarchyPropertiesDisplayOptions.searchText = "";
831         this.displayClearSearch = false;
832         this.advanceSearch.clearAll();
833         this.searchQuery = '';
834     };
835
836     clickOnClearSearch = () => {
837         this.clearSearch();
838         this.selectFirstInstanceByDefault();
839         this.hierarchyNavTabs.triggerTabChange('Composition');
840     };
841
842     private isInput = (instanceType:string):boolean =>{
843         return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;
844     }
845
846 }