Declare properties as policies
[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>this.changedData.shift();
536                                 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
537                             });
538                             resolve(response);
539                             console.log("updated instance properties: ", response);
540                         };
541                     }
542                 } else if (this.selectedInstanceData instanceof GroupInstance) {
543                     request = this.componentInstanceServiceNg2
544                         .updateComponentGroupInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties);
545                     handleSuccess = (response) => {
546                         // reset each changed property with new value and remove it from changed properties list
547                         response.forEach((resProp) => {
548                             const changedProp = <PropertyFEModel>this.changedData.shift();
549                             this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
550                         });
551                         resolve(response);
552                         console.log("updated group instance properties: ", response);
553                     };
554                 } else if (this.selectedInstanceData instanceof PolicyInstance) {
555                     request = this.componentInstanceServiceNg2
556                         .updateComponentPolicyInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties);
557                     handleSuccess = (response) => {
558                         // reset each changed property with new value and remove it from changed properties list
559                         response.forEach((resProp) => {
560                             const changedProp = <PropertyFEModel>this.changedData.shift();
561                             this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
562                         });
563                         resolve(response);
564                         console.log("updated policy instance properties: ", response);
565                     };
566                 }
567             } else if (this.isInputsTabSelected) {
568                 const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => {
569                     changedInput = <InputFEModel>changedInput;
570                     const inputBE = new InputBEModel(changedInput);
571                     inputBE.defaultValue = changedInput.getJSONDefaultValue();
572                     return inputBE;
573                 });
574                 request = this.componentServiceNg2
575                     .updateComponentInputs(this.component, changedInputs);
576                 handleSuccess = (response) => {
577                     // reset each changed property with new value and remove it from changed properties list
578                     response.forEach((resInput) => {
579                         const changedInput = <InputFEModel>this.changedData.shift();
580                         this.inputsUtils.resetInputDefaultValue(changedInput, resInput.defaultValue);
581                     });
582                     console.log("updated the component inputs and got this response: ", response);
583                 }
584             }
585
586             this.savingChangedData = true;
587             request.subscribe(
588                 (response) => {
589                     this.savingChangedData = false;
590                     handleSuccess && handleSuccess(response);
591                     this.updateHasChangedData();
592                     resolve(response);
593                 },
594                 (error) => {
595                     this.savingChangedData = false;
596                     handleError && handleError(error);
597                     this.updateHasChangedData();
598                     reject(error);
599                 }
600             );
601         });
602     };
603
604     reverseChangedData = ():void => {
605         // make reverse item handler
606         let handleReverseItem;
607         if (this.isPropertiesTabSelected) {
608             handleReverseItem = (changedItem) => {
609                 changedItem = <PropertyFEModel>changedItem;
610                 this.propertiesUtils.resetPropertyValue(changedItem, changedItem.value);
611             };
612         } else if (this.isInputsTabSelected) {
613             handleReverseItem = (changedItem) => {
614                 changedItem = <InputFEModel>changedItem;
615                 this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue);
616             };
617         }
618
619         this.changedData.forEach(handleReverseItem);
620         this.changedData = [];
621         this.updateHasChangedData();
622     };
623
624     updateHasChangedData = ():boolean => {
625         const curHasChangedData:boolean = (this.changedData.length > 0);
626         if (curHasChangedData !== this.hasChangedData) {
627             this.hasChangedData = curHasChangedData;
628             if(this.hasChangedData) {
629                 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert);
630             } else {
631                 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
632             }
633         } 
634         return this.hasChangedData;
635     };
636
637     doSaveChangedData = (onSuccessFunction?:Function, onError?:Function):void => {
638         this.saveChangedData().then(
639             () => {
640                 this.Notification.success({
641                     message: 'Successfully saved changes',
642                     title: 'Saved'
643                 });
644                 if(onSuccessFunction) onSuccessFunction();
645             },
646             () => {
647                 this.Notification.error({
648                     message: 'Failed to save changes!',
649                     title: 'Failure'
650                 });
651                 if(onError) onError();
652             }
653         );
654     };
655
656     showUnsavedChangesAlert = ():Promise<any> => {
657         let modalTitle:string;
658         if (this.isPropertiesTabSelected) {
659             modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`;
660         } else if (this.isInputsTabSelected) {
661             modalTitle = `Unsaved inputs for ${this.component.name}`;
662         }
663
664         return new Promise<any>((resolve, reject) => {
665             const modal = this.ModalServiceSdcUI.openCustomModal(
666                 {
667                     title: modalTitle,
668                     size: 'sm',
669                     type: 'custom',
670                     testId: "id",
671                     
672                     buttons: [
673                         {id: 'cancelButton', text: 'Cancel', type: 'secondary', size: 'xsm', closeModal: true, callback: () => reject()},
674                         {id: 'discardButton', text: 'Discard', type: 'secondary', size: 'xsm', closeModal: true, callback: () => { this.reverseChangedData(); resolve()}},
675                         {id: 'saveButton', text: 'Save', type: 'primary', size: 'xsm', closeModal: true, disabled: !this.isValidChangedData, callback: () => this.doSaveChangedData(resolve, reject)}
676                 ] as IModalButtonComponent[]
677             }, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData});
678         });
679
680     }
681
682     updatePropertyValueAfterDeclare = (input: InputFEModel) => {
683         if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
684             let propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => {
685                 return feProperty.name == input.relatedPropertyName;
686             });
687             let inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined;
688             propertyForUpdatindVal.setAsDeclared(inputPath); //set prop as declared before assigning value
689             this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, inputPath);
690             this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, input.relatedPropertyValue, inputPath);
691         }
692     }
693
694     //used for declare button, to keep count of newly checked properties (and ignore declared properties)
695     updateCheckedPropertyCount = (increment: boolean): void => {
696         this.checkedPropertiesCount += (increment) ? 1 : -1;
697         console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount);
698     };
699
700     setInputTabIndication = (numInputs: number): void => {
701         this.propertyInputTabs.setTabIndication('Inputs', numInputs);
702     };
703
704     setPolicyTabIndication = (numPolicies: number): void => {
705         this.propertyInputTabs.setTabIndication('Policies', numPolicies);
706     };
707
708     resetUnsavedChangesForInput = (input:InputFEModel) => {
709         this.inputsUtils.resetInputDefaultValue(input, input.defaultValue);
710         this.changedData = this.changedData.filter((changedItem) => changedItem.uniqueId !== input.uniqueId);
711         this.updateHasChangedData();
712     }
713
714     deleteInput = (input: InputFEModel) => {
715         //reset any unsaved changes to the input before deleting it
716         this.resetUnsavedChangesForInput(input);
717
718         console.log("==>" + this.constructor.name + ": deleteInput");
719         let inputToDelete = new InputBEModel(input);
720
721         this.componentServiceNg2
722             .deleteInput(this.component, inputToDelete)
723             .subscribe(response => {
724                 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
725
726                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
727                 this.changeSelectedInstance(this.selectedInstanceData);
728                 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
729
730                 // if (instanceFeProperties) {
731                 //     let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
732                 //         return prop.name == input.propertyName;
733                 //     });
734
735                 //     if (propToEnable) {
736                 //         if (propToEnable.name == response.inputPath) response.inputPath = null;
737                 //         propToEnable.setNonDeclared(response.inputPath);
738                 //         //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
739                 //         this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
740                 //     }
741                 // }
742             }, error => {}); //ignore error
743     };
744
745     deletePolicy = (policy: PolicyInstance) => {
746         this.loadingPolicies = true;
747         this.componentServiceNg2
748             .deletePolicy(this.component, policy)
749             .subscribe(response => {
750                 this.policies = this.policies.filter(policy => policy.uniqueId !== response.uniqueId);
751                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
752                 this.changeSelectedInstance(this.selectedInstanceData);
753                 this.loadingPolicies = false;
754             });
755     };
756
757     deleteProperty = (property: PropertyFEModel) => {
758         let propertyToDelete = new PropertyFEModel(property);
759         this.loadingProperties = true;
760         let feMap = this.instanceFePropertiesMap;
761         this.componentServiceNg2
762             .deleteServiceProperty(this.component, propertyToDelete)
763             .subscribe(response => {
764                 const props = feMap[this.component.uniqueId];
765                 props.splice(props.findIndex(p => p.uniqueId === response),1);
766                 this.loadingProperties = false;
767             }, error => {
768                 this.loadingProperties = false;
769                 console.error(error);
770             });
771     };
772
773     /*** addProperty ***/
774     addProperty = () => {
775         let modalTitle = 'Add Property';
776         const modal = this.ModalService.createCustomModal(new ModalModel(
777             'sm',
778             modalTitle,
779             null,
780             [
781                 new ButtonModel('Save', 'blue', () => {
782                     modal.instance.dynamicContent.instance.isLoading = true;
783                     const newProperty: PropertyBEModel = modal.instance.dynamicContent.instance.propertyModel;
784                     this.componentServiceNg2.createServiceProperty(this.component, newProperty)
785                         .subscribe(response => {
786                             modal.instance.dynamicContent.instance.isLoading = false;
787                             let newProp: PropertyFEModel = this.propertiesUtils.convertAddPropertyBAToPropertyFE(response);
788                             this.instanceFePropertiesMap[this.component.uniqueId].push(newProp);
789                             modal.instance.close();
790                         }, (error) => {
791                             modal.instance.dynamicContent.instance.isLoading = false;
792                             this.Notification.error({
793                                 message: 'Failed to add property:' + error,
794                                 title: 'Failure'
795                             });
796                         });
797
798                 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
799                 new ButtonModel('Cancel', 'outline grey', () => {
800                     modal.instance.close();
801                 }),
802             ],
803             null
804         ));
805         this.ModalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
806         modal.instance.open();
807     };
808
809     /*** SEARCH RELATED FUNCTIONS ***/
810     searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
811         let instanceBePropertiesMap:InstanceBePropertiesMap;
812         this.componentServiceNg2
813             .filterComponentInstanceProperties(this.component, filterData)
814             .subscribe(response => {
815
816                 this.processInstancePropertiesResponse(response, false);
817                 this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
818                 this.searchPropertyName = filterData.propertyName;//mark in table
819                 this.hierarchyNavTabs.triggerTabChange('Composition');
820                 this.propertiesNavigationData = [];
821                 this.displayClearSearch = true;
822             }, error => {}); //ignore error
823
824     };
825
826     clearSearch = () => {
827         this.instancesNavigationData = this.instances;
828         this.searchPropertyName = "";
829         this.hierarchyPropertiesDisplayOptions.searchText = "";
830         this.displayClearSearch = false;
831         this.advanceSearch.clearAll();
832         this.searchQuery = '';
833     };
834
835     clickOnClearSearch = () => {
836         this.clearSearch();
837         this.selectFirstInstanceByDefault();
838         this.hierarchyNavTabs.triggerTabChange('Composition');
839     };
840
841     private isInput = (instanceType:string):boolean =>{
842         return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;
843     }
844
845 }