Fix Bug 2121: NG-UI uses unmaintained Chokidar version
[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 { isNullOrUndefined } from 'util';
22 import { HttpErrorResponse, HttpHeaders } from '@angular/common/http';
23 import { EventEmitter, Injectable, Output } from '@angular/core';
24 import { FormArray, FormGroup } from '@angular/forms';
25 import { Router } from '@angular/router';
26 import { TranslateService } from '@ngx-translate/core';
27 import {
28     CONSTANTNUMBER,
29     DOMAINS,
30     ERRORDATA,
31     FILESETTINGS,
32     GETAPIURLHEADER,
33     PACKAGEINFO,
34     PAGERSMARTTABLE,
35     SMARTTABLECLASS,
36     TARSETTINGS,
37     TYPESECTION
38 } from 'CommonModel';
39 import { environment } from 'environment';
40 import * as HttpStatus from 'http-status-codes';
41 import * as untar from 'js-untar';
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     /** express number for time manupulation -2 */
102     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
103     private epochTimeMinus2: number = -2;
104
105     /** express number for time manupulation 1000 */
106     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
107     private epochTime1000: number = 1000;
108
109     /** Random string generator length */
110     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
111     private randomStringLength: number = 4;
112
113     /** Instance of the rest service @private */
114     private restService: RestService;
115
116     /** Service holds the router information @private */
117     private router: Router;
118
119     /** Random color string generator length @private */
120     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
121     private colorStringLength: number = 256;
122
123     /** Check for the root directory @private */
124     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
125     private directoryCount: number = 2;
126
127     /** Contains tranlsate instance @private */
128     private translateService: TranslateService;
129
130     constructor(restService: RestService, router: Router, translateService: TranslateService) {
131         this.restService = restService;
132         this.router = router;
133         this.translateService = translateService;
134     }
135
136     /** convert epoch time function @public */
137     public convertEpochTime(unixtimestamp: number): string {
138         if (!isNullOrUndefined(unixtimestamp)) {
139             const monthsArr: string[] = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
140                 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
141             const date: Date = new Date(unixtimestamp * this.epochTime1000);
142             const year: number = date.getFullYear();
143             const month: string = monthsArr[date.getMonth()];
144             const day: number = date.getDate();
145             const hours: number = date.getHours();
146             const minutes: string = '0' + date.getMinutes();
147             const seconds: string = '0' + date.getSeconds();
148             // eslint-disable-next-line deprecation/deprecation
149             return month + '-' + day + '-' + year + ' ' + hours + ':' + minutes.substr(this.epochTimeMinus2) + ':'
150                 // eslint-disable-next-line deprecation/deprecation
151                 + seconds.substr(this.epochTimeMinus2);
152         }
153         return this.translateService.instant('NODATE');
154     }
155
156     /** Download Files function @public */
157     public downloadFiles(name: string, binaryData: Blob[], filetype: string): void {
158         const downloadLink: HTMLAnchorElement = document.createElement('a');
159         downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, { type: filetype }));
160         if (name !== undefined) {
161             // eslint-disable-next-line @typescript-eslint/no-explicit-any
162             const newVariable: any = window.navigator;
163             if (newVariable.msSaveOrOpenBlob) {
164                 newVariable.msSaveBlob(new Blob(binaryData, { type: filetype }), 'OSM_Export_' + name + '.tar.gz');
165             } else {
166                 downloadLink.setAttribute('download', 'OSM_Export_' + name + '.tar.gz');
167                 document.body.appendChild(downloadLink);
168                 downloadLink.click();
169             }
170         }
171     }
172
173     /** Call this method after delete perform action is completed in the ng-smart-table data @public */
174     public callData(): void {
175         this.dataEvent.emit();
176     }
177
178     /** Generate random string @public */
179     public randomString(): string {
180         const chars: string = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
181         let result: string = '';
182         for (let randomStringRef: number = this.randomStringLength; randomStringRef > 0; --randomStringRef) {
183             result += chars[Math.floor(Math.random() * chars.length)];
184         }
185         return result;
186     }
187
188     /** Function to read uploaded file String @public */
189     public async getFileString(files: FileList, fileType: string): Promise<string | ArrayBuffer> {
190         const reader: FileReader = new FileReader();
191         return new Promise<string | ArrayBuffer>((resolve: Function, reject: Function): void => {
192             if (this.vaildataFileInfo(files[0], fileType)) {
193                 this.readFileContent(reader, files[0], fileType);
194             } else {
195                 reject('typeError');
196             }
197             reader.onload = (): void => {
198                 if (reader.result === null) {
199                     reject('contentError');
200                 }
201                 resolve(reader.result);
202             };
203             reader.onerror = (event: Event): void => {
204                 reject('contentError');
205             };
206         });
207     }
208
209     /** Method to handle tar and tar.gz file for shared YAML file content @public */
210     public async targzFile(packageInfo: PACKAGEINFO): Promise<string | ArrayBuffer> {
211         return new Promise<string | ArrayBuffer>((resolve: Function, reject: Function): void => {
212             const httpOptions: GETAPIURLHEADER = this.getHttpOptions();
213             let apiUrl: string = '';
214             apiUrl = packageInfo.packageType === 'nsd' ? environment.NSDESCRIPTORS_URL + '/' + packageInfo.id + '/nsd_content' :
215                 environment.VNFPACKAGES_URL + '/' + packageInfo.id + '/package_content';
216             this.restService.getResource(apiUrl, httpOptions).subscribe((response: ArrayBuffer): void => {
217                 try {
218                     // eslint-disable-next-line @typescript-eslint/no-explicit-any
219                     const tar: any = new Tar();
220                     const originalInput: Uint8Array = pako.inflate(response, { to: 'Uint8Array' });
221                     untar(originalInput.buffer).then((extractedFiles: TARSETTINGS[]): void => {
222                         const getFoldersFiles: {}[] = extractedFiles;
223                         const folderNameStr: string = extractedFiles[0].name;
224                         getFoldersFiles.forEach((value: TARSETTINGS): void => {
225                             const fileValueObj: FILESETTINGS = this.createFileValueObject(value);
226                             const getRootFolder: string[] = value.name.split('/');
227                             if (value.name.startsWith(folderNameStr) &&
228                                 (value.name.endsWith('.yaml') || value.name.endsWith('.yml')) &&
229                                 getRootFolder.length === this.directoryCount) {
230                                 tar.append(value.name, packageInfo.descriptor, fileValueObj);
231                             } else {
232                                 if (value.type !== 'L') {
233                                     tar.append(value.name, new Uint8Array(value.buffer), fileValueObj);
234                                 }
235                             }
236                         });
237                         const out: Uint8Array = tar.out;
238                         const originalOutput: Uint8Array = pako.gzip(out);
239                         resolve(originalOutput.buffer);
240                     }, (err: string): void => {
241                         reject('');
242                     });
243                 } catch (e) {
244                     reject('');
245                 }
246             }, (error: HttpErrorResponse): void => {
247                 if (error.status === HttpStatus.NOT_FOUND || error.status === HttpStatus.UNAUTHORIZED) {
248                     this.router.navigateByUrl('404', { skipLocationChange: true }).catch((): void => {
249                         // Catch Navigation Error
250                     });
251                 } else {
252                     this.restService.handleError(error, 'get');
253                     reject('');
254                 }
255             });
256         });
257     }
258
259     /** Method to return the file information @public */
260     public createFileValueObject(value: TARSETTINGS): FILESETTINGS {
261         return {
262             type: value.type,
263             linkname: value.linkname,
264             owner: value.uname,
265             group: value.gname
266         };
267     }
268
269     /** Method to check given string is JSON or not @public */
270     public checkJson(jsonString: string): boolean {
271         jsonString = jsonString.replace(/'/g, '"');
272         try {
273             JSON.parse(jsonString);
274         } catch (e) {
275             return false;
276         }
277         return true;
278     }
279
280     /** Clean the form before submit @public */
281     public cleanForm(formGroup: FormGroup, formName?: String): void {
282         Object.keys(formGroup.controls).forEach((key: string) => {
283             if ((!isNullOrUndefined((formGroup.get(key) as FormArray | FormGroup).controls)) && key !== 'config') {
284                 // eslint-disable-next-line @typescript-eslint/no-shadow
285                 for (const { item, index } of (formGroup.get(key).value).map((item: {}, index: number) => ({ item, index }))) {
286                     // eslint-disable-next-line security/detect-object-injection
287                     const newFormGroup: FormGroup = (formGroup.get(key) as FormArray).controls[index] as FormGroup;
288                     this.cleanForm(newFormGroup);
289                 }
290             } else if (formGroup.get(key).value !== undefined && formGroup.get(key).value !== null && key !== 'config') {
291                 if (!Array.isArray(formGroup.get(key).value)) {
292                     if (typeof formGroup.get(key).value === 'string') {
293                         formGroup.get(key).setValue(formGroup.get(key).value.trim());
294                     }
295                 }
296             } else if (key === 'config' && formName === 'vim') {
297                 const newFormGroup: FormGroup = formGroup.get(key) as FormGroup;
298                 this.cleanForm(newFormGroup);
299             }
300         });
301     }
302
303     /** Method to return the config of pager value for ngSmarttable @public */
304     public paginationPagerConfig(): PAGERSMARTTABLE {
305         return {
306             display: true,
307             perPage: environment.paginationNumber
308         };
309     }
310
311     /** Method to return the class for the table for ngSmarttable @public */
312     public tableClassConfig(): SMARTTABLECLASS {
313         return {
314             class: 'table list-data'
315         };
316     }
317
318     /** Method to return all languages name and its code @public */
319     public languageCodeList(): {}[] {
320         return [
321             { code: 'en', language: 'English' },
322             { code: 'es', language: 'Spanish' },
323             { code: 'pt', language: 'Portuguese' },
324             { code: 'de', language: 'German' }
325         ];
326     }
327
328     /** Fetch OSM Version @public */
329     public fetchOSMVersion(): void {
330         this.restService.getResource(environment.OSM_VERSION_URL).subscribe((res: { version: string }): void => {
331             const version: string[] = res.version.split('+');
332             if (!isNullOrUndefined(version[0])) {
333                 this.osmVersion = version[0];
334             } else {
335                 this.osmVersion = null;
336             }
337         }, (error: ERRORDATA): void => {
338             this.osmVersion = null;
339             this.restService.handleError(error, 'get');
340         });
341     }
342
343     /** Random RGB color code generator @public */
344     public generateColor(): string {
345         const x: number = Math.floor(Math.random() * this.colorStringLength);
346         const y: number = Math.floor(Math.random() * this.colorStringLength);
347         const z: number = Math.floor(Math.random() * this.colorStringLength);
348         return 'rgb(' + x + ',' + y + ',' + z + ')';
349     }
350
351     /** Add custom name/tag to the dropdown @public */
352     public addCustomTag(tag: string): string {
353         return tag;
354     }
355
356     /** Fetch file extension @public */
357     public fetchFileExtension(fileInfo: FileList): string {
358         return fileInfo[0].name.substring(fileInfo[0].name.lastIndexOf('.') + 1);
359     }
360
361     /** Get domain name @private */
362     public getDomainName(): Observable<TYPESECTION[]> {
363         return this.restService.getResource(environment.DOMAIN_URL).pipe(map((domains: DOMAINS): TYPESECTION[] => {
364             const domainList: TYPESECTION[] = [];
365             try {
366                 let domainNames: string[] = [];
367                 if (!isNullOrUndefined(domains.project_domain_name)) {
368                     domainNames = domainNames.concat(domains.project_domain_name.split(','));
369                 }
370                 if (!isNullOrUndefined(domains.user_domain_name)) {
371                     domainNames = domainNames.concat(domains.user_domain_name.split(','));
372                 }
373                 domainNames = Array.from(new Set(domainNames));
374                 if (domainNames.length > 0) {
375                     domainNames.forEach((domainName: string): void => {
376                         if (!domainName.endsWith(':ro')) {
377                             domainList.push({ title: domainName, value: domainName });
378                         }
379                     });
380                 }
381                 return domainList;
382             } catch (e) {
383                 return domainList;
384             }
385         }));
386     }
387
388     /** Method to validate file extension and size @private */
389     private vaildataFileInfo(fileInfo: File, fileType: string): boolean {
390         const extension: string = fileInfo.name.substring(fileInfo.name.lastIndexOf('.') + 1);
391         const packageSize: number = CONSTANTNUMBER.oneMB * environment.packageSize;
392         if (fileType === 'yaml' && (extension.toLowerCase() === 'yaml' || extension.toLowerCase() === 'yml')
393             && fileInfo.size <= packageSize) {
394             return true;
395         } else if (extension.toLowerCase() === fileType && fileInfo.size <= packageSize) {
396             return true;
397         }
398         return false;
399     }
400
401     /** Method to read file content based on type @private */
402     private readFileContent(reader: FileReader, fileInfo: File, fileType: string): void {
403         if (fileType === 'gz') {
404             reader.readAsArrayBuffer(fileInfo);
405         } else {
406             reader.readAsText(fileInfo);
407         }
408     }
409
410     /** Method to handle http options @public */
411     private getHttpOptions(): GETAPIURLHEADER {
412         return {
413             headers: new HttpHeaders({
414                 Accept: 'application/gzip, application/json',
415                 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
416             }),
417             responseType: 'arraybuffer'
418         };
419     }
420 }