[sdc] update code of sdc
[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         // Update the header in the navigation tree with property name.
246         this.propertyStructureHeader = (property.propertiesName.split('#'))[0];
247
248         // Set selected property in table
249         this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName);
250         this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Property Structure']);
251     };
252
253
254     selectInstanceRow = ($event) => {//get instance name
255         this.selectedInstanceData =  _.find(this.instancesNavigationData, (instance:ComponentInstance) => {
256             return instance.name == $event;
257         });
258         this.renderer.invokeElementMethod(
259             this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
260     };
261
262     tabChanged = (event) => {
263         console.log("==>" + this.constructor.name + ": tabChanged " + event);
264         this.isInpusTabSelected = event.title === "Inputs";
265         this.propertyStructureHeader = null;
266         this.searchQuery = '';
267     };
268
269
270
271     /*** DECLARE PROPERTIES/INPUTS ***/
272     declareProperties = (): void => {
273         console.log("==>" + this.constructor.name + ": declareProperties");
274
275         let selectedProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
276
277         let instancesNames = new KeysPipe().transform(this.instanceFePropertiesMap, []);
278         angular.forEach(instancesNames, (instanceName: string): void => {
279             selectedProperties[instanceName] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceName]);
280             //selectedProperties[this.selectedInstanceData.uniqueId] = this.propertiesService.getCheckedProperties(this.properties);
281         });
282
283         let inputsToCreate: InstancePropertiesAPIMap;
284         if (this.selectedInstanceType !== ResourceType.VF) {
285             inputsToCreate = new InstancePropertiesAPIMap(null, selectedProperties);
286         } else {
287             inputsToCreate = new InstancePropertiesAPIMap(selectedProperties, null);
288         }
289         this.componentServiceNg2
290             .createInput(this.component, inputsToCreate)
291             .subscribe(response => {
292                 this.setInputTabIndication(response.length);
293                 this.checkedPropertiesCount = 0;
294                 _.forEach(response, (input: InputBEModel) => {
295                     this.inputs.push(new InputFEModel(input));
296                     this.updatePropertyValueAfterDeclare(input);
297                 });
298             });
299     };
300
301     updatePropertyValueAfterDeclare = (input: InputBEModel) => {
302         _.forEach(input.properties, (property: ComponentInstanceProperty) => {
303             this.updatePropertyOrInputValueAfterDeclare(property, input);
304         });
305
306         _.forEach(input.inputs, (inputInstance: ComponentInstanceInput) => {
307             this.updatePropertyOrInputValueAfterDeclare(inputInstance, input);
308         });
309     }
310
311     updatePropertyOrInputValueAfterDeclare = (inputSource: ComponentInstanceProperty | ComponentInstanceInput, input: InputBEModel) => {
312         if (this.instanceFePropertiesMap[inputSource.componentInstanceId]) {
313             let propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[inputSource.componentInstanceId], (feProperty: PropertyFEModel) => {
314                 return feProperty.name == inputSource.name;
315             });
316
317             if (input.inputPath == propertyForUpdatindVal.name) input.inputPath = null; //Fix - if inputPath is sent for parent props, remove it
318
319             propertyForUpdatindVal.setAsDeclared(input.inputPath); //set prop as declared before assigning value
320             this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, input.inputPath);
321             this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, inputSource.value, input.inputPath);
322             // if (input.inputPath) {
323             //     let childProp = _.find(propertyForUpdatindVal.flattenedChildren, (child: DerivedFEProperty) => {
324             //         return child.propertiesName == input.inputPath;
325             //     });
326             //     this.propertiesUtils.assignFlattenedChildrenValues(JSON.parse(inputSource.value), [childProp], inputSource.name);
327             // } else {
328             //     propertyForUpdatindVal.valueObj = inputSource.value;
329             // }
330         }
331     }
332
333     //used for declare button, to keep count of newly checked properties (and ignore declared properties)
334     updateCheckedPropertyCount = (increment: boolean): void => {
335         this.checkedPropertiesCount += (increment) ? 1 : -1;
336         console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount);
337     };
338
339     setInputTabIndication = (numInputs: number): void => {
340         this.renderer.invokeElementMethod(this.propertyInputTabs, 'setTabIndication', ['Inputs', numInputs]);
341     };
342
343     deleteInput = (input: InputFEModel) => {
344         console.log("==>" + this.constructor.name + ": deleteInput");
345         let inputToDelete = new PropertyBEModel(input);
346
347         this.componentServiceNg2
348             .deleteInput(this.component, inputToDelete)
349             .subscribe(response => {
350                 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
351
352                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
353                 this.onInstanceSelectedUpdate(this.selectedInstanceData);
354                 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
355
356                 // if (instanceFeProperties) {
357                 //     let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
358                 //         return prop.name == input.propertyName;
359                 //     });
360
361                 //     if (propToEnable) {
362                 //         if (propToEnable.name == response.inputPath) response.inputPath = null;
363                 //         propToEnable.setNonDeclared(response.inputPath);
364                 //         //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
365                 //         this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
366                 //     }
367                 // }
368             });
369     };
370
371     getInstanceUniqueId = (instanceName: string): string => {
372         let wantedInstance: ComponentInstance = this.instances.find((instance) => {
373             return instance.normalizedName === instanceName;
374         });
375
376         return wantedInstance.uniqueId;
377     };
378
379
380
381     /*** SEARCH RELATED FUNCTIONS ***/
382     searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
383         let instanceBePropertiesMap:InstanceBePropertiesMap;
384         this.componentServiceNg2
385             .filterComponentInstanceProperties(this.component, filterData)
386             .subscribe(response => {
387
388                 this.processInstancePropertiesResponse(response);
389                 this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
390                 this.searchPropertyName = filterData.propertyName;//mark in table
391                 this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
392                 this.propertiesNavigationData = [];
393                 this.displayClearSearch = true;
394             });
395
396     };
397
398     clearSearch = () => {
399         this.instancesNavigationData = this.instances;
400         this.searchPropertyName = "";
401         this.hierarchyPropertiesDisplayOptions.searchText = "";
402         this.displayClearSearch = false;
403         this.advanceSearch.clearAll();
404     };
405
406     clickOnClearSearch = () => {
407         this.clearSearch();
408         this.selectFirstInstanceByDefault();
409         this.renderer.invokeElementMethod(
410             this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
411     };
412
413 }