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 { 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';
38 import { environment } from 'environment';
39 import * as HttpStatus from 'http-status-codes';
40 import * as untar from 'js-untar';
41 import { ActiveToast, ToastrService } from 'ngx-toastr';
42 import * as pako from 'pako';
43 import { RestService } from 'RestService';
44 import { Observable } from 'rxjs';
45 import { map } from 'rxjs/operators';
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;
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
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<{}>();
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.,?'\\+&%$#=~_-]+))*$/);
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}$/);
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}))$/);
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}$/);
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}).*$/);
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})?))$/);
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})?))$/);
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;
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;
92 /** FormGroup instance added to the form @ html @public */
93 public formGroup: FormGroup;
95 /** Controls the go to top button on scroll @public */
96 public showGotoTop: boolean;
98 /** Holds OSM Version value @public */
99 public osmVersion: string;
101 /** Holds Last Login Toaster Message @public */
102 public lastLoginMessage: string;
104 /** Holds Failed Attempts Toaster Message @public */
105 public failedAttemptsMessage: string;
107 /** Holds No Of Days Toaster Message @public */
108 public daysMessage: string;
110 /** express number for time manupulation -2 */
111 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
112 private epochTimeMinus2: number = -2;
114 /** express number for time manupulation 1000 */
115 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
116 private epochTime1000: number = 1000;
118 /** express number for time manupulation 60 */
119 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
120 private epochTime60: number = 60;
122 /** express number for time manupulation 24 */
123 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
124 private epochTime24: number = 24;
126 /** Random string generator length */
127 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
128 private randomStringLength: number = 4;
130 /** express number for rgb manipulation */
131 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
132 private colourHour: number = 10;
134 /** express number for rgb manipulation*/
135 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
136 private colourMin: number = 5;
138 /** Instance of the rest service @private */
139 private restService: RestService;
141 /** Service holds the router information @private */
142 private router: Router;
144 /** Check for the root directory @private */
145 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
146 private directoryCount: number = 2;
148 /** express number for time manupulation 1000 */
149 private toasterSettings: {} = {
155 /** Contains tranlsate instance @private */
156 private translateService: TranslateService;
158 /** Contains toaster instance @private */
159 private toaster: ToastrService;
161 constructor(restService: RestService, router: Router, translateService: TranslateService, toaster: ToastrService) {
162 this.restService = restService;
163 this.router = router;
164 this.translateService = translateService;
165 this.toaster = toaster;
168 /** convert epoch time function @public */
169 public convertEpochTime(unixtimestamp: number): string {
170 if (!isNullOrUndefined(unixtimestamp)) {
171 const monthsArr: string[] = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
172 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
173 const date: Date = new Date(unixtimestamp * this.epochTime1000);
174 const year: number = date.getFullYear();
175 const month: string = monthsArr[date.getMonth()];
176 const day: number = date.getDate();
177 const hours: number = date.getHours();
178 const minutes: string = '0' + date.getMinutes();
179 const seconds: string = '0' + date.getSeconds();
180 // eslint-disable-next-line deprecation/deprecation
181 return month + '-' + day + '-' + year + ' ' + hours + ':' + minutes.substr(this.epochTimeMinus2) + ':'
182 // eslint-disable-next-line deprecation/deprecation
183 + seconds.substr(this.epochTimeMinus2);
185 return this.translateService.instant('NODATE');
188 /** convert epoch time function to No of days @public */
189 public converEpochToDays(date: string): number {
190 if (!isNullOrUndefined(date)) {
191 const today: Date = new Date();
192 const accountDate: Date = new Date(date);
193 const toasterDate: number = (accountDate.getTime() -
194 today.getTime()) / this.epochTime1000 / this.epochTime60 / this.epochTime60 / this.epochTime24;
195 if (toasterDate >= 0 || toasterDate < 1) {
196 return Math.round(toasterDate);
198 return Math.floor(toasterDate);
200 return this.translateService.instant('N/A');
203 /** show toaster for password & account expiry @public */
204 public showToaster(lastLogin: string, failedAttempts: string, passwordNoOfDays: string,
205 accountNoOfDays: string, passwordExpireMessage: string, accountExpireMessage: string,
206 passwordMessage: string, accountMessage: string): ActiveToast<string> {
207 this.lastLoginMessage = this.translateService.instant('PAGE.LOGIN.LASTACCESS');
208 this.failedAttemptsMessage = this.translateService.instant('PAGE.LOGIN.FAILED');
209 return this.toaster.info(this.lastLoginMessage + ':' + ' ' + lastLogin +
210 '</br>' + this.failedAttemptsMessage + ':' + ' ' + failedAttempts +
211 '</br>' + passwordExpireMessage + ' ' + passwordNoOfDays + ' ' + passwordMessage +
212 '</br>' + accountExpireMessage + ' ' + accountNoOfDays + ' ' + accountMessage,
213 this.translateService.instant('PAGE.LOGIN.LOGINHISTORY'), this.toasterSettings);
216 /** show toaster for password expiry @public */
217 public passwordToaster(lastLogin: string, failedAttempts: string, passwordNoOfDays: string,
218 passwordExpireMessage: string, passwordMessage: string): ActiveToast<string> {
219 this.lastLoginMessage = this.translateService.instant('PAGE.LOGIN.LASTACCESS');
220 this.failedAttemptsMessage = this.translateService.instant('PAGE.LOGIN.FAILED');
221 return this.toaster.info(this.lastLoginMessage + ':' + ' ' + lastLogin +
222 '</br>' + this.failedAttemptsMessage + ':' + ' ' + failedAttempts +
223 '</br>' + passwordExpireMessage + ' ' + passwordNoOfDays + ' ' + passwordMessage,
224 this.translateService.instant('PAGE.LOGIN.LOGINHISTORY'), this.toasterSettings);
227 /** show toaster for account expiry @public */
228 public accountToaster(lastLogin: string, failedAttempts: string,
229 accountNoOfDays: string, accountExpireMessage: string, accountMessage: string): ActiveToast<string> {
230 this.lastLoginMessage = this.translateService.instant('PAGE.LOGIN.LASTACCESS');
231 this.failedAttemptsMessage = this.translateService.instant('PAGE.LOGIN.FAILED');
232 return this.toaster.info(this.lastLoginMessage + ':' + ' ' + lastLogin +
233 '</br>' + this.failedAttemptsMessage + ':' + ' ' + failedAttempts +
234 '</br>' + accountExpireMessage + ' ' + accountNoOfDays + ' ' + accountMessage,
235 this.translateService.instant('PAGE.LOGIN.LOGINHISTORY'), this.toasterSettings);
238 /** Download Files function @public */
239 public downloadFiles(name: string, binaryData: Blob[], filetype: string): void {
240 const downloadLink: HTMLAnchorElement = document.createElement('a');
241 downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, { type: filetype }));
242 if (name !== undefined) {
243 // eslint-disable-next-line @typescript-eslint/no-explicit-any
244 const newVariable: any = window.navigator;
245 if (newVariable.msSaveOrOpenBlob) {
246 newVariable.msSaveBlob(new Blob(binaryData, { type: filetype }), 'OSM_Export_' + name + '.tar.gz');
248 downloadLink.setAttribute('download', 'OSM_Export_' + name + '.tar.gz');
249 document.body.appendChild(downloadLink);
250 downloadLink.click();
255 /** Call this method after delete perform action is completed in the ng-smart-table data @public */
256 public callData(): void {
257 this.dataEvent.emit();
260 /** Generate random string @public */
261 public randomString(): string {
262 let result: string = '';
263 for (let randomStringRef: number = this.randomStringLength; randomStringRef > 0; --randomStringRef) {
264 result += new Date().getSeconds();
269 /** Function to read uploaded file String @public */
270 public async getFileString(files: FileList, fileType: string): Promise<string | ArrayBuffer> {
271 const reader: FileReader = new FileReader();
272 return new Promise<string | ArrayBuffer>((resolve: Function, reject: Function): void => {
273 if (this.vaildataFileInfo(files[0], fileType)) {
274 this.readFileContent(reader, files[0], fileType);
278 reader.onload = (): void => {
279 if (reader.result === null) {
280 reject('contentError');
282 resolve(reader.result);
284 reader.onerror = (event: Event): void => {
285 reject('contentError');
290 /** Method to handle tar and tar.gz file for shared YAML file content @public */
291 public async targzFile(packageInfo: PACKAGEINFO): Promise<string | ArrayBuffer> {
292 return new Promise<string | ArrayBuffer>((resolve: Function, reject: Function): void => {
293 const httpOptions: GETAPIURLHEADER = this.getHttpOptions();
294 let apiUrl: string = '';
295 apiUrl = packageInfo.packageType === 'nsd' ? environment.NSDESCRIPTORS_URL + '/' + packageInfo.id + '/nsd_content' :
296 environment.VNFPACKAGES_URL + '/' + packageInfo.id + '/package_content';
297 this.restService.getResource(apiUrl, httpOptions).subscribe((response: ArrayBuffer): void => {
299 // eslint-disable-next-line @typescript-eslint/no-explicit-any
300 const tar: any = new Tar();
301 const originalInput: Uint8Array = pako.inflate(response, { to: 'Uint8Array' });
302 untar(originalInput.buffer).then((extractedFiles: TARSETTINGS[]): void => {
303 const getFoldersFiles: {}[] = extractedFiles;
304 const folderNameStr: string = extractedFiles[0].name;
305 getFoldersFiles.forEach((value: TARSETTINGS): void => {
306 const fileValueObj: FILESETTINGS = this.createFileValueObject(value);
307 const getRootFolder: string[] = value.name.split('/');
308 if (value.name.startsWith(folderNameStr) &&
309 (value.name.endsWith('.yaml') || value.name.endsWith('.yml')) &&
310 getRootFolder.length === this.directoryCount) {
311 tar.append(value.name, packageInfo.descriptor, fileValueObj);
313 if (value.type !== 'L') {
314 tar.append(value.name, new Uint8Array(value.buffer), fileValueObj);
318 const out: Uint8Array = tar.out;
319 const originalOutput: Uint8Array = pako.gzip(out);
320 resolve(originalOutput.buffer);
321 }, (err: string): void => {
327 }, (error: HttpErrorResponse): void => {
328 if (error.status === HttpStatus.NOT_FOUND || error.status === HttpStatus.UNAUTHORIZED) {
329 this.router.navigateByUrl('404', { skipLocationChange: true }).catch((): void => {
330 // Catch Navigation Error
333 this.restService.handleError(error, 'get');
340 /** Method to return the file information @public */
341 public createFileValueObject(value: TARSETTINGS): FILESETTINGS {
344 linkname: value.linkname,
350 /** Method to check given string is JSON or not @public */
351 public checkJson(jsonString: string): boolean {
352 jsonString = jsonString.replace(/'/g, '"');
354 JSON.parse(jsonString);
361 /** Clean the form before submit @public */
362 public cleanForm(formGroup: FormGroup, formName?: String): void {
363 Object.keys(formGroup.controls).forEach((key: string) => {
364 if ((!isNullOrUndefined((formGroup.get(key) as FormArray | FormGroup).controls)) && key !== 'config') {
365 // eslint-disable-next-line @typescript-eslint/no-shadow
366 for (const { item, index } of (formGroup.get(key).value).map((item: {}, index: number) => ({ item, index }))) {
367 // eslint-disable-next-line security/detect-object-injection
368 const newFormGroup: FormGroup = (formGroup.get(key) as FormArray).controls[index] as FormGroup;
369 this.cleanForm(newFormGroup);
371 } else if (formGroup.get(key).value !== undefined && formGroup.get(key).value !== null && key !== 'config') {
372 if (!Array.isArray(formGroup.get(key).value)) {
373 if (typeof formGroup.get(key).value === 'string') {
374 formGroup.get(key).setValue(formGroup.get(key).value.trim());
377 } else if (key === 'config' && formName === 'vim') {
378 const newFormGroup: FormGroup = formGroup.get(key) as FormGroup;
379 this.cleanForm(newFormGroup);
384 /** Method to return the config of pager value for ngSmarttable @public */
385 public paginationPagerConfig(): PAGERSMARTTABLE {
388 perPage: environment.paginationNumber
392 /** Method to return the class for the table for ngSmarttable @public */
393 public tableClassConfig(): SMARTTABLECLASS {
395 class: 'table list-data'
399 /** Method to return all languages name and its code @public */
400 public languageCodeList(): {}[] {
402 { code: 'en', language: 'English' },
403 { code: 'es', language: 'Spanish' },
404 { code: 'pt', language: 'Portuguese' },
405 { code: 'de', language: 'German' }
409 /** Fetch OSM Version @public */
410 public fetchOSMVersion(): void {
411 this.restService.getResource(environment.OSM_VERSION_URL).subscribe((res: { version: string }): void => {
412 const version: string[] = res.version.split('+');
413 if (!isNullOrUndefined(version[0])) {
414 this.osmVersion = version[0];
415 sessionStorage.setItem('version', version[0]);
417 this.osmVersion = null;
419 }, (error: ERRORDATA): void => {
420 this.osmVersion = null;
421 this.restService.handleError(error, 'get');
425 /** Random RGB color code generator @public */
426 public generateColor(): string {
427 const x: number = Math.floor((new Date().getHours()) * this.colourHour);
428 const y: number = Math.floor((new Date().getMinutes()) * this.colourMin);
429 const z: number = Math.floor((new Date().getSeconds()) * this.colourMin);
430 return 'rgb(' + x + ',' + y + ',' + z + ')';
433 /** Add custom name/tag to the dropdown @public */
434 public addCustomTag(tag: string): string {
438 /** Fetch file extension @public */
439 public fetchFileExtension(fileInfo: FileList): string {
440 return fileInfo[0].name.substring(fileInfo[0].name.lastIndexOf('.') + 1);
443 /** Get domain name @private */
444 public getDomainName(): Observable<TYPESECTION[]> {
445 return this.restService.getResource(environment.DOMAIN_URL).pipe(map((domains: DOMAINS): TYPESECTION[] => {
446 const domainList: TYPESECTION[] = [];
448 let domainNames: string[] = [];
449 if (!isNullOrUndefined(domains.project_domain_name)) {
450 domainNames = domainNames.concat(domains.project_domain_name.split(','));
452 if (!isNullOrUndefined(domains.user_domain_name)) {
453 domainNames = domainNames.concat(domains.user_domain_name.split(','));
455 domainNames = Array.from(new Set(domainNames));
456 if (domainNames.length > 0) {
457 domainNames.forEach((domainName: string): void => {
458 if (!domainName.endsWith(':ro')) {
459 domainList.push({ title: domainName, value: domainName });
470 /** Sorting the list based on date @public */
471 public compareFunction = (dir: number, a: string, b: string): number => {
472 const first: number = new Date(a).getTime();
473 const second: number = new Date(b).getTime();
474 if (first < second) {
477 if (first > second) {
483 /** Method to validate file extension and size @private */
484 private vaildataFileInfo(fileInfo: File, fileType: string): boolean {
485 const extension: string = fileInfo.name.substring(fileInfo.name.lastIndexOf('.') + 1);
486 const packageSize: number = CONSTANTNUMBER.oneMB * environment.packageSize;
487 if (fileType === 'yaml' && (extension.toLowerCase() === 'yaml' || extension.toLowerCase() === 'yml')
488 && fileInfo.size <= packageSize) {
490 } else if (extension.toLowerCase() === fileType && fileInfo.size <= packageSize) {
496 /** Method to read file content based on type @private */
497 private readFileContent(reader: FileReader, fileInfo: File, fileType: string): void {
498 if (fileType === 'gz') {
499 reader.readAsArrayBuffer(fileInfo);
501 reader.readAsText(fileInfo);
505 /** Method to handle http options @public */
506 private getHttpOptions(): GETAPIURLHEADER {
508 headers: new HttpHeaders({
509 Accept: 'application/gzip, application/json',
510 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
512 responseType: 'arraybuffer'
517 /** Method to handle null or undefined @public */
518 // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
519 export const isNullOrUndefined = (data: any): boolean => data === null || data === undefined;