2 Copyright 2020 TATA ELXSI
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
8 http://www.apache.org/licenses/LICENSE-2.0
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.
16 Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
19 * @file Provider for Shared Service
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';
39 import { environment } from 'environment';
40 import * as HttpStatus from 'http-status-codes';
41 import * as untar from 'js-untar';
42 import { ActiveToast, ToastrService } from 'ngx-toastr';
43 import * as pako from 'pako';
44 import { RestService } from 'RestService';
45 import { Observable } from 'rxjs';
46 import { map } from 'rxjs/operators';
48 /** This is added globally by the tar.js library */
49 // eslint-disable-next-line @typescript-eslint/no-explicit-any
50 declare const Tar: any;
53 * An Injectable is a class adorned with the @Injectable decorator function.
54 * @Injectable takes a metadata object that tells Angular how to compile and run module code
59 /** Exporting a class @exports SharedService */
60 export class SharedService {
61 /** call the parent using event information @private */
62 @Output() public dataEvent: EventEmitter<{}> = new EventEmitter<{}>();
64 /** Variables to hold regexp pattern for URL */
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.,?'\\+&%$#=~_-]+))*$/);
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}$/);
70 /** Variables to hold regexp pattern for Port Number */
71 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 /** Variables to hold regexp pattern for DPID */
74 public REGX_DPID_PATTERN: RegExp = new RegExp(/^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){7}$/);
76 /** Variable to hold regexp pattern for password */
77 public REGX_PASSWORD_PATTERN: RegExp = new RegExp(/^.*(?=.{8,})((?=.*[!@#$%^&*()\-_=+{};:,<.>]){1})(?=.*\d)((?=.*[a-z]){1})((?=.*[A-Z]){1}).*$/);
79 /** Variables to hold regexp pattern for Latitude */
80 public REGX_LAT_PATTERN: RegExp = new RegExp(/^(\+|-)?(?:90(?:(?:\.0{1,6})?)|(?:[0-9]|[1-8][0-9])(?:(?:\.[0-9]{1,15})?))$/);
82 /** Variables to hold regexp pattern for Longitude */
83 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})?))$/);
85 /** Variables to hold maxlength for the description @public */
86 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
87 public MAX_LENGTH_DESCRIPTION: number = 500;
89 /** Variables to hold maxlength for the name @public */
90 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
91 public MAX_LENGTH_NAME: number = 50;
93 /** FormGroup instance added to the form @ html @public */
94 public formGroup: FormGroup;
96 /** Controls the go to top button on scroll @public */
97 public showGotoTop: boolean;
99 /** Holds OSM Version value @public */
100 public osmVersion: string;
102 /** Holds Last Login Toaster Message @public */
103 public lastLoginMessage: string;
105 /** Holds Failed Attempts Toaster Message @public */
106 public failedAttemptsMessage: string;
108 /** Holds No Of Days Toaster Message @public */
109 public daysMessage: string;
111 /** express number for time manupulation -2 */
112 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
113 private epochTimeMinus2: number = -2;
115 /** express number for time manupulation 1000 */
116 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
117 private epochTime1000: number = 1000;
119 /** express number for time manupulation 60 */
120 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
121 private epochTime60: number = 60;
123 /** express number for time manupulation 24 */
124 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
125 private epochTime24: number = 24;
127 /** Random string generator length */
128 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
129 private randomStringLength: number = 4;
131 /** Max length of Uint8Array */
132 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
133 private unit8Array: number = 255;
135 /** Instance of the rest service @private */
136 private restService: RestService;
138 /** Service holds the router information @private */
139 private router: Router;
141 /** Random color string generator length @private */
142 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
143 private colorStringLength: number = 256;
145 /** Check for the root directory @private */
146 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
147 private directoryCount: number = 2;
149 /** express number for time manupulation 1000 */
150 private toasterSettings: {} = {
156 /** Contains tranlsate instance @private */
157 private translateService: TranslateService;
159 /** Contains toaster instance @private */
160 private toaster: ToastrService;
162 constructor(restService: RestService, router: Router, translateService: TranslateService, toaster: ToastrService) {
163 this.restService = restService;
164 this.router = router;
165 this.translateService = translateService;
166 this.toaster = toaster;
169 /** convert epoch time function @public */
170 public convertEpochTime(unixtimestamp: number): string {
171 if (!isNullOrUndefined(unixtimestamp)) {
172 const monthsArr: string[] = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
173 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
174 const date: Date = new Date(unixtimestamp * this.epochTime1000);
175 const year: number = date.getFullYear();
176 const month: string = monthsArr[date.getMonth()];
177 const day: number = date.getDate();
178 const hours: number = date.getHours();
179 const minutes: string = '0' + date.getMinutes();
180 const seconds: string = '0' + date.getSeconds();
181 // eslint-disable-next-line deprecation/deprecation
182 return month + '-' + day + '-' + year + ' ' + hours + ':' + minutes.substr(this.epochTimeMinus2) + ':'
183 // eslint-disable-next-line deprecation/deprecation
184 + seconds.substr(this.epochTimeMinus2);
186 return this.translateService.instant('NODATE');
189 /** convert epoch time function to No of days @public */
190 public converEpochToDays(date: string): number {
191 if (!isNullOrUndefined(date)) {
192 const today: Date = new Date();
193 const accountDate: Date = new Date(date);
194 const toasterDate: number = (accountDate.getTime() -
195 today.getTime()) / this.epochTime1000 / this.epochTime60 / this.epochTime60 / this.epochTime24;
196 if (toasterDate >= 0 || toasterDate < 1) {
197 return Math.round(toasterDate);
199 return Math.floor(toasterDate);
201 return this.translateService.instant('N/A');
204 /** show toaster for password & account expiry @public */
205 public showToaster(lastLogin: string, failedAttempts: string, passwordNoOfDays: string,
206 accountNoOfDays: string, passwordExpireMessage: string, accountExpireMessage: string,
207 passwordMessage: string, accountMessage: string): ActiveToast<string> {
208 this.lastLoginMessage = this.translateService.instant('PAGE.LOGIN.LASTACCESS');
209 this.failedAttemptsMessage = this.translateService.instant('PAGE.LOGIN.FAILED');
210 return this.toaster.info(this.lastLoginMessage + ':' + ' ' + lastLogin +
211 '</br>' + this.failedAttemptsMessage + ':' + ' ' + failedAttempts +
212 '</br>' + passwordExpireMessage + ' ' + passwordNoOfDays + ' ' + passwordMessage +
213 '</br>' + accountExpireMessage + ' ' + accountNoOfDays + ' ' + accountMessage,
214 this.translateService.instant('PAGE.LOGIN.LOGINHISTORY'), this.toasterSettings);
217 /** show toaster for password expiry @public */
218 public passwordToaster(lastLogin: string, failedAttempts: string, passwordNoOfDays: string,
219 passwordExpireMessage: string, passwordMessage: string): ActiveToast<string> {
220 this.lastLoginMessage = this.translateService.instant('PAGE.LOGIN.LASTACCESS');
221 this.failedAttemptsMessage = this.translateService.instant('PAGE.LOGIN.FAILED');
222 return this.toaster.info(this.lastLoginMessage + ':' + ' ' + lastLogin +
223 '</br>' + this.failedAttemptsMessage + ':' + ' ' + failedAttempts +
224 '</br>' + passwordExpireMessage + ' ' + passwordNoOfDays + ' ' + passwordMessage,
225 this.translateService.instant('PAGE.LOGIN.LOGINHISTORY'), this.toasterSettings);
228 /** show toaster for account expiry @public */
229 public accountToaster(lastLogin: string, failedAttempts: string,
230 accountNoOfDays: string, accountExpireMessage: string, accountMessage: string): ActiveToast<string> {
231 this.lastLoginMessage = this.translateService.instant('PAGE.LOGIN.LASTACCESS');
232 this.failedAttemptsMessage = this.translateService.instant('PAGE.LOGIN.FAILED');
233 return this.toaster.info(this.lastLoginMessage + ':' + ' ' + lastLogin +
234 '</br>' + this.failedAttemptsMessage + ':' + ' ' + failedAttempts +
235 '</br>' + accountExpireMessage + ' ' + accountNoOfDays + ' ' + accountMessage,
236 this.translateService.instant('PAGE.LOGIN.LOGINHISTORY'), this.toasterSettings);
239 /** Download Files function @public */
240 public downloadFiles(name: string, binaryData: Blob[], filetype: string): void {
241 const downloadLink: HTMLAnchorElement = document.createElement('a');
242 downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, { type: filetype }));
243 if (name !== undefined) {
244 // eslint-disable-next-line @typescript-eslint/no-explicit-any
245 const newVariable: any = window.navigator;
246 if (newVariable.msSaveOrOpenBlob) {
247 newVariable.msSaveBlob(new Blob(binaryData, { type: filetype }), 'OSM_Export_' + name + '.tar.gz');
249 downloadLink.setAttribute('download', 'OSM_Export_' + name + '.tar.gz');
250 document.body.appendChild(downloadLink);
251 downloadLink.click();
256 /** Call this method after delete perform action is completed in the ng-smart-table data @public */
257 public callData(): void {
258 this.dataEvent.emit();
261 /** Generate random string @public */
262 public randomString(): string {
263 const chars: string = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
264 let result: string = '';
265 for (let randomStringRef: number = this.randomStringLength; randomStringRef > 0; --randomStringRef) {
266 result += chars[Math.floor(Math.random() * chars.length)];
271 /** Function to read uploaded file String @public */
272 public async getFileString(files: FileList, fileType: string): Promise<string | ArrayBuffer> {
273 const reader: FileReader = new FileReader();
274 return new Promise<string | ArrayBuffer>((resolve: Function, reject: Function): void => {
275 if (this.vaildataFileInfo(files[0], fileType)) {
276 this.readFileContent(reader, files[0], fileType);
280 reader.onload = (): void => {
281 if (reader.result === null) {
282 reject('contentError');
284 resolve(reader.result);
286 reader.onerror = (event: Event): void => {
287 reject('contentError');
292 /** Method to handle tar and tar.gz file for shared YAML file content @public */
293 public async targzFile(packageInfo: PACKAGEINFO): Promise<string | ArrayBuffer> {
294 return new Promise<string | ArrayBuffer>((resolve: Function, reject: Function): void => {
295 const httpOptions: GETAPIURLHEADER = this.getHttpOptions();
296 let apiUrl: string = '';
297 apiUrl = packageInfo.packageType === 'nsd' ? environment.NSDESCRIPTORS_URL + '/' + packageInfo.id + '/nsd_content' :
298 environment.VNFPACKAGES_URL + '/' + packageInfo.id + '/package_content';
299 this.restService.getResource(apiUrl, httpOptions).subscribe((response: ArrayBuffer): void => {
301 // eslint-disable-next-line @typescript-eslint/no-explicit-any
302 const tar: any = new Tar();
303 const originalInput: Uint8Array = pako.inflate(response, { to: 'Uint8Array' });
304 untar(originalInput.buffer).then((extractedFiles: TARSETTINGS[]): void => {
305 const getFoldersFiles: {}[] = extractedFiles;
306 const folderNameStr: string = extractedFiles[0].name;
307 getFoldersFiles.forEach((value: TARSETTINGS): void => {
308 const fileValueObj: FILESETTINGS = this.createFileValueObject(value);
309 const getRootFolder: string[] = value.name.split('/');
310 if (value.name.startsWith(folderNameStr) &&
311 (value.name.endsWith('.yaml') || value.name.endsWith('.yml')) &&
312 getRootFolder.length === this.directoryCount) {
313 tar.append(value.name, packageInfo.descriptor, fileValueObj);
315 if (value.type !== 'L') {
316 tar.append(value.name, new Uint8Array(value.buffer), fileValueObj);
320 const out: Uint8Array = tar.out;
321 const originalOutput: Uint8Array = pako.gzip(out);
322 resolve(originalOutput.buffer);
323 }, (err: string): void => {
329 }, (error: HttpErrorResponse): void => {
330 if (error.status === HttpStatus.NOT_FOUND || error.status === HttpStatus.UNAUTHORIZED) {
331 this.router.navigateByUrl('404', { skipLocationChange: true }).catch((): void => {
332 // Catch Navigation Error
335 this.restService.handleError(error, 'get');
342 /** Method to return the file information @public */
343 public createFileValueObject(value: TARSETTINGS): FILESETTINGS {
346 linkname: value.linkname,
352 /** Method to check given string is JSON or not @public */
353 public checkJson(jsonString: string): boolean {
354 jsonString = jsonString.replace(/'/g, '"');
356 JSON.parse(jsonString);
363 /** Clean the form before submit @public */
364 public cleanForm(formGroup: FormGroup, formName?: String): void {
365 Object.keys(formGroup.controls).forEach((key: string) => {
366 if ((!isNullOrUndefined((formGroup.get(key) as FormArray | FormGroup).controls)) && key !== 'config') {
367 // eslint-disable-next-line @typescript-eslint/no-shadow
368 for (const { item, index } of (formGroup.get(key).value).map((item: {}, index: number) => ({ item, index }))) {
369 // eslint-disable-next-line security/detect-object-injection
370 const newFormGroup: FormGroup = (formGroup.get(key) as FormArray).controls[index] as FormGroup;
371 this.cleanForm(newFormGroup);
373 } else if (formGroup.get(key).value !== undefined && formGroup.get(key).value !== null && key !== 'config') {
374 if (!Array.isArray(formGroup.get(key).value)) {
375 if (typeof formGroup.get(key).value === 'string') {
376 formGroup.get(key).setValue(formGroup.get(key).value.trim());
379 } else if (key === 'config' && formName === 'vim') {
380 const newFormGroup: FormGroup = formGroup.get(key) as FormGroup;
381 this.cleanForm(newFormGroup);
386 /** Method to return the config of pager value for ngSmarttable @public */
387 public paginationPagerConfig(): PAGERSMARTTABLE {
390 perPage: environment.paginationNumber
394 /** Method to return the class for the table for ngSmarttable @public */
395 public tableClassConfig(): SMARTTABLECLASS {
397 class: 'table list-data'
401 /** Method to return all languages name and its code @public */
402 public languageCodeList(): {}[] {
404 { code: 'en', language: 'English' },
405 { code: 'es', language: 'Spanish' },
406 { code: 'pt', language: 'Portuguese' },
407 { code: 'de', language: 'German' }
411 /** Fetch OSM Version @public */
412 public fetchOSMVersion(): void {
413 this.restService.getResource(environment.OSM_VERSION_URL).subscribe((res: { version: string }): void => {
414 const version: string[] = res.version.split('+');
415 if (!isNullOrUndefined(version[0])) {
416 this.osmVersion = version[0];
418 this.osmVersion = null;
420 }, (error: ERRORDATA): void => {
421 this.osmVersion = null;
422 this.restService.handleError(error, 'get');
426 /** Random RGB color code generator @public */
427 public generateColor(): string {
428 const x: number = Math.floor(Math.random() * this.colorStringLength);
429 const y: number = Math.floor(Math.random() * this.colorStringLength);
430 const z: number = Math.floor(Math.random() * this.colorStringLength);
431 return 'rgb(' + x + ',' + y + ',' + z + ')';
434 /** Add custom name/tag to the dropdown @public */
435 public addCustomTag(tag: string): string {
439 /** Fetch file extension @public */
440 public fetchFileExtension(fileInfo: FileList): string {
441 return fileInfo[0].name.substring(fileInfo[0].name.lastIndexOf('.') + 1);
444 /** Get domain name @private */
445 public getDomainName(): Observable<TYPESECTION[]> {
446 return this.restService.getResource(environment.DOMAIN_URL).pipe(map((domains: DOMAINS): TYPESECTION[] => {
447 const domainList: TYPESECTION[] = [];
449 let domainNames: string[] = [];
450 if (!isNullOrUndefined(domains.project_domain_name)) {
451 domainNames = domainNames.concat(domains.project_domain_name.split(','));
453 if (!isNullOrUndefined(domains.user_domain_name)) {
454 domainNames = domainNames.concat(domains.user_domain_name.split(','));
456 domainNames = Array.from(new Set(domainNames));
457 if (domainNames.length > 0) {
458 domainNames.forEach((domainName: string): void => {
459 if (!domainName.endsWith(':ro')) {
460 domainList.push({ title: domainName, value: domainName });
471 /** Sorting the list based on date @public */
472 public compareFunction = (dir: number, a: string, b: string): number => {
473 const first: number = new Date(a).getTime();
474 const second: number = new Date(b).getTime();
475 if (first < second) {
478 if (first > second) {
484 /** Method to validate file extension and size @private */
485 private vaildataFileInfo(fileInfo: File, fileType: string): boolean {
486 const extension: string = fileInfo.name.substring(fileInfo.name.lastIndexOf('.') + 1);
487 const packageSize: number = CONSTANTNUMBER.oneMB * environment.packageSize;
488 if (fileType === 'yaml' && (extension.toLowerCase() === 'yaml' || extension.toLowerCase() === 'yml')
489 && fileInfo.size <= packageSize) {
491 } else if (extension.toLowerCase() === fileType && fileInfo.size <= packageSize) {
497 /** Method to read file content based on type @private */
498 private readFileContent(reader: FileReader, fileInfo: File, fileType: string): void {
499 if (fileType === 'gz') {
500 reader.readAsArrayBuffer(fileInfo);
502 reader.readAsText(fileInfo);
506 /** Method to handle http options @public */
507 private getHttpOptions(): GETAPIURLHEADER {
509 headers: new HttpHeaders({
510 Accept: 'application/gzip, application/json',
511 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
513 responseType: 'arraybuffer'