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 /** Variable to hold regexp pattern for EMAIL */
85 public REGX_EMAIL_PATTERN: RegExp = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
87 /** Variables to hold maxlength for the description @public */
88 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
89 public MAX_LENGTH_DESCRIPTION: number = 500;
91 /** Variables to hold maxlength for the name @public */
92 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
93 public MAX_LENGTH_NAME: number = 50;
95 /** FormGroup instance added to the form @ html @public */
96 public formGroup: FormGroup;
98 /** Controls the go to top button on scroll @public */
99 public showGotoTop: boolean;
101 /** Holds OSM Version value @public */
102 public osmVersion: string;
104 /** Holds Last Login Toaster Message @public */
105 public lastLoginMessage: string;
107 /** Holds Failed Attempts Toaster Message @public */
108 public failedAttemptsMessage: string;
110 /** Holds No Of Days Toaster Message @public */
111 public daysMessage: string;
113 /** express number for time manupulation -2 */
114 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
115 private epochTimeMinus2: number = -2;
117 /** express number for time manupulation 1000 */
118 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
119 private epochTime1000: number = 1000;
121 /** express number for time manupulation 60 */
122 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
123 private epochTime60: number = 60;
125 /** express number for time manupulation 24 */
126 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
127 private epochTime24: number = 24;
129 /** Random string generator length */
130 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
131 private randomStringLength: number = 4;
133 /** express number for rgb manipulation */
134 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
135 private colourHour: number = 10;
137 /** express number for rgb manipulation*/
138 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
139 private colourMin: number = 5;
141 /** Instance of the rest service @private */
142 private restService: RestService;
144 /** Service holds the router information @private */
145 private router: Router;
147 /** Check for the root directory @private */
148 // eslint-disable-next-line @typescript-eslint/no-magic-numbers
149 private directoryCount: number = 2;
151 /** express number for time manupulation 1000 */
152 private toasterSettings: {} = {
158 /** Contains tranlsate instance @private */
159 private translateService: TranslateService;
161 /** Contains toaster instance @private */
162 private toaster: ToastrService;
164 constructor(restService: RestService, router: Router, translateService: TranslateService, toaster: ToastrService) {
165 this.restService = restService;
166 this.router = router;
167 this.translateService = translateService;
168 this.toaster = toaster;
171 /** convert epoch time function @public */
172 public convertEpochTime(unixtimestamp: number): string {
173 if (!isNullOrUndefined(unixtimestamp)) {
174 const monthsArr: string[] = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
175 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
176 const date: Date = new Date(unixtimestamp * this.epochTime1000);
177 const year: number = date.getFullYear();
178 const month: string = monthsArr[date.getMonth()];
179 const day: number = date.getDate();
180 const hours: number = date.getHours();
181 const minutes: string = '0' + date.getMinutes();
182 const seconds: string = '0' + date.getSeconds();
183 // eslint-disable-next-line deprecation/deprecation
184 return month + '-' + day + '-' + year + ' ' + hours + ':' + minutes.substr(this.epochTimeMinus2) + ':'
185 // eslint-disable-next-line deprecation/deprecation
186 + seconds.substr(this.epochTimeMinus2);
188 return this.translateService.instant('NODATE');
191 /** convert epoch time function to No of days @public */
192 public converEpochToDays(date: string): number {
193 if (!isNullOrUndefined(date)) {
194 const today: Date = new Date();
195 const accountDate: Date = new Date(date);
196 const toasterDate: number = (accountDate.getTime() -
197 today.getTime()) / this.epochTime1000 / this.epochTime60 / this.epochTime60 / this.epochTime24;
198 if (toasterDate >= 0 || toasterDate < 1) {
199 return Math.round(toasterDate);
201 return Math.floor(toasterDate);
203 return this.translateService.instant('N/A');
206 /** show toaster for password & account expiry @public */
207 public showToaster(lastLogin: string, failedAttempts: string, passwordNoOfDays: string,
208 accountNoOfDays: string, passwordExpireMessage: string, accountExpireMessage: string,
209 passwordMessage: string, accountMessage: string): ActiveToast<string> {
210 this.lastLoginMessage = this.translateService.instant('PAGE.LOGIN.LASTACCESS');
211 this.failedAttemptsMessage = this.translateService.instant('PAGE.LOGIN.FAILED');
212 return this.toaster.info(this.lastLoginMessage + ':' + ' ' + lastLogin +
213 '</br>' + this.failedAttemptsMessage + ':' + ' ' + failedAttempts +
214 '</br>' + passwordExpireMessage + ' ' + passwordNoOfDays + ' ' + passwordMessage +
215 '</br>' + accountExpireMessage + ' ' + accountNoOfDays + ' ' + accountMessage,
216 this.translateService.instant('PAGE.LOGIN.LOGINHISTORY'), this.toasterSettings);
219 /** show toaster for password expiry @public */
220 public passwordToaster(lastLogin: string, failedAttempts: string, passwordNoOfDays: string,
221 passwordExpireMessage: string, passwordMessage: string): ActiveToast<string> {
222 this.lastLoginMessage = this.translateService.instant('PAGE.LOGIN.LASTACCESS');
223 this.failedAttemptsMessage = this.translateService.instant('PAGE.LOGIN.FAILED');
224 return this.toaster.info(this.lastLoginMessage + ':' + ' ' + lastLogin +
225 '</br>' + this.failedAttemptsMessage + ':' + ' ' + failedAttempts +
226 '</br>' + passwordExpireMessage + ' ' + passwordNoOfDays + ' ' + passwordMessage,
227 this.translateService.instant('PAGE.LOGIN.LOGINHISTORY'), this.toasterSettings);
230 /** show toaster for account expiry @public */
231 public accountToaster(lastLogin: string, failedAttempts: string,
232 accountNoOfDays: string, accountExpireMessage: string, accountMessage: string): ActiveToast<string> {
233 this.lastLoginMessage = this.translateService.instant('PAGE.LOGIN.LASTACCESS');
234 this.failedAttemptsMessage = this.translateService.instant('PAGE.LOGIN.FAILED');
235 return this.toaster.info(this.lastLoginMessage + ':' + ' ' + lastLogin +
236 '</br>' + this.failedAttemptsMessage + ':' + ' ' + failedAttempts +
237 '</br>' + accountExpireMessage + ' ' + accountNoOfDays + ' ' + accountMessage,
238 this.translateService.instant('PAGE.LOGIN.LOGINHISTORY'), this.toasterSettings);
241 /** Download Files function @public */
242 public downloadFiles(name: string, binaryData: Blob[], filetype: string): void {
243 const downloadLink: HTMLAnchorElement = document.createElement('a');
244 downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, { type: filetype }));
245 if (name !== undefined) {
246 // eslint-disable-next-line @typescript-eslint/no-explicit-any
247 const newVariable: any = window.navigator;
248 if (newVariable.msSaveOrOpenBlob) {
249 newVariable.msSaveBlob(new Blob(binaryData, { type: filetype }), 'OSM_Export_' + name + '.tar.gz');
251 downloadLink.setAttribute('download', 'OSM_Export_' + name + '.tar.gz');
252 document.body.appendChild(downloadLink);
253 downloadLink.click();
258 /** Call this method after delete perform action is completed in the ng-smart-table data @public */
259 public callData(): void {
260 this.dataEvent.emit();
263 /** Generate random string @public */
264 public randomString(): string {
265 let result: string = '';
266 for (let randomStringRef: number = this.randomStringLength; randomStringRef > 0; --randomStringRef) {
267 result += new Date().getSeconds();
272 /** Function to read uploaded file String @public */
273 public async getFileString(files: FileList, fileType: string): Promise<string | ArrayBuffer> {
274 const reader: FileReader = new FileReader();
275 return new Promise<string | ArrayBuffer>((resolve: Function, reject: Function): void => {
276 if (this.vaildataFileInfo(files[0], fileType)) {
277 this.readFileContent(reader, files[0], fileType);
281 reader.onload = (): void => {
282 if (reader.result === null) {
283 reject('contentError');
285 resolve(reader.result);
287 reader.onerror = (event: Event): void => {
288 reject('contentError');
293 /** Method to handle tar and tar.gz file for shared YAML file content @public */
294 public async targzFile(packageInfo: PACKAGEINFO): Promise<string | ArrayBuffer> {
295 return new Promise<string | ArrayBuffer>((resolve: Function, reject: Function): void => {
296 const httpOptions: GETAPIURLHEADER = this.getHttpOptions();
297 let apiUrl: string = '';
298 apiUrl = packageInfo.packageType === 'nsd' ? environment.NSDESCRIPTORS_URL + '/' + packageInfo.id + '/nsd_content' :
299 environment.VNFPACKAGES_URL + '/' + packageInfo.id + '/package_content';
300 this.restService.getResource(apiUrl, httpOptions).subscribe((response: ArrayBuffer): void => {
302 // eslint-disable-next-line @typescript-eslint/no-explicit-any
303 const tar: any = new Tar();
304 const originalInput: Uint8Array = pako.inflate(response, { to: 'Uint8Array' });
305 untar(originalInput.buffer).then((extractedFiles: TARSETTINGS[]): void => {
306 const getFoldersFiles: {}[] = extractedFiles;
307 const folderNameStr: string = extractedFiles[0].name;
308 getFoldersFiles.forEach((value: TARSETTINGS): void => {
309 const fileValueObj: FILESETTINGS = this.createFileValueObject(value);
310 const getRootFolder: string[] = value.name.split('/');
311 if (value.name.startsWith(folderNameStr) &&
312 (value.name.endsWith('.yaml') || value.name.endsWith('.yml')) &&
313 getRootFolder.length === this.directoryCount) {
314 tar.append(value.name, packageInfo.descriptor, fileValueObj);
316 if (value.type !== 'L') {
317 tar.append(value.name, new Uint8Array(value.buffer), fileValueObj);
321 const out: Uint8Array = tar.out;
322 const originalOutput: Uint8Array = pako.gzip(out);
323 resolve(originalOutput.buffer);
324 }, (err: string): void => {
330 }, (error: HttpErrorResponse): void => {
331 if (error.status === HttpStatus.NOT_FOUND || error.status === HttpStatus.UNAUTHORIZED) {
332 this.router.navigateByUrl('404', { skipLocationChange: true }).catch((): void => {
333 // Catch Navigation Error
336 this.restService.handleError(error, 'get');
343 /** Method to return the file information @public */
344 public createFileValueObject(value: TARSETTINGS): FILESETTINGS {
347 linkname: value.linkname,
353 /** Method to check given string is JSON or not @public */
354 public checkJson(jsonString: string): boolean {
355 jsonString = jsonString.replace(/'/g, '"');
357 JSON.parse(jsonString);
364 /** Clean the form before submit @public */
365 public cleanForm(formGroup: FormGroup, formName?: String): void {
366 Object.keys(formGroup.controls).forEach((key: string) => {
367 if ((!isNullOrUndefined((formGroup.get(key) as FormArray | FormGroup).controls)) && key !== 'config') {
368 // eslint-disable-next-line @typescript-eslint/no-shadow
369 for (const { item, index } of (formGroup.get(key).value).map((item: {}, index: number) => ({ item, index }))) {
370 // eslint-disable-next-line security/detect-object-injection
371 const newFormGroup: FormGroup = (formGroup.get(key) as FormArray).controls[index] as FormGroup;
372 this.cleanForm(newFormGroup);
374 } else if (formGroup.get(key).value !== undefined && formGroup.get(key).value !== null && key !== 'config') {
375 if (!Array.isArray(formGroup.get(key).value)) {
376 if (typeof formGroup.get(key).value === 'string') {
377 formGroup.get(key).setValue(formGroup.get(key).value.trim());
380 } else if (key === 'config' && formName === 'vim') {
381 const newFormGroup: FormGroup = formGroup.get(key) as FormGroup;
382 this.cleanForm(newFormGroup);
387 /** Method to return the config of pager value for ngSmarttable @public */
388 public paginationPagerConfig(): PAGERSMARTTABLE {
391 perPage: environment.paginationNumber
395 /** Method to return the class for the table for ngSmarttable @public */
396 public tableClassConfig(): SMARTTABLECLASS {
398 class: 'table list-data'
402 /** Method to return all languages name and its code @public */
403 public languageCodeList(): {}[] {
405 { code: 'en', language: 'English' },
406 { code: 'es', language: 'Spanish' },
407 { code: 'pt', language: 'Portuguese' },
408 { code: 'de', language: 'German' }
412 /** Fetch OSM Version @public */
413 public fetchOSMVersion(): void {
414 this.restService.getResource(environment.OSM_VERSION_URL).subscribe((res: { version: string }): void => {
415 const version: string[] = res.version.split('+');
416 if (!isNullOrUndefined(version[0])) {
417 this.osmVersion = version[0];
418 sessionStorage.setItem('version', version[0]);
420 this.osmVersion = null;
422 }, (error: ERRORDATA): void => {
423 this.osmVersion = null;
424 this.restService.handleError(error, 'get');
428 /** Random RGB color code generator @public */
429 public generateColor(): string {
430 const x: number = Math.floor((new Date().getHours()) * this.colourHour);
431 const y: number = Math.floor((new Date().getMinutes()) * this.colourMin);
432 const z: number = Math.floor((new Date().getSeconds()) * this.colourMin);
433 return 'rgb(' + x + ',' + y + ',' + z + ')';
436 /** Add custom name/tag to the dropdown @public */
437 public addCustomTag(tag: string): string {
441 /** Fetch file extension @public */
442 public fetchFileExtension(fileInfo: FileList): string {
443 return fileInfo[0].name.substring(fileInfo[0].name.lastIndexOf('.') + 1);
446 /** Get domain name @private */
447 public getDomainName(): Observable<TYPESECTION[]> {
448 return this.restService.getResource(environment.DOMAIN_URL).pipe(map((domains: DOMAINS): TYPESECTION[] => {
449 const domainList: TYPESECTION[] = [];
451 let domainNames: string[] = [];
452 if (!isNullOrUndefined(domains.project_domain_name)) {
453 domainNames = domainNames.concat(domains.project_domain_name.split(','));
455 if (!isNullOrUndefined(domains.user_domain_name)) {
456 domainNames = domainNames.concat(domains.user_domain_name.split(','));
458 domainNames = Array.from(new Set(domainNames));
459 if (domainNames.length > 0) {
460 domainNames.forEach((domainName: string): void => {
461 if (!domainName.endsWith(':ro')) {
462 domainList.push({ title: domainName, value: domainName });
473 /** Sorting the list based on date @public */
474 public compareFunction = (dir: number, a: string, b: string): number => {
475 const first: number = new Date(a).getTime();
476 const second: number = new Date(b).getTime();
477 if (first < second) {
480 if (first > second) {
486 /** Method to validate file extension and size @private */
487 private vaildataFileInfo(fileInfo: File, fileType: string): boolean {
488 const extension: string = fileInfo.name.substring(fileInfo.name.lastIndexOf('.') + 1);
489 const packageSize: number = CONSTANTNUMBER.oneMB * environment.packageSize;
490 if (fileType === 'yaml' && (extension.toLowerCase() === 'yaml' || extension.toLowerCase() === 'yml')
491 && fileInfo.size <= packageSize) {
493 } else if (extension.toLowerCase() === fileType && fileInfo.size <= packageSize) {
499 /** Method to read file content based on type @private */
500 private readFileContent(reader: FileReader, fileInfo: File, fileType: string): void {
501 if (fileType === 'gz') {
502 reader.readAsArrayBuffer(fileInfo);
504 reader.readAsText(fileInfo);
508 /** Method to handle http options @public */
509 private getHttpOptions(): GETAPIURLHEADER {
511 headers: new HttpHeaders({
512 Accept: 'application/gzip, application/json',
513 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
515 responseType: 'arraybuffer'
520 /** Method to handle null or undefined @public */
521 // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
522 export const isNullOrUndefined = (data: any): boolean => data === null || data === undefined;