Fix for Bug 1495 NG-UI does not start on Air-gapped environments.
[osm/NG-UI.git] / src / app / vim-accounts / vim-account-details / VimAccountDetailsComponent.ts
1 /*
2  Copyright 2020 TATA ELXSI
3
4  Licensed under the Apache License, Version 2.0 (the 'License');
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8     http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15
16  Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
17 */
18 /**
19  * @file Vim Account Component.
20  */
21 import { Component, Injector, OnInit } from '@angular/core';
22 import { Router } from '@angular/router';
23 import { TranslateService } from '@ngx-translate/core';
24 import { CONFIGCONSTANT, CONSTANTNUMBER, ERRORDATA, VIM_TYPES } from 'CommonModel';
25 import { DataService } from 'DataService';
26 import { environment } from 'environment';
27 import { LocalDataSource } from 'ng2-smart-table';
28 import { NSInstanceDetails } from 'NSInstanceModel';
29 import Feature from 'ol/Feature';
30 import Point from 'ol/geom/Point';
31 import { defaults as defaultInteractions } from 'ol/interaction';
32 import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';
33 import Map from 'ol/Map';
34 import Overlay from 'ol/Overlay';
35 import { fromLonLat } from 'ol/proj.js';
36 import OSM from 'ol/source/OSM';
37 import VectorSource from 'ol/source/Vector';
38 import { Icon, Style } from 'ol/style';
39 import View from 'ol/View';
40 import { RestService } from 'RestService';
41 import { Subscription } from 'rxjs';
42 import { SharedService } from 'SharedService';
43 import { VimAccountDetails, VIMData } from 'VimAccountModel';
44 import { VimAccountsActionComponent } from 'VimAccountsAction';
45 /**
46  * Creating component
47  * @Component takes VimAccountDetailsComponent.html as template url
48  */
49 @Component({
50     selector: 'app-vim-account-details',
51     templateUrl: './VimAccountDetailsComponent.html',
52     styleUrls: ['./VimAccountDetailsComponent.scss']
53 })
54 /** Exporting a class @exports VimAccountDetailsComponent */
55 export class VimAccountDetailsComponent implements OnInit {
56     /** To inject services @public */
57     public injector: Injector;
58     /** handle translate @public */
59     public translateService: TranslateService;
60     /** initially show the map container@public */
61     public showMap: boolean;
62     /**  hide and show popup @public */
63     public popupShow: boolean = false;
64     /** Data of smarttable populate through LocalDataSource @public */
65     public dataSource: LocalDataSource = new LocalDataSource();
66     /** Columns list of the smart table @public */
67     public columnLists: object = {};
68     /** Settings for smarttable to populate the table with columns @public */
69     public settings: object = {};
70     /** initially hide the list@private */
71     public showList: boolean;
72     /** to store locations name @public */
73     public getLocation: GetLocation[];
74     /** Contains content for map popup @public */
75     public popupData: string;
76     /** Check the loading results @public */
77     public isLoadingResults: boolean = true;
78     /** Give the message for the loading @public */
79     public message: string = 'PLEASEWAIT';
80     /** Class for empty and present data @public */
81     public checkDataClass: string;
82     /** Formation of appropriate Data for LocalDatasource @public */
83     public vimData: VIMData[];
84     /** operational State init data @public */
85     public operationalStateFirstStep: string = CONFIGCONSTANT.vimOperationalStateFirstStep;
86     /** operational State running data @public */
87     public operationalStateSecondStep: string = CONFIGCONSTANT.vimOperationalStateStateSecondStep;
88     /** operational State failed data @public */
89     public operationalStateThirdStep: string = CONFIGCONSTANT.vimOperationalStateThirdStep;
90     /** NS Instances operational State failed data @public */
91     public nsinstancesoperationalStateRunning: string = CONFIGCONSTANT.operationalStateSecondStep;
92     /** Instance of the rest service @private */
93     private restService: RestService;
94     /** dataService to pass the data from one component to another @private */
95     private dataService: DataService;
96     /** Contains all methods related to shared @private */
97     private sharedService: SharedService;
98     /** Holds the instance of router class @private */
99     private router: Router;
100     /** ns INstance Data @private */
101     private nsData: NSInstanceDetails[];
102     /** map object @private */
103     private map: Map;
104     /**  used to bind marker @private */
105     private vectorSource: VectorSource;
106     /** used to bind vectorSource @private */
107     private vectorLayer: VectorLayer;
108     /** marker @private */
109     private marker: Feature;
110     /** latitude @private */
111     private lat: number;
112     /** longitude @private */
113     private lng: number;
114     /**  each vector layer of marker is pushed to layers array @private */
115     private layers: VectorLayer[] = [];
116     /** locationData @private */
117     private locationData: VimAccountDetails[];
118     /** popup array @private */
119     private overLay: Overlay[] = [];
120     /** Instance of subscriptions @private */
121     private generateDataSub: Subscription;
122
123     constructor(injector: Injector) {
124         this.injector = injector;
125         this.restService = this.injector.get(RestService);
126         this.dataService = this.injector.get(DataService);
127         this.sharedService = this.injector.get(SharedService);
128         this.router = this.injector.get(Router);
129         this.translateService = this.injector.get(TranslateService);
130     }
131     /** Lifecyle Hooks the trigger before component is instantiate @public */
132     public ngOnInit(): void {
133         this.osmMapView();
134         this.listView();
135         this.generateColumns();
136         this.generateSettings();
137         this.generateData();
138         this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
139     }
140
141     /** smart table Header Colums @public */
142     public generateColumns(): void {
143         this.columnLists = {
144             name: { title: this.translateService.instant('NAME'), width: '15%', sortDirection: 'asc' },
145             identifier: { title: this.translateService.instant('IDENTIFIER'), width: '25%' },
146             type: {
147                 title: this.translateService.instant('TYPE'), width: '15%',
148                 filter: {
149                     type: 'list',
150                     config: {
151                         selectText: 'Select',
152                         list: VIM_TYPES
153                     }
154                 }
155             },
156             operationalState: {
157                 title: this.translateService.instant('OPERATIONALSTATUS'), width: '15%', type: 'html',
158                 filter: {
159                     type: 'list',
160                     config: {
161                         selectText: 'Select',
162                         list: [
163                             { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep },
164                             { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep },
165                             { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep }
166                         ]
167                     }
168                 },
169                 valuePrepareFunction: (cell: VIMData, row: VIMData): string => {
170                     if (row.operationalState === this.operationalStateFirstStep) {
171                         return `<span class="icon-label" title="${row.operationalState}">
172                         <i class="fas fa-clock text-warning"></i>
173                         </span>`;
174                     } else if (row.operationalState === this.operationalStateSecondStep) {
175                         return `<span class="icon-label" title="${row.operationalState}">
176                         <i class="fas fa-check-circle text-success"></i>
177                         </span>`;
178                     } else if (row.operationalState === this.operationalStateThirdStep) {
179                         return `<span class="icon-label" title="${row.operationalState}">
180                         <i class="fas fa-times-circle text-danger"></i>
181                         </span>`;
182                     } else {
183                         return `<span>${row.operationalState}</span>`;
184                     }
185                 }
186             },
187             description: { title: this.translateService.instant('DESCRIPTION'), width: '25%' },
188             Actions: {
189                 name: 'Action', width: '5%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom',
190                 valuePrepareFunction: (cell: VIMData, row: VIMData): VIMData => row,
191                 renderComponent: VimAccountsActionComponent
192             }
193         };
194     }
195
196     /** smart table Data Settings @public */
197     public generateSettings(): void {
198         this.settings = {
199             edit: {
200                 editButtonContent: '<i class="fa fa-edit" title="Edit"></i>',
201                 confirmSave: true
202             },
203             delete: {
204                 deleteButtonContent: '<i class="far fa-trash-alt" title="delete"></i>',
205                 confirmDelete: true
206             },
207             columns: this.columnLists,
208             actions: {
209                 add: false,
210                 edit: false,
211                 delete: false,
212                 position: 'right'
213             },
214             attr: this.sharedService.tableClassConfig(),
215             pager: this.sharedService.paginationPagerConfig(),
216             noDataMessage: this.translateService.instant('NODATAMSG')
217         };
218     }
219
220     /** smart table listing manipulation @public */
221     public onChange(perPageValue: number): void {
222         this.dataSource.setPaging(1, perPageValue, true);
223     }
224
225     /** smart table listing manipulation @public */
226     public onUserRowSelect(event: MessageEvent): void {
227         Object.assign(event.data, { page: 'vim-account' });
228         this.dataService.changeMessage(event.data);
229     }
230
231     /** on Navigate to Composer Page @public */
232     public composeVIM(): void {
233         this.router.navigate(['vim/new']).catch(() => {
234             //empty block
235         });
236     }
237
238     /** To show map conatainer @public */
239     public mapView(): void {
240         this.showList = true;
241         this.showMap = false;
242     }
243     /** To show listview @public */
244     public listView(): void {
245         this.showMap = true;
246         this.showList = false;
247     }
248     /** Load the datasource appropriatetype @public */
249     public loadDatasource(getdata: VIMData[]): void {
250         if (getdata.length > 0) {
251             this.checkDataClass = 'dataTables_present';
252         } else {
253             this.checkDataClass = 'dataTables_empty';
254         }
255         this.dataSource.load(getdata).then((data: boolean) => {
256             //empty block
257         }).catch(() => {
258             //empty block
259         });
260     }
261
262     /** Generate generateVIMData object from loop and return for the datasource @public */
263     public generateVIMData(vimAccountData: VimAccountDetails): VIMData {
264         return {
265             name: vimAccountData.name,
266             identifier: vimAccountData._id,
267             type: vimAccountData.vim_type,
268             operationalState: vimAccountData._admin.operationalState,
269             description: vimAccountData.description,
270             instancesData: this.nsData
271         };
272     }
273
274     /**
275      * Lifecyle hook which get trigger on component destruction
276      */
277     public ngOnDestroy(): void {
278         this.generateDataSub.unsubscribe();
279     }
280
281     /** Fetching the data from server to Load in the smarttable @protected */
282     private generateData(): void {
283         this.isLoadingResults = true;
284         this.vimData = [];
285         this.getNSData().then((): void => {
286             this.restService.getResource(environment.VIMACCOUNTS_URL).subscribe((vimAccountsData: VimAccountDetails[]) => {
287                 this.locationData = vimAccountsData;
288                 vimAccountsData.forEach((vimAccountData: VimAccountDetails) => {
289                     const vimDataObj: VIMData = this.generateVIMData(vimAccountData);
290                     this.vimData.push(vimDataObj);
291                 });
292                 this.loadDatasource(this.vimData);
293                 this.removeLayersOverLay();
294                 this.arrayOfLocation();
295                 this.isLoadingResults = false;
296             }, (error: ERRORDATA) => {
297                 this.restService.handleError(error, 'get');
298                 this.isLoadingResults = false;
299             });
300         }).catch((error: ERRORDATA): void => {
301             this.restService.handleError(error, 'get');
302             this.isLoadingResults = false;
303         });
304     }
305
306     /** fetching the nsdata @private */
307     private async getNSData(): Promise<Boolean> {
308         return new Promise<Boolean>((resolve: Function, reject: Function): void => {
309             this.nsData = [];
310             this.restService.getResource(environment.NSDINSTANCES_URL).subscribe((nsdInstancesData: NSInstanceDetails[]) => {
311                 const nsRunningInstancesData: NSInstanceDetails[] = nsdInstancesData.filter((instancesData: NSInstanceDetails) =>
312                     instancesData['operational-status'] === this.nsinstancesoperationalStateRunning);
313                 this.nsData = nsRunningInstancesData;
314                 resolve(true);
315             }, (error: ERRORDATA) => {
316                 this.restService.handleError(error, 'get');
317                 resolve(true);
318             });
319         });
320     }
321
322     /** create map view @private */
323     private osmMapView(): void {
324         this.map = new Map({
325             target: 'map',
326             layers: [new TileLayer({
327                 source: new OSM()
328             })],
329             interactions: defaultInteractions({
330                 mouseWheelZoom: true
331             }),
332             view: new View({
333                 center: fromLonLat([CONSTANTNUMBER.osmapviewlong, CONSTANTNUMBER.osmapviewlat]),
334                 zoom: 3,
335                 minZoom: 1.5
336             })
337         });
338     }
339
340     /** remove the layers and overlay @private */
341     private removeLayersOverLay(): void {
342         this.layers.forEach((layer: VectorLayer) => {
343             this.map.removeLayer(layer);
344         });
345         this.overLay.forEach((lay: Overlay) => {
346             this.map.removeOverlay(lay);
347         });
348     }
349
350     /** filter locations from vimaccounts @private */
351     private arrayOfLocation(): void {
352         this.getLocation = [];
353         this.locationData.filter((item: VimAccountDetails) => {
354             if (item.hasOwnProperty('config')) {
355                 if (item.config.hasOwnProperty('location')) {
356                     this.getLocation.push({ name: item.name, location: item.config.location, id: item._id });
357                 }
358             }
359         });
360         if (this.getLocation !== []) {
361             this.getLocation.filter((loc: GetLocation) => {
362                 if (loc.location !== '') {
363                     const getLatLong: string[] = loc.location.split(',');
364                     this.lng = +getLatLong[CONSTANTNUMBER.splitLongitude];
365                     this.lat = +getLatLong[CONSTANTNUMBER.splitLatitude];
366                     this.addMarker(getLatLong[0], loc.id, loc.name);
367                 }
368             });
369         }
370     }
371     /** add markers on map @private */
372     private addMarker(loc: string, id: string, name: string): void {
373         const container: HTMLElement = document.getElementById('popup');
374         const closer: HTMLElement = document.getElementById('popup-closer');
375         this.popupShow = true;
376         const overlay: Overlay = this.addOverlay(container);
377         this.marker = this.addFeature(loc, id, name);
378         this.setStyleMarker();
379         this.setVectorSource();
380         this.setVectorLayer();
381         this.map.addLayer(this.vectorLayer);
382         this.layers.push(this.vectorLayer);
383         if (this.layers.length === 1) {
384             this.markerClickEvent(closer, overlay);
385         }
386     }
387     /** Create an overlay to anchor the popup to the map @private */
388     private addOverlay(container: HTMLElement): Overlay {
389         return new Overlay({
390             element: container,
391             autoPan: true,
392             autoPanAnimation: {
393                 duration: 250
394             }
395         });
396     }
397     /** Return the Feature of creating a marker in the map @private */
398     private addFeature(loc: string, id: string, name: string): Feature {
399         return new Feature({
400             geometry: new Point(fromLonLat([this.lng, this.lat])),
401             location: loc,
402             Id: id,
403             vimName: name
404         });
405     }
406     /** Set the style of the marker @private */
407     private setStyleMarker(): void {
408         this.marker.setStyle(new Style({
409             image: new Icon(({
410                 crossOrigin: 'anonymous',
411                 src: 'assets/images/map-icon.png'
412             }))
413         }));
414     }
415     /** Set the map vector source @private */
416     private setVectorSource(): void {
417         this.vectorSource = new VectorSource({
418             features: [this.marker]
419         });
420     }
421     /** Set the map vector layer @private */
422     private setVectorLayer(): void {
423         this.vectorLayer = new VectorLayer({
424             source: this.vectorSource
425         });
426     }
427     /** Add a click handler to the map to render the popup. @private */
428     private markerClickEvent(closer: HTMLElement, overlay: Overlay): void {
429         // tslint:disable-next-line: no-any
430         this.map.on('singleclick', (evt: any) => {
431             const feature: Feature = this.map.forEachFeatureAtPixel(evt.pixel,
432                 (f: Feature) => {
433                     return f;
434                 });
435             if (feature) {
436                 this.setCoordinates(feature, overlay);
437             } else {
438                 this.map.removeOverlay(overlay);
439             }
440         });
441         /** Handle close event for overlay */
442         closer.onclick = (): boolean => {
443             overlay.setPosition(undefined);
444             closer.blur();
445             return false;
446         };
447     }
448     /** Set the coordinates point if the feature is available @private */
449     // tslint:disable-next-line: no-any
450     private setCoordinates(feature: any, overlay: Overlay): void {
451         this.popupData = '';
452         this.popupData += '<h3 class="popover-header">' + feature.values_.vimName + '- (' + feature.values_.location + ')</h3>';
453         this.popupData += '<ul class="list-unstyled m-2">';
454         const instnaceData: NSInstanceDetails[] = this.nsData.filter((item: NSInstanceDetails) => {
455             if (item.datacenter === feature.values_.Id) {
456                 this.popupData += '<li class="m-2"><a class="d-block text-truncate" target="_parent" href="instances/ns/' + item._id + '"><i class="fa-sitemap fas icons mr-1"></i>'
457                     + item.name + '</a></li>';
458                 return item;
459             }
460         });
461         if (instnaceData.length === 0) {
462             this.popupData += '<li class="m-2">' + this.translateService.instant('PAGE.DASHBOARD.NOINSTANCES') + '</li>';
463         }
464         this.popupData += '</ul>';
465         const coordinates: number[] = feature.getGeometry().getCoordinates();
466         overlay.setPosition(coordinates);
467         this.overLay.push(overlay);
468         this.map.addOverlay(overlay);
469     }
470 }
471
472 /** interface for get location */
473 interface GetLocation {
474     location: string;
475     id: string;
476     name?: string;
477 }