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