[sdc] - last merges before moving to LF
[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 { PropertiesService } from "../../services/properties.service";
3 import { HierarchyNavService } from "../../services/hierarchy-nav.service";
4 import { PropertiesUtils } from './properties.utils';
5 import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData } from "app/models";
6 import { PROPERTY_TYPES, ResourceType } from "app/utils";
7 import property = require("lodash/property");
8 import {ComponentServiceNg2} from "../../services/component-services/component.service";
9 import {ComponentInstanceServiceNg2} from "../../services/component-instance-services/component-instance.service"
10 import { InputBEModel, InputFEModel, ComponentInstance, PropertyBEModel, DerivedPropertyType, DerivedFEProperty, ResourceInstance, SimpleFlatProperty } from "app/models";
11 import {HierarchyDisplayOptions} from "../../components/hierarchy-navigtion/hierarchy-display-options"
12 import {PropertyRowSelectedEvent} from "./../../components/properties-table/properties-table.component";
13 import { KeysPipe } from 'app/ng2/pipes/keys.pipe';
14 import {FilterPropertiesAssignmentComponent} from "../../components/filter-properties-assignment/filter-properties-assignment.component";
15 import { ComponentModeService } from "app/ng2/services/component-mode.service"
16 import {WorkspaceMode, EVENTS} from "../../../utils/constants";
17 import {EventListenerService} from "app/services/event-listener-service"
18 @Component({
19     templateUrl: './properties-assignment.page.component.html',
20     styleUrls: ['./properties-assignment.page.component.less']
21 })
22 export class PropertiesAssignmentComponent {
23     title = "Properties & Inputs";
24
25     component: ComponentData;
26     componentInstanceNamesMap: Map<string, string> = new Map<string, string>();//instanceUniqueId, name
27
28     propertiesNavigationData = [];
29     instancesNavigationData = [];
30
31     instanceFePropertiesMap:InstanceFePropertiesMap;
32     inputs: Array<InputFEModel> = [];
33     instances: Array<ComponentInstance> = [];
34     searchQuery: string;
35     propertyStructureHeader: string;
36
37     selectedFlatProperty: SimpleFlatProperty = new SimpleFlatProperty();
38     selectedInstanceType: string;
39     selectedInstanceData: ComponentInstance = new ComponentInstance();
40     checkedPropertiesCount: number = 0;
41
42     hierarchyPropertiesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('path', 'name', 'childrens');
43     hierarchyInstancesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name');
44     displayClearSearch = false;
45     searchPropertyName:string;
46     isInpusTabSelected:boolean;
47     isReadonly:boolean;
48     loadingInstances:boolean = false;
49     loadingInputs:boolean = false;
50     loadingProperties:boolean = false;
51
52     @ViewChild('hierarchyNavTabs') hierarchyNavTabs: ElementRef;
53     @ViewChild('propertyInputTabs') propertyInputTabs: ElementRef;
54     @ViewChild('advanceSearch') advanceSearch: FilterPropertiesAssignmentComponent;
55
56     constructor(private propertiesService: PropertiesService,
57                 private hierarchyNavService: HierarchyNavService,
58                 private propertiesUtils:PropertiesUtils,
59                 private componentServiceNg2:ComponentServiceNg2,
60                 private componentInstanceServiceNg2:ComponentInstanceServiceNg2,
61                 @Inject("$stateParams") _stateParams,
62                 private renderer: Renderer,
63                 private componentModeService:ComponentModeService,
64                 private EventListenerService:EventListenerService) {
65
66         this.instanceFePropertiesMap = new InstanceFePropertiesMap();
67
68         /* 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
69         than if the data is already exist, no need to call the api again - Ask orit if you have any questions*/
70         this.component = _stateParams.component;
71         this.EventListenerService.registerObserverCallback(EVENTS.ON_CHECKOUT, this.onCheckout);
72         this.updateViewMode();
73     }
74
75     ngOnInit() {
76         console.log("==>" + this.constructor.name + ": ngOnInit");
77         this.loadingInputs = true;
78         this.loadingInstances = true;
79         this.loadingProperties = true;
80         this.componentServiceNg2
81             .getComponentInputs(this.component)
82             .subscribe(response => {
83                 _.forEach(response.inputs, (input: InputBEModel) => {
84                     this.inputs.push(new InputFEModel(input)); //only push items that were declared via SDC
85                 });
86                 this.loadingInputs = false;
87
88             });
89         this.componentServiceNg2
90             .getComponentResourceInstances(this.component)
91             .subscribe(response => {
92                 this.instances = response.componentInstances;
93
94                 _.forEach(this.instances, (instance) => {
95                     this.instancesNavigationData.push(instance);
96                     this.componentInstanceNamesMap[instance.uniqueId] = instance.name;
97                 });
98                 this.loadingInstances = false;
99                 if (this.instancesNavigationData[0] == undefined) {
100                     this.loadingProperties = false;
101                 }
102                 this.selectFirstInstanceByDefault();
103             });
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(this.isInput(resourceInstance.originType)) {
135             this.componentInstanceServiceNg2
136                 .getComponentInstanceInputs(this.component, resourceInstance)
137                 .subscribe(response => {
138                     instanceBePropertiesMap[resourceInstance.uniqueId] = response;
139                     this.processInstancePropertiesResponse(instanceBePropertiesMap, true);
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, false);
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, originTypeIsVF: boolean) => {
165         this.instanceFePropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(instanceBePropertiesMap, originTypeIsVF, 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.isInput(this.selectedInstanceData.originType)) {
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.isInput(this.selectedInstanceType)) {
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                     let newInput: InputFEModel = new InputFEModel(input);
296                     this.inputs.push(newInput);
297                     this.updatePropertyValueAfterDeclare(newInput);
298                 });
299             });
300     };
301
302
303     updatePropertyValueAfterDeclare = (input: InputFEModel) => {
304         if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
305             let propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => {
306                 return feProperty.name == input.relatedPropertyName;
307             });
308             let inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined;
309             propertyForUpdatindVal.setAsDeclared(inputPath); //set prop as declared before assigning value
310             this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, inputPath);
311             this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, input.relatedPropertyValue, inputPath);
312         }
313     }
314
315     //used for declare button, to keep count of newly checked properties (and ignore declared properties)
316     updateCheckedPropertyCount = (increment: boolean): void => {
317         this.checkedPropertiesCount += (increment) ? 1 : -1;
318         console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount);
319     };
320
321     setInputTabIndication = (numInputs: number): void => {
322         this.renderer.invokeElementMethod(this.propertyInputTabs, 'setTabIndication', ['Inputs', numInputs]);
323     };
324
325     deleteInput = (input: InputFEModel) => {
326         console.log("==>" + this.constructor.name + ": deleteInput");
327         let inputToDelete = new PropertyBEModel(input);
328
329         this.componentServiceNg2
330             .deleteInput(this.component, inputToDelete)
331             .subscribe(response => {
332                 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
333
334                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
335                 this.onInstanceSelectedUpdate(this.selectedInstanceData);
336                 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
337
338                 // if (instanceFeProperties) {
339                 //     let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
340                 //         return prop.name == input.propertyName;
341                 //     });
342
343                 //     if (propToEnable) {
344                 //         if (propToEnable.name == response.inputPath) response.inputPath = null;
345                 //         propToEnable.setNonDeclared(response.inputPath);
346                 //         //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
347                 //         this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
348                 //     }
349                 // }
350             });
351     };
352
353
354
355     /*** SEARCH RELATED FUNCTIONS ***/
356     searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
357         let instanceBePropertiesMap:InstanceBePropertiesMap;
358         this.componentServiceNg2
359             .filterComponentInstanceProperties(this.component, filterData)
360             .subscribe(response => {
361
362                 this.processInstancePropertiesResponse(response, false);
363                 this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
364                 this.searchPropertyName = filterData.propertyName;//mark in table
365                 this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
366                 this.propertiesNavigationData = [];
367                 this.displayClearSearch = true;
368             });
369
370     };
371
372     clearSearch = () => {
373         this.instancesNavigationData = this.instances;
374         this.searchPropertyName = "";
375         this.hierarchyPropertiesDisplayOptions.searchText = "";
376         this.displayClearSearch = false;
377         this.advanceSearch.clearAll();
378         this.searchQuery = '';
379     };
380
381     clickOnClearSearch = () => {
382         this.clearSearch();
383         this.selectFirstInstanceByDefault();
384         this.renderer.invokeElementMethod(
385             this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
386     };
387
388     private isInput = (instanceType:string):boolean =>{
389         return instanceType === ResourceType.VF || instanceType === ResourceType.PNF;
390     }
391
392 }