Merge "[sdc] - repositories proxied by LF are removed"
[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             }, error => {}); //ignore error
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             }, error => {}); //ignore error
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                 }, error => {}); //ignore error
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                 }, error => {}); //ignore error
171         }
172
173         if(resourceInstance.componentName === "vnfConfiguration") {
174             this.isReadonly = true;
175         }
176
177         if( this.searchPropertyName ){
178             this.clearSearch();
179         }
180         //clear selected property from the navigation
181         this.selectedFlatProperty = new SimpleFlatProperty();
182         this.propertiesNavigationData = [];
183     };
184
185     /**
186      * Entry point handling response from server
187      */
188     processInstancePropertiesResponse = (instanceBePropertiesMap: InstanceBePropertiesMap, originTypeIsVF: boolean) => {
189         this.instanceFePropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(instanceBePropertiesMap, originTypeIsVF, this.inputs); //create flattened children, disable declared props, and init values
190         this.checkedPropertiesCount = 0;
191     };
192
193
194     /*** VALUE CHANGE EVENTS ***/
195     propertyValueChanged = (event: PropertyFEModel) => {
196         console.log("==>" + this.constructor.name + ": propertyValueChanged " + event);
197         // Copying the actual value from the object ref into the value if it's from a complex type
198         event.value = event.getJSONValue();
199
200         if (this.isInput(this.selectedInstanceData.originType)) {
201             console.log("I want to update input value on the resource instance");
202             let inputToUpdate = new PropertyBEModel(event);
203             this.componentInstanceServiceNg2
204                 .updateInstanceInput(this.component, this.selectedInstanceData.uniqueId, inputToUpdate)
205                 .subscribe(response => {
206                     console.log("Update resource instance input response: ", response);
207                 }, error => {}); //ignore error
208         }
209         else {
210             let propertyBe = new PropertyBEModel(event);
211             this.componentInstanceServiceNg2
212                 .updateInstanceProperty(this.component, this.selectedInstanceData.uniqueId, propertyBe)
213                 .subscribe(response => {
214                     console.log("Update resource instance property response: ", response);
215                 }, error => {}); //ignore error
216             console.log(event);
217         }
218
219     };
220
221     inputValueChanged = (event) => {
222         console.log("==>" + this.constructor.name + ": inputValueChanged");
223         let inputToUpdate = new PropertyBEModel(event);
224
225         this.componentServiceNg2
226             .updateComponentInput(this.component, inputToUpdate)
227             .subscribe(response => {
228                 console.log("updated the component input and got this response: ", response);
229             }, error => {}); //ignore error
230     };
231
232
233     /*** HEIRARCHY/NAV RELATED FUNCTIONS ***/
234
235     /**
236      * Handle select node in navigation area, and select the row in table
237      */
238     onPropertySelectedUpdate = ($event) => {
239         console.log("==>" + this.constructor.name + ": onPropertySelectedUpdate");
240         this.selectedFlatProperty = $event;
241         let parentProperty:PropertyFEModel = this.propertiesService.getParentPropertyFEModelFromPath(this.instanceFePropertiesMap[this.selectedFlatProperty.instanceName], this.selectedFlatProperty.path);
242         parentProperty.expandedChildPropertyId = this.selectedFlatProperty.path;
243     };
244
245     /**
246      * When user select row in table, this will prepare the hirarchy object for the tree.
247      */
248     selectPropertyRow = (propertyRowSelectedEvent:PropertyRowSelectedEvent) => {
249         console.log("==>" + this.constructor.name + ": selectPropertyRow " + propertyRowSelectedEvent.propertyModel.name);
250         let property = propertyRowSelectedEvent.propertyModel;
251         let instanceName = propertyRowSelectedEvent.instanceName;
252         this.propertyStructureHeader = null;
253
254         // Build hirarchy tree for the navigation and update propertiesNavigationData with it.
255         if(this.selectedInstanceData.originType !== ResourceType.VF) {
256             let simpleFlatProperty:Array<SimpleFlatProperty>;
257             if (property instanceof PropertyFEModel) {
258                 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(property, instanceName);
259             } else if (property instanceof DerivedFEProperty) {
260                 // Need to find parent PropertyFEModel
261                 let parentPropertyFEModel:PropertyFEModel = _.find(this.instanceFePropertiesMap[instanceName], (tmpFeProperty):boolean => {
262                     return property.propertiesName.indexOf(tmpFeProperty.name)===0;
263                 });
264                 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(parentPropertyFEModel, instanceName);
265             }
266             this.propertiesNavigationData = simpleFlatProperty;
267         }
268
269         // Update the header in the navigation tree with property name.
270         this.propertyStructureHeader = (property.propertiesName.split('#'))[0];
271
272         // Set selected property in table
273         this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName);
274         this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Property Structure']);
275     };
276
277
278     selectInstanceRow = ($event) => {//get instance name
279         this.selectedInstanceData =  _.find(this.instancesNavigationData, (instance:ComponentInstance) => {
280             return instance.name == $event;
281         });
282         this.renderer.invokeElementMethod(
283             this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
284     };
285
286     tabChanged = (event) => {
287         console.log("==>" + this.constructor.name + ": tabChanged " + event);
288         this.isInpusTabSelected = event.title === "Inputs";
289         this.propertyStructureHeader = null;
290         this.searchQuery = '';
291     };
292
293
294
295     /*** DECLARE PROPERTIES/INPUTS ***/
296     declareProperties = (): void => {
297         console.log("==>" + this.constructor.name + ": declareProperties");
298
299         let selectedProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
300         let selectedInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
301         let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
302
303         angular.forEach(instancesIds, (instanceId: string): void => {
304             let selectedInstanceData: ResourceInstance = this.instances.find(instance => instance.uniqueId == instanceId);
305             let originType: string = (selectedInstanceData) ? selectedInstanceData.originType : this.selectedInstanceType;
306             if (!this.isInput(originType)) {
307                 selectedProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
308             } else {
309                 selectedInputs[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
310             }
311         });
312
313         let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedInputs, selectedProperties);
314
315         this.componentServiceNg2
316             .createInput(this.component, inputsToCreate)
317             .subscribe(response => {
318                 this.setInputTabIndication(response.length);
319                 this.checkedPropertiesCount = 0;
320                 _.forEach(response, (input: InputBEModel) => {
321                     let newInput: InputFEModel = new InputFEModel(input);
322                     this.inputs.push(newInput);
323                     this.updatePropertyValueAfterDeclare(newInput);
324                 });
325             }, error => {}); //ignore error
326     };
327
328
329     updatePropertyValueAfterDeclare = (input: InputFEModel) => {
330         if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
331             let propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => {
332                 return feProperty.name == input.relatedPropertyName;
333             });
334             let inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined;
335             propertyForUpdatindVal.setAsDeclared(inputPath); //set prop as declared before assigning value
336             this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, inputPath);
337             this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, input.relatedPropertyValue, inputPath);
338         }
339     }
340
341     //used for declare button, to keep count of newly checked properties (and ignore declared properties)
342     updateCheckedPropertyCount = (increment: boolean): void => {
343         this.checkedPropertiesCount += (increment) ? 1 : -1;
344         console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount);
345     };
346
347     setInputTabIndication = (numInputs: number): void => {
348         this.renderer.invokeElementMethod(this.propertyInputTabs, 'setTabIndication', ['Inputs', numInputs]);
349     };
350
351     deleteInput = (input: InputFEModel) => {
352         console.log("==>" + this.constructor.name + ": deleteInput");
353         let inputToDelete = new PropertyBEModel(input);
354
355         this.componentServiceNg2
356             .deleteInput(this.component, inputToDelete)
357             .subscribe(response => {
358                 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
359
360                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
361                 this.onInstanceSelectedUpdate(this.selectedInstanceData);
362                 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
363
364                 // if (instanceFeProperties) {
365                 //     let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
366                 //         return prop.name == input.propertyName;
367                 //     });
368
369                 //     if (propToEnable) {
370                 //         if (propToEnable.name == response.inputPath) response.inputPath = null;
371                 //         propToEnable.setNonDeclared(response.inputPath);
372                 //         //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
373                 //         this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
374                 //     }
375                 // }
376             }, error => {}); //ignore error
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, false);
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             }, error => {}); //ignore error
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         this.searchQuery = '';
405     };
406
407     clickOnClearSearch = () => {
408         this.clearSearch();
409         this.selectFirstInstanceByDefault();
410         this.renderer.invokeElementMethod(
411             this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
412     };
413
414     private isInput = (instanceType:string):boolean =>{
415         return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC;
416     }
417
418 }