Bug 1253 Updating the VNFD via ngUI breaks charms execution
[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, FILESETTINGS, 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 fileValueObj: FILESETTINGS = this.createFileValueObject(value);
198                             const getRootFolder: string[] = value.name.split('/');
199                             if (value.name.startsWith(folderNameStr) &&
200                                 (value.name.endsWith('.yaml') || value.name.endsWith('.yml')) &&
201                                 getRootFolder.length === this.directoryCount) {
202                                 tar.append(value.name, packageInfo.descriptor, fileValueObj);
203                             } else {
204                                 if (value.type !== 'L') {
205                                     tar.append(value.name, new Uint8Array(value.buffer), fileValueObj);
206                                 }
207                             }
208                         });
209                         const out: Uint8Array = tar.out;
210                         const originalOutput: Uint8Array = pako.gzip(out);
211                         resolve(originalOutput.buffer);
212                     }, (err: string) => {
213                         reject('');
214                     });
215                 } catch (e) {
216                     reject('');
217                 }
218             }, (error: HttpErrorResponse) => {
219                 if (error.status === HttpStatus.NOT_FOUND || error.status === HttpStatus.UNAUTHORIZED) {
220                     this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
221                 } else {
222                     this.restService.handleError(error, 'get');
223                     reject('');
224                 }
225             });
226         });
227     }
228     /** Method to return the file information @public */
229     public createFileValueObject(value: TARSETTINGS): FILESETTINGS {
230         return {
231             type: value.type,
232             linkname: value.linkname,
233             owner: value.uname,
234             group: value.gname
235         };
236     }
237     /** Method to check given string is JSON or not @public */
238     public checkJson(jsonString: string): boolean {
239         jsonString = jsonString.replace(/'/g, '"');
240         try {
241             JSON.parse(jsonString);
242         } catch (e) {
243             return false;
244         }
245         return true;
246     }
247     /** Clean the form before submit @public */
248     public cleanForm(formGroup: FormGroup, formName?: String): void {
249         Object.keys(formGroup.controls).forEach((key: string) => {
250             if ((!isNullOrUndefined((formGroup.get(key) as FormArray | FormGroup).controls)) && key !== 'config') {
251                 // tslint:disable-next-line: no-shadowed-variable
252                 for (const { item, index } of (formGroup.get(key).value).map((item: {}, index: number) => ({ item, index }))) {
253                     const newFormGroup: FormGroup = (formGroup.get(key) as FormArray).controls[index] as FormGroup;
254                     this.cleanForm(newFormGroup);
255                 }
256             } else if (formGroup.get(key).value !== undefined && formGroup.get(key).value !== null && key !== 'config') {
257                 if (!Array.isArray(formGroup.get(key).value)) {
258                     if (typeof formGroup.get(key).value === 'string') {
259                         formGroup.get(key).setValue(formGroup.get(key).value.trim());
260                     }
261                 }
262             } else if (key === 'config' && formName === 'vim') {
263                 const newFormGroup: FormGroup = formGroup.get(key) as FormGroup;
264                 this.cleanForm(newFormGroup);
265             }
266         });
267     }
268     /** Method to return the config of pager value for ngSmarttable @public */
269     public paginationPagerConfig(): PAGERSMARTTABLE {
270         return {
271             display: true,
272             perPage: environment.paginationNumber
273         };
274     }
275     /** Method to return the class for the table for ngSmarttable @public */
276     public tableClassConfig(): SMARTTABLECLASS {
277         return {
278             class: 'table list-data'
279         };
280     }
281     /** Method to return all languages name and its code @public */
282     public languageCodeList(): {}[] {
283         return [
284             { code: 'en', language: 'English' },
285             { code: 'es', language: 'Spanish' },
286             { code: 'pt', language: 'Portuguese' },
287             { code: 'de', language: 'German' }
288         ];
289     }
290     /** Fetch OSM Version @public */
291     public fetchOSMVersion(): void {
292         this.restService.getResource(environment.OSM_VERSION_URL).subscribe((res: { version: string }) => {
293             const version: string[] = res.version.split('+');
294             if (!isNullOrUndefined(version[0])) {
295                 this.osmVersion = version[0];
296             } else {
297                 this.osmVersion = null;
298             }
299         }, (error: ERRORDATA) => {
300             this.osmVersion = null;
301             this.restService.handleError(error, 'get');
302         });
303     }
304     /** Random RGB color code generator @public */
305     public generateColor(): string {
306         const x: number = Math.floor(Math.random() * this.colorStringLength);
307         const y: number = Math.floor(Math.random() * this.colorStringLength);
308         const z: number = Math.floor(Math.random() * this.colorStringLength);
309         return 'rgb(' + x + ',' + y + ',' + z + ')';
310     }
311
312     /** Add custom name/tag to the dropdown @public */
313     public addCustomTag(tag: string): string {
314         return tag;
315     }
316
317     /** Fetch file extension @public */
318     public fetchFileExtension(fileInfo: FileList): string {
319         return fileInfo[0].name.substring(fileInfo[0].name.lastIndexOf('.') + 1);
320     }
321
322     /** Method to validate file extension and size @private */
323     private vaildataFileInfo(fileInfo: File, fileType: string): boolean {
324         const extension: string = fileInfo.name.substring(fileInfo.name.lastIndexOf('.') + 1);
325         const packageSize: number = CONSTANTNUMBER.oneMB * environment.packageSize;
326         if (fileType === 'yaml' && (extension.toLowerCase() === 'yaml' || extension.toLowerCase() === 'yml')
327             && fileInfo.size <= packageSize) {
328             return true;
329         } else if (extension.toLowerCase() === fileType && fileInfo.size <= packageSize) {
330             return true;
331         }
332         return false;
333     }
334     /** Method to read file content based on type @private */
335     private readFileContent(reader: FileReader, fileInfo: File, fileType: string): void {
336         if (fileType === 'gz') {
337             reader.readAsArrayBuffer(fileInfo);
338         } else {
339             reader.readAsText(fileInfo);
340         }
341     }
342     /** Method to handle http options @public */
343     private getHttpOptions(): GETAPIURLHEADER {
344         return {
345             headers: new HttpHeaders({
346                 Accept: 'application/gzip, application/json',
347                 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
348             }),
349             responseType: 'arraybuffer'
350         };
351     }
352 }