Initial Commit - NG UI
* Roboto and font-awesome fonts are added in package.json
* Replace Nginx alpine varient to stable
* Devops files are added
* Docker file aligned as per community reviews
* Enhancement - NS primitive, Azure inclusion and domain name
* RWD changes
Change-Id: If543efbf127964cbd8f4be4c5a67260c91407fd9
Signed-off-by: kumaran.m <kumaran.m@tataelxsi.co.in>
diff --git a/src/services/AcessGuardService.ts b/src/services/AcessGuardService.ts
new file mode 100644
index 0000000..42d36a5
--- /dev/null
+++ b/src/services/AcessGuardService.ts
@@ -0,0 +1,43 @@
+/*
+ Copyright 2020 TATA ELXSI
+
+ Licensed under the Apache License, Version 2.0 (the 'License');
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+ */
+
+/**
+ * @file Auth gaurd
+ */
+import { Injectable } from '@angular/core';
+import { CanLoad, Route } from '@angular/router';
+import { Observable } from 'rxjs';
+
+/**
+ * An Injectable is a class adorned with the @Injectable decorator function.
+ * @Injectable takes a metadata object that tells Angular how to compile and run module code
+ */
+@Injectable()
+export class AcessGuardService implements CanLoad {
+ /**
+ * check if module can be loaded
+ */
+ public canLoad(route: Route): Observable<boolean> | Promise<boolean> | boolean {
+ // Need to get the Role and valid here for authorization
+ if (localStorage.getItem('role') === 'Admin') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/src/services/AuthGuardService.ts b/src/services/AuthGuardService.ts
new file mode 100644
index 0000000..0c8d1e4
--- /dev/null
+++ b/src/services/AuthGuardService.ts
@@ -0,0 +1,61 @@
+/*
+ Copyright 2020 TATA ELXSI
+
+ Licensed under the Apache License, Version 2.0 (the 'License');
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+ */
+
+/**
+ * @file Auth gaurd
+ */
+import { Injectable } from '@angular/core';
+import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
+import { AuthenticationService } from 'AuthenticationService';
+import { Observable } from 'rxjs';
+import { map, take } from 'rxjs/operators';
+
+/**
+ * An Injectable is a class adorned with the @Injectable decorator function.
+ * @Injectable takes a metadata object that tells Angular how to compile and run module code
+ */
+@Injectable()
+export class AuthGuardService implements CanActivate {
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+ /** Holds teh instance of Router class of type Router @private */
+ private authService: AuthenticationService;
+
+ constructor(router: Router, authService: AuthenticationService) {
+ this.router = router;
+ this.authService = authService;
+ }
+
+ /**
+ * Returns Observable<boolean> if authorized @public
+ */
+ public canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+ return this.authService.isLoggedIn
+ .pipe(
+ take(1),
+ map((isLoggedIn: boolean) => {
+ if (!isLoggedIn) {
+ this.router.navigate(['/login']).catch(() => {
+ //TODO: Handle error notification
+ });
+ }
+ return true;
+ })
+ );
+ }
+}
diff --git a/src/services/AuthInterceptorService.ts b/src/services/AuthInterceptorService.ts
new file mode 100644
index 0000000..ede10a8
--- /dev/null
+++ b/src/services/AuthInterceptorService.ts
@@ -0,0 +1,126 @@
+/*
+ Copyright 2020 TATA ELXSI
+
+ Licensed under the Apache License, Version 2.0 (the 'License');
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+ */
+
+/**
+ * @file HttpInterceptor file
+ */
+import {
+ HttpErrorResponse, HttpHandler, HttpHeaderResponse, HttpInterceptor, HttpProgressEvent,
+ HttpRequest, HttpResponse, HttpSentEvent, HttpUserEvent
+} from '@angular/common/http';
+import { Injectable, Injector } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { AuthenticationService } from 'AuthenticationService';
+import * as HttpStatus from 'http-status-codes';
+import { Observable, throwError } from 'rxjs';
+import { catchError, retry } from 'rxjs/operators';
+
+/**
+ * An Injectable is a class adorned with the @Injectable decorator function.
+ * @Injectable takes a metadata object that tells Angular how to compile and run module code
+ */
+@Injectable()
+export class AuthInterceptorService implements HttpInterceptor {
+ /** Holds header options @private */
+ private clonedReq: HttpRequest<{}>;
+
+ /** To inject services @private */
+ private injector: Injector;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Utilizes auth service for any auth operations @private */
+ private authService: AuthenticationService;
+
+ /** create the instance of the component */
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.notifierService = this.injector.get(NotifierService);
+ this.authService = this.injector.get(AuthenticationService);
+ this.translateService = this.injector.get(TranslateService);
+ }
+
+ /**
+ * intercept logic
+ * @param req
+ * @param next
+ */
+ public intercept(req: HttpRequest<{}>, next: HttpHandler): Observable<HttpSentEvent |
+ // tslint:disable-next-line:no-any
+ HttpHeaderResponse | HttpProgressEvent | HttpResponse<{}> | HttpUserEvent<any> | any> {
+ const idToken: string = localStorage.getItem('id_token');
+ const excludedUrl: string[] = ['osm/admin/v1/tokens', 'assets/i18n/', 'osm/version'];
+ if (excludedUrl.some((x: string): boolean => { return req.url.includes(x); })) { return next.handle(req); }
+ if (idToken.length > 0) {
+ this.setHeader(req, idToken);
+ return next.handle(this.clonedReq).pipe(
+ catchError((err: HttpErrorResponse) => {
+ this.errorRes(err, req, next);
+ return throwError(err);
+ })
+ );
+ } else {
+ //TODO: Handle error via notification service
+ }
+ }
+
+ /** Set header options @public */
+ // tslint:disable-next-line:no-any
+ public setHeader(req: HttpRequest<any>, idToken: string): void {
+ if (req.body !== null && req.body.byteLength !== null) {
+ this.clonedReq = req.clone({
+ setHeaders: { Authorization: 'Bearer ' + idToken, 'Cache-Control': 'no-cache', Pragma: 'no-cache' }
+ });
+ } else {
+ this.clonedReq = req.clone({
+ setHeaders: { Authorization: 'Bearer ' + idToken, 'Content-Type': 'charset=UTF-8',
+ 'Cache-Control': 'no-cache', Pragma: 'no-cache' }
+ });
+ }
+ }
+
+ /** Handles error response @public */
+ public errorRes(err: HttpErrorResponse, req: HttpRequest<{}>, next: HttpHandler): Observable<{}> {
+ if (err instanceof HttpErrorResponse) {
+ switch (err.status) {
+ case HttpStatus.UNAUTHORIZED || HttpStatus.FORBIDDEN:
+ this.handleError(err);
+ break;
+ default: return throwError(err);
+ }
+ } else { return throwError(err); }
+ }
+
+ /** Method to handle 401 & 403 error */
+ private handleError(err: HttpErrorResponse): void {
+ if (err.error.detail === 'Expired Token or Authorization HTTP header' ||
+ err.error.detail === 'Invalid Token or Authorization HTTP header') {
+ this.notifierService.hideAll();
+ this.authService.logoutResponse();
+ if (this.authService.handle401) {
+ this.notifierService.notify('error', this.translateService.instant('SESSIONEXPIRY'));
+ this.authService.handle401 = false;
+ }
+ }
+ }
+}
diff --git a/src/services/AuthenticationService.ts b/src/services/AuthenticationService.ts
new file mode 100644
index 0000000..728d4ac
--- /dev/null
+++ b/src/services/AuthenticationService.ts
@@ -0,0 +1,195 @@
+/*
+ Copyright 2020 TATA ELXSI
+
+ Licensed under the Apache License, Version 2.0 (the 'License');
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+ */
+/**
+ * @file Auth service
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Injectable, Injector } from '@angular/core';
+import { Router } from '@angular/router';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { Idle } from '@ng-idle/core';
+import { APIURLHEADER, ERRORDATA } from 'CommonModel';
+import { environment } from 'environment';
+import { BehaviorSubject, Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { isNullOrUndefined } from 'util';
+import { ProjectModel } from '../models/VNFDModel';
+import { RestService } from './RestService';
+
+/**
+ * An Injectable is a class adorned with the @Injectable decorator function.
+ * @Injectable takes a metadata object that tells Angular how to compile and run module code
+ */
+@Injectable()
+export class AuthenticationService {
+ /**
+ * Get method for Observable loggedIn
+ */
+ get isLoggedIn(): Observable<boolean> {
+ return this.loggedIn.asObservable();
+ }
+
+ /**
+ * Get method for Observable Username
+ */
+ get username(): Observable<string> {
+ return this.userName.asObservable();
+ }
+
+ /** Get method for project name */
+ get ProjectName(): Observable<string> {
+ return this.projectName$.asObservable();
+ }
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance for modal service @public */
+ public modalService: NgbModal;
+
+ /** Handle 401 response for multiple API calls */
+ public handle401: boolean = true;
+
+ /** contains return URL link @public */
+ public returnUrl: string;
+
+ /** Holds the username in condition of type BehaviorSubject<string> @public */
+ public userName: BehaviorSubject<string> = new BehaviorSubject<string>('');
+
+ /** Holds the projectname in condition of type BehaviorSubject<string> @public */
+ public projectName$: BehaviorSubject<string> = new BehaviorSubject<string>('');
+
+ /** Holds the instance of router class @private */
+ private router: Router;
+
+ /** Holds the logged in condition of type BehaviorSubject<boolean> @private */
+ private loggedIn: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
+
+ /** Hold Rest Service Objects */
+ private restService: RestService;
+
+ /** Holds auth payloads @private */
+ private payLoad: {};
+
+ /** Holds header options for auth service @private */
+ private httpOptions: HttpHeaders;
+
+ /** handle idle time out service @private */
+ private idle: Idle;
+
+ /** create the instance of the component */
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.router = this.injector.get(Router);
+ this.restService = this.injector.get(RestService);
+ this.modalService = this.injector.get(NgbModal);
+ this.idle = this.injector.get(Idle);
+ if (localStorage.getItem('id_token') !== null) {
+ this.loggedIn.next(true);
+ } else {
+ this.loggedIn.next(false);
+ }
+ this.userName.next(localStorage.getItem('username'));
+ }
+
+ /**
+ * Send request and authenticate the user
+ * @param user of type User
+ */
+ public login(username: string, password: string): Observable<{}> {
+ this.setHeader();
+ this.setPayLoad(username, password);
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.GENERATETOKEN_URL,
+ httpOptions: { headers: this.httpOptions }
+ };
+ return this.restService.postResource(apiURLHeader, this.payLoad)
+ .pipe(map((data: ProjectModel) => {
+ if (data) {
+ this.setLocalStorage(data);
+ this.idle.watch(true);
+ this.loggedIn.next(true);
+ this.handle401 = true;
+ this.userName.next(data.username);
+ return this.loggedIn;
+ }
+ }, (error: ERRORDATA) => { this.restService.handleError(error, 'post'); }
+ ));
+ }
+
+ /** Set headers for auth session @public */
+ public setHeader(): void {
+ this.httpOptions = new HttpHeaders({
+ 'Content-Type': 'application/json; charset=UTF-8',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ }
+
+ /** Set payloads for auth session @public */
+ public setPayLoad(username: string, password: string): void {
+ this.payLoad = JSON.stringify({
+ username,
+ password
+ });
+ }
+
+ /** set local storage on auth process @public */
+ public setLocalStorage(data: ProjectModel): void {
+ localStorage.setItem('id_token', data.id);
+ localStorage.setItem('expires', data.expires.toString());
+ localStorage.setItem('username', data.username);
+ localStorage.setItem('isAdmin', (data.admin) ? 'true' : 'false');
+ localStorage.setItem('project_id', data.project_id);
+ localStorage.setItem('project', data.project_name);
+ localStorage.setItem('token_state', data.id);
+ this.projectName$.next(data.project_name);
+ }
+ /** Destory tokens API response handling @public */
+ public logoutResponse(): void {
+ this.loggedIn.next(false);
+ const langCode: string = localStorage.getItem('languageCode');
+ const redirecturl: string = isNullOrUndefined(localStorage.getItem('returnUrl')) ? '/' : localStorage.getItem('returnUrl');
+ localStorage.clear();
+ localStorage.setItem('languageCode', langCode);
+ localStorage.setItem('returnUrl', redirecturl);
+ localStorage.setItem('token_state', null);
+ this.idle.stop();
+ this.router.navigate(['login']).catch();
+ }
+ /**
+ * Logout the user & clearing the token.
+ */
+ public logout(): void {
+ this.returnUrl = this.router.url;
+ localStorage.setItem('returnUrl', this.returnUrl);
+ this.modalService.dismissAll();
+ this.destoryToken();
+ }
+ /** Destory tokens on logout @private */
+ private destoryToken(): void {
+ const tokenID: string = localStorage.getItem('id_token');
+ if (tokenID !== null) {
+ const deletingURl: string = environment.GENERATETOKEN_URL + '/' + tokenID;
+ this.restService.deleteResource(deletingURl).subscribe((res: {}) => {
+ this.logoutResponse();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'delete');
+ });
+ }
+ }
+}
diff --git a/src/services/DataService.ts b/src/services/DataService.ts
new file mode 100644
index 0000000..e64c34c
--- /dev/null
+++ b/src/services/DataService.ts
@@ -0,0 +1,40 @@
+/*
+ Copyright 2020 TATA ELXSI
+
+ Licensed under the Apache License, Version 2.0 (the 'License');
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+ */
+
+/**
+ * @file Data Services for user related information.
+ */
+import { Injectable } from '@angular/core';
+import { BehaviorSubject, Observable } from 'rxjs';
+
+/**
+ * This file created during the angular project creation
+ */
+@Injectable()
+/** Exporting a class @exports DataService */
+export class DataService {
+ /** message source @public */
+ public messageSource: BehaviorSubject<{}> = new BehaviorSubject<{}>({});
+
+ /** current message @public */
+ public currentMessage: Observable<{}> = this.messageSource.asObservable();
+ /** change message function @public */
+ public changeMessage(message: {}): void {
+ this.messageSource.next(message);
+ }
+}
diff --git a/src/services/DeviceCheckService.ts b/src/services/DeviceCheckService.ts
new file mode 100644
index 0000000..98ab2fc
--- /dev/null
+++ b/src/services/DeviceCheckService.ts
@@ -0,0 +1,52 @@
+/*
+ Copyright 2020 TATA ELXSI
+
+ Licensed under the Apache License, Version 2.0 (the 'License');
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+ */
+import { Injectable } from '@angular/core';
+import { BehaviorSubject, Observable } from 'rxjs';
+/**
+ * @file Provider for Device Check Service
+ */
+/**
+ * An Injectable is a class adorned with the @Injectable decorator function.
+ * @Injectable takes a metadata object that tells Angular how to compile and run module code
+ */
+@Injectable({
+ providedIn: 'root'
+})
+/** Exporting a class @exports DeviceCheckService */
+export class DeviceCheckService {
+ /** Get method for Observable isMobile */
+ get isMobile(): Observable<boolean> {
+ return this.isMobile$.asObservable();
+ }
+ /** Holds the mobile condition of type BehaviorSubject<boolean> @private */
+ private isMobile$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
+
+ /** Return the Device type @public */
+ public checkDeviceType(): void {
+ if (navigator.userAgent.match(/Android/i)
+ || navigator.userAgent.match(/webOS/i)
+ || navigator.userAgent.match(/iPhone/i)
+ || navigator.userAgent.match(/iPod/i)
+ || navigator.userAgent.match(/BlackBerry/i)
+ || navigator.userAgent.match(/Windows Phone/i)) {
+ this.isMobile$.next(true);
+ } else {
+ this.isMobile$.next(false);
+ }
+ }
+}
diff --git a/src/services/ProjectService.ts b/src/services/ProjectService.ts
new file mode 100644
index 0000000..c22f33a
--- /dev/null
+++ b/src/services/ProjectService.ts
@@ -0,0 +1,114 @@
+/*
+ Copyright 2020 TATA ELXSI
+
+ Licensed under the Apache License, Version 2.0 (the 'License');
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+ */
+/**
+ * @file Project Service
+ */
+import { Injectable, Injector } from '@angular/core';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { AuthenticationService } from 'AuthenticationService';
+import { environment } from 'environment';
+import { ProjectData } from 'ProjectModel';
+import { BehaviorSubject, Observable, Subscription } from 'rxjs';
+import { SwitchProjectComponent } from 'SwitchProjectComponent';
+import { ProjectRoleMappings, UserDetail } from 'UserModel';
+import { ProjectModel } from 'VNFDModel';
+import { RestService } from './RestService';
+
+/**
+ * An Injectable is a class adorned with the @Injectable decorator function.
+ * @Injectable takes a metadata object that tells Angular how to compile and run module code
+ */
+@Injectable({
+ providedIn: 'root'
+})
+/** Exporting a class @exports ProjectService */
+export class ProjectService {
+ /** Get method for project list */
+ get projectList(): Observable<{}[]> {
+ return this.projectList$.asObservable();
+ }
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Holds all the projects details */
+ public allProjectList: string[];
+
+ /** Observable holds logined value @public */
+ public username$: Observable<string>;
+
+ /** Hold Rest Service Objects */
+ private restService: RestService;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Utilizes auth service for any auth operations @private */
+ private authService: AuthenticationService;
+
+ /** Holds the username in condition of type BehaviorSubject<string> @private */
+ private projectList$: BehaviorSubject<{}[]> = new BehaviorSubject<{}[]>([]);
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.modalService = this.injector.get(NgbModal);
+ this.authService = this.injector.get(AuthenticationService);
+ }
+
+ /** List all the projects @public */
+ public getAllProjects(): Observable<{}> {
+ return this.restService.getResource(environment.PROJECTS_URL);
+ }
+
+ /** Get current project details from local storage @public */
+ public getCurrentProjectDetails(): Observable<{}> {
+ const project: string = localStorage.getItem('project_id');
+ return this.restService.getResource(environment.PROJECTS_URL + '/' + project);
+ }
+
+ /** Returns all the projects for a particular users @public */
+ public getUserProjects(): Observable<{}> {
+ const username: string = localStorage.getItem('username');
+ return this.restService.getResource(environment.USERS_URL + '/' + username);
+ }
+
+ /** Set header projects @public */
+ public setHeaderProjects(): void {
+ this.getUserProjects().subscribe((projects: UserDetail) => {
+ const projectList: {}[] = projects.project_role_mappings;
+ projectList.filter((list: ProjectModel) => {
+ if (list.project === localStorage.getItem('project_id')) {
+ localStorage.setItem('project', list.project_name);
+ this.authService.projectName$.next(list.project_name);
+ }
+ });
+ const projectDistinctList: {}[] = projectList.filter(
+ (thing: ProjectRoleMappings, i: number, arr: []) => arr
+ .findIndex((t: ProjectRoleMappings) => t.project_name === thing.project_name) === i
+ );
+ this.projectList$.next(projectDistinctList);
+ });
+ }
+
+ /** Toggle projects on selection @public */
+ public switchProjectModal(list: ProjectData): void {
+ const username: string = localStorage.getItem('username');
+ this.modalService.open(SwitchProjectComponent, { backdrop: 'static' })
+ .componentInstance.params = { projectID: list.project, username };
+ }
+}
diff --git a/src/services/RestService.ts b/src/services/RestService.ts
new file mode 100644
index 0000000..d7ded6f
--- /dev/null
+++ b/src/services/RestService.ts
@@ -0,0 +1,144 @@
+/*
+ Copyright 2020 TATA ELXSI
+
+ Licensed under the Apache License, Version 2.0 (the 'License');
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+ */
+
+/**
+ * @file Provider for REST Service
+ */
+
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { ERRORDATA } from 'CommonModel';
+import * as HttpStatus from 'http-status-codes';
+import { Observable } from 'rxjs';
+
+/**
+ * An Injectable is a class adorned with the @Injectable decorator function.
+ * @Injectable takes a metadata object that tells Angular how to compile and run module code
+ */
+@Injectable()
+/** Exporting a class @exports RestService */
+export class RestService {
+ /** convenient way to modify request made by the http service both before they are sent and after they return */
+ private http: HttpClient;
+ /** API URL. Disabled tslint since server doesn't support https protocol */
+ private apiURL: string = '';
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+ /** Instance for active modal service @public */
+ private activeModal: NgbModal;
+ /** insatnce for translate @private */
+ private translateService: TranslateService;
+
+ constructor(http: HttpClient, notifierService: NotifierService, activeModal: NgbModal, translateService: TranslateService) {
+ this.http = http;
+ this.notifierService = notifierService;
+ this.activeModal = activeModal;
+ this.translateService = translateService;
+ }
+
+ /**
+ * Get a resource from the server which identified by a URI.
+ * @param apiURL The URL of the resource to be retrieved.
+ */
+
+ public getResource(apiURL: string, httpHeaders?: { headers: HttpHeaders }): Observable<{}> {
+ return this.http.get(apiURL, httpHeaders);
+ }
+
+ /**
+ * Create a new resource on the server.
+ * @param apiURL The URL of the resource to be created.
+ * @param payload The request data to be sent to server.
+ */
+
+ public postResource(apiURLHeader: { url: string, httpOptions?: { headers: HttpHeaders } }, payload: {}): Observable<{}> {
+ return this.http.post(apiURLHeader.url, payload, apiURLHeader.httpOptions);
+ }
+
+ /**
+ * Modify the resource on the server.
+ * @param apiURL The URL of the resource to be created.
+ * @param payload The request data to be sent to server.
+ */
+
+ public patchResource(apiURLHeader: { url: string, httpOptions?: { headers: HttpHeaders } }, payload: {}): Observable<object> {
+ return this.http.patch(apiURLHeader.url, payload, apiURLHeader.httpOptions);
+ }
+
+ /**
+ * Replace the resource on the server.
+ * @param apiName The URL of the resource to be created.
+ * @param payload The request data to be sent to server.
+ */
+
+ public putResource(apiURLHeader: { url: string, httpOptions?: { headers: HttpHeaders } }, payload: {}): Observable<object> {
+ return this.http.put(apiURLHeader.url, payload, apiURLHeader.httpOptions);
+ }
+
+ /**
+ * Delete a resource identified by a URL.
+ * @param apiURL The URL of the resource to be deleted.
+ */
+
+ public deleteResource(apiURL: string, httpHeaders?: { headers: HttpHeaders }): Observable<object> {
+ return this.http.delete(apiURL, httpHeaders);
+ }
+ /**
+ * Handle Error response based on the status.
+ * @param error The error response reecieved from API call.
+ * @param method The http request method.
+ */
+ // tslint:disable-next-line: cyclomatic-complexity
+ public handleError(err: ERRORDATA, method?: string): void {
+ if (err.error.status === HttpStatus.UNAUTHORIZED) {
+ if (method !== 'get') {
+ if (err.error.detail !== 'Expired Token or Authorization HTTP header' &&
+ err.error.detail !== 'Invalid Token or Authorization HTTP header') {
+ this.notifierService.notify('error', err.error.detail !== undefined ?
+ err.error.detail : this.translateService.instant('HTTPERROR.401'));
+ }
+ this.activeModal.dismissAll();
+ }
+ } else if (err.error.status === HttpStatus.BAD_REQUEST) {
+ this.notifierService.notify('error', err.error.detail !== undefined ?
+ err.error.detail : this.translateService.instant('HTTPERROR.400'));
+ } else if (err.error.status === HttpStatus.NOT_FOUND) {
+ this.notifierService.notify('error', err.error.detail !== undefined ?
+ err.error.detail : this.translateService.instant('HTTPERROR.404'));
+ } else if (err.error.status === HttpStatus.CONFLICT) {
+ this.notifierService.notify('error', err.error.detail !== undefined ?
+ err.error.detail : this.translateService.instant('HTTPERROR.409'));
+ this.activeModal.dismissAll();
+ } else if (err.error.status === HttpStatus.INTERNAL_SERVER_ERROR) {
+ this.notifierService.notify('error', err.error.detail !== undefined ?
+ err.error.detail : this.translateService.instant('HTTPERROR.500'));
+ } else if (err.error.status === HttpStatus.BAD_GATEWAY) {
+ this.notifierService.notify('error', this.translateService.instant('HTTPERROR.502'));
+ } else if (err.error.status === HttpStatus.SERVICE_UNAVAILABLE) {
+ this.notifierService.notify('error', this.translateService.instant('HTTPERROR.503'));
+ } else if (err.error.status === HttpStatus.GATEWAY_TIMEOUT) {
+ this.notifierService.notify('error', this.translateService.instant('HTTPERROR.504'));
+ } else {
+ this.notifierService.notify('error', err.error.detail !== undefined ?
+ err.error.detail : this.translateService.instant('ERROR'));
+ }
+ }
+}
diff --git a/src/services/SharedService.ts b/src/services/SharedService.ts
new file mode 100644
index 0000000..3a138e5
--- /dev/null
+++ b/src/services/SharedService.ts
@@ -0,0 +1,304 @@
+/*
+ Copyright 2020 TATA ELXSI
+
+ Licensed under the Apache License, Version 2.0 (the 'License');
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
+ */
+/**
+ * @file Provider for Shared Service
+ */
+import { HttpErrorResponse, HttpHeaders } from '@angular/common/http';
+import { EventEmitter, Injectable, Output } from '@angular/core';
+import { FormArray, FormGroup } from '@angular/forms';
+import { Router } from '@angular/router';
+import { CONSTANTNUMBER, ERRORDATA, GETAPIURLHEADER, PACKAGEINFO, PAGERSMARTTABLE, SMARTTABLECLASS, TARSETTINGS } from 'CommonModel';
+import { environment } from 'environment';
+import * as HttpStatus from 'http-status-codes';
+import * as untar from 'js-untar';
+import * as pako from 'pako';
+import { RestService } from 'RestService';
+import { isNullOrUndefined } from 'util';
+
+/** This is added globally by the tar.js library */
+// tslint:disable-next-line: no-any
+declare const Tar: any;
+
+/**
+ * An Injectable is a class adorned with the @Injectable decorator function.
+ * @Injectable takes a metadata object that tells Angular how to compile and run module code
+ */
+@Injectable({
+ providedIn: 'root'
+})
+/** Exporting a class @exports SharedService */
+export class SharedService {
+ /** call the parent using event information @private */
+ @Output() public dataEvent: EventEmitter<{}> = new EventEmitter<{}>();
+
+ /** Variables to hold regexp pattern for URL */
+ // tslint:disable-next-line: max-line-length
+ 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.,?'\\+&%$#=~_-]+))*$/);
+
+ /** Variables to hold regexp pattern for IP Address */
+ public REGX_IP_PATTERN: RegExp = new RegExp(/^(?:(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(?!$)|$)){4}$/);
+
+ /** Variables to hold regexp pattern for Port Number */
+ // tslint:disable-next-line: max-line-length
+ 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}))$/);
+
+ /** Variables to hold regexp pattern for DPID */
+ public REGX_DPID_PATTERN: RegExp = new RegExp(/^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){7}$/);
+
+ /** Variable to hold regexp pattern for password */
+ // tslint:disable-next-line: max-line-length
+ public REGX_PASSWORD_PATTERN: RegExp = new RegExp(/^.*(?=.{8,})((?=.*[!@#$%^&*()\-_=+{};:,<.>]){1})(?=.*\d)((?=.*[a-z]){1})((?=.*[A-Z]){1}).*$/);
+
+ /** FormGroup instance added to the form @ html @public */
+ public formGroup: FormGroup;
+
+ /** Controls the go to top button on scroll @public */
+ public showGotoTop: boolean;
+
+ /** Holds OSM Version value @public */
+ public osmVersion: string;
+
+ /** express number for time manupulation -2 */
+ private epochTimeMinus2: number = -2;
+
+ /** express number for time manupulation 1000 */
+ private epochTime1000: number = 1000;
+
+ /** Random string generator length */
+ private randomStringLength: number = 4;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** Service holds the router information @private */
+ private router: Router;
+
+ /** Check for the root directory @private */
+ private directoryCount: number = 2;
+
+ constructor(restService: RestService, router: Router) {
+ this.restService = restService;
+ this.router = router;
+ }
+
+ /** convert epoch time function @public */
+ public convertEpochTime(unixtimestamp: number): string {
+ const monthsArr: string[] = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+ const date: Date = new Date(unixtimestamp * this.epochTime1000);
+ const year: number = date.getFullYear();
+ const month: string = monthsArr[date.getMonth()];
+ const day: number = date.getDate();
+ const hours: number = date.getHours();
+ const minutes: string = '0' + date.getMinutes();
+ const seconds: string = '0' + date.getSeconds();
+ return month + '-' + day + '-' + year + ' ' + hours + ':' + minutes.substr(this.epochTimeMinus2) + ':'
+ + seconds.substr(this.epochTimeMinus2);
+ }
+
+ /** Download Files function @public */
+ public downloadFiles(shortName: string, binaryData: Blob[], filetype: string): void {
+ const downloadLink: HTMLAnchorElement = document.createElement('a');
+ downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, { type: filetype }));
+ if (shortName !== undefined) {
+ if (window.navigator.msSaveOrOpenBlob) {
+ window.navigator.msSaveBlob(new Blob(binaryData, { type: filetype }), 'OSM_Export_' + shortName + '.tar.gz');
+ } else {
+ downloadLink.setAttribute('download', 'OSM_Export_' + shortName + '.tar.gz');
+ document.body.appendChild(downloadLink);
+ downloadLink.click();
+ }
+ }
+ }
+
+ /** Call this method after delete perform action is completed in the ng-smart-table data @public */
+ public callData(): void {
+ this.dataEvent.emit();
+ }
+
+ /** Generate random string @public */
+ public randomString(): string {
+ const chars: string = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ let result: string = '';
+ // tslint:disable-next-line:no-increment-decrement
+ for (let randomStringRef: number = this.randomStringLength; randomStringRef > 0; --randomStringRef) {
+ result += chars[Math.floor(Math.random() * chars.length)];
+ }
+ return result;
+ }
+ /** Function to read uploaded file String @public */
+ public async getFileString(files: FileList, fileType: string): Promise<string | ArrayBuffer> {
+ const reader: FileReader = new FileReader();
+ return new Promise<string | ArrayBuffer>((resolve: Function, reject: Function): void => {
+ if (this.vaildataFileInfo(files[0], fileType)) {
+ this.readFileContent(reader, files[0], fileType);
+ } else {
+ reject('typeError');
+ }
+ reader.onload = (): void => {
+ if (reader.result === null) {
+ reject('contentError');
+ }
+ resolve(reader.result);
+ };
+ reader.onerror = (event: Event): void => {
+ reject('contentError');
+ };
+ });
+ }
+ /** Method to handle tar and tar.gz file for shared YAML file content @public */
+ public async targzFile(packageInfo: PACKAGEINFO): Promise<string | ArrayBuffer> {
+ return new Promise<string | ArrayBuffer>((resolve: Function, reject: Function): void => {
+ const httpOptions: GETAPIURLHEADER = this.getHttpOptions();
+ let apiUrl: string = '';
+ apiUrl = packageInfo.packageType === 'nsd' ? environment.NSDESCRIPTORS_URL + '/' + packageInfo.id + '/nsd_content' :
+ environment.VNFPACKAGES_URL + '/' + packageInfo.id + '/package_content';
+ this.restService.getResource(apiUrl, httpOptions).subscribe((response: ArrayBuffer) => {
+ try {
+ // tslint:disable-next-line: no-any
+ const tar: any = new Tar();
+ const originalInput: Uint8Array = pako.inflate(response, { to: 'Uint8Array' });
+ untar(originalInput.buffer).then((extractedFiles: TARSETTINGS[]) => {
+ const getFoldersFiles: {}[] = extractedFiles;
+ const folderNameStr: string = extractedFiles[0].name;
+ getFoldersFiles.forEach((value: TARSETTINGS) => {
+ const getRootFolder: string[] = value.name.split('/');
+ if (value.name.startsWith(folderNameStr) &&
+ (value.name.endsWith('.yaml') || value.name.endsWith('.yml')) &&
+ getRootFolder.length === this.directoryCount) {
+ tar.append(value.name, packageInfo.descriptor, { type: value.type });
+ } else {
+ if (value.type !== 'L') {
+ tar.append(value.name, new Uint8Array(value.buffer), { type: value.type });
+ }
+ }
+ });
+ const out: Uint8Array = tar.out;
+ const originalOutput: Uint8Array = pako.gzip(out);
+ resolve(originalOutput.buffer);
+ }, (err: string) => {
+ reject('');
+ });
+ } catch (e) {
+ reject('');
+ }
+ }, (error: HttpErrorResponse) => {
+ if (error.status === HttpStatus.NOT_FOUND || error.status === HttpStatus.UNAUTHORIZED) {
+ this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
+ } else {
+ this.restService.handleError(error, 'get');
+ reject('');
+ }
+ });
+ });
+ }
+ /** Method to check given string is JSON or not @public */
+ public checkJson(jsonString: string): boolean {
+ jsonString = jsonString.replace(/'/g, '"');
+ try {
+ JSON.parse(jsonString);
+ } catch (e) {
+ return false;
+ }
+ return true;
+ }
+ /** Clean the form before submit @public */
+ public cleanForm(formGroup: FormGroup): void {
+ Object.keys(formGroup.controls).forEach((key: string) => {
+ if ((!isNullOrUndefined((formGroup.get(key) as FormArray | FormGroup).controls)) && key !== 'vimconfig') {
+ // tslint:disable-next-line: no-shadowed-variable
+ for (const { item, index } of (formGroup.get(key).value).map((item: {}, index: number) => ({ item, index }))) {
+ const newFormGroup: FormGroup = (formGroup.get(key) as FormArray).controls[index] as FormGroup;
+ this.cleanForm(newFormGroup);
+ }
+ } else if (formGroup.get(key).value !== undefined && formGroup.get(key).value !== null && key !== 'vimconfig') {
+ if (!Array.isArray(formGroup.get(key).value)) {
+ if (typeof formGroup.get(key).value === 'string') {
+ formGroup.get(key).setValue(formGroup.get(key).value.trim());
+ }
+ }
+ } else if (key === 'vimconfig') {
+ const newFormGroup: FormGroup = formGroup.get(key) as FormGroup;
+ this.cleanForm(newFormGroup);
+ }
+ });
+ }
+ /** Method to return the config of pager value for ngSmarttable @public */
+ public paginationPagerConfig(): PAGERSMARTTABLE {
+ return {
+ display: true,
+ perPage: environment.paginationNumber
+ };
+ }
+ /** Method to return the class for the table for ngSmarttable @public */
+ public tableClassConfig(): SMARTTABLECLASS {
+ return {
+ class: 'table list-data'
+ };
+ }
+ /** Method to return all languages name and its code @public */
+ public languageCodeList(): {}[] {
+ return [
+ { code: 'en', language: 'English' },
+ { code: 'es', language: 'Spanish' },
+ { code: 'pt', language: 'Portuguese' },
+ { code: 'de', language: 'German' }
+ ];
+ }
+ /** Fetch OSM Version @public */
+ public fetchOSMVersion(): void {
+ this.restService.getResource(environment.OSM_VERSION_URL).subscribe((res: { version: string }) => {
+ const version: string[] = res.version.split('+');
+ if (!isNullOrUndefined(version[0])) {
+ this.osmVersion = version[0];
+ } else {
+ this.osmVersion = null;
+ }
+ }, (error: ERRORDATA) => {
+ this.osmVersion = null;
+ this.restService.handleError(error, 'get');
+ });
+ }
+ /** Method to validate file extension and size @private */
+ private vaildataFileInfo(fileInfo: File, fileType: string): boolean {
+ const extension: string = fileInfo.name.substring(fileInfo.name.lastIndexOf('.') + 1);
+ const packageSize: number = CONSTANTNUMBER.oneMB * environment.packageSize;
+ if (extension.toLowerCase() === fileType && fileInfo.size <= packageSize) {
+ return true;
+ }
+ return false;
+ }
+ /** Method to read file content based on type @private */
+ private readFileContent(reader: FileReader, fileInfo: File, fileType: string): void {
+ if (fileType === 'gz') {
+ reader.readAsArrayBuffer(fileInfo);
+ } else {
+ reader.readAsText(fileInfo);
+ }
+ }
+ /** Method to handle http options @public */
+ private getHttpOptions(): GETAPIURLHEADER {
+ return {
+ headers: new HttpHeaders({
+ Accept: 'application/gzip, application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ }),
+ responseType: 'arraybuffer'
+ };
+ }
+}