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