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