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