Angular upgrade
[osm/NG-UI.git] / src / services / SharedService.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 Provider for Shared Service
20  */
21 import { HttpErrorResponse, HttpHeaders } from '@angular/common/http';
22 import { EventEmitter, Injectable, Output } from '@angular/core';
23 import { FormArray, FormGroup } from '@angular/forms';
24 import { Router } from '@angular/router';
25 import { TranslateService } from '@ngx-translate/core';
26 import {
27     CONSTANTNUMBER,
28     DOMAINS,
29     ERRORDATA,
30     FILESETTINGS,
31     GETAPIURLHEADER,
32     PACKAGEINFO,
33     PAGERSMARTTABLE,
34     SMARTTABLECLASS,
35     TARSETTINGS,
36     TYPESECTION
37 } from 'CommonModel';
38 import { environment } from 'environment';
39 import * as HttpStatus from 'http-status-codes';
40 import * as untar from 'js-untar';
41 import { ActiveToast, ToastrService } from 'ngx-toastr';
42 import * as pako from 'pako';
43 import { RestService } from 'RestService';
44 import { Observable } from 'rxjs';
45 import { map } from 'rxjs/operators';
46
47 /** This is added globally by the tar.js library */
48 // eslint-disable-next-line @typescript-eslint/no-explicit-any
49 declare const Tar: any;
50
51 /**
52  * An Injectable is a class adorned with the @Injectable decorator function.
53  * @Injectable takes a metadata object that tells Angular how to compile and run module code
54  */
55 @Injectable({
56     providedIn: 'root'
57 })
58 /** Exporting a class @exports SharedService */
59 export class SharedService {
60     /** call the parent using event information @private */
61     @Output() public dataEvent: EventEmitter<{}> = new EventEmitter<{}>();
62
63     /** Variables to hold regexp pattern for URL */
64     public REGX_URL_PATTERN: RegExp = new RegExp(/^(http?|ftp|https):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z0-9]{2,15})(:((6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([0-5]{0,5})|([0-9]{1,4})))*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/);
65
66     /** Variables to hold regexp pattern for IP Address */
67     public REGX_IP_PATTERN: RegExp = new RegExp(/^(?:(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(?!$)|$)){4}$/);
68
69     /** Variables to hold regexp pattern for Port Number */
70     public REGX_PORT_PATTERN: RegExp = new RegExp(/^((6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([0-5]{0,5})|([0-9]{1,4}))$/);
71
72     /** Variables to hold regexp pattern for DPID */
73     public REGX_DPID_PATTERN: RegExp = new RegExp(/^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){7}$/);
74
75     /** Variable to hold regexp pattern for password */
76     public REGX_PASSWORD_PATTERN: RegExp = new RegExp(/^.*(?=.{8,})((?=.*[!@#$%^&*()\-_=+{};:,<.>]){1})(?=.*\d)((?=.*[a-z]){1})((?=.*[A-Z]){1}).*$/);
77
78     /** Variables to hold regexp pattern for Latitude */
79     public REGX_LAT_PATTERN: RegExp = new RegExp(/^(\+|-)?(?:90(?:(?:\.0{1,6})?)|(?:[0-9]|[1-8][0-9])(?:(?:\.[0-9]{1,15})?))$/);
80
81     /** Variables to hold regexp pattern for Longitude */
82     public REGX_LONG_PATTERN: RegExp = new RegExp(/^(\+|-)?(?:180(?:(?:\.0{1,6})?)|(?:[0-9]|[1-9][0-9]|1[0-7][0-9])(?:(?:\.[0-9]{1,15})?))$/);
83
84     /** Variables to hold maxlength for the description @public */
85     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
86     public MAX_LENGTH_DESCRIPTION: number = 500;
87
88     /** Variables to hold maxlength for the name @public */
89     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
90     public MAX_LENGTH_NAME: number = 50;
91
92     /** FormGroup instance added to the form @ html @public */
93     public formGroup: FormGroup;
94
95     /** Controls the go to top button on scroll  @public */
96     public showGotoTop: boolean;
97
98     /** Holds OSM Version value @public */
99     public osmVersion: string;
100
101     /** Holds Last Login Toaster Message @public */
102     public lastLoginMessage: string;
103
104     /** Holds Failed Attempts Toaster Message @public */
105     public failedAttemptsMessage: string;
106
107     /** Holds No Of Days Toaster Message @public */
108     public daysMessage: string;
109
110     /** express number for time manupulation -2 */
111     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
112     private epochTimeMinus2: number = -2;
113
114     /** express number for time manupulation 1000 */
115     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
116     private epochTime1000: number = 1000;
117
118     /** express number for time manupulation 60 */
119     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
120     private epochTime60: number = 60;
121
122     /** express number for time manupulation 24 */
123     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
124     private epochTime24: number = 24;
125
126     /** Random string generator length */
127     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
128     private randomStringLength: number = 4;
129
130     /** express number for rgb manipulation */
131     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
132     private colourHour: number = 10;
133
134     /** express number for rgb manipulation*/
135     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
136     private colourMin: number = 5;
137
138     /** Instance of the rest service @private */
139     private restService: RestService;
140
141     /** Service holds the router information @private */
142     private router: Router;
143
144     /** Check for the root directory @private */
145     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
146     private directoryCount: number = 2;
147
148     /** express number for time manupulation 1000 */
149     private toasterSettings: {} = {
150         enableHtml: true,
151         closeButton: true,
152         timeOut: 2000
153     };
154
155     /** Contains tranlsate instance @private */
156     private translateService: TranslateService;
157
158     /** Contains toaster instance @private */
159     private toaster: ToastrService;
160
161     constructor(restService: RestService, router: Router, translateService: TranslateService, toaster: ToastrService) {
162         this.restService = restService;
163         this.router = router;
164         this.translateService = translateService;
165         this.toaster = toaster;
166     }
167
168     /** convert epoch time function @public */
169     public convertEpochTime(unixtimestamp: number): string {
170         if (!isNullOrUndefined(unixtimestamp)) {
171             const monthsArr: string[] = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
172                 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
173             const date: Date = new Date(unixtimestamp * this.epochTime1000);
174             const year: number = date.getFullYear();
175             const month: string = monthsArr[date.getMonth()];
176             const day: number = date.getDate();
177             const hours: number = date.getHours();
178             const minutes: string = '0' + date.getMinutes();
179             const seconds: string = '0' + date.getSeconds();
180             // eslint-disable-next-line deprecation/deprecation
181             return month + '-' + day + '-' + year + ' ' + hours + ':' + minutes.substr(this.epochTimeMinus2) + ':'
182                 // eslint-disable-next-line deprecation/deprecation
183                 + seconds.substr(this.epochTimeMinus2);
184         }
185         return this.translateService.instant('NODATE');
186     }
187
188     /** convert epoch time function to No of days @public */
189     public converEpochToDays(date: string): number {
190         if (!isNullOrUndefined(date)) {
191             const today: Date = new Date();
192             const accountDate: Date = new Date(date);
193             const toasterDate: number = (accountDate.getTime() -
194                 today.getTime()) / this.epochTime1000 / this.epochTime60 / this.epochTime60 / this.epochTime24;
195             if (toasterDate >= 0 || toasterDate < 1) {
196                 return Math.round(toasterDate);
197             }
198             return Math.floor(toasterDate);
199         }
200         return this.translateService.instant('N/A');
201     }
202
203     /** show toaster for password & account expiry @public */
204     public showToaster(lastLogin: string, failedAttempts: string, passwordNoOfDays: string,
205         accountNoOfDays: string, passwordExpireMessage: string, accountExpireMessage: string,
206         passwordMessage: string, accountMessage: string): ActiveToast<string> {
207         this.lastLoginMessage = this.translateService.instant('PAGE.LOGIN.LASTACCESS');
208         this.failedAttemptsMessage = this.translateService.instant('PAGE.LOGIN.FAILED');
209         return this.toaster.info(this.lastLoginMessage + ':' + '&nbsp' + lastLogin +
210             '</br>' + this.failedAttemptsMessage + ':' + '&nbsp' + failedAttempts +
211             '</br>' + passwordExpireMessage + '&nbsp' + passwordNoOfDays + '&nbsp' + passwordMessage +
212             '</br>' + accountExpireMessage + '&nbsp' + accountNoOfDays + '&nbsp' + accountMessage,
213             this.translateService.instant('PAGE.LOGIN.LOGINHISTORY'), this.toasterSettings);
214     }
215
216     /** show toaster for password expiry @public */
217     public passwordToaster(lastLogin: string, failedAttempts: string, passwordNoOfDays: string,
218         passwordExpireMessage: string, passwordMessage: string): ActiveToast<string> {
219         this.lastLoginMessage = this.translateService.instant('PAGE.LOGIN.LASTACCESS');
220         this.failedAttemptsMessage = this.translateService.instant('PAGE.LOGIN.FAILED');
221         return this.toaster.info(this.lastLoginMessage + ':' + '&nbsp' + lastLogin +
222             '</br>' + this.failedAttemptsMessage + ':' + '&nbsp' + failedAttempts +
223             '</br>' + passwordExpireMessage + '&nbsp' + passwordNoOfDays + '&nbsp' + passwordMessage,
224             this.translateService.instant('PAGE.LOGIN.LOGINHISTORY'), this.toasterSettings);
225     }
226
227     /** show toaster for account expiry @public */
228     public accountToaster(lastLogin: string, failedAttempts: string,
229         accountNoOfDays: string, accountExpireMessage: string, accountMessage: string): ActiveToast<string> {
230         this.lastLoginMessage = this.translateService.instant('PAGE.LOGIN.LASTACCESS');
231         this.failedAttemptsMessage = this.translateService.instant('PAGE.LOGIN.FAILED');
232         return this.toaster.info(this.lastLoginMessage + ':' + '&nbsp' + lastLogin +
233             '</br>' + this.failedAttemptsMessage + ':' + '&nbsp' + failedAttempts +
234             '</br>' + accountExpireMessage + '&nbsp' + accountNoOfDays + '&nbsp' + accountMessage,
235             this.translateService.instant('PAGE.LOGIN.LOGINHISTORY'), this.toasterSettings);
236     }
237
238     /** Download Files function @public */
239     public downloadFiles(name: string, binaryData: Blob[], filetype: string): void {
240         const downloadLink: HTMLAnchorElement = document.createElement('a');
241         downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, { type: filetype }));
242         if (name !== undefined) {
243             // eslint-disable-next-line @typescript-eslint/no-explicit-any
244             const newVariable: any = window.navigator;
245             if (newVariable.msSaveOrOpenBlob) {
246                 newVariable.msSaveBlob(new Blob(binaryData, { type: filetype }), 'OSM_Export_' + name + '.tar.gz');
247             } else {
248                 downloadLink.setAttribute('download', 'OSM_Export_' + name + '.tar.gz');
249                 document.body.appendChild(downloadLink);
250                 downloadLink.click();
251             }
252         }
253     }
254
255     /** Call this method after delete perform action is completed in the ng-smart-table data @public */
256     public callData(): void {
257         this.dataEvent.emit();
258     }
259
260     /** Generate random string @public */
261     public randomString(): string {
262         let result: string = '';
263         for (let randomStringRef: number = this.randomStringLength; randomStringRef > 0; --randomStringRef) {
264             result += new Date().getSeconds();
265         }
266         return result;
267     }
268
269     /** Function to read uploaded file String @public */
270     public async getFileString(files: FileList, fileType: string): Promise<string | ArrayBuffer> {
271         const reader: FileReader = new FileReader();
272         return new Promise<string | ArrayBuffer>((resolve: Function, reject: Function): void => {
273             if (this.vaildataFileInfo(files[0], fileType)) {
274                 this.readFileContent(reader, files[0], fileType);
275             } else {
276                 reject('typeError');
277             }
278             reader.onload = (): void => {
279                 if (reader.result === null) {
280                     reject('contentError');
281                 }
282                 resolve(reader.result);
283             };
284             reader.onerror = (event: Event): void => {
285                 reject('contentError');
286             };
287         });
288     }
289
290     /** Method to handle tar and tar.gz file for shared YAML file content @public */
291     public async targzFile(packageInfo: PACKAGEINFO): Promise<string | ArrayBuffer> {
292         return new Promise<string | ArrayBuffer>((resolve: Function, reject: Function): void => {
293             const httpOptions: GETAPIURLHEADER = this.getHttpOptions();
294             let apiUrl: string = '';
295             apiUrl = packageInfo.packageType === 'nsd' ? environment.NSDESCRIPTORS_URL + '/' + packageInfo.id + '/nsd_content' :
296                 environment.VNFPACKAGES_URL + '/' + packageInfo.id + '/package_content';
297             this.restService.getResource(apiUrl, httpOptions).subscribe((response: ArrayBuffer): void => {
298                 try {
299                     // eslint-disable-next-line @typescript-eslint/no-explicit-any
300                     const tar: any = new Tar();
301                     const originalInput: Uint8Array = pako.inflate(response, { to: 'Uint8Array' });
302                     untar(originalInput.buffer).then((extractedFiles: TARSETTINGS[]): void => {
303                         const getFoldersFiles: {}[] = extractedFiles;
304                         const folderNameStr: string = extractedFiles[0].name;
305                         getFoldersFiles.forEach((value: TARSETTINGS): void => {
306                             const fileValueObj: FILESETTINGS = this.createFileValueObject(value);
307                             const getRootFolder: string[] = value.name.split('/');
308                             if (value.name.startsWith(folderNameStr) &&
309                                 (value.name.endsWith('.yaml') || value.name.endsWith('.yml')) &&
310                                 getRootFolder.length === this.directoryCount) {
311                                 tar.append(value.name, packageInfo.descriptor, fileValueObj);
312                             } else {
313                                 if (value.type !== 'L') {
314                                     tar.append(value.name, new Uint8Array(value.buffer), fileValueObj);
315                                 }
316                             }
317                         });
318                         const out: Uint8Array = tar.out;
319                         const originalOutput: Uint8Array = pako.gzip(out);
320                         resolve(originalOutput.buffer);
321                     }, (err: string): void => {
322                         reject('');
323                     });
324                 } catch (e) {
325                     reject('');
326                 }
327             }, (error: HttpErrorResponse): void => {
328                 if (error.status === HttpStatus.NOT_FOUND || error.status === HttpStatus.UNAUTHORIZED) {
329                     this.router.navigateByUrl('404', { skipLocationChange: true }).catch((): void => {
330                         // Catch Navigation Error
331                     });
332                 } else {
333                     this.restService.handleError(error, 'get');
334                     reject('');
335                 }
336             });
337         });
338     }
339
340     /** Method to return the file information @public */
341     public createFileValueObject(value: TARSETTINGS): FILESETTINGS {
342         return {
343             type: value.type,
344             linkname: value.linkname,
345             owner: value.uname,
346             group: value.gname
347         };
348     }
349
350     /** Method to check given string is JSON or not @public */
351     public checkJson(jsonString: string): boolean {
352         jsonString = jsonString.replace(/'/g, '"');
353         try {
354             JSON.parse(jsonString);
355         } catch (e) {
356             return false;
357         }
358         return true;
359     }
360
361     /** Clean the form before submit @public */
362     public cleanForm(formGroup: FormGroup, formName?: String): void {
363         Object.keys(formGroup.controls).forEach((key: string) => {
364             if ((!isNullOrUndefined((formGroup.get(key) as FormArray | FormGroup).controls)) && key !== 'config') {
365                 // eslint-disable-next-line @typescript-eslint/no-shadow
366                 for (const { item, index } of (formGroup.get(key).value).map((item: {}, index: number) => ({ item, index }))) {
367                     // eslint-disable-next-line security/detect-object-injection
368                     const newFormGroup: FormGroup = (formGroup.get(key) as FormArray).controls[index] as FormGroup;
369                     this.cleanForm(newFormGroup);
370                 }
371             } else if (formGroup.get(key).value !== undefined && formGroup.get(key).value !== null && key !== 'config') {
372                 if (!Array.isArray(formGroup.get(key).value)) {
373                     if (typeof formGroup.get(key).value === 'string') {
374                         formGroup.get(key).setValue(formGroup.get(key).value.trim());
375                     }
376                 }
377             } else if (key === 'config' && formName === 'vim') {
378                 const newFormGroup: FormGroup = formGroup.get(key) as FormGroup;
379                 this.cleanForm(newFormGroup);
380             }
381         });
382     }
383
384     /** Method to return the config of pager value for ngSmarttable @public */
385     public paginationPagerConfig(): PAGERSMARTTABLE {
386         return {
387             display: true,
388             perPage: environment.paginationNumber
389         };
390     }
391
392     /** Method to return the class for the table for ngSmarttable @public */
393     public tableClassConfig(): SMARTTABLECLASS {
394         return {
395             class: 'table list-data'
396         };
397     }
398
399     /** Method to return all languages name and its code @public */
400     public languageCodeList(): {}[] {
401         return [
402             { code: 'en', language: 'English' },
403             { code: 'es', language: 'Spanish' },
404             { code: 'pt', language: 'Portuguese' },
405             { code: 'de', language: 'German' }
406         ];
407     }
408
409     /** Fetch OSM Version @public */
410     public fetchOSMVersion(): void {
411         this.restService.getResource(environment.OSM_VERSION_URL).subscribe((res: { version: string }): void => {
412             const version: string[] = res.version.split('+');
413             if (!isNullOrUndefined(version[0])) {
414                 this.osmVersion = version[0];
415                 sessionStorage.setItem('version', version[0]);
416             } else {
417                 this.osmVersion = null;
418             }
419         }, (error: ERRORDATA): void => {
420             this.osmVersion = null;
421             this.restService.handleError(error, 'get');
422         });
423     }
424
425     /** Random RGB color code generator @public */
426     public generateColor(): string {
427         const x: number = Math.floor((new Date().getHours()) * this.colourHour);
428         const y: number = Math.floor((new Date().getMinutes()) * this.colourMin);
429         const z: number = Math.floor((new Date().getSeconds()) * this.colourMin);
430         return 'rgb(' + x + ',' + y + ',' + z + ')';
431     }
432
433     /** Add custom name/tag to the dropdown @public */
434     public addCustomTag(tag: string): string {
435         return tag;
436     }
437
438     /** Fetch file extension @public */
439     public fetchFileExtension(fileInfo: FileList): string {
440         return fileInfo[0].name.substring(fileInfo[0].name.lastIndexOf('.') + 1);
441     }
442
443     /** Get domain name @private */
444     public getDomainName(): Observable<TYPESECTION[]> {
445         return this.restService.getResource(environment.DOMAIN_URL).pipe(map((domains: DOMAINS): TYPESECTION[] => {
446             const domainList: TYPESECTION[] = [];
447             try {
448                 let domainNames: string[] = [];
449                 if (!isNullOrUndefined(domains.project_domain_name)) {
450                     domainNames = domainNames.concat(domains.project_domain_name.split(','));
451                 }
452                 if (!isNullOrUndefined(domains.user_domain_name)) {
453                     domainNames = domainNames.concat(domains.user_domain_name.split(','));
454                 }
455                 domainNames = Array.from(new Set(domainNames));
456                 if (domainNames.length > 0) {
457                     domainNames.forEach((domainName: string): void => {
458                         if (!domainName.endsWith(':ro')) {
459                             domainList.push({ title: domainName, value: domainName });
460                         }
461                     });
462                 }
463                 return domainList;
464             } catch (e) {
465                 return domainList;
466             }
467         }));
468     }
469
470     /** Sorting the list based on date @public */
471     public compareFunction = (dir: number, a: string, b: string): number => {
472         const first: number = new Date(a).getTime();
473         const second: number = new Date(b).getTime();
474         if (first < second) {
475             return -1 * dir;
476         }
477         if (first > second) {
478             return dir;
479         }
480         return 0;
481     };
482
483     /** Method to validate file extension and size @private */
484     private vaildataFileInfo(fileInfo: File, fileType: string): boolean {
485         const extension: string = fileInfo.name.substring(fileInfo.name.lastIndexOf('.') + 1);
486         const packageSize: number = CONSTANTNUMBER.oneMB * environment.packageSize;
487         if (fileType === 'yaml' && (extension.toLowerCase() === 'yaml' || extension.toLowerCase() === 'yml')
488             && fileInfo.size <= packageSize) {
489             return true;
490         } else if (extension.toLowerCase() === fileType && fileInfo.size <= packageSize) {
491             return true;
492         }
493         return false;
494     }
495
496     /** Method to read file content based on type @private */
497     private readFileContent(reader: FileReader, fileInfo: File, fileType: string): void {
498         if (fileType === 'gz') {
499             reader.readAsArrayBuffer(fileInfo);
500         } else {
501             reader.readAsText(fileInfo);
502         }
503     }
504
505     /** Method to handle http options @public */
506     private getHttpOptions(): GETAPIURLHEADER {
507         return {
508             headers: new HttpHeaders({
509                 Accept: 'application/gzip, application/json',
510                 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
511             }),
512             responseType: 'arraybuffer'
513         };
514     }
515 }
516
517 /** Method to handle null or undefined @public */
518 // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
519 export const isNullOrUndefined = (data: any): boolean => data === null || data === undefined;
520