[SDC-29] rebase continue work to align source
[sdc.git] / catalog-ui / src / app / ng2 / pages / properties-assignment / properties-assignment.page.component.ts
1 import {Component, ViewChild, ElementRef, Renderer, Inject} from "@angular/core";
2 import {PostsService} from "../../services/posts.service";
3 import { PropertiesService } from "../../services/properties.service";
4 import { HierarchyNavService } from "../../services/hierarchy-nav.service";
5 import { PropertiesUtils } from './properties.utils';
6 import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData } from "app/models";
7 import { PROPERTY_TYPES, ResourceType } from "app/utils";
8 import property = require("lodash/property");
9 import {ComponentServiceNg2} from "../../services/component-services/component.service";
10 import {ComponentInstanceServiceNg2} from "../../services/component-instance-services/component-instance.service"
11 import { InputFEModel, ComponentInstance, PropertyBEModel, DerivedPropertyType, DerivedFEProperty, ResourceInstance, SimpleFlatProperty } from "app/models";
12 import {HierarchyDisplayOptions} from "../../components/hierarchy-navigtion/hierarchy-display-options"
13 import {PropertyRowSelectedEvent} from "./../../components/properties-table/properties-table.component";
14 import { KeysPipe } from 'app/ng2/pipes/keys.pipe';
15 import {FilterPropertiesAssignmentComponent} from "../../components/filter-properties-assignment/filter-properties-assignment.component";
16 import { ComponentModeService } from "app/ng2/services/component-mode.service"
17 import {WorkspaceMode, EVENTS} from "../../../utils/constants";
18 import {ComponentInstanceProperty, InputBEModel} from "app/models"
19 import {ComponentInstanceInput} from "../../../models/properties-inputs/input-be-model";
20 import {EventListenerService} from "app/services/event-listener-service"
21 @Component({
22     templateUrl: './properties-assignment.page.component.html',
23     styleUrls: ['./properties-assignment.page.component.less']
24 })
25 export class PropertiesAssignmentComponent {
26     title = "Properties & Inputs";
27
28     component:ComponentData;
29
30     propertiesNavigationData = [];
31     instancesNavigationData = [];
32
33     instanceFePropertiesMap:InstanceFePropertiesMap;
34     inputs: Array<InputFEModel> = [];
35     instances: Array<ComponentInstance> = [];
36     searchQuery: string;
37     propertyStructureHeader: string;
38
39     selectedFlatProperty: SimpleFlatProperty = new SimpleFlatProperty();
40     selectedInstanceType: string;
41     selectedInstanceData: ComponentInstance = new ComponentInstance();
42     checkedPropertiesCount: number = 0;
43
44     hierarchyPropertiesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('path', 'name', 'childrens');
45     hierarchyInstancesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name');
46     displayClearSearch = false;
47     searchPropertyName:string;
48     isInpusTabSelected:boolean;
49     isReadonly:boolean;
50     loadingInstances:boolean = false;
51     loadingInputs:boolean = false;
52     loadingProperties:boolean = false;
53
54     @ViewChild('hierarchyNavTabs') hierarchyNavTabs: ElementRef;
55     @ViewChild('propertyInputTabs') propertyInputTabs: ElementRef;
56     @ViewChild('advanceSearch') advanceSearch: FilterPropertiesAssignmentComponent;
57
58     constructor(private propertiesService: PropertiesService,
59                 private hierarchyNavService: HierarchyNavService,
60                 private propertiesUtils:PropertiesUtils,
61                 private componentServiceNg2:ComponentServiceNg2,
62                 private componentInstanceServiceNg2:ComponentInstanceServiceNg2,
63                 @Inject("$stateParams") _stateParams,
64                 private renderer: Renderer,
65                 private componentModeService:ComponentModeService,
66                 private EventListenerService:EventListenerService) {
67
68         this.instanceFePropertiesMap = new InstanceFePropertiesMap();
69
70         /* 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
71         than if the data is already exist, no need to call the api again - Ask orit if you have any questions*/
72         this.component = _stateParams.component;
73         this.EventListenerService.registerObserverCallback(EVENTS.ON_CHECKOUT, this.onCheckout);
74         this.updateViewMode();
75     }
76
77     ngOnInit() {
78         console.log("==>" + this.constructor.name + ": ngOnInit");
79         this.loadingInputs = true;
80         this.loadingInstances = true;
81         this.loadingProperties = true;
82         this.componentServiceNg2
83             .getComponentInputs(this.component)
84             .subscribe(response => {
85                 _.forEach(response.inputs, (input: InputBEModel) => {
86                     this.inputs.push(new InputFEModel(input));
87                 });
88                 this.loadingInputs = false;
89
90             });
91         this.componentServiceNg2
92             .getComponentResourceInstances(this.component)
93             .subscribe(response => {
94                 this.instances = response.componentInstances;
95
96                 _.forEach(this.instances, (instance) => {
97                     this.instancesNavigationData.push(instance);
98                 });
99                 this.loadingInstances = false;
100                 if (this.instancesNavigationData[0] == undefined) {
101                     this.loadingProperties = false;
102                 }
103                 this.selectFirstInstanceByDefault();
104             });
105     };
106
107     ngOnDestroy() {
108         this.EventListenerService.unRegisterObserver(EVENTS.ON_CHECKOUT);
109     }
110
111     selectFirstInstanceByDefault = () => {
112         if (this.instancesNavigationData[0] !== undefined) {
113             this.onInstanceSelectedUpdate(this.instancesNavigationData[0]);
114         }
115     };
116
117     updateViewMode = () => {
118         this.isReadonly = this.componentModeService.getComponentMode(this.component) === WorkspaceMode.VIEW;
119     }
120
121     onCheckout = (component:ComponentData) => {
122         this.component = component;
123         this.updateViewMode();
124     }
125
126
127     onInstanceSelectedUpdate = (resourceInstance: ResourceInstance) => {
128         console.log("==>" + this.constructor.name + ": onInstanceSelectedUpdate");
129         let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
130         this.selectedInstanceData = resourceInstance;
131         this.selectedInstanceType = resourceInstance.originType;
132
133         this.loadingProperties = true;
134         if(resourceInstance.originType === ResourceType.VF) {
135             this.componentInstanceServiceNg2
136                 .getComponentInstanceInputs(this.component, resourceInstance)
137                 .subscribe(response => {
138                     instanceBePropertiesMap[resourceInstance.uniqueId] = response;
139                     this.processInstancePropertiesResponse(instanceBePropertiesMap);
140                     this.loadingProperties = false;
141
142                 });
143         } else {
144             this.componentInstanceServiceNg2
145                 .getComponentInstanceProperties(this.component, resourceInstance.uniqueId)
146                 .subscribe(response => {
147                     instanceBePropertiesMap[resourceInstance.uniqueId] = response;
148                     this.processInstancePropertiesResponse(instanceBePropertiesMap);
149                     this.loadingProperties = false;
150                 });
151         }
152
153         if( this.searchPropertyName ){
154             this.clearSearch();
155         }
156         //clear selected property from the navigation
157         this.selectedFlatProperty = new SimpleFlatProperty();
158         this.propertiesNavigationData = [];
159     };
160
161     /**
162      * Entry point handling response from server
163      */
164     processInstancePropertiesResponse = (instanceBePropertiesMap:InstanceBePropertiesMap) => {
165         this.instanceFePropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(instanceBePropertiesMap, this.inputs); //create flattened children, disable declared props, and init values
166         this.checkedPropertiesCount = 0;
167     };
168
169
170     /*** VALUE CHANGE EVENTS ***/
171     propertyValueChanged = (event: PropertyFEModel) => {
172         console.log("==>" + this.constructor.name + ": propertyValueChanged " + event);
173         // Copying the actual value from the object ref into the value if it's from a complex type
174         event.value = event.getJSONValue();
175
176         if (this.selectedInstanceData.originType === ResourceType.VF) {
177             console.log("I want to update input value on the resource instance");
178             let inputToUpdate = new PropertyBEModel(event);
179             this.componentInstanceServiceNg2
180                 .updateInstanceInput(this.component, this.selectedInstanceData.uniqueId, inputToUpdate)
181                 .subscribe(response => {
182                     console.log("update resource instance input and got this response: ", response);
183                 })
184         }
185         else {
186             let propertyBe = new PropertyBEModel(event);
187             this.componentInstanceServiceNg2
188                 .updateInstanceProperty(this.component, this.selectedInstanceData.uniqueId, propertyBe)
189                 .subscribe(response => {
190                     console.log("updated resource instance property and got this response: ", response);
191                 });
192             console.log(event);
193         }
194
195     };
196
197     inputValueChanged = (event) => {
198         console.log("==>" + this.constructor.name + ": inputValueChanged");
199         let inputToUpdate = new PropertyBEModel(event);
200
201         this.componentServiceNg2
202             .updateComponentInput(this.component, inputToUpdate)
203             .subscribe(response => {
204                 console.log("updated the component input and got this response: ", response);
205             })
206     };
207
208
209     /*** HEIRARCHY/NAV RELATED FUNCTIONS ***/
210
211     /**
212      * Handle select node in navigation area, and select the row in table
213      */
214     onPropertySelectedUpdate = ($event) => {
215         console.log("==>" + this.constructor.name + ": onPropertySelectedUpdate");
216         this.selectedFlatProperty = $event;
217         let parentProperty:PropertyFEModel = this.propertiesService.getParentPropertyFEModelFromPath(this.instanceFePropertiesMap[this.selectedFlatProperty.instanceName], this.selectedFlatProperty.path);
218         parentProperty.expandedChildPropertyId = this.selectedFlatProperty.path;
219     };
220
221     /**
222      * When user select row in table, this will prepare the hirarchy object for the tree.
223      */
224     selectPropertyRow = (propertyRowSelectedEvent:PropertyRowSelectedEvent) => {
225         console.log("==>" + this.constructor.name + ": selectPropertyRow " + propertyRowSelectedEvent.propertyModel.name);
226         let property = propertyRowSelectedEvent.propertyModel;
227         let instanceName = propertyRowSelectedEvent.instanceName;
228         this.propertyStructureHeader = null;
229
230         // Build hirarchy tree for the navigation and update propertiesNavigationData with it.
231         if(this.selectedInstanceData.originType !== ResourceType.VF) {
232             let simpleFlatProperty:Array<SimpleFlatProperty>;
233             if (property instanceof PropertyFEModel) {
234                 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(property, instanceName);
235             } else if (property instanceof DerivedFEProperty) {
236                 // Need to find parent PropertyFEModel
237                 let parentPropertyFEModel:PropertyFEModel = _.find(this.instanceFePropertiesMap[instanceName], (tmpFeProperty):boolean => {
238                     return property.propertiesName.indexOf(tmpFeProperty.name)===0;
239                 });
240                 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(parentPropertyFEModel, instanceName);
241             }
242             this.propertiesNavigationData = simpleFlatProperty;
243         }
244
245         // Updatet the header in the navigation tree with property name.
246         if(property instanceof DerivedFEProperty) {
247             this.propertyStructureHeader = (property.propertiesName.split('#'))[0];
248         }
249
250         // Set selected property in table
251         this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName);
252         this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Property Structure']);
253     };
254
255
256     selectInstanceRow = ($event) => {//get instance name
257         this.selectedInstanceData =  _.find(this.instancesNavigationData, (instance:ComponentInstance) => {
258             return instance.name == $event;
259         });
260         this.renderer.invokeElementMethod(
261             this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
262     };
263
264     tabChanged = (event) => {
265         console.log("==>" + this.constructor.name + ": tabChanged " + event);
266         this.isInpusTabSelected = event.title === "Inputs";
267         this.propertyStructureHeader = null;
268         this.searchQuery = '';
269     };
270
271
272
273     /*** DECLARE PROPERTIES/INPUTS ***/
274     declareProperties = (): void => {
275         console.log("==>" + this.constructor.name + ": declareProperties");
276
277         let selectedProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
278
279         let instancesNames = new KeysPipe().transform(this.instanceFePropertiesMap, []);
280         angular.forEach(instancesNames, (instanceName: string): void => {
281             selectedProperties[instanceName] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceName]);
282             //selectedProperties[this.selectedInstanceData.uniqueId] = this.propertiesService.getCheckedProperties(this.properties);
283         });
284
285         let inputsToCreate: InstancePropertiesAPIMap;
286         if (this.selectedInstanceType !== ResourceType.VF) {
287             inputsToCreate = new InstancePropertiesAPIMap(null, selectedProperties);
288         } else {
289             inputsToCreate = new InstancePropertiesAPIMap(selectedProperties, null);
290         }
291         this.componentServiceNg2
292             .createInput(this.component, inputsToCreate)
293             .subscribe(response => {
294                 this.setInputTabIndication(response.length);
295                 this.checkedPropertiesCount = 0;
296                 _.forEach(response, (input: InputBEModel) => {
297                     this.inputs.push(new InputFEModel(input));
298                     this.updatePropertyValueAfterDeclare(input);
299                 });
300             });
301     };
302
303     updatePropertyValueAfterDeclare = (input: InputBEModel) => {
304         _.forEach(input.properties, (property: ComponentInstanceProperty) => {
305             this.updatePropertyOrInputValueAfterDeclare(property, input);
306         });
307
308         _.forEach(input.inputs, (inputInstance: ComponentInstanceInput) => {
309             this.updatePropertyOrInputValueAfterDeclare(inputInstance, input);
310         });
311     }
312
313     updatePropertyOrInputValueAfterDeclare = (inputSource: ComponentInstanceProperty | ComponentInstanceInput, input: InputBEModel) => {
314         if (this.instanceFePropertiesMap[inputSource.componentInstanceId]) {
315             let propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[inputSource.componentInstanceId], (feProperty: PropertyFEModel) => {
316                 return feProperty.name == inputSource.name;
317             });
318
319             if (input.inputPath == propertyForUpdatindVal.name) input.inputPath = null; //Fix - if inputPath is sent for parent props, remove it
320
321             propertyForUpdatindVal.setAsDeclared(input.inputPath); //set prop as declared before assigning value
322             this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, input.inputPath);
323             this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, inputSource.value, input.inputPath);
324             // if (input.inputPath) {
325             //     let childProp = _.find(propertyForUpdatindVal.flattenedChildren, (child: DerivedFEProperty) => {
326             //         return child.propertiesName == input.inputPath;
327             //     });
328             //     this.propertiesUtils.assignFlattenedChildrenValues(JSON.parse(inputSource.value), [childProp], inputSource.name);
329             // } else {
330             //     propertyForUpdatindVal.valueObj = inputSource.value;
331             // }
332         }
333     }
334
335     //used for declare button, to keep count of newly checked properties (and ignore declared properties)
336     updateCheckedPropertyCount = (increment: boolean): void => {
337         this.checkedPropertiesCount += (increment) ? 1 : -1;
338         console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount);
339     };
340
341     setInputTabIndication = (numInputs: number): void => {
342         this.renderer.invokeElementMethod(this.propertyInputTabs, 'setTabIndication', ['Inputs', numInputs]);
343     };
344
345     deleteInput = (input: InputFEModel) => {
346         console.log("==>" + this.constructor.name + ": deleteInput");
347         let inputToDelete = new PropertyBEModel(input);
348
349         this.componentServiceNg2
350             .deleteInput(this.component, inputToDelete)
351             .subscribe(response => {
352                 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
353
354                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
355                 this.onInstanceSelectedUpdate(this.selectedInstanceData);
356                 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
357
358                 // if (instanceFeProperties) {
359                 //     let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
360                 //         return prop.name == input.propertyName;
361                 //     });
362
363                 //     if (propToEnable) {
364                 //         if (propToEnable.name == response.inputPath) response.inputPath = null;
365                 //         propToEnable.setNonDeclared(response.inputPath);
366                 //         //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
367                 //         this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
368                 //     }
369                 // }
370             });
371     };
372
373     getInstanceUniqueId = (instanceName: string): string => {
374         let wantedInstance: ComponentInstance = this.instances.find((instance) => {
375             return instance.normalizedName === instanceName;
376         });
377
378         return wantedInstance.uniqueId;
379     };
380
381
382
383     /*** SEARCH RELATED FUNCTIONS ***/
384     searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
385         let instanceBePropertiesMap:InstanceBePropertiesMap;
386         this.componentServiceNg2
387             .filterComponentInstanceProperties(this.component, filterData)
388             .subscribe(response => {
389
390                 this.processInstancePropertiesResponse(response);
391                 this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
392                 this.searchPropertyName = filterData.propertyName;//mark in table
393                 this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
394                 this.propertiesNavigationData = [];
395                 this.displayClearSearch = true;
396             });
397
398     };
399
400     clearSearch = () => {
401         this.instancesNavigationData = this.instances;
402         this.searchPropertyName = "";
403         this.hierarchyPropertiesDisplayOptions.searchText = "";
404         this.displayClearSearch = false;
405         this.advanceSearch.clearAll();
406     };
407
408     clickOnClearSearch = () => {
409         this.clearSearch();
410         this.selectFirstInstanceByDefault();
411         this.renderer.invokeElementMethod(
412             this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
413     };
414
415 }