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/app/AppComponent.html b/src/app/AppComponent.html
new file mode 100644
index 0000000..bee03c8
--- /dev/null
+++ b/src/app/AppComponent.html
@@ -0,0 +1,19 @@
+<!--
+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)
+-->
+<router-outlet> </router-outlet>
+<notifier-container></notifier-container>
\ No newline at end of file
diff --git a/src/app/AppComponent.scss b/src/app/AppComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/AppComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/AppComponent.ts b/src/app/AppComponent.ts
new file mode 100644
index 0000000..04ad8d8
--- /dev/null
+++ b/src/app/AppComponent.ts
@@ -0,0 +1,123 @@
+/*
+ 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 App Components
+ */
+import { Component, HostListener, Injector } from '@angular/core';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { DEFAULT_INTERRUPTSOURCES, Idle } from '@ng-idle/core';
+import { AuthenticationService } from 'AuthenticationService';
+import { DeviceCheckService } from 'DeviceCheckService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+
+/**
+ * Creating component
+ * @Component takes AppComponent.html as template url
+ */
+@Component({
+ selector: 'app-root',
+ templateUrl: './AppComponent.html',
+ styleUrls: ['./AppComponent.scss']
+})
+/** Exporting a class @exports AppComponent */
+export class AppComponent {
+ /** To inject services @public */
+ public injector: Injector;
+ /** Instance for modal service @public */
+ public modalService: NgbModal;
+ /** Device Check service @private */
+ private deviceCheckService: DeviceCheckService;
+ /** Utilizes auth service for any auth operations @private */
+ private authService: AuthenticationService;
+ /** Handle idle time out service @private */
+ private idle: Idle;
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.idle = this.injector.get(Idle);
+ this.authService = this.injector.get(AuthenticationService);
+ this.modalService = this.injector.get(NgbModal);
+ this.deviceCheckService = this.injector.get(DeviceCheckService);
+ this.handleIdle();
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.sharedService.fetchOSMVersion();
+ }
+
+ /** To handle handleIdle @public */
+ public handleIdle(): void {
+ const idleTime: number = 1200;
+ const idleTimeOutWarning: number = 5;
+ // sets an idle timeout in seconds.
+ this.idle.setIdle(idleTime);
+ //sets a timeout period in seconds. after idleTime seconds of inactivity, the user will be considered timed out.
+ this.idle.setTimeout(idleTimeOutWarning);
+ // sets the default interrupts, in this case, things like clicks, scrolls, touches to the document
+ this.idle.setInterrupts(DEFAULT_INTERRUPTSOURCES);
+ this.idle.watch(true);
+ this.idleTimeOut();
+ }
+
+ /** Method to capture idle time out event @public */
+ public idleTimeOut(): void {
+ this.idle.onTimeout.subscribe(() => {
+ this.idle.stop();
+ if (localStorage.getItem('id_token') !== null) {
+ this.authService.logout();
+ }
+ });
+ }
+
+ /** Handling Window's Storage Hostlistener @public */
+ @HostListener('window:storage', ['$event'])
+ public handleLocalStorageEvent(evt: StorageEvent): void {
+ // On Token Change
+ if (evt.key === 'token_state' && !isNullOrUndefined(evt.key)) {
+ if (evt.oldValue !== evt.newValue) {
+ window.location.reload();
+ }
+ }
+ // On Langauges Change
+ if (evt.key === 'languageCode' && !isNullOrUndefined(evt.key)) {
+ if (evt.oldValue !== evt.newValue) {
+ window.location.reload();
+ }
+ }
+ }
+
+ /** Handling Window's POP State Hostlistener @public */
+ @HostListener('window:popstate', ['$event'])
+ public handleOnPOPState(evt: PopStateEvent): void {
+ this.modalService.dismissAll();
+ }
+
+ /** Handling Window's orientationchange Hostlistener @public */
+ @HostListener('window:resize', ['$event'])
+ public onResize(event: Event): void {
+ this.deviceCheckService.checkDeviceType();
+ }
+}
diff --git a/src/app/AppModule.ts b/src/app/AppModule.ts
new file mode 100644
index 0000000..02fa736
--- /dev/null
+++ b/src/app/AppModule.ts
@@ -0,0 +1,231 @@
+/*
+ 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 Instance Module file
+ */
+import { CommonModule, LOCATION_INITIALIZED } from '@angular/common';
+import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http';
+import { APP_INITIALIZER, Injector, NgModule } from '@angular/core';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { BrowserModule } from '@angular/platform-browser';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { RouterModule } from '@angular/router';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
+import { TranslateHttpLoader } from '@ngx-translate/http-loader';
+import { NotifierModule, NotifierOptions } from 'angular-notifier';
+import { AuthInterceptorService } from 'AuthInterceptorService';
+import { HeaderComponent } from 'HeaderComponent';
+import { LayoutComponent } from 'LayoutComponent';
+import { Ng2SmartTableModule } from 'ng2-smart-table';
+import { RestService } from 'RestService';
+import { SidebarComponent } from 'SidebarComponent';
+import { AppComponent } from './AppComponent';
+
+import { appRoutes } from './approutes.module';
+
+import { DataService } from 'DataService';
+import { ProjectService } from 'ProjectService';
+import { SharedService } from 'SharedService';
+
+import { CodemirrorModule } from '@ctrl/ngx-codemirror';
+import { NgSelectModule } from '@ng-select/ng-select';
+
+import { NgIdleKeepaliveModule } from '@ng-idle/keepalive';
+import { AuthenticationService } from 'AuthenticationService';
+import { AuthGuardService } from 'AuthGuardService';
+import { BreadcrumbComponent } from 'BreadCrumb';
+import { ComposePackages } from 'ComposePackages';
+import { ConfirmationTopologyComponent } from 'ConfirmationTopology';
+import { DeleteComponent } from 'DeleteComponent';
+import { DeviceCheckService } from 'DeviceCheckService';
+import { GoToTopDirective } from 'GoToTopDirective';
+import { InstantiateNetSliceTemplateComponent } from 'InstantiateNetSliceTemplate';
+import { InstantiateNsComponent } from 'InstantiateNs';
+import { LoaderModule } from 'LoaderModule';
+import { LoginComponent } from 'LoginComponent';
+import { NetsliceInstancesActionComponent } from 'NetsliceInstancesActionComponent';
+import { NetslicePackagesActionComponent } from 'NetslicePackagesAction';
+import { NSInstancesActionComponent } from 'NSInstancesActionComponent';
+import { NsPackagesActionComponent } from 'NsPackagesAction';
+import { PageNotFoundComponent } from 'PageNotFound';
+import { PDUInstancesActionComponent } from 'PDUInstancesActionComponent';
+import { ProjectLinkComponent } from 'ProjectLinkComponent';
+import { ProjectsActionComponent } from 'ProjectsAction';
+import { SDNControllerActionComponent } from 'SDNControllerActionComponent';
+import { ShowInfoComponent } from 'ShowInfoComponent';
+import { SwitchProjectComponent } from 'SwitchProjectComponent';
+import { UsersActionComponent } from 'UsersActionComponent';
+import { UserSettingsComponent } from 'UserSettingsComponent';
+import { VimAccountsActionComponent } from 'VimAccountsAction';
+import { VNFInstancesActionComponent } from 'VNFInstancesActionComponent';
+import { VNFLinkComponent } from 'VNFLinkComponent';
+import { VNFPackagesActionComponent } from 'VNFPackagesAction';
+import { WIMAccountsActionComponent } from 'WIMAccountsAction';
+
+/**
+ * Custom angular notifier options
+ */
+const customNotifierOptions: NotifierOptions = {
+ position: { horizontal: { position: 'right' }, vertical: { position: 'top' } },
+ behaviour: { autoHide: 3000, onClick: 'hide', onMouseover: 'pauseAutoHide' }
+};
+
+/**
+ * An NgModule is a class adorned with the @NgModule decorator function.
+ * @NgModule takes a metadata object that tells Angular how to compile and run module code.
+ */
+@NgModule({
+ declarations: [
+ AppComponent,
+ LayoutComponent,
+ HeaderComponent,
+ SidebarComponent,
+ LoginComponent,
+ PageNotFoundComponent,
+ VNFPackagesActionComponent,
+ NsPackagesActionComponent,
+ NSInstancesActionComponent,
+ VNFInstancesActionComponent,
+ VNFLinkComponent,
+ NetsliceInstancesActionComponent,
+ BreadcrumbComponent,
+ DeleteComponent,
+ NetslicePackagesActionComponent,
+ UsersActionComponent,
+ VimAccountsActionComponent,
+ ProjectsActionComponent,
+ ProjectLinkComponent,
+ UserSettingsComponent,
+ ShowInfoComponent,
+ InstantiateNetSliceTemplateComponent,
+ InstantiateNsComponent,
+ ConfirmationTopologyComponent,
+ ComposePackages,
+ WIMAccountsActionComponent,
+ PDUInstancesActionComponent,
+ SDNControllerActionComponent,
+ SwitchProjectComponent,
+ GoToTopDirective
+ ],
+ imports: [
+ NotifierModule.withConfig(customNotifierOptions),
+ CommonModule,
+ BrowserModule,
+ BrowserAnimationsModule,
+ FormsModule,
+ ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }),
+ Ng2SmartTableModule,
+ CodemirrorModule,
+ NgSelectModule,
+ HttpClientModule,
+ TranslateModule.forRoot({
+ loader: {
+ provide: TranslateLoader,
+ useFactory: HttpLoaderFactory,
+ deps: [HttpClient]
+ }
+ }),
+ NgbModule,
+ NgSelectModule,
+ RouterModule.forRoot(appRoutes, { useHash: false }),
+ NgIdleKeepaliveModule.forRoot(),
+ LoaderModule
+ ],
+ providers: [
+ {
+ provide: APP_INITIALIZER,
+ useFactory: appInitializerFactory,
+ deps: [TranslateService, Injector],
+ multi: true
+ },
+ {
+ provide: HTTP_INTERCEPTORS,
+ useClass: AuthInterceptorService,
+ multi: true
+ },
+ RestService,
+ AuthenticationService,
+ AuthGuardService,
+ DataService,
+ ProjectService,
+ SharedService,
+ DeviceCheckService
+ ],
+ bootstrap: [AppComponent],
+ entryComponents: [
+ VNFPackagesActionComponent,
+ NsPackagesActionComponent,
+ NSInstancesActionComponent,
+ VNFInstancesActionComponent,
+ VNFLinkComponent,
+ NetsliceInstancesActionComponent,
+ BreadcrumbComponent,
+ DeleteComponent,
+ NetslicePackagesActionComponent,
+ UsersActionComponent,
+ VimAccountsActionComponent,
+ ProjectsActionComponent,
+ ProjectLinkComponent,
+ UserSettingsComponent,
+ ShowInfoComponent,
+ InstantiateNetSliceTemplateComponent,
+ InstantiateNsComponent,
+ ConfirmationTopologyComponent,
+ ComposePackages,
+ WIMAccountsActionComponent,
+ PDUInstancesActionComponent,
+ SDNControllerActionComponent,
+ SwitchProjectComponent
+ ]
+})
+
+/** Exporting a class @exports AppModule */
+export class AppModule {
+ /** Variables declared to avoid state-less class */
+ private appModule: string;
+}
+
+/**
+ * HttpLoaderFactory is for translate service of the application.
+ */
+// tslint:disable:function-name
+export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
+ const now: number = new Date().getTime();
+ return new TranslateHttpLoader(http, './assets/i18n/', '.json?locale=' + now);
+}
+/**
+ * HttpLoaderFactory is for translate service of the application.
+ */
+// tslint:disable:function-name
+export function appInitializerFactory(translate: TranslateService, injector: Injector): Object {
+ // tslint:disable-next-line: no-any
+ return async (): Promise<any> => {
+ await injector.get(LOCATION_INITIALIZED, Promise.resolve(null));
+ translate.setDefaultLang('en');
+ const languageCode: string = localStorage.getItem('languageCode');
+ if (languageCode !== null && languageCode !== undefined && languageCode !== '') {
+ await translate.use(languageCode).toPromise().catch(() => {
+ translate.setDefaultLang('en');
+ });
+ } else {
+ await translate.use('en').toPromise();
+ localStorage.setItem('languageCode', 'en');
+ }
+ };
+}
diff --git a/src/app/approutes.module.ts b/src/app/approutes.module.ts
new file mode 100644
index 0000000..e2f863c
--- /dev/null
+++ b/src/app/approutes.module.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 Routing Module
+ */
+import { Routes } from '@angular/router';
+import { AuthGuardService } from 'AuthGuardService';
+import { LayoutComponent } from 'LayoutComponent';
+import { LoginComponent } from 'LoginComponent';
+import { PageNotFoundComponent } from 'PageNotFound';
+
+/** Exporting a function using Routes @exports AppRoutes */
+export const appRoutes: Routes = [
+ {
+ path: 'login',
+ component: LoginComponent
+ },
+ {
+ path: '',
+ component: LayoutComponent,
+ canActivate: [AuthGuardService],
+ children: [
+ {
+ path: '',
+ // tslint:disable-next-line: no-any
+ loadChildren: async (): Promise<any> => import('./dashboard/DashboardModule')
+ .then((m: typeof import('./dashboard/DashboardModule')) => m.DashboardModule),
+ canActivate: [AuthGuardService]
+ },
+ {
+ path: 'packages',
+ // tslint:disable-next-line: no-any
+ loadChildren: async (): Promise<any> => import('./packages/PackagesModule')
+ .then((m: typeof import('./packages/PackagesModule')) => m.PackagesModule),
+ canActivate: [AuthGuardService]
+ },
+ {
+ path: 'instances',
+ // tslint:disable-next-line: no-any
+ loadChildren: async (): Promise<any> => import('./instances/InstancesModule')
+ .then((m: typeof import('./instances/InstancesModule')) => m.InstancesModule),
+ canActivate: [AuthGuardService]
+ },
+ {
+ path: 'vim',
+ // tslint:disable-next-line: no-any
+ loadChildren: async (): Promise<any> => import('./vim-accounts/VimAccountsModule')
+ .then((m: typeof import('./vim-accounts/VimAccountsModule')) => m.VimAccountsModule),
+ canActivate: [AuthGuardService]
+ },
+ {
+ path: 'wim',
+ // tslint:disable-next-line: no-any
+ loadChildren: async (): Promise<any> => import('./wim-accounts/WIMAccountsModule')
+ .then((m: typeof import('./wim-accounts/WIMAccountsModule')) => m.WIMAccountsModule),
+ canActivate: [AuthGuardService]
+ },
+ {
+ path: 'sdn',
+ // tslint:disable-next-line: no-any
+ loadChildren: async (): Promise<any> => import('./sdn-controller/SDNControllerModule')
+ .then((m: typeof import('./sdn-controller/SDNControllerModule')) => m.SDNControllerModule),
+ canActivate: [AuthGuardService]
+ },
+ {
+ path: 'users',
+ // tslint:disable-next-line: no-any
+ loadChildren: async (): Promise<any> => import('./users/UsersModule')
+ .then((m: typeof import('./users/UsersModule')) => m.UsersModule),
+ canActivate: [AuthGuardService]
+ },
+ {
+ path: 'projects',
+ // tslint:disable-next-line: no-any
+ loadChildren: async (): Promise<any> => import('./projects/ProjectsModule')
+ .then((m: typeof import('./projects/ProjectsModule')) => m.ProjectsModule),
+ canActivate: [AuthGuardService]
+ },
+ {
+ path: 'roles',
+ // tslint:disable-next-line: no-any
+ loadChildren: async (): Promise<any> => import('./roles/RolesModule')
+ .then((m: typeof import('./roles/RolesModule')) => m.RolesModule),
+ canActivate: [AuthGuardService]
+ },
+ {
+ path: 'k8s',
+ // tslint:disable-next-line: no-any
+ loadChildren: async (): Promise<any> => import('./k8s/K8sModule')
+ .then((m: typeof import('./k8s/K8sModule')) => m.K8sModule),
+ canActivate: [AuthGuardService]
+ }
+ ]
+ },
+ {
+ path: '**',
+ component: PageNotFoundComponent
+ }
+];
diff --git a/src/app/dashboard/DashboardComponent.html b/src/app/dashboard/DashboardComponent.html
new file mode 100644
index 0000000..5b09dc9
--- /dev/null
+++ b/src/app/dashboard/DashboardComponent.html
@@ -0,0 +1,167 @@
+<!--
+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)
+-->
+<div class="row dashboard">
+ <div class="col-xs-9 col-sm-12 col-md-12 col-lg-9 col-xl-9 p-0">
+ <div class="row instances">
+ <div class="col-xs-8 col-sm-8 col-md-12 col-lg-8 col-xl-8">
+ <div class="custom-card card mb-3 text-center graph-section">
+ <div class="card-title">
+ <h6 class="font-weight-bold" *ngIf="nsRunningInstance.length">
+ {{'PAGE.DASHBOARD.UPTIME' | translate}} {{'PAGE.DASHBOARD.RUNNINGINSTANCES' | translate}}
+ </h6>
+ <h6 *ngIf="!nsRunningInstance.length">
+ <span> {{'PAGE.DASHBOARD.NOINSTANCES' | translate}}</span>
+ </h6>
+ <div [ngClass]="{'show-canvas':nsRunningInstance.length}" class="instances-canvas">
+ <canvas id="canvas"></canvas>
+ </div>
+ </div>
+ </div>
+ <app-loader [waitingMessage]="message" *ngIf="isCanvasLoadingResults"></app-loader>
+ </div>
+ <div class="col-xs-4 col-sm-4 col-md-12 col-lg-4 col-xl-4">
+ <div class="custom-card card card mb-3">
+ <div class="card-header custom-card-header">
+ {{'PAGE.DASHBOARD.FAILEDINSTANCES' | translate}}
+ </div>
+ <div class="card-body list-overflow failed-instances">
+ <ul class="list-group">
+ <li class="list-group-item text-left" *ngIf="!nsFailedInstances.length">
+ {{'PAGE.DASHBOARD.NOINSTANCES' | translate}}</li>
+ <li class="list-group-item text-left d-flex justify-content-between align-items-center"
+ *ngFor="let nsFailedInstance of nsFailedInstances">
+ <span class="text-truncate">{{nsFailedInstance.name}}</span>
+ <ng-template #popTitle>
+ <strong>{{nsFailedInstance.name}}</strong>
+ <button class="button-xs close" type="button" (click)="p.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </ng-template>
+ <span class="badge badge-pill">
+ <i placement="left" container="body"
+ ngbPopover="{{'DETAILEDSTATUS' | translate}}: {{nsFailedInstance['detailed-status']}}"
+ triggers="manual" #p="ngbPopover" (click)="p.open()" [autoClose]="'outside'"
+ [popoverTitle]="popTitle"
+ class="fas fa-exclamation-circle text-danger float-left"></i></span>
+ </li>
+ </ul>
+ </div>
+ </div>
+ <app-loader [waitingMessage]="message" *ngIf="isCanvasLoadingResults"></app-loader>
+ </div>
+ </div>
+ <div class="row module-counts">
+ <div class="col-xs-4 col-sm-4 col-md-4 col-lg-4 col-xl-4 p-0">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 col-xl-12 mb-3">
+ <div class="status-card custom-card card-hover card aqua-card" routerLink="/packages/ns">
+ <i class="fas fa-box-open"></i>
+ <h3>{{ (nsdPackageCount)?nsdPackageCount:0 }}</h3>
+ <h6>{{'NSPACKAGES' | translate}}</h6>
+ <span class="link-icon">
+ <i class="fa fa-link" aria-hidden="true"></i>
+ </span>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 col-xl-12 mb-3">
+ <div class="status-card custom-card card-hover card aqua-card" routerLink="/instances/ns">
+ <i class="fas fa-sitemap"></i>
+ <h3>{{ (nsInstanceCount)?nsInstanceCount:0 }}</h3>
+ <h6>{{'NSINSTANCES' | translate}}</h6>
+ <span class="link-icon">
+ <i class="fa fa-link" aria-hidden="true"></i>
+ </span>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-4 col-sm-4 col-md-4 col-lg-4 col-xl-4 p-0">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 col-xl-12 mb-3">
+ <div class="status-card custom-card card-hover card purple-card" routerLink="/packages/vnf">
+ <i class="fas fa-box-open"></i>
+ <h3>{{ (vnfdPackageCount)?vnfdPackageCount:0 }}</h3>
+ <h6>{{'VNFPACKAGES' | translate}}</h6>
+ <span class="link-icon">
+ <i class="fa fa-link" aria-hidden="true"></i>
+ </span>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 col-xl-12 mb-3">
+ <div class="status-card custom-card card-hover card purple-card" routerLink="/instances/vnf">
+ <i class="fas fa-hdd"></i>
+ <h3>{{ (vnfInstanceCount)?vnfInstanceCount:0 }}</h3>
+ <h6>{{'VNFINSTANCES' | translate}}</h6>
+ <span class="link-icon">
+ <i class="fa fa-link" aria-hidden="true"></i>
+ </span>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-4 col-sm-4 col-md-4 col-lg-4 col-xl-4 p-0">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 col-xl-12 mb-3">
+ <div class="status-card custom-card card-hover card pink-card" routerLink="/vim/details">
+ <i class="fas fa-server"></i>
+ <h3>{{ (vimAccountCount)?vimAccountCount:0 }}</h3>
+ <h6>{{'VIMACCOUNTS' | translate}}</h6>
+ <span class="link-icon">
+ <i class="fa fa-link" aria-hidden="true"></i>
+ </span>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 col-xl-12 mb-3">
+ <div class="status-card custom-card card-hover card pink-card" routerLink="/sdn/details">
+ <i class="fas fa-globe"></i>
+ <h3>{{ (sdnControllerCount)?sdnControllerCount:0 }}</h3>
+ <h6>{{'SDNCONTROLLER' | translate}}</h6>
+ <span class="link-icon">
+ <i class="fa fa-link" aria-hidden="true"></i>
+ </span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-3 col-sm-12 col-md-12 col-lg-3 col-xl-3 p-0">
+ <div class="custom-card">
+ <div class="card-header p-0 custom-card-header">
+ <a routerLink="/projects">
+ {{'ALL' | translate}} {{'PAGE.DASHBOARD.PROJECTS' | translate}}
+ <span
+ class="badge badge-primary badge-pill bg-white text-body font-weight-bold">{{allProjectList.length}}</span>
+ </a>
+ </div>
+ <div class="card-body list-overflow project-list">
+ <ul class="list-group">
+ <li class="list-group-item text-left d-flex justify-content-between align-items-center selectProject"
+ *ngFor='let list of allProjectList'>
+ {{list.projectName}}
+ <span class="badge badge-pill" *ngFor='let listcheck of projectList' placement="top"
+ container="body"
+ ngbTooltip="{{ (list.projectName === (selectedProject | async) ? 'CURRENTPROJECT' : 'SWITCHPROJECT') | translate}}">
+ <i *ngIf="list.projectName === (selectedProject | async) && listcheck.project === list.project"
+ [ngClass]="list.projectName === (selectedProject | async) ? 'activeProjectLink' : ''"
+ class="fas fa-check-circle text-success"></i>
+ <i *ngIf="list.projectName !== (selectedProject | async) && listcheck.project === list.project"
+ (click)="this.projectService.switchProjectModal(list)"
+ class="fas fa-exchange-alt text-danger selectProjectLink"></i>
+ </span>
+ </li>
+ </ul>
+ </div>
+ <app-loader [waitingMessage]="message" *ngIf="isProjectsLoadingResults"></app-loader>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/src/app/dashboard/DashboardComponent.scss b/src/app/dashboard/DashboardComponent.scss
new file mode 100644
index 0000000..df3c6b1
--- /dev/null
+++ b/src/app/dashboard/DashboardComponent.scss
@@ -0,0 +1,160 @@
+/*
+ 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 "../../assets/scss/mixins/mixin";
+ @import "../../assets/scss/variable";
+ $min-height-set: 200px;
+ .dashboard {
+ .custom-card {
+ word-wrap: break-word;
+ @include box-shadow(0px, 1px, 15px, 0px, rgba(69, 90, 100, 0.1));
+ @include transition(all, 0.2s, null, null);
+ @include roundedCorners(5);
+ @include border(all, 1, solid, rgba(238, 238, 238, 0.75));
+ color: $white;
+ &.card-hover {
+ @include transition(all, 0.25s, ease, null);
+ &:hover {
+ -moz-transform: translateY(-4px) scale(1.01);
+ -ms-transform: translateY(-4px) scale(1.01);
+ -o-transform: translateY(-4px) scale(1.01);
+ transform: translateY(-4px) scale(1.01);
+ }
+ }
+ &.pink-card {
+ @include background(
+ linear-gradient(to left top, #d81b60, #e0306d, #e7407a, #ee4f87, #f55c94),
+ null,
+ null,
+ null,
+ null
+ );
+ }
+ &.purple-card {
+ @include background(
+ linear-gradient(to left top, #605ca8, #736ebb, #8681ce, #9994e2, #aca7f6),
+ null,
+ null,
+ null,
+ null
+ );
+ }
+ &.aqua-card {
+ @include background(
+ linear-gradient(to left top, #00c0ef, #00cdf5, #00dafa, #00e6fd, #0af3ff),
+ null,
+ null,
+ null,
+ null
+ );
+ }
+ .custom-card-header {
+ @include background(null, $primary, null, null, null);
+ @include roundedTop(5);
+ a {
+ color: $white;
+ @include flexbox(flex, space-between, null, null, center, null);
+ @include padding-value(12, 20, 12, 20);
+ }
+ }
+ .card-body {
+ @include padding-value(5, 5, 5, 10);
+ &.list-overflow {
+ overflow-y: scroll;
+ .list-group {
+ .list-group-item {
+ cursor: default;
+ @include border(all, 0, solid, $black-coral);
+ @include border(bottom, 1, solid, rgba(0, 0, 0, 0.125));
+ @include padding-value(10, 0, 10, 0);
+ @include margin-value(0, 0, 0, 0);
+ color: $gray-600;
+ i {
+ cursor: pointer;
+ @include font(null, 14px, null);
+ &.activeProjectLink {
+ cursor: default;
+ }
+ }
+ &:last-child {
+ @include border(bottom, 0, solid, rgba(0, 0, 0, 0.125));
+ }
+ }
+ }
+ &.failed-instances {
+ max-height: $min-height-set;
+ }
+ &.project-list {
+ max-height: 65vh;
+ }
+ }
+ }
+ }
+ .instances {
+ .graph-section {
+ min-height: $min-height-set;
+ @include flexbox(null, center, null, null, null, null);
+ @include padding-value(10, 10, 10, 10);
+ .card-title {
+ color: $gray-600;
+ }
+ .instances-canvas {
+ @include flexbox(none !important, null, null, null, null, null);
+ &.show-canvas {
+ @include flexbox(block !important, null, null, null, null, null);
+ }
+ #canvas{
+ @include wh-value(100%, $min-height-set);
+ }
+ }
+ }
+ }
+ .module-counts {
+ .status-card {
+ overflow: hidden;
+ @include wh-value(null, 130px);
+ @include roundedCorners(4);
+ @include box-shadow(0px, 5px, 20px, 2px, $transparent-dark-bg);
+ cursor: pointer;
+ @include flexbox(null, null, null, null, center, null);
+ @include padding-value(20, 20, 20, 20);
+ @include border(all, 0, solid, $gray-80);
+ i {
+ @include font(null, 2rem, null);
+ @include margin-value(0, 0, 8, 0);
+ }
+ h6 {
+ @include font(null, 0.8rem, null);
+ }
+ &:hover .link-icon {
+ @include position_value(null, null, -60px, null, null);
+ }
+ .link-icon {
+ @include background(null, rgba(255, 255, 255, 0.5), null, null, null);
+ @include position_value(absolute, 0px, -130px, null, null);
+ @include wh-value(130px, 130px);
+ @include font(null, 30px, null);
+ @include padding-value(40, 20, 40, 20);
+ @include roundedCornersPercentage(50%);
+ @include transition(all, 0.3s, ease-in-out, null);
+ i {
+ @include font(null, 1.875rem, null);
+ }
+ }
+ }
+ }
+ }
\ No newline at end of file
diff --git a/src/app/dashboard/DashboardComponent.ts b/src/app/dashboard/DashboardComponent.ts
new file mode 100644
index 0000000..4ab802f
--- /dev/null
+++ b/src/app/dashboard/DashboardComponent.ts
@@ -0,0 +1,402 @@
+/*
+ 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 Dashboard Component
+ */
+import { Component, Injector, OnInit } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { AuthenticationService } from 'AuthenticationService';
+import { Chart } from 'chart.js';
+import { ERRORDATA } from 'CommonModel';
+import { environment } from 'environment';
+import { NSDDetails } from 'NSDModel';
+import { NSInstanceDetails } from 'NSInstanceModel';
+import { ProjectData, ProjectDetails } from 'ProjectModel';
+import { ProjectService } from 'ProjectService';
+import { RestService } from 'RestService';
+import { Observable, Subscription } from 'rxjs';
+import { SDNControllerModel } from 'SDNControllerModel';
+import { SharedService } from 'SharedService';
+import { ProjectRoleMappings, UserDetail } from 'UserModel';
+import { VimAccountDetails } from 'VimAccountModel';
+import { VNFDDetails } from 'VNFDModel';
+import { VNFInstanceDetails } from 'VNFInstanceModel';
+
+/**
+ * Creating component
+ * @Component takes DashboardComponent.html as template url
+ */
+@Component({
+ styleUrls: ['./DashboardComponent.scss'],
+ templateUrl: './DashboardComponent.html'
+})
+
+/**
+ * This file created during the angular project creation
+ */
+
+/** Exporting a class @exports DashboardComponent */
+export class DashboardComponent implements OnInit {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Observable holds logined value @public */
+ public username$: Observable<string>;
+
+ /** Variables holds admin is logged or not @public */
+ public isAdmin: boolean;
+
+ /** List of NS failed Instances @public */
+ public nsFailedInstances: {}[] = [];
+
+ /** Setting up count for vnfdPackages @public */
+ public vnfdPackageCount: number;
+
+ /** Setting up count for nsdPackage @public */
+ public nsdPackageCount: number;
+
+ /** Setting up count for nsInstance @public */
+ public nsInstanceCount: number;
+
+ /** Setting up count for vnfInstance @public */
+ public vnfInstanceCount: number;
+
+ /** Setting up count for vimAccount @public */
+ public vimAccountCount: number;
+
+ /** Setting up count for sdnController @public */
+ public sdnControllerCount: number;
+
+ /** Variables holds current project details @public */
+ public currentProjectDetails: {};
+
+ /** Array holds all the projects @public */
+ public projectList: {}[] = [];
+
+ /** Array holds all the projects @public */
+ public allProjectList: {}[] = [];
+
+ /** Variables holds the selected project @public */
+ public selectedProject: Observable<string>;
+
+ /** Check the Instances loading results @public */
+ public isCanvasLoadingResults: boolean = true;
+
+ /** Check the Projects loading results @public */
+ public isProjectsLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** List of NS Success Instances @private */
+ public nsRunningInstance: string[] = [];
+
+ /** Utilizes rest service for any CRUD operations @private */
+ private restService: RestService;
+
+ /** Utilizes auth service for any auth operations @private */
+ private authService: AuthenticationService;
+
+ /** Used to subscribe vnfdPackage @private */
+ private vnfdPackageCountSub: Subscription;
+
+ /** Used to subscribe nsdPackage @private */
+ private nsdPackageCountSub: Subscription;
+
+ /** Used to subscribe nsInstance @private */
+ private nsInstanceCountSub: Subscription;
+
+ /** Used to subscribe vnfInstance @private */
+ private vnfInstanceCountSub: Subscription;
+
+ /** Used to subscribe vimAccount @private */
+ private vimAccountCountSub: Subscription;
+
+ /** Used to subscribe sdnController @private */
+ private sdnControllerCountSub: Subscription;
+
+ /** No of Hours of NS Success Instances @private */
+ private noOfHours: number[] = [];
+
+ /** collects charts objects @private */
+ private charts: object = [];
+
+ /** Contains all methods related to projects @private */
+ private projectService: ProjectService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Contains NS Instance Details */
+ private nsInstancesDataArr: {}[];
+
+ /** Container created time array @private */
+ private createdTimes: string[] = [];
+
+ /** Contains slice limit const @private */
+ private sliceLimit: number = 10;
+
+ /** Contians hour converter @private */
+ private hourConverter: number = 3600;
+
+ /** Contians color code for chart @private */
+ private chartColorPink: string = '#e4397c';
+
+ /** Contians color code for chart @private */
+ private chartColorPurple: string = '#605ca8';
+
+ /** Contians color code for chart @private */
+ private chartColorCyan: string = '#00c0ef';
+
+ /** Contians color code for chart @private */
+ private chartColorBlue: string = '#054C8C';
+
+ /** Contians color code for chart @private */
+ private chartColorYellow: string = '#ffce56';
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.authService = this.injector.get(AuthenticationService);
+ this.projectService = this.injector.get(ProjectService);
+ this.sharedService = this.injector.get(SharedService);
+ this.translateService = this.injector.get(TranslateService);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.username$ = this.authService.username;
+ this.isAdmin = (localStorage.getItem('isAdmin') === 'true') ? true : false;
+ this.selectedProject = this.authService.ProjectName;
+ this.checkAdminPrivilege();
+ this.getUserAccessedProjects();
+ this.getAllProjects();
+ this.getVnfdPackageCount();
+ this.getNsdPackageCount();
+ this.getNsInstanceCount();
+ this.getVnfInstanceCount();
+ this.getVimAccountCount();
+ this.getSDNControllerCount();
+ }
+
+ /** Get all the projects @public */
+ public getUserAccessedProjects(): void {
+ this.projectService.getUserProjects().subscribe((projects: UserDetail) => {
+ const projectList: {}[] = projects.project_role_mappings;
+ this.projectList = projectList.filter(
+ (thing: ProjectRoleMappings, i: number, arr: []) => arr
+ .findIndex((t: ProjectRoleMappings) => t.project_name === thing.project_name) === i
+ );
+ }, (error: Error) => {
+ // TODO: Handle failure
+ });
+ }
+
+ /** Fetching all the Project in dashboard @public */
+ public getAllProjects(): void {
+ this.isProjectsLoadingResults = true;
+ this.restService.getResource(environment.PROJECTS_URL).subscribe((projectsData: ProjectDetails[]) => {
+ this.allProjectList = [];
+ projectsData.forEach((projectData: ProjectDetails) => {
+ const projectDataObj: ProjectData = this.generateProjectData(projectData);
+ this.allProjectList.push(projectDataObj);
+ });
+ this.isProjectsLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isProjectsLoadingResults = false;
+ });
+ }
+
+ /** Generate Projects object from loop and return for the datasource @public */
+ public generateProjectData(projectData: ProjectDetails): ProjectData {
+ return {
+ projectName: projectData.name,
+ modificationDate: this.sharedService.convertEpochTime(projectData._admin.modified),
+ creationDate: this.sharedService.convertEpochTime(projectData._admin.created),
+ id: projectData._id,
+ project: projectData._id
+ };
+ }
+
+ /** Function to check admin privilege @public */
+ public checkAdminPrivilege(): void {
+ if (!this.isAdmin) {
+ this.projectService.getCurrentProjectDetails().subscribe((projectDetails: {}) => {
+ this.currentProjectDetails = projectDetails;
+ }, (error: Error) => {
+ // TODO: Handle failure
+ });
+ }
+ }
+
+ /** Get VNFD Package details @public */
+ public getVnfdPackageCount(): void {
+ this.vnfdPackageCountSub = this.restService.getResource(environment.VNFPACKAGESCONTENT_URL)
+ .subscribe((vnfdPackageData: VNFDDetails[]) => {
+ this.vnfdPackageCount = vnfdPackageData.length;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+
+ /** Get NSD Package details @public */
+ public getNsdPackageCount(): void {
+ this.nsdPackageCountSub = this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL)
+ .subscribe((nsdPackageData: NSDDetails[]) => {
+ this.nsdPackageCount = nsdPackageData.length;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+
+ /** Get NS Instance details @public */
+ public getNsInstanceCount(): void {
+ this.isCanvasLoadingResults = true;
+ this.nsInstanceCountSub = this.restService.getResource(environment.NSDINSTANCES_URL)
+ .subscribe((nsInstancesData: NSInstanceDetails[]) => {
+ this.nsInstancesDataArr = nsInstancesData;
+ this.nsInstanceCount = nsInstancesData.length;
+ this.nsInstanceChart();
+ this.isCanvasLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isCanvasLoadingResults = false;
+ });
+ }
+
+ /** Get NS Instance chart details @public */
+ public nsInstanceChart(): void {
+ this.nsInstancesDataArr.forEach((nsdInstanceData: NSDDetails) => {
+ const operationalStatus: string = nsdInstanceData['operational-status'];
+ const configStatus: string = nsdInstanceData['config-status'];
+ if (operationalStatus === 'failed' || configStatus === 'failed') {
+ this.nsFailedInstances.push(nsdInstanceData);
+ } else if (operationalStatus === 'running' && configStatus === 'configured') {
+ this.nsRunningInstance.push(nsdInstanceData.name);
+ this.createdTimes.push(((nsdInstanceData._admin.created).toString()).slice(0, this.sliceLimit));
+ }
+ });
+ const now: Date = new Date();
+ const currentTime: number = Number((now.getTime().toString().slice(0, this.sliceLimit)));
+ this.createdTimes.forEach((createdTime: string) => {
+ this.noOfHours.push((Math.round((currentTime - Number(createdTime)) / this.hourConverter)));
+ });
+ this.drawNsChart();
+ }
+
+ /** Prepare and sketch NS instance chart */
+ public drawNsChart(): void {
+ this.charts = new Chart('canvas', {
+ type: 'bar',
+ data: {
+ labels: this.nsRunningInstance,
+ datasets: [{
+ data: this.noOfHours,
+ label: this.translateService.instant('NOOFHOURS'),
+ borderColor: [this.chartColorPurple, this.chartColorPink, this.chartColorCyan,
+ this.chartColorBlue, this.chartColorYellow],
+ fill: false,
+ backgroundColor: [this.chartColorPurple, this.chartColorPink, this.chartColorCyan,
+ this.chartColorBlue, this.chartColorYellow]
+ }]
+ },
+ options: {
+ legend: { display: false },
+ scales: {
+ xAxes: [{
+ display: true,
+ ticks: {
+ // tslint:disable-next-line: no-any
+ callback: (label: any, index: number, labels: string): string => {
+ const length: number = 20;
+ const ending: string = '...';
+ if (label.length > length) {
+ return label.substring(0, length - ending.length) + ending;
+ } else {
+ return label;
+ }
+ }
+ },
+ scaleLabel: {
+ display: true,
+ labelString: this.translateService.instant('INSTANCES')
+ }
+ }],
+ yAxes: [{
+ ticks: {
+ beginAtZero: true
+ },
+ display: true,
+ scaleLabel: {
+ display: true,
+ labelString: this.translateService.instant('NOOFHOURS')
+ }
+ }]
+ }
+ }
+ });
+ }
+
+ /** Get VNFD instance details @public */
+ public getVnfInstanceCount(): void {
+ this.vnfInstanceCountSub = this.restService.getResource(environment.NSDINSTANCES_URL)
+ .subscribe((vnfInstanceData: VNFInstanceDetails[]) => {
+ this.vnfInstanceCount = vnfInstanceData.length;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+
+ /** Get VIM account details @public */
+ public getVimAccountCount(): void {
+ this.vimAccountCountSub = this.restService.getResource(environment.VIMACCOUNTS_URL)
+ .subscribe((vimAccountData: VimAccountDetails[]) => {
+ this.vimAccountCount = vimAccountData.length;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+
+ /** Get SDN Controller Count @public */
+ public getSDNControllerCount(): void {
+ this.sdnControllerCountSub = this.restService.getResource(environment.SDNCONTROLLER_URL)
+ .subscribe((sdnControllerData: SDNControllerModel[]) => {
+ this.sdnControllerCount = sdnControllerData.length;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is deleted
+ */
+ public ngOnDestroy(): void {
+ this.vnfdPackageCountSub.unsubscribe();
+ this.nsdPackageCountSub.unsubscribe();
+ this.nsInstanceCountSub.unsubscribe();
+ this.vnfInstanceCountSub.unsubscribe();
+ this.vimAccountCountSub.unsubscribe();
+ this.sdnControllerCountSub.unsubscribe();
+ }
+}
diff --git a/src/app/dashboard/DashboardModule.ts b/src/app/dashboard/DashboardModule.ts
new file mode 100644
index 0000000..4d882e2
--- /dev/null
+++ b/src/app/dashboard/DashboardModule.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 Dashboard Module
+ */
+import { CommonModule } from '@angular/common';
+import { HttpClientModule } from '@angular/common/http';
+import { NgModule } from '@angular/core';
+import { FlexLayoutModule } from '@angular/flex-layout';
+import { FormsModule } from '@angular/forms';
+import { RouterModule, Routes } from '@angular/router';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateModule } from '@ngx-translate/core';
+import { DashboardComponent } from 'DashboardComponent';
+import { LoaderModule } from 'LoaderModule';
+import { ChartsModule } from 'ng2-charts';
+
+/** To halndle project information */
+const projectInfo: {} = { title: '{project}', url: '/' };
+
+/** const values for dashboard Routes */
+const routes: Routes = [
+ {
+ path: '',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo]
+ },
+ component: DashboardComponent
+ }
+];
+/**
+ * An NgModule is a class adorned with the @NgModule decorator function.
+ * @NgModule takes a metadata object that tells Angular how to compile and run module code.
+ */
+@NgModule({
+ imports: [FormsModule, CommonModule, HttpClientModule, FlexLayoutModule, TranslateModule,
+ ChartsModule, RouterModule.forChild(routes), NgbModule, LoaderModule],
+ declarations: [DashboardComponent]
+})
+/** Exporting a class @exports DashboardModule */
+export class DashboardModule {
+ /** Variables declared to avoid state-less class */
+ private dashboardModule: string;
+}
diff --git a/src/app/instances/InstancesComponent.html b/src/app/instances/InstancesComponent.html
new file mode 100644
index 0000000..06b8876
--- /dev/null
+++ b/src/app/instances/InstancesComponent.html
@@ -0,0 +1,18 @@
+<!--
+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)
+-->
+<router-outlet></router-outlet>
diff --git a/src/app/instances/InstancesComponent.scss b/src/app/instances/InstancesComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/instances/InstancesComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/instances/InstancesComponent.ts b/src/app/instances/InstancesComponent.ts
new file mode 100644
index 0000000..fe46d8c
--- /dev/null
+++ b/src/app/instances/InstancesComponent.ts
@@ -0,0 +1,55 @@
+/*
+ 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 Instance components
+ */
+import { Component, Injector } from '@angular/core';
+import { Router, RouterEvent } from '@angular/router';
+/**
+ * Creating component
+ * @Component takes InstancesComponent.html as template url
+ */
+@Component({
+ selector: 'app-instances',
+ templateUrl: './InstancesComponent.html',
+ styleUrls: ['./InstancesComponent.scss']
+})
+/** Exporting a class @exports InstancesComponent */
+export class InstancesComponent {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ // creates packages component
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.router = this.injector.get(Router);
+ this.router.events.subscribe((event: RouterEvent) => {
+ this.redirectToList(event.url);
+ });
+ }
+
+ /** Return to list NS Package List */
+ public redirectToList(getURL: string): void {
+ if (getURL === '/instances') {
+ this.router.navigate(['/instances/ns']).catch();
+ }
+ }
+}
diff --git a/src/app/instances/InstancesModule.ts b/src/app/instances/InstancesModule.ts
new file mode 100644
index 0000000..7e47d32
--- /dev/null
+++ b/src/app/instances/InstancesModule.ts
@@ -0,0 +1,132 @@
+/*
+ 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 Instance module
+ */
+import { CommonModule } from '@angular/common';
+import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
+import { FlexLayoutModule } from '@angular/flex-layout';
+import { FormsModule } from '@angular/forms';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterModule, Routes } from '@angular/router';
+import { CodemirrorModule } from '@ctrl/ngx-codemirror';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { TranslateModule } from '@ngx-translate/core';
+import { AddPDUInstancesComponent } from 'AddPDUInstancesComponent';
+import { DataService } from 'DataService';
+import { HistoryOperationsComponent } from 'HistoryOperationsComponent';
+import { InstancesComponent } from 'InstancesComponent';
+import { LoaderModule } from 'LoaderModule';
+import { NetsliceInstancesComponent } from 'NetsliceInstancesComponent';
+import { SidebarModule } from 'ng-sidebar';
+import { Ng2SmartTableModule } from 'ng2-smart-table';
+import { NSInstancesComponent } from 'NSInstancesComponent';
+import { NSPrimitiveComponent } from 'NSPrimitiveComponent';
+import { NSTopologyComponent } from 'NSTopologyComponent';
+import { PagePerRowModule } from 'PagePerRowModule';
+import { PageReloadModule } from 'PageReloadModule';
+import { PDUInstancesComponent } from 'PDUInstancesComponent';
+import { VNFInstancesComponent } from 'VNFInstancesComponent';
+
+/** To halndle project information */
+const projectInfo: {} = { title: '{project}', url: '/' };
+
+/** Exporting a function using Routes @exports routes */
+const routes: Routes = [
+ {
+ path: '',
+ component: InstancesComponent,
+ children: [
+ {
+ path: 'ns',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'NSINSTANCES', url: null }]
+ },
+ component: NSInstancesComponent
+ },
+ {
+ path: 'vnf',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'VNFINSTANCES', url: null }]
+ },
+ component: VNFInstancesComponent
+ },
+ {
+ path: 'pdu',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'PDUINSTANCES', url: null }]
+ },
+ component: PDUInstancesComponent
+ },
+ {
+ path: 'netslice',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'PAGE.DASHBOARD.NETSLICEINSTANCE', url: null }]
+ },
+ component: NetsliceInstancesComponent
+ },
+ {
+ path: ':type/history-operations/:id',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: '{type}', url: '/instances/{type}' }, { title: '{id}', url: null }]
+ },
+ component: HistoryOperationsComponent
+ },
+ {
+ path: 'ns/:id',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'NSINSTANCES', url: '/instances/ns' }, { title: '{id}', url: null }]
+ },
+ component: NSTopologyComponent
+ }
+ ]
+ }
+];
+
+/**
+ * An NgModule is a class adorned with the @NgModule decorator function.
+ * @NgModule takes a metadata object that tells Angular how to compile and run module code.
+ */
+@NgModule({
+ imports: [ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }), FormsModule, TranslateModule,
+ CodemirrorModule, CommonModule, Ng2SmartTableModule, FlexLayoutModule, RouterModule.forChild(routes), NgbModule,
+ NgSelectModule, PagePerRowModule, LoaderModule, SidebarModule.forRoot(), PageReloadModule],
+ declarations: [InstancesComponent, NSInstancesComponent, VNFInstancesComponent, PDUInstancesComponent, AddPDUInstancesComponent,
+ NetsliceInstancesComponent, HistoryOperationsComponent, NSTopologyComponent, NSPrimitiveComponent],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
+ providers: [DataService],
+ entryComponents: [NSPrimitiveComponent, AddPDUInstancesComponent]
+})
+/** Exporting a class @exports InstancesModule */
+export class InstancesModule {
+ /** Resolves state-less class */
+ private instancesModule: string;
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.instancesModule = '';
+ }
+}
diff --git a/src/app/instances/netslice-instances/NetsliceInstancesComponent.html b/src/app/instances/netslice-instances/NetsliceInstancesComponent.html
new file mode 100644
index 0000000..e8b3c0c
--- /dev/null
+++ b/src/app/instances/netslice-instances/NetsliceInstancesComponent.html
@@ -0,0 +1,44 @@
+<!--
+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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'PAGE.DASHBOARD.NETSLICEINSTANCE' | translate}}</div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" placement="top" container="body"
+ ngbTooltip="{{'PAGE.NETSLICEINSTANCE.CREATENETSLICEINSTANCE' | translate}}" (click)="instantiateNetSlice()">
+ <i class="fa fa-paper-plane" aria-hidden="true"></i>
+ {{'PAGE.NETSLICEINSTANCE.CREATENETSLICEINSTANCE' | translate}}
+ </button>
+ </span>
+</div>
+<div class="row mt-2 mb-0 list-utilites-actions">
+ <div class="col-auto mr-auto">
+ <nav class="custom-items-config">
+ <span><i class="fas fa-clock text-warning"></i>{{operationalStateFirstStep}}</span>
+ <span><i class="fas fa-check-circle text-success"></i>{{operationalStateSecondStep}} /
+ {{configStateSecondStep}}</span>
+ <span><i class="fas fa-times-circle text-danger"></i>{{operationalStateThirdStep}}</span>
+ </nav>
+ </div>
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/instances/netslice-instances/NetsliceInstancesComponent.scss b/src/app/instances/netslice-instances/NetsliceInstancesComponent.scss
new file mode 100644
index 0000000..0ecd95d
--- /dev/null
+++ b/src/app/instances/netslice-instances/NetsliceInstancesComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+ */
\ No newline at end of file
diff --git a/src/app/instances/netslice-instances/NetsliceInstancesComponent.ts b/src/app/instances/netslice-instances/NetsliceInstancesComponent.ts
new file mode 100644
index 0000000..3b9564a
--- /dev/null
+++ b/src/app/instances/netslice-instances/NetsliceInstancesComponent.ts
@@ -0,0 +1,283 @@
+/*
+ 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 Netslice Instance Component
+ */
+import { Component, Injector, OnInit } from '@angular/core';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { CONFIGCONSTANT, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { InstantiateNetSliceTemplateComponent } from 'InstantiateNetSliceTemplate';
+import { NetsliceInstancesActionComponent } from 'NetsliceInstancesActionComponent';
+import { NSTInstanceData, NSTInstanceDetails } from 'NetworkSliceModel';
+import { LocalDataSource } from 'ng2-smart-table';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes NetsliceInstancesComponent.html as template url
+ */
+@Component({
+ templateUrl: './NetsliceInstancesComponent.html',
+ styleUrls: ['./NetsliceInstancesComponent.scss']
+})
+
+/** Exporting a class @exports NetsliceInstancesComponent */
+export class NetsliceInstancesComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Columns list of the smart table @public */
+ public columnLists: object = {};
+
+ /** Settings for smarttable to populate the table with columns @public */
+ public settings: object = {};
+
+ /** Datasource instance inititated @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** Datasource table Data for the NST @public */
+ public nstInstanceData: NSTInstanceData[] = [];
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** operational State init data @public */
+ public operationalStateFirstStep: string = CONFIGCONSTANT.operationalStateFirstStep;
+
+ /** operational State running data @public */
+ public operationalStateSecondStep: string = CONFIGCONSTANT.operationalStateSecondStep;
+
+ /** operational State failed data @public */
+ public operationalStateThirdStep: string = CONFIGCONSTANT.operationalStateThirdStep;
+
+ /** Config State init data @public */
+ public configStateFirstStep: string = CONFIGCONSTANT.configStateFirstStep;
+
+ /** Config State init data @public */
+ public configStateSecondStep: string = CONFIGCONSTANT.configStateSecondStep;
+
+ /** Config State init data @public */
+ public configStateThirdStep: string = CONFIGCONSTANT.configStateThirdStep;
+
+ /** config status assign @public */
+ public configStatusCheck: string;
+
+ /** To consume REST API calls @private */
+ private dataService: DataService;
+
+ /** Utilizes rest service for any CRUD operations @public */
+ private restService: RestService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ this.modalService = this.injector.get(NgbModal);
+ this.dataService = this.injector.get(DataService);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.generateTableColumn();
+ this.generateTableSettings();
+ this.generateData();
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** smart table listing manipulation @private */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** smart table listing manipulation @private */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'net-slice-instance' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Instantiate Net Slice using modalservice @public */
+ public instantiateNetSlice(): void {
+ const modalRef: NgbModalRef = this.modalService.open(InstantiateNetSliceTemplateComponent, { backdrop: 'static' });
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.generateData();
+ }
+ }).catch();
+ }
+
+ /** Generate smart table row title and filters @public */
+ public generateTableSettings(): void {
+ this.settings = {
+ columns: this.columnLists,
+ actions: { add: false, edit: false, delete: false, position: 'right' },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** Generate smart table row title and filters @public */
+ public generateTableColumn(): void {
+ this.columnLists = {
+ name: { title: this.translateService.instant('NAME'), width: '15%', sortDirection: 'asc' },
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '15%' },
+ NstName: { title: this.translateService.instant('NSTNAME'), width: '15%' },
+ OperationalStatus: {
+ type: 'html',
+ title: this.translateService.instant('OPERATIONALSTATUS'),
+ width: '15%',
+ filter: {
+ type: 'list',
+ config: {
+ selectText: 'Select',
+ list: [
+ { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep },
+ { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep },
+ { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep }
+ ]
+ }
+ },
+ valuePrepareFunction: (cell: NSTInstanceData, row: NSTInstanceData): string => {
+ if (row.OperationalStatus === this.operationalStateFirstStep) {
+ return `<span class="icon-label" title="${row.OperationalStatus}">
+ <i class="fas fa-clock text-warning"></i>
+ </span>`;
+ } else if (row.OperationalStatus === this.operationalStateSecondStep) {
+ return `<span class="icon-label" title="${row.OperationalStatus}">
+ <i class="fas fa-check-circle text-success"></i>
+ </span>`;
+ } else if (row.OperationalStatus === this.operationalStateThirdStep) {
+ return `<span class="icon-label" title="${row.OperationalStatus}">
+ <i class="fas fa-times-circle text-danger"></i>
+ </span>`;
+ } else {
+ return `<span>${row.OperationalStatus}</span>`;
+ }
+ }
+ },
+ ConfigStatus: {
+ type: 'html',
+ title: this.translateService.instant('CONFIGSTATUS'),
+ width: '15%',
+ filter: {
+ type: 'list',
+ config: {
+ selectText: 'Select',
+ list: [
+ { value: this.configStateFirstStep, title: this.configStateFirstStep },
+ { value: this.configStateSecondStep, title: this.configStateSecondStep },
+ { value: this.configStateThirdStep, title: this.configStateThirdStep }
+ ]
+ }
+ },
+ valuePrepareFunction: (cell: NSTInstanceData, row: NSTInstanceData): string => {
+ if (row.ConfigStatus === this.configStateFirstStep) {
+ return `<span class="icon-label" title="${row.ConfigStatus}">
+ <i class="fas fa-clock text-warning"></i>
+ </span>`;
+ } else if (row.ConfigStatus === this.configStateSecondStep) {
+ return `<span class="icon-label" title="${row.ConfigStatus}">
+ <i class="fas fa-check-circle text-success"></i>
+ </span>`;
+ } else if (row.ConfigStatus === this.configStateThirdStep) {
+ return `<span class="icon-label" title="${row.ConfigStatus}">
+ <i class="fas fa-times-circle text-danger"></i>
+ </span>`;
+ } else {
+ return `<span>${row.ConfigStatus}</span>`;
+ }
+ }
+ },
+ DetailedStatus: { title: this.translateService.instant('DETAILEDSTATUS'), width: '15%' },
+ Actions: {
+ name: 'Action', width: '10%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom',
+ valuePrepareFunction: (cell: NSTInstanceData, row: NSTInstanceData): NSTInstanceData => row,
+ renderComponent: NetsliceInstancesActionComponent
+ }
+ };
+ }
+
+ /** generateData initiate the net-slice-instance list @public */
+ public generateData(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.NETWORKSLICEINSTANCESCONTENT_URL)
+ .subscribe((netSliceInstancesData: NSTInstanceDetails[]) => {
+ this.nstInstanceData = [];
+ netSliceInstancesData.forEach((netSliceInstanceData: NSTInstanceDetails) => {
+ if (netSliceInstanceData['config-status'] !== undefined) {
+ this.configStatusCheck = netSliceInstanceData['config-status'];
+ } else {
+ this.configStatusCheck = netSliceInstanceData['operational-status'];
+ }
+ const netSliceDataObj: NSTInstanceData = {
+ name: netSliceInstanceData.name,
+ identifier: netSliceInstanceData.id,
+ NstName: netSliceInstanceData['nst-ref'],
+ OperationalStatus: netSliceInstanceData['operational-status'],
+ ConfigStatus: this.configStatusCheck,
+ DetailedStatus: netSliceInstanceData['detailed-status']
+ };
+ this.nstInstanceData.push(netSliceDataObj);
+ });
+ if (this.nstInstanceData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.nstInstanceData).then((data: {}) => {
+ this.isLoadingResults = false;
+ }).catch();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+}
diff --git a/src/app/instances/ns-history-operations/HistoryOperationsComponent.html b/src/app/instances/ns-history-operations/HistoryOperationsComponent.html
new file mode 100644
index 0000000..7890a13
--- /dev/null
+++ b/src/app/instances/ns-history-operations/HistoryOperationsComponent.html
@@ -0,0 +1,36 @@
+<!--
+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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'HISTORYOFOPERATIONS' | translate}}</div>
+</div>
+<div class="row mt-2 mb-0 list-utilites-actions">
+ <div class="col-auto mr-auto">
+ <nav class="custom-items-config">
+ <span><i class="fas fa-clock text-warning"></i>{{historyStateFirstStep}}</span>
+ <span><i class="fas fa-check-circle text-success"></i>{{historyStateSecondStep}}</span>
+ <span><i class="fas fa-times-circle text-danger"></i>{{historyStateThirdStep}}</span>
+ </nav>
+ </div>
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)" (custom)="showInformation($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/instances/ns-history-operations/HistoryOperationsComponent.scss b/src/app/instances/ns-history-operations/HistoryOperationsComponent.scss
new file mode 100644
index 0000000..fdec4ed
--- /dev/null
+++ b/src/app/instances/ns-history-operations/HistoryOperationsComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+ */
diff --git a/src/app/instances/ns-history-operations/HistoryOperationsComponent.ts b/src/app/instances/ns-history-operations/HistoryOperationsComponent.ts
new file mode 100644
index 0000000..a0cac86
--- /dev/null
+++ b/src/app/instances/ns-history-operations/HistoryOperationsComponent.ts
@@ -0,0 +1,265 @@
+/*
+ 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 NS History Of Operations Component
+ */
+import { Component, Injector, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { Router } from '@angular/router';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { CONFIGCONSTANT, ERRORDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import * as HttpStatus from 'http-status-codes';
+import { LocalDataSource } from 'ng2-smart-table';
+import { NSDInstanceData } from 'NSInstanceModel';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+import { ShowInfoComponent } from 'ShowInfoComponent';
+
+/**
+ * Creating component
+ * @Component takes HistoryOperationsComponent.html as template url
+ */
+@Component({
+ templateUrl: './HistoryOperationsComponent.html',
+ styleUrls: ['./HistoryOperationsComponent.scss']
+})
+/** Exporting a class @exports HistoryOperationsComponent */
+export class HistoryOperationsComponent implements OnInit {
+ /** Injector to invoke other services @public */
+ public injector: Injector;
+
+ /** NS Instance array @public */
+ public nsAndnstInstanceData: object[] = [];
+
+ /** Datasource instance @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** Instance component are stored in settings @public */
+ public settings: {} = {};
+
+ /** Contains objects for smart table title and filter settings @public */
+ public columnList: {} = {};
+
+ /** Variable handles the page name @public */
+ public page: string;
+
+ /** Variable handles the title name @public */
+ public titleName: string;
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** History State init data @public */
+ public historyStateFirstStep: string = CONFIGCONSTANT.historyStateFirstStep;
+
+ /** History State running data @public */
+ public historyStateSecondStep: string = CONFIGCONSTANT.historyStateSecondStep;
+
+ /** History State failed data @public */
+ public historyStateThirdStep: string = CONFIGCONSTANT.historyStateThirdStep;
+
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Utilizes rest service for any CRUD operations @private */
+ private restService: RestService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private activatedRoute: ActivatedRoute;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** variables contains paramsID @private */
+ private paramsID: string;
+
+ /** variables contains paramsID @private */
+ private paramsType: string;
+
+ /** variables conatins URL of the History operations @public */
+ private historyURL: string;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ /** Service holds the router information @private */
+ private router: Router;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.sharedService = this.injector.get(SharedService);
+ this.activatedRoute = this.injector.get(ActivatedRoute);
+ this.modalService = this.injector.get(NgbModal);
+ this.translateService = this.injector.get(TranslateService);
+ this.router = this.injector.get(Router);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ this.paramsID = this.activatedRoute.snapshot.paramMap.get('id');
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ this.paramsType = this.activatedRoute.snapshot.paramMap.get('type');
+ if (this.paramsType === 'ns') {
+ this.historyURL = environment.NSHISTORYOPERATIONS_URL + '/?nsInstanceId=' + this.paramsID;
+ this.page = 'ns-history-operation';
+ this.titleName = 'INSTANCEDETAILS';
+ } else if (this.paramsType === 'netslice') {
+ this.historyURL = environment.NSTHISTORYOPERATIONS_URL + '/?netsliceInstanceId=' + this.paramsID;
+ this.page = 'nst-history-operation';
+ this.titleName = 'INSTANCEDETAILS';
+ }
+ this.generateTableColumn();
+ this.generateTableSettings();
+ this.generateData();
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** Generate smart table row title and filters @public */
+ public generateTableSettings(): void {
+ this.settings = {
+ columns: this.columnList,
+ actions: {
+ add: false, edit: false, delete: false, position: 'right',
+ custom: [{
+ name: 'showInformation', title: '<i class="fas fa-info" title=" ' + this.translateService.instant('INFO') + ' "></i>'}]
+ },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** Generate smart table row title and filters @public */
+ public generateTableColumn(): void {
+ this.columnList = {
+ id: { title: this.translateService.instant('ID'), width: '30%' },
+ type: { title: this.translateService.instant('TYPE'), width: '20%' },
+ state: {
+ type: 'html', title: this.translateService.instant('OPERATIONSTATE'), width: '15%',
+ filter: {
+ type: 'list',
+ config: {
+ selectText: 'Select',
+ list: [
+ { value: this.historyStateFirstStep, title: this.historyStateFirstStep },
+ { value: this.historyStateSecondStep, title: this.historyStateSecondStep },
+ { value: this.historyStateThirdStep, title: this.historyStateThirdStep }
+ ]
+ }
+ },
+ valuePrepareFunction: (cell: NSDInstanceData, row: NSDInstanceData): string => {
+ if (row.state === this.historyStateFirstStep) {
+ return `<span class="icon-label" title="${row.state}">
+ <i class="fas fa-clock text-warning"></i>
+ </span>`;
+ } else if (row.state === this.historyStateSecondStep) {
+ return `<span class="icon-label" title="${row.state}">
+ <i class="fas fa-check-circle text-success"></i>
+ </span>`;
+ } else if (row.state === this.historyStateThirdStep) {
+ return `<span class="icon-label" title="${row.state}">
+ <i class="fas fa-times-circle text-danger"></i>
+ </span>`;
+ } else {
+ return `<span>${row.state}</span>`;
+ }
+ }
+ },
+ startTime: { title: this.translateService.instant('STARTTIME'), width: '15%' },
+ statusEnteredTime: { title: this.translateService.instant('STATUSENTEREDTIME'), width: '15%' }
+ };
+ }
+
+ /** smart table listing manipulation @public */
+ public onUserRowSelect(event: MessageEvent): void {
+ this.dataService.changeMessage(event.data);
+ }
+ /** smart table listing manipulation @public */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+ /** show information methods modal with ns history info */
+ public showInformation(event: MessageEvent): void {
+ this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = {
+ id: event.data.id,
+ page: this.page,
+ titleName: this.titleName
+ };
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+
+ /** generateData initiate the ns-instance list @private */
+ private generateData(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(this.historyURL).subscribe((nsdInstancesData: {}[]) => {
+ this.nsAndnstInstanceData = [];
+ nsdInstancesData.forEach((nsdAndnstInstanceData: NSDInstanceData) => {
+ const nsAndnstDataObj: {} = {
+ id: nsdAndnstInstanceData.id,
+ type: nsdAndnstInstanceData.lcmOperationType,
+ state: nsdAndnstInstanceData.operationState,
+ startTime: this.sharedService.convertEpochTime(nsdAndnstInstanceData.startTime),
+ statusEnteredTime: this.sharedService.convertEpochTime(nsdAndnstInstanceData.statusEnteredTime)
+ };
+ this.nsAndnstInstanceData.push(nsAndnstDataObj);
+ });
+
+ if (this.nsAndnstInstanceData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.nsAndnstInstanceData).then((data: {}) => {
+ //empty block
+ }).catch();
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
+ this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
+ } else {
+ this.restService.handleError(error, 'get');
+ }
+ });
+ }
+}
diff --git a/src/app/instances/ns-instances/NSInstancesComponent.html b/src/app/instances/ns-instances/NSInstancesComponent.html
new file mode 100644
index 0000000..6047a2f
--- /dev/null
+++ b/src/app/instances/ns-instances/NSInstancesComponent.html
@@ -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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'NSINSTANCES' | translate}}</div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" placement="top" container="body" ngbTooltip="{{'PAGE.NSINSTANCE.NEWNSINSTANCE' | translate}}"
+ (click)="instantiateNS()">
+ <i class="fa fa-paper-plane" aria-hidden="true"></i> {{'PAGE.NSINSTANCE.NEWNSINSTANCE' | translate}}
+ </button>
+ </span>
+</div>
+<div class="row mt-2 mb-0 list-utilites-actions">
+ <div class="col-auto mr-auto">
+ <nav class="custom-items-config">
+ <span><i class="fas fa-clock text-warning"></i>{{operationalStateFirstStep}}</span>
+ <span><i class="fas fa-check-circle text-success"></i>{{operationalStateSecondStep}} /
+ {{configStateSecondStep}}</span>
+ <span><i class="fas fa-times-circle text-danger"></i>{{operationalStateThirdStep}}</span>
+ </nav>
+ </div>
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/instances/ns-instances/NSInstancesComponent.scss b/src/app/instances/ns-instances/NSInstancesComponent.scss
new file mode 100644
index 0000000..0ecd95d
--- /dev/null
+++ b/src/app/instances/ns-instances/NSInstancesComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+ */
\ No newline at end of file
diff --git a/src/app/instances/ns-instances/NSInstancesComponent.ts b/src/app/instances/ns-instances/NSInstancesComponent.ts
new file mode 100644
index 0000000..07184da
--- /dev/null
+++ b/src/app/instances/ns-instances/NSInstancesComponent.ts
@@ -0,0 +1,278 @@
+/*
+ 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 NS Instance Component
+ */
+import { Component, Injector, OnInit } from '@angular/core';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { CONFIGCONSTANT, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { InstantiateNsComponent } from 'InstantiateNs';
+import { LocalDataSource } from 'ng2-smart-table';
+import { NSDInstanceData, NSInstanceDetails } from 'NSInstanceModel';
+import { NSInstancesActionComponent } from 'NSInstancesActionComponent';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes NSInstancesComponent.html as template url
+ */
+@Component({
+ templateUrl: './NSInstancesComponent.html',
+ styleUrls: ['./NSInstancesComponent.scss']
+})
+/** Exporting a class @exports NSInstancesComponent */
+export class NSInstancesComponent implements OnInit {
+ /** Injector to invoke other services @public */
+ public injector: Injector;
+
+ /** NS Instance array @public */
+ public nsInstanceData: object[] = [];
+
+ /** Datasource instance @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** SelectedRows array @public */
+ public selectedRows: object[] = [];
+
+ /** Selected list array @public */
+ public selectList: object[] = [];
+
+ /** Instance component are stored in settings @public */
+ public settings: {} = {};
+
+ /** Contains objects for menu settings @public */
+ public columnList: {} = {};
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** operational State init data @public */
+ public operationalStateFirstStep: string = CONFIGCONSTANT.operationalStateFirstStep;
+
+ /** operational State running data @public */
+ public operationalStateSecondStep: string = CONFIGCONSTANT.operationalStateSecondStep;
+
+ /** operational State failed data @public */
+ public operationalStateThirdStep: string = CONFIGCONSTANT.operationalStateThirdStep;
+
+ /** Config State init data @public */
+ public configStateFirstStep: string = CONFIGCONSTANT.configStateFirstStep;
+
+ /** Config State init data @public */
+ public configStateSecondStep: string = CONFIGCONSTANT.configStateSecondStep;
+
+ /** Config State init data @public */
+ public configStateThirdStep: string = CONFIGCONSTANT.configStateThirdStep;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Utilizes rest service for any CRUD operations @private */
+ private restService: RestService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.sharedService = this.injector.get(SharedService);
+ this.translateService = this.injector.get(TranslateService);
+ this.modalService = this.injector.get(NgbModal);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.generateTableColumn();
+ this.generateTableSettings();
+ this.generateData();
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** Generate smart table row title and filters @public */
+ public generateTableSettings(): void {
+ this.settings = {
+ columns: this.columnList,
+ actions: { add: false, edit: false, delete: false, position: 'right' },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** Generate smart table row title and filters @public */
+ public generateTableColumn(): void {
+ this.columnList = {
+ name: { title: this.translateService.instant('NAME'), width: '15%', sortDirection: 'asc' },
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' },
+ NsdName: { title: this.translateService.instant('NSDNAME'), width: '15%' },
+ OperationalStatus: {
+ title: this.translateService.instant('OPERATIONALSTATUS'), width: '10%', type: 'html',
+ filter: {
+ type: 'list',
+ config: {
+ selectText: 'Select',
+ list: [
+ { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep },
+ { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep },
+ { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep }
+ ]
+ }
+ },
+ valuePrepareFunction: (cell: NSDInstanceData, row: NSDInstanceData): string => {
+ if (row.OperationalStatus === this.operationalStateFirstStep) {
+ return `<span class="icon-label" title="${row.OperationalStatus}">
+ <i class="fas fa-clock text-warning"></i>
+ </span>`;
+ } else if (row.OperationalStatus === this.operationalStateSecondStep) {
+ return `<span class="icon-label" title="${row.OperationalStatus}">
+ <i class="fas fa-check-circle text-success"></i>
+ </span>`;
+ } else if (row.OperationalStatus === this.operationalStateThirdStep) {
+ return `<span class="icon-label" title="${row.OperationalStatus}">
+ <i class="fas fa-times-circle text-danger"></i>
+ </span>`;
+ } else {
+ return `<span>${row.OperationalStatus}</span>`;
+ }
+ }
+ },
+ ConfigStatus: {
+ title: this.translateService.instant('CONFIGSTATUS'), width: '10%', type: 'html',
+ filter: {
+ type: 'list',
+ config: {
+ selectText: 'Select',
+ list: [
+ { value: this.configStateFirstStep, title: this.configStateFirstStep },
+ { value: this.configStateSecondStep, title: this.configStateSecondStep },
+ { value: this.configStateThirdStep, title: this.configStateThirdStep }
+ ]
+ }
+ },
+ valuePrepareFunction: (cell: NSDInstanceData, row: NSDInstanceData): string => {
+ if (row.ConfigStatus === this.configStateFirstStep) {
+ return `<span class="icon-label" title="${row.ConfigStatus}">
+ <i class="fas fa-clock text-warning"></i>
+ </span>`;
+ } else if (row.ConfigStatus === this.configStateSecondStep) {
+ return `<span class="icon-label" title="${row.ConfigStatus}">
+ <i class="fas fa-check-circle text-success"></i>
+ </span>`;
+ } else if (row.ConfigStatus === this.configStateThirdStep) {
+ return `<span class="icon-label" title="${row.ConfigStatus}">
+ <i class="fas fa-times-circle text-danger"></i>
+ </span>`;
+ } else {
+ return `<span>${row.ConfigStatus}</span>`;
+ }
+ }
+ },
+ DetailedStatus: { title: this.translateService.instant('DETAILEDSTATUS'), width: '15%' },
+ Actions: {
+ name: 'Action', width: '15%', filter: false, sort: false, type: 'custom',
+ title: this.translateService.instant('ACTIONS'),
+ valuePrepareFunction: (cell: NSDInstanceData, row: NSDInstanceData): NSDInstanceData => row,
+ renderComponent: NSInstancesActionComponent
+ }
+ };
+ }
+
+ /** generateData initiate the ns-instance list @public */
+ public generateData(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.NSDINSTANCES_URL).subscribe((nsdInstancesData: NSInstanceDetails[]) => {
+ this.nsInstanceData = [];
+ nsdInstancesData.forEach((nsdInstanceData: NSInstanceDetails) => {
+ const nsDataObj: NSDInstanceData = {
+ name: nsdInstanceData.name,
+ identifier: nsdInstanceData.id,
+ NsdName: nsdInstanceData['nsd-name-ref'],
+ OperationalStatus: nsdInstanceData['operational-status'],
+ ConfigStatus: nsdInstanceData['config-status'],
+ DetailedStatus: nsdInstanceData['detailed-status'],
+ memberIndex: nsdInstanceData.nsd['constituent-vnfd'],
+ nsConfig: nsdInstanceData.nsd['ns-configuration']
+ };
+ this.nsInstanceData.push(nsDataObj);
+ });
+ if (this.nsInstanceData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.nsInstanceData).then((data: {}) => {
+ this.isLoadingResults = false;
+ }).catch();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** smart table listing manipulation @public */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** smart table listing manipulation @public */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'ns-instance' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Instantiate NS using modalservice @public */
+ public instantiateNS(): void {
+ const modalRef: NgbModalRef = this.modalService.open(InstantiateNsComponent, { backdrop: 'static' });
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.generateData();
+ }
+ }).catch();
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+}
diff --git a/src/app/instances/ns-primitive/NSPrimitiveComponent.html b/src/app/instances/ns-primitive/NSPrimitiveComponent.html
new file mode 100644
index 0000000..646c8ae
--- /dev/null
+++ b/src/app/instances/ns-primitive/NSPrimitiveComponent.html
@@ -0,0 +1,92 @@
+<!--
+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)
+-->
+<div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'PERFORMACTION' | translate}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+</div>
+<form [formGroup]="primitiveForm" (ngSubmit)="execNSPrimitive()">
+ <div class="modal-body">
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{'PRIMITIVETYPE' | translate}}*</label>
+ <div class="col-sm-8">
+ <ng-select (change)="primitiveTypeChange($event)" [clearable]="false"
+ placeholder="{{'SELECT' | translate}}" [items]="primitiveTypeList" bindLabel="title"
+ bindValue="value" [(ngModel)]="primitiveType" id="primitiveType"
+ [ngModelOptions]="{standalone: true}"></ng-select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <ng-container *ngIf="primitiveType == 'VNF_Primitive'">
+ <label class="col-sm-4 col-form-label">VNF {{'MEMBERINDEX' | translate}}*</label>
+ <div class="col-sm-3">
+ <ng-select (change)="indexChange($event)" [clearable]="false" placeholder="{{'SELECT' | translate}}"
+ [items]="params.memberIndex" bindLabel="member-vnf-index" bindValue="member-vnf-index"
+ formControlName="vnf_member_index" id="vnf_member_index"
+ [ngClass]="{ 'is-invalid': submitted && f.vnf_member_index.errors }"></ng-select>
+ </div>
+ </ng-container>
+ <label [ngClass]="{ 'col-sm-4': primitiveType != 'VNF_Primitive', 'col-sm-2': primitiveType == 'VNF_Primitive' }" class="col-form-label">
+ {{'PAGE.NSPRIMITIVE.PRIMITIVE' | translate}}*</label>
+ <div [ngClass]="{ 'col-sm-8': primitiveType != 'VNF_Primitive', 'col-sm-3': primitiveType == 'VNF_Primitive' }" class="col-sm-3">
+ <ng-select (change)="primitiveChange($event)" [clearable]="false" placeholder="{{'SELECT' | translate}}"
+ [items]="primitiveList" bindLabel="name" bindValue="name" formControlName="primitive" id="primitive"
+ [ngClass]="{ 'is-invalid': submitted && f.primitive.errors }"></ng-select>
+ </div>
+ </div>
+ <ng-container *ngIf="primitiveParameter.length > 0">
+ <div class="form-group row p-2 bg-light text-white justify-content-end">
+ <div class="col-5">
+ <button type="button" class="btn btn-primary" (click)="createPrimitiveParams()">
+ <i class="fas fa-plus-circle"></i>
+ {{'PAGE.NSPRIMITIVE.ADDPRIMITIVEPARAMS' | translate}}</button>
+ </div>
+ </div>
+ <div formArrayName="primitive_params" *ngFor="let params of getControls(); let i = index;">
+ <div class="form-group row" [formGroupName]="i">
+ <label class="col-sm-2 col-form-label">{{'NAME' | translate}}:</label>
+ <div class="col-sm-3">
+ <ng-select placeholder="{{'SELECT' | translate}}" [clearable]="false"
+ [items]="primitiveParameter" bindLabel="name" bindValue="name"
+ formControlName="primitive_params_name" id="parameter{{i}}"
+ [ngClass]="{ 'is-invalid': submitted && params.controls.primitive_params_name.errors }">
+ </ng-select>
+ </div>
+ <div class="col-sm-1"></div>
+ <label class="col-sm-2 col-form-label">{{'VALUE' | translate}}:</label>
+ <div class="col-sm-3">
+ <input type="text" class="form-control" formControlName="primitive_params_value"
+ [ngClass]="{ 'is-invalid': submitted && params.controls.primitive_params_value.errors }">
+ </div>
+ <div class="col-sm-1" [hidden]="i==0">
+ <button type="button" class="btn btn-sm btn-danger remove-mapping"
+ (click)="removePrimitiveParams(i)">
+ <i class="fas fa-times-circle"></i>
+ </button>
+ </div>
+ </div>
+ </div>
+ </ng-container>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'EXECUTE' | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/instances/ns-primitive/NSPrimitiveComponent.scss b/src/app/instances/ns-primitive/NSPrimitiveComponent.scss
new file mode 100644
index 0000000..edad97f
--- /dev/null
+++ b/src/app/instances/ns-primitive/NSPrimitiveComponent.scss
@@ -0,0 +1,34 @@
+/*
+ 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 '../../../assets/scss/mixins/mixin';
+@import '../../../assets/scss/variable';
+
+.primitive-params-head {
+ @include padding-value(10, 10, 10, 10);
+ line-height: 2em;
+ .btn-params {
+ @include border(all, 1, solid, $white);
+ }
+}
+
+.remove-params {
+ display: flex;
+ align-items: center;
+ font-size: 20px;
+ cursor: pointer;
+}
\ No newline at end of file
diff --git a/src/app/instances/ns-primitive/NSPrimitiveComponent.ts b/src/app/instances/ns-primitive/NSPrimitiveComponent.ts
new file mode 100644
index 0000000..02269d3
--- /dev/null
+++ b/src/app/instances/ns-primitive/NSPrimitiveComponent.ts
@@ -0,0 +1,271 @@
+/*
+ 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 NS Instance Primitive Component
+ */
+import { Component, Injector, Input, OnInit } from '@angular/core';
+import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, URLPARAMS } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { NSData } from 'NSDModel';
+import { NSPrimitiveParams } from 'NSInstanceModel';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+
+/**
+ * Creating component
+ * @Component takes NSPrimitiveComponent.html as template url
+ */
+@Component({
+ templateUrl: './NSPrimitiveComponent.html',
+ styleUrls: ['./NSPrimitiveComponent.scss']
+})
+/** Exporting a class @exports NSPrimitiveComponent */
+export class NSPrimitiveComponent implements OnInit {
+ /** Form valid on submit trigger @public */
+ public submitted: boolean = false;
+
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** FormGroup instance added to the form @ html @public */
+ public primitiveForm: FormGroup;
+
+ /** Primitive params array @public */
+ public primitiveParams: FormArray;
+
+ /** Variable set for twoway binding @public */
+ public nsdId: string;
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Contains list of primitive parameter @public */
+ public primitiveParameter: {}[] = [];
+
+ /** Input contains component objects @public */
+ @Input() public params: URLPARAMS;
+
+ /** Contains list of primitive actions @public */
+ public primitiveList: {}[];
+
+ /** Contains objects that is used to hold types of primitive @public */
+ public primitiveTypeList: {}[] = [];
+
+ /** Model value used to hold selected primitive type @public */
+ public primitiveType: string;
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** Utilizes rest service for any CRUD operations @private */
+ private restService: RestService;
+
+ /** packages data service collections @private */
+ private dataService: DataService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Contains objects that is used to convert key/value pair @private */
+ private objectPrimitiveParams: {} = {};
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.translateService = this.injector.get(TranslateService);
+ this.notifierService = this.injector.get(NotifierService);
+ this.sharedService = this.injector.get(SharedService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.formBuilder = this.injector.get(FormBuilder);
+ this.primitiveTypeList = [{ title: this.translateService.instant('VNFPRIMITIVE'), value: 'VNF_Primitive' }];
+ this.primitiveType = 'VNF_Primitive';
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ /** Setting up initial value for NSD */
+ this.dataService.currentMessage.subscribe((event: NSData) => {
+ if (event.identifier !== undefined || event.identifier !== '' || event.identifier !== null) {
+ this.nsdId = event.identifier;
+ }
+ });
+ if (!isNullOrUndefined(this.params.nsConfig)) {
+ this.primitiveTypeList.push({ title: this.translateService.instant('NSPRIMITIVE'), value: 'NS_Primitive' });
+ }
+ this.initializeForm();
+ }
+
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.primitiveForm.controls; }
+
+ /** initialize Forms @public */
+ public initializeForm(): void {
+ this.primitiveForm = this.formBuilder.group({
+ primitive: [null, [Validators.required]],
+ vnf_member_index: [null, [Validators.required]],
+ primitive_params: this.formBuilder.array([this.primitiveParamsBuilder()])
+ });
+ }
+
+ /** Generate primitive params @public */
+ public primitiveParamsBuilder(): FormGroup {
+ return this.formBuilder.group({
+ primitive_params_name: [null, [Validators.required]],
+ primitive_params_value: ['', [Validators.required]]
+ });
+ }
+
+ /** Handle FormArray Controls @public */
+ public getControls(): AbstractControl[] {
+ return (this.getFormControl('primitive_params') as FormArray).controls;
+ }
+
+ /** Push all primitive params on user's action @public */
+ public createPrimitiveParams(): void {
+ this.primitiveParams = this.getFormControl('primitive_params') as FormArray;
+ this.primitiveParams.push(this.primitiveParamsBuilder());
+ }
+
+ /** Remove primitive params on user's action @public */
+ public removePrimitiveParams(index: number): void {
+ this.primitiveParams.removeAt(index);
+ }
+
+ /** Execute NS Primitive @public */
+ public execNSPrimitive(): void {
+ this.submitted = true;
+ this.objectPrimitiveParams = {};
+ this.sharedService.cleanForm(this.primitiveForm);
+ if (this.primitiveForm.invalid) { return; } // Proceed, onces form is valid
+ this.primitiveForm.value.primitive_params.forEach((params: NSPrimitiveParams) => {
+ if (params.primitive_params_name !== null && params.primitive_params_value !== '') {
+ this.objectPrimitiveParams[params.primitive_params_name] = params.primitive_params_value;
+ }
+ });
+ //Prepare primitive params
+ const primitiveParamsPayLoads: {} = {
+ primitive: this.primitiveForm.value.primitive,
+ primitive_params: this.objectPrimitiveParams
+ };
+ if (this.primitiveType === 'VNF_Primitive') {
+ // tslint:disable-next-line: no-string-literal
+ primitiveParamsPayLoads['vnf_member_index'] = this.primitiveForm.value.vnf_member_index;
+ }
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.NSDINSTANCES_URL + '/' + this.nsdId + '/action'
+ };
+ this.isLoadingResults = true;
+ this.restService.postResource(apiURLHeader, primitiveParamsPayLoads).subscribe((result: {}) => {
+ this.activeModal.dismiss();
+ this.notifierService.notify('success', this.translateService.instant('PAGE.NSPRIMITIVE.EXECUTEDSUCCESSFULLY'));
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'post');
+ });
+ }
+ /** Primitive type change event @public */
+ public primitiveTypeChange(data: { value: string }): void {
+ this.primitiveList = [];
+ this.primitiveParameter = [];
+ this.initializeForm();
+ if (data.value === 'NS_Primitive') {
+ this.primitiveList = !isNullOrUndefined(this.params.nsConfig['config-primitive']) ?
+ this.params.nsConfig['config-primitive'] : [];
+ this.getFormControl('vnf_member_index').setValidators([]);
+ }
+ }
+ /** Member index change event */
+ public indexChange(data: {}): void {
+ if (data) {
+ this.getVnfdInfo(data['vnfd-id-ref']);
+ } else {
+ this.primitiveList = [];
+ this.getFormControl('primitive').setValue(null);
+ this.primitiveParameter = [];
+ }
+ }
+ /** Primivtive change event */
+ public primitiveChange(data: { parameter: {}[] }): void {
+ this.primitiveParameter = [];
+ const formArr: FormArray = this.getFormControl('primitive_params') as FormArray;
+ formArr.controls = [];
+ this.createPrimitiveParams();
+ if (data) {
+ this.updatePrimitive(data);
+ }
+ }
+ /** Update primitive value based on parameter */
+ private updatePrimitive(primitive: { parameter: {}[] }): void {
+ if (primitive.parameter) {
+ this.primitiveParameter = primitive.parameter;
+ } else {
+ this.primitiveParameter = [];
+ const formArr: AbstractControl[] = this.getControls();
+ formArr.forEach((formGp: FormGroup) => {
+ formGp.controls.primitive_params_name.setValidators([]);
+ formGp.controls.primitive_params_name.updateValueAndValidity();
+ formGp.controls.primitive_params_value.setValidators([]);
+ formGp.controls.primitive_params_value.updateValueAndValidity();
+ });
+ }
+ }
+ /** Get primivitive actions from vnfd data */
+ private getVnfdInfo(vnfdRef: string): void {
+ this.primitiveList = [];
+ this.primitiveParameter = [];
+ this.getFormControl('primitive').setValue(null);
+ const apiUrl: string = environment.VNFPACKAGES_URL + '?short-name=' + vnfdRef;
+ this.isLoadingResults = true;
+ this.restService.getResource(apiUrl)
+ .subscribe((vnfdInfo: {}) => {
+ if (vnfdInfo[0]['vnf-configuration']) {
+ this.primitiveList = vnfdInfo[0]['vnf-configuration']['config-primitive'];
+ }
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'get');
+ });
+ }
+ /** Used to get the AbstractControl of controlName passed @private */
+ private getFormControl(controlName: string): AbstractControl {
+ return this.primitiveForm.controls[controlName];
+ }
+}
diff --git a/src/app/instances/ns-topology/NSTopologyComponent.html b/src/app/instances/ns-topology/NSTopologyComponent.html
new file mode 100644
index 0000000..0a3051c
--- /dev/null
+++ b/src/app/instances/ns-topology/NSTopologyComponent.html
@@ -0,0 +1,173 @@
+<!--
+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)
+-->
+<ng-sidebar-container class="ns-instance-topology-sidebar-container">
+ <!-- A sidebar -->
+ <ng-sidebar [(opened)]="sideBarOpened" position="left">
+ <div class="sidebar-header">
+ <span class="topology_title" *ngIf="isShowNSDetails">{{'NS' | translate}} {{'VIEW' | translate}}</span>
+ <span class="topology_title" *ngIf="isShowVLetails">{{'PAGE.TOPOLOGY.VIRTUALLINK' | translate}}</span>
+ <span class="topology_title" *ngIf="isShowVNFRDetails">{{'PAGE.TOPOLOGY.VNF' | translate}}</span>
+ </div>
+ <div class="sidebar-body">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="isShowNSDetails && nsInfo">
+ <div class="row">
+ <div class="col-12 p-0">
+ <table class="table table-bordered text-dark custom-table">
+ <tr>
+ <td>{{'NS' | translate}} {{'INSTANCE' | translate}} Id</td>
+ <td>{{(nsInfo.nsInstanceID)?nsInfo.nsInstanceID:'-'}}</td>
+ </tr>
+ <tr>
+ <td>{{'NSDNAME' | translate}}</td>
+ <td>{{(nsInfo.nsName)?nsInfo.nsName:'-'}}</td>
+ </tr>
+ <tr>
+ <td>{{'OPERATIONALSTATUS' | translate}}</td>
+ <td>{{(nsInfo.nsOperationalStatus)?nsInfo.nsOperationalStatus:'-'}}</td>
+ </tr>
+ <tr>
+ <td>{{'CONFIGSTATUS' | translate}}</td>
+ <td>{{(nsInfo.nsConfigStatus)?nsInfo.nsConfigStatus:'-'}}</td>
+ </tr>
+ <tr>
+ <td>{{'DETAILEDSTATUS' | translate}}</td>
+ <td>{{(nsInfo.nsDetailedStatus)?nsInfo.nsDetailedStatus:'-'}}</td>
+ </tr>
+ <tr>
+ <td>{{'RESOURCEORCHESTRATOR' | translate}}</td>
+ <td>{{(nsInfo.nsResourceOrchestrator)?nsInfo.nsResourceOrchestrator:'-'}}</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="isShowVLetails && virtualLink">
+ <div class="row">
+ <div class="col-12 p-0">
+ <table class="table table-bordered text-dark custom-table">
+ <tbody>
+ <tr>
+ <td>short-name</td>
+ <td>{{(virtualLink.shortName)?virtualLink.shortName:'-'}}</td>
+ </tr>
+ <tr>
+ <td>Name</td>
+ <td>{{(virtualLink.name)?virtualLink.name:'-'}}</td>
+ </tr>
+ <tr>
+ <td>Vim network name</td>
+ <td>{{(virtualLink.vimNetworkName)?virtualLink.vimNetworkName:'-'}}</td>
+ </tr>
+ <tr>
+ <td>Type</td>
+ <td>{{(virtualLink.type)?virtualLink.type:'-'}}</td>
+ </tr>
+ <tr>
+ <td>Id</td>
+ <td>{{(virtualLink.id)?virtualLink.id:'-'}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="isShowVNFRDetails && vnfr">
+ <div class="row">
+ <div class="col-12 p-0">
+ <table class="table table-bordered text-dark custom-table">
+ <tbody>
+ <tr>
+ <td>VIM Id</td>
+ <td>{{(vnfr.vimID)?vnfr.vimID:'-'}}</td>
+ </tr>
+ <tr>
+ <td>_id</td>
+ <td>{{(vnfr._id)?vnfr._id:'-'}}</td>
+ </tr>
+ <tr>
+ <td>IP</td>
+ <td>{{(vnfr.ip)?vnfr.ip:'-'}}</td>
+ </tr>
+ <tr>
+ <td>Nsr Id</td>
+ <td>{{(vnfr.nsrID)?vnfr.nsrID:'-'}}</td>
+ </tr>
+ <tr>
+ <td>Id</td>
+ <td>{{(vnfr.id)?vnfr.id:'-'}}</td>
+ </tr>
+ <tr>
+ <td>Vnfd Ref</td>
+ <td>{{(vnfr.vnfdRef)?vnfr.vnfdRef:'-'}}</td>
+ </tr>
+ <tr>
+ <td>Vnfd Id</td>
+ <td>{{(vnfr.vnfdID)?vnfr.vnfdID:'-'}}</td>
+ </tr>
+ <tr>
+ <td>Member index</td>
+ <td>{{(vnfr.memberIndex)?vnfr.memberIndex:'-'}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </ng-sidebar>
+ <!-- Page content -->
+ <div ng-sidebar-content>
+ <button (click)="toggleSidebar()" class="btn btn-default" placement="right" ngbTooltip="{{'OPEN' | translate }}">
+ <i class="fa fa-arrow-right detail-sidebar" aria-hidden="true"></i>
+ </button>
+ </div>
+</ng-sidebar-container>
+<div class="container-fluid text-dark">
+ <div class="row ns-instance-form justify-content-end ">
+ <div class="col-xs-9 col-sm-9 col-md-9 col-lg-9">
+ <div class="row">
+ <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 pl-0">
+ <div class="btn-group list" role="group" aria-label="Basic example">
+ <button type="button" class="btn btn-primary topology-btn" (click)="onFreeze()"
+ [class.pinned]="classApplied" placement="top" container="body"
+ ngbTooltip="{{(classApplied ? 'UNFREEZE' : 'FREEZE') | translate}}">
+ <i class="fas fa-thumbtack"></i>
+ </button>
+ </div>
+ </div>
+ <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 text-right pr-0 badgegroup">
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/VNFD.svg" class="ns-svg" draggable="false"/>
+ <br>VNFR</span>
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/VL.svg" class="ns-svg" draggable="false"/>
+ <br>VL</span>
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/CP.svg" class="ns-svg" draggable="false"/>
+ <br>CP</span>
+ </div>
+ </div>
+ <div class="row border-all">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 svg-container">
+ <svg preserveAspectRatio="xMidYMin slice" id="graphContainer" #graphContainer></svg>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/instances/ns-topology/NSTopologyComponent.scss b/src/app/instances/ns-topology/NSTopologyComponent.scss
new file mode 100644
index 0000000..d750ccc
--- /dev/null
+++ b/src/app/instances/ns-topology/NSTopologyComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/instances/ns-topology/NSTopologyComponent.ts b/src/app/instances/ns-topology/NSTopologyComponent.ts
new file mode 100644
index 0000000..44c6309
--- /dev/null
+++ b/src/app/instances/ns-topology/NSTopologyComponent.ts
@@ -0,0 +1,573 @@
+/*
+ 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 NS Topology Component
+ */
+/* tslint:disable:no-increment-decrement */
+import { Component, ElementRef, Injector, ViewChild, ViewEncapsulation } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { Router } from '@angular/router';
+import { TranslateService } from '@ngx-translate/core';
+import { ERRORDATA } from 'CommonModel';
+import * as d3 from 'd3';
+import { environment } from 'environment';
+import * as HttpStatus from 'http-status-codes';
+import { VNFDCONNECTIONPOINTREF } from 'NSDModel';
+import { COMPOSERNODES, CONNECTIONPOINT, NSD, NSDVLD, NSINFO, NSInstanceDetails, NSINSTANCENODES, VLINFO, VNFRINFO } from 'NSInstanceModel';
+import { GRAPHDETAILS, Tick, TickPath } from 'NSTopologyModel';
+import { RestService } from 'src/services/RestService';
+import { isNullOrUndefined } from 'util';
+
+/**
+ * Creating component
+ * @Component takes NSTopologyComponent.html as template url
+ */
+@Component({
+ selector: 'app-ns-topology',
+ templateUrl: './NSTopologyComponent.html',
+ styleUrls: ['./NSTopologyComponent.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+/** Exporting a class @exports NSTopologyComponent */
+export class NSTopologyComponent {
+ /** Injector to invoke other services @public */
+ public injector: Injector;
+ /** View child contains graphContainer ref @public */
+ @ViewChild('graphContainer', { static: true }) public graphContainer: ElementRef;
+ /** Holds the basic information of NS @public */
+ public nsInfo: NSINFO;
+ /** Contains tranlsate instance @private */
+ public translateService: TranslateService;
+ /** Add the activeclass for the selected @public */
+ public activeClass: string = 'active';
+ /** Add the fixed class for the freeze @public */
+ public fixedClass: string = 'fixed';
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+ /** Assign the forcesimulation active @public */
+ public forceSimulationActive: boolean = false;
+ /** Assign pinned class for the button when freezed @public */
+ public classApplied: boolean = false;
+ /** Contains sidebar open status @public */
+ public sideBarOpened: boolean = true;
+ /** Need to show the NS Details @public */
+ public isShowNSDetails: boolean = true;
+ /** Need to show the VL Details @public */
+ public isShowVLetails: boolean = false;
+ /** Need to show the VNFR Details @public */
+ public isShowVNFRDetails: boolean = false;
+ /** Show right side info of Virtual Link @public */
+ public virtualLink: VLINFO;
+ /** Show right side info of Virtual Link @public */
+ public vnfr: VNFRINFO;
+
+ /** Contains lastkeypressed instance @private */
+ private lastKeyDown: number = -1;
+ /** Instance of the rest service @private */
+ private restService: RestService;
+ /** Holds the instance of AuthService class of type AuthService @private */
+ private activatedRoute: ActivatedRoute;
+ /** Holds the NS Id @private */
+ private nsIdentifier: string;
+ /** Contains SVG attributes @private */
+ // tslint:disable-next-line:no-any
+ private svg: any;
+ /** Contains forced node animations @private */
+ // tslint:disable-next-line:no-any
+ private force: any;
+ /** Contains path information of the node */
+ // tslint:disable-next-line:no-any
+ private path: any;
+ /** Contains node network @private */
+ // tslint:disable-next-line:no-any
+ private network: any;
+ /** Contains node square @private */
+ // tslint:disable-next-line:no-any
+ private square: any;
+ /** Contains node circle @private */
+ // tslint:disable-next-line:no-any
+ private circle: any;
+ /** Contains the NS information @private */
+ private nsData: NSInstanceDetails;
+ /** Contains NDS information of a descriptors */
+ private nsdData: NSD;
+ /** Contains node information @private */
+ private nodes: NSINSTANCENODES[] = [];
+ /** Contains links information @private */
+ private links: {}[] = [];
+ /** holds cp count/iteration @private */
+ private cpCount: number;
+ /** VNFD nodes @private */
+ private vnfdNodes: {}[] = [];
+ /** VLD nodes @private */
+ private vldNodes: {}[] = [];
+ /** Connection CP nodes @private */
+ private cpNodes: {}[] = [];
+ /** Set timeout @private */
+ private TIMEOUT: number = 2000;
+ /** Rendered nodes represent vnf @private */
+ // tslint:disable-next-line:no-any
+ private gSquare: any;
+ /** Rendered nodes represent network @private */
+ // tslint:disable-next-line:no-any
+ private gNetwork: any;
+ /** Rendered nodes represent network @private */
+ // tslint:disable-next-line:no-any
+ private gCircle: any;
+ /** Service holds the router information @private */
+ private router: Router;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.activatedRoute = this.injector.get(ActivatedRoute);
+ this.translateService = this.injector.get(TranslateService);
+ this.router = this.injector.get(Router);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate @public
+ */
+ public ngOnInit(): void {
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ this.nsIdentifier = this.activatedRoute.snapshot.paramMap.get('id');
+ this.generateData();
+ }
+ /** Event to freeze the animation @public */
+ public onFreeze(): void {
+ this.classApplied = !this.classApplied;
+ const alreadyFixedIsActive: boolean = d3.select('svg#graphContainer').classed(this.fixedClass);
+ d3.select('svg#graphContainer').classed(this.fixedClass, !alreadyFixedIsActive);
+ if (alreadyFixedIsActive) {
+ this.force.stop();
+ }
+ this.forceSimulationActive = alreadyFixedIsActive;
+ this.nodes.forEach((d: COMPOSERNODES) => {
+ d.fx = (alreadyFixedIsActive) ? null : d.x;
+ d.fy = (alreadyFixedIsActive) ? null : d.y;
+ });
+ if (alreadyFixedIsActive) {
+ this.force.restart();
+ }
+ }
+ /** Events handles when dragended @public */
+ public toggleSidebar(): void {
+ this.sideBarOpened = !this.sideBarOpened;
+ this.deselectAllNodes();
+ this.showRightSideInfo(true, false, false);
+ }
+ /** Get the default Configuration of containers @private */
+ private getGraphContainerAttr(): GRAPHDETAILS {
+ return {
+ width: 700,
+ height: 400,
+ nodeHeight: 50,
+ nodeWidth: 35,
+ textX: -35,
+ textY: 30,
+ radius: 5,
+ distance: 50,
+ strength: -500,
+ forcex: 2,
+ forcey: 2,
+ sourcePaddingYes: 17,
+ sourcePaddingNo: 12,
+ targetPaddingYes: 4,
+ targetPaddingNo: 3,
+ alphaTarget: 0.3,
+ imageX: -25,
+ imageY: -25,
+ shiftKeyCode: 17
+ };
+ }
+ /** Show the right-side information @private */
+ private showRightSideInfo(nsDetails: boolean, vlDetails: boolean, vnfrDeails: boolean): void {
+ this.isShowNSDetails = nsDetails;
+ this.isShowVLetails = vlDetails;
+ this.isShowVNFRDetails = vnfrDeails;
+ }
+ /** De-select all the selected nodes @private */
+ private deselectAllNodes(): void {
+ this.square.select('image').classed(this.activeClass, false);
+ this.network.select('image').classed(this.activeClass, false);
+ this.circle.select('image').classed(this.activeClass, false);
+ }
+ /** Prepare all the information for node creation @private */
+ private generateData(): void {
+ this.restService.getResource(environment.NSINSTANCESCONTENT_URL + '/' + this.nsIdentifier).subscribe((nsData: NSInstanceDetails) => {
+ this.nsData = nsData;
+ this.nsInfo = {
+ nsInstanceID: nsData._id,
+ nsName: nsData.name,
+ nsOperationalStatus: nsData['operational-status'],
+ nsConfigStatus: nsData['config-status'],
+ nsDetailedStatus: nsData['detailed-status'],
+ nsResourceOrchestrator: nsData['resource-orchestrator']
+ };
+ if (this.nsData['constituent-vnfr-ref'] !== undefined) {
+ this.generateVNFRCPNodes();
+ }
+ if (this.nsData.vld !== undefined) {
+ this.generateVLDNetworkNodes();
+ }
+ setTimeout(() => {
+ this.pushAllNodes();
+ this.generateVNFDCP();
+ this.generateVLDCP();
+ this.isLoadingResults = false;
+ this.createNode(this.nodes, this.links);
+ }, this.TIMEOUT);
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
+ this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
+ } else {
+ this.restService.handleError(error, 'get');
+ }
+ });
+ }
+
+ /** Fetching all the VNFR Information @private */
+ private generateVNFRCPNodes(): void {
+ this.nsData['constituent-vnfr-ref'].forEach((vnfdrID: string) => {
+ this.restService.getResource(environment.VNFINSTANCES_URL + '/' + vnfdrID).subscribe((vndfrDetail: NSD) => {
+ this.nodes.push({
+ id: vndfrDetail['vnfd-ref'] + ':' + vndfrDetail['member-vnf-index-ref'],
+ nodeTypeRef: 'vnfd',
+ cp: vndfrDetail['connection-point'],
+ vdur: vndfrDetail.vdur,
+ vld: vndfrDetail.vld,
+ nsID: vndfrDetail['nsr-id-ref'],
+ vnfdID: vndfrDetail['vnfd-id'],
+ vimID: vndfrDetail['vim-account-id'],
+ vndfrID: vndfrDetail.id,
+ ipAddress: vndfrDetail['ip-address'],
+ memberIndex: vndfrDetail['member-vnf-index-ref'],
+ vnfdRef: vndfrDetail['vnfd-ref'],
+ selectorId: 'nsInst-' + vndfrDetail.id
+ });
+ // Fetching all the connection point of VNF & Interface
+ vndfrDetail['connection-point'].forEach((cp: CONNECTIONPOINT) => {
+ this.nodes.push({
+ id: cp.name + ':' + vndfrDetail['member-vnf-index-ref'],
+ vndfCPRef: vndfrDetail['vnfd-ref'] + ':' + vndfrDetail['member-vnf-index-ref'],
+ nodeTypeRef: 'cp',
+ name: cp.name
+ });
+ });
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ });
+ }
+
+ /** Fetching all the VLD/Network Information @private */
+ private generateVLDNetworkNodes(): void {
+ this.nsdData = this.nsData.nsd;
+ this.nsdData.vld.forEach((ref: NSDVLD) => {
+ this.nodes.push({
+ id: ref.id,
+ nodeTypeRef: 'vld',
+ name: ref.name,
+ type: ref.type,
+ vnfdCP: ref['vnfd-connection-point-ref'],
+ vimNetworkName: ref['vim-network-name'],
+ shortName: ref['short-name'],
+ selectorId: 'nsInst-' + ref.id
+ });
+ });
+ }
+
+ /** Pushing connection points of path/links nodes @private */
+ private pushAllNodes(): void {
+ this.nodes.forEach((nodeList: NSINSTANCENODES) => {
+ if (nodeList.nodeTypeRef === 'vnfd') {
+ this.vnfdNodes.push(nodeList);
+ } else if (nodeList.nodeTypeRef === 'vld') {
+ this.vldNodes.push(nodeList);
+ } else if (nodeList.nodeTypeRef === 'cp') {
+ this.cpNodes.push(nodeList);
+ }
+ });
+ }
+
+ /** Get CP position based on vndf @private */
+ private generateVNFDCP(): void {
+ this.vnfdNodes.forEach((list: NSINSTANCENODES) => {
+ const vndfPos: number = this.nodes.map((e: NSINSTANCENODES) => { return e.id; }).indexOf(list.id);
+ this.cpCount = 0;
+ this.nodes.forEach((res: NSINSTANCENODES) => {
+ if (res.nodeTypeRef === 'cp' && res.vndfCPRef === list.id) {
+ this.links.push({ source: this.nodes[vndfPos], target: this.nodes[this.cpCount] });
+ }
+ this.cpCount++;
+ });
+ });
+ }
+
+ /** Get CP position based on vld @private */
+ private generateVLDCP(): void {
+ let vldPos: number = 0;
+ this.vldNodes.forEach((list: NSINSTANCENODES) => {
+ if (!isNullOrUndefined(list.vnfdCP)) {
+ list.vnfdCP.forEach((cpRef: VNFDCONNECTIONPOINTREF) => {
+ this.cpCount = 0;
+ this.nodes.forEach((res: NSINSTANCENODES) => {
+ if (res.nodeTypeRef === 'cp' && res.id === cpRef['vnfd-connection-point-ref'] + ':' + cpRef['member-vnf-index-ref']) {
+ this.links.push({ source: this.nodes[vldPos], target: this.nodes[this.cpCount] });
+ }
+ this.cpCount++;
+ });
+ });
+ vldPos++;
+ }
+ });
+ }
+
+ /** Node is created and render at D3 region @private */
+ private createNode(nodes: NSINSTANCENODES[], links: {}[]): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ d3.selectAll('svg#graphContainer > *').remove();
+ d3.select(window).on('keydown', () => { this.keyDown(); });
+ d3.select(window).on('keyup', () => { this.keyUp(); });
+ this.svg = d3.select('#graphContainer')
+ .attr('oncontextmenu', 'return false;')
+ .attr('width', graphContainerAttr.width)
+ .attr('height', graphContainerAttr.height);
+ this.force = d3.forceSimulation()
+ .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength))
+ .force('link', d3.forceLink().id((d: TickPath) => d.id).distance(graphContainerAttr.distance))
+ .force('center', d3.forceCenter(graphContainerAttr.width / graphContainerAttr.forcex,
+ graphContainerAttr.height / graphContainerAttr.forcey))
+ .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex))
+ .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey))
+ .on('tick', () => { this.tick(); });
+ // handles to link and node element groups
+ this.path = this.svg.append('svg:g').selectAll('path');
+ this.network = this.svg.append('svg:g').selectAll('network');
+ this.square = this.svg.append('svg:g').selectAll('rect');
+ this.circle = this.svg.append('svg:g').selectAll('circle');
+ this.restart(nodes, links);
+ }
+
+ /** Update force layout (called automatically each iteration) @private */
+ private tick(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ // draw directed edges with proper padding from node centers
+ this.path.attr('class', 'link').attr('d', (d: Tick) => {
+ const deltaX: number = d.target.x - d.source.x;
+ const deltaY: number = d.target.y - d.source.y;
+ const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+ const normX: number = deltaX / dist;
+ const normY: number = deltaY / dist;
+ const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo;
+ const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo;
+ const sourceX: number = d.source.x + (sourcePadding * normX);
+ const sourceY: number = d.source.y + (sourcePadding * normY);
+ const targetX: number = d.target.x - (targetPadding * normX);
+ const targetY: number = d.target.y - (targetPadding * normY);
+ return `M${sourceX},${sourceY}L${targetX},${targetY}`;
+ });
+ this.network.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
+ this.square.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
+ this.circle.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
+ }
+
+ /** Update graph (called when needed) @private */
+ private restart(nodes: NSINSTANCENODES[], links: {}[]): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.path = this.path.data(links);
+ const vnfdNodes: {}[] = []; const vldNodes: {}[] = []; const cpNodes: {}[] = []; // NB: Nodes are known by id, not by index!
+ nodes.forEach((nodeList: NSINSTANCENODES) => {
+ if (nodeList.nodeTypeRef === 'vnfd') { vnfdNodes.push(nodeList); }
+ else if (nodeList.nodeTypeRef === 'vld') { vldNodes.push(nodeList); }
+ else if (nodeList.nodeTypeRef === 'cp') { cpNodes.push(nodeList); }
+ });
+ this.square = this.square.data(vnfdNodes, (d: { id: number }) => d.id);
+ this.network = this.network.data(vldNodes, (d: { id: number }) => d.id);
+ this.circle = this.circle.data(cpNodes, (d: { id: number }) => d.id);
+ this.resetAndCreateNodes();
+ this.force.nodes(nodes).force('link').links(links); //Set the graph in motion
+ this.force.alphaTarget(graphContainerAttr.alphaTarget).restart();
+ }
+
+ /** Rest and create nodes @private */
+ private resetAndCreateNodes(): void {
+ this.path.exit().remove();
+ this.square.exit().remove();
+ this.network.exit().remove();
+ this.circle.exit().remove();
+ // tslint:disable-next-line:no-any
+ const gPath: any = this.path.enter().append('svg:path').attr('class', 'link');
+ this.getgSquare();
+ this.getgNetwork();
+ this.getgCircle();
+ this.square = this.gSquare.merge(this.square);
+ this.network = this.gNetwork.merge(this.network);
+ this.path = gPath.merge(this.path);
+ this.circle = this.gCircle.merge(this.circle);
+ }
+
+ /** Events handles when Shift Click to perform create cp @private */
+ // tslint:disable-next-line: no-any
+ private singleClick(nodeSelected: any, d: COMPOSERNODES): void {
+ this.selectNodeExclusive(nodeSelected, d);
+ }
+ /** Selected nodes @private */
+ // tslint:disable-next-line: no-any
+ private selectNodeExclusive(nodeSelected: any, d: COMPOSERNODES): void {
+ const alreadyIsActive: boolean = nodeSelected.select('#' + d.selectorId).classed(this.activeClass);
+ this.deselectAllNodes();
+ nodeSelected.select('#' + d.selectorId).classed(this.activeClass, !alreadyIsActive);
+ if (d.nodeTypeRef === 'vld' && !alreadyIsActive) {
+ this.virtualLink = {
+ id: d.id,
+ name: d.name,
+ type: d.type,
+ shortName: d.shortName,
+ vimNetworkName: d.vimNetworkName
+ };
+ this.showRightSideInfo(false, true, false);
+ } else if (d.nodeTypeRef === 'vnfd' && !alreadyIsActive) {
+ this.vnfr = {
+ vimID: d.vimID,
+ _id: d.vndfrID,
+ ip: d.ipAddress,
+ nsrID: d.nsID,
+ id: d.selectorId,
+ vnfdRef: d.vnfdRef,
+ vnfdId: d.vnfdID,
+ memberIndex: d.memberIndex
+ };
+ this.showRightSideInfo(false, false, true);
+ } else {
+ this.showRightSideInfo(true, false, false);
+ }
+ }
+ /** Setting all the square/vnf attributes of nodes @private */
+ private getgSquare(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gSquare = this.square.enter().append('svg:g');
+ this.gSquare.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gSquare.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/VNFD.svg')
+ .on('click', (d: COMPOSERNODES) => { this.singleClick(this.gSquare, d); this.onNodeClickToggleSidebar(); });
+ this.gSquare.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: COMPOSERNODES) => d.id);
+ }
+
+ /** Settings all the network attributes of nodes @private */
+ private getgNetwork(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gNetwork = this.network.enter().append('svg:g');
+ this.gNetwork.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gNetwork.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/VL.svg')
+ .on('click', (d: COMPOSERNODES) => { this.singleClick(this.gNetwork, d); this.onNodeClickToggleSidebar(); });
+ this.gNetwork.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: COMPOSERNODES) => d.name);
+ }
+
+ /** Settings all the connection point attributes of nodes @private */
+ private getgCircle(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gCircle = this.circle.enter().append('svg:g');
+ this.gCircle.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gCircle.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/CP.svg');
+ this.gCircle.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: COMPOSERNODES) => d.name);
+ }
+
+ /** Key press event @private */
+ private keyDown(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ if (this.lastKeyDown !== -1) { return; }
+ this.lastKeyDown = d3.event.keyCode;
+ if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
+ this.gSquare.call(d3.drag()
+ .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
+ );
+ this.gNetwork.call(d3.drag()
+ .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
+ );
+ this.gCircle.call(d3.drag()
+ .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
+ );
+ this.svg.classed('ctrl', true);
+ }
+ }
+ /** Key realse event @private */
+ private keyUp(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.lastKeyDown = -1;
+ if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
+ this.gSquare.on('.drag', null);
+ this.gNetwork.on('.drag', null);
+ this.gCircle.on('.drag', null);
+ this.svg.classed('ctrl', false);
+ }
+ }
+ /** Events handles when dragstarted @private */
+ private dragstarted(d: COMPOSERNODES): void {
+ d.fx = d.x;
+ d.fy = d.y;
+ }
+ /** Events handles when dragged @private */
+ private dragged(d: COMPOSERNODES): void {
+ d.fx = d.x = d3.event.x;
+ d.fy = d.y = d3.event.y;
+ }
+ /** Events handles when dragended @private */
+ private dragended(d: COMPOSERNODES): void {
+ if (this.forceSimulationActive) {
+ d.fx = null;
+ d.fy = null;
+ } else {
+ d.fx = d.x;
+ d.fy = d.y;
+ this.forceSimulationActive = false;
+ }
+ }
+ /** Events handles when node single click @private */
+ private onNodeClickToggleSidebar(): void {
+ this.sideBarOpened = true;
+ }
+}
diff --git a/src/app/instances/pdu-instances/PDUInstancesComponent.html b/src/app/instances/pdu-instances/PDUInstancesComponent.html
new file mode 100644
index 0000000..3c18bfa
--- /dev/null
+++ b/src/app/instances/pdu-instances/PDUInstancesComponent.html
@@ -0,0 +1,36 @@
+<!--
+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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'PDUINSTANCES' | translate}}</div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" placement="top" container="body"
+ ngbTooltip="{{'PAGE.PDUINSTANCE.NEWPDUINSTANCE' | translate}}" (click)="addPDUInstanceModal()">
+ <i class="fas fa-plus-circle" aria-hidden="true"></i>
+ {{'PAGE.PDUINSTANCE.NEWPDUINSTANCE' | translate}}
+ </button>
+ </span>
+</div>
+<div class="row mt-2 mb-0 form-group justify-content-end list-utilites-actions">
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/instances/pdu-instances/PDUInstancesComponent.scss b/src/app/instances/pdu-instances/PDUInstancesComponent.scss
new file mode 100644
index 0000000..0ecd95d
--- /dev/null
+++ b/src/app/instances/pdu-instances/PDUInstancesComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+ */
\ No newline at end of file
diff --git a/src/app/instances/pdu-instances/PDUInstancesComponent.ts b/src/app/instances/pdu-instances/PDUInstancesComponent.ts
new file mode 100644
index 0000000..20b44df
--- /dev/null
+++ b/src/app/instances/pdu-instances/PDUInstancesComponent.ts
@@ -0,0 +1,197 @@
+/*
+ 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 PDU Instance Component
+ */
+import { Component, Injector, OnDestroy, OnInit } from '@angular/core';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { AddPDUInstancesComponent } from 'AddPDUInstancesComponent';
+import { ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { LocalDataSource } from 'ng2-smart-table';
+import { PDUInstanceDetails } from 'PDUInstanceModel';
+import { PDUInstancesActionComponent } from 'PDUInstancesActionComponent';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes PDUInstancesComponent.html as template url
+ */
+@Component({
+ templateUrl: './PDUInstancesComponent.html',
+ styleUrls: ['./PDUInstancesComponent.scss']
+})
+/** Exporting a class @exports PDUInstancesComponent */
+export class PDUInstancesComponent implements OnInit, OnDestroy {
+ /** Injector to invoke other services @public */
+ public injector: Injector;
+
+ /** NS Instance array @public */
+ public pduInstanceData: object[] = [];
+
+ /** Datasource instance @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** SelectedRows array @public */
+ public selectedRows: object[] = [];
+
+ /** Selected list array @public */
+ public selectList: object[] = [];
+
+ /** Instance component are stored in settings @public */
+ public settings: {} = {};
+
+ /** Contains objects for menu settings @public */
+ public columnList: {} = {};
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Utilizes rest service for any CRUD operations @private */
+ private restService: RestService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.sharedService = this.injector.get(SharedService);
+ this.translateService = this.injector.get(TranslateService);
+ this.modalService = this.injector.get(NgbModal);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.generateTableColumn();
+ this.generateTableSettings();
+ this.generateData();
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** Generate smart table row title and filters @public */
+ public generateTableSettings(): void {
+ this.settings = {
+ columns: this.columnList,
+ actions: { add: false, edit: false, delete: false, position: 'right' },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** Generate smart table row title and filters @public */
+ public generateTableColumn(): void {
+ this.columnList = {
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '25%' },
+ name: { title: this.translateService.instant('NAME'), width: '20%', sortDirection: 'asc' },
+ type: { title: this.translateService.instant('TYPE'), width: '15%' },
+ usageState: { title: this.translateService.instant('USAGESTATE'), width: '15%' },
+ CreatedAt: { title: this.translateService.instant('CREATEDAT'), width: '15%' },
+ Actions: {
+ name: 'Action', width: '10%', filter: false, sort: false, type: 'custom',
+ title: this.translateService.instant('ACTIONS'),
+ valuePrepareFunction: (cell: PDUInstanceDetails, row: PDUInstanceDetails): PDUInstanceDetails => row,
+ renderComponent: PDUInstancesActionComponent
+ }
+ };
+ }
+
+ /** generateData initiate the ns-instance list @public */
+ public generateData(): void {
+ this.pduInstanceData = [];
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.PDUINSTANCE_URL).subscribe((pduInstancesData: PDUInstanceDetails[]) => {
+ pduInstancesData.forEach((pduInstanceData: PDUInstanceDetails) => {
+ const pduDataObj: {} = {
+ name: pduInstanceData.name,
+ identifier: pduInstanceData._id,
+ type: pduInstanceData.type,
+ usageState: pduInstanceData._admin.usageState,
+ CreatedAt: this.sharedService.convertEpochTime(Number(pduInstanceData._admin.created))
+ };
+ this.pduInstanceData.push(pduDataObj);
+ });
+ if (this.pduInstanceData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.pduInstanceData).then((data: {}) => {
+ this.isLoadingResults = false;
+ }).catch();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** smart table listing manipulation @public */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** smart table listing manipulation @public */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'pdu-instances' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Add PDU Instance modal using modalservice @public */
+ public addPDUInstanceModal(): void {
+ const modalRef: NgbModalRef = this.modalService.open(AddPDUInstancesComponent, { backdrop: 'static' });
+ modalRef.componentInstance.title = this.translateService.instant('PAGE.PDUINSTANCE.NEWPDUINSTANCE');
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.generateData();
+ }
+ }).catch();
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+}
diff --git a/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.html b/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.html
new file mode 100644
index 0000000..f785426
--- /dev/null
+++ b/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.html
@@ -0,0 +1,95 @@
+<!--
+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)
+-->
+<div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{title}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+</div>
+<form [formGroup]="pduInstancesForm" (ngSubmit)="createPDUInstances()" autocomplete="off">
+ <div class="modal-body">
+ <div class="form-group row">
+ <label class="col-sm-12 col-form-label mandatory-label" [ngClass]="{'text-danger': pduInstancesForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <label class="col-sm-3 col-form-label" for="name">{{'NAME' | translate}}*</label>
+ <div class="col-sm-3">
+ <input placeholder="{{'NAME' | translate}}" type="text" class="form-control" formControlName="name" id="name" [ngClass]="{ 'is-invalid': submitted && f.name.errors }"
+ required>
+ </div>
+ <label class="col-sm-3 col-form-label" for="type">{{'PAGE.PDUINSTANCE.PDUTYPE' | translate}}*</label>
+ <div class="col-sm-3">
+ <input placeholder="{{'PAGE.PDUINSTANCE.PDUTYPE' | translate}}" type="text" class="form-control" formControlName="type" id="type" [ngClass]="{ 'is-invalid': submitted && f.type.errors }">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-3 col-form-label" for="vim_accounts">{{'VIMACCOUNTS' | translate}}*</label>
+ <div class="col-sm-9">
+ <ng-select placeholder="{{'VIMACCOUNTS' | translate}}" [items]="vimAccountSelect" multiple="true" bindLabel="name" bindValue="_id" formControlName="vim_accounts" id="vim_accounts"
+ [ngClass]="{ 'is-invalid': submitted && f.vim_accounts.errors }" [(ngModel)]="selectedVIM">
+ </ng-select>
+ </div>
+ </div>
+ <div class="form-group row p-2 bg-light text-dark">
+ <div class="col-sm-7 align-self-center"><span>{{'PAGE.PDUINSTANCE.PARAMETERS' | translate}}</span></div>
+ <div class="col-sm-5">
+ <button type="button" class="btn btn-primary" (click)="createInterfaces()">
+ <i class="fas fa-plus-square"></i> {{'PAGE.PDUINSTANCE.ADDINSTANCEPARAMS' | translate}}</button>
+ </div>
+ </div>
+ <div formArrayName="interfaces" *ngFor="let params of getControls(); let i = index;">
+ <div class="form-group" [formGroupName]="i">
+ <div class="row">
+ <div class="col-sm-11">
+ <div class="row">
+ <label class="col-sm-2 col-form-label" for="name_{{i}}">{{'NAME' | translate}}*</label>
+ <div class="col-sm-4">
+ <input placeholder="{{'NAME' | translate}}" type="text" class="form-control" formControlName="name" id="name_{{i}}" [ngClass]="{ 'is-invalid': submitted && params.controls.name.errors }">
+ </div>
+ <label class="col-sm-2 col-form-label padLeft0 padRight0" for="ipAddress{{i}}">{{'IPADDRESS' | translate}}*</label>
+ <div class="col-sm-4">
+ <input placeholder="{{'IPADDRESS' | translate}}" type="text" class="form-control" formControlName="ip-address" id="ipAddress{{i}}" [ngClass]="{ 'is-invalid': submitted && params.controls['ip-address'].errors }">
+ <div *ngIf="pduInstancesForm.invalid" class="invalid-feedback">
+ <div *ngIf="params.controls['ip-address'].errors && params.controls['ip-address'].value">{{'DOMVALIDATIONS.INVALIDIPADDRESS' | translate}}</div>
+ </div>
+ </div>
+ </div>
+ <div class="row mr-top-5">
+ <label class="col-sm-2 col-form-label" for="mgmt{{i}}">{{'MGMT' | translate}}*</label>
+ <div class="col-sm-4">
+ <ng-select placeholder="{{'SELECT' | translate}} {{'MGMT' | translate}}" [items]="mgmtState" bindLabel="name" bindValue="value" formControlName="mgmt" id="mgmt{{i}}" [(ngModel)]="selectedMgmt" [ngClass]="{ 'is-invalid': submitted && params.controls.mgmt.errors }"></ng-select>
+ </div>
+ <label class="col-sm-2 col-form-label padLeft0 padRight0" for="vimNetName{{i}}">{{'NETNAME' | translate}}*</label>
+ <div class="col-sm-4">
+ <input placeholder="{{'NETNAME' | translate}}" type="text" class="form-control" formControlName="vim-network-name" id="vimNetName{{i}}" [ngClass]="{ 'is-invalid': submitted && params.controls['vim-network-name'].errors }">
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-1 remove-params" [hidden]="i==0">
+ <button type="button" class="btn btn-sm btn-danger remove-mapping" (click)="removeInterfaces(i)">
+ <i class="fas fa-times-circle"></i>
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'CREATE' | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.scss b/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.scss
new file mode 100644
index 0000000..950bb39
--- /dev/null
+++ b/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.scss
@@ -0,0 +1,34 @@
+/*
+ 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 '../../../../assets/scss/mixins/mixin';
+@import '../../../../assets/scss/variable';
+
+.head {
+ @include padding-value(10, 10, 10, 10);
+ line-height: 2em;
+ .btn-params {
+ @include border(all, 1, solid, $white);
+ }
+}
+
+.remove-params {
+ display: flex;
+ align-items: center;
+ font-size: 20px;
+ cursor: pointer;
+}
\ No newline at end of file
diff --git a/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.ts b/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.ts
new file mode 100644
index 0000000..0dcbb60
--- /dev/null
+++ b/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.ts
@@ -0,0 +1,197 @@
+/*
+ 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 ADD PDU Instances Component
+ */
+import { Component, Injector, Input, OnInit, Output } from '@angular/core';
+import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { PDUInstanceDetails } from 'PDUInstanceModel';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { VimAccountDetails } from 'VimAccountModel';
+
+/**
+ * Creating component
+ * @Component takes AddPDUInstancesComponent.html as template url
+ */
+@Component({
+ templateUrl: './AddPDUInstancesComponent.html',
+ styleUrls: ['./AddPDUInstancesComponent.scss']
+})
+/** Exporting a class @exports AddPDUInstancesComponent */
+export class AddPDUInstancesComponent implements OnInit {
+ /** Form valid on submit trigger @public */
+ public submitted: boolean = false;
+
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** FormGroup instance added to the form @ html @public */
+ public pduInstancesForm: FormGroup;
+
+ /** Primitive params array @public */
+ public pduInterfaces: FormArray;
+
+ /** Variable set for twoway binding @public */
+ public pduInstanceId: string;
+
+ /** Set mgmt field to empty on load @public */
+ public selectedMgmt: string;
+
+ /** Set vim field to empty on load @public */
+ public selectedVIM: string;
+
+ /** Contains boolean value as select options for mgmt @public */
+ public mgmtState: {}[] = [{ name: 'True', value: true }, { name: 'False', value: false }];
+
+ /** Input contains Modal dialog component Instance @private */
+ @Input() public title: string;
+
+ /** Contains all the vim accounts list @public */
+ public vimAccountSelect: VimAccountDetails;
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** Utilizes rest service for any CRUD operations @private */
+ private restService: RestService;
+
+ /** packages data service collections @private */
+ private dataService: DataService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.translateService = this.injector.get(TranslateService);
+ this.notifierService = this.injector.get(NotifierService);
+ this.sharedService = this.injector.get(SharedService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.formBuilder = this.injector.get(FormBuilder);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ /** Setting up initial value for NSD */
+ this.dataService.currentMessage.subscribe((event: PDUInstanceDetails) => {
+ if (event.identifier !== undefined || event.identifier !== '' || event.identifier !== null) {
+ this.pduInstanceId = event.identifier;
+ }
+ });
+ this.generateVIMAccounts();
+ this.initializeForm();
+ }
+
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.pduInstancesForm.controls; }
+
+ /** initialize Forms @public */
+ public initializeForm(): void {
+ this.pduInstancesForm = this.formBuilder.group({
+ name: ['', [Validators.required]],
+ type: ['', [Validators.required]],
+ vim_accounts: ['', [Validators.required]],
+ interfaces: this.formBuilder.array([this.interfacesBuilder()])
+ });
+ }
+
+ /** Generate interfaces fields @public */
+ public interfacesBuilder(): FormGroup {
+ return this.formBuilder.group({
+ name: ['', [Validators.required]],
+ 'ip-address': ['', [Validators.required, Validators.pattern(this.sharedService.REGX_IP_PATTERN)]],
+ mgmt: ['', [Validators.required]],
+ 'vim-network-name': ['', [Validators.required]]
+ });
+ }
+
+ /** Handle FormArray Controls @public */
+ public getControls(): AbstractControl[] {
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ return (this.pduInstancesForm.get('interfaces') as FormArray).controls;
+ }
+
+ /** Push all primitive params on user's action @public */
+ public createInterfaces(): void {
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ this.pduInterfaces = this.pduInstancesForm.get('interfaces') as FormArray;
+ this.pduInterfaces.push(this.interfacesBuilder());
+ }
+
+ /** Remove interfaces on user's action @public */
+ public removeInterfaces(index: number): void {
+ this.pduInterfaces.removeAt(index);
+ }
+
+ /** Execute New PDU Instances @public */
+ public createPDUInstances(): void {
+ this.submitted = true;
+ this.sharedService.cleanForm(this.pduInstancesForm);
+ if (this.pduInstancesForm.invalid) { return; } // Proceed, onces form is valid
+ this.isLoadingResults = true;
+ const modalData: MODALCLOSERESPONSEDATA = {
+ message: 'Done'
+ };
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.PDUINSTANCE_URL
+ };
+ this.restService.postResource(apiURLHeader, this.pduInstancesForm.value).subscribe((result: {}) => {
+ this.activeModal.close(modalData);
+ this.notifierService.notify('success', this.translateService.instant('PAGE.PDUINSTANCE.CREATEDSUCCESSFULLY'));
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'post');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** Generate vim accounts list @public */
+ public generateVIMAccounts(): void {
+ this.restService.getResource(environment.VIMACCOUNTS_URL).subscribe((vimData: VimAccountDetails) => {
+ this.vimAccountSelect = vimData;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+}
diff --git a/src/app/instances/vnf-instances/VNFInstancesComponent.html b/src/app/instances/vnf-instances/VNFInstancesComponent.html
new file mode 100644
index 0000000..e36cc3d
--- /dev/null
+++ b/src/app/instances/vnf-instances/VNFInstancesComponent.html
@@ -0,0 +1,29 @@
+<!--
+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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'VNFINSTANCES' | translate}}</div>
+</div>
+<div class="row mt-2 mb-0 form-group justify-content-end list-utilites-actions">
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/instances/vnf-instances/VNFInstancesComponent.scss b/src/app/instances/vnf-instances/VNFInstancesComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/instances/vnf-instances/VNFInstancesComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/instances/vnf-instances/VNFInstancesComponent.ts b/src/app/instances/vnf-instances/VNFInstancesComponent.ts
new file mode 100644
index 0000000..d829ee2
--- /dev/null
+++ b/src/app/instances/vnf-instances/VNFInstancesComponent.ts
@@ -0,0 +1,187 @@
+/*
+ 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 NVF Instance Component
+ */
+import { Component, Injector, OnInit } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { ERRORDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { LocalDataSource } from 'ng2-smart-table';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+import { VNFInstanceData, VNFInstanceDetails } from 'VNFInstanceModel';
+import { VNFInstancesActionComponent } from 'VNFInstancesActionComponent';
+import { VNFLinkComponent } from 'VNFLinkComponent';
+
+/**
+ * Creating component
+ * @Component takes VNFInstancesComponent.html as template url
+ */
+@Component({
+ templateUrl: './VNFInstancesComponent.html',
+ styleUrls: ['./VNFInstancesComponent.scss']
+})
+/** Exporting a class @exports VNFInstancesComponent */
+export class VNFInstancesComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** smart table data service collections @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** Instance component are stored in settings @public */
+ public settings: {} = {};
+
+ /** Contains objects for menu settings @public */
+ public columnList: {} = {};
+
+ /** vnf instance array @public */
+ public vnfInstanceData: {}[] = [];
+
+ /** selected rows array @public */
+ public selectedRows: string[] = [];
+
+ /** selected list array @public */
+ public selectList: string[] = [];
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** Utilizes rest service for any CRUD operations @private */
+ private restService: RestService;
+
+ /** packages data service collections @private */
+ private dataService: DataService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.sharedService = this.injector.get(SharedService);
+ this.translateService = this.injector.get(TranslateService);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.generateTableColumn();
+ this.generateTableSettings();
+ this.generateData();
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** Generate smart table row title and filters @public */
+ public generateTableSettings(): void {
+ this.settings = {
+ actions: { add: false, edit: false, delete: false, position: 'right' },
+ columns: this.columnList,
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** Generate smart table row title and filters @public */
+ public generateTableColumn(): void {
+ this.columnList = {
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '25%', sortDirection: 'asc' },
+ VNFD: {
+ title: this.translateService.instant('VNFD'), width: '20%', type: 'custom',
+ valuePrepareFunction: (cell: VNFInstanceData, row: VNFInstanceData): VNFInstanceData => row,
+ renderComponent: VNFLinkComponent
+ },
+ MemberIndex: { title: this.translateService.instant('MEMBERINDEX'), width: '15%' },
+ NS: { title: this.translateService.instant('NS'), width: '20%' },
+ CreatedAt: { title: this.translateService.instant('CREATEDAT'), width: '15%' },
+ Actions: {
+ name: 'Action', width: '5%', filter: false, sort: false, type: 'custom',
+ title: this.translateService.instant('ACTIONS'),
+ valuePrepareFunction: (cell: VNFInstanceData, row: VNFInstanceData): VNFInstanceData => row,
+ renderComponent: VNFInstancesActionComponent
+ }
+ };
+ }
+
+ /** generateData initiate the vnf-instance list */
+ public generateData(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.VNFINSTANCES_URL).subscribe((vnfInstancesData: VNFInstanceDetails[]) => {
+ this.vnfInstanceData = [];
+ vnfInstancesData.forEach((vnfInstanceData: VNFInstanceDetails) => {
+ const vnfDataObj: {} =
+ {
+ VNFD: vnfInstanceData['vnfd-ref'],
+ identifier: vnfInstanceData._id,
+ MemberIndex: vnfInstanceData['member-vnf-index-ref'],
+ NS: vnfInstanceData['nsr-id-ref'],
+ VNFID: vnfInstanceData['vnfd-id'],
+ CreatedAt: this.sharedService.convertEpochTime(Number(vnfInstanceData['created-time']))
+ };
+ this.vnfInstanceData.push(vnfDataObj);
+ });
+ if (this.vnfInstanceData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.vnfInstanceData).then((data: {}) => {
+ this.isLoadingResults = false;
+ }).catch();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** smart table listing manipulation @public */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** smart table listing manipulation @public */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'vnf-instance' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+}
diff --git a/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.html b/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.html
new file mode 100644
index 0000000..450e8a3
--- /dev/null
+++ b/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.html
@@ -0,0 +1,18 @@
+<!--
+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)
+-->
+<a class="link" routerLink="/packages/vnf/edit/{{value.VNFID}}">{{value.VNFD}}</a>
diff --git a/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.scss b/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.ts b/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.ts
new file mode 100644
index 0000000..15ea6ff
--- /dev/null
+++ b/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.ts
@@ -0,0 +1,46 @@
+/*
+ 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 VNFD Link Component.
+ */
+import { Component, Injector, OnInit } from '@angular/core';
+import { VNFInstanceData } from 'VNFInstanceModel';
+/**
+ * Creating component
+ * @Component takes VNFLinkComponent.html as template url
+ */
+@Component({
+ selector: 'app-vnf-link',
+ templateUrl: './VNFLinkComponent.html',
+ styleUrls: ['./VNFLinkComponent.scss']
+})
+/** Exporting a class @exports VnfLinkComponent */
+export class VNFLinkComponent implements OnInit {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+ /** To get the value from the VNFInstance via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: VNFInstanceData;
+ constructor(injector: Injector) {
+ this.injector = injector;
+ }
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ //empty
+ }
+
+}
diff --git a/src/app/k8s/K8sComponent.html b/src/app/k8s/K8sComponent.html
new file mode 100644
index 0000000..3f96ff8
--- /dev/null
+++ b/src/app/k8s/K8sComponent.html
@@ -0,0 +1,18 @@
+<!--
+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)
+-->
+<router-outlet></router-outlet>
\ No newline at end of file
diff --git a/src/app/k8s/K8sComponent.scss b/src/app/k8s/K8sComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/k8s/K8sComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/k8s/K8sComponent.ts b/src/app/k8s/K8sComponent.ts
new file mode 100644
index 0000000..5427a8f
--- /dev/null
+++ b/src/app/k8s/K8sComponent.ts
@@ -0,0 +1,56 @@
+/*
+ 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 k8s.ts.
+ */
+import { Component, Injector } from '@angular/core';
+import { Router, RouterEvent } from '@angular/router';
+/**
+ * Creating Component
+ * @Component takes K8sComponent.html as template url
+ */
+@Component({
+ selector: 'app-k8s',
+ templateUrl: './K8sComponent.html',
+ styleUrls: ['./K8sComponent.scss']
+})
+/** Exporting a class @exports K8sComponent */
+export class K8sComponent {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ /** creates k8s component */
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.router = this.injector.get(Router);
+ this.router.events.subscribe((event: RouterEvent) => {
+ this.redirectToList(event.url);
+ });
+ }
+
+ /** Return to list NS Package List */
+ public redirectToList(getURL: string): void {
+ if (getURL === '/k8s') {
+ this.router.navigate(['/k8s/cluster']).catch();
+ }
+ }
+
+}
diff --git a/src/app/k8s/K8sModule.ts b/src/app/k8s/K8sModule.ts
new file mode 100644
index 0000000..51f27b2
--- /dev/null
+++ b/src/app/k8s/K8sModule.ts
@@ -0,0 +1,106 @@
+/*
+ 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 K8s Module.
+ */
+import { CommonModule } from '@angular/common';
+import { HttpClientModule } from '@angular/common/http';
+import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterModule, Routes } from '@angular/router';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { TranslateModule } from '@ngx-translate/core';
+import { DataService } from 'DataService';
+import { K8sActionComponent } from 'K8sActionComponent';
+import { K8sAddClusterComponent } from 'K8sAddClusterComponent';
+import { K8sAddRepoComponent } from 'K8sAddRepoComponent';
+import { K8sClusterComponent } from 'K8sClusterComponent';
+import { K8sComponent } from 'K8sComponent';
+import { K8sRepositoryComponent } from 'K8sRepositoryComponent';
+import { LoaderModule } from 'LoaderModule';
+import { Ng2SmartTableModule } from 'ng2-smart-table';
+import { PagePerRowModule } from 'PagePerRowModule';
+import { PageReloadModule } from 'PageReloadModule';
+
+/** To halndle project information */
+const projectInfo: {} = { title: '{project}', url: '/' };
+
+/**
+ * configures routers
+ */
+const routes: Routes = [
+ {
+ path: '',
+ component: K8sComponent,
+ children: [
+ {
+ path: 'cluster',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'PAGE.K8S.MENUK8SCLUSTER', url: null }]
+ },
+ component: K8sClusterComponent
+ },
+ {
+ path: 'repo',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'PAGE.K8S.MENUK8SREPO', url: null }]
+ },
+ component: K8sRepositoryComponent
+ }
+ ]
+ }
+];
+/**
+ * Creating @NgModule component for Modules
+ */
+@NgModule({
+ imports: [
+ ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }),
+ FormsModule,
+ CommonModule,
+ HttpClientModule,
+ NgSelectModule,
+ Ng2SmartTableModule,
+ TranslateModule,
+ RouterModule.forChild(routes),
+ NgbModule,
+ PagePerRowModule,
+ LoaderModule,
+ PageReloadModule
+ ],
+ declarations: [
+ K8sComponent,
+ K8sClusterComponent,
+ K8sRepositoryComponent,
+ K8sActionComponent,
+ K8sAddClusterComponent,
+ K8sAddRepoComponent
+ ],
+ providers: [DataService],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
+ entryComponents: [K8sActionComponent, K8sAddClusterComponent, K8sAddRepoComponent]
+})
+/** Exporting a class @exports K8sModule */
+export class K8sModule {
+ /** Variables declared to avoid state-less class */
+ private k8sModule: string;
+}
diff --git a/src/app/k8s/k8s-action/K8sActionComponent.html b/src/app/k8s/k8s-action/K8sActionComponent.html
new file mode 100644
index 0000000..c35bb19
--- /dev/null
+++ b/src/app/k8s/k8s-action/K8sActionComponent.html
@@ -0,0 +1,27 @@
+<!--
+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)
+-->
+<div class="btn-group list action" role="group">
+ <button type="button" class="btn btn-primary" (click)="infoK8s(getK8sType)" placement="top" container="body"
+ ngbTooltip="{{'INFO' | translate}}">
+ <i class="fas fa-info icons list" title="info"></i>
+ </button>
+ <button type="button" class="btn btn-primary" (click)="deleteK8s(getK8sType)" placement="top"
+ container="body" ngbTooltip="{{'DELETE' | translate}}">
+ <i class="far fa-trash-alt icons" title="delete"></i>
+ </button>
+</div>
\ No newline at end of file
diff --git a/src/app/k8s/k8s-action/K8sActionComponent.scss b/src/app/k8s/k8s-action/K8sActionComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/k8s/k8s-action/K8sActionComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/k8s/k8s-action/K8sActionComponent.ts b/src/app/k8s/k8s-action/K8sActionComponent.ts
new file mode 100644
index 0000000..a524277
--- /dev/null
+++ b/src/app/k8s/k8s-action/K8sActionComponent.ts
@@ -0,0 +1,103 @@
+/*
+ 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 K8 Action Component
+ */
+import { Component, Injector } from '@angular/core';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DeleteComponent } from 'DeleteComponent';
+import { K8SCLUSTERDATADISPLAY, K8SREPODATADISPLAY } from 'K8sModel';
+import { SharedService } from 'SharedService';
+import { ShowInfoComponent } from 'ShowInfoComponent';
+/**
+ * Creating component
+ * @Component takes K8sActionComponent.html as template url
+ */
+@Component({
+ selector: 'app-k8s-action',
+ templateUrl: './K8sActionComponent.html',
+ styleUrls: ['./K8sActionComponent.scss']
+})
+/** Exporting a class @exports K8sActionComponent */
+export class K8sActionComponent{
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** To get the value from the Users action via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: K8SCLUSTERDATADISPLAY | K8SREPODATADISPLAY;
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Contains K8s Type @private */
+ public getK8sType: string;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Contains instance ID @private */
+ private instanceID: string;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.modalService = this.injector.get(NgbModal);
+ this.sharedService = this.injector.get(SharedService);
+ this.translateService = this.injector.get(TranslateService);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.instanceID = this.value.identifier;
+ this.getK8sType = this.value.pageType;
+ }
+
+ /** Delete User Account @public */
+ public deleteK8s(pageType: string): void {
+ const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' });
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /** Shows information using modalservice @public */
+ public infoK8s(pageType: string): void {
+ let pageName: string = '';
+ let title: string = '';
+ if (pageType === 'repo') {
+ pageName = 'k8s-repo';
+ title = 'PAGE.K8S.K8SREPODETAILS';
+ } else {
+ pageName = 'k8s-cluster';
+ title = 'PAGE.K8S.K8SCLUSTERDETAILS';
+ }
+ this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = {
+ id: this.instanceID,
+ page: pageName,
+ titleName: title
+ };
+ }
+}
diff --git a/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.html b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.html
new file mode 100644
index 0000000..84f2150
--- /dev/null
+++ b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.html
@@ -0,0 +1,92 @@
+<!--
+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)
+-->
+<form [formGroup]="k8sclusterForm" (ngSubmit)="k8sAddClusterSubmit();">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'PAGE.K8S.NEWK8SCLUSTER' | translate}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </div>
+ <div class="modal-body modal-body-custom-height">
+ <div class="form-group row">
+ <label class="col-sm-12 col-form-label mandatory-label"
+ [ngClass]="{'text-danger': k8sclusterForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <label class="col-sm-4 col-form-label" for="name">{{'PAGE.K8S.NAME' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control" placeholder="{{'PAGE.K8S.NAME' | translate}}" type="text"
+ formControlName="name" id="name" [ngClass]="{ 'is-invalid': submitted && f.name.errors }" required>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="k8s_version">{{'PAGE.K8S.K8SVERSION' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control" placeholder="{{'PAGE.K8S.K8SVERSION' | translate}}" type="text"
+ formControlName="k8s_version" id="k8s_version" [ngClass]="{ 'is-invalid': submitted && f.k8s_version.errors }"
+ required>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="vim_account">{{'PAGE.K8S.VIMACCOUNT' | translate}}*</label>
+ <div class="col-sm-8">
+ <ng-select placeholder="{{'SELECT' | translate}} {{'PAGE.K8S.VIMACCOUNT' | translate}}"
+ [items]="vimAccountSelect" bindLabel="name" bindValue="_id" formControlName="vim_account" id="vimAccountId"
+ [ngClass]="{ 'is-invalid': submitted && f.vim_account.errors }" required>
+ </ng-select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="description">{{'PAGE.K8S.DESCRIPTION' | translate}}*</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'PAGE.K8S.DESCRIPTION' | translate}}" type="text"
+ formControlName="description" id="description" [ngClass]="{ 'is-invalid': submitted && f.description.errors }"
+ required></textarea>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="nets">{{'PAGE.K8S.NETS' | translate}}*</label>
+ <div class="col-sm-8">
+ <textarea rows="5" cols="50" class="form-control" placeholder="{{'PAGE.K8S.NETSPLACEHOLDER' | translate}}"
+ formControlName="nets" id="nets" [ngClass]="{ 'is-invalid': submitted && f.nets.errors }" required></textarea>
+ <div class="fileupload-text mt-1 mb-1">{{'FILEUPLOADLABEL' | translate}}</div>
+ <div class="custom-file">
+ <input type="file" #fileInputNets class="custom-file-input" (change)="netsFile($event.target.files)"
+ id="customFileNets">
+ <label class="custom-file-label" #fileInputNetsLabel for="customFileNets">{{'CHOOSEFILE' | translate}}</label>
+ </div>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="credentials">{{'PAGE.K8S.CREDENTIALS' | translate}}*</label>
+ <div class="col-sm-8">
+ <textarea rows="5" cols="50" class="form-control" placeholder="{{'YAMLCONFIG' | translate}}" formControlName="credentials"
+ id="credentials" [ngClass]="{ 'is-invalid': submitted && f.credentials.errors }" required></textarea>
+ <div class="fileupload-text mt-1 mb-1">{{'FILEUPLOADLABEL' | translate}}</div>
+ <div class="custom-file">
+ <input type="file" #fileInputCredentials class="custom-file-input" (change)="credentialsFile($event.target.files)"
+ id="customFileCredentials">
+ <label class="custom-file-label" #fileInputCredentialsLabel for="customFileCredentials">{{'CHOOSEFILE' | translate}}</label>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'CREATE' | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.scss b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.ts b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.ts
new file mode 100644
index 0000000..0295b35
--- /dev/null
+++ b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.ts
@@ -0,0 +1,233 @@
+/*
+ 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 K8sAddClusterComponent.ts.
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { environment } from 'environment';
+import * as jsyaml from 'js-yaml';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+import { VimAccountDetails } from 'VimAccountModel';
+/**
+ * Creating Component
+ * @Component takes K8sAddClusterComponent.html as template url
+ */
+@Component({
+ selector: 'app-k8s-add-cluster',
+ templateUrl: './K8sAddClusterComponent.html',
+ styleUrls: ['./K8sAddClusterComponent.scss']
+})
+/** Exporting a class @exports K8sAddClusterComponent */
+export class K8sAddClusterComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** FormGroup instance added to the form @ html @public */
+ public k8sclusterForm: FormGroup;
+
+ /** Contains all vim account collections */
+ public vimAccountSelect: VimAccountDetails;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Variable set for twoway bindng @public */
+ public vimAccountId: string;
+
+ /** Form submission Add */
+ public submitted: boolean = false;
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Element ref for fileInputNets @public */
+ @ViewChild('fileInputNets', { static: true }) public fileInputNets: ElementRef;
+
+ /** Element ref for fileInputNetsLabel @public */
+ @ViewChild('fileInputNetsLabel', { static: true }) public fileInputNetsLabel: ElementRef;
+
+ /** Element ref for fileInputCredentials @public */
+ @ViewChild('fileInputCredentials', { static: true }) public fileInputCredentials: ElementRef;
+
+ /** Element ref for fileInputCredentialsLabel @public */
+ @ViewChild('fileInputCredentialsLabel', { static: true }) public fileInputCredentialsLabel: ElementRef;
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** Utilizes rest service for any CRUD operations @private */
+ private restService: RestService;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.formBuilder = this.injector.get(FormBuilder);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ public ngOnInit(): void {
+ /** On Initializing call the methods */
+ this.k8sclusterFormAction();
+ this.getDetailsvimAccount();
+ this.headers = new HttpHeaders({
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ }
+
+ /** On modal initializing forms @public */
+ public k8sclusterFormAction(): void {
+ this.k8sclusterForm = this.formBuilder.group({
+ name: ['', [Validators.required]],
+ k8s_version: ['', [Validators.required]],
+ vim_account: [null, [Validators.required]],
+ description: ['', [Validators.required]],
+ nets: ['', [Validators.required]],
+ credentials: ['', [Validators.required]]
+ });
+ }
+
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.k8sclusterForm.controls; }
+
+ /** Call the vimAccount details in the selection options @public */
+ public getDetailsvimAccount(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.VIMACCOUNTS_URL).subscribe((vimAccounts: VimAccountDetails) => {
+ this.vimAccountSelect = vimAccounts;
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** On modal submit k8sAddClusterSubmit will called @public */
+ public k8sAddClusterSubmit(): void {
+ this.submitted = true;
+ this.sharedService.cleanForm(this.k8sclusterForm);
+ if (this.k8sclusterForm.invalid) {
+ return;
+ }
+ const modalData: MODALCLOSERESPONSEDATA = {
+ message: 'Done'
+ };
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.K8SCLUSTER_URL,
+ httpOptions: { headers: this.headers }
+ };
+ const validJSONCredentails: boolean = this.sharedService.checkJson(this.k8sclusterForm.value.credentials);
+ if (validJSONCredentails) {
+ this.k8sclusterForm.value.credentials = jsyaml.load(this.k8sclusterForm.value.credentials.toString(), { json: true });
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('INVALIDCONFIG'));
+ return;
+ }
+ const validJSONNets: boolean = this.sharedService.checkJson(this.k8sclusterForm.value.nets);
+ if (validJSONNets) {
+ this.k8sclusterForm.value.nets = jsyaml.load(this.k8sclusterForm.value.nets.toString(), { json: true });
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('INVALIDCONFIG'));
+ return;
+ }
+ this.isLoadingResults = true;
+ this.restService.postResource(apiURLHeader, this.k8sclusterForm.value).subscribe((result: {}) => {
+ this.activeModal.close(modalData);
+ this.isLoadingResults = false;
+ this.notifierService.notify('success', this.k8sclusterForm.value.name +
+ this.translateService.instant('PAGE.K8S.CREATEDSUCCESSFULLY'));
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'post');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** Nets file process @private */
+ public netsFile(files: FileList): void {
+ if (files && files.length === 1) {
+ this.sharedService.getFileString(files, 'json').then((fileContent: string): void => {
+ const getNetsJson: string = jsyaml.load(fileContent, { json: true });
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.k8sclusterForm.get('nets').setValue(JSON.stringify(getNetsJson));
+ }).catch((err: string): void => {
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('JSONFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ this.fileInputNetsLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
+ this.fileInputNets.nativeElement.value = null;
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ this.fileInputNetsLabel.nativeElement.innerText = files[0].name;
+ this.fileInputNets.nativeElement.value = null;
+ }
+
+ /** credentials file process @private */
+ public credentialsFile(files: FileList): void {
+ if (files && files.length === 1) {
+ this.sharedService.getFileString(files, 'yaml').then((fileContent: string): void => {
+ const getCredentialsJson: string = jsyaml.load(fileContent, { json: true });
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.k8sclusterForm.get('credentials').setValue(JSON.stringify(getCredentialsJson));
+ }).catch((err: string): void => {
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ this.fileInputCredentialsLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
+ this.fileInputCredentials.nativeElement.value = null;
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ this.fileInputCredentialsLabel.nativeElement.innerText = files[0].name;
+ this.fileInputCredentials.nativeElement.value = null;
+ }
+
+}
diff --git a/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.html b/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.html
new file mode 100644
index 0000000..8caec5e
--- /dev/null
+++ b/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.html
@@ -0,0 +1,68 @@
+<!--
+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)
+-->
+<form [formGroup]="k8srepoForm" (ngSubmit)="k8sAddRepoSubmit();">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'PAGE.K8S.NEWK8SREPO' | translate}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </div>
+ <div class="modal-body k8saddrepo">
+ <div class="form-group row">
+ <label class="col-sm-12 col-form-label mandatory-label"
+ [ngClass]="{'text-danger': k8srepoForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <label class="col-sm-4 col-form-label" for="name">{{'PAGE.K8S.NAME' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control" placeholder="{{'PAGE.K8S.NAME' | translate}}" type="text"
+ formControlName="name" id="name" [ngClass]="{ 'is-invalid': submitted && f.name.errors }" required>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="type">{{'PAGE.K8S.TYPE' | translate}}*</label>
+ <div class="col-sm-8">
+ <ng-select [items]="repoTypeSelect" bindLabel="name" bindValue="id"
+ placeholder="{{'SELECT' | translate}} {{'PAGE.K8S.TYPE' | translate}}" formControlName="type" id="type"
+ [ngClass]="{ 'is-invalid': submitted && f.type.errors }" required>
+ </ng-select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="url">{{'PAGE.K8S.URL' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control" placeholder="{{'PAGE.K8S.URL' | translate}}" type="url"
+ formControlName="url" id="url" [ngClass]="{ 'is-invalid': submitted && f.url.errors }" required>
+ <div *ngIf="k8srepoForm.invalid" class="invalid-feedback">
+ <div *ngIf="f.url.errors && f.url.value">{{'DOMVALIDATIONS.INVALIDURL' | translate}}</div>
+ </div>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="description">{{'PAGE.K8S.DESCRIPTION' | translate}}*</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'PAGE.K8S.DESCRIPTION' | translate}}" type="text"
+ formControlName="description" id="description" [ngClass]="{ 'is-invalid': submitted && f.description.errors }"
+ required></textarea>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'CREATE' | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.scss b/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.scss
new file mode 100644
index 0000000..fdec4ed
--- /dev/null
+++ b/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+ */
diff --git a/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.ts b/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.ts
new file mode 100644
index 0000000..c8b96bb
--- /dev/null
+++ b/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.ts
@@ -0,0 +1,134 @@
+/*
+ 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 k8sAddRepoComponent.ts.
+ */
+import { Component, Injector, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { environment } from 'environment';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+/**
+ * Creating Component
+ * @Component takes K8sAddRepoComponent.html as template url
+ */
+@Component({
+ selector: 'app-k8s-add-repo',
+ templateUrl: './K8sAddRepoComponent.html',
+ styleUrls: ['./K8sAddRepoComponent.scss']
+})
+/** Exporting a class @exports K8sAddRepoComponent */
+export class K8sAddRepoComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** FormGroup instance added to the form @ html @public */
+ public k8srepoForm: FormGroup;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Form submission Add */
+ public submitted: boolean = false;
+
+ /** Supported Vim type for the dropdown */
+ public repoTypeSelect: {}[];
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** Utilizes rest service for any CRUD operations @private */
+ private restService: RestService;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.formBuilder = this.injector.get(FormBuilder);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ public ngOnInit(): void {
+ this.repoTypeSelect = [
+ { id: 'helm-chart', name: 'Helm Chart' },
+ { id: 'juju-bundle', name: 'Juju Bundle' }
+ ];
+ /** On Initializing call the methods */
+ this.k8srepoFormAction();
+ }
+
+ /** On modal initializing forms @public */
+ public k8srepoFormAction(): void {
+ this.k8srepoForm = this.formBuilder.group({
+ name: ['', [Validators.required]],
+ type: [null, [Validators.required]],
+ url: ['', [Validators.required, Validators.pattern(this.sharedService.REGX_URL_PATTERN)]],
+ description: ['', [Validators.required]]
+ });
+ }
+
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.k8srepoForm.controls; }
+
+ /** On modal submit k8sAddRepoSubmit will called @public */
+ public k8sAddRepoSubmit(): void {
+ this.submitted = true;
+ this.sharedService.cleanForm(this.k8srepoForm);
+ if (this.k8srepoForm.invalid) {
+ return;
+ }
+ this.isLoadingResults = true;
+ const modalData: MODALCLOSERESPONSEDATA = {
+ message: 'Done'
+ };
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.K8REPOS_URL
+ };
+ this.restService.postResource(apiURLHeader, this.k8srepoForm.value).subscribe((result: {}) => {
+ this.activeModal.close(modalData);
+ this.notifierService.notify('success', this.k8srepoForm.value.name +
+ this.translateService.instant('PAGE.K8S.CREATEDSUCCESSFULLY'));
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'post');
+ });
+ }
+
+}
diff --git a/src/app/k8s/k8scluster/K8sClusterComponent.html b/src/app/k8s/k8scluster/K8sClusterComponent.html
new file mode 100644
index 0000000..ebed3e4
--- /dev/null
+++ b/src/app/k8s/k8scluster/K8sClusterComponent.html
@@ -0,0 +1,42 @@
+<!--
+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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'PAGE.K8S.REGISTERK8CLUSTER' | translate}}</div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" placement="top" container="body" ngbTooltip="{{'PAGE.K8S.ADDK8CLUSTER' | translate}}"
+ (click)="addK8sCluster()">
+ <i class="fas fa-plus-circle" aria-hidden="true"></i> {{'PAGE.K8S.ADDK8CLUSTER' | translate}}
+ </button>
+ </span>
+</div>
+<div class="row mt-2 mb-0 list-utilites-actions">
+ <div class="col-auto mr-auto">
+ <nav class="custom-items-config">
+ <span><i class="fas fa-clock text-warning"></i>{{operationalStateFirstStep}}</span>
+ <span><i class="fas fa-check-circle text-success"></i>{{operationalStateSecondStep}}</span>
+ <span><i class="fas fa-times-circle text-danger"></i>{{operationalStateThirdStep}}</span>
+ </nav>
+ </div>
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/k8s/k8scluster/K8sClusterComponent.scss b/src/app/k8s/k8scluster/K8sClusterComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/k8s/k8scluster/K8sClusterComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/k8s/k8scluster/K8sClusterComponent.ts b/src/app/k8s/k8scluster/K8sClusterComponent.ts
new file mode 100644
index 0000000..7ab7583
--- /dev/null
+++ b/src/app/k8s/k8scluster/K8sClusterComponent.ts
@@ -0,0 +1,236 @@
+/*
+ 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 k8sclustercomponent.ts.
+ */
+import { Component, Injector, OnDestroy, OnInit } from '@angular/core';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { CONFIGCONSTANT, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { K8sActionComponent } from 'K8sActionComponent';
+import { K8sAddClusterComponent } from 'K8sAddClusterComponent';
+import { K8SCLUSTERDATA, K8SCLUSTERDATADISPLAY } from 'K8sModel';
+import { LocalDataSource } from 'ng2-smart-table';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+/**
+ * Creating Component
+ * @Component takes K8sClusterComponent.html as template url
+ */
+@Component({
+ selector: 'app-k8scluster',
+ templateUrl: './K8sClusterComponent.html',
+ styleUrls: ['./K8sClusterComponent.scss']
+})
+/** Exporting a class @exports K8sClusterComponent */
+export class K8sClusterComponent implements OnInit, OnDestroy {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Data of smarttable populate through LocalDataSource @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** Columns list of the smart table @public */
+ public columnList: object = {};
+
+ /** Settings for smarttable to populate the table with columns @public */
+ public settings: object = {};
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** operational State init data @public */
+ public operationalStateFirstStep: string = CONFIGCONSTANT.k8OperationalStateFirstStep;
+
+ /** operational State running data @public */
+ public operationalStateSecondStep: string = CONFIGCONSTANT.k8OperationalStateStateSecondStep;
+
+ /** operational State failed data @public */
+ public operationalStateThirdStep: string = CONFIGCONSTANT.k8OperationalStateThirdStep;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Formation of appropriate Data for LocalDatasource @private */
+ private k8sClusterData: {}[] = [];
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.sharedService = this.injector.get(SharedService);
+ this.translateService = this.injector.get(TranslateService);
+ this.modalService = this.injector.get(NgbModal);
+ }
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.generateColumns();
+ this.generateSettings();
+ this.generateData();
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** smart table Header Colums @public */
+ public generateColumns(): void {
+ this.columnList = {
+ name: { title: this.translateService.instant('NAME'), width: '20%', sortDirection: 'asc' },
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' },
+ version: { title: this.translateService.instant('K8VERSION'), width: '10%' },
+ operationalState: {
+ title: this.translateService.instant('OPERATIONALSTATE'), width: '15%', type: 'html',
+ filter: {
+ type: 'list',
+ config: {
+ selectText: 'Select',
+ list: [
+ { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep },
+ { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep },
+ { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep }
+ ]
+ }
+ },
+ valuePrepareFunction: (cell: K8SCLUSTERDATADISPLAY, row: K8SCLUSTERDATADISPLAY): string => {
+ if (row.operationalState === this.operationalStateFirstStep) {
+ return `<span class="icon-label" title="${row.operationalState}">
+ <i class="fas fa-clock text-warning"></i>
+ </span>`;
+ } else if (row.operationalState === this.operationalStateSecondStep) {
+ return `<span class="icon-label" title="${row.operationalState}">
+ <i class="fas fa-check-circle text-success"></i>
+ </span>`;
+ } else if (row.operationalState === this.operationalStateThirdStep) {
+ return `<span class="icon-label" title="${row.operationalState}">
+ <i class="fas fa-times-circle text-danger"></i>
+ </span>`;
+ } else {
+ return `<span>${row.operationalState}</span>`;
+ }
+ }
+ },
+ created: { title: this.translateService.instant('CREATED'), width: '15%' },
+ modified: { title: this.translateService.instant('MODIFIED'), width: '15%' },
+ Actions: {
+ name: 'Action', width: '5%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom',
+ valuePrepareFunction: (cell: K8SCLUSTERDATADISPLAY, row: K8SCLUSTERDATADISPLAY): K8SCLUSTERDATADISPLAY => row,
+ renderComponent: K8sActionComponent
+ }
+ };
+ }
+
+ /** smart table Data Settings @public */
+ public generateSettings(): void {
+ this.settings = {
+ columns: this.columnList,
+ actions: { add: false, edit: false, delete: false, position: 'right' },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** smart table listing manipulation @public */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** smart table listing manipulation @public */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'k8-cluster' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Compose new K8s Cluster Accounts @public */
+ public addK8sCluster(): void {
+ const modalRef: NgbModalRef = this.modalService.open(K8sAddClusterComponent, { backdrop: 'static' });
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+
+ /** Generate nsData object from loop and return for the datasource @public */
+ public generateK8sclusterData(k8sClusterdata: K8SCLUSTERDATA): K8SCLUSTERDATADISPLAY {
+ return {
+ name: k8sClusterdata.name,
+ identifier: k8sClusterdata._id,
+ operationalState: k8sClusterdata._admin.operationalState,
+ version: k8sClusterdata.k8s_version,
+ created: this.sharedService.convertEpochTime(Number(k8sClusterdata._admin.created)),
+ modified: this.sharedService.convertEpochTime(Number(k8sClusterdata._admin.modified)),
+ pageType: 'cluster'
+ };
+ }
+
+ /** Fetching the data from server to Load in the smarttable @protected */
+ protected generateData(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.K8SCLUSTER_URL).subscribe((k8sClusterDatas: K8SCLUSTERDATA[]) => {
+ this.k8sClusterData = [];
+ k8sClusterDatas.forEach((k8sClusterdata: K8SCLUSTERDATA) => {
+ const k8sClusterDataObj: K8SCLUSTERDATADISPLAY = this.generateK8sclusterData(k8sClusterdata);
+ this.k8sClusterData.push(k8sClusterDataObj);
+ });
+ if (this.k8sClusterData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.k8sClusterData).then((data: boolean) => {
+ this.isLoadingResults = false;
+ }).catch(() => {
+ this.isLoadingResults = false;
+ });
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+
+}
diff --git a/src/app/k8s/k8srepository/K8sRepositoryComponent.html b/src/app/k8s/k8srepository/K8sRepositoryComponent.html
new file mode 100644
index 0000000..e546356
--- /dev/null
+++ b/src/app/k8s/k8srepository/K8sRepositoryComponent.html
@@ -0,0 +1,35 @@
+<!--
+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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'PAGE.K8S.REGISTERK8REPO' | translate}}</div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" placement="top" container="body" ngbTooltip="{{'PAGE.K8S.ADDK8REPO' | translate}}"
+ (click)="addK8sRepo()">
+ <i class="fas fa-plus-circle" aria-hidden="true"></i> {{'PAGE.K8S.ADDK8REPO' | translate}}
+ </button>
+ </span>
+</div>
+<div class="row mt-2 mb-0 form-group justify-content-end list-utilites-actions">
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/k8s/k8srepository/K8sRepositoryComponent.scss b/src/app/k8s/k8srepository/K8sRepositoryComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/k8s/k8srepository/K8sRepositoryComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/k8s/k8srepository/K8sRepositoryComponent.ts b/src/app/k8s/k8srepository/K8sRepositoryComponent.ts
new file mode 100644
index 0000000..1b6c9f7
--- /dev/null
+++ b/src/app/k8s/k8srepository/K8sRepositoryComponent.ts
@@ -0,0 +1,198 @@
+/*
+ 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 K8sRepositoryComponent.ts.
+ */
+import { Component, Injector, OnDestroy, OnInit } from '@angular/core';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { K8sActionComponent } from 'K8sActionComponent';
+import { K8sAddRepoComponent } from 'K8sAddRepoComponent';
+import { K8SREPODATA, K8SREPODATADISPLAY } from 'K8sModel';
+import { LocalDataSource } from 'ng2-smart-table';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+/**
+ * Creating Component
+ * @Component takes K8sRepositoryComponent.html as template url
+ */
+@Component({
+ selector: 'app-k8srepository',
+ templateUrl: './K8sRepositoryComponent.html',
+ styleUrls: ['./K8sRepositoryComponent.scss']
+})
+/** Exporting a class @exports K8sRepositoryComponent */
+export class K8sRepositoryComponent implements OnInit, OnDestroy {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Data of smarttable populate through LocalDataSource @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** Columns list of the smart table @public */
+ public columnList: object = {};
+
+ /** Settings for smarttable to populate the table with columns @public */
+ public settings: object = {};
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Formation of appropriate Data for LocalDatasource @private */
+ private k8sRepoData: {}[] = [];
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.sharedService = this.injector.get(SharedService);
+ this.translateService = this.injector.get(TranslateService);
+ this.modalService = this.injector.get(NgbModal);
+ }
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.generateColumns();
+ this.generateSettings();
+ this.generateData();
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** smart table Header Colums @public */
+ public generateColumns(): void {
+ this.columnList = {
+ name: { title: this.translateService.instant('NAME'), width: '20%', sortDirection: 'asc' },
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' },
+ url: { title: this.translateService.instant('URL'), width: '15%' },
+ type: { title: this.translateService.instant('TYPE'), width: '10%' },
+ created: { title: this.translateService.instant('CREATED'), width: '15%' },
+ modified: { title: this.translateService.instant('MODIFIED'), width: '15%' },
+ Actions: {
+ name: 'Action', width: '5%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom',
+ valuePrepareFunction: (cell: K8SREPODATADISPLAY, row: K8SREPODATADISPLAY): K8SREPODATADISPLAY => row,
+ renderComponent: K8sActionComponent
+ }
+ };
+ }
+
+ /** smart table Data Settings @public */
+ public generateSettings(): void {
+ this.settings = {
+ columns: this.columnList,
+ actions: { add: false, edit: false, delete: false, position: 'right' },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** smart table listing manipulation @public */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** smart table listing manipulation @public */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'k8-repo' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Compose new K8s Repo Accounts @public */
+ public addK8sRepo(): void {
+ const modalRef: NgbModalRef = this.modalService.open(K8sAddRepoComponent, { backdrop: 'static' });
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+
+ /** Generate nsData object from loop and return for the datasource @public */
+ // tslint:disable-next-line: typedef
+ public generateK8sRepoData(k8sRepodata: K8SREPODATA): K8SREPODATADISPLAY {
+ return {
+ name: k8sRepodata.name,
+ identifier: k8sRepodata._id,
+ url: k8sRepodata.url,
+ type: k8sRepodata.type,
+ created: this.sharedService.convertEpochTime(Number(k8sRepodata._admin.created)),
+ modified: this.sharedService.convertEpochTime(Number(k8sRepodata._admin.modified)),
+ pageType: 'repo'
+ };
+ }
+
+ /** Fetching the data from server to Load in the smarttable @protected */
+ protected generateData(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.K8REPOS_URL).subscribe((k8sRepoDatas: K8SREPODATA[]) => {
+ this.k8sRepoData = [];
+ k8sRepoDatas.forEach((k8sRepodata: K8SREPODATA) => {
+ const k8sRepoDataObj: K8SREPODATADISPLAY = this.generateK8sRepoData(k8sRepodata);
+ this.k8sRepoData.push(k8sRepoDataObj);
+ });
+ if (this.k8sRepoData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.k8sRepoData).then((data: boolean) => {
+ this.isLoadingResults = false;
+ }).catch(() => {
+ this.isLoadingResults = false;
+ });
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+
+}
diff --git a/src/app/layouts/LayoutComponent.html b/src/app/layouts/LayoutComponent.html
new file mode 100644
index 0000000..96f1302
--- /dev/null
+++ b/src/app/layouts/LayoutComponent.html
@@ -0,0 +1,37 @@
+<!--
+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)
+-->
+<app-header></app-header>
+<div id="main-wrapper" class="container-fluid" dir="ltr" appGottoTop>
+ <div class="layout-wrapper mt-2">
+ <app-sidebar></app-sidebar>
+ <div class="content-section pl-4">
+ <app-breadcrumb></app-breadcrumb>
+ <div class="mt-2">
+ <div class="card custom-card shadow">
+ <div class="card-body custom-card-body">
+ <router-outlet></router-outlet>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <button class="btn btn-sm btn-primary border border-radius-default goto-top" type="button"
+ *ngIf="sharedService.showGotoTop" (click)="gotoTop()">
+ <i class="fas fa-angle-up"></i>
+ </button>
+</div>
\ No newline at end of file
diff --git a/src/app/layouts/LayoutComponent.scss b/src/app/layouts/LayoutComponent.scss
new file mode 100644
index 0000000..649387a
--- /dev/null
+++ b/src/app/layouts/LayoutComponent.scss
@@ -0,0 +1,36 @@
+/*
+ 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 "../../assets/scss/mixins/mixin";
+@import "../../assets/scss/variable";
+#main-wrapper {
+ @include wh-value(100%, null);
+ .layout-wrapper {
+ @include flexbox(flex, null, null, null, stretch, null);
+ .content-section {
+ @include wh-value(100%, null);
+ @include transition(all, 0.3s, null, null);
+ overflow-x: auto;
+ }
+ }
+ .goto-top {
+ @include position_value(fixed, null, 30px, 10px, null);
+ @include font(null, 20px, null);
+ outline: none;
+ @include wh-value(40px, 40px);
+ }
+}
\ No newline at end of file
diff --git a/src/app/layouts/LayoutComponent.ts b/src/app/layouts/LayoutComponent.ts
new file mode 100644
index 0000000..b99221a
--- /dev/null
+++ b/src/app/layouts/LayoutComponent.ts
@@ -0,0 +1,44 @@
+/*
+ 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 NS Instance Component
+ */
+import { Component } from '@angular/core';
+import { SharedService } from 'SharedService';
+/**
+ * Creating component
+ * @Component takes LayoutComponent.html as template url
+ */
+@Component({
+ selector: 'app-layout',
+ templateUrl: './LayoutComponent.html',
+ styleUrls: ['./LayoutComponent.scss']
+})
+/** Exporting a class @exports LayoutComponent */
+export class LayoutComponent {
+ /** Contains all methods related to shared @private */
+ public sharedService: SharedService;
+
+ constructor(sharedService: SharedService) {
+ this.sharedService = sharedService;
+ }
+ /** method to handle go to top event @public */
+ public gotoTop(): void {
+ window.scroll({ behavior: 'smooth', top: 0, left: 0 });
+ }
+}
diff --git a/src/app/layouts/breadcrumb/BreadcrumbComponent.html b/src/app/layouts/breadcrumb/BreadcrumbComponent.html
new file mode 100644
index 0000000..9d028b3
--- /dev/null
+++ b/src/app/layouts/breadcrumb/BreadcrumbComponent.html
@@ -0,0 +1,27 @@
+<!--
+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)
+-->
+<nav aria-label="breadcrumb" class="breadcrumb-holder">
+ <ul class="breadcrumb-custom pl-0">
+ <li class="breadcrumb-item-custom" [routerLink]="item.url" *ngFor="let item of menuItems; let i = index" [class.active]="i===menuItems.length-1">
+ <a *ngIf="i!==menuItems.length-1">
+ <i *ngIf="i==0" class="fas fa-th-large"></i> {{item.title | translate}}
+ </a>
+ <span *ngIf="i===menuItems.length-1"><i *ngIf="i==0" class="fas fa-th-large"></i> {{item.title | translate}}</span>
+ </li>
+ </ul>
+</nav>
\ No newline at end of file
diff --git a/src/app/layouts/breadcrumb/BreadcrumbComponent.scss b/src/app/layouts/breadcrumb/BreadcrumbComponent.scss
new file mode 100644
index 0000000..7ed3da6
--- /dev/null
+++ b/src/app/layouts/breadcrumb/BreadcrumbComponent.scss
@@ -0,0 +1,81 @@
+/*
+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 "../../../assets/scss/mixins/mixin";
+@import "../../../assets/scss/variable";
+.breadcrumb-holder {
+ .breadcrumb-custom {
+ @include box-shadow(0, 2px, 5px, 0, rgba(0, 0, 0, 0.25));
+ @include roundedCorners(5);
+ @include background(null, $white, null, null, null);
+ @include margin-value(0, 0, 0, 0);
+ @include flexbox(inline-block, null, null, null, null, null);
+ overflow: hidden;
+ .breadcrumb-item-custom {
+ @include flexbox(block, null, null, null, null, null);
+ @include line-height(35px);
+ @include font(null, 12px, null);
+ @include padding-value(0, 10, 0, 30);
+ @include position_value(relative, null, null, null, null);
+ float: left;
+ cursor: pointer;
+ &:hover {
+ @include background(null, $secondary, null, null, null);
+ a {
+ color: $white;
+ }
+ &::after {
+ @include background(null, $secondary, null, null, null);
+ }
+ }
+ a {
+ color: $primary;
+ }
+ &:first-child {
+ padding-left: 20px;
+ @include roundedTopLeftRadius(5);
+ @include roundedBottomLeftRadius(5);
+ &::before {
+ @include position_value(null, null, null, null, 14px);
+ }
+ }
+ &:last-child {
+ @include roundedTopRightRadius(5);
+ @include roundedBottomRightRadius(5);
+ padding-right: 20px;
+ &::after {
+ content: none;
+ }
+ }
+ &::after {
+ content: "";
+ @include position_value(absolute, 0, -18px, null, null);
+ @include wh-value(36px, 36px);
+ @include background(null, $white, null, null, null);
+ @include box-shadow(2px, -2px, 0, 1px, $breadcrumb-after-color);
+ transform: scale(0.707) rotate(45deg);
+ z-index: 1;
+ @include roundedTopRightRadius(5);
+ @include roundedBottomLeftRadius(50);
+ }
+ &.active {
+ @include background(null, $primary, null, null, null);
+ color: $white;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/layouts/breadcrumb/BreadcrumbComponent.ts b/src/app/layouts/breadcrumb/BreadcrumbComponent.ts
new file mode 100644
index 0000000..ccb9588
--- /dev/null
+++ b/src/app/layouts/breadcrumb/BreadcrumbComponent.ts
@@ -0,0 +1,140 @@
+/*
+ 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 Bread Crumb component.
+ */
+import { Component, Injector, OnInit } from '@angular/core';
+import { ActivatedRoute, NavigationEnd, Router, RouterEvent, UrlSegment } from '@angular/router';
+import { TranslateService } from '@ngx-translate/core';
+import { BREADCRUMBITEM } from 'CommonModel';
+import { filter, startWith } from 'rxjs/operators';
+import { isNullOrUndefined } from 'util';
+
+/**
+ * Creating component
+ * @Component takes BreadcrumbComponent.html as template url
+ */
+@Component({
+ selector: 'app-breadcrumb',
+ templateUrl: './BreadcrumbComponent.html',
+ styleUrls: ['./BreadcrumbComponent.scss']
+})
+
+/** Exporting a class @exports BreadcrumbComponent */
+export class BreadcrumbComponent implements OnInit {
+ /** To inject breadCrumb @public */
+ public static readonly ROUTE_DATA_BREADCRUMB: string = 'breadcrumb';
+
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** To inject breadCrumb Default icon and url @public */
+ public readonly home: {} = { icon: 'pi pi-th-large', url: '/' };
+
+ /** To inject breadCrumb Menus @public */
+ public menuItems: BREADCRUMBITEM[];
+
+ /** Service holds the router information @private */
+ private router: Router;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private activatedRoute: ActivatedRoute;
+
+ /** handle translate service @private */
+ private translateService: TranslateService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.router = this.injector.get(Router);
+ this.activatedRoute = this.injector.get(ActivatedRoute);
+ this.translateService = this.injector.get(TranslateService);
+ }
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.router.events
+ .pipe(filter((event: RouterEvent) => event instanceof NavigationEnd), startWith(undefined))
+ .subscribe(() => this.menuItems = this.createBreadcrumbs(this.activatedRoute.root));
+ }
+
+ /** Generate breadcrumbs from data given the module routes @private */
+ private createBreadcrumbs(route: ActivatedRoute, url: string = '', breadcrumbs: BREADCRUMBITEM[] = []):
+ BREADCRUMBITEM[] {
+ const children: ActivatedRoute[] = route.children;
+ if (children.length === 0) {
+ return breadcrumbs;
+ }
+ for (const child of children) {
+ const routeURL: string = child.snapshot.url.map((segment: UrlSegment) => segment.path).join('/');
+ if (routeURL !== '') {
+ url += `/${routeURL}`;
+ }
+ let menuLIst: BREADCRUMBITEM[] = child.snapshot.data[BreadcrumbComponent.ROUTE_DATA_BREADCRUMB];
+ if (!isNullOrUndefined(menuLIst)) {
+ menuLIst = JSON.parse(JSON.stringify(menuLIst));
+ menuLIst.forEach((item: BREADCRUMBITEM) => {
+ if (!isNullOrUndefined(item.title)) {
+ item.title = item.title.replace('{type}', this.checkTitle(item, child.snapshot.params.type));
+ item.title = item.title.replace('{id}', child.snapshot.params.id);
+ item.title = item.title.replace('{project}', localStorage.getItem('project'));
+ }
+ if (!isNullOrUndefined(item.url)) {
+ item.url = item.url.replace('{type}', child.snapshot.params.type);
+ item.url = item.url.replace('{id}', child.snapshot.params.id);
+ }
+ breadcrumbs.push(item);
+ });
+ }
+ return this.createBreadcrumbs(child, url, breadcrumbs);
+ }
+ }
+ /** Check and update title based on type of operations @private */
+ private checkTitle(breadcrumbItem: BREADCRUMBITEM, opertionType: string): string {
+ if (!isNullOrUndefined(breadcrumbItem.url)) {
+ if (breadcrumbItem.url.indexOf('packages') !== -1) {
+ return this.matchPackageTitle(opertionType);
+ }
+ if (breadcrumbItem.url.indexOf('instances') !== -1) {
+ return this.matchInstanceTitle(opertionType);
+ }
+ return breadcrumbItem.title;
+ }
+ }
+ /** check and update package title based on package type @private */
+ private matchPackageTitle(opertionType: string): string {
+ if (opertionType === 'ns') {
+ return this.translateService.instant('NSPACKAGES');
+ } else if (opertionType === 'vnf') {
+ return this.translateService.instant('VNFPACKAGES');
+ } else {
+ return this.translateService.instant('PAGE.DASHBOARD.NETSLICETEMPLATE');
+ }
+ }
+ /** check and update package title based on instance type @private */
+ private matchInstanceTitle(opertionType: string): string {
+ if (opertionType === 'ns') {
+ return this.translateService.instant('NSINSTANCES');
+ } else if (opertionType === 'vnf') {
+ return this.translateService.instant('VNFINSTANCES');
+ } else if (opertionType === 'pdu') {
+ return this.translateService.instant('PDUINSTANCES');
+ } else {
+ return this.translateService.instant('PAGE.DASHBOARD.NETSLICEINSTANCE');
+ }
+ }
+
+}
diff --git a/src/app/layouts/header/HeaderComponent.html b/src/app/layouts/header/HeaderComponent.html
new file mode 100644
index 0000000..a9ff5ac
--- /dev/null
+++ b/src/app/layouts/header/HeaderComponent.html
@@ -0,0 +1,71 @@
+<!--
+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)
+-->
+<span class="d-none px-3 py-2 text-center text-bold bg-primary text-white">Here is a newer {{'APPVERSION' | translate}} {{PACKAGEVERSION}}
+ of OSM!</span>
+
+<nav class="navbar navbar-expand-md sticky-top bg-white" role="navigation">
+ <a class="navbar-brand">
+ <img routerLink="/" src="assets/images/logo.png" class="osm-logo"
+ alt="OPEN SOURCE MANO" draggable="false">
+ </a>
+ <div class="nav navbar-nav nav-flex-icons ml-auto">
+ <ul class="navbar-nav cursor-pointer" ngbDropdown display="dynamic" placement="bottom-right">
+ <li class="nav-item dropdown">
+ <a class="nav-link dropdown-toggle" id="navbarDropdownProject" ngbDropdownToggle>
+ <i class="fas fa-folder-open"></i> {{'PAGE.DASHBOARD.PROJECTS' | translate}}
+ ({{selectedProject | async}})
+ </a>
+ <div class="dropdown-menu custom-dropdown-menu m-0 p-0" ngbDropdownMenu *ngIf="getSelectedProject"
+ aria-labelledby="navbarDropdownProject">
+ <a ngbDropdownItem class="dropdown-item project-item"
+ [ngClass]="list.project_name === getSelectedProject ? 'activeProject' : ''"
+ (click)="list.project_name === getSelectedProject ? '' : this.projectService.switchProjectModal(list)"
+ *ngFor="let list of projectList$ | async" placement="left" container="body"
+ ngbTooltip="{{ (list.project_name === getSelectedProject ? 'CURRENTPROJECT' : 'SWITCHPROJECT') | translate}}">
+ <span>{{list.project_name}}</span>
+ <i *ngIf="list.project_name === getSelectedProject"
+ class="fas fa-check-circle text-success"></i>
+ <i *ngIf="list.project_name !== getSelectedProject" class="fas fa-exchange-alt text-danger"></i>
+ </a>
+ </div>
+ </li>
+ </ul>
+ <ul class="navbar-nav cursor-pointer text-right" ngbDropdown display="dynamic" placement="bottom-right">
+ <li class="nav-item dropdown">
+ <a class="nav-link dropdown-toggle" id="navbarDropdown" ngbDropdownToggle>
+ <i class="fas fa-user-circle"></i> {{'USER' | translate}} ({{username$ | async}})
+ </a>
+ <div class="dropdown-menu custom-dropdown-menu m-0 p-0" ngbDropdownMenu
+ aria-labelledby="navbarDropdown">
+ <a ngbDropdownItem class="dropdown-item project-item" (click)="userSettings()">
+ <span>{{'PAGE.DASHBOARD.USERSETTINGS' | translate}}</span>
+ <i class="fas fa-users-cog"></i>
+ </a>
+ <a ngbDropdownItem class="dropdown-item project-item" (click)="logout()">
+ <span>{{'PAGE.DASHBOARD.LOGOUT' | translate}}</span>
+ <i class="fas fa-sign-out-alt"></i>
+ </a>
+ <div class="custom-divider"></div>
+ <a *ngIf="sharedService.osmVersion" ngbDropdownItem class="dropdown-item project-item osm-version" href="javascript:void(0);">
+ <span>{{'OSMVERSION' | translate}} {{sharedService.osmVersion}}</span>
+ </a>
+ </div>
+ </li>
+ </ul>
+ </div>
+</nav>
\ No newline at end of file
diff --git a/src/app/layouts/header/HeaderComponent.scss b/src/app/layouts/header/HeaderComponent.scss
new file mode 100644
index 0000000..86200b7
--- /dev/null
+++ b/src/app/layouts/header/HeaderComponent.scss
@@ -0,0 +1,51 @@
+/*
+ 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 '../../../assets/scss/mixins/mixin';
+@import '../../../assets/scss/variable';
+
+.navbar{
+ @include box-shadow(0px, 0px, 12px, 0px, rgba($black,0.14));
+ @include border(all, 0, solid, $gray-80);
+ .osm-logo{
+ cursor: pointer;
+ }
+ .custom-dropdown-menu {
+ .dropdown-item{
+ &.project-item{
+ @include flexbox(flex, space-between, row, null, center, null);
+ }
+ &:active, &:hover, &.active{
+ @include background(null, $theme-bg-color, null, null, null);
+ color: $primary;
+ }
+ &.activeProject{
+ cursor: default;
+ }
+ }
+ .custom-divider{
+ @include wh-value(null, 0);
+ @include border(top, 2, solid, $primary);
+ overflow: hidden;
+ }
+ }
+ .osm-version{
+ @include background(null, $theme-bg-color, null, null, null);
+ color: $primary;
+ cursor: default;
+ }
+}
\ No newline at end of file
diff --git a/src/app/layouts/header/HeaderComponent.ts b/src/app/layouts/header/HeaderComponent.ts
new file mode 100644
index 0000000..4d7f3b1
--- /dev/null
+++ b/src/app/layouts/header/HeaderComponent.ts
@@ -0,0 +1,106 @@
+/*
+ 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 Header Component
+ */
+import { Component, Injector, OnInit } from '@angular/core';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { AuthenticationService } from 'AuthenticationService';
+import { environment } from 'environment';
+import { ProjectService } from 'ProjectService';
+import { Observable } from 'rxjs';
+import { SharedService } from 'SharedService';
+import { ProjectRoleMappings, UserDetail } from 'UserModel';
+import { UserSettingsComponent } from 'UserSettingsComponent';
+
+/**
+ * Creating component
+ * @Component takes HeaderComponent.html as template url
+ */
+@Component({
+ selector: 'app-header',
+ templateUrl: './HeaderComponent.html',
+ styleUrls: ['./HeaderComponent.scss']
+})
+/** Exporting a class @exports HeaderComponent */
+export class HeaderComponent implements OnInit {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** Variables holds all the projects @public */
+ public projectList$: Observable<{}[]>;
+
+ /** Observable holds logined value @public */
+ public username$: Observable<string>;
+
+ /** Variables holds admin is logged or not @public */
+ public isAdmin: boolean;
+
+ /** Variables holds the selected project @public */
+ public selectedProject: Observable<string>;
+
+ /** project @public */
+ public getSelectedProject: string;
+
+ /** Version holds packages version @public */
+ public PACKAGEVERSION: string;
+
+ /** Contains all methods related to shared @public */
+ public sharedService: SharedService;
+
+ /** Utilizes auth service for any auth operations @private */
+ private authService: AuthenticationService;
+
+ /** Holds all project details @private */
+ private projectService: ProjectService;
+
+ /** Utilizes modal service for any modal operations @private */
+ private modalService: NgbModal;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.authService = this.injector.get(AuthenticationService);
+ this.modalService = this.injector.get(NgbModal);
+ this.projectService = this.injector.get(ProjectService);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.isAdmin = (localStorage.getItem('isAdmin') === 'true') ? true : false;
+ this.selectedProject = this.authService.ProjectName;
+ this.authService.ProjectName.subscribe((projectNameFinal: string) => {
+ this.getSelectedProject = projectNameFinal;
+ });
+ this.username$ = this.authService.username;
+ this.projectService.setHeaderProjects();
+ this.projectList$ = this.projectService.projectList;
+ this.PACKAGEVERSION = environment.packageVersion;
+ }
+
+ /** Logout function @public */
+ public logout(): void {
+ this.authService.logout();
+ }
+
+ /** Implementation of model for UserSettings options.@public */
+ public userSettings(): void {
+ this.modalService.open(UserSettingsComponent, { backdrop: 'static' });
+ }
+}
diff --git a/src/app/layouts/sidebar/SidebarComponent.html b/src/app/layouts/sidebar/SidebarComponent.html
new file mode 100644
index 0000000..1fa4f82
--- /dev/null
+++ b/src/app/layouts/sidebar/SidebarComponent.html
@@ -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)
+-->
+<div id="sidebar" class="p-2 sidebar-mini" [class.sidebar-collapse]="classAppliedForDesktop" [class.sidebar-open]="classAppliedForMobile">
+ <div class="custom-menu" (click)="sideBarOpenClose()">
+ <button type="button" id="sidebarCollapse" class="btn btn-primary">
+ <i class="fa fa-bars"></i>
+ </button>
+ </div>
+ <nav>
+ <ul class="scrollable-menu">
+ <li *ngFor="let menu of getMenus; let i = index" [ngClass]="{'hasChild': menu.isChildExists }"
+ class="{{menu.liClass}}" id="{{menu.clickFunction}}{{i}}"
+ (click)="handleMenuFunction(i, menu.clickFunction, 'menu-open', menu.isChildExists)"
+ [routerLinkActiveOptions]="{exact: menu.routerLinkActiveOptions !== undefined ? menu.routerLinkActiveOptions : false}"
+ [routerLinkActive]="menu.routerLinkActive !== undefined ? menu.routerLinkActive : ''">
+ <a *ngIf="menu.isChildExists" class="{{menu.anchorTagClass}}">
+ <i class="{{menu.icon}}"></i>
+ <span>{{ menu.menuName | translate}}</span>
+ </a>
+ <a *ngIf="!menu.isChildExists" class="{{menu.anchorTagClass}}" [routerLink]="menu.routerLink">
+ <i class="{{menu.icon}}"></i>
+ <span>{{ menu.menuName | translate}}</span>
+ </a>
+ <ul *ngIf="menu.isChildExists" class="{{menu.ulClass}}">
+ <li *ngFor="let childMenu of menu.childItems"
+ [routerLinkActiveOptions]="{exact: childMenu.routerLinkActiveOptions}"
+ [routerLinkActive]="childMenu.routerLinkActive" (click)="checkAndCloseSideBar(childMenu.isChildExists)">
+ <a class="{{childMenu.anchorTagClass}}" [routerLink]="childMenu.routerLink">
+ <i class="{{childMenu.icon}}"></i>
+ <span>{{childMenu.menuName | translate}}</span>
+ </a>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </nav>
+</div>
\ No newline at end of file
diff --git a/src/app/layouts/sidebar/SidebarComponent.scss b/src/app/layouts/sidebar/SidebarComponent.scss
new file mode 100644
index 0000000..7b5f92e
--- /dev/null
+++ b/src/app/layouts/sidebar/SidebarComponent.scss
@@ -0,0 +1,225 @@
+/*
+ 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 "../../../assets/scss/mixins/mixin";
+@import "../../../assets/scss/variable";
+$active-margin-left: -230;
+$minimize-left: 45px;
+.sidebar-mini {
+ @include transition(all, 0.3s, ease-in-out, 0s);
+ @include position_value(relative, null, null, null, null);
+ z-index: 10;
+ .custom-menu {
+ @include flexbox(inline-block, null, null, null, null, null);
+ @include position_value(absolute, 25px, 4px, null, null);
+ @include margin-value(0, -20, 0, 0);
+ .btn {
+ @include wh-value(55px, 55px);
+ @include roundedCornersPercentage(50%);
+ @include position_value(relative, null, null, null, null);
+ @include background(null, transparent, null, null, null);
+ @include border(all, 0, solid, transparent);
+ i {
+ @include margin-value(0, -40, 0, 0);
+ @include font(null, 14px, null);
+ }
+ &.btn-primary {
+ &:after {
+ content: "";
+ @include position_value(absolute, 0, 0, 0, 0);
+ @include background(null, $secondary, null, null, null);
+ @include roundedCorners(10);
+ @include rotate(45);
+ z-index: -1;
+ }
+ }
+ &:hover,
+ &:focus,
+ &:active {
+ @include background(null, transparent !important, null, null, null);
+ }
+ }
+ }
+ nav {
+ @include wh-value(200px, null);
+ @include transition(width, 0.2s, ease-in-out, 0s);
+ ul {
+ @include padding-value(0, 0, 0, 0);
+ @include margin-value(0, 0, 0, 0);
+ @include wh-value(100%, null);
+ @include background(null, $theme-bg-color, null, null, null);
+ li {
+ @include position_value(relative, null, null, null, null);
+ @include background(null, $secondary, null, null, null);
+ @include line-height(14px);
+ list-style-type: none;
+ cursor: pointer;
+ color: $white;
+ &.round-edge-top-3 {
+ @include roundedTopRightRadius(3);
+ @include roundedTopLeftRadius(3);
+ }
+ &.round-edge-bottom-3 {
+ @include roundedBottomLeftRadius(3);
+ @include roundedBottomRightRadius(3);
+ }
+ &.border-bottom-none {
+ @include border(bottom, 0, !important, null);
+ }
+ &.header-menu {
+ @include background(null, $theme-bg-color, null, null, null);
+ @include padding-value(10, 20, 0, 4);
+ @include font(null, null, bold);
+ cursor: default;
+ .heading {
+ @include border(bottom, 2, solid, $primary);
+ @include font(null, 12px, null);
+ @include line-height(16px);
+ @include flexbox(block, null, null, null, null, null);
+ cursor: default;
+ color: $primary;
+ }
+ }
+ a {
+ &.individual {
+ @include padding-value(12, 5, 12, 15);
+ color: $white;
+ }
+ i {
+ @include wh-value(30px, null);
+ }
+ &.parentlink::after {
+ content: "\f105";
+ @include font("Font Awesome 5 Free", null, 900);
+ @include position_value(absolute, 14px, 15px, null, null);
+ @include transition(all, 0.3s, ease, 0s);
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ }
+ }
+ .sidebar-submenu {
+ opacity: 0;
+ @include transition(opacity, 0.4s, ease-in-out, 0s);
+ li {
+ @include background(null, $secondary, null, null, null);
+ @include wh-value(null, 0);
+ @include transition(height, 200ms, ease-in, 0s);
+ overflow: hidden;
+ &:last-child {
+ @include roundedBottomLeftRadius(4);
+ @include roundedBottomRightRadius(4);
+ }
+ .link {
+ color: $gray-500;
+ }
+ &:hover,
+ &.active {
+ .link {
+ color: $white;
+ }
+ }
+ }
+ }
+ &.menu-open {
+ @include background(null, $primary, null, null, null);
+ a {
+ &::after {
+ @include rotate(90);
+ }
+ }
+ .sidebar-submenu {
+ opacity: 1;
+ li {
+ @include wh-value(null, 45px);
+ }
+ }
+ }
+ &.parentactive {
+ @include background(null, $primary, null, null, null);
+ .parentlink {
+ color: $white;
+ }
+ }
+ .parentlink,
+ .link {
+ @include padding-value(12, 5, 12, 15);
+ @include flexbox(flex, null, null, null, null, null);
+ }
+ }
+ }
+ }
+ &.sidebar-collapse {
+ @include background(null, transparent, null, null, null);
+ nav {
+ transform: translate(0, 0);
+ @include wh-value($minimize-left, null);
+ ul {
+ &.scrollable-menu {
+ li {
+ a {
+ span {
+ @include position_value(null, 0, null, null, null);
+ @include padding-value(12, 5, 12, 20);
+ }
+ &.individual {
+ span {
+ @include roundedBottomRightRadius(4);
+ }
+ }
+ &.parentlink,
+ &.individual {
+ span {
+ @include background(null, $primary, null, null, null);
+ @include roundedTopRightRadius(4);
+ }
+ }
+ }
+ &:hover {
+ .sidebar-submenu {
+ li {
+ @include wh-value(null, 45px);
+ }
+ }
+ .sidebar-submenu,
+ a span {
+ @include flexbox(block !important, null, null, null, null, null);
+ @include position_value(absolute, null, null, null, $minimize-left);
+ @include wh-value(220px, null);
+ opacity: 1;
+ }
+ }
+ &.header-menu,
+ .sidebar-submenu {
+ @include flexbox(none !important, null, null, null, null, null);
+ transform: translateZ(0);
+ }
+ }
+ }
+ li {
+ a::after,
+ span {
+ @include flexbox(none !important, null, null, null, null, null);
+ transform: translateZ(0);
+ }
+ }
+ }
+ }
+ }
+ &.sidebar-open {
+ @include margin-value(0, 0, 0, $active-margin-left);
+ }
+}
\ No newline at end of file
diff --git a/src/app/layouts/sidebar/SidebarComponent.ts b/src/app/layouts/sidebar/SidebarComponent.ts
new file mode 100644
index 0000000..9cae634
--- /dev/null
+++ b/src/app/layouts/sidebar/SidebarComponent.ts
@@ -0,0 +1,148 @@
+/*
+ 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 Sidebar Component
+ */
+import { Component, Injector, OnInit } from '@angular/core';
+import { DeviceCheckService } from 'DeviceCheckService';
+import { MENU_ITEMS, MENUITEMS } from 'src/models/MenuModel';
+import { isNullOrUndefined } from 'util';
+
+/**
+ * Creating component
+ * @Component takes SidebarComponent.html as template url
+ */
+@Component({
+ selector: 'app-sidebar',
+ templateUrl: './SidebarComponent.html',
+ styleUrls: ['./SidebarComponent.scss']
+})
+/** Exporting a class @exports SidebarComponent */
+export class SidebarComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** submenu router endpoints @public */
+ public routerEndpoint: string;
+
+ /** submenu router endpoints @public */
+ public getMenus: MENUITEMS[];
+
+ /** selected Menu @public */
+ public selectedItem: string;
+
+ /** get the classlist @public */
+ public elementCheck: HTMLCollection;
+
+ /** Apply active class for Desktop @public */
+ public classAppliedForDesktop: boolean = false;
+
+ /** Apply active class for mobile @public */
+ public classAppliedForMobile: boolean = false;
+
+ /** Device Check service @public */
+ public deviceCheckService: DeviceCheckService;
+
+ /** Check for the mobile @public */
+ public isMobile$: boolean;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.deviceCheckService = this.injector.get(DeviceCheckService);
+ this.getMenus = MENU_ITEMS;
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.deviceCheckService.checkDeviceType();
+ this.deviceCheckService.isMobile.subscribe((checkIsMobile: boolean) => {
+ this.isMobile$ = checkIsMobile;
+ this.getDeviceType();
+ });
+ }
+ /** method to open sideBarOpen in all the views */
+ public sideBarOpenClose(): void {
+ if (this.isMobile$) {
+ this.classAppliedForMobile = !this.classAppliedForMobile;
+ } else {
+ this.classAppliedForDesktop = !this.classAppliedForDesktop;
+ }
+ this.addClassMainWrapper();
+ }
+ /** Add class for mobile/Desktop in main-wrapper @public */
+ public addClassMainWrapper(): void {
+ const elementMain: HTMLElement = document.querySelector('#main-wrapper');
+ if (!isNullOrUndefined(elementMain)) {
+ if (this.isMobile$) {
+ elementMain.classList.toggle('sidebar-mobile');
+ } else {
+ elementMain.classList.toggle('sidebar-desktop');
+ }
+ }
+ }
+ /** Return the Device type @public */
+ public getDeviceType(): void {
+ if (this.isMobile$) {
+ this.classAppliedForMobile = true;
+ this.classAppliedForDesktop = false;
+ } else {
+ this.classAppliedForMobile = false;
+ this.classAppliedForDesktop = false;
+ }
+ this.addClassMainWrapper();
+ }
+ /** Set the SideBar Menus click function @public */
+ public handleMenuFunction(index: number, method: string, className: string, childExists: boolean): void {
+ this.selectedItem = method;
+ if (!isNullOrUndefined(method)) {
+ this.parentactiveClassAddRemove(index, method, className, childExists);
+ }
+ }
+ /** Removing the parentactive class which is already present and add it to current @public */
+ public parentactiveClassAddRemove(index: number, method: string, className: string, childExists: boolean): void {
+ const element: HTMLElement = document.querySelector('#' + method + '' + index);
+ const checkOpenedelement: boolean = element.classList.contains(className);
+ if (!checkOpenedelement) {
+ this.elementCheck = document.getElementsByClassName(className);
+ if (this.elementCheck.length > 0) {
+ this.removeClasses(className);
+ }
+ }
+ if (method !== 'nosubmenu') {
+ element.classList.toggle(className);
+ }
+ if (this.isMobile$ && !childExists) {
+ this.checkAndCloseSideBar(childExists);
+ }
+ }
+ /** Hide / Show Menus based on the clicking in the menus @public */
+ public checkAndCloseSideBar(childExists: boolean): void {
+ event.stopPropagation();
+ if (this.isMobile$ && !childExists) {
+ this.sideBarOpenClose();
+ }
+ }
+ /** remove existing Class @public */
+ public removeClasses(className: string): void {
+ this.elementCheck[0].classList.remove(className);
+ if (this.elementCheck[0]) {
+ this.removeClasses(className);
+ }
+ }
+}
diff --git a/src/app/login/LoginComponent.html b/src/app/login/LoginComponent.html
new file mode 100644
index 0000000..296d51b
--- /dev/null
+++ b/src/app/login/LoginComponent.html
@@ -0,0 +1,57 @@
+<!--
+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)
+-->
+<div class="login-container">
+ <div class="wrap-login">
+ <div class="login-logo">
+ <img src="assets/images/logo.png" alt="Logo">
+ </div>
+ <form [formGroup]="loginForm" (ngSubmit)="onSubmit()" class="login-form" autocomplete="off">
+ <div class="wrap-input">
+ <input autocomplete="off" class="input-control" type="text" formControlName="userName" placeholder="{{ 'PAGE.LOGIN.USERNAME' | translate }}"
+ />
+ <span class="input-icon">
+ <i class="fa fa-user" aria-hidden="true"></i>
+ </span>
+ </div>
+ <div *ngIf="submitted && loginForm.controls['userName'].errors" class="input-validation-msg">
+ <div *ngIf="loginForm.controls['userName'].errors.required">{{'PAGE.LOGIN.USERNAMEVALIDMESSAGE' | translate}}</div>
+ </div>
+ <div class="wrap-input">
+ <input autocomplete="off" class="input-control" type="password" formControlName="password" placeholder="{{ 'PAGE.LOGIN.PASSWORD' | translate }}"
+ />
+ <span class="input-icon">
+ <i class="fa fa-lock" aria-hidden="true"></i>
+ </span>
+ </div>
+ <div *ngIf="submitted && loginForm.controls['password'].errors" class="input-validation-msg">
+ <div *ngIf="loginForm.controls['password'].errors.required">{{'PAGE.LOGIN.PASSWORDVALIDMESSAGE' | translate}}</div>
+ </div>
+ <button type="submit" class="submit-btn">
+ <i class="fa fa-arrow-right" aria-hidden="true"></i>
+ </button>
+ <div class="signup-text-center">
+ <span class="caret">{{'PAGE.LOGIN.SIGNINMSG' | translate}}</span>
+ </div>
+
+ </form>
+ </div>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
+<div class="login-footer">
+ <a href="https://osm.etsi.org" target="_blank">{{'OSMSOURCEMANO' | translate}} {{sharedService.osmVersion}}</a>
+</div>
diff --git a/src/app/login/LoginComponent.scss b/src/app/login/LoginComponent.scss
new file mode 100644
index 0000000..28ac159
--- /dev/null
+++ b/src/app/login/LoginComponent.scss
@@ -0,0 +1,156 @@
+/*
+ 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 "../../assets/scss/mixins/mixin";
+@import "../../assets/scss/variable";
+
+.login-container {
+ @include wh-value(100%, 100vh);
+ @include flexbox(flex, center, null, null, center, null);
+ @include background(url("../../assets/images/login_background.jpg"), null, cover, no-repeat, center);
+ background-attachment: fixed;
+ flex-wrap: wrap;
+ .wrap-login {
+ @include background(
+ linear-gradient(
+ to left bottom,
+ #00c0ef,
+ #00b3f9,
+ #3ea3fd,
+ #7190f8,
+ #9c78e8,
+ #a86cdd,
+ #b25fd1,
+ #bb51c3,
+ #b151c4,
+ #a652c6,
+ #9b53c6,
+ #9053c7
+ ),
+ null,
+ null,
+ null,
+ null
+ );
+ @include roundedCorners(15);
+ @include flexbox(flex, space-between, null, null, null, null);
+ @include position_value(relative, null, null, null, null);
+ @include box-shadow(0px, 3px, 10px, 0px, rgba($black, 0.5));
+ @include padding-value(20, 30, 20, 30);
+ flex-wrap: wrap;
+ color: $white;
+ overflow: visible;
+ .login-logo {
+ @include flexbox(flex, center, null, null, center, null);
+ @include position_value(absolute, -80px, null, null, 95px);
+ @include box-shadow(1px, 2px, 0px, 0px, $cerise-pink);
+ @include margin-value(0, 0, 10, 0);
+ @include wh-value(160px, 150px);
+ @include background(null, $white, null, null, null);
+ @include roundedCornersPercentage(50%);
+ img {
+ @include wh-value(130px, auto);
+ @include position_value(null, 50px, null, null, null);
+ }
+ }
+ .login-form {
+ @include wh-value(290px, null);
+ @include padding-value(70, 0, 0, 0);
+ text-align: center;
+ .wrap-input {
+ @include position_value(relative, null, null, null, null);
+ @include wh-value(100%, null);
+ @include margin-value(0, 0, 10, 0);
+ z-index: 1;
+ .input-control {
+ @include font(null, 15px, null);
+ @include line-height(1.5);
+ @include wh-value(100%, 42px);
+ @include padding-value(0, 30, 0, 65);
+ @include flexbox(block, null, null, null, null, null);
+ @include roundedCorners(25);
+ @include border(all, 0, solid, $gray-80);
+ @include background(null, $white, null, null, null);
+ &:focus + .input-icon {
+ color: $cerise-pink;
+ @include padding-value(0, 0, 0, 25);
+ }
+ }
+ .input-icon {
+ @include font(null, 13px, null);
+ @include flexbox(flex, null, null, null, center, null);
+ @include position_value(absolute, null, null, 0, 0);
+ @include wh-value(100%, 100%);
+ @include padding-value(0, 0, 0, 35);
+ @include roundedCorners(25);
+ @include transition(all, 0.5s, null, null);
+ pointer-events: none;
+ color: $gray-600;
+ }
+ }
+ .submit-btn {
+ @include background(null, $white, null, null, null);
+ @include roundedCornersPercentage(50%);
+ @include border(all, 0, solid, $gray-80);
+ @include transition(all, 0.3s, null, null);
+ @include box-shadow(1px, 5px, 5px, 0px, rgba($black, 0.3), inset);
+ cursor: pointer;
+ @include font(null, 25px, null);
+ @include wh-value(60px, 60px);
+ @include margin-value(0, 0, 10, 0);
+ @include padding-value(0, 0, 0, 0);
+ color: $cerise-pink;
+ &:hover {
+ @include box-shadow(1px, 5px, 10px, 0px, rgba($black, 0.3));
+ }
+ .fa {
+ @include transition(all, 0.4s, null, null);
+ @include flexbox(block, null, null, null, null, null);
+ @include padding-value(18, 18, 18, 18);
+ &:hover {
+ transform: scale(1.2);
+ }
+ }
+ }
+ .input-validation-msg {
+ @include roundedCorners(25);
+ @include background(null, $cerise-pink, null, null, null);
+ @include margin-value(0, 0, 10, 0);
+ @include padding-value(1, 0, 1, 15);
+ @include font(null, 11px, null);
+ color: $white;
+ text-align: left;
+ }
+ }
+ }
+}
+.login-footer {
+ @include flexbox(flex, space-between, null, null, center, null);
+ @include position_value(fixed, null, null, 0px, null);
+ @include background(null, $purple, null, null, null);
+ @include wh-value(100%, 40px);
+ @include padding-value(0, 10, 0, 10);
+ color: $white;
+ opacity: 0.9;
+ a {
+ @include font(null, null, bold);
+ color: $white;
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/login/LoginComponent.ts b/src/app/login/LoginComponent.ts
new file mode 100644
index 0000000..2f4f67e
--- /dev/null
+++ b/src/app/login/LoginComponent.ts
@@ -0,0 +1,131 @@
+/*
+ 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 Page for Login component
+ */
+import { HttpErrorResponse } from '@angular/common/http';
+import { Component, Injector, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { Router } from '@angular/router';
+import { AuthenticationService } from 'AuthenticationService';
+import { RestService } from 'RestService';
+import { Observable } from 'rxjs';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+
+/**
+ * Creating component
+ * @Component takes LoginComponent.html as template url
+ */
+@Component({
+ selector: 'app-login',
+ templateUrl: './LoginComponent.html',
+ styleUrls: ['./LoginComponent.scss']
+})
+/** Exporting a class @exports LoginComponent */
+export class LoginComponent implements OnInit {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** contains loginform group information @public */
+ public loginForm: FormGroup;
+
+ /** submitted set to boolean state @public */
+ public submitted: boolean = false;
+
+ /** contains return URL link @public */
+ public returnUrl: string;
+
+ /** Observable Hold the value of subscription @public */
+ public isLoggedIn$: Observable<boolean>;
+
+ /** contains access token information @public */
+ public accessToken: string;
+
+ /** Utilizes rest service for any CRUD operations @public */
+ public restService: RestService;
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Contains all methods related to shared @public */
+ public sharedService: SharedService;
+
+ /** Utilizes auth service for any auth operations @private */
+ private authService: AuthenticationService;
+
+ /** contians form builder module @private */
+ private formBuilder: FormBuilder;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ // creates instance of login component
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.authService = this.injector.get(AuthenticationService);
+ this.formBuilder = this.injector.get(FormBuilder);
+ this.router = this.injector.get(Router);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.isLoggedIn$ = this.authService.isLoggedIn;
+ if (this.isLoggedIn$) {
+ this.router.navigate(['/']).catch(() => {
+ // Catch Navigation Error
+ });
+ }
+ this.loginForm = this.formBuilder.group({
+ userName: ['', [Validators.required]],
+ password: ['', [Validators.required]]
+ });
+ this.returnUrl = isNullOrUndefined(localStorage.getItem('returnUrl')) ? '/' : localStorage.getItem('returnUrl');
+ }
+
+ /**
+ * called on form submit @private onSubmit
+ */
+ public onSubmit(): void {
+ this.submitted = true;
+ if (this.loginForm.invalid) {
+ return;
+ }
+ this.isLoadingResults = true;
+ this.sharedService.cleanForm(this.loginForm);
+ this.authService.login(this.loginForm.value.userName, this.loginForm.value.password).subscribe(
+ (data: {}) => {
+ this.isLoadingResults = false;
+ this.router.navigate([this.returnUrl]).catch(() => {
+ // Catch Navigation Error
+ });
+ localStorage.removeItem('returnUrl');
+ }, (err: HttpErrorResponse) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(err, 'post');
+ });
+ }
+}
diff --git a/src/app/packages/PackagesComponent.html b/src/app/packages/PackagesComponent.html
new file mode 100644
index 0000000..06b8876
--- /dev/null
+++ b/src/app/packages/PackagesComponent.html
@@ -0,0 +1,18 @@
+<!--
+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)
+-->
+<router-outlet></router-outlet>
diff --git a/src/app/packages/PackagesComponent.scss b/src/app/packages/PackagesComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/packages/PackagesComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/packages/PackagesComponent.ts b/src/app/packages/PackagesComponent.ts
new file mode 100644
index 0000000..eb9991d
--- /dev/null
+++ b/src/app/packages/PackagesComponent.ts
@@ -0,0 +1,55 @@
+/*
+ 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 Packages-Component.ts.
+ */
+import { Component, Injector } from '@angular/core';
+import { Router, RouterEvent } from '@angular/router';
+/**
+ * Creating Component
+ * @Component takes PackagesComponent.html as template url
+ */
+@Component({
+ selector: 'app-packages',
+ templateUrl: './PackagesComponent.html',
+ styleUrls: ['./PackagesComponent.scss']
+})
+/** Exporting a class @exports PackagesComponent */
+export class PackagesComponent{
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ // creates packages component
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.router = this.injector.get(Router);
+ this.router.events.subscribe((event: RouterEvent) => {
+ this.redirectToList(event.url);
+ });
+ }
+
+ /** Return to list NS Package List */
+ public redirectToList(getURL: string): void {
+ if (getURL === '/packages') {
+ this.router.navigate(['/packages/ns']).catch();
+ }
+ }
+}
diff --git a/src/app/packages/PackagesModule.ts b/src/app/packages/PackagesModule.ts
new file mode 100644
index 0000000..e6c6628
--- /dev/null
+++ b/src/app/packages/PackagesModule.ts
@@ -0,0 +1,127 @@
+/*
+ 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 Packages Module.
+ */
+import { CommonModule } from '@angular/common';
+import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
+import { ReactiveFormsModule } from '@angular/forms';
+import { FormsModule } from '@angular/forms';
+import { RouterModule, Routes } from '@angular/router';
+import { CodemirrorModule } from '@ctrl/ngx-codemirror';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { TranslateModule } from '@ngx-translate/core';
+import { ClonePackageComponent } from 'ClonePackage';
+import { DataService } from 'DataService';
+import { DragDirective } from 'DragDirective';
+import { EditPackagesComponent } from 'EditPackagesComponent';
+import { LoaderModule } from 'LoaderModule';
+import { NetsliceTemplateComponent } from 'NetsliceTemplate';
+import { SidebarModule } from 'ng-sidebar';
+import { Ng2SmartTableModule } from 'ng2-smart-table';
+import { NSComposerComponent } from 'NSComposer';
+import { NSPackagesComponent } from 'NSPackages';
+import { PackagesComponent } from 'Packages';
+import { PagePerRowModule } from 'PagePerRowModule';
+import { PageReloadModule } from 'PageReloadModule';
+import { ShowContentComponent } from 'ShowContent';
+import { VNFComposerComponent } from 'VNFComposer';
+import { VNFPackagesComponent } from 'VNFPackages';
+
+/** To halndle project information */
+const projectInfo: {} = { title: '{project}', url: '/' };
+
+/**
+ * configures routers
+ */
+const routes: Routes = [
+ {
+ path: '',
+ component: PackagesComponent,
+ children: [
+ {
+ path: 'ns',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'NSPACKAGES', url: null }]
+ },
+ component: NSPackagesComponent
+ },
+ {
+ path: 'vnf',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'VNFPACKAGES', url: null }]
+ },
+ component: VNFPackagesComponent
+ },
+ {
+ path: 'netslice',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }
+ , projectInfo, { title: 'PAGE.DASHBOARD.NETSLICETEMPLATE', url: null }]
+ },
+ component: NetsliceTemplateComponent
+ },
+ {
+ path: ':type/edit/:id',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }
+ , projectInfo, { title: '{type}', url: '/packages/{type}' }, { title: '{id}', url: null }]
+ },
+ component: EditPackagesComponent
+ },
+ {
+ path: 'ns/compose/:id',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }
+ , projectInfo, { title: 'NSPACKAGES', url: '/packages/ns' }, { title: '{id}', url: null }]
+ },
+ component: NSComposerComponent
+ },
+ {
+ path: 'vnf/compose/:id',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'VNFPACKAGES', url: '/packages/vnf' }, { title: '{id}', url: null }]
+ },
+ component: VNFComposerComponent
+ }
+ ]
+ }
+];
+
+/**
+ * Creating @NgModule component for Modules
+ */
+@NgModule({
+ imports: [ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }), FormsModule, CommonModule, Ng2SmartTableModule,
+ CodemirrorModule, TranslateModule, RouterModule.forChild(routes), NgbModule, NgSelectModule,
+ PagePerRowModule, SidebarModule.forRoot(), LoaderModule, PageReloadModule],
+ declarations: [PackagesComponent, NSPackagesComponent, VNFPackagesComponent, NetsliceTemplateComponent,
+ DragDirective, ShowContentComponent, NSComposerComponent, VNFComposerComponent, EditPackagesComponent, ClonePackageComponent],
+ providers: [DataService],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
+ entryComponents: [ShowContentComponent, ClonePackageComponent]
+})
+/** Exporting a class @exports PackagesModule */
+export class PackagesModule {
+ /** Variables declared to avoid state-less class */
+ private packagesModule: string;
+}
diff --git a/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.html b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.html
new file mode 100644
index 0000000..af52927
--- /dev/null
+++ b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.html
@@ -0,0 +1,98 @@
+<!--
+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)
+-->
+<form [formGroup]="netSliceInstantiateForm" (ngSubmit)="instantiateNSTSubmit()">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'NEW' | translate}} NSI</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </div>
+ <div class="modal-body modal-body-custom-height netsliceinstantiate-ns">
+ <div class="form-group row">
+ <label class="col-sm-12 col-form-label mandatory-label"
+ [ngClass]="{'text-danger': netSliceInstantiateForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <label class="col-sm-4 col-form-label" for="nsiName">{{'PAGE.NSTINSTANCEINSTANTIATE.NSNAME' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.NSTINSTANCEINSTANTIATE.NSNAME' | translate}}" type="text" formControlName="nsiName"
+ id="nsiName" [ngClass]="{ 'is-invalid': submitted && f.nsiName.errors }" required>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label"
+ for="nsiDescription">{{'PAGE.NSTINSTANCEINSTANTIATE.DESCRIPTION' | translate}}*</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'PAGE.NSTINSTANCEINSTANTIATE.DESCRIPTION' | translate}}"
+ type="text" formControlName="nsiDescription" id="nsiDescription"
+ [ngClass]="{ 'is-invalid': submitted && f.nsiDescription.errors }" required></textarea>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="nstId">{{'PAGE.NSTINSTANCEINSTANTIATE.NSTID' | translate}}*</label>
+ <div class="col-sm-8">
+ <ng-select [items]="netSliceSelect" bindLabel="name" bindValue="_id"
+ placeholder="{{'SELECT' | translate}} {{'PAGE.NSTINSTANCEINSTANTIATE.NSTID' | translate}}"
+ formControlName="nstId" [(ngModel)]="netsliceNstId" id="nstId"
+ [ngClass]="{ 'is-invalid': submitted && f.nstId.errors }" required>
+ </ng-select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label"
+ for="vimAccountId">{{'PAGE.NSTINSTANCEINSTANTIATE.VIMACCOUNT' | translate}}*</label>
+ <div class="col-sm-8">
+ <ng-select [items]="vimDetailsSelect" bindLabel="name" bindValue="_id"
+ placeholder="{{'SELECT' | translate}} {{'PAGE.NSTINSTANCEINSTANTIATE.VIMACCOUNT' | translate}}"
+ formControlName="vimAccountId" [(ngModel)]="vimAccountId" id="vimAccountId"
+ [ngClass]="{ 'is-invalid': submitted && f.vimAccountId.errors }" required>
+ </ng-select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="ssh_keys">{{'PAGE.NSTINSTANCEINSTANTIATE.SSHKEY' | translate}}</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'PAGE.NSTINSTANCEINSTANTIATE.SSHKEYMSG' | translate}}"
+ formControlName="ssh_keys" id="ssh_keys"></textarea>
+ <div class="fileupload-text mt-1 mb-1">{{'FILEUPLOADLABEL' | translate}}</div>
+ <div class="custom-file">
+ <input type="file" #fileInputSSH class="custom-file-input" (change)="sshFile($event.target.files)"
+ id="customSSHFile">
+ <label class="custom-file-label" #fileInputSSHLabel for="customSSHFile">{{'CHOOSEFILE' | translate}}</label>
+ </div>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="config">{{'CONFIG' | translate}}</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'YAMLCONFIG' | translate}}" formControlName="config"
+ id="config"></textarea>
+ <div class="fileupload-text mt-1 mb-1">{{'FILEUPLOADLABEL' | translate}}</div>
+ <div class="custom-file">
+ <input type="file" #fileInputConfig class="custom-file-input" (change)="configFile($event.target.files)"
+ id="customConfigFile">
+ <label class="custom-file-label" #fileInputConfigLabel for="customConfigFile">{{'CHOOSEFILE' | translate}}</label>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'CREATE' | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.scss b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.scss
new file mode 100644
index 0000000..0ecd95d
--- /dev/null
+++ b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+ */
\ No newline at end of file
diff --git a/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.ts b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.ts
new file mode 100644
index 0000000..ed5e414
--- /dev/null
+++ b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.ts
@@ -0,0 +1,282 @@
+/*
+ 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 Instantiate NS Modal Component.
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { Router } from '@angular/router';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import * as jsyaml from 'js-yaml';
+import { NetworkSliceData } from 'NetworkSliceModel';
+import { NSICREATEPARAMS } from 'NSDModel';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+import { VimAccountDetails } from 'VimAccountModel';
+/**
+ * Creating component
+ * @Component takes InstantiateNetSliceTemplateComponent.html as template url
+ */
+@Component({
+ selector: 'app-instantiate-net-slice-template',
+ templateUrl: './InstantiateNetSliceTemplateComponent.html',
+ styleUrls: ['./InstantiateNetSliceTemplateComponent.scss']
+})
+/** Exporting a class @exports InstantiateNetSliceTemplateComponent */
+export class InstantiateNetSliceTemplateComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** FormGroup instance added to the form @ html @public */
+ public netSliceInstantiateForm: FormGroup;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Variable set for twoway bindng @public */
+ public vimAccountId: string;
+
+ /** Contains all the net slice data collections */
+ public netSliceSelect: NetworkSliceData;
+
+ /** Contains all the VIM data collections */
+ public vimDetailsSelect: VimAccountDetails;
+
+ /** Variable set for twoway binding @public */
+ public netsliceNstId: string;
+
+ /** Form submission Add */
+ public submitted: boolean = false;
+
+ /** Check the loading results for loader status @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Element ref for fileInputConfig @public */
+ @ViewChild('fileInputConfig', { static: true }) public fileInputConfig: ElementRef;
+
+ /** Element ref for fileInputConfigLabel @public */
+ @ViewChild('fileInputConfigLabel', { static: true }) public fileInputConfigLabel: ElementRef;
+
+ /** Element ref for fileInputSSH @public */
+ @ViewChild('fileInputSSH', { static: true }) public fileInputSSH: ElementRef;
+
+ /** Element ref for fileInputSSHLabel @public */
+ @ViewChild('fileInputSSHLabel', { static: true }) public fileInputSSHLabel: ElementRef;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** Utilizes rest service for any CRUD operations @private */
+ private restService: RestService;
+
+ /** Utilizes data service for any communication @private */
+ private dataService: DataService;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Contains the ssh key to be hosted in dom @private */
+ private copySSHKey: string;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.formBuilder = this.injector.get(FormBuilder);
+ this.dataService = this.injector.get(DataService);
+ this.notifierService = this.injector.get(NotifierService);
+ this.router = this.injector.get(Router);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ /** Setting up initial value for NSD */
+ this.netsliceNstId = '';
+ this.dataService.currentMessage.subscribe((event: NetworkSliceData) => {
+ if (event.identifier !== undefined || event.identifier !== '' || event.identifier !== null) {
+ this.netsliceNstId = event.identifier;
+ }
+ });
+ this.netSliceInstantiateFormAction();
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ /** On Initializing call the methods */
+ this.getNetSliceDetails();
+ this.getVIMDetails();
+ }
+
+ /** Call the netSlice details in the selection options @public */
+ public getNetSliceDetails(): void {
+ this.restService.getResource(environment.NETWORKSLICETEMPLATECONTENT_URL).subscribe((netSlicePackages: NetworkSliceData) => {
+ this.netSliceSelect = netSlicePackages;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+
+ /** Call the VIM details in the selection options @public */
+ public getVIMDetails(): void {
+ this.restService.getResource(environment.VIMACCOUNTS_URL).subscribe((vimDetails: VimAccountDetails) => {
+ this.vimDetailsSelect = vimDetails;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+
+ /** On modal initializing forms @public */
+ public netSliceInstantiateFormAction(): void {
+ this.netSliceInstantiateForm = this.formBuilder.group({
+ nsiName: ['', [Validators.required]],
+ nsiDescription: ['', [Validators.required]],
+ nstId: ['', [Validators.required]],
+ vimAccountId: ['', [Validators.required]],
+ ssh_keys: [null],
+ config: [null]
+ });
+ }
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.netSliceInstantiateForm.controls; }
+
+ /** On modal submit instantiateNsSubmit will called @public */
+ public instantiateNSTSubmit(): void {
+ this.submitted = true;
+ this.sharedService.cleanForm(this.netSliceInstantiateForm);
+ if (this.netSliceInstantiateForm.invalid) {
+ return;
+ }
+ const modalData: MODALCLOSERESPONSEDATA = {
+ message: 'Done'
+ };
+ if (isNullOrUndefined(this.netSliceInstantiateForm.value.ssh_keys) || this.netSliceInstantiateForm.value.ssh_keys === '') {
+ delete this.netSliceInstantiateForm.value.ssh_keys;
+ } else {
+ this.copySSHKey = JSON.parse(JSON.stringify(this.netSliceInstantiateForm.value.ssh_keys));
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.netSliceInstantiateForm.get('ssh_keys').setValue(this.copySSHKey);
+ }
+ if (isNullOrUndefined(this.netSliceInstantiateForm.value.config) || this.netSliceInstantiateForm.value.config === '') {
+ delete this.netSliceInstantiateForm.value.config;
+ } else {
+ const validJSON: boolean = this.sharedService.checkJson(this.netSliceInstantiateForm.value.config);
+ if (validJSON) {
+ this.netSliceInstantiateForm.value.config = JSON.parse(this.netSliceInstantiateForm.value.config);
+ Object.keys(this.netSliceInstantiateForm.value.config).forEach((item: string) => {
+ this.netSliceInstantiateForm.value[item] = this.netSliceInstantiateForm.value.config[item];
+ });
+ delete this.netSliceInstantiateForm.value.config;
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('INVALIDCONFIG'));
+ return;
+ }
+ }
+ this.isLoadingResults = true;
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.NETWORKSLICEINSTANCESCONTENT_URL,
+ httpOptions: { headers: this.headers }
+ };
+ this.restService.postResource(apiURLHeader, this.netSliceInstantiateForm.value)
+ .subscribe((result: {}) => {
+ this.activeModal.close(modalData);
+ this.isLoadingResults = false;
+ this.notifierService.notify('success', this.netSliceInstantiateForm.value.nsiName +
+ this.translateService.instant('PAGE.NETSLICE.CREATEDSUCCESSFULLY'));
+ this.router.navigate(['/instances/netslice']).catch();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'post');
+ if (!isNullOrUndefined(this.copySSHKey)) {
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.netSliceInstantiateForm.get('ssh_keys').setValue(this.copySSHKey);
+ }
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** ssh file process @private */
+ public sshFile(files: FileList): void {
+ if (files && files.length === 1) {
+ this.sharedService.getFileString(files, 'pub').then((fileContent: string): void => {
+ const getSSHJson: string = jsyaml.load(fileContent, { json: true });
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.netSliceInstantiateForm.get('ssh_keys').setValue(getSSHJson);
+ }).catch((err: string): void => {
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('PUBFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ this.fileInputSSHLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
+ this.fileInputSSH.nativeElement.value = null;
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ this.fileInputSSHLabel.nativeElement.innerText = files[0].name;
+ this.fileInputSSH.nativeElement.value = null;
+ }
+
+ /** Config file process @private */
+ public configFile(files: FileList): void {
+ if (files && files.length === 1) {
+ this.sharedService.getFileString(files, 'yaml').then((fileContent: string): void => {
+ const getConfigJson: string = jsyaml.load(fileContent, { json: true });
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.netSliceInstantiateForm.get('config').setValue(JSON.stringify(getConfigJson));
+ }).catch((err: string): void => {
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ this.fileInputConfigLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
+ this.fileInputConfig.nativeElement.value = null;
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ this.fileInputConfigLabel.nativeElement.innerText = files[0].name;
+ this.fileInputConfig.nativeElement.value = null;
+ }
+
+}
diff --git a/src/app/packages/instantiate-ns/InstantiateNsComponent.html b/src/app/packages/instantiate-ns/InstantiateNsComponent.html
new file mode 100644
index 0000000..dabd469
--- /dev/null
+++ b/src/app/packages/instantiate-ns/InstantiateNsComponent.html
@@ -0,0 +1,103 @@
+<!--
+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)
+-->
+<form [formGroup]="instantiateForm" (ngSubmit)="instantiateNsSubmit();">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'PAGE.INSTANCEINSTANTIATE.NEWINSTANCE' | translate}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </div>
+ <div class="modal-body modal-body-custom-height instantiate-ns">
+ <div class="form-group row">
+ <label class="col-sm-12 col-form-label mandatory-label"
+ [ngClass]="{'text-danger': instantiateForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <label class="col-sm-4 col-form-label"
+ for="nsName">{{'PAGE.INSTANCEINSTANTIATE.NSNAME' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.INSTANCEINSTANTIATE.NSNAME' | translate}}" type="text" formControlName="nsName"
+ id="nsName" (keydown.space)="$event.preventDefault();"
+ [ngClass]="{ 'is-invalid': submitted && f.nsName.errors }" required>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label"
+ for="nsDescription">{{'PAGE.INSTANCEINSTANTIATE.DESCRIPTION' | translate}}*</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'PAGE.INSTANCEINSTANTIATE.DESCRIPTION' | translate}}"
+ type="text" formControlName="nsDescription" id="nsDescription"
+ [ngClass]="{ 'is-invalid': submitted && f.nsDescription.errors }" required></textarea>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="nsdId">{{'PAGE.INSTANCEINSTANTIATE.NSID' | translate}}*</label>
+ <div class="col-sm-8">
+ <ng-select [items]="nsdSelect" bindLabel="name" bindValue="_id"
+ placeholder="{{'SELECT' | translate}} {{'PAGE.INSTANCEINSTANTIATE.NSID' | translate}}"
+ formControlName="nsdId" [(ngModel)]="nsdId" id="nsdId"
+ [ngClass]="{ 'is-invalid': submitted && f.nsdId.errors }" required>
+ </ng-select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label"
+ for="vimAccountId">{{'PAGE.INSTANCEINSTANTIATE.VIMACCOUNT' | translate}}*</label>
+ <div class="col-sm-8">
+ <ng-select [items]="vimAccountSelect" bindLabel="name" bindValue="_id"
+ placeholder="{{'SELECT' | translate}} {{'PAGE.INSTANCEINSTANTIATE.VIMACCOUNT' | translate}}"
+ formControlName="vimAccountId" [(ngModel)]="vimAccountId" id="vimAccountId"
+ [ngClass]="{ 'is-invalid': submitted && f.vimAccountId.errors }" required>
+ </ng-select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label"
+ for="ssh_keys">{{'PAGE.INSTANCEINSTANTIATE.SSHKEY' | translate}}</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'PAGE.INSTANCEINSTANTIATE.SSHKEYMSG' | translate}}"
+ formControlName="ssh_keys" id="ssh_keys"></textarea>
+ <div class="fileupload-text mt-1 mb-1">{{'FILEUPLOADLABEL' | translate}}</div>
+ <div class="custom-file">
+ <input type="file" #fileInputSSH class="custom-file-input" (change)="sshFile($event.target.files)"
+ id="customSSHFile">
+ <label class="custom-file-label" #fileInputSSHLabel
+ for="customSSHFile">{{'CHOOSEFILE' | translate}}</label>
+ </div>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="config">{{'CONFIG' | translate}}</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'YAMLCONFIG' | translate}}" formControlName="config"
+ id="config"></textarea>
+ <div class="fileupload-text mt-1 mb-1">{{'FILEUPLOADLABEL' | translate}}</div>
+ <div class="custom-file">
+ <input type="file" #fileInputConfig class="custom-file-input"
+ (change)="configFile($event.target.files)" id="customConfigFile">
+ <label class="custom-file-label" #fileInputConfigLabel
+ for="customConfigFile">{{'CHOOSEFILE' | translate}}</label>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'CREATE' | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/packages/instantiate-ns/InstantiateNsComponent.scss b/src/app/packages/instantiate-ns/InstantiateNsComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/packages/instantiate-ns/InstantiateNsComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/packages/instantiate-ns/InstantiateNsComponent.ts b/src/app/packages/instantiate-ns/InstantiateNsComponent.ts
new file mode 100644
index 0000000..515a24b
--- /dev/null
+++ b/src/app/packages/instantiate-ns/InstantiateNsComponent.ts
@@ -0,0 +1,268 @@
+/*
+ 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 Instantiate NS Modal Component.
+ */
+import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { Router } from '@angular/router';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import * as jsyaml from 'js-yaml';
+import { NSCREATEPARAMS, NSData, NSDDetails } from 'NSDModel';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+import { VimAccountDetails } from 'VimAccountModel';
+
+/**
+ * Creating component
+ * @Component takes InstantiateNsComponent.html as template url
+ */
+@Component({
+ selector: 'app-instantiate-ns',
+ templateUrl: './InstantiateNsComponent.html',
+ styleUrls: ['./InstantiateNsComponent.scss']
+})
+/** Exporting a class @exports InstantiateNsComponent */
+export class InstantiateNsComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Contains all the nsd data collections */
+ public nsdSelect: NSDDetails;
+
+ /** FormGroup instance added to the form @ html @public */
+ public instantiateForm: FormGroup;
+
+ /** Contains all vim account collections */
+ public vimAccountSelect: VimAccountDetails;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Variable set for twoway binding @public */
+ public nsdId: string;
+
+ /** Variable set for twoway bindng @public */
+ public vimAccountId: string;
+
+ /** Form submission Add */
+ public submitted: boolean = false;
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Element ref for fileInputConfig @public */
+ @ViewChild('fileInputConfig', { static: true }) public fileInputConfig: ElementRef;
+
+ /** Element ref for fileInputConfigLabel @public */
+ @ViewChild('fileInputConfigLabel', { static: true }) public fileInputConfigLabel: ElementRef;
+
+ /** Element ref for fileInputSSH @public */
+ @ViewChild('fileInputSSH', { static: true }) public fileInputSSH: ElementRef;
+
+ /** Element ref for fileInputSSHLabel @public */
+ @ViewChild('fileInputSSHLabel', { static: true }) public fileInputSSHLabel: ElementRef;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** Utilizes rest service for any CRUD operations @private */
+ private restService: RestService;
+
+ /** Utilizes data service for any communication @private */
+ private dataService: DataService;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Contains the ssh key to be hosted in dom @private */
+ private copySSHKey: string;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.formBuilder = this.injector.get(FormBuilder);
+ this.dataService = this.injector.get(DataService);
+ this.notifierService = this.injector.get(NotifierService);
+ this.router = this.injector.get(Router);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ public ngOnInit(): void {
+ /** Setting up initial value for NSD */
+ this.dataService.currentMessage.subscribe((event: NSData) => {
+ if (event.identifier !== undefined || event.identifier !== '' || event.identifier !== null) {
+ this.nsdId = event.identifier;
+ }
+ });
+ /** On Initializing call the methods */
+ this.instantiateFormAction();
+ this.getDetailsnsd();
+ this.getDetailsvimAccount();
+ }
+
+ /** On modal initializing forms @public */
+ public instantiateFormAction(): void {
+ this.instantiateForm = this.formBuilder.group({
+ nsName: ['', [Validators.required]],
+ nsDescription: ['', [Validators.required]],
+ nsdId: ['', [Validators.required]],
+ vimAccountId: ['', [Validators.required]],
+ ssh_keys: [null],
+ config: [null]
+ });
+ }
+
+ /** Convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.instantiateForm.controls; }
+
+ /** Call the nsd details in the selection options @public */
+ public getDetailsnsd(): void {
+ this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL).subscribe((nsPackages: NSDDetails) => {
+ this.nsdSelect = nsPackages;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+
+ /** Call the vimAccount details in the selection options @public */
+ public getDetailsvimAccount(): void {
+ this.restService.getResource(environment.VIMACCOUNTS_URL).subscribe((vimAccounts: VimAccountDetails) => {
+ this.vimAccountSelect = vimAccounts;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+
+ /** On modal submit instantiateNsSubmit will called @public */
+ public instantiateNsSubmit(): void {
+ this.submitted = true;
+ this.sharedService.cleanForm(this.instantiateForm);
+ if (this.instantiateForm.invalid) {
+ return;
+ }
+ const modalData: MODALCLOSERESPONSEDATA = {
+ message: 'Done'
+ };
+ if (isNullOrUndefined(this.instantiateForm.value.ssh_keys) || this.instantiateForm.value.ssh_keys === '') {
+ delete this.instantiateForm.value.ssh_keys;
+ } else {
+ this.copySSHKey = JSON.parse(JSON.stringify(this.instantiateForm.value.ssh_keys));
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.instantiateForm.get('ssh_keys').setValue([this.copySSHKey]);
+ }
+ if (isNullOrUndefined(this.instantiateForm.value.config) || this.instantiateForm.value.config === '') {
+ delete this.instantiateForm.value.config;
+ } else {
+ const validJSON: boolean = this.sharedService.checkJson(this.instantiateForm.value.config);
+ if (validJSON) {
+ this.instantiateForm.value.config = JSON.parse(this.instantiateForm.value.config);
+ Object.keys(this.instantiateForm.value.config).forEach((item: string) => {
+ this.instantiateForm.value[item] = this.instantiateForm.value.config[item];
+ });
+ delete this.instantiateForm.value.config;
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('INVALIDCONFIG'));
+ return;
+ }
+ }
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.NSINSTANCESCONTENT_URL
+ };
+ this.isLoadingResults = true;
+ this.restService.postResource(apiURLHeader, this.instantiateForm.value).subscribe((result: {}) => {
+ this.activeModal.close(modalData);
+ this.notifierService.notify('success', this.instantiateForm.value.nsName +
+ this.translateService.instant('PAGE.NSINSTANCE.CREATEDSUCCESSFULLY'));
+ this.router.navigate(['/instances/ns']).catch();
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'post');
+ if (!isNullOrUndefined(this.copySSHKey)) {
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.instantiateForm.get('ssh_keys').setValue(this.copySSHKey);
+ }
+ });
+ }
+
+ /** ssh file process @private */
+ public sshFile(files: FileList): void {
+ if (files && files.length === 1) {
+ this.sharedService.getFileString(files, 'pub').then((fileContent: string): void => {
+ const getSSHJson: string = jsyaml.load(fileContent, { json: true });
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.instantiateForm.get('ssh_keys').setValue(getSSHJson);
+ }).catch((err: string): void => {
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('PUBFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ this.fileInputSSHLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
+ this.fileInputSSH.nativeElement.value = null;
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ this.fileInputSSHLabel.nativeElement.innerText = files[0].name;
+ this.fileInputSSH.nativeElement.value = null;
+ }
+
+ /** Config file process @private */
+ public configFile(files: FileList): void {
+ if (files && files.length === 1) {
+ this.sharedService.getFileString(files, 'yaml').then((fileContent: string): void => {
+ const getConfigJson: string = jsyaml.load(fileContent, { json: true });
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.instantiateForm.get('config').setValue(JSON.stringify(getConfigJson));
+ }).catch((err: string): void => {
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ this.fileInputConfigLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
+ this.fileInputConfig.nativeElement.value = null;
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ this.fileInputConfigLabel.nativeElement.innerText = files[0].name;
+ this.fileInputConfig.nativeElement.value = null;
+ }
+}
diff --git a/src/app/packages/netslice-template/NetsliceTemplateComponent.html b/src/app/packages/netslice-template/NetsliceTemplateComponent.html
new file mode 100644
index 0000000..6d41e11
--- /dev/null
+++ b/src/app/packages/netslice-template/NetsliceTemplateComponent.html
@@ -0,0 +1,38 @@
+<!--
+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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'PAGE.DASHBOARD.NETSLICETEMPLATE' | translate}}</div>
+</div>
+<div class="row">
+ <div class="dropzone mt-2" appDrag (click)="fileInput.click()" (files)="filesDropped($event)">
+ <input hidden type="file" #fileInput (change)="filesDropped($event.target.files)">
+ <div class="text-wrapper">
+ <div class="text-center file-drop-title">
+ <i class="fas fa-upload"></i> {{'DROPFILES' | translate}}</div>
+ </div>
+ </div>
+</div>
+<div class="row mt-2 mb-0 form-group justify-content-end list-utilites-actions">
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/packages/netslice-template/NetsliceTemplateComponent.scss b/src/app/packages/netslice-template/NetsliceTemplateComponent.scss
new file mode 100644
index 0000000..2c07d1b
--- /dev/null
+++ b/src/app/packages/netslice-template/NetsliceTemplateComponent.scss
@@ -0,0 +1,20 @@
+/*
+ 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)
+*/
+.ng2-smart-th.identifier{
+ width:40% !important;
+}
\ No newline at end of file
diff --git a/src/app/packages/netslice-template/NetsliceTemplateComponent.ts b/src/app/packages/netslice-template/NetsliceTemplateComponent.ts
new file mode 100644
index 0000000..5360518
--- /dev/null
+++ b/src/app/packages/netslice-template/NetsliceTemplateComponent.ts
@@ -0,0 +1,250 @@
+/*
+ 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 Netslice-Template.
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, ViewChild } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { NetslicePackagesActionComponent } from 'NetslicePackagesAction';
+import { NetworkSliceData, NetworkSliceModel } from 'NetworkSliceModel';
+import { LocalDataSource } from 'ng2-smart-table';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes NetsliceTemplateComponent.html as template url
+ */
+@Component({
+ selector: 'app-netslice-template',
+ templateUrl: './NetsliceTemplateComponent.html',
+ styleUrls: ['./NetsliceTemplateComponent.scss']
+})
+/** Exporting a class @exports NetsliceTemplateComponent */
+export class NetsliceTemplateComponent {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Values for map config @public */
+ public selectedRows: object[] = [];
+
+ /** To consume REST API calls @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Columns list of the smart table @public */
+ public columnLists: object = {};
+
+ /** Settings for smarttable to populate the table with columns @public */
+ public settings: object = {};
+
+ /** Values for map config @public */
+ public selectList: object[] = [];
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** Element ref for fileInput @public */
+ @ViewChild('fileInput', { static: true }) public fileInput: ElementRef;
+
+ /** To consume REST API calls @private */
+ private dataService: DataService;
+
+ /** To consume REST API calls @private */
+ private restService: RestService;
+
+ /** Formation of appropriate Data for LocalDatasource @private */
+ private networkSliceData: NetworkSliceData[] = [];
+
+ /** variables holds file information @private */
+ private fileData: string | ArrayBuffer;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.translateService = this.injector.get(TranslateService);
+ this.notifierService = this.injector.get(NotifierService);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.generateColumns();
+ this.generateSettings();
+ this.generateData();
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/x-yaml',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** smart table Header Colums @public */
+ public generateColumns(): void {
+ this.columnLists = {
+ name: { title: this.translateService.instant('NAME'), width: '20%', sortDirection: 'asc' },
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '50%' },
+ usageState: { title: this.translateService.instant('USAGESTATE'), width: '20%' },
+ Actions: {
+ name: 'Actions', width: '10%', filter: false, sort: false, type: 'custom',
+ title: this.translateService.instant('ACTIONS'),
+ valuePrepareFunction: (cell: NetworkSliceData, row: NetworkSliceData): NetworkSliceData => row,
+ renderComponent: NetslicePackagesActionComponent
+ }
+ };
+ }
+
+ /** smart table Data Settings @public */
+ public generateSettings(): void {
+ this.settings = {
+ edit: {
+ editButtonContent: '<i class="fa fa-edit" title="Edit"></i>',
+ confirmSave: true
+ },
+ delete: {
+ deleteButtonContent: '<i class="far fa-trash-alt" title="delete"></i>',
+ confirmDelete: true
+ },
+ columns: this.columnLists,
+ actions: {
+ add: false,
+ edit: false,
+ delete: false,
+ position: 'right'
+ },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** smart table listing manipulation @private */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** smart table listing manipulation @private */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'network-slice' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Drag and drop feature and fetchind the details of files @private */
+ public filesDropped(files: FileList): void {
+ if (files && files.length === 1) {
+ this.isLoadingResults = true;
+ this.sharedService.getFileString(files, 'yaml').then((fileContent: String | ArrayBuffer): void => {
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.NETWORKSLICETEMPLATECONTENT_URL,
+ httpOptions: { headers: this.headers }
+ };
+ this.saveFileData(apiURLHeader, fileContent);
+ }).catch((err: string): void => {
+ this.isLoadingResults = false;
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ }
+
+ /** Post the droped files and reload the page @public */
+ public saveFileData(urlHeader: APIURLHEADER, fileData: {}): void {
+ this.fileInput.nativeElement.value = null;
+ this.restService.postResource(urlHeader, fileData).subscribe((result: {}) => {
+ this.notifierService.notify('success', this.translateService.instant('PAGE.NETSLICE.TEMPLATECREATEDSUCCESSFULLY'));
+ this.generateData();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'post');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** Generate nsData object from loop and return for the datasource @public */
+ public generateNetworkSliceData(networkSlicePackageData: NetworkSliceModel): NetworkSliceData {
+ return {
+ name: networkSlicePackageData.name,
+ identifier: networkSlicePackageData._id,
+ usageState: networkSlicePackageData._admin.usageState
+ };
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+
+ /** Fetching the data from server to Load in the smarttable @protected */
+ protected generateData(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.NETWORKSLICETEMPLATECONTENT_URL).subscribe((networkSliceList: NetworkSliceModel[]) => {
+ this.networkSliceData = [];
+ networkSliceList.forEach((networkSlicePackageData: NetworkSliceModel) => {
+ const networkSliceDataObj: NetworkSliceData = this.generateNetworkSliceData(networkSlicePackageData);
+ this.networkSliceData.push(networkSliceDataObj);
+ });
+ if (this.networkSliceData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.networkSliceData).then((data: boolean) => {
+ this.isLoadingResults = false;
+ }).catch(() => {
+ this.isLoadingResults = false;
+ });
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+}
diff --git a/src/app/packages/ns-packages/NSPackagesComponent.html b/src/app/packages/ns-packages/NSPackagesComponent.html
new file mode 100644
index 0000000..5b67268
--- /dev/null
+++ b/src/app/packages/ns-packages/NSPackagesComponent.html
@@ -0,0 +1,44 @@
+<!--
+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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">NS {{'PACKAGES' | translate}}</div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" placement="top" container="body" ngbTooltip="{{'PAGE.NSPACKAGE.ADDNSPACKAGE' | translate}}"
+ (click)="composeNSPackage()">
+ <i class="fas fa-plus-circle" aria-hidden="true"></i> {{'PAGE.NSPACKAGE.ADDNSPACKAGE' | translate}}
+ </button>
+ </span>
+</div>
+<div class="row">
+ <div class="dropzone mt-2" appDrag (click)="fileInput.click()" (files)="filesDropped($event)">
+ <input hidden type="file" #fileInput (change)="filesDropped($event.target.files)">
+ <div class="text-wrapper">
+ <div class="text-center file-drop-title">
+ <i class="fas fa-upload"></i> {{'DROPFILES' | translate}}</div>
+ </div>
+ </div>
+</div>
+<div class="row mt-2 mb-0 form-group justify-content-end list-utilites-actions">
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/packages/ns-packages/NSPackagesComponent.scss b/src/app/packages/ns-packages/NSPackagesComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/packages/ns-packages/NSPackagesComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/packages/ns-packages/NSPackagesComponent.ts b/src/app/packages/ns-packages/NSPackagesComponent.ts
new file mode 100644
index 0000000..46366e0
--- /dev/null
+++ b/src/app/packages/ns-packages/NSPackagesComponent.ts
@@ -0,0 +1,258 @@
+/*
+ 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 NS-Packages component.
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA } from 'CommonModel';
+import { ComposePackages } from 'ComposePackages';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { LocalDataSource } from 'ng2-smart-table';
+import { NSData, NSDDetails } from 'NSDModel';
+import { NsPackagesActionComponent } from 'NsPackagesAction';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes NSPackagesComponent.html as template url
+ */
+@Component({
+ selector: 'app-ns-packages',
+ templateUrl: './NSPackagesComponent.html',
+ styleUrls: ['./NSPackagesComponent.scss']
+})
+
+/** Exporting a class @exports NSPackagesComponent */
+export class NSPackagesComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Formation of appropriate Data for LocalDatasource @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Columns list of the smart table @public */
+ public columnLists: object = {};
+
+ /** Settings for smarttable to populate the table with columns @public */
+ public settings: object = {};
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** Element ref for fileInput @public */
+ @ViewChild('fileInput', { static: true }) public fileInput: ElementRef;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Formation of appropriate Data for LocalDatasource @private */
+ private nsData: NSData[] = [];
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** variables holds file information @private */
+ private fileData: string | ArrayBuffer;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.sharedService = this.injector.get(SharedService);
+ this.translateService = this.injector.get(TranslateService);
+ this.notifierService = this.injector.get(NotifierService);
+ this.modalService = this.injector.get(NgbModal);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.generateColumns();
+ this.generateSettings();
+ this.generateData();
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/gzip',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** smart table Header Colums @public */
+ public generateColumns(): void {
+ this.columnLists = {
+ shortName: { title: this.translateService.instant('SHORTNAME'), width: '15%', sortDirection: 'asc' },
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' },
+ description: { title: this.translateService.instant('DESCRIPTION'), width: '25%' },
+ vendor: { title: this.translateService.instant('VENDOR'), width: '15%' },
+ version: { title: this.translateService.instant('VERSION'), width: '10%' },
+ Actions: {
+ name: 'Actions', width: '15%', filter: false, sort: false, type: 'custom',
+ title: this.translateService.instant('ACTIONS'),
+ valuePrepareFunction: (cell: NSData, row: NSData): NSData => row, renderComponent: NsPackagesActionComponent
+ }
+ };
+ }
+
+ /** smart table Data Settings @public */
+ public generateSettings(): void {
+ this.settings = {
+ edit: {
+ editButtonContent: '<i class="fa fa-edit" title="Edit"></i>',
+ confirmSave: true
+ },
+ delete: {
+ deleteButtonContent: '<i class="far fa-trash-alt" title="delete"></i>',
+ confirmDelete: true
+ },
+ columns: this.columnLists,
+ actions: {
+ add: false,
+ edit: false,
+ delete: false,
+ position: 'right'
+ },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** smart table listing manipulation @public */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** smart table listing manipulation @public */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'ns-package' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Drag and drop feature and fetchind the details of files @public */
+ public filesDropped(files: FileList): void {
+ if (files && files.length === 1) {
+ this.isLoadingResults = true;
+ this.sharedService.getFileString(files, 'gz').then((fileContent: ArrayBuffer): void => {
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.NSDESCRIPTORSCONTENT_URL,
+ httpOptions: { headers: this.headers }
+ };
+ this.saveFileData(apiURLHeader, fileContent);
+ }).catch((err: string): void => {
+ this.isLoadingResults = false;
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('GZFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ }
+
+ /** Post the droped files and reload the page @public */
+ public saveFileData(urlHeader: APIURLHEADER, fileData: {}): void {
+ this.fileInput.nativeElement.value = null;
+ this.restService.postResource(urlHeader, fileData).subscribe((result: {}) => {
+ this.notifierService.notify('success', this.translateService.instant('PAGE.NSPACKAGE.CREATEDSUCCESSFULLY'));
+ this.generateData();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'post');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** Generate nsData object from loop and return for the datasource @public */
+ public generateNSData(nsdpackagedata: NSDDetails): NSData {
+ return {
+ shortName: nsdpackagedata['short-name'],
+ identifier: nsdpackagedata._id,
+ description: nsdpackagedata.description,
+ vendor: nsdpackagedata.vendor,
+ version: nsdpackagedata.version
+ };
+ }
+
+ /** Fetching the data from server to Load in the smarttable @public */
+ public generateData(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL).subscribe((nsdPackageData: NSDDetails[]) => {
+ this.nsData = [];
+ nsdPackageData.forEach((nsdpackagedata: NSDDetails) => {
+ const nsDataObj: NSData = this.generateNSData(nsdpackagedata);
+ this.nsData.push(nsDataObj);
+ });
+ if (this.nsData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.nsData).then((data: boolean) => {
+ this.isLoadingResults = false;
+ }).catch(() => {
+ this.isLoadingResults = false;
+ });
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+ /** Handle compose new ns package method @public */
+ public composeNSPackage(): void {
+ this.modalService.open(ComposePackages, { backdrop: 'static' }).componentInstance.params = { page: 'ns-package' };
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+}
diff --git a/src/app/packages/ns-packages/ns-composer/NSComposerComponent.html b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.html
new file mode 100644
index 0000000..4e78c16
--- /dev/null
+++ b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.html
@@ -0,0 +1,264 @@
+<!--
+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)
+-->
+<ng-sidebar-container class="ns-topology-sidebar-container">
+ <!-- A sidebar -->
+ <ng-sidebar [(opened)]="sideBarOpened" position="left">
+ <div class="sidebar-header">
+ <span class="topology_title" *ngIf="isShowNSDDetails">{{'PAGE.TOPOLOGY.NSD' | translate}}</span>
+ <span class="topology_title" *ngIf="isShowVLDetails">{{'PAGE.TOPOLOGY.VIRTUALLINK' | translate}}</span>
+ <span class="topology_title" *ngIf="isShowVNFDetails">{{'PAGE.TOPOLOGY.VNF' | translate}}</span>
+ <span class="topology_title" *ngIf="isShowCPDetails">{{'PAGE.TOPOLOGY.CONNECTIONPOINT' | translate}}</span>
+ <button (click)="toggleSidebar()" class="close" type="button">
+ <i class="fas fa-times-circle text-danger" aria-hidden="true"></i>
+ </button>
+ </div>
+ <div class="sidebar-body">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="isShowNSDDetails">
+ <div class="row">
+ <div class="col-12 p-0">
+ <form autocomplete="off">
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'SHORTNAME' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="{{ 'SHORTNAME' | translate }}" name="shortName"
+ [(ngModel)]="vnfdPackageDetails.shortName">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'VENDOR' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="{{ 'VENDOR' | translate }}" name="vendor"
+ [(ngModel)]="vnfdPackageDetails.vendor">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'DESCRIPTION' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <textarea type="text" class="form-control" placeholder="{{ 'DESCRIPTION' | translate }}"
+ name="description" [(ngModel)]="vnfdPackageDetails.description"></textarea>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'VERSION' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="{{ 'VERSION' | translate }}" name="version"
+ [(ngModel)]="vnfdPackageDetails.version">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'ID' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="{{ 'ID' | translate }}" name="id"
+ [(ngModel)]="vnfdPackageDetails.id">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'NAME' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="{{ 'NAME' | translate }}" name="name"
+ [(ngModel)]="vnfdPackageDetails.name">
+ </div>
+ </div>
+ <button type="button" class="btn btn-primary" (click)="saveNSD()" placement="top"
+ ngbTooltip="Save">
+ <i class="fas fa-save"></i> {{'SAVE' | translate}}
+ </button>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="isShowVLDetails">
+ <div class="row">
+ <div class="col-12 p-0">
+ <form autocomplete="off">
+ <div class="form-group row">
+ <label class="col-sm-4 p-0 col-form-label">{{ 'NAME' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="{{ 'NAME' | translate }}" name="name"
+ [(ngModel)]="vlDetails.name">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 p-0 col-form-label">{{'PAGE.NSPACKAGE.NSCOMPOSE.MGMTNETWORK' | translate}}</label>
+ <div class="col-sm-8 p-0">
+ <select class="form-control custom-select" name="mgmt-network" [(ngModel)]="vlDetails['mgmt-network']">
+ <option [value]="true">True</option>
+ <option [value]="false">False</option>
+ </select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 p-0 col-form-label">{{'PAGE.NSPACKAGE.NSCOMPOSE.VIMNETWORKNAME' | translate}}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="Vim network name" name="vim-network-name"
+ [(ngModel)]="vlDetails['vim-network-name']">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 p-0 col-form-label">{{'TYPE' | translate}}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="Type" name="type" [(ngModel)]="vlDetails.type">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 p-0 col-form-label">{{ 'ID' | translate }}</label>
+ <div class="col-sm-8 p-0">
+ <input type="text" class="form-control" placeholder="{{ 'ID' | translate }}" name="id"
+ [(ngModel)]="vlDetails.id">
+ </div>
+ </div>
+ <button type="button" class="btn btn-primary" placement="top" ngbTooltip="Save"
+ (click)="saveVL(vlDetails.id)">
+ <i class="fas fa-save"></i> {{'SAVE' | translate}}
+ </button>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="isShowVNFDetails">
+ <div class="row">
+ <div class="col-12 p-0">
+ <table class="table table-bordered text-dark custom-table">
+ <tbody>
+ <tr>
+ <td>{{'PAGE.NSPACKAGE.NSCOMPOSE.MEMBER-VNF-INDEX' | translate}}</td>
+ <td>{{ vnfData['member-vnf-index'] }}</td>
+ </tr>
+ <tr>
+ <td>{{'PAGE.NSPACKAGE.NSCOMPOSE.VNFD-ID-REF' | translate}}</td>
+ <td>{{ vnfData['vnfd-id-ref'] }}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="isShowCPDetails">
+ <div class="row">
+ <div class="col-12 p-0">
+ <table class="table table-bordered text-dark custom-table">
+ <tbody>
+ <tr>
+ <td>{{'PAGE.NSPACKAGE.NSCOMPOSE.VLD-ID' | translate}}</td>
+ <td>{{ vlDetails['name'] }}</td>
+ </tr>
+ <tr>
+ <td>{{'PAGE.NSPACKAGE.NSCOMPOSE.VNFD-CP-REF' | translate}}</td>
+ <td>{{ cpData['vnfd-connection-point-ref'] }}</td>
+ </tr>
+ <tr>
+ <td>{{'MEMBERINDEX' | translate}}</td>
+ <td>{{ cpData['member-vnf-index-ref'] }}</td>
+ </tr>
+ <tr>
+ <td>{{'PAGE.NSPACKAGE.NSCOMPOSE.VNFD-ID-REF' | translate}}</td>
+ <td>{{ cpData['vnfd-id-ref'] }}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </ng-sidebar>
+ <!-- Page content -->
+ <div ng-sidebar-content>
+ <button (click)="toggleSidebar()" class="btn btn-default" placement="right" ngbTooltip="{{'OPEN' | translate }}">
+ <i class="fa fa-arrow-right detail-sidebar" aria-hidden="true"></i>
+ </button>
+ </div>
+</ng-sidebar-container>
+<div class="container-fluid text-dark">
+ <div class="row bg-white ns-composer-form">
+ <div class="col-xs-3 col-sm-3 col-md-3 col-lg-3 pl-0 px-0">
+ <div class="row">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2">
+ <fieldset class="p-2">
+ <legend class="vl-legend">
+ {{'PAGE.TOPOLOGY.SELECTELEMENT' | translate}}
+ </legend>
+ <ul class="list-group list-group-flush dragable">
+ <li class="list-group-item" draggable="true" (dragstart)="drag($event)" id="vl">
+ <img src="assets/images/VL.svg" class="ns-svg" draggable="false"/>
+ <span class="span-overflow-text font-weight-bold">{{'PAGE.TOPOLOGY.VL' | translate}}</span>
+ <span class="drag-icon pull-right"><i class="fas fa-arrows-alt"></i></span>
+ </li>
+ </ul>
+ </fieldset>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
+ <fieldset class="p-2">
+ <legend class="vnfd-legend">
+ {{'PAGE.TOPOLOGY.VNFD' | translate}}
+ </legend>
+ <ul class="list-group list-group-flush dragable scroll-box">
+ <li id="list['id']" class="list-group-item" draggable="true" (dragstart)="drag($event)"
+ [attr.data-id]="list['id']" *ngFor="let list of vnfList" placement="top"
+ container="body" ngbTooltip="{{ list['short-name'] }}">
+ <img src="assets/images/VNFD.svg" class="ns-svg" draggable="false"/>
+ <span class="span-overflow-text font-weight-bold">{{ list['short-name'] }}</span>
+ <span class="drag-icon pull-right"><i class="fas fa-arrows-alt"></i></span>
+ </li>
+ </ul>
+ </fieldset>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-9 col-sm-9 col-md-9 col-lg-9">
+ <div class="row">
+ <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 pl-0">
+ <div class="btn-group list" role="group" aria-label="Basic example">
+ <button type="button" class="btn btn-primary topology-btn" (click)="onFreeze()"
+ [class.pinned]="classApplied" placement="top" container="body" ngbTooltip="{{(classApplied ? 'UNFREEZE' : 'FREEZE') | translate}}">
+ <i class="fas fa-thumbtack"></i>
+ </button>
+ <button type="button" class="btn btn-primary topology-btn" (click)="onEdit()" placement="top"
+ container="body" ngbTooltip="{{'EDIT' | translate}}">
+ <i class="fa fa-edit"></i>
+ </button>
+ <button type="button" class="btn btn-primary topology-btn" (click)="showInfo()" placement="top"
+ container="body" ngbTooltip="{{'PAGE.TOPOLOGY.HELP' | translate}}">
+ <i class="fas fa-info"></i>
+ </button>
+ </div>
+ </div>
+ <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 text-right pr-0 badgegroup">
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/VNFD.svg" class="ns-svg" draggable="false"/>
+ <br>{{'PAGE.TOPOLOGY.VNF' | translate}}</span>
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/VL.svg" class="ns-svg" draggable="false"/>
+ <br>{{'PAGE.TOPOLOGY.VL' | translate}}</span>
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/CP.svg" class="ns-svg" draggable="false"/>
+ <br>{{'PAGE.TOPOLOGY.CP' | translate}}</span>
+ </div>
+ </div>
+ <div class="row border-all">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 svg-container">
+ <svg preserveAspectRatio="xMidYMin slice" (drop)="drop($event)" (dragover)="allowDrop($event)"
+ id="graphContainer" #graphContainer>
+ </svg>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/packages/ns-packages/ns-composer/NSComposerComponent.scss b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.scss
new file mode 100644
index 0000000..d750ccc
--- /dev/null
+++ b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/packages/ns-packages/ns-composer/NSComposerComponent.ts b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.ts
new file mode 100644
index 0000000..082496e
--- /dev/null
+++ b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.ts
@@ -0,0 +1,1089 @@
+/*
+ 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 NS Compose Component
+ */
+// tslint:disable: no-increment-decrement
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, ViewChild, ViewEncapsulation } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, CONSTANTNUMBER, ERRORDATA, MODALCLOSERESPONSEDATA, MODALCLOSERESPONSEWITHCP } from 'CommonModel';
+import { ConfirmationTopologyComponent } from 'ConfirmationTopology';
+import * as d3 from 'd3';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import * as HttpStatus from 'http-status-codes';
+import * as jsyaml from 'js-yaml';
+import { COMPOSERNODES, CONSTITUENTVNFD, GRAPHDETAILS, NSDDetails, Tick, TickPath, VLD, VNFDCONNECTIONPOINTREF } from 'NSDModel';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+import { VNFData, VNFDDetails } from 'VNFDModel';
+
+/**
+ * Creating component
+ * @Component takes NSComposerComponent.html as template url
+ */
+@Component({
+ selector: 'app-ns-composer',
+ templateUrl: './NSComposerComponent.html',
+ styleUrls: ['./NSComposerComponent.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+/** Exporting a class @exports NSComposerComponent */
+export class NSComposerComponent {
+ /** To inject services @public */
+ public injector: Injector;
+ /** View child contains graphContainer ref @public */
+ @ViewChild('graphContainer', { static: true }) public graphContainer: ElementRef;
+ /** dataService to pass the data from one component to another @public */
+ public dataService: DataService;
+ /** Contains VNFD Informations @public */
+ public vnfdPackageDetails: VNFData = { identifier: '', shortName: '', vendor: '', description: '', version: '', id: '', name: '' };
+ /** Contains VL Details @public */
+ public vlDetails: VLD = {
+ name: '',
+ 'mgmt-network': true,
+ 'vim-network-name': '',
+ type: '',
+ id: ''
+ };
+ /** Contains the information of the type of modification @public */
+ public putType: string;
+ /** Conatins mousedown action @public */
+ public mousedownNode: COMPOSERNODES = null;
+ /** Conatins mouseup action @public */
+ public mouseupNode: COMPOSERNODES = null;
+ /** Conatins mousedownLink action @public */
+ public mousedownLink: COMPOSERNODES = null;
+ /** Conatins current Selection node action @public */
+ public currentSelectedNode: COMPOSERNODES = null;
+ /** Conatins current Selection node action @public */
+ public currentSelectedLink: COMPOSERNODES = null;
+ /** Need to show the NSD Details @public */
+ public isShowNSDDetails: boolean = true;
+ /** Contains the node information of VL @public */
+ public vlNodes: {}[] = [];
+ /** Need to show the VL Details @public */
+ public isShowVLDetails: boolean = false;
+ /** Contains the node information of VNF @public */
+ public vnfNodes: {}[] = [];
+ /** contains the VNF Details @public */
+ public vnfData: CONSTITUENTVNFD;
+ /** Need to show the VNF Details @public */
+ public isShowVNFDetails: boolean = false;
+ /** Contains the node information of CP @public */
+ public cpNodes: {}[] = [];
+ /** Need to show the CP Details */
+ public cpData: VNFDCONNECTIONPOINTREF;
+ /** Need to show the VNF Details @public */
+ public isShowCPDetails: boolean = false;
+ /** random number count @public */
+ public randomNumberLength: number;
+ /** Contains the vnfd information @public */
+ public vnfList: VNFDDetails[] = [];
+ /** Add the activeclass for the selected @public */
+ public activeClass: string = 'active';
+ /** Add the fixed class for the freeze @public */
+ public fixedClass: string = 'fixed';
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+ /** Get VNF selected node @public */
+ public getVNFSelectedData: VNFDDetails[];
+ /** Assign the forcesimulation active @public */
+ public forceSimulationActive: boolean = false;
+ /** Assign pinned class for the button when freezed @public */
+ public classApplied: boolean = false;
+ /** Contains sidebar open status @public */
+ public sideBarOpened: boolean = false;
+ /** Contains SVG attributes @private */
+ // tslint:disable-next-line:no-any
+ private svg: any;
+ /** Contains the Drag line */
+ // tslint:disable-next-line: no-any
+ private dragLine: any;
+ /** Contains VL node @private */
+ // tslint:disable-next-line:no-any
+ private vlNode: any;
+ /** Contains VNFD node @private */
+ // tslint:disable-next-line:no-any
+ private vnfdnode: any;
+ /** Contains CP node @private */
+ // tslint:disable-next-line:no-any
+ private cpnode: any;
+ /** Rendered nodes represent VL @private */
+ // tslint:disable-next-line:no-any
+ private gvlNode: any;
+ /** Rendered nodes represent VL @private */
+ // tslint:disable-next-line:no-any
+ private gvnfdNode: any;
+ /** Rendered nodes represent VL @private */
+ // tslint:disable-next-line:no-any
+ private gcpNode: any;
+ /** Contains forced node animations @private */
+ // tslint:disable-next-line:no-any
+ private force: any;
+ /** Contains all the selected node @private */
+ private selectedNode: COMPOSERNODES[] = [];
+ /** variables used for CP @private */
+ private iConnectionPointRef: number = 0;
+ /** Contains the connected point @private */
+ private connectionPoint: string;
+ /** Contains all the NSD information @private */
+ private nsd: string;
+ /** Contains all the VNFD information @private */
+ private vnfd: string;
+ /** Contains id of the node @private */
+ private identifier: string;
+ /** Variables used for cp @private */
+ private jConnectionPointRef: number = 0;
+ /** Contains copy of NSD information @private */
+ private nsdCopy: string;
+ /** Contains the VNFD copy @private */
+ private vnfdCopy: string;
+ /** Contains name of the node @private */
+ private name: string;
+ /** Contains member vnf index value of the node @private */
+ private memberVnfIndexValue: number = 0;
+ /** Contains path information of the node */
+ // tslint:disable-next-line:no-any
+ private path: any;
+ /** Contains the node information @private */
+ private nodes: COMPOSERNODES[] = [];
+ /** Contains the link information of nodes @private */
+ private links: {}[] = [];
+ /** Contains the NS information @private */
+ private nsData: NSDDetails;
+ /** Instance of the rest service @private */
+ private restService: RestService;
+ /** Service holds the router information @private */
+ private router: Router;
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private activatedRoute: ActivatedRoute;
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+ /** Contains lastkeypressed instance @private */
+ private lastKeyDown: number = -1;
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+ /** Setting the Value of connection point refrence of the CP @private */
+ private setVnfdConnectionPointRef: string;
+ /** Setting the Value of VL name for confirmation @private */
+ private vlName: string;
+ /** Setting the Value of VNFD name for confirmation @private */
+ private setVnfdName: string;
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+ /** Contains selected node VLD objects @private */
+ private selectedVLDResult: VLD;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.router = this.injector.get(Router);
+ this.activatedRoute = this.injector.get(ActivatedRoute);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.modalService = this.injector.get(NgbModal);
+ this.sharedService = this.injector.get(SharedService);
+ }
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ this.identifier = this.activatedRoute.snapshot.paramMap.get('id');
+ this.generateData();
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/zip',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ }
+ /** Events handles at drag on D3 region @public */
+ // tslint:disable-next-line:no-any
+ public drag(ev: any): void {
+ if (ev.target.id === 'vl') {
+ ev.dataTransfer.setData('text', ev.target.id);
+ } else {
+ ev.dataTransfer.setData('text', ev.target.attributes['data-id'].value);
+ }
+ }
+ /** On clicking redirect to NS edit page @public */
+ public onEdit(): void {
+ this.router.navigate(['/packages/ns/edit/', this.identifier]).catch(() => {
+ // Catch Navigation Error
+ });
+ }
+ /** Events handles drop at D3 region @public */
+ public drop(ev: DragEvent): void {
+ event.preventDefault();
+ this.name = ev.dataTransfer.getData('text');
+ if (this.name === 'vl') {
+ this.svg.selectAll('*').remove();
+ this.vldropComposer();
+ } else {
+ this.svg.selectAll('*').remove();
+ this.vnfd = ev.dataTransfer.getData('text');
+ this.vnfdropComposer();
+ }
+ }
+ /** Drop VL Composer Data @public */
+ public vldropComposer(): void {
+ this.randomNumberLength = CONSTANTNUMBER.randomNumber;
+ const generateId: string = 'ns_vl_' + this.randomString(
+ this.randomNumberLength,
+ '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ );
+ if (this.nsData.vld !== undefined) {
+ this.nsData.vld.push({
+ 'vim-network-name': 'PUBLIC',
+ name: generateId,
+ 'mgmt-network': true,
+ type: 'ELAN',
+ id: generateId
+ });
+ } else {
+ Object.assign(this.nsData, {
+ vld: [{
+ 'vim-network-name': 'PUBLIC',
+ name: generateId,
+ 'mgmt-network': true,
+ type: 'ELAN',
+ id: generateId
+ }]
+ });
+ }
+ this.putType = 'nsdadd';
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ /** Drop VNFD Composer Data @public */
+ public vnfdropComposer(): void {
+ if (this.nsData['constituent-vnfd'] !== undefined) {
+ this.nsData['constituent-vnfd'].push({
+ 'vnfd-id-ref': this.vnfd,
+ 'member-vnf-index': ++this.memberVnfIndexValue
+ });
+ } else {
+ Object.assign(this.nsData, {
+ 'constituent-vnfd': [{
+ 'vnfd-id-ref': this.vnfd,
+ 'member-vnf-index': ++this.memberVnfIndexValue
+ }]
+ });
+ }
+ this.putType = 'vnfdadd';
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ /** Events handles allow drop on D3 region @public */
+ public allowDrop(ev: DragEvent): void {
+ ev.preventDefault();
+ }
+ /** Save NSD Information @public */
+ public saveNSD(): void {
+ if (this.vnfdPackageDetails.shortName !== undefined) {
+ this.nsData['short-name'] = this.vnfdPackageDetails.shortName;
+ }
+ if (this.vnfdPackageDetails.vendor !== undefined) {
+ this.nsData.vendor = this.vnfdPackageDetails.vendor;
+ }
+ if (this.vnfdPackageDetails.description !== undefined) {
+ this.nsData.description = this.vnfdPackageDetails.description;
+ }
+ if (this.vnfdPackageDetails.version !== undefined) {
+ this.nsData.version = this.vnfdPackageDetails.version;
+ }
+ if (this.vnfdPackageDetails.id !== undefined) {
+ this.nsData.id = this.vnfdPackageDetails.id;
+ }
+ if (this.vnfdPackageDetails.name !== undefined) {
+ this.nsData.name = this.vnfdPackageDetails.name;
+ }
+ this.putType = 'nsdUpdate';
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ /** Save Virtual Link @public */
+ public saveVL(vlid: string): void {
+ this.nsData.vld.forEach((result: VLD) => {
+ if (result.id === vlid) {
+ result.name = this.vlDetails.name;
+ result['mgmt-network'] = !isNullOrUndefined(this.vlDetails['mgmt-network']) ? this.vlDetails['mgmt-network'] : true;
+ result['vim-network-name'] = !isNullOrUndefined(this.vlDetails['vim-network-name']) ? this.vlDetails['vim-network-name'] : '';
+ result.type = this.vlDetails.type;
+ result.id = this.vlDetails.id;
+ }
+ });
+ this.putType = 'vlUpdate';
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ /** Add the new Data @public */
+ public addData(apiURL: string, identifier: string, data: NSDDetails, putType: string): void {
+ this.isLoadingResults = true;
+ let successMessage: string = '';
+ if (putType === 'nsdadd') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNSD';
+ } else if (putType === 'vnfdadd') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDVNFD';
+ } else if (putType === 'cpAdded') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNS';
+ } else if (putType === 'nsdUpdate') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY';
+ } else if (putType === 'vlUpdate') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY';
+ } else if (putType === 'nsddelete') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENSD';
+ } else if (putType === 'vnfddelete') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETEVNFD';
+ } else if (putType === 'nsdelete') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENS';
+ } else if (putType === 'linkdelete') {
+ successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETELINK';
+ }
+ /** Below hide for conflicts with light weight UI */
+ const apiURLHeader: APIURLHEADER = {
+ url: apiURL + '/' + identifier + '/nsd_content',
+ httpOptions: { headers: this.headers }
+ };
+ const nsData: {} = {};
+ nsData['nsd:nsd-catalog'] = {};
+ nsData['nsd:nsd-catalog'].nsd = [];
+ nsData['nsd:nsd-catalog'].nsd.push(data);
+ const descriptorInfo: string = jsyaml.dump(nsData, {sortKeys: true});
+ this.sharedService.targzFile({ packageType: 'nsd', id: this.identifier, descriptor: descriptorInfo })
+ .then((content: ArrayBuffer): void => {
+ this.restService.putResource(apiURLHeader, content).subscribe((res: {}) => {
+ this.generateData();
+ this.notifierService.notify('success', this.translateService.instant(successMessage));
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.generateData();
+ this.restService.handleError(error, 'put');
+ this.isLoadingResults = false;
+ });
+ }).catch((): void => {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ this.isLoadingResults = false;
+ });
+ }
+ /** Show Info @public */
+ public showInfo(): void {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Info';
+ modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.INFO');
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ // empty
+ }
+ }).catch();
+ }
+ /** Event to freeze the animation @public */
+ public onFreeze(): void {
+ this.classApplied = !this.classApplied;
+ const alreadyFixedIsActive: boolean = d3.select('svg#graphContainer').classed(this.fixedClass);
+ d3.select('svg#graphContainer').classed(this.fixedClass, !alreadyFixedIsActive);
+ if (alreadyFixedIsActive) {
+ this.force.stop();
+ }
+ this.forceSimulationActive = alreadyFixedIsActive;
+ this.nodes.forEach((d: COMPOSERNODES) => {
+ d.fx = (alreadyFixedIsActive) ? null : d.x;
+ d.fy = (alreadyFixedIsActive) ? null : d.y;
+ });
+ if (alreadyFixedIsActive) {
+ this.force.restart();
+ }
+ }
+ /** Events handles when dragended @public */
+ public toggleSidebar(): void {
+ this.sideBarOpened = !this.sideBarOpened;
+ this.deselectAllNodes();
+ this.showRightSideInfo(true, false, false, false);
+ }
+ /** Prepare information for node creation of VNFD @private */
+ private generateData(): void {
+ this.generateVNFData();
+ this.generateDataNSDTopology();
+ this.sideBarOpened = false;
+ }
+ /** Prepare the information of the VNFD @private */
+ private generateVNFData(): void {
+ this.restService.getResource(environment.VNFPACKAGESCONTENT_URL).subscribe((vnfdPackageData: VNFDDetails[]) => {
+ this.vnfList = vnfdPackageData;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+ /** Prepare information for node creation of NSD Topology @private */
+ private generateDataNSDTopology(): void {
+ this.nodes = [];
+ this.links = [];
+ this.iConnectionPointRef = 0;
+ this.jConnectionPointRef = 0;
+ this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL + '/' + this.identifier).subscribe((nsData: NSDDetails) => {
+ delete nsData._admin;
+ delete nsData._id;
+ this.nsData = nsData;
+ this.vnfdPackageDetails.shortName = nsData['short-name'];
+ this.vnfdPackageDetails.vendor = nsData.vendor;
+ this.vnfdPackageDetails.description = nsData.description;
+ this.vnfdPackageDetails.version = nsData.version;
+ this.vnfdPackageDetails.id = nsData.id;
+ this.vnfdPackageDetails.name = nsData.name;
+ if (nsData.vld !== undefined) {
+ /** Details of the VL */
+ this.nsDataVLD(nsData);
+ }
+ if (nsData['constituent-vnfd'] !== undefined) {
+ /** Details of the VNFD */
+ this.nsDataConstituentVNFD(nsData);
+ }
+ if (nsData.vld !== undefined) {
+ this.nsDataVLDLinkCreation(nsData);
+ }
+ this.separateAndCreatenode();
+ }, (error: ERRORDATA) => {
+ if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
+ this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
+ } else {
+ this.restService.handleError(error, 'get');
+ }
+ this.isLoadingResults = false;
+ this.isShowNSDDetails = false;
+ });
+ }
+ /** nsData-vld undefined Call this function @private */
+ private nsDataVLD(nsData: NSDDetails): void {
+ nsData.vld.forEach((res: VLD) => {
+ this.nodes.push({ id: res.id, reflexive: false, type: 'vld', name: res.id, selectorId: res.id });
+ this.nsd = res.id;
+ if (res['vnfd-connection-point-ref'] !== undefined) {
+ res['vnfd-connection-point-ref'].forEach((result: VNFDCONNECTIONPOINTREF) => {
+ this.nodes.push(
+ {
+ id: this.nsd + ++this.iConnectionPointRef + ':' + result['vnfd-connection-point-ref'],
+ reflexive: false,
+ type: 'ns',
+ name: result['vnfd-connection-point-ref'],
+ nodeIndex: result['member-vnf-index-ref'],
+ selectorId: result['vnfd-connection-point-ref'] + '_' + result['member-vnf-index-ref'] + '-osm-' + this.nsd
+ });
+ });
+ }
+ });
+ }
+ /** nsData constituent-vnfd undefined Call this function @private */
+ private nsDataConstituentVNFD(nsData: NSDDetails): void {
+ nsData['constituent-vnfd'].forEach((res: CONSTITUENTVNFD) => {
+ this.nodes.push(
+ {
+ id: res['vnfd-id-ref'] + ':' + res['member-vnf-index'],
+ reflexive: false,
+ type: 'vnfd',
+ name: res['vnfd-id-ref'],
+ nodeIndex: res['member-vnf-index'],
+ selectorId: res['vnfd-id-ref'] + '_' + res['member-vnf-index']
+ });
+ this.vnfd = res['vnfd-id-ref'];
+ this.memberVnfIndexValue = res['member-vnf-index'];
+ });
+ }
+
+ /** nsData-vld undefined Call this function @private */
+ private nsDataVLDLinkCreation(nsData: NSDDetails): void {
+ nsData.vld.forEach((res: VLD) => {
+ this.nsdCopy = res.id;
+ if (res['vnfd-connection-point-ref'] !== undefined) {
+ this.nsDataVNFDConnectionPointRefrence(res);
+ }
+ });
+ }
+ /** nsData-vnfd-connection-point-ref undefined Call this function @private */
+ private nsDataVNFDConnectionPointRefrence(res: VLD): void {
+ res['vnfd-connection-point-ref'].forEach((result: VNFDCONNECTIONPOINTREF) => {
+ this.connectionPoint = this.nsdCopy + ++this.jConnectionPointRef + ':' + result['vnfd-connection-point-ref'];
+ this.vnfdCopy = result['vnfd-id-ref'] + ':' + result['member-vnf-index-ref'];
+ const connectionPointPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.connectionPoint);
+ const nsdPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.nsdCopy);
+ const vnfdPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.vnfdCopy);
+ this.links.push(
+ {
+ source: this.nodes[connectionPointPos],
+ target: this.nodes[nsdPos]
+ },
+ {
+ source: this.nodes[connectionPointPos],
+ target: this.nodes[vnfdPos]
+ });
+ });
+ }
+ /** Generate random string @private */
+ private randomString(length: number, chars: string): string {
+ let result: string = '';
+ for (let randomStringRef: number = length; randomStringRef > 0; --randomStringRef) {
+ result += chars[Math.floor(Math.random() * chars.length)];
+ }
+ return result;
+ }
+ /** Separate and create node @private */
+ private separateAndCreatenode(): void {
+ this.seprateNodes(this.nodes);
+ this.createnode(this.nodes);
+ this.isLoadingResults = false;
+ }
+ /** Get the default Configuration of containers @private */
+ private getGraphContainerAttr(): GRAPHDETAILS {
+ return {
+ width: 700,
+ height: 400,
+ nodeHeight: 50,
+ nodeWidth: 35,
+ textX: -35,
+ textY: 30,
+ radius: 5,
+ distance: 50,
+ strength: -500,
+ forcex: 2,
+ forcey: 2,
+ sourcePaddingYes: 17,
+ sourcePaddingNo: 12,
+ targetPaddingYes: 4,
+ targetPaddingNo: 3,
+ alphaTarget: 0.3,
+ imageX: -25,
+ imageY: -25,
+ shiftKeyCode: 17
+ };
+ }
+ /** Separate the nodes along with its tyep @private */
+ private seprateNodes(node: COMPOSERNODES[]): void {
+ this.vlNodes = []; this.vnfNodes = []; this.cpNodes = [];
+ node.forEach((nodeList: COMPOSERNODES) => {
+ if (nodeList.type === 'vld') {
+ this.vlNodes.push(nodeList);
+ } else if (nodeList.type === 'vnfd') {
+ this.vnfNodes.push(nodeList);
+ } else if (nodeList.type === 'ns') {
+ this.cpNodes.push(nodeList);
+ }
+ });
+ }
+ /** Node is created and render at D3 region @private */
+ private createnode(node: COMPOSERNODES[]): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ d3.selectAll('svg#graphContainer > *').remove();
+ d3.select(window).on('keydown', () => { this.keyDown(); });
+ d3.select(window).on('keyup', () => { this.keyUp(); });
+ this.svg = d3.select('#graphContainer')
+ .attr('oncontextmenu', 'return false;')
+ .attr('width', graphContainerAttr.width)
+ .attr('height', graphContainerAttr.height)
+ .on('mousemove', () => { this.mousemove(); });
+ this.force = d3.forceSimulation()
+ .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength))
+ .force('link', d3.forceLink().id((d: TickPath) => d.id).distance(graphContainerAttr.distance))
+ .force('center', d3.forceCenter(graphContainerAttr.width / graphContainerAttr.forcex,
+ graphContainerAttr.height / graphContainerAttr.forcey))
+ .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex))
+ .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey))
+ .on('tick', () => { this.tick(); });
+ this.dragLine = this.svg.append('svg:path').attr('class', 'link dragline hidden').attr('d', 'M0,0L0,0');
+ this.path = this.svg.append('svg:g').selectAll('path');
+ this.vlNode = this.svg.append('svg:g').selectAll('vlnode');
+ this.vnfdnode = this.svg.append('svg:g').selectAll('vnfdnode');
+ this.cpnode = this.svg.append('svg:g').selectAll('cpnode');
+ // app starts here
+ this.restart(node);
+ }
+ /** update force layout (called automatically each iteration) @private */
+ private tick(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ // draw directed edges with proper padding from node centers
+ this.path.attr('class', 'link').attr('d', (d: Tick) => {
+ const deltaX: number = d.target.x - d.source.x;
+ const deltaY: number = d.target.y - d.source.y;
+ const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+ const normX: number = deltaX / dist;
+ const normY: number = deltaY / dist;
+ const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo;
+ const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo;
+ const sourceX: number = d.source.x + (sourcePadding * normX);
+ const sourceY: number = d.source.y + (sourcePadding * normY);
+ const targetX: number = d.target.x - (targetPadding * normX);
+ const targetY: number = d.target.y - (targetPadding * normY);
+ return `M${sourceX},${sourceY}L${targetX},${targetY}`;
+ }).on('dblclick', (d: Tick) => { this.getDeleteLinkConfirmation(d); });
+ this.vlNode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
+ this.vnfdnode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
+ this.cpnode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
+ }
+ /** Update graph (called when needed) at D3 region @private */
+ private restart(node: COMPOSERNODES[]): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.path = this.path.data(this.links);
+ this.vlNode = this.vlNode.data(this.vlNodes, (d: COMPOSERNODES) => d.id);
+ this.vnfdnode = this.vnfdnode.data(this.vnfNodes, (d: COMPOSERNODES) => d.id);
+ this.cpnode = this.cpnode.data(this.cpNodes, (d: COMPOSERNODES) => d.id);
+ this.resetAndCreateNodes();
+ this.force.nodes(node).force('link').links(this.links);
+ this.force.alphaTarget(graphContainerAttr.alphaTarget).restart();
+ }
+ /** Rest and create nodes @private */
+ private resetAndCreateNodes(): void {
+ this.path.exit().remove();
+ this.vlNode.exit().remove();
+ this.vnfdnode.exit().remove();
+ this.cpnode.exit().remove();
+ this.getPathNodes();
+ this.getVLNodes();
+ this.getVNFDNodes();
+ this.getCPNodes();
+ this.path.merge(this.path);
+ this.vlNode = this.gvlNode.merge(this.vlNode);
+ this.vnfdnode = this.gvnfdNode.merge(this.vnfdnode);
+ this.cpnode = this.gcpNode.merge(this.cpnode);
+ }
+ /** setting the Path @private */
+ private getPathNodes(): void {
+ this.path = this.path.enter().append('svg:path');
+ }
+ /** Setting all the VL nodes @private */
+ private getVLNodes(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gvlNode = this.vlNode.enter().append('svg:g');
+ this.gvlNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gvlNode.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/VL.svg')
+ .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); })
+ .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); })
+ .on('click', (d: COMPOSERNODES) => { this.singleClick(this.vlNode, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gvlNode.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: COMPOSERNODES) => d.id);
+ }
+ /** Setting all the VNFD nodes @private */
+ private getVNFDNodes(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gvnfdNode = this.vnfdnode.enter().append('svg:g');
+ this.gvnfdNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gvnfdNode.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/VNFD.svg')
+ .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); })
+ .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); })
+ .on('click', (d: COMPOSERNODES) => { this.singleClick(this.vnfdnode, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gvnfdNode.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: COMPOSERNODES) => d.id);
+ }
+ /** Setting all the CP nodes @private */
+ private getCPNodes(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gcpNode = this.cpnode.enter().append('svg:g');
+ this.gcpNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gcpNode.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/CP.svg')
+ .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); })
+ .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); })
+ .on('click', (d: COMPOSERNODES) => { this.singleClick(this.cpnode, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gcpNode.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: COMPOSERNODES) => d.id);
+ }
+ /** Events handles when mousemove it will capture the selected node data @private */
+ private mousemove(): void {
+ if (!this.mousedownNode) { return; }
+ this.dragLine.attr('d',
+ `M${this.mousedownNode.x},${this.mousedownNode.y}L${d3.mouse(d3.event.currentTarget)[0]},${d3.mouse(d3.event.currentTarget)[1]}`);
+ }
+ /** Get confirmation Before Deleting the Link in Topology @private */
+ private getAddConfirmation(mouseData: COMPOSERNODES, getNsData: NSDDetails, addType: string, getVLDIndex: number): void {
+ let findVNFName: string = '';
+ let findVLDID: string = '';
+ if (mouseData.type === 'vld') {
+ findVNFName = this.mouseupNode.name;
+ findVLDID = this.mousedownNode.id;
+ } else {
+ findVNFName = this.mousedownNode.name;
+ findVLDID = this.mouseupNode.id;
+ }
+ getNsData.vld.forEach((result: VLD) => {
+ if (result.id === findVLDID) {
+ this.vlName = result.name;
+ this.getVNFSelectedData = this.vnfList.filter((vnfList: VNFDDetails) => vnfList.id === findVNFName);
+ this.setVnfdConnectionPointRef = this.getVNFSelectedData[0]['mgmt-interface'].cp;
+ this.setVnfdName = this.getVNFSelectedData[0].name;
+ this.selectedVLDResult = result;
+ }
+ });
+ if (this.vlName !== undefined && this.setVnfdName !== undefined && this.setVnfdConnectionPointRef !== undefined) {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Add';
+ modalRef.componentInstance.cpDetails = this.getVNFSelectedData[0]['connection-point'];
+ this.translateService.get('PAGE.TOPOLOGY.ADDINGCP', {
+ vlname: '<b>' + this.vlName + '</b>',
+ vnfdname: '<b>' + this.setVnfdName + '</b>',
+ cpname: '<b>' + this.setVnfdConnectionPointRef + '</b>'
+ }).subscribe((res: string) => {
+ modalRef.componentInstance.topologyname = res;
+ });
+ modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.CONNECTIONPOINT');
+ modalRef.result.then((result: MODALCLOSERESPONSEWITHCP) => {
+ if (result) {
+ this.nsData = getNsData;
+ this.generateCPForVNF(this.selectedVLDResult, result.connection_point, getVLDIndex);
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, getNsData, addType);
+ } else {
+ this.deselectPath();
+ }
+ }).catch();
+ } else {
+ this.deselectPath();
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ }
+
+ /** Generate connection point for vnf using vld @private */
+ private generateCPForVNF(result: VLD, cp: string, getVLDIndex: number): void {
+ if (result['vnfd-connection-point-ref'] !== undefined) {
+ result['vnfd-connection-point-ref'].push({
+ 'member-vnf-index-ref': getVLDIndex,
+ 'vnfd-connection-point-ref': cp,
+ 'vnfd-id-ref': this.getVNFSelectedData[0].name
+ });
+ } else {
+ Object.assign(result, {
+ 'vnfd-connection-point-ref': [{
+ 'member-vnf-index-ref': getVLDIndex,
+ 'vnfd-connection-point-ref': cp,
+ 'vnfd-id-ref': this.getVNFSelectedData[0].name
+ }]
+ });
+ }
+ }
+
+ /** Events handles when mousedown click it will capture the selected node data @private */
+ private mouseDown(d: COMPOSERNODES): void {
+ event.preventDefault();
+ if (d3.event.ctrlKey) { return; }
+ if (d3.event.shiftKey) {
+ if (d.type === 'vnfd') {
+ this.selectedNode.push(d);
+ }
+ this.mousedownNode = d;
+ this.currentSelectedNode = (this.mousedownNode === this.currentSelectedNode) ? null : this.mousedownNode;
+ this.currentSelectedLink = null;
+ this.dragLine.style('marker-end', 'url(#end-arrow)').classed('hidden', false)
+ .attr('d', `M${this.mousedownNode.x},${this.mousedownNode.y}L${this.mousedownNode.x},${this.mousedownNode.y}`);
+ }
+ }
+ /** Event handles when mouseup event occures @private */
+ private mouseUp(d: COMPOSERNODES): void {
+ if (!this.mousedownNode) { return; }
+ this.dragLine.classed('hidden', true).style('marker-end', '');
+ this.mouseupNode = d;
+ if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vnfd') {
+ const getOldVLDIndex: string[] = this.mouseupNode.id.split(':');
+ const setOldVLDindex: number = +getOldVLDIndex[1];
+ this.putType = 'cpAdded';
+ this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex);
+ } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vld') {
+ const getOldVLDIndex: string[] = this.mousedownNode.id.split(':');
+ const setOldVLDindex: number = +getOldVLDIndex[1];
+ this.putType = 'cpAdded';
+ this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex);
+ } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'ns') {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
+ } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'ns') {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
+ } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vld') {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVL'));
+ } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vnfd') {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNF'));
+ } else if (this.mousedownNode.type === 'ns' && this.mouseupNode.type === 'ns') {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKCP'));
+ } else {
+ this.deselectPath();
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVLVNF'));
+ }
+ this.resetMouseVars();
+ // select new link
+ this.currentSelectedLink = d;
+ this.currentSelectedNode = null;
+ }
+ /** Mosue Drag Line false if it is not satisfied @private */
+ private deselectPath(): void {
+ this.dragLine.classed('hidden', true).style('marker-end', '').attr('d', 'M0,0L0,0');
+ }
+ /** reset Mouse varaibles @private */
+ private resetMouseVars(): void {
+ this.mousedownNode = null;
+ this.mouseupNode = null;
+ this.mousedownLink = null;
+ }
+ /** De-select all the selected nodes @private */
+ private deselectAllNodes(): void {
+ this.vlNode.select('image').classed(this.activeClass, false);
+ this.vnfdnode.select('image').classed(this.activeClass, false);
+ this.cpnode.select('image').classed(this.activeClass, false);
+ }
+ /** Show the right-side information @private */
+ private showRightSideInfo(nsdDetails: boolean, vldDetails: boolean, vnfDeails: boolean, cpDetails: boolean): void {
+ this.isShowNSDDetails = nsdDetails;
+ this.isShowVLDetails = vldDetails;
+ this.isShowVNFDetails = vnfDeails;
+ this.isShowCPDetails = cpDetails;
+ }
+ /** Events handles when Shift Click to perform create cp @private */
+ // tslint:disable-next-line: no-any
+ private singleClick(nodeSelected: any, d: COMPOSERNODES): void {
+ this.selectNodeExclusive(nodeSelected, d);
+ }
+ /** Selected nodes @private */
+ // tslint:disable-next-line: no-any
+ private selectNodeExclusive(nodeSeleced: any, d: COMPOSERNODES): void {
+ const alreadyIsActive: boolean = nodeSeleced.select('#' + d.selectorId).classed(this.activeClass);
+ this.deselectAllNodes();
+ nodeSeleced.select('#' + d.selectorId).classed(this.activeClass, !alreadyIsActive);
+ if (d.type === 'vld' && !alreadyIsActive) {
+ this.nsData.vld.forEach((result: VLD) => {
+ if (result.id === d.id) {
+ this.showRightSideInfo(false, true, false, false);
+ this.vlDetails = result;
+ }
+ });
+ } else if (d.type === 'vnfd' && !alreadyIsActive) {
+ this.nsData['constituent-vnfd'].forEach((result: CONSTITUENTVNFD) => {
+ if (result['member-vnf-index'] === d.nodeIndex && result['vnfd-id-ref'] === d.name) {
+ this.showRightSideInfo(false, false, true, false);
+ this.vnfData = result;
+ }
+ });
+ } else if (d.type === 'ns' && !alreadyIsActive) {
+ this.nsData.vld.forEach((result: VLD) => {
+ if (result['vnfd-connection-point-ref'] !== undefined) {
+ result['vnfd-connection-point-ref'].forEach((resultCP: VNFDCONNECTIONPOINTREF) => {
+ if (resultCP['member-vnf-index-ref'] === d.nodeIndex && resultCP['vnfd-connection-point-ref'] === d.name) {
+ this.cpData = resultCP;
+ this.vlDetails = result;
+ this.showRightSideInfo(false, false, false, true);
+ }
+ });
+ }
+ });
+ } else {
+ this.showRightSideInfo(true, false, false, false);
+ }
+ }
+ /** Get confirmation Before Deleting the Link in Topology @private */
+ private getDeleteLinkConfirmation(d: Tick): void {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Delete';
+ modalRef.componentInstance.topologyname = this.translateService.instant('PAGE.TOPOLOGY.LINK');
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.LINK';
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.doubleClickLink(d);
+ }
+ }).catch();
+ }
+ /** Events handles when Double Click to Delete the link @private */
+ private doubleClickLink(d: Tick): void {
+ let getID: string = '';
+ if (d.target.type === 'vld') {
+ getID = d.target.id;
+ } else if (d.source.type === 'vld') {
+ getID = d.source.id;
+ }
+ this.nodes.forEach((res: COMPOSERNODES) => {
+ if (res.id === getID) {
+ if (this.nsData.vld !== undefined) {
+ this.nsData.vld.forEach((vldresult: VLD) => {
+ if (vldresult.id === getID) {
+ delete vldresult['vnfd-connection-point-ref'];
+ }
+ });
+ }
+ }
+ });
+ this.putType = 'linkdelete';
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ /** Get confirmation Before Deleting the Node in Topology @private */
+ private getDeleteConfirmation(d: COMPOSERNODES): void {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Delete';
+ modalRef.componentInstance.topologyname = d.name;
+ if (d.type === 'vld') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VIRTUALLINK';
+ } else if (d.type === 'vnfd') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VNF';
+ } else if (d.type === 'ns') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.CONNECTIONPOINT';
+ }
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.doubleClick(d);
+ }
+ }).catch();
+ }
+ /** Events handles when Double Click to Delete @private */
+ private doubleClick(d: COMPOSERNODES): void {
+ const deletedNode: COMPOSERNODES = d;
+ this.nodes.forEach((res: COMPOSERNODES) => {
+ if (res.id === d.id) {
+ if (deletedNode.type === 'vld') {
+ const pos: number = this.nsData.vld.map((e: VLD) => { return e.id; }).indexOf(d.id);
+ this.nsData.vld.splice(pos, 1);
+ this.putType = 'nsddelete';
+ } else if (deletedNode.type === 'vnfd') {
+ const constituentVNFD: string[] = [];
+ if (this.nsData['constituent-vnfd'] !== undefined) {
+ this.nsData['constituent-vnfd'].forEach((ref: CONSTITUENTVNFD) => {
+ constituentVNFD.push(ref['vnfd-id-ref'] + ':' + ref['member-vnf-index']);
+ });
+ }
+ const pos: number = constituentVNFD.map((e: string) => { return e; }).indexOf(d.id);
+ this.nsData['constituent-vnfd'].splice(pos, 1);
+ const getCP: string[] = d.id.split(':');
+ const memberVnfIndexRef: number = +getCP[1];
+ const vnfdIDRef: string = getCP[0];
+ if (this.nsData.vld !== undefined) {
+ this.nsData.vld.forEach((resf: VLD) => {
+ if (resf['vnfd-connection-point-ref'] !== undefined) {
+ resf['vnfd-connection-point-ref'].forEach((connectionPoint: VNFDCONNECTIONPOINTREF, index: number) => {
+ if (+connectionPoint['member-vnf-index-ref'] === memberVnfIndexRef && connectionPoint['vnfd-id-ref'] === vnfdIDRef) {
+ resf['vnfd-connection-point-ref'].splice(index, 1);
+ }
+ });
+ }
+ });
+ }
+ this.putType = 'vnfddelete';
+ } else if (deletedNode.type === 'ns') {
+ const getCP: string[] = d.selectorId.split('-osm-');
+ const memberVnfIndexRef: number = d.nodeIndex;
+ const vnfdIDRef: string = getCP[getCP.length - 1];
+ if (this.nsData.vld !== undefined) {
+ this.nsData.vld.forEach((resf: VLD) => {
+ if (resf['vnfd-connection-point-ref'] !== undefined && resf.id === vnfdIDRef) {
+ resf['vnfd-connection-point-ref'].forEach((connectionPoint: VNFDCONNECTIONPOINTREF, index: number) => {
+ if (connectionPoint['member-vnf-index-ref'] === memberVnfIndexRef) {
+ resf['vnfd-connection-point-ref'].splice(index, 1);
+ }
+ });
+ }
+ });
+ }
+ this.putType = 'nsdelete';
+ }
+ this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
+ }
+ });
+ }
+ /** Key press event @private */
+ private keyDown(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ if (this.lastKeyDown !== -1) { return; }
+ this.lastKeyDown = d3.event.keyCode;
+ if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
+ this.gvlNode.call(d3.drag()
+ .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
+ );
+ this.gvnfdNode.call(d3.drag()
+ .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
+ );
+ this.gcpNode.call(d3.drag()
+ .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
+ );
+ this.svg.classed('ctrl', true);
+ }
+ }
+ /** Key realse event @private */
+ private keyUp(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.lastKeyDown = -1;
+ if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
+ this.gvlNode.on('.drag', null);
+ this.gvnfdNode.on('.drag', null);
+ this.gcpNode.on('.drag', null);
+ this.svg.classed('ctrl', false);
+ }
+ }
+ /** Events handles when dragstarted @private */
+ private dragstarted(d: COMPOSERNODES): void {
+ d.fx = d.x;
+ d.fy = d.y;
+ }
+ /** Events handles when dragged @private */
+ private dragged(d: COMPOSERNODES): void {
+ d.fx = d.x = d3.event.x;
+ d.fy = d.y = d3.event.y;
+ }
+ /** Events handles when dragended @private */
+ private dragended(d: COMPOSERNODES): void {
+ if (this.forceSimulationActive) {
+ d.fx = null;
+ d.fy = null;
+ } else {
+ d.fx = d.x;
+ d.fy = d.y;
+ this.forceSimulationActive = false;
+ }
+ }
+ /** Events handles when node double click @private */
+ private onNodedblClickToggleSidebar(): void {
+ this.sideBarOpened = false;
+ }
+ /** Events handles when node single click @private */
+ private onNodeClickToggleSidebar(): void {
+ this.sideBarOpened = true;
+ }
+}
diff --git a/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.html b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.html
new file mode 100644
index 0000000..2aa8f12
--- /dev/null
+++ b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.html
@@ -0,0 +1,312 @@
+<!--
+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)
+-->
+<ng-sidebar-container class="vnf-topology-sidebar-container">
+ <!-- A sidebar -->
+ <ng-sidebar [(opened)]="sideBarOpened" position="left">
+ <div class="sidebar-header">
+ <span class="topology_title" *ngIf="showRightSideInfo === 'vnfdInfo'">{{'PAGE.TOPOLOGY.VNFD' | translate}}</span>
+ <span class="topology_title" *ngIf="showRightSideInfo === 'vduInfo'">{{'PAGE.TOPOLOGY.VDU' | translate}}</span>
+ <span class="topology_title" *ngIf="showRightSideInfo === 'intvlInfo'">{{'PAGE.TOPOLOGY.VIRTUALLINK' | translate}}</span>
+ <span class="topology_title" *ngIf="showRightSideInfo === 'cpInfo'">{{'PAGE.TOPOLOGY.CONNECTIONPOINT' | translate}}</span>
+ <span class="topology_title" *ngIf="showRightSideInfo === 'intcpInfo'">{{'PAGE.TOPOLOGY.INTCONNECTIONPOINT' | translate}}</span>
+ <button (click)="toggleSidebar()" class="close" type="button">
+ <i class="fas fa-times-circle text-danger" aria-hidden="true"></i>
+ </button>
+ </div>
+ <div class="sidebar-body">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="showRightSideInfo === 'vnfdInfo'">
+ <div class="row">
+ <div class="col-12 p-0">
+ <form>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'SHORTNAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'SHORTNAME' | translate }}" name="shortName"
+ [(ngModel)]="vnfdInfo.shortName">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'DESCRIPTION' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'DESCRIPTION' | translate }}" name="description"
+ [(ngModel)]="vnfdInfo.description">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'VERSION' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'VERSION' | translate }}" name="version"
+ [(ngModel)]="vnfdInfo.version">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'ID' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'ID' | translate }}" name="id"
+ [(ngModel)]="vnfdInfo.id">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'NAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'NAME' | translate }}" name="name"
+ [(ngModel)]="vnfdInfo.name">
+ </div>
+ </div>
+ <button type="button" class="btn btn-primary btn-sm pull-right" (click)="saveVNFD()" placement="top" ngbTooltip="Save">
+ <i class="fas fa-save"></i> {{'SAVE' | translate}}
+ </button>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="showRightSideInfo === 'vduInfo'">
+ <div class="row">
+ <div class="col-12 p-0">
+ <form>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'COUNT' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'COUNT' | translate }}" name="count"
+ [(ngModel)]="vduInfo.count">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'DESCRIPTION' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'DESCRIPTION' | translate }}" name="description"
+ [(ngModel)]="vduInfo.description">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'IMAGE' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'IMAGE' | translate }}" name="image"
+ [(ngModel)]="vduInfo.image">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'ID' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'ID' | translate }}" name="id"
+ [(ngModel)]="vduInfo.id">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'NAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'NAME' | translate }}" name="name"
+ [(ngModel)]="vduInfo.name">
+ </div>
+ </div>
+ <button type="button" class="btn btn-primary btn-sm pull-right" (click)="saveVDU(vduInfo.id)" placement="top"
+ ngbTooltip="Save">
+ <i class="fas fa-save"></i> {{'SAVE' | translate}}
+ </button>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="showRightSideInfo === 'intvlInfo'">
+ <div class="row">
+ <div class="col-12 p-0">
+ <form>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'SHORTNAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'SHORTNAME' | translate }}" name="shortName"
+ [(ngModel)]="intvlInfo.shortName">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'NAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'NAME' | translate }}" name="name"
+ [(ngModel)]="intvlInfo.name">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'TYPE' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'TYPE' | translate }}" name="type"
+ [(ngModel)]="intvlInfo.type">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'IPPROFILEREF' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'IP Profile Ref' | translate }}"
+ name="ipProfileRef" [(ngModel)]="intvlInfo.ipProfileRef">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'ID' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'ID' | translate }}" name="id"
+ [(ngModel)]="intvlInfo.id">
+ </div>
+ </div>
+ <button type="button" class="btn btn-primary btn-sm pull-right" (click)="saveIntVL(intvlInfo.id)" placement="top"
+ ngbTooltip="Save">
+ <i class="fas fa-save"></i> {{'SAVE' | translate}}
+ </button>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="showRightSideInfo === 'cpInfo'">
+ <div class="row">
+ <div class="col-12 p-0">
+ <form>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'TYPE' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'TYPE' | translate }}" name="type"
+ [(ngModel)]="cpInfo.type">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'NAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" placeholder="{{ 'NAME' | translate }}" name="name"
+ [(ngModel)]="cpInfo.name">
+ </div>
+ </div>
+ <button type="button" class="btn btn-primary btn-sm pull-right" (click)="saveCP(cpInfo.name)" placement="top"
+ ngbTooltip="Save">
+ <i class="fas fa-save"></i> {{'SAVE' | translate}}
+ </button>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2" *ngIf="showRightSideInfo === 'intcpInfo'">
+ <div class="row">
+ <div class="col-12 p-0">
+ <form>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'SHORTNAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" name="shortName" [(ngModel)]="intcpInfo.shortName" disabled>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'TYPE' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" name="type" [(ngModel)]="intcpInfo.type" disabled>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'ID' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" name="id" [(ngModel)]="intcpInfo.id" disabled>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label">{{ 'NAME' | translate }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" name="name" [(ngModel)]="intcpInfo.name" disabled>
+ </div>
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+ </div>
+ </ng-sidebar>
+ <!-- Page content -->
+ <div ng-sidebar-content>
+ <button (click)="toggleSidebar()" class="btn btn-default" placement="right" ngbTooltip="{{'OPEN' | translate }}">
+ <i class="fa fa-arrow-right detail-sidebar" aria-hidden="true"></i>
+ </button>
+ </div>
+</ng-sidebar-container>
+<div class="container-fluid text-dark">
+ <div class="row bg-white vnf-composer-form">
+ <div class="col-xs-3 col-sm-3 col-md-3 col-lg-3 pl-0 px-0">
+ <div class="row">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 mb-2">
+ <fieldset class="p-2">
+ <legend class="element-legend">
+ {{'PAGE.TOPOLOGY.SELECTELEMENT' | translate}}
+ </legend>
+ <ul class="list-group list-group-flush dragable">
+ <li class="list-group-item" draggable="true" (dragstart)="drag($event)" id="vdu">
+ <img src="assets/images/VDU.svg" class="vnf-svg" draggable="false"/>
+ <span class="span-overflow-text font-weight-bold">{{'PAGE.TOPOLOGY.VDU' | translate}}</span>
+ <span class="drag-icon pull-right"><i class="fas fa-arrows-alt"></i></span>
+ </li>
+ <li class="list-group-item" draggable="true" (dragstart)="drag($event)" id="cp">
+ <img src="assets/images/CP-VNF.svg" class="vnf-svg" draggable="false"/>
+ <span class="span-overflow-text font-weight-bold">{{'PAGE.TOPOLOGY.CP' | translate}}</span>
+ <span class="drag-icon pull-right"><i class="fas fa-arrows-alt"></i></span>
+ </li>
+ <li class="list-group-item" draggable="true" (dragstart)="drag($event)" id="intvl">
+ <img src="assets/images/INTVL.svg" class="vnf-svg" draggable="false"/>
+ <span class="span-overflow-text font-weight-bold">{{'PAGE.TOPOLOGY.INTVL' | translate}}</span>
+ <span class="drag-icon pull-right"><i class="fas fa-arrows-alt"></i></span>
+ </li>
+ </ul>
+ </fieldset>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-9 col-sm-9 col-md-9 col-lg-9">
+ <div class="row">
+ <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 pl-0">
+ <div class="btn-group list" role="group" aria-label="Basic example">
+ <button type="button" class="btn btn-primary topology-btn" (click)="onFreeze()"
+ [class.pinned]="classApplied" placement="top" container="body" ngbTooltip="{{(classApplied ? 'UNFREEZE' : 'FREEZE') | translate}}">
+ <i class="fas fa-thumbtack"></i>
+ </button>
+ <button type="button" class="btn btn-primary topology-btn" (click)="onEdit()" placement="top"
+ container="body" ngbTooltip="{{'EDIT' | translate}}">
+ <i class="fas fa-edit"></i>
+ </button>
+ <button type="button" class="btn btn-primary topology-btn" (click)="showInfo()" placement="top"
+ container="body" ngbTooltip="{{'PAGE.TOPOLOGY.HELP' | translate}}">
+ <i class="fas fa-info"></i>
+ </button>
+ </div>
+ </div>
+ <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 text-right pr-0 badgegroup">
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/VDU.svg" class="vnf-svg" draggable="false"/>
+ <br>{{'PAGE.TOPOLOGY.VDU' | translate}}</span>
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/CP-VNF.svg" class="vnf-svg" draggable="false"/>
+ <br>{{'PAGE.TOPOLOGY.CP' | translate}}</span>
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/INTVL.svg" class="vnf-svg" draggable="false"/>
+ <br>{{'PAGE.TOPOLOGY.INTVL' | translate}}</span>
+ <span class="badge badge-primary badge-pill bg-white text-body font-weight-bold">
+ <img src="assets/images/INTCP.svg" class="vnf-svg" draggable="false"/>
+ <br>{{'PAGE.TOPOLOGY.INTCP' | translate}}</span>
+ </div>
+ </div>
+ <div class="row border-all">
+ <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 svg-container">
+ <svg preserveAspectRatio="xMidYMin slice" (drop)="drop($event)" (dragover)="allowDrop($event)"
+ id="graphContainer" #graphContainer>
+ </svg>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.scss b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.scss
new file mode 100644
index 0000000..4473c67
--- /dev/null
+++ b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.ts b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.ts
new file mode 100644
index 0000000..21aad71
--- /dev/null
+++ b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.ts
@@ -0,0 +1,1020 @@
+/*
+ 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 VNFComposerComponent
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, ViewChild, ViewEncapsulation } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, GETAPIURLHEADER, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { ConfirmationTopologyComponent } from 'ConfirmationTopology';
+import * as d3 from 'd3';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import * as HttpStatus from 'http-status-codes';
+import * as jsyaml from 'js-yaml';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+import {
+ COMPOSERNODES, CONNECTIONPOINT, GRAPHDETAILS, InternalVLD, Tick, TickPath,
+ VDU, VDUInternalConnectionPoint, VLDInternalConnectionPoint, VNFDInterface, VNFDNODE
+} from 'VNFDModel';
+
+/**
+ * Creating component
+ * @Component takes VNFComposerComponent.html as template url
+ */
+@Component({
+ templateUrl: './VNFComposerComponent.html',
+ styleUrls: ['./VNFComposerComponent.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+/** Exporting a class @exports VNFComposerComponent */
+export class VNFComposerComponent {
+ /** To inject services @public */
+ public injector: Injector;
+ /** View child contains graphContainer ref @public */
+ @ViewChild('graphContainer', { static: true }) public graphContainer: ElementRef;
+ /** dataService to pass the data from one component to another @public */
+ public dataService: DataService;
+ /** random number count @public */
+ public randomNumberLength: number;
+ /** Contains the vnfd information @public */
+ public vnfList: string[] = [];
+ /** Contains node type @public */
+ public nodeTypeRef: string;
+ /** Contains VNFD Information @public */
+ public vnfdInfo: VNFDNODE = { shortName: '', description: '', version: '', id: '', name: '' };
+ /** Contains right panel box information @public */
+ public showRightSideInfo: string = '';
+ /** Add the fixed class for the freeze @public */
+ public fixedClass: string = 'fixed';
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+ /** Assign the forcesimulation active @public */
+ public forceSimulationActive: boolean = false;
+ /** Assign pinned class for the button when freezed @public */
+ public classApplied: boolean = false;
+ /** Contains sidebar open status @public */
+ public sideBarOpened: boolean = false;
+
+ /** Contains SVG attributes @private */
+ // tslint:disable-next-line:no-any
+ private svg: any;
+ /** Contains forced node animations @private */
+ // tslint:disable-next-line:no-any
+ private force: any;
+ /** Contains the Drag line */
+ // tslint:disable-next-line: no-any
+ private dragLine: any;
+ /** Contains id of the node @private */
+ private identifier: string;
+ /** Contains path information of the node */
+ // tslint:disable-next-line:no-any
+ private path: any;
+ /** Contains node network @private */
+ // tslint:disable-next-line:no-any
+ private network: any;
+ /** Contains node network @private */
+ // tslint:disable-next-line:no-any
+ private virutualDeploymentUnit: any;
+ /** Contains node connectionPoint @private */
+ // tslint:disable-next-line:no-any
+ private connectionPoint: any;
+ /** Contains node intConnectionPoint @private */
+ // tslint:disable-next-line:no-any
+ private intConnectionPoint: any;
+ /** Contains the node information @private */
+ private nodes: VNFDNODE[] = [];
+ /** Contains the link information of nodes @private */
+ private links: {}[] = [];
+ /** Instance of the rest service @private */
+ private restService: RestService;
+ /** Service holds the router information @private */
+ private router: Router;
+ /** Service contails all the shared service information @private */
+ private sharedService: SharedService;
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private activatedRoute: ActivatedRoute;
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+ /** Rendered nodes represent network @private */
+ // tslint:disable-next-line:no-any
+ private gNetwork: any;
+ /** Rendered nodes represent VDU @private */
+ // tslint:disable-next-line:no-any
+ private gVirutualDeploymentUnit: any;
+ /** Rendered nodes represent connection point @private */
+ // tslint:disable-next-line:no-any
+ private gConnectionPoint: any;
+ /** Rendered nodes represent internal connection point @private */
+ // tslint:disable-next-line:no-any
+ private gIntConnectionPoint: any;
+ /** Contains all the information about VNF Details @private */
+ private vnfdPackageDetails: VNFDNODE;
+ /** Conatins mousedown action @private */
+ private mousedownNode: COMPOSERNODES = null;
+ /** Conatins mouseup action @private */
+ private mouseupNode: COMPOSERNODES = null;
+ /** Conatins current Selection node action @private */
+ private currentSelectedNode: COMPOSERNODES = null;
+ /** Add the activeNode for the selected @private */
+ private activeNode: string = 'active';
+ /** Contains lastkeypressed instance @private */
+ private lastKeyDown: number = -1;
+ /** Contains VDU Information @private */
+ private vduInfo: VDU;
+ /** Contains Internal VL Information @private */
+ private intvlInfo: InternalVLD;
+ /** Contains Connection Point Information @private */
+ private cpInfo: CONNECTIONPOINT;
+ /** Contains Internal Connection Point Information @private */
+ private intcpInfo: VLDInternalConnectionPoint;
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.router = this.injector.get(Router);
+ this.activatedRoute = this.injector.get(ActivatedRoute);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ this.modalService = this.injector.get(NgbModal);
+ }
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ this.identifier = this.activatedRoute.snapshot.paramMap.get('id');
+ this.generateData();
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/zip',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ }
+
+ /** Prepare information for node creation of VNFD @public */
+ public generateData(): void {
+ this.nodes = []; this.links = []; this.vnfdPackageDetails = null;
+ this.showRightSideInfo = 'vnfdInfo';
+ const httpOptions: GETAPIURLHEADER = {
+ headers: new HttpHeaders({
+ 'Content-Type': 'application/zip',
+ Accept: 'text/plain',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ }),
+ responseType: 'text'
+ };
+ this.restService.getResource(environment.VNFPACKAGES_URL + '/' + this.identifier + '/vnfd', httpOptions)
+ .subscribe((vnfdPackageDetails: VNFDNODE) => {
+ try {
+ const getJson: string = jsyaml.load(vnfdPackageDetails.toString(), { json: true });
+ if (getJson.hasOwnProperty('vnfd-catalog')) {
+ this.vnfdPackageDetails = getJson['vnfd-catalog'].vnfd[0];
+ } else if (getJson.hasOwnProperty('vnfd:vnfd-catalog')) {
+ this.vnfdPackageDetails = getJson['vnfd:vnfd-catalog'].vnfd[0];
+ } else if (getJson.hasOwnProperty('vnfd')) {
+ // tslint:disable-next-line: no-string-literal
+ this.vnfdPackageDetails = getJson['vnfd'][0];
+ }
+ this.generateCPPoint(this.vnfdPackageDetails);
+ this.generateVDU(this.vnfdPackageDetails);
+ this.generateInternalVLD(this.vnfdPackageDetails);
+ this.generateInternalCP(this.vnfdPackageDetails);
+ this.generateIntVLCPLinks(this.vnfdPackageDetails);
+ this.generateVDUCPLinks(this.vnfdPackageDetails);
+ this.createNode(this.nodes);
+ this.vnfdInfo.shortName = this.vnfdPackageDetails['short-name'];
+ this.vnfdInfo.description = this.vnfdPackageDetails.description;
+ this.vnfdInfo.version = this.vnfdPackageDetails.version;
+ this.vnfdInfo.id = this.vnfdPackageDetails.id;
+ this.vnfdInfo.name = this.vnfdPackageDetails.name;
+ } catch (e) {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ error.error = typeof error.error === 'string' ? jsyaml.load(error.error) : error.error;
+ if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
+ this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
+ } else {
+ this.restService.handleError(error, 'get');
+ }
+ this.isLoadingResults = false;
+ this.showRightSideInfo = '';
+ });
+ }
+ /** Events handles at drag on D3 region @public */
+ // tslint:disable-next-line:no-any
+ public drag(ev: any): void {
+ ev.dataTransfer.setData('text', ev.target.id);
+ }
+ /** Events handles drop at D3 region @public */
+ public drop(ev: DragEvent): void {
+ ev.preventDefault();
+ this.nodeTypeRef = ev.dataTransfer.getData('text');
+ if (this.nodeTypeRef === 'vdu') {
+ this.svg.selectAll('*').remove();
+ this.vduDropCompose();
+ } else if (this.nodeTypeRef === 'cp') {
+ this.svg.selectAll('*').remove();
+ this.cpDropCompose();
+ } else if (this.nodeTypeRef === 'intvl') {
+ this.svg.selectAll('*').remove();
+ this.intvlDropCompose();
+ }
+ }
+ /** Events handles allow drop on D3 region @public */
+ public allowDrop(ev: DragEvent): void {
+ ev.preventDefault();
+ }
+ /** Generate and list CP points @public */
+ public generateCPPoint(vnfdPackageDetails: VNFDNODE): void {
+ if (vnfdPackageDetails['connection-point'] !== undefined) {
+ vnfdPackageDetails['connection-point'].forEach((cp: CONNECTIONPOINT) => {
+ this.nodes.push({ id: cp.name, nodeTypeRef: 'cp', name: cp.name, type: cp.type });
+ });
+ }
+ }
+ /** Generate and list VDU @public */
+ public generateVDU(vnfdPackageDetails: VNFDNODE): void {
+ if (vnfdPackageDetails.vdu !== undefined) {
+ vnfdPackageDetails.vdu.forEach((vdu: VDU) => {
+ this.nodes.push({
+ id: vdu.name, nodeTypeRef: 'vdu', 'cloud-init-file': vdu['cloud-init-file'], count: vdu.count, description: vdu.description,
+ vduID: vdu.id, name: vdu.name, interface: vdu.interface, 'vm-flavor': vdu['vm-flavor']
+ });
+ });
+ }
+ }
+ /** Generate and list Internal VLD @public */
+ public generateInternalVLD(vnfdPackageDetails: VNFDNODE): void {
+ if (vnfdPackageDetails['internal-vld'] !== undefined) {
+ vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => {
+ this.nodes.push({
+ id: internalVLD.name, nodeTypeRef: 'intvl', intVLID: internalVLD.id,
+ 'internal-connection-point': internalVLD['internal-connection-point'],
+ 'ip-profile-ref': internalVLD['ip-profile-ref'], name: internalVLD.name, 'short-name': internalVLD['short-name'],
+ type: internalVLD.type
+ });
+ });
+ }
+ }
+ /** Generate and list Internal CP @public */
+ public generateInternalCP(vnfdPackageDetails: VNFDNODE): void {
+ if (vnfdPackageDetails.vdu !== undefined) {
+ vnfdPackageDetails.vdu.forEach((intCP: VDUInternalConnectionPoint) => {
+ if (intCP['internal-connection-point'] !== undefined) {
+ intCP['internal-connection-point'].forEach((internalCP: VDUInternalConnectionPoint) => {
+ this.nodes.push({
+ id: internalCP.name, nodeTypeRef: 'intcp', name: internalCP.name,
+ 'short-name': internalCP['short-name'], type: internalCP.type
+ });
+ });
+ }
+ });
+ }
+ }
+ /** Generate VDU External and Internal CP Links @public */
+ public generateVDUCPLinks(vnfdPackageDetails: VNFDNODE): void {
+ if (vnfdPackageDetails.vdu !== undefined) {
+ vnfdPackageDetails.vdu.forEach((vdu: VDU) => {
+ const vduLink: string = vdu.name;
+ if (vdu.interface !== undefined) {
+ vdu.interface.forEach((interfaceDetails: VNFDInterface) => {
+ if (interfaceDetails['external-connection-point-ref'] !== undefined) {
+ this.links.push({ source: vduLink, target: interfaceDetails['external-connection-point-ref'] });
+ }
+ if (interfaceDetails['internal-connection-point-ref'] !== undefined) {
+ this.links.push({ source: vduLink, target: interfaceDetails['internal-connection-point-ref'] });
+ }
+ });
+ }
+ });
+ }
+ }
+ /** Generate Network/VLD/Internal VirtualLink, CP Links @public */
+ public generateIntVLCPLinks(vnfdPackageDetails: VNFDNODE): void {
+ if (vnfdPackageDetails['internal-vld'] !== undefined) {
+ vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => {
+ const vldName: string = internalVLD.name;
+ if (internalVLD['internal-connection-point'] !== undefined) {
+ internalVLD['internal-connection-point'].forEach((intCP: VLDInternalConnectionPoint) => {
+ this.links.push({ source: vldName, target: intCP['id-ref'] });
+ });
+ }
+ });
+ }
+ }
+ /** VNFD details can be saved on users inputs @public */
+ public saveVNFD(): void {
+ this.vnfdPackageDetails['short-name'] = this.vnfdInfo.shortName;
+ this.vnfdPackageDetails.description = this.vnfdInfo.description;
+ this.vnfdPackageDetails.version = this.vnfdInfo.version;
+ this.vnfdPackageDetails.id = this.vnfdInfo.id;
+ this.vnfdPackageDetails.name = this.vnfdInfo.name;
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ delete this.vnfdPackageDetails.shortName;
+ }
+ /** VDU details can be saved on users inputs @public */
+ public saveVDU(vduID: string): void {
+ this.vnfdPackageDetails.vdu.forEach((ref: VDU) => {
+ if (ref.id === vduID) {
+ ref.count = this.vduInfo.count;
+ ref.description = this.vduInfo.description;
+ ref.image = this.vduInfo.image;
+ ref.id = this.vduInfo.id;
+ ref.name = this.vduInfo.name;
+ }
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ }
+ /** IntVL details can be saved on users inputs @public */
+ public saveIntVL(intVLID: string): void {
+ this.vnfdPackageDetails['internal-vld'].forEach((ref: InternalVLD) => {
+ if (ref.id === intVLID) {
+ ref['short-name'] = this.intvlInfo.shortName;
+ ref.name = this.intvlInfo.name;
+ ref.type = this.intvlInfo.type;
+ ref['ip-profile-ref'] = !isNullOrUndefined(this.intvlInfo.ipProfileRef) ? this.intvlInfo.ipProfileRef : '';
+ ref.id = this.intvlInfo.id;
+ delete ref.shortName;
+ delete ref.ipProfileRef;
+ }
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ }
+ /** IntVL details can be saved on users inputs @public */
+ public saveCP(cpName: string): void {
+ this.vnfdPackageDetails['connection-point'].forEach((ref: CONNECTIONPOINT) => {
+ if (ref.name === cpName) {
+ if (!isNullOrUndefined(this.cpInfo.type)) {
+ ref.type = this.cpInfo.type;
+ }
+ ref.name = this.cpInfo.name;
+ }
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ }
+ /** Edit topology @public */
+ public onEdit(): void {
+ this.router.navigate(['/packages/vnf/edit/', this.identifier]).catch();
+ }
+ /** Show Info @public */
+ public showInfo(): void {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Info';
+ modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.INFO');
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ // empty
+ }
+ }).catch();
+ }
+ /** Event to freeze the animation @public */
+ public onFreeze(): void {
+ this.classApplied = !this.classApplied;
+ const alreadyFixedIsActive: boolean = d3.select('svg#graphContainer').classed(this.fixedClass);
+ d3.select('svg#graphContainer').classed(this.fixedClass, !alreadyFixedIsActive);
+ if (alreadyFixedIsActive) {
+ this.force.stop();
+ }
+ this.forceSimulationActive = alreadyFixedIsActive;
+ this.nodes.forEach((d: COMPOSERNODES) => {
+ d.fx = (alreadyFixedIsActive) ? null : d.x;
+ d.fy = (alreadyFixedIsActive) ? null : d.y;
+ });
+ if (alreadyFixedIsActive) {
+ this.force.restart();
+ }
+ }
+ /** Events handles when dragended @public */
+ public toggleSidebar(): void {
+ this.sideBarOpened = !this.sideBarOpened;
+ this.deselectAllNodes();
+ this.showRightSideInfo = 'vnfdInfo';
+ }
+ /** Get the default Configuration of containers @private */
+ private getGraphContainerAttr(): GRAPHDETAILS {
+ return {
+ width: 700,
+ height: 400,
+ nodeHeight: 50,
+ nodeWidth: 35,
+ textX: -35,
+ textY: 30,
+ radius: 5,
+ distance: 50,
+ strength: -500,
+ forcex: 2,
+ forcey: 2,
+ sourcePaddingYes: 17,
+ sourcePaddingNo: 12,
+ targetPaddingYes: 4,
+ targetPaddingNo: 3,
+ alphaTarget: 0.3,
+ imageX: -25,
+ imageY: -25,
+ shiftKeyCode: 17
+ };
+ }
+ /** Node is created and render at D3 region @private */
+ private createNode(nodes: VNFDNODE[]): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ d3.selectAll('svg#graphContainer > *').remove();
+ d3.select(window).on('keydown', () => { this.keyDown(); });
+ d3.select(window).on('keyup', () => { this.keyUp(); });
+ this.svg = d3.select('#graphContainer').attr('oncontextmenu', 'return false;').attr('width', graphContainerAttr.width)
+ .attr('height', graphContainerAttr.height).on('mousemove', () => { this.mousemove(); });
+ this.force = d3.forceSimulation()
+ .force('link', d3.forceLink().id((d: TickPath) => d.id).distance(graphContainerAttr.distance))
+ .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength))
+ .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex))
+ .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey))
+ .on('tick', () => { this.tick(); });
+ this.path = this.svg.append('svg:g').selectAll('path');
+ this.dragLine = this.svg.append('svg:path').attr('class', 'link dragline hidden').attr('d', 'M0,0L0,0');
+ this.network = this.svg.append('svg:g').selectAll('network');
+ this.virutualDeploymentUnit = this.svg.append('svg:g').selectAll('virutualDeploymentUnit');
+ this.connectionPoint = this.svg.append('svg:g').selectAll('connectionPoint');
+ this.intConnectionPoint = this.svg.append('svg:g').selectAll('intConnectionPoint');
+ this.restart(nodes);
+ }
+ /** Update force layout (called automatically each iteration) @private */
+ private tick(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.path.attr('d', (d: Tick) => {
+ const deltaX: number = d.target.x - d.source.x; const deltaY: number = d.target.y - d.source.y;
+ const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+ const normX: number = deltaX / dist; const normY: number = deltaY / dist;
+ const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo;
+ const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo;
+ const sourceX: number = d.source.x + (sourcePadding * normX); const sourceY: number = d.source.y + (sourcePadding * normY);
+ const targetX: number = d.target.x - (targetPadding * normX); const targetY: number = d.target.y - (targetPadding * normY);
+ return `M${sourceX},${sourceY}L${targetX},${targetY}`;
+ }).on('dblclick', (d: Tick) => { this.getDeleteLinkConfirmation(d); });
+ this.network.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`);
+ this.virutualDeploymentUnit.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`);
+ this.connectionPoint.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`);
+ this.intConnectionPoint.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`);
+ }
+ /** Update graph (called when needed) @private */
+ private restart(nodes: VNFDNODE[]): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.path = this.path.data(this.links);
+ const cpNodes: {}[] = []; const vduNodes: {}[] = []; const vlNodes: {}[] = []; const intcpNodes: {}[] = []; //Nodes are known by id
+ nodes.forEach((nodeList: VNFDNODE) => {
+ if (nodeList.nodeTypeRef === 'cp') { cpNodes.push(nodeList); }
+ else if (nodeList.nodeTypeRef === 'vdu') { vduNodes.push(nodeList); }
+ else if (nodeList.nodeTypeRef === 'intvl') { vlNodes.push(nodeList); }
+ else if (nodeList.nodeTypeRef === 'intcp') { intcpNodes.push(nodeList); }
+ });
+ this.network = this.network.data(vlNodes, (d: { id: number }) => d.id);
+ this.virutualDeploymentUnit = this.virutualDeploymentUnit.data(vduNodes, (d: { id: number }) => d.id);
+ this.connectionPoint = this.connectionPoint.data(cpNodes, (d: { id: number }) => d.id);
+ this.intConnectionPoint = this.intConnectionPoint.data(intcpNodes, (d: { id: number }) => d.id);
+ this.resetAndCreateNodes();
+ this.force.nodes(nodes).force('link').links(this.links); //Set the graph in motion
+ this.force.alphaTarget(graphContainerAttr.alphaTarget).restart();
+ }
+ /** Rest and create nodes @private */
+ private resetAndCreateNodes(): void {
+ this.path.exit().remove();
+ this.network.exit().remove();
+ this.virutualDeploymentUnit.exit().remove();
+ this.connectionPoint.exit().remove();
+ this.intConnectionPoint.exit().remove();
+ this.getPathNodes();
+ this.getgNetwork();
+ this.getgVirutualDeploymentUnit();
+ this.getgConnectionPoint();
+ this.getgIntConnectionPoint();
+ this.network = this.gNetwork.merge(this.network);
+ this.virutualDeploymentUnit = this.gVirutualDeploymentUnit.merge(this.virutualDeploymentUnit);
+ this.connectionPoint = this.gConnectionPoint.merge(this.connectionPoint);
+ this.intConnectionPoint = this.gIntConnectionPoint.merge(this.intConnectionPoint);
+ this.path.merge(this.path);
+ }
+ /** Setting the Path @private */
+ private getPathNodes(): void {
+ this.path = this.path.enter().append('svg:path').attr('class', 'link');
+ }
+ /** Settings all the network attributes of nodes @private */
+ private getgNetwork(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gNetwork = this.network.enter().append('svg:g');
+ this.gNetwork.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gNetwork.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: VNFDNODE) => { return d.id; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/INTVL.svg')
+ .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); })
+ .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); })
+ .on('click', (d: VNFDNODE) => { this.singleClick(this.network, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gNetwork.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: { id: number }) => d.id);
+ }
+ /** Settings all the connection point attributes of nodes @private */
+ private getgVirutualDeploymentUnit(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gVirutualDeploymentUnit = this.virutualDeploymentUnit.enter().append('svg:g');
+ this.gVirutualDeploymentUnit.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gVirutualDeploymentUnit.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: VNFDNODE) => { return d.id; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/VDU.svg')
+ .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); })
+ .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); })
+ .on('click', (d: VNFDNODE) => { this.singleClick(this.virutualDeploymentUnit, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gVirutualDeploymentUnit.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: { id: string }) => d.id);
+ }
+ /** Settings all the connection point attributes of nodes @private */
+ private getgConnectionPoint(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gConnectionPoint = this.connectionPoint.enter().append('svg:g');
+ this.gVirutualDeploymentUnit.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gConnectionPoint.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: VNFDNODE) => { return d.id; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/CP-VNF.svg')
+ .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); })
+ .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); })
+ .on('click', (d: VNFDNODE) => { this.singleClick(this.connectionPoint, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gConnectionPoint.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: { id: string }) => d.id);
+ }
+ /** Settings all the internal connection point attributes of nodes @private */
+ private getgIntConnectionPoint(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.gIntConnectionPoint = this.intConnectionPoint.enter().append('svg:g');
+ this.gIntConnectionPoint.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
+ this.gIntConnectionPoint.append('svg:image')
+ .style('opacity', 1)
+ .attr('x', graphContainerAttr.imageX)
+ .attr('y', graphContainerAttr.imageY)
+ .attr('id', (d: VNFDNODE) => { return d.id; })
+ .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
+ .attr('xlink:href', 'assets/images/INTCP.svg')
+ .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); })
+ .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); })
+ .on('click', (d: VNFDNODE) => { this.singleClick(this.intConnectionPoint, d); this.onNodeClickToggleSidebar(); })
+ .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); });
+ this.gIntConnectionPoint.append('svg:text')
+ .attr('class', 'node_text')
+ .attr('y', graphContainerAttr.textY)
+ .text((d: { id: string }) => d.id);
+ }
+ /** Drop VDU Composer Data @private */
+ private vduDropCompose(): void {
+ const randomID: string = this.sharedService.randomString();
+ const vduNode: VNFDNODE[] = [{
+ nodeTypeRef: 'vdu', id: 'vdu_' + randomID, count: 1, description: '', name: 'vdu_' + randomID, image: 'ubuntu',
+ interface: [], 'internal-connection-point': [], 'monitoring-param': [], 'vm-flavor': {}
+ }];
+ const nodeCopy: VNFDNODE[] = this.nodes;
+ Array.prototype.push.apply(vduNode, nodeCopy);
+ this.nodes = vduNode;
+ if (this.vnfdPackageDetails.vdu === undefined) { this.vnfdPackageDetails.vdu = []; }
+ this.vnfdPackageDetails.vdu.push({
+ id: 'vdu_' + randomID, count: 1, description: '', name: 'vdu_' + randomID, image: 'ubuntu',
+ interface: [], 'internal-connection-point': [], 'monitoring-param': [], 'vm-flavor': {}
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ }
+ /** Drop CP Composer Data @private */
+ private cpDropCompose(): void {
+ const randomID: string = this.sharedService.randomString();
+ const cpNode: VNFDNODE[] = [{ nodeTypeRef: 'cp', id: 'cp_' + randomID, name: 'cp_' + randomID }];
+ const nodeCopy: VNFDNODE[] = this.nodes;
+ Array.prototype.push.apply(cpNode, nodeCopy);
+ this.nodes = cpNode;
+ if (this.vnfdPackageDetails['connection-point'] === undefined) {
+ this.vnfdPackageDetails['connection-point'] = [];
+ }
+ this.vnfdPackageDetails['connection-point'].push({
+ id: 'cp_' + randomID, name: 'cp_' + randomID, type: 'VPORT'
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ }
+ /** Drop IntVL Composer Data @private */
+ private intvlDropCompose(): void {
+ const randomID: string = this.sharedService.randomString();
+ const intvlNode: VNFDNODE[] = [{
+ nodeTypeRef: 'intvl', id: 'vnf_vl_' + randomID, name: 'vnf_vl_' + randomID, 'short-name': 'vnf_vl_' + randomID, 'ip-profile-ref': '',
+ type: 'ELAN'
+ }];
+ const nodeCopy: VNFDNODE[] = this.nodes;
+ Array.prototype.push.apply(intvlNode, nodeCopy);
+ this.nodes = intvlNode;
+ if (this.vnfdPackageDetails['internal-vld'] === undefined) { this.vnfdPackageDetails['internal-vld'] = []; }
+ this.vnfdPackageDetails['internal-vld'].push({
+ id: 'vnf_vl_' + randomID, name: 'vnf_vl_' + randomID, 'short-name': 'vnf_vl_' + randomID,
+ 'ip-profile-ref': '', type: 'ELAN', 'internal-connection-point': []
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ }
+ /** Add the Add Nodes Data @private */
+ private addNodes(apiURL: string, identifier: string, data: VNFDNODE): void {
+ this.isLoadingResults = true;
+ const apiURLHeader: APIURLHEADER = {
+ url: apiURL + '/' + identifier + '/package_content',
+ httpOptions: { headers: this.headers }
+ };
+ const vnfData: {} = {};
+ vnfData['vnfd:vnfd-catalog'] = {};
+ vnfData['vnfd:vnfd-catalog'].vnfd = [];
+ vnfData['vnfd:vnfd-catalog'].vnfd.push(data);
+ const descriptorInfo: string = jsyaml.dump(vnfData, { sortKeys: true });
+ this.sharedService.targzFile({ packageType: 'vnfd', id: this.identifier, descriptor: descriptorInfo })
+ .then((content: ArrayBuffer): void => {
+ this.restService.putResource(apiURLHeader, content).subscribe((res: {}) => {
+ this.generateData();
+ this.notifierService.notify('success', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.UPDATEDSUCCESSFULLY'));
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.generateData();
+ this.restService.handleError(error, 'put');
+ this.isLoadingResults = false;
+ });
+ }).catch((): void => {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ this.isLoadingResults = false;
+ });
+ }
+ /** Events handles when mousedown click it will capture the selected node data @private */
+ private mouseDown(d: VNFDNODE): void {
+ event.preventDefault();
+ if (d3.event.ctrlKey) { return; }
+ if (d3.event.shiftKey) {
+ this.mousedownNode = d;
+ this.currentSelectedNode = (this.mousedownNode === this.currentSelectedNode) ? null : this.mousedownNode;
+ this.dragLine.classed('hidden', false)
+ .attr('d', `M${this.mousedownNode.x},${this.mousedownNode.y}L${this.mousedownNode.x},${this.mousedownNode.y}`);
+ }
+ }
+ /** Event handles when mouseup event occures @private */
+ private mouseUp(d: VNFDNODE): void {
+ if (!this.mousedownNode) { return; }
+ this.dragLine.classed('hidden', true);
+ this.mouseupNode = d;
+ if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'intcp') {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKVDUANDINTCP'));
+ this.deselectPath();
+ }
+ else if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'vdu') {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKVDUANDVDU'));
+ this.deselectPath();
+ }
+ else if (this.mousedownNode.nodeTypeRef === 'intcp' && this.mouseupNode.nodeTypeRef === 'vdu') {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKINTCPANDVDU'));
+ this.deselectPath();
+ }
+ else if (this.mousedownNode.nodeTypeRef === 'cp' && this.mouseupNode.nodeTypeRef === 'intvl') {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKCPANDVNFVL'));
+ this.deselectPath();
+ }
+ else if (this.mousedownNode.nodeTypeRef === 'intvl' && this.mouseupNode.nodeTypeRef === 'cp') {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKVNFVLANDCP'));
+ this.deselectPath();
+ }
+ else if (this.mousedownNode.nodeTypeRef === 'intcp' && this.mouseupNode.nodeTypeRef === 'cp') {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKINTCPANDCP'));
+ this.deselectPath();
+ }
+ else if (this.mousedownNode.nodeTypeRef === 'cp' && this.mouseupNode.nodeTypeRef === 'intcp') {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKCPANDINTCP'));
+ this.deselectPath();
+ } else if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'cp') {
+ this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => {
+ if (vduDetails.id === this.mousedownNode.id) {
+ if (vduDetails.interface === undefined) { vduDetails.interface = []; }
+ vduDetails.interface.push({
+ 'external-connection-point-ref': this.mouseupNode.id, 'mgmt-interface': true,
+ name: 'eth_' + this.sharedService.randomString(),
+ 'virtual-interface': { type: 'VIRTIO' },
+ type: 'EXTERNAL'
+ });
+ if (vduDetails['internal-connection-point'] === undefined) {
+ vduDetails['internal-connection-point'] = [];
+ }
+ if (vduDetails['monitoring-param'] === undefined) {
+ vduDetails['monitoring-param'] = [];
+ }
+ if (vduDetails['vm-flavor'] === undefined) {
+ vduDetails['vm-flavor'] = {};
+ }
+ }
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ this.deselectPath();
+ } else if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'intvl') {
+ const setIntCP: string = 'intcp_' + this.sharedService.randomString();
+ this.vnfdPackageDetails['internal-vld'].forEach((vldInternal: InternalVLD) => {
+ if (vldInternal.id === this.mouseupNode.id) {
+ if (vldInternal['internal-connection-point'] === undefined) { vldInternal['internal-connection-point'] = []; }
+ vldInternal['internal-connection-point'].push({ 'id-ref': setIntCP });
+ }
+ });
+ this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => {
+ if (vduDetails.id === this.mousedownNode.id) {
+ if (vduDetails.interface === undefined) {
+ vduDetails.interface = [];
+ }
+ vduDetails.interface.push({
+ 'internal-connection-point-ref': setIntCP, name: 'int_' + setIntCP, type: 'INTERNAL', 'virtual-interface': { type: 'VIRTIO' }
+ });
+ if (vduDetails['internal-connection-point'] === undefined) {
+ vduDetails['internal-connection-point'] = [];
+ }
+ vduDetails['internal-connection-point'].push({
+ id: setIntCP, name: setIntCP, 'short-name': setIntCP, type: 'VPORT'
+ });
+ }
+ });
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ this.deselectPath();
+ }
+ else {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.INVALIDSELECTION'));
+ this.deselectPath();
+ }
+ this.resetMouseActions();
+ this.currentSelectedNode = null;
+ }
+ /** Events handles when mousemove it will capture the selected node data @private */
+ private mousemove(): void {
+ if (!this.mousedownNode) { return; }
+ this.dragLine.attr('d',
+ `M${this.mousedownNode.x},${this.mousedownNode.y}L${d3.mouse(d3.event.currentTarget)[0]},${d3.mouse(d3.event.currentTarget)[1]}`);
+ }
+ /** reset Mouse varaibles @private */
+ private resetMouseActions(): void {
+ this.mousedownNode = null;
+ this.mouseupNode = null;
+ }
+ /** Key press event @private */
+ private keyDown(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ if (this.lastKeyDown !== -1) { return; }
+ this.lastKeyDown = d3.event.keyCode;
+ if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
+ this.gNetwork.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended));
+ this.gVirutualDeploymentUnit.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended));
+ this.gConnectionPoint.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended));
+ this.gIntConnectionPoint.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended));
+ this.svg.classed('ctrl', true);
+ }
+ }
+ /** Key realse event @private */
+ private keyUp(): void {
+ const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
+ this.lastKeyDown = -1;
+ if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
+ this.gNetwork.on('.drag', null);
+ this.gVirutualDeploymentUnit.on('.drag', null);
+ this.gConnectionPoint.on('.drag', null);
+ this.gIntConnectionPoint.on('.drag', null);
+ this.svg.classed('ctrl', false);
+ }
+ }
+ /** Mosue Drag Line false if it is not satisfied @private */
+ private deselectPath(): void {
+ this.dragLine.classed('hidden', true).attr('d', 'M0,0L0,0');
+ }
+ /** Events handles when Shift Click to perform create cp @private */
+ // tslint:disable-next-line: no-any
+ private singleClick(nodeSelected: any, d: VNFDNODE): void {
+ this.selectedNode(nodeSelected, d);
+ }
+ /** Get confirmation Before Deleting the Node in Topology @private */
+ private getDeleteNodeConfirmation(d: VNFDNODE): void {
+ const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
+ modalRef.componentInstance.topologyType = 'Delete';
+ modalRef.componentInstance.topologyname = d.name;
+ if (d.nodeTypeRef === 'vdu') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VDU';
+ } else if (d.nodeTypeRef === 'intvl') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.INTVL';
+ } else if (d.nodeTypeRef === 'cp') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.CONNECTIONPOINT';
+ } else if (d.nodeTypeRef === 'intcp') {
+ modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.INTCP';
+ }
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.deleteNode(d);
+ }
+ }).catch();
+ }
+ /** Delete nodes @private */
+ private deleteNode(d: VNFDNODE): void {
+ const deletedNode: VNFDNODE = d;
+ this.nodes.forEach((node: VNFDNODE) => {
+ if (node.id === d.id) {
+ if (deletedNode.nodeTypeRef === 'cp') {
+ if (this.vnfdPackageDetails.vdu !== undefined) {
+ this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => {
+ if (vduDetails.interface !== undefined) {
+ const interfacePos: number = vduDetails.interface.map((e: VNFDInterface) => { return e['external-connection-point-ref']; })
+ .indexOf(d.id);
+ if (interfacePos >= 0) {
+ vduDetails.interface.splice(interfacePos, 1);
+ }
+ }
+ });
+ }
+ const cpPos: number = this.vnfdPackageDetails['connection-point'].map((e: CONNECTIONPOINT) => { return e.name; })
+ .indexOf(d.id);
+ if (cpPos >= 0) {
+ this.vnfdPackageDetails['connection-point'].splice(cpPos, 1);
+ }
+ } else if (deletedNode.nodeTypeRef === 'intcp') {
+ this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => {
+ // Delete Interface
+ const interfacePos: number = vduDetails.interface.map((e: VNFDInterface) => { return e['internal-connection-point-ref']; })
+ .indexOf(d.id);
+ if (interfacePos >= 0) {
+ vduDetails.interface.splice(interfacePos, 1);
+ }
+ // Delete Internal CP
+ const interCPPos: number = vduDetails['internal-connection-point']
+ .map((e: VDUInternalConnectionPoint) => { return e.id; })
+ .indexOf(d.id);
+ if (interCPPos >= 0) {
+ vduDetails['internal-connection-point'].splice(interCPPos, 1);
+ }
+ });
+ if (this.vnfdPackageDetails['internal-vld'] !== undefined && this.vnfdPackageDetails['internal-vld'].length > 0) {
+ this.vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => {
+ const interfacePos: number = internalVLD['internal-connection-point']
+ .map((e: VLDInternalConnectionPoint) => { return e['id-ref']; }).indexOf(d.id);
+ if (interfacePos >= 0) {
+ internalVLD['internal-connection-point'].splice(interfacePos, 1);
+ }
+ });
+ }
+ } else if (deletedNode.nodeTypeRef === 'intvl') {
+ const intvlPos: number = this.vnfdPackageDetails['internal-vld']
+ .map((e: InternalVLD) => { return e.name; }).indexOf(d.id);
+ if (intvlPos >= 0) {
+ this.vnfdPackageDetails['internal-vld'].splice(intvlPos, 1);
+ }
+ } else if (deletedNode.nodeTypeRef === 'vdu') {
+ const internalCPList: string[] = [];
+ if (deletedNode.interface !== undefined && deletedNode.interface.length > 0) {
+ deletedNode.interface.forEach((interfaceNode: InternalVLD) => {
+ if (interfaceNode['internal-connection-point-ref'] !== undefined) {
+ internalCPList.push(interfaceNode['internal-connection-point-ref']);
+ }
+ });
+ }
+ internalCPList.forEach((list: string) => {
+ if (this.vnfdPackageDetails['internal-vld'] !== undefined && this.vnfdPackageDetails['internal-vld'].length > 0) {
+ this.vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => {
+ const interfacePos: number = internalVLD['internal-connection-point']
+ .map((e: VLDInternalConnectionPoint) => { return e['id-ref']; }).indexOf(list);
+ if (interfacePos >= 0) {
+ internalVLD['internal-connection-point'].splice(interfacePos, 1);
+ }
+ });
+ }
+ });
+ const vduPos: number = this.vnfdPackageDetails.vdu.map((e: VDU) => { return e.id; }).indexOf(d.id);
+ if (vduPos >= 0) {
+ this.vnfdPackageDetails.vdu.splice(vduPos, 1);
+ }
+ } else {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.INVALIDSELECTION'));
+ }
+ this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
+ }
+ });
+ }
+ /** Get confirmation before deleting the ink in Topology @private */
+ private getDeleteLinkConfirmation(d: Tick): void {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.YOUCANNOTDELETELINK'));
+ }
+ /** Selected nodes @private */
+ // tslint:disable-next-line: no-any
+ private selectedNode(nodeSeleced: any, d: VNFDNODE): void {
+ const alreadyIsActive: boolean = nodeSeleced.select('#' + d.id).classed(this.activeNode);
+ this.deselectAllNodes();
+ nodeSeleced.select('#' + d.id).classed(this.activeNode, !alreadyIsActive);
+ if (d.nodeTypeRef === 'vdu' && !alreadyIsActive) {
+ this.vnfdPackageDetails.vdu.forEach((res: VDU) => {
+ if (res.name === d.id) {
+ this.showRightSideInfo = 'vduInfo';
+ this.vduInfo = res;
+ }
+ });
+ } else if (d.nodeTypeRef === 'intvl' && !alreadyIsActive) {
+ this.vnfdPackageDetails['internal-vld'].forEach((res: InternalVLD) => {
+ if (res.name === d.id) {
+ this.showRightSideInfo = 'intvlInfo';
+ this.intvlInfo = res;
+ this.intvlInfo.shortName = res['short-name'];
+ this.intvlInfo.ipProfileRef = res['ip-profile-ref'];
+ }
+ });
+ } else if (d.nodeTypeRef === 'cp' && !alreadyIsActive) {
+ this.vnfdPackageDetails['connection-point'].forEach((res: CONNECTIONPOINT) => {
+ if (res.name === d.id) {
+ this.showRightSideInfo = 'cpInfo';
+ this.cpInfo = res;
+ }
+ });
+ }
+ else if (d.nodeTypeRef === 'intcp' && !alreadyIsActive) {
+ this.intcpInfo = d;
+ this.showRightSideInfo = 'intcpInfo';
+ this.intcpInfo.shortName = d['short-name'];
+ } else {
+ this.showRightSideInfo = 'vnfdInfo';
+ }
+ }
+ /** De-select all the selected nodes @private */
+ private deselectAllNodes(): void {
+ this.network.select('image').classed(this.activeNode, false);
+ this.virutualDeploymentUnit.select('image').classed(this.activeNode, false);
+ this.connectionPoint.select('image').classed(this.activeNode, false);
+ this.intConnectionPoint.select('image').classed(this.activeNode, false);
+ }
+ /** Events handles when dragstarted @private */
+ private dragstarted(d: COMPOSERNODES): void {
+ d.fx = d.x;
+ d.fy = d.y;
+ }
+ /** Events handles when dragged @private */
+ private dragged(d: COMPOSERNODES): void {
+ d.fx = d.x = d3.event.x;
+ d.fy = d.y = d3.event.y;
+ }
+ /** Events handles when dragended @private */
+ private dragended(d: COMPOSERNODES): void {
+ if (this.forceSimulationActive) {
+ d.fx = null;
+ d.fy = null;
+ } else {
+ d.fx = d.x;
+ d.fy = d.y;
+ this.forceSimulationActive = false;
+ }
+ }
+ /** Events handles when node double click @private */
+ private onNodedblClickToggleSidebar(): void {
+ this.sideBarOpened = false;
+ }
+ /** Events handles when node single click @private */
+ private onNodeClickToggleSidebar(): void {
+ this.sideBarOpened = true;
+ }
+}
diff --git a/src/app/packages/show-content/ShowContentComponent.html b/src/app/packages/show-content/ShowContentComponent.html
new file mode 100644
index 0000000..dbb0935
--- /dev/null
+++ b/src/app/packages/show-content/ShowContentComponent.html
@@ -0,0 +1,44 @@
+<!--
+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)
+-->
+<div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'FILES' | translate}} {{'IN' | translate}}
+ {{(params.id)?params.id:'-'}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+</div>
+<div class="modal-body">
+ <table class="table table-bored show-content" *ngIf="contents else noData">
+ <tr>
+ <th># {{'FILES' | translate}} {{'NAME' | translate}}</th>
+ <th>{{'ACTIONS' | translate }}</th>
+ </tr>
+ <tr *ngFor="let data of contents">
+ <td>- {{(data)?data:'-'}}</td>
+ <td>
+ <button class="btn btn-xs">
+ <i class="far fa-folder-open icons"></i>
+ </button>
+ </td>
+ </tr>
+ </table>
+ <ng-template #noData>{{'NODATAERROR' | translate}}</ng-template>
+</div>
+<div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+</div>
\ No newline at end of file
diff --git a/src/app/packages/show-content/ShowContentComponent.scss b/src/app/packages/show-content/ShowContentComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/packages/show-content/ShowContentComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/packages/show-content/ShowContentComponent.ts b/src/app/packages/show-content/ShowContentComponent.ts
new file mode 100644
index 0000000..e2431e8
--- /dev/null
+++ b/src/app/packages/show-content/ShowContentComponent.ts
@@ -0,0 +1,82 @@
+/*
+ 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 Show content modal
+ */
+import { Component, Injector, Input, OnInit } from '@angular/core';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { ERRORDATA, URLPARAMS } from 'CommonModel';
+import { environment } from 'environment';
+import { RestService } from 'RestService';
+
+/** Shows json data in the information modal */
+const defaults: {} = { 'text/json': '' };
+
+/**
+ * Creating component
+ * @Component takes ShowContentComponent.html as template url
+ */
+@Component({
+ selector: 'app-show-content',
+ templateUrl: './ShowContentComponent.html',
+ styleUrls: ['./ShowContentComponent.scss']
+})
+/** Exporting a class @exports ShowContentComponent */
+export class ShowContentComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Contains files information @public */
+ public contents: {}[];
+
+ /** Input contains component objects @public */
+ @Input() public params: URLPARAMS;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate @public
+ */
+ public ngOnInit(): void {
+ if (this.params.page === 'nsd') {
+ this.restService.getResource(environment.NSDESCRIPTORS_URL + '/' + this.params.id + '/artifacts/artifactPath')
+ .subscribe((nsd: {}[]) => {
+ this.contents = nsd;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ } else if (this.params.page === 'vnfd') {
+ this.restService.getResource(environment.VNFPACKAGES_URL + '/' + this.params.id + '/artifacts/artifactPath')
+ .subscribe((vnfd: {}[]) => {
+ this.contents = vnfd;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ });
+ }
+ }
+}
diff --git a/src/app/packages/vnf-packages/VNFPackagesComponent.html b/src/app/packages/vnf-packages/VNFPackagesComponent.html
new file mode 100644
index 0000000..2d7298d
--- /dev/null
+++ b/src/app/packages/vnf-packages/VNFPackagesComponent.html
@@ -0,0 +1,44 @@
+<!--
+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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">VNF {{'PACKAGES' | translate}}</div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" placement="top" container="body" ngbTooltip="{{'PAGE.VNFPACKAGE.ADDVNFPACKAGE' | translate}}"
+ (click)="composeVNFPackage()">
+ <i class="fas fa-plus-circle" aria-hidden="true"></i> {{'PAGE.VNFPACKAGE.ADDVNFPACKAGE' | translate}}
+ </button>
+ </span>
+</div>
+<div class="row">
+ <div class="dropzone mt-2" appDrag (click)="fileInput.click()" (files)="filesDropped($event)">
+ <input hidden type="file" #fileInput (change)="filesDropped($event.target.files)">
+ <div class="text-wrapper">
+ <div class="text-center file-drop-title">
+ <i class="fas fa-upload"></i> {{'DROPFILES' | translate}}</div>
+ </div>
+ </div>
+</div>
+<div class="row mt-2 mb-0 form-group justify-content-end list-utilites-actions">
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/packages/vnf-packages/VNFPackagesComponent.scss b/src/app/packages/vnf-packages/VNFPackagesComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/packages/vnf-packages/VNFPackagesComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/packages/vnf-packages/VNFPackagesComponent.ts b/src/app/packages/vnf-packages/VNFPackagesComponent.ts
new file mode 100644
index 0000000..13707e1
--- /dev/null
+++ b/src/app/packages/vnf-packages/VNFPackagesComponent.ts
@@ -0,0 +1,275 @@
+/*
+ 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 VNF Package details Component.
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA } from 'CommonModel';
+import { ComposePackages } from 'ComposePackages';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { LocalDataSource } from 'ng2-smart-table';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+import { VNFData, VNFDDetails } from 'VNFDModel';
+import { VNFPackagesActionComponent } from 'VNFPackagesAction';
+
+/**
+ * Creating component
+ * @Component takes VNFPackagesComponent.html as template url
+ */
+@Component({
+ selector: 'app-vnf-packages',
+ templateUrl: './VNFPackagesComponent.html',
+ styleUrls: ['./VNFPackagesComponent.scss']
+})
+/** Exporting a class @exports VNFPackagesComponent */
+export class VNFPackagesComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Data of smarttable populate through LocalDataSource @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Columns list of the smart table @public */
+ public columnLists: object = {};
+
+ /** Settings for smarttable to populate the table with columns @public */
+ public settings: object = {};
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** Element ref for fileInput @public */
+ @ViewChild('fileInput', { static: true }) public fileInput: ElementRef;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Formation of appropriate Data for LocalDatasource @private */
+ private vnfData: VNFData[] = [];
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** variables contains file information */
+ private fileData: string | ArrayBuffer;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.sharedService = this.injector.get(SharedService);
+ this.translateService = this.injector.get(TranslateService);
+ this.notifierService = this.injector.get(NotifierService);
+ this.modalService = this.injector.get(NgbModal);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.generateColumns();
+ this.generateSettings();
+ this.generateData();
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/gzip',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** smart table Header Colums @public */
+ public generateColumns(): void {
+ this.columnLists = {
+ shortName: { title: this.translateService.instant('SHORTNAME'), width: '15%', sortDirection: 'asc' },
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' },
+ type: {
+ title: this.translateService.instant('TYPE'),
+ filter: {
+ type: 'list',
+ config: {
+ selectText: 'Select',
+ list: [
+ { value: 'vnfd', title: 'VNF' },
+ { value: 'pnfd', title: 'PNF' },
+ { value: 'hnfd', title: 'HNF' }
+ ]
+ }
+ },
+ width: '10%'
+ },
+ description: { title: this.translateService.instant('DESCRIPTION'), width: '20%' },
+ vendor: { title: this.translateService.instant('VENDOR'), width: '10%' },
+ version: { title: this.translateService.instant('VERSION'), width: '10%' },
+ Actions: {
+ name: 'Action', width: '15%', filter: false, sort: false, type: 'custom',
+ title: this.translateService.instant('ACTIONS'),
+ valuePrepareFunction: (cell: VNFData, row: VNFData): VNFData => row, renderComponent: VNFPackagesActionComponent
+ }
+ };
+ }
+
+ /** smart table Data Settings @public */
+ public generateSettings(): void {
+ this.settings = {
+ edit: {
+ editButtonContent: '<i class="fa fa-edit" title="Edit"></i>',
+ confirmSave: true
+ },
+ delete: {
+ deleteButtonContent: '<i class="far fa-trash-alt" title="delete"></i>',
+ confirmDelete: true
+ },
+ columns: this.columnLists,
+ actions: {
+ add: false,
+ edit: false,
+ delete: false,
+ position: 'right'
+ },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** smart table listing manipulation @public */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** OnUserRowSelect Function @public */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'vnf-package' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Drag and drop feature and fetchind the details of files @public */
+ public filesDropped(files: FileList): void {
+ if (files && files.length === 1) {
+ this.isLoadingResults = true;
+ this.sharedService.getFileString(files, 'gz').then((fileContent: ArrayBuffer): void => {
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.VNFPACKAGESCONTENT_URL,
+ httpOptions: { headers: this.headers }
+ };
+ this.saveFileData(apiURLHeader, fileContent);
+ }).catch((err: string): void => {
+ this.isLoadingResults = false;
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('GZFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ }
+
+ /** Post the droped files and reload the page @public */
+ public saveFileData(urlHeader: APIURLHEADER, fileData: {}): void {
+ this.fileInput.nativeElement.value = null;
+ this.restService.postResource(urlHeader, fileData).subscribe((result: {}) => {
+ this.notifierService.notify('success', this.translateService.instant('PAGE.VNFPACKAGE.CREATEDSUCCESSFULLY'));
+ this.generateData();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'post');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** Generate nsData object from loop and return for the datasource @public */
+ public generatevnfdData(vnfdpackagedata: VNFDDetails): VNFData {
+ return {
+ shortName: vnfdpackagedata['short-name'],
+ identifier: vnfdpackagedata._id,
+ type: vnfdpackagedata._admin.type,
+ description: vnfdpackagedata.description,
+ vendor: vnfdpackagedata.vendor,
+ version: vnfdpackagedata.version
+ };
+ }
+ /** Handle compose new ns package method @public */
+ public composeVNFPackage(): void {
+ this.modalService.open(ComposePackages, { backdrop: 'static' }).componentInstance.params = { page: 'vnf-package' };
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+
+ /** Fetching the data from server to Load in the smarttable @protected */
+ protected generateData(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.VNFPACKAGESCONTENT_URL).subscribe((vnfdPackageData: VNFDDetails[]) => {
+ this.vnfData = [];
+ vnfdPackageData.forEach((vnfdpackagedata: VNFDDetails) => {
+ const vnfDataObj: VNFData = this.generatevnfdData(vnfdpackagedata);
+ this.vnfData.push(vnfDataObj);
+ });
+ if (this.vnfData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.vnfData).then((data: boolean) => {
+ this.isLoadingResults = false;
+ }).catch(() => {
+ this.isLoadingResults = false;
+ });
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+}
diff --git a/src/app/page-not-found/PageNotFoundComponent.html b/src/app/page-not-found/PageNotFoundComponent.html
new file mode 100644
index 0000000..f546e3d
--- /dev/null
+++ b/src/app/page-not-found/PageNotFoundComponent.html
@@ -0,0 +1,27 @@
+<!--
+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)
+-->
+<div class="d-flex flex-column align-items-center flex-grow-1 text-dark font-weight-bold page-not-found">
+ <img src="../../assets/images/page-not-found.jpg">
+ <h1>{{'PAGENOTFOUND.OOPS' | translate}}</h1>
+ <span>
+ {{'PAGENOTFOUND.CONTENT' | translate}}
+ </span>
+ <span>
+ {{'PAGENOTFOUND.MEAN' | translate}} <a routerLink="/">{{'PAGE.DASHBOARD.DASHBOARD' | translate}}</a>
+ </span>
+</div>
\ No newline at end of file
diff --git a/src/app/page-not-found/PageNotFoundComponent.scss b/src/app/page-not-found/PageNotFoundComponent.scss
new file mode 100644
index 0000000..0e81f2b
--- /dev/null
+++ b/src/app/page-not-found/PageNotFoundComponent.scss
@@ -0,0 +1,29 @@
+/*
+ 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 "../../assets/scss/mixins/mixin";
+@import "../../assets/scss/variable";
+.page-not-found {
+ @include padding-percentage-value(10rem, 10rem, 10rem, 10rem);
+ .four-not-four {
+ @include font(null, 5rem, bold);
+ }
+ a:hover {
+ color: $primary;
+ text-decoration: underline;
+ }
+}
\ No newline at end of file
diff --git a/src/app/page-not-found/PageNotFoundComponent.ts b/src/app/page-not-found/PageNotFoundComponent.ts
new file mode 100644
index 0000000..ef515ec
--- /dev/null
+++ b/src/app/page-not-found/PageNotFoundComponent.ts
@@ -0,0 +1,42 @@
+/*
+ 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 Page not found component
+ */
+import { Component, OnInit } from '@angular/core';
+
+/**
+ * Creating component
+ * @Component takes PageNotFoundComponent.html as template url
+ */
+@Component({
+ selector: 'app-page-not-found',
+ templateUrl: './PageNotFoundComponent.html',
+ styleUrls: ['./PageNotFoundComponent.scss']
+})
+/** Exporting a class @exports PageNotFoundComponent */
+export class PageNotFoundComponent implements OnInit {
+ constructor() {
+ //donothing
+ }
+
+ public ngOnInit(): void {
+ //donothing
+ }
+
+}
diff --git a/src/app/projects/ProjectsComponent.html b/src/app/projects/ProjectsComponent.html
new file mode 100644
index 0000000..ec15861
--- /dev/null
+++ b/src/app/projects/ProjectsComponent.html
@@ -0,0 +1,35 @@
+<!--
+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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'PAGE.DASHBOARD.PROJECTS' | translate}}</div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" (click)="projectAdd()" placement="top" container="body" ngbTooltip="{{'PAGE.PROJECT.NEWPROJECT' | translate}}">
+ <i class="fas fa-plus-circle" aria-hidden="true"></i>
+ {{'PAGE.PROJECT.NEWPROJECT' | translate}}
+ </button>
+ </span>
+</div>
+<div class="row mt-2 mb-0 form-group justify-content-end list-utilites-actions">
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/projects/ProjectsComponent.scss b/src/app/projects/ProjectsComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/projects/ProjectsComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/projects/ProjectsComponent.ts b/src/app/projects/ProjectsComponent.ts
new file mode 100644
index 0000000..7524994
--- /dev/null
+++ b/src/app/projects/ProjectsComponent.ts
@@ -0,0 +1,203 @@
+/*
+ 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 details Component.
+ */
+import { Component, Injector, OnDestroy, OnInit } from '@angular/core';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { LocalDataSource } from 'ng2-smart-table';
+import { ProjectCreateUpdateComponent } from 'ProjectCreateUpdate';
+import { ProjectLinkComponent } from 'ProjectLinkComponent';
+import { ProjectData, ProjectDetails } from 'ProjectModel';
+import { ProjectsActionComponent } from 'ProjectsAction';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes ProjectsComponent.html as template url
+ */
+@Component({
+ selector: 'app-projects',
+ templateUrl: './ProjectsComponent.html',
+ styleUrls: ['./ProjectsComponent.scss']
+})
+/** Exporting a class @exports ProjectsComponent */
+export class ProjectsComponent implements OnInit, OnDestroy {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Data of smarttable populate through LocalDataSource @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** Columns list of the smart table @public */
+ public columnLists: object = {};
+
+ /** Settings for smarttable to populate the table with columns @public */
+ public settings: object = {};
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Formation of appropriate Data for LocalDatasource @private */
+ private projectData: ProjectData[] = [];
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.sharedService = this.injector.get(SharedService);
+ this.modalService = this.injector.get(NgbModal);
+ this.translateService = this.injector.get(TranslateService);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.generateColumns();
+ this.generateSettings();
+ this.generateData();
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** smart table Header Colums @public */
+ public generateColumns(): void {
+ this.columnLists = {
+ projectName: {
+ title: this.translateService.instant('NAME'), width: '55%', sortDirection: 'asc', type: 'custom',
+ valuePrepareFunction: (cell: ProjectData, row: ProjectData): ProjectData => row,
+ renderComponent: ProjectLinkComponent
+ },
+ modificationDate: { title: this.translateService.instant('MODIFIED'), width: '20%' },
+ creationDate: { title: this.translateService.instant('CREATED'), width: '20%' },
+ Actions: {
+ name: 'Action', width: '5%', filter: false, sort: false, type: 'custom',
+ title: this.translateService.instant('ACTIONS'),
+ valuePrepareFunction: (cell: ProjectData, row: ProjectData): ProjectData => row,
+ renderComponent: ProjectsActionComponent
+ }
+ };
+ }
+
+ /** smart table Data Settings @public */
+ public generateSettings(): void {
+ this.settings = {
+ edit: {
+ editButtonContent: '<i class="fa fa-edit" title="Edit"></i>',
+ confirmSave: true
+ },
+ delete: {
+ deleteButtonContent: '<i class="far fa-trash-alt" title="Delete"></i>',
+ confirmDelete: true
+ },
+ columns: this.columnLists,
+ actions: {
+ add: false,
+ edit: false,
+ delete: false,
+ position: 'right'
+ },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** Modal service to initiate the project add @private */
+ public projectAdd(): void {
+ const modalRef: NgbModalRef = this.modalService.open(ProjectCreateUpdateComponent, { backdrop: 'static' });
+ modalRef.componentInstance.projectType = 'Add';
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.generateData();
+ }
+ }).catch();
+ }
+
+ /** smart table listing manipulation @private */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** convert UserRowSelect Function @private */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'projects' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Generate Projects object from loop and return for the datasource @public */
+ public generateProjectData(projectData: ProjectDetails): ProjectData {
+ return {
+ projectName: projectData.name,
+ modificationDate: this.sharedService.convertEpochTime(projectData._admin.modified),
+ creationDate: this.sharedService.convertEpochTime(projectData._admin.created),
+ id: projectData._id,
+ project: projectData._id
+ };
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+
+ /** Fetching the data from server to Load in the smarttable @protected */
+ protected generateData(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.PROJECTS_URL).subscribe((projectsData: ProjectDetails[]) => {
+ this.projectData = [];
+ projectsData.forEach((projectData: ProjectDetails) => {
+ const projectDataObj: ProjectData = this.generateProjectData(projectData);
+ this.projectData.push(projectDataObj);
+ });
+ this.dataSource.load(this.projectData).then((data: boolean) => {
+ this.isLoadingResults = false;
+ }).catch();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+}
diff --git a/src/app/projects/ProjectsModule.ts b/src/app/projects/ProjectsModule.ts
new file mode 100644
index 0000000..16147b9
--- /dev/null
+++ b/src/app/projects/ProjectsModule.ts
@@ -0,0 +1,86 @@
+/*
+ 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 SDN Controller module.
+ */
+import { CommonModule } from '@angular/common';
+import { HttpClientModule } from '@angular/common/http';
+import { NgModule } from '@angular/core';
+import { FlexLayoutModule } from '@angular/flex-layout';
+import { FormsModule } from '@angular/forms';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterModule, Routes } from '@angular/router';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { TranslateModule } from '@ngx-translate/core';
+import { DataService } from 'DataService';
+import { LoaderModule } from 'LoaderModule';
+import { Ng2SmartTableModule } from 'ng2-smart-table';
+import { PagePerRowModule } from 'PagePerRowModule';
+import { PageReloadModule } from 'PageReloadModule';
+import { ProjectCreateUpdateComponent } from 'ProjectCreateUpdate';
+import { ProjectsComponent } from 'ProjectsComponent';
+
+/**
+ * configures routers
+ */
+const routes: Routes = [
+ {
+ path: '',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: null }]
+ },
+ component: ProjectsComponent
+ }
+];
+
+/**
+ * Creating @NgModule component for Modules
+ */
+// tslint:disable-next-line: no-stateless-class
+@NgModule({
+ imports: [
+ FormsModule,
+ CommonModule,
+ HttpClientModule,
+ Ng2SmartTableModule,
+ FlexLayoutModule,
+ TranslateModule,
+ RouterModule.forChild(routes),
+ NgbModule,
+ PagePerRowModule,
+ ReactiveFormsModule,
+ LoaderModule,
+ PageReloadModule,
+ NgSelectModule
+ ],
+ declarations: [
+ ProjectsComponent,
+ ProjectCreateUpdateComponent
+ ],
+ providers: [
+ DataService
+ ],
+ entryComponents: [
+ ProjectCreateUpdateComponent
+ ]
+})
+/** Exporting a class @exports ProjectsModule */
+export class ProjectsModule {
+ // empty module
+}
diff --git a/src/app/projects/project-create-update/ProjectCreateUpdateComponent.html b/src/app/projects/project-create-update/ProjectCreateUpdateComponent.html
new file mode 100644
index 0000000..c327119
--- /dev/null
+++ b/src/app/projects/project-create-update/ProjectCreateUpdateComponent.html
@@ -0,0 +1,51 @@
+<!--
+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)
+-->
+<form [formGroup]="projectForm" (ngSubmit)="projectAction(getProjectType)">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{ (getProjectType == 'Add' ? 'NEW' : 'EDIT') | translate}} {{'PROJECT' | translate}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </div>
+ <div class="modal-body project-create-update">
+ <div class="form-group row">
+ <label class="col-sm-12 col-form-label mandatory-label" [ngClass]="{'text-danger': projectForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <label class="col-sm-4 col-form-label">{{'PROJECT' | translate}} {{'NAME' | translate}}*</label>
+ <div class="col-sm-8">
+ <input placeholder="{{'PROJECT' | translate}} {{'NAME' | translate}}" type="text" class="form-control" formControlName="project_name" [(ngModel)]="projectName" [ngClass]="{ 'is-invalid': submitted && f.project_name.errors }" required>
+ </div>
+ </div>
+ <div class="form-group row" *ngIf="getProjectType === 'Add'">
+ <label class="col-sm-4 col-form-label">{{'DOMAIN' | translate}} {{'NAME' | translate}}</label>
+ <div class="col-sm-8">
+ <ng-select [clearable]="false" placeholder="{{'SELECT' | translate}}"
+ [items]="domains" bindLabel="text" bindValue="id" formControlName="domain_name" id="domain_name"
+ [ngClass]="{ 'is-invalid': submitted && f.domain_name.errors }"></ng-select> </div>
+ </div>
+ <div class="form-group row" *ngIf="getProjectType === 'Add'">
+ <label class="col-xs-10 col-sm-10 col-md-10 col-lg-10 col-xl-10">{{'RECENTLY' | translate}} {{'CREATED' | translate}} {{'PROJECT' | translate}}:
+ <b>{{(recentProject)?recentProject.name:''}}</b>
+ </label>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{(getProjectType == 'Add' ? 'CREATE' : 'APPLY') | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
diff --git a/src/app/projects/project-create-update/ProjectCreateUpdateComponent.scss b/src/app/projects/project-create-update/ProjectCreateUpdateComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/projects/project-create-update/ProjectCreateUpdateComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/projects/project-create-update/ProjectCreateUpdateComponent.ts b/src/app/projects/project-create-update/ProjectCreateUpdateComponent.ts
new file mode 100644
index 0000000..ea0bb8a
--- /dev/null
+++ b/src/app/projects/project-create-update/ProjectCreateUpdateComponent.ts
@@ -0,0 +1,233 @@
+/*
+ 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 Add Modal
+ */
+import { Component, Injector, Input, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { ProjectData, ProjectDetails } from 'ProjectModel';
+import { ProjectService } from 'ProjectService';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+
+/**
+ * Creating component
+ * @Component takes ProjectCreateUpdateComponent.html as template url
+ */
+@Component({
+ selector: 'app-project-create-update',
+ templateUrl: './ProjectCreateUpdateComponent.html',
+ styleUrls: ['./ProjectCreateUpdateComponent.scss']
+})
+/** Exporting a class @exports ProjectCreateUpdateComponent */
+export class ProjectCreateUpdateComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance of the rest service @public */
+ public restService: RestService;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Contains the recently created project details @public */
+ public recentProject: ProjectDetails;
+
+ /** Contains project name @public */
+ public projectName: string;
+
+ /** Contains project create or edit @public */
+ public getProjectType: string;
+
+ /** To inject input type services @public */
+ @Input() public projectType: string;
+
+ /** FormGroup user Edit Account added to the form @ html @public */
+ public projectForm: FormGroup;
+
+ /** Form submission Add */
+ public submitted: boolean = false;
+
+ /** Check the loading results for loader status @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Holds list of domains @public */
+ public domains: {}[] = [];
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** DataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Contains project name ref @private */
+ private projectRef: string;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** ModalData instance of modal @private */
+ private modalData: MODALCLOSERESPONSEDATA;
+
+ /** Holds all project details @private */
+ private projectService: ProjectService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.formBuilder = this.injector.get(FormBuilder);
+ this.restService = this.injector.get(RestService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.dataService = this.injector.get(DataService);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ this.projectService = this.injector.get(ProjectService);
+ /** Initializing Form Action */
+ this.projectForm = this.formBuilder.group({
+ project_name: ['', Validators.required],
+ domain_name: [null]
+ });
+ }
+
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.projectForm.controls; }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.getProjectType = this.projectType;
+ if (this.getProjectType === 'Edit') {
+ this.dataService.currentMessage.subscribe((data: ProjectData) => {
+ if (data.projectName !== undefined || data.projectName !== '' || data.projectName !== null) {
+ this.projectName = data.projectName;
+ this.projectRef = data.id;
+ }
+ });
+ } else {
+ this.getProjects();
+ }
+ }
+
+ /** Get the last project name @public */
+ public getProjects(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.PROJECTS_URL).subscribe((projects: ProjectDetails[]) => {
+ this.recentProject = projects.slice(-1).pop();
+ this.getDomainName();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** On modal submit users acction will called @public */
+ public projectAction(userType: string): void {
+ this.submitted = true;
+ this.modalData = {
+ message: 'Done'
+ };
+ this.sharedService.cleanForm(this.projectForm);
+ if (!this.projectForm.invalid) {
+ if (userType === 'Add') {
+ this.createProject();
+ } else if (userType === 'Edit') {
+ this.editProject();
+ }
+ }
+ }
+
+ /** Create project @public */
+ public createProject(): void {
+ this.isLoadingResults = true;
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.PROJECTS_URL
+ };
+ const projectPayload: {} = {
+ name: this.projectForm.value.project_name,
+ domain_name: !isNullOrUndefined(this.projectForm.value.domain_name) ? this.projectForm.value.domain_name : undefined
+ };
+ this.restService.postResource(apiURLHeader, projectPayload).subscribe(() => {
+ this.activeModal.close(this.modalData);
+ this.isLoadingResults = false;
+ this.notifierService.notify('success', this.translateService.instant('PAGE.PROJECT.CREATEDSUCCESSFULLY'));
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'post');
+ this.isLoadingResults = false;
+ });
+ }
+ /** Edit project @public */
+ public editProject(): void {
+ this.isLoadingResults = true;
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.PROJECTS_URL + '/' + this.projectRef
+ };
+ this.restService.patchResource(apiURLHeader, { name: this.projectForm.value.project_name }).subscribe(() => {
+ this.activeModal.close(this.modalData);
+ this.isLoadingResults = false;
+ this.projectService.setHeaderProjects();
+ this.notifierService.notify('success', this.translateService.instant('PAGE.PROJECT.UPDATEDSUCCESSFULLY'));
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'patch');
+ this.isLoadingResults = false;
+ });
+ }
+ /** Get domain name @private */
+ private getDomainName(): void {
+ this.restService.getResource(environment.DOMAIN_URL).subscribe((domains: { project_domain_name: string, user_domain_name: string }) => {
+ let domainNames: string[] = [];
+ if (!isNullOrUndefined(domains.project_domain_name)) {
+ domainNames = domainNames.concat(domains.project_domain_name.split(','));
+ }
+ if (!isNullOrUndefined(domains.user_domain_name)) {
+ domainNames = domainNames.concat(domains.user_domain_name.split(','));
+ }
+ domainNames = Array.from(new Set(domainNames));
+ this.checkDomainNames(domainNames);
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** Check the domain names and create modal for domain select @private */
+ private checkDomainNames(domainNames: string[]): void {
+ if (domainNames.length > 0) {
+ domainNames.forEach((domainName: string) => {
+ if (!domainName.endsWith(':ro')) {
+ this.domains.push({ id: domainName, text: domainName });
+ }
+ });
+ }
+ }
+}
diff --git a/src/app/roles/RolesComponent.html b/src/app/roles/RolesComponent.html
new file mode 100644
index 0000000..da59906
--- /dev/null
+++ b/src/app/roles/RolesComponent.html
@@ -0,0 +1,18 @@
+<!--
+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)
+-->
+<router-outlet></router-outlet>
\ No newline at end of file
diff --git a/src/app/roles/RolesComponent.scss b/src/app/roles/RolesComponent.scss
new file mode 100644
index 0000000..f6cb0fe
--- /dev/null
+++ b/src/app/roles/RolesComponent.scss
@@ -0,0 +1,18 @@
+/*
+ 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)
+ */
+
diff --git a/src/app/roles/RolesComponent.ts b/src/app/roles/RolesComponent.ts
new file mode 100644
index 0000000..82f20b4
--- /dev/null
+++ b/src/app/roles/RolesComponent.ts
@@ -0,0 +1,58 @@
+/*
+ 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 Roles component.
+ */
+import { Component, Injector } from '@angular/core';
+import { Router, RouterEvent } from '@angular/router';
+
+/**
+ * Creating component
+ * @Component takes RolesComponent.html as template url
+ */
+@Component({
+ selector: 'app-roles',
+ templateUrl: './RolesComponent.html',
+ styleUrls: ['./RolesComponent.scss']
+})
+
+/** Exporting a class @exports RolesComponent */
+export class RolesComponent {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** Holds teh instance of router service @private */
+ private router: Router;
+
+ // creates role datails component
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.router = this.injector.get(Router);
+ this.router.events.subscribe((event: RouterEvent) => {
+ this.redirectToList(event.url);
+ });
+ }
+
+ /** Return to role datails list */
+ public redirectToList(getURL: string): void {
+ if (getURL === '/roles') {
+ this.router.navigate(['/roles/details']).catch();
+ }
+ }
+
+}
diff --git a/src/app/roles/RolesModule.ts b/src/app/roles/RolesModule.ts
new file mode 100644
index 0000000..88e1225
--- /dev/null
+++ b/src/app/roles/RolesModule.ts
@@ -0,0 +1,87 @@
+/*
+ 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 Roles Module
+ */
+import { CommonModule } from '@angular/common';
+import { HttpClientModule } from '@angular/common/http';
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterModule, Routes } from '@angular/router';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateModule } from '@ngx-translate/core';
+import { LoaderModule } from 'LoaderModule';
+import { Ng2SmartTableModule } from 'ng2-smart-table';
+import { PagePerRowModule } from 'PagePerRowModule';
+import { PageReloadModule } from 'PageReloadModule';
+import { RolesComponent } from 'Roles';
+import { RolesActionComponent } from 'RolesAction';
+import { RolesCreateEditComponent } from 'RolesCreateEdit';
+import { RolesDetailsComponent } from 'RolesDetails';
+
+/** const values for Roles Routes */
+const routes: Routes = [
+ {
+ path: '',
+ component: RolesComponent,
+ children: [
+ {
+ path: 'details',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'ROLES', url: null }]
+ },
+ component: RolesDetailsComponent
+ },
+ {
+ path: 'create',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'ROLES', url: '/roles/details' },
+ { title: 'PAGE.ROLES.CREATEROLE', url: null }]
+ },
+ component: RolesCreateEditComponent
+ },
+ {
+ path: 'edit/:id',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'ROLES', url: '/roles/details' },
+ { title: '{id}', url: null }]
+ },
+ component: RolesCreateEditComponent
+ }
+ ]
+ }
+];
+/**
+ * An NgModule is a class adorned with the @NgModule decorator function.
+ * @NgModule takes a metadata object that tells Angular how to compile and run module code.
+ */
+@NgModule({
+ imports: [FormsModule, ReactiveFormsModule, CommonModule, HttpClientModule, TranslateModule,
+ RouterModule.forChild(routes), NgbModule, PagePerRowModule, Ng2SmartTableModule, LoaderModule, PageReloadModule],
+ declarations: [RolesComponent, RolesDetailsComponent, RolesActionComponent, RolesCreateEditComponent],
+ entryComponents: [RolesActionComponent]
+})
+/**
+ * AppModule is the Root module for application
+ */
+export class RolesModule {
+ /** Variables declared to avoid state-less class */
+ private rolesModule: string;
+}
diff --git a/src/app/roles/roles-create-edit/RolesCreateEditComponent.html b/src/app/roles/roles-create-edit/RolesCreateEditComponent.html
new file mode 100644
index 0000000..7bb2a1f
--- /dev/null
+++ b/src/app/roles/roles-create-edit/RolesCreateEditComponent.html
@@ -0,0 +1,92 @@
+<!--
+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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">
+ {{ (getRoleType == 'Add' ? 'PAGE.ROLES.CREATEROLE' : 'PAGE.ROLES.EDITROLE') | translate}}</div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" (click)="viewSelection()" placement="top" container="body"
+ ngbTooltip="{{ (viewMode == 'text' ? 'PAGE.ROLES.PREVIEW' : 'PAGE.ROLES.TEXTVIEW') | translate}}"> <i
+ [ngClass]="{ 'fa': 'true', 'fa-comment': viewMode == 'text', 'fa-edit': viewMode == 'preview' }"></i> {{ (viewMode == 'text' ? 'PAGE.ROLES.PREVIEW' : 'PAGE.ROLES.TEXTVIEW') | translate}}
+ </button>
+ </span>
+</div>
+<form [formGroup]="roleForm" (ngSubmit)="roleCheck()" autocomplete="off">
+ <div class="form-group row">
+ <label class="col-sm-12 col-form-label mandatory-label"
+ [ngClass]="{'text-danger': roleForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <label class="col-sm-2 col-form-label">{{'NAME' | translate}}*</label>
+ <div class="col-sm-5">
+ <input placeholder="{{'NAME' | translate}}" type="text" class="form-control" maxlength="30"
+ formControlName="roleName" [ngClass]="{ 'is-invalid': submitted && f.roleName.errors }" required>
+ </div>
+ </div>
+ <div class="form-group row" *ngIf="viewMode == 'text'">
+ <label class="col-sm-2 col-form-label">{{'PAGE.ROLES.PERMISSIONS' | translate}}</label>
+ <div class="col-sm-5">
+ <textarea placeholder="{{'PAGE.ROLES.YAMLPERMISSIONS' | translate}}" rows="10" cols="50" type="text"
+ class="form-control" formControlName="permissions"></textarea>
+ </div>
+ </div>
+ <div class="form-group row" [ngClass]="{ 'justify-content-end': viewMode == 'text' }">
+ <div class="col-sm-6">
+ <button type="button" class="btn btn-danger" routerLink="/roles/details">{{'CANCEL' | translate}}</button>
+ <button class="btn btn-primary ml-3"
+ type="submit">{{ (getRoleType == 'Add' ? 'CREATE' : 'APPLY') | translate}}</button>
+ </div>
+ </div>
+</form>
+<ngb-accordion class="roles-section" [closeOthers]="true" type="secondary" *ngIf="rolePermissions && viewMode == 'preview'">
+ <ngb-panel *ngFor="let permissionGroup of rolePermissions; let i = index">
+ <ng-template ngbPanelHeader let-opened="opened">
+ <div class="d-flex align-items-center justify-content-between">
+ <button ngbPanelToggle class="btn text-dark container-fluid text-left pl-0"><i
+ [ngClass]="{ 'fas': true, 'fa-angle-down': opened, 'fa-angle-right': !opened }"></i> {{permissionGroup.title}}</button>
+ </div>
+ </ng-template>
+ <ng-template ngbPanelContent>
+ <div class="row">
+ <div class="col-lg-4 col-md-6 col-sm-12" *ngFor="let permission of permissionGroup.permissions">
+ <div class="row">
+ <div class="col-md-6 col-sm-6 p-2">{{permission.operation}}</div>
+ <div class="btn-group btn-group-toggle custom-button col-md-6 col-sm-6" ngbRadioGroup name="permission"
+ [(ngModel)]="permission.value">
+ <label ngbButtonLabel ngbTooltip="true">
+ <input ngbButton type="radio" [value]="true">
+ <span class="bg-success"><img src="assets/images/TICK.svg" alt="Checked Icon" /></span>
+ </label>
+ <label ngbButtonLabel ngbTooltip="NA">
+ <input ngbButton type="radio" value="NA">
+ <span class="bg-warning"><img src="assets/images/TICK.svg" alt="Checked Icon" /></span>
+ </label>
+ <label ngbButtonLabel ngbTooltip="false">
+ <input ngbButton type="radio" [value]="false">
+ <span class="bg-danger"><img src="assets/images/TICK.svg" alt="Checked Icon" /></span>
+ </label>
+ </div>
+ </div>
+ </div>
+ </div>
+ </ng-template>
+ </ngb-panel>
+</ngb-accordion>
+<div class="float-right" *ngIf="viewMode == 'preview'">
+ <button type="button" class="btn btn-danger" routerLink="/roles/details">{{'CANCEL' | translate}}</button>
+ <button class="btn btn-primary ml-3"
+ (click)="roleCheck()">{{ (getRoleType == 'Add' ? 'CREATE' : 'APPLY') | translate}}</button>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/roles/roles-create-edit/RolesCreateEditComponent.scss b/src/app/roles/roles-create-edit/RolesCreateEditComponent.scss
new file mode 100644
index 0000000..362973a
--- /dev/null
+++ b/src/app/roles/roles-create-edit/RolesCreateEditComponent.scss
@@ -0,0 +1,51 @@
+/*
+ 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 '../../../assets/scss/mixins/mixin';
+@import '../../../assets/scss/variable';
+ .custom-button{
+ label {
+ @include padding-value(2,2,2,2);
+ &.active {
+ @include background(null, $white, null, null, null);
+ span{
+ img {
+ opacity: 1;
+ }
+ }
+ }
+ input[type="radio"] {
+ display: none;
+ }
+ span {
+ display: inline-block;
+ @include wh-value(30px,30px);
+ cursor: pointer;
+ border-radius: 50%;
+ @include border(all,2,solid, $white);
+ @include box-shadow(0px, 1px, 3px, 0px, $dark);
+ @include background(null, null, null, no-repeat, center);
+ text-align: center;
+ @include line-height(25px);
+ img {
+ width:50%;
+ opacity: 0;
+ @include transition(all, 0.3, null, ease);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/roles/roles-create-edit/RolesCreateEditComponent.ts b/src/app/roles/roles-create-edit/RolesCreateEditComponent.ts
new file mode 100644
index 0000000..0419c5f
--- /dev/null
+++ b/src/app/roles/roles-create-edit/RolesCreateEditComponent.ts
@@ -0,0 +1,335 @@
+/*
+ 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 Roles Create and Edit Component
+ */
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { Component, Injector, Input, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA } from 'CommonModel';
+import { environment } from 'environment';
+import * as jsonpath from 'jsonpath';
+import { RestService } from 'RestService';
+import { Permission, PermissionGroup, RoleConfig, RoleData } from 'RolesModel';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+
+/**
+ * Creating component
+ * @Component takes RolesCreateEditComponent.html as template url
+ */
+@Component({
+ selector: 'app-roles-create-edit',
+ templateUrl: './RolesCreateEditComponent.html',
+ styleUrls: ['./RolesCreateEditComponent.scss']
+})
+/** Exporting a class @exports RolesCreateEditComponent */
+export class RolesCreateEditComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** contains role permissions from config file @public */
+ public rolePermissions: {}[];
+
+ /** contains role form group information @public */
+ public roleForm: FormGroup;
+
+ /** Instance of the rest service @public */
+ public restService: RestService;
+
+ /** Check the loading results for loader status @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Instance to check role form submitted status @public */
+ public submitted: boolean = false;
+
+ /** Contains role create or edit value @public */
+ public getRoleType: string;
+
+ /** Contains view selection value either text or preview @public */
+ public viewMode: string = 'text';
+
+ /** Contains role id value @private */
+ private roleRef: string;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** contians form builder module @private */
+ private formBuilder: FormBuilder;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Varaibles to hold http client @private */
+ private httpClient: HttpClient;
+
+ /** Holds the instance of activatedRoute of router service @private */
+ private activatedRoute: ActivatedRoute;
+
+ /** Holds the instance of roter service @private */
+ private router: Router;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.formBuilder = this.injector.get(FormBuilder);
+ this.sharedService = this.injector.get(SharedService);
+ this.httpClient = this.injector.get(HttpClient);
+ this.activatedRoute = this.injector.get(ActivatedRoute);
+ this.router = this.injector.get(Router);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ this.roleForm = this.formBuilder.group({
+ roleName: ['', [Validators.required]],
+ permissions: ['']
+ });
+ this.getRolePermissions();
+ }
+
+ /** Get role permission information based on action @public */
+ public getRolePermissions(): void {
+ this.isLoadingResults = true;
+ this.loadPermissions().then((response: RoleConfig) => {
+ this.rolePermissions = response.rolePermissions;
+ if (this.activatedRoute.snapshot.url[0].path === 'create') {
+ this.getRoleType = 'Add';
+ this.isLoadingResults = false;
+ } else {
+ this.getRoleType = 'Edit';
+ this.getRoleData();
+ }
+ }).catch(() => {
+ // Empty Block
+ });
+ }
+
+ /** Check Role Create or Edit and proceed action @public */
+ public roleCheck(): void {
+ this.submitted = true;
+ if (!this.roleForm.valid) {
+ const errorIp: Element = document.querySelector('.ng-invalid[formControlName]');
+ if (errorIp) {
+ errorIp.scrollIntoView({ behavior: 'smooth', block: 'center' });
+ }
+ } else {
+ if (this.getRoleType === 'Add') {
+ this.createRole();
+ } else {
+ this.editRole();
+ }
+ }
+ }
+
+ /** Convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.roleForm.controls; }
+
+ /** Create role @private */
+ public createRole(): void {
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.ROLES_URL,
+ httpOptions: { headers: this.headers }
+ };
+ let permissionData: Object = {};
+ this.sharedService.cleanForm(this.roleForm);
+ if (!this.roleForm.invalid) {
+ if (this.viewMode === 'preview') {
+ this.isLoadingResults = true;
+ permissionData = this.generatePermissions();
+ this.roleCreateAPI(apiURLHeader, permissionData);
+ } else {
+ if (this.checkPermission()) {
+ this.isLoadingResults = true;
+ permissionData = this.roleForm.value.permissions !== '' ? JSON.parse(this.roleForm.value.permissions) : {};
+ this.roleCreateAPI(apiURLHeader, permissionData);
+ }
+ }
+ }
+ }
+
+ /** Method to handle role create API call @public */
+ public roleCreateAPI(apiURLHeader: APIURLHEADER, permissionData: {}): void {
+ const postData: {} = {
+ name: this.roleForm.value.roleName,
+ permissions: !isNullOrUndefined(permissionData) ? permissionData : {}
+ };
+ this.restService.postResource(apiURLHeader, postData).subscribe(() => {
+ this.notifierService.notify('success', this.translateService.instant('PAGE.ROLES.CREATEDSUCCESSFULLY'));
+ this.router.navigate(['/roles/details']).catch();
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'post');
+ });
+ }
+
+ /** Edit role @private */
+ public editRole(): void {
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.ROLES_URL + '/' + this.roleRef,
+ httpOptions: { headers: this.headers }
+ };
+ let permissionData: Object = {};
+ this.sharedService.cleanForm(this.roleForm);
+ if (!this.roleForm.invalid) {
+ if (this.viewMode === 'preview') {
+ this.isLoadingResults = true;
+ permissionData = this.generatePermissions();
+ this.roleEditAPI(apiURLHeader, permissionData);
+ } else {
+ if (this.checkPermission()) {
+ this.isLoadingResults = true;
+ permissionData = this.roleForm.value.permissions !== '' ? JSON.parse(this.roleForm.value.permissions) : {};
+ this.roleEditAPI(apiURLHeader, permissionData);
+ }
+ }
+ }
+ }
+
+ /** Method to handle role edit API call */
+ public roleEditAPI(apiURLHeader: APIURLHEADER, permissionData: {}): void {
+ const postData: {} = {
+ name: this.roleForm.value.roleName,
+ permissions: !isNullOrUndefined(permissionData) ? permissionData : {}
+ };
+ this.restService.patchResource(apiURLHeader, postData).subscribe(() => {
+ this.notifierService.notify('success', this.translateService.instant('PAGE.ROLES.UPDATEDSUCCESSFULLY'));
+ this.router.navigate(['/roles/details']).catch();
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'patch');
+ });
+ }
+
+ /** Method to handle toggle role view selection @public */
+ public viewSelection(): void {
+ if (this.viewMode === 'text' && this.checkPermission()) {
+ this.loadPermissions().then((response: RoleConfig) => {
+ this.rolePermissions = response.rolePermissions;
+ const permissions: {} = this.roleForm.value.permissions !== '' ? JSON.parse(this.roleForm.value.permissions) : {};
+ this.filterRoleData(permissions);
+ this.viewMode = 'preview';
+ }).catch(() => {
+ // Empty Block
+ });
+ } else {
+ const rolePermission: {} = this.generatePermissions();
+ if (Object.keys(rolePermission).length !== 0) {
+ this.roleForm.patchValue({ permissions: JSON.stringify(rolePermission, null, '\t') });
+ }
+ this.viewMode = 'text';
+ }
+
+ }
+
+ /** Generate role permission post data @private */
+ private generatePermissions(): Object {
+ const permissions: Object = {};
+ this.rolePermissions.forEach((permissionGroup: PermissionGroup) => {
+ if (!isNullOrUndefined(permissionGroup)) {
+ permissionGroup.permissions.forEach((permission: Permission) => {
+ if (!isNullOrUndefined(permission.value) && permission.value !== 'NA') {
+ permissions[permission.operation] = permission.value;
+ }
+ });
+ }
+ });
+ return permissions;
+ }
+
+ /** Validation method for check permission json string @private */
+ private checkPermission(): boolean {
+ if (this.roleForm.value.permissions) {
+ if (!this.sharedService.checkJson(this.roleForm.value.permissions)) {
+ this.notifierService.notify('error', this.translateService.instant('PAGE.ROLES.ROLEJSONERROR'));
+ return false;
+ } else {
+ this.roleForm.value.permissions = this.roleForm.value.permissions.replace(/'/g, '"');
+ const rolePermission: {} = JSON.parse(this.roleForm.value.permissions);
+ for (const key of Object.keys(rolePermission)) {
+ if (typeof rolePermission[key] !== 'boolean') {
+ this.notifierService.notify('error', this.translateService.instant('PAGE.ROLES.ROLEKEYERROR', { roleKey: key }));
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /** Get role data from NBI based on ID and apply filter @private */
+ private getRoleData(): void {
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.roleRef = this.activatedRoute.snapshot.paramMap.get('id');
+ if (!isNullOrUndefined(this.roleRef)) {
+ this.restService.getResource(environment.ROLES_URL + '/' + this.roleRef).subscribe((data: RoleData) => {
+ this.roleForm.setValue({ roleName: data.name, permissions: JSON.stringify(data.permissions, null, '\t') });
+ this.filterRoleData(data.permissions);
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.router.navigate(['/roles/details']).catch();
+ this.restService.handleError(error, 'get');
+ });
+ }
+ }
+
+ /** Method to filter role data @private */
+ private filterRoleData(permissions: {}): void {
+ Object.keys(permissions).forEach((permission: string) => {
+ jsonpath.apply(this.rolePermissions, '$..permissions[?(@.operation == "' + permission + '")]', (response: Permission) => {
+ if (response.operation === permission) {
+ response.value = permissions[permission];
+ }
+ return response;
+ });
+ });
+ }
+
+ /** Method to load the role permission Json file @private */
+ private async loadPermissions(): Promise<Object> {
+ const jsonFile: string = environment.PERMISSIONS_CONFIG_FILE + '?cb=' + new Date().getTime();
+ return new Promise<Object>((resolve: Function, reject: Function): void => {
+ this.httpClient.get(jsonFile).subscribe((response: Response) => {
+ resolve(response);
+ }, (error: {}) => {
+ reject();
+ });
+ });
+ }
+
+}
diff --git a/src/app/roles/roles-details/RolesDetailsComponent.html b/src/app/roles/roles-details/RolesDetailsComponent.html
new file mode 100644
index 0000000..28a3fe7
--- /dev/null
+++ b/src/app/roles/roles-details/RolesDetailsComponent.html
@@ -0,0 +1,36 @@
+<!--
+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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'ROLES' | translate}}</div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" (click)="createRole()" placement="top"
+ container="body" ngbTooltip="{{'PAGE.ROLES.CREATEROLE' | translate}}">
+ <i class="fas fa-plus-circle" aria-hidden="true"></i>
+ {{'PAGE.ROLES.CREATEROLE' | translate}}
+ </button>
+ </span>
+</div>
+<div class="row mt-2 mb-0 form-group justify-content-end list-utilites-actions">
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/roles/roles-details/RolesDetailsComponent.scss b/src/app/roles/roles-details/RolesDetailsComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/roles/roles-details/RolesDetailsComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/roles/roles-details/RolesDetailsComponent.ts b/src/app/roles/roles-details/RolesDetailsComponent.ts
new file mode 100644
index 0000000..2568f5a
--- /dev/null
+++ b/src/app/roles/roles-details/RolesDetailsComponent.ts
@@ -0,0 +1,191 @@
+/*
+ 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 Roles Deatils component.
+ */
+import { Component, Injector, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { TranslateService } from '@ngx-translate/core';
+import { ERRORDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { LocalDataSource } from 'ng2-smart-table';
+import { RestService } from 'RestService';
+import { RolesActionComponent } from 'RolesAction';
+import { RoleData, RoleDetails } from 'RolesModel';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes RolesComponent.html as template url
+ */
+@Component({
+ selector: 'app-roles-details',
+ templateUrl: './RolesDetailsComponent.html',
+ styleUrls: ['./RolesDetailsComponent.scss']
+})
+export class RolesDetailsComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Formation of appropriate Data for LocalDatasource @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Columns list of the smart table @public */
+ public columnLists: object = {};
+
+ /** Settings for smarttable to populate the table with columns @public */
+ public settings: object = {};
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Contains role details data @private */
+ private roleData: RoleData[] = [];
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ /** Holds the instance of roter service @private */
+ private router: Router;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.sharedService = this.injector.get(SharedService);
+ this.translateService = this.injector.get(TranslateService);
+ this.router = this.injector.get(Router);
+ }
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.generateColumns();
+ this.generateSettings();
+ this.generateData();
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+ /** smart table Header Colums @public */
+ public generateColumns(): void {
+ this.columnLists = {
+ name: { title: this.translateService.instant('NAME'), width: '30%', sortDirection: 'asc' },
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '35%' },
+ modified: { title: this.translateService.instant('MODIFIED'), width: '15%' },
+ created: { title: this.translateService.instant('CREATED'), width: '15%' },
+ Actions: {
+ name: 'Actions', width: '5%', filter: false, sort: false, type: 'custom',
+ title: this.translateService.instant('ACTIONS'),
+ valuePrepareFunction: (cell: RoleData, row: RoleData): RoleData => row,
+ renderComponent: RolesActionComponent
+ }
+ };
+ }
+
+ /** smart table Data Settings @public */
+ public generateSettings(): void {
+ this.settings = {
+ edit: {
+ editButtonContent: '<i class="fa fa-edit" title="Edit"></i>',
+ confirmSave: true
+ },
+ delete: {
+ deleteButtonContent: '<i class="far fa-trash-alt" title="delete"></i>',
+ confirmDelete: true
+ },
+ columns: this.columnLists,
+ actions: {
+ add: false,
+ edit: false,
+ delete: false,
+ topology: false,
+ position: 'right'
+ },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** smart table listing manipulation @public */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** convert UserRowSelect Function @private */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'roles' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Fetching the role data from API and Load it in the smarttable @public */
+ public generateData(): void {
+ this.isLoadingResults = true;
+ this.roleData = [];
+ this.restService.getResource(environment.ROLES_URL).subscribe((roleList: RoleDetails[]) => {
+ roleList.forEach((role: RoleDetails) => {
+ const roleDataObj: RoleData = this.generateRoleData(role);
+ this.roleData.push(roleDataObj);
+ });
+ this.dataSource.load(this.roleData).then((data: boolean) => {
+ this.isLoadingResults = false;
+ }).catch();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** Generate role data object and return for the datasource @public */
+ public generateRoleData(roleData: RoleDetails): RoleData {
+ return {
+ name: roleData.name,
+ identifier: roleData._id,
+ modified: this.sharedService.convertEpochTime(Number(roleData._admin.modified)),
+ created: this.sharedService.convertEpochTime(Number(roleData._admin.created)),
+ permissions: roleData.permissions
+ };
+ }
+
+ /** Create role click handler @public */
+ public createRole(): void {
+ this.router.navigate(['/roles/create']).catch(() => {
+ // Catch Navigation Error
+ });
+ }
+
+ /** Lifecyle hook which get trigger on component destruction @private */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+
+}
diff --git a/src/app/sdn-controller/SDNControllerComponent.html b/src/app/sdn-controller/SDNControllerComponent.html
new file mode 100644
index 0000000..3f96ff8
--- /dev/null
+++ b/src/app/sdn-controller/SDNControllerComponent.html
@@ -0,0 +1,18 @@
+<!--
+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)
+-->
+<router-outlet></router-outlet>
\ No newline at end of file
diff --git a/src/app/sdn-controller/SDNControllerComponent.scss b/src/app/sdn-controller/SDNControllerComponent.scss
new file mode 100644
index 0000000..5a72947
--- /dev/null
+++ b/src/app/sdn-controller/SDNControllerComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
diff --git a/src/app/sdn-controller/SDNControllerComponent.ts b/src/app/sdn-controller/SDNControllerComponent.ts
new file mode 100644
index 0000000..661882c
--- /dev/null
+++ b/src/app/sdn-controller/SDNControllerComponent.ts
@@ -0,0 +1,55 @@
+/*
+ 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 SDN Controller Component.
+ */
+import { Component, Injector } from '@angular/core';
+import { Router, RouterEvent } from '@angular/router';
+
+/**
+ * Creating component
+ * @Component takes SDNControllerComponent.html as template url
+ */
+@Component({
+ templateUrl: './SDNControllerComponent.html',
+ styleUrls: ['./SDNControllerComponent.scss']
+})
+/** Exporting a class @exports SDNControllerComponent */
+export class SDNControllerComponent {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ // creates packages component
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.router = this.injector.get(Router);
+ this.router.events.subscribe((event: RouterEvent) => {
+ this.redirectToList(event.url);
+ });
+ }
+
+ /** Return to list NS Package List */
+ public redirectToList(getURL: string): void {
+ if (getURL === '/sdn') {
+ this.router.navigate(['/sdn/details']).catch();
+ }
+ }
+}
diff --git a/src/app/sdn-controller/SDNControllerModule.ts b/src/app/sdn-controller/SDNControllerModule.ts
new file mode 100644
index 0000000..6da4a5c
--- /dev/null
+++ b/src/app/sdn-controller/SDNControllerModule.ts
@@ -0,0 +1,82 @@
+/*
+ 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 SDN Controller module.
+ */
+import { CommonModule } from '@angular/common';
+import { HttpClientModule } from '@angular/common/http';
+import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { RouterModule, Routes } from '@angular/router';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { TranslateModule } from '@ngx-translate/core';
+import { DataService } from 'DataService';
+import { LoaderModule } from 'LoaderModule';
+import { NewSDNControllerComponent } from 'NewSDNControllerComponent';
+import { Ng2SmartTableModule } from 'ng2-smart-table';
+import { PagePerRowModule } from 'PagePerRowModule';
+import { PageReloadModule } from 'PageReloadModule';
+import { SDNControllerComponent } from 'SDNControllerComponent';
+import { SDNControllerDetailsComponent } from 'SDNControllerDetailsComponent';
+import { SDNControllerInfoComponent } from 'SDNControllerInfoComponent';
+
+/** To halndle project information */
+const projectInfo: {} = localStorage.getItem('project') !== null ? { title: localStorage.getItem('project'), url: '/' } : {};
+
+/**
+ * configures routers
+ */
+const routes: Routes = [
+ {
+ path: '',
+ component: SDNControllerComponent,
+ children: [
+ {
+ path: 'details',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'SDNCONTROLLER', url: null }]
+ },
+ component: SDNControllerDetailsComponent
+ }
+ ]
+ }
+];
+
+/**
+ * Creating @NgModule component for Modules
+ */
+@NgModule({
+ imports: [ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }), FormsModule, CommonModule,
+ HttpClientModule, NgSelectModule, Ng2SmartTableModule, TranslateModule, RouterModule.forChild(routes), NgbModule,
+ PagePerRowModule, LoaderModule, PageReloadModule],
+ declarations: [SDNControllerComponent, SDNControllerDetailsComponent, SDNControllerInfoComponent, NewSDNControllerComponent],
+ providers: [DataService],
+ entryComponents: [SDNControllerInfoComponent, NewSDNControllerComponent],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA]
+})
+/** Exporting a class @exports SDNControllerModule */
+export class SDNControllerModule {
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ //Empty Class
+ }
+}
diff --git a/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.html b/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.html
new file mode 100644
index 0000000..7b909b1
--- /dev/null
+++ b/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.html
@@ -0,0 +1,104 @@
+<!--
+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)
+-->
+<form [formGroup]="sdnControllerForm" (ngSubmit)="sdnControllerFormSubmit()" autocomplete="off">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'PAGE.SDNCONTROLLER.NEWSDNCONTROLLER' | translate}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </div>
+ <div class="modal-body modal-body-custom-height">
+ <div class="form-group row">
+ <label class="col-sm-12 col-form-label mandatory-label"
+ [ngClass]="{'text-danger': sdnControllerForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <label class="col-sm-4 col-form-label" for="name">{{'NAME' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control" placeholder="{{'NAME' | translate}}" type="text"
+ formControlName="name" id="name" [ngClass]="{ 'is-invalid': submitted && f.name.errors }" required>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="sdn_type">{{'TYPE' | translate}}*</label>
+ <div class="col-sm-8">
+ <ng-select [items]="sdnType" placeholder="{{'SELECT' | translate}}" bindLabel="title" bindValue="value"
+ formControlName="type" id="sdn_type" [(ngModel)]="sdnTypeMod"
+ [ngClass]="{ 'is-invalid': submitted && f.type.errors }" required>
+ </ng-select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="username">{{'USERNAME' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control" placeholder="{{'USERNAME' | translate}}" type="text"
+ formControlName="user" id="username" [ngClass]="{ 'is-invalid': submitted && f.user.errors }"
+ required>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="password">{{'PASSWORD' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control" placeholder="{{'PASSWORD' | translate}}" type="password"
+ formControlName="password" id="password"
+ [ngClass]="{ 'is-invalid': submitted && f.password.errors }" required>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="ip">{{'IP' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control" placeholder="{{'IP' | translate}}" type="text"
+ formControlName="ip" id="ip" [ngClass]="{ 'is-invalid': submitted && f.ip.errors }" required>
+ <div *ngIf="sdnControllerForm.invalid" class="invalid-feedback">
+ <div *ngIf="f.ip.errors && f.ip.value">{{'DOMVALIDATIONS.INVALIDIPADDRESS' | translate}}</div>
+ </div>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="port">{{'PORT' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control" placeholder="{{'PORT' | translate}}" type="text"
+ formControlName="port" id="port" [ngClass]="{ 'is-invalid': submitted && f.port.errors }" required>
+ <div *ngIf="sdnControllerForm.invalid" class="invalid-feedback">
+ <div *ngIf="f.port.errors && f.port.value">{{'DOMVALIDATIONS.INVALIDPORTADDRESS' | translate}}</div>
+ </div>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="dpid">{{'PAGE.SDNCONTROLLER.DPID' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.SDNCONTROLLER.DPIDPLACEHOLDER' | translate}}" type="text"
+ formControlName="dpid" id="dpid" [ngClass]="{ 'is-invalid': submitted && f.dpid.errors }" required>
+ <div *ngIf="sdnControllerForm.invalid" class="invalid-feedback">
+ <div *ngIf="f.dpid.errors && f.dpid.value">{{'DOMVALIDATIONS.INVALIDDPID' | translate}}</div>
+ </div>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="version">{{'VERSION' | translate}}</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control" placeholder="{{'VERSION' | translate}}" type="text"
+ formControlName="version" id="version">
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'CREATE' | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
diff --git a/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.scss b/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.ts b/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.ts
new file mode 100644
index 0000000..75fc854
--- /dev/null
+++ b/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.ts
@@ -0,0 +1,151 @@
+/*
+ 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 SDN Controller Component.
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, Injector, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA, SDN_TYPES, TYPESECTION } from 'CommonModel';
+import { environment } from 'environment';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes NewSDNControllerComponent.html as template url
+ */
+@Component({
+ templateUrl: './NewSDNControllerComponent.html',
+ styleUrls: ['./NewSDNControllerComponent.scss']
+})
+/** Exporting a class @exports NewSDNControllerComponent */
+export class NewSDNControllerComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Set SDN Type select to empty @public */
+ public sdnTypeMod: string = null;
+
+ /** Setting SDN types in array @public */
+ public sdnType: TYPESECTION[];
+
+ /** New SDN controller form controls using formgroup @public */
+ public sdnControllerForm: FormGroup;
+
+ /** Form submission Add */
+ public submitted: boolean = false;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Check the loading results for loader status @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.formBuilder = this.injector.get(FormBuilder);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.sharedService = this.injector.get(SharedService);
+
+ /** Initializing Form Action */
+ this.sdnControllerForm = this.formBuilder.group({
+ name: ['', Validators.required],
+ type: ['', Validators.required],
+ user: ['', Validators.required],
+ password: ['', Validators.required],
+ ip: ['', Validators.pattern(this.sharedService.REGX_IP_PATTERN)],
+ port: ['', Validators.pattern(this.sharedService.REGX_PORT_PATTERN)],
+ dpid: ['', Validators.pattern(this.sharedService.REGX_DPID_PATTERN)],
+ version: ['']
+ });
+ }
+
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.sdnControllerForm.controls; }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.sdnType = SDN_TYPES;
+ this.headers = new HttpHeaders({
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ }
+
+ /** On modal submit sdnControllerFormSubmit will called @public */
+ public sdnControllerFormSubmit(): void {
+ this.submitted = true;
+ const modalData: MODALCLOSERESPONSEDATA = {
+ message: 'Done'
+ };
+ this.sharedService.cleanForm(this.sdnControllerForm);
+ if (!this.sdnControllerForm.invalid) {
+ this.isLoadingResults = true;
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.SDNCONTROLLER_URL,
+ httpOptions: { headers: this.headers }
+ };
+ if (this.sdnControllerForm.value.port) {
+ this.sdnControllerForm.value.port = +this.sdnControllerForm.value.port;
+ }
+ if (this.sdnControllerForm.value.version === '') {
+ this.sdnControllerForm.value.version = undefined;
+ }
+ this.restService.postResource(apiURLHeader, this.sdnControllerForm.value)
+ .subscribe((result: {}) => {
+ this.activeModal.close(modalData);
+ this.isLoadingResults = false;
+ this.notifierService.notify('success', this.translateService.instant('PAGE.SDNCONTROLLER.CREATEDSUCCESSFULLY'));
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'post');
+ this.isLoadingResults = false;
+ });
+ }
+ }
+}
diff --git a/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.html b/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.html
new file mode 100644
index 0000000..c316942
--- /dev/null
+++ b/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.html
@@ -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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'PAGE.SDNCONTROLLER.REGISTEREDSDNCONTROLLER' | translate}}</div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" placement="top" container="body" ngbTooltip="{{'PAGE.SDNCONTROLLER.NEWSDNCONTROLLER' | translate}}"
+ (click)="composeSDN()">
+ <i class="fas fa-plus-circle" aria-hidden="true"></i>
+ {{'PAGE.SDNCONTROLLER.NEWSDNCONTROLLER' | translate}}
+ </button>
+ </span>
+</div>
+<div class="row mt-2 mb-0 list-utilites-actions">
+ <div class="col-auto mr-auto">
+ <nav class="custom-items-config">
+ <span><i class="fas fa-clock text-warning"></i>{{operationalStateFirstStep}}</span>
+ <span><i class="fas fa-check-circle text-success"></i>{{operationalStateSecondStep}}</span>
+ <span><i class="fas fa-times-circle text-danger"></i>{{operationalStateThirdStep}}</span>
+ </nav>
+ </div>
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.scss b/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.scss
new file mode 100644
index 0000000..8c2b739
--- /dev/null
+++ b/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
diff --git a/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.ts b/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.ts
new file mode 100644
index 0000000..125d0f4
--- /dev/null
+++ b/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.ts
@@ -0,0 +1,249 @@
+/*
+ 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 SDN Controller details Component.
+ */
+import { Component, Injector, OnDestroy, OnInit } from '@angular/core';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { CONFIGCONSTANT, ERRORDATA, MODALCLOSERESPONSEDATA, SDN_TYPES } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { NewSDNControllerComponent } from 'NewSDNControllerComponent';
+import { LocalDataSource } from 'ng2-smart-table';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SDNControllerActionComponent } from 'SDNControllerActionComponent';
+import { SDNControllerList, SDNControllerModel } from 'SDNControllerModel';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes SDNControllerDetailsComponent.html as template url
+ */
+@Component({
+ templateUrl: './SDNControllerDetailsComponent.html',
+ styleUrls: ['./SDNControllerDetailsComponent.scss']
+})
+/** Exporting a class @exports SDNControllerDetailsComponent */
+export class SDNControllerDetailsComponent implements OnInit, OnDestroy {
+ /** Injector to invoke other services @public */
+ public injector: Injector;
+
+ /** Selected list array @public */
+ public selectList: object[] = [];
+
+ /** Instance component are stored in settings @public */
+ public settings: {} = {};
+
+ /** Contains objects for menu settings @public */
+ public columnList: {} = {};
+
+ /** Data of smarttable populate through LocalDataSource @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** operational State init data @public */
+ public operationalStateFirstStep: string = CONFIGCONSTANT.sdnOperationalStateFirstStep;
+
+ /** operational State running data @public */
+ public operationalStateSecondStep: string = CONFIGCONSTANT.sdnOperationalStateStateSecondStep;
+
+ /** operational State failed data @public */
+ public operationalStateThirdStep: string = CONFIGCONSTANT.sdnOperationalStateThirdStep;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Formation of appropriate Data for LocalDatasource @private */
+ private sdnData: {}[] = [];
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ this.dataService = this.injector.get(DataService);
+ this.modalService = this.injector.get(NgbModal);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.generateTableColumn();
+ this.generateTableSettings();
+ this.generateData();
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** Generate smart table row title and filters @public */
+ public generateTableSettings(): void {
+ this.settings = {
+ columns: this.columnList,
+ actions: { add: false, edit: false, delete: false, position: 'right' },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** Generate smart table row title and filters @public */
+ public generateTableColumn(): void {
+ this.columnList = {
+ name: { title: this.translateService.instant('NAME'), width: '15%', sortDirection: 'asc' },
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' },
+ type: {
+ title: this.translateService.instant('TYPE'), width: '15%',
+ filter: {
+ type: 'list',
+ config: {
+ selectText: 'Select',
+ list: SDN_TYPES
+ }
+ }
+ },
+ operationalState: {
+ title: this.translateService.instant('OPERATIONALSTATUS'), width: '15%', type: 'html',
+ filter: {
+ type: 'list',
+ config: {
+ selectText: 'Select',
+ list: [
+ { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep },
+ { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep },
+ { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep }
+ ]
+ }
+ },
+ valuePrepareFunction: (cell: SDNControllerList, row: SDNControllerList): string => {
+ if (row.operationalState === this.operationalStateFirstStep) {
+ return `<span class="icon-label" title="${row.operationalState}">
+ <i class="fas fa-clock text-warning"></i>
+ </span>`;
+ } else if (row.operationalState === this.operationalStateSecondStep) {
+ return `<span class="icon-label" title="${row.operationalState}">
+ <i class="fas fa-check-circle text-success"></i>
+ </span>`;
+ } else if (row.operationalState === this.operationalStateThirdStep) {
+ return `<span class="icon-label" title="${row.operationalState}">
+ <i class="fas fa-times-circle text-danger"></i>
+ </span>`;
+ } else {
+ return `<span>${row.operationalState}</span>`;
+ }
+ }
+ },
+ ip: { title: this.translateService.instant('IP'), width: '15%' },
+ port: { title: this.translateService.instant('PORT'), width: '15%' },
+ Actions: {
+ name: 'Action', width: '5%', filter: false, sort: false, type: 'custom',
+ title: this.translateService.instant('ACTIONS'),
+ valuePrepareFunction: (cell: SDNControllerList, row: SDNControllerList): SDNControllerList => row,
+ renderComponent: SDNControllerActionComponent
+ }
+ };
+ }
+
+ /** smart table listing manipulation @public */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** smart table listing manipulation @public */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'sdn-controller' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Generate generateSDNData object from loop and return for the datasource @public */
+ public generateSDNList(sdn: SDNControllerModel): SDNControllerList {
+ return {
+ name: sdn.name,
+ identifier: sdn._id,
+ type: sdn.type,
+ operationalState: sdn._admin.operationalState,
+ ip: sdn.ip,
+ port: sdn.port
+ };
+ }
+
+ /** Compose new SDN Controller @public */
+ public composeSDN(): void {
+ const modalRef: NgbModalRef = this.modalService.open(NewSDNControllerComponent, { backdrop: 'static' });
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+
+ /** Fetching the data from server to Load in the smarttable @protected */
+ protected generateData(): void {
+ this.isLoadingResults = true;
+ this.sdnData = [];
+ this.restService.getResource(environment.SDNCONTROLLER_URL).subscribe((sdnDetails: {}[]) => {
+ sdnDetails.forEach((res: SDNControllerModel) => {
+ const sdnDataObj: SDNControllerList = this.generateSDNList(res);
+ this.sdnData.push(sdnDataObj);
+ });
+ if (this.sdnData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.sdnData).then((data: {}) => {
+ this.isLoadingResults = false;
+ }).catch();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+
+}
diff --git a/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.html b/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.html
new file mode 100644
index 0000000..842d101
--- /dev/null
+++ b/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.html
@@ -0,0 +1,90 @@
+<!--
+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)
+-->
+<div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'PAGE.SDNCONTROLLER.DETAILS' | translate}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+</div>
+<div class="modal-body modal-body-custom-height p-0">
+ <table class="table table-striped table-layout-fixed mb-0" *ngIf="sdnDetails else noData">
+ <tr>
+ <td colspan="2">
+ <b>{{'ID' | translate}}:</b> {{(sdnDetails._id)?sdnDetails._id:''}}
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <b>{{'OPERATIONALSTATE' | translate}}:</b>
+ <span class="badge ml-1" [ngClass]="{'badge-info':sdnDetails._admin.operationalState === operationalStateFirstStep,
+ 'badge-success':sdnDetails._admin.operationalState === operationalStateSecondStep,
+ 'badge-danger':sdnDetails._admin.operationalState === operationalStateThirdStep}">
+ {{(sdnDetails._admin.operationalState)?sdnDetails._admin.operationalState:''}}</span>
+ </td>
+ <td>
+ <b>{{'IP' | translate}}:</b> {{(sdnDetails.ip)?sdnDetails.ip:''}}
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <b>{{'NAME' | translate}}:</b> {{(sdnDetails.name)?sdnDetails.name:''}}
+ </td>
+ <td>
+ <b>{{'PORT' | translate}}:</b> {{(sdnDetails.port)?sdnDetails.port:''}}
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <b>{{'TYPE' | translate}}:</b> {{(sdnDetails.type)?sdnDetails.type:''}}
+ </td>
+ <td>
+ <b>{{'VERSION' | translate}}:</b> {{(sdnDetails.version)?sdnDetails.version:''}}
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <b>{{'USER' | translate}}:</b> {{(sdnDetails.user)?sdnDetails.user:''}}
+ </td>
+ <td>
+ <b>{{'CREATED' | translate}}:</b>
+ {{(sdnDetails._admin.created)?this.sharedService.convertEpochTime(sdnDetails._admin.created):''}}
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <b>{{'PAGE.SDNCONTROLLER.DPID' | translate}}:</b> {{(sdnDetails.dpid)?sdnDetails.dpid:''}}
+ </td>
+ <td>
+ <b>{{'MODIFIED' | translate}}:</b>
+ {{(sdnDetails._admin.modified)?this.sharedService.convertEpochTime(sdnDetails._admin.modified):''}}
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>{{'DEPLOYED' | translate}}</b> <br>
+ <b>{{'PAGE.SDNCONTROLLER.RO' | translate}}:</b> {{(sdnDetails._admin.deployed.RO)?sdnDetails._admin.deployed.RO:''}}
+ <br>
+ </td>
+ </tr>
+ </table>
+ <ng-template #noData>{{'NODATAERROR' | translate}}</ng-template>
+</div>
+<div class="modal-footer">
+ <button (click)="activeModal.close()" class="btn btn-danger">{{'CANCEL' | translate}}</button>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.scss b/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.scss
new file mode 100644
index 0000000..c68960c
--- /dev/null
+++ b/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.ts b/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.ts
new file mode 100644
index 0000000..8c23d1d
--- /dev/null
+++ b/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.ts
@@ -0,0 +1,96 @@
+/*
+ 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 Info SDN Controller Info Component
+ */
+import { Component, Injector, Input, OnInit } from '@angular/core';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { CONFIGCONSTANT, ERRORDATA, URLPARAMS } from 'CommonModel';
+import { environment } from 'environment';
+import { RestService } from 'RestService';
+import { SDNControllerModel } from 'SDNControllerModel';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes SDNControllerInfoComponent.html as template url
+ */
+@Component({
+ templateUrl: './SDNControllerInfoComponent.html',
+ styleUrls: ['./SDNControllerInfoComponent.scss']
+})
+/** Exporting a class @exports SDNControllerInfoComponent */
+export class SDNControllerInfoComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Input contains component objects @public */
+ @Input() public params: URLPARAMS;
+
+ /** Contains sdn details @public */
+ public sdnDetails: SDNControllerModel;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Check the loading results for loader status @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** operational State init data @public */
+ public operationalStateFirstStep: string = CONFIGCONSTANT.sdnOperationalStateFirstStep;
+
+ /** operational State running data @public */
+ public operationalStateSecondStep: string = CONFIGCONSTANT.sdnOperationalStateStateSecondStep;
+
+ /** operational State failed data @public */
+ public operationalStateThirdStep: string = CONFIGCONSTANT.sdnOperationalStateThirdStep;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.generateData();
+ }
+
+ /** Generate Data function @public */
+ public generateData(): void {
+ this.restService.getResource(environment.SDNCONTROLLER_URL + '/' + this.params.id).subscribe((sdnDetails: SDNControllerModel) => {
+ this.sdnDetails = sdnDetails;
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+}
diff --git a/src/app/user-settings/UserSettingsComponent.html b/src/app/user-settings/UserSettingsComponent.html
new file mode 100644
index 0000000..2f878a3
--- /dev/null
+++ b/src/app/user-settings/UserSettingsComponent.html
@@ -0,0 +1,44 @@
+<!--
+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)
+-->
+<form [formGroup]="usersettingsForm" (ngSubmit)="usersettingsSubmit();">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'PAGE.DASHBOARD.USERSETTINGS' | translate}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </div>
+ <div class="modal-body user-settings">
+ <label class="col-sm-12 col-form-label mandatory-label" [ngClass]="{'text-danger': usersettingsForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <div class="row form-group">
+ <div class="col-sm-3">
+ <label for="selectedLanguage">{{'PAGE.USERSETTINGS.LANGUAGE' | translate}}* </label>
+ </div>
+ <div class="col-sm-9">
+ <ng-select [items]="languageList" bindLabel="language" bindValue="code"
+ placeholder="{{'SELECT' | translate}} {{'PAGE.USERSETTINGS.LANGUAGE' | translate}}"
+ formControlName="selectedLanguage" id="selectedLanguage"
+ [ngClass]="{ 'is-invalid': submitted && f.selectedLanguage.errors }" required>
+ </ng-select>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'SAVE' | translate}}</button>
+ </div>
+</form>
\ No newline at end of file
diff --git a/src/app/user-settings/UserSettingsComponent.scss b/src/app/user-settings/UserSettingsComponent.scss
new file mode 100644
index 0000000..bdfb8f2
--- /dev/null
+++ b/src/app/user-settings/UserSettingsComponent.scss
@@ -0,0 +1,23 @@
+/*
+ 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 '../../assets/scss/mixins/mixin';
+.user-settings{
+ form label{
+ @include margin-value-percentage(0.5rem, auto, auto, auto);
+ }
+}
\ No newline at end of file
diff --git a/src/app/user-settings/UserSettingsComponent.ts b/src/app/user-settings/UserSettingsComponent.ts
new file mode 100644
index 0000000..19b525f
--- /dev/null
+++ b/src/app/user-settings/UserSettingsComponent.ts
@@ -0,0 +1,111 @@
+/*
+ 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 User Settings Modal Component.
+ */
+
+import { Component, Injector, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes UserSettingsComponent.html as template url
+ */
+@Component({
+ templateUrl: './UserSettingsComponent.html',
+ styleUrls: ['./UserSettingsComponent.scss']
+})
+/** Exporting a class @exports UserSettingsComponent */
+export class UserSettingsComponent implements OnInit {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** Supported language list for the dropdown */
+ public languageList: {}[];
+
+ /** FormGroup instance added to the form @ html @public */
+ public usersettingsForm: FormGroup;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Form submission Add */
+ public submitted: boolean = false;
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** Instance for translate service @private */
+ private translateService: TranslateService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.formBuilder = this.injector.get(FormBuilder);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.initializeSettings();
+ }
+
+ /** Initialize user's settings */
+ public initializeSettings(): void {
+ this.languageList = this.sharedService.languageCodeList();
+ /** Initializing Form Action */
+ this.usersettingsForm = this.formBuilder.group({
+ selectedLanguage: [null, [Validators.required]]
+ });
+ const setLanguage: string = localStorage.getItem('languageCode');
+ if (setLanguage !== null && this.validateLanguageList(setLanguage)) {
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ this.usersettingsForm.get('selectedLanguage').setValue(setLanguage);
+ } else {
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ this.usersettingsForm.get('selectedLanguage').setValue('en');
+ }
+ }
+
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.usersettingsForm.controls; }
+
+ /** On modal submit UserSettingsSubmit will called @public */
+ public usersettingsSubmit(): void {
+ this.submitted = true;
+ if (!this.usersettingsForm.invalid) {
+ const selectedLanguage: string = this.usersettingsForm.value.selectedLanguage;
+ localStorage.setItem('languageCode', this.usersettingsForm.value.selectedLanguage);
+ this.translateService.use(selectedLanguage);
+ location.reload();
+ }
+ }
+ /** Validate language code in the language list @private */
+ private validateLanguageList(setLanguage: string): boolean {
+ return this.languageList.some((item: { code: string }) => item.code === setLanguage);
+ }
+}
diff --git a/src/app/users/UsersComponent.html b/src/app/users/UsersComponent.html
new file mode 100644
index 0000000..3f96ff8
--- /dev/null
+++ b/src/app/users/UsersComponent.html
@@ -0,0 +1,18 @@
+<!--
+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)
+-->
+<router-outlet></router-outlet>
\ No newline at end of file
diff --git a/src/app/users/UsersComponent.scss b/src/app/users/UsersComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/users/UsersComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/users/UsersComponent.ts b/src/app/users/UsersComponent.ts
new file mode 100644
index 0000000..178b979
--- /dev/null
+++ b/src/app/users/UsersComponent.ts
@@ -0,0 +1,55 @@
+/*
+ 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 users details Component.
+ */
+import { Component, Injector } from '@angular/core';
+import { Router, RouterEvent } from '@angular/router';
+
+/**
+ * Creating component
+ * @Component takes UsersComponent.html as template url
+ */
+@Component({
+ templateUrl: './UsersComponent.html',
+ styleUrls: ['./UsersComponent.scss']
+})
+/** Exporting a class @exports UsersComponent */
+export class UsersComponent {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ // creates packages component
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.router = this.injector.get(Router);
+ this.router.events.subscribe((event: RouterEvent) => {
+ this.redirectToList(event.url);
+ });
+ }
+
+ /** Return to list NS Package List */
+ public redirectToList(getURL: string): void {
+ if (getURL === '/users') {
+ this.router.navigate(['/users/details']).catch();
+ }
+ }
+}
diff --git a/src/app/users/UsersModule.ts b/src/app/users/UsersModule.ts
new file mode 100644
index 0000000..2014c48
--- /dev/null
+++ b/src/app/users/UsersModule.ts
@@ -0,0 +1,76 @@
+/*
+ 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 Users module.
+ */
+import { CommonModule } from '@angular/common';
+import { HttpClientModule } from '@angular/common/http';
+import { NgModule } from '@angular/core';
+import { FlexLayoutModule } from '@angular/flex-layout';
+import { FormsModule } from '@angular/forms';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterModule, Routes } from '@angular/router';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { TranslateModule } from '@ngx-translate/core';
+import { AddEditUserComponent } from 'AddEditUserComponent';
+import { DataService } from 'DataService';
+import { LoaderModule } from 'LoaderModule';
+import { Ng2SmartTableModule } from 'ng2-smart-table';
+import { PagePerRowModule } from 'PagePerRowModule';
+import { PageReloadModule } from 'PageReloadModule';
+import { ProjectRoleComponent } from 'ProjectRoleComponent';
+import { UserDetailsComponent } from 'UserDetailsComponent';
+import { UsersComponent } from 'UsersComponent';
+
+/** const values for dashboard Routes */
+const routes: Routes = [
+ {
+ path: '',
+ component: UsersComponent,
+ children: [
+ {
+ path: 'details',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.USERS', url: null }]
+ },
+ component: UserDetailsComponent
+ }
+ ]
+ }
+];
+
+/**
+ * Creating @NgModule component for Modules
+ */
+@NgModule({
+ imports: [ReactiveFormsModule, FormsModule, CommonModule, HttpClientModule, Ng2SmartTableModule, TranslateModule,
+ FlexLayoutModule, NgSelectModule, NgbModule, RouterModule.forChild(routes), PagePerRowModule, LoaderModule, PageReloadModule],
+ declarations: [UsersComponent, UserDetailsComponent, AddEditUserComponent, ProjectRoleComponent],
+ providers: [DataService],
+ entryComponents: [AddEditUserComponent, ProjectRoleComponent]
+})
+/** Exporting a class @exports UsersModule */
+export class UsersModule {
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ // Empty Block
+ }
+}
diff --git a/src/app/users/add-user/AddEditUserComponent.html b/src/app/users/add-user/AddEditUserComponent.html
new file mode 100644
index 0000000..b4d9d28
--- /dev/null
+++ b/src/app/users/add-user/AddEditUserComponent.html
@@ -0,0 +1,89 @@
+<!--
+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)
+-->
+<form [formGroup]="userForm" (ngSubmit)="userAction(userType)" autocomplete="off">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{userTitle}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </div>
+ <div class="modal-body">
+ <label class="col-sm-12 col-form-label mandatory-label"
+ [ngClass]="{'text-danger': userForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <div class="row form-group" *ngIf="userType === 'add' || userType === 'editUserName'">
+ <div class="col-sm-4">
+ <label for="userName">{{'PAGE.USERS.USERNAME' | translate}} *</label>
+ </div>
+ <div class="col-sm-8">
+ <input class="form-control" placeholder="{{'PAGE.USERS.USERNAME' | translate}}" type="text"
+ formControlName="userName" id="userName" [ngClass]="{ 'is-invalid': submitted && f.userName.errors }"
+ required>
+ </div>
+ <div *ngIf="submitted && f.userName.errors" class="input-validation-msg">
+ <div *ngIf="f.userName.errors.minlength">
+ {{'PAGE.LOGIN.USERNAMEMINLENGTHVALIDMESSAGE' | translate}} </div>
+ </div>
+ </div>
+ <ng-container *ngIf="userType === 'add' || userType === 'editPassword'">
+ <div class="row form-group">
+ <div class="col-sm-4">
+ <label for="password">{{'PAGE.USERS.PASSWORD' | translate}} *</label>
+ </div>
+ <div class="col-sm-8">
+ <input class="form-control" placeholder="{{'PAGE.USERS.PASSWORD' | translate}}" minlength="8" maxlength="50"
+ type="password" formControlName="password" id="password" autocomplete="new-password"
+ [ngClass]="{ 'is-invalid': submitted && f.password.errors }" required>
+ </div>
+ <div class="input-validation-msg">
+ <div *ngIf="userForm?.controls.password.hasError('minlength') || userForm?.controls.password.errors?.pattern">
+ {{'PAGE.LOGIN.PASSWORDMINLENGTHVALIDMESSAGE' | translate}} </div>
+ </div>
+ </div>
+ <div class="row form-group">
+ <div class="col-sm-4">
+ <label for="password2">{{'PAGE.USERS.CONFPASSWORD' | translate}} *</label>
+ </div>
+ <div class="col-sm-8">
+ <input class="form-control" placeholder="{{'PAGE.USERS.CONFPASSWORD' | translate}}" type="password"
+ formControlName="password2" id="password2" autocomplete="new-password"
+ [ngClass]="{ 'is-invalid': submitted && f.password2.errors }" required>
+ <div class="mr-top-5" *ngIf="userForm?.controls.password.value && userForm?.controls.password2.value">
+ <i class="far"
+ [ngClass]="{'fa-times-circle text-danger':userForm?.controls.password.value !== userForm?.controls.password2.value,
+ 'fa-check-circle text-success':userForm?.controls.password.value === userForm?.controls.password2.value}"></i>
+ {{'PAGE.USERS.PASSWORDMATCH' | translate}}
+ </div>
+ </div>
+ </div>
+ </ng-container>
+ <div class="form-group row" *ngIf="userType === 'add'">
+ <label class="col-sm-4 col-form-label">{{'DOMAIN' | translate}} {{'NAME' | translate}}</label>
+ <div class="col-sm-8">
+ <ng-select [clearable]="false" placeholder="{{'SELECT' | translate}}" [items]="domains" bindLabel="text"
+ bindValue="id" formControlName="domain_name" id="domain_name"
+ [ngClass]="{ 'is-invalid': submitted && f.domain_name.errors }"></ng-select>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button *ngIf="userType==='add'" type="submit" class="btn btn-primary">{{'CREATE' | translate}}</button>
+ <button *ngIf="userType!=='add'" type="submit" class="btn btn-primary">{{'APPLY' | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/users/add-user/AddEditUserComponent.scss b/src/app/users/add-user/AddEditUserComponent.scss
new file mode 100644
index 0000000..05f2819
--- /dev/null
+++ b/src/app/users/add-user/AddEditUserComponent.scss
@@ -0,0 +1,25 @@
+/*
+ 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 '../../../assets/scss/mixins/mixin';
+@import '../../../assets/scss/variable';
+.input-validation-msg{
+ color:$red;
+ text-align:left;
+ @include padding-value(0, 0, 0, 10);
+ @include font(null, 11px, null);
+}
\ No newline at end of file
diff --git a/src/app/users/add-user/AddEditUserComponent.ts b/src/app/users/add-user/AddEditUserComponent.ts
new file mode 100644
index 0000000..61540e8
--- /dev/null
+++ b/src/app/users/add-user/AddEditUserComponent.ts
@@ -0,0 +1,255 @@
+/*
+ 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 Add Edit Component.
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, Injector, Input, OnInit } from '@angular/core';
+import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { AuthenticationService } from 'AuthenticationService';
+import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { environment } from 'environment';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+
+/**
+ * Creating component
+ * @Component takes AddEditUserComponent.html as template url
+ */
+@Component({
+ templateUrl: './AddEditUserComponent.html',
+ styleUrls: ['./AddEditUserComponent.scss']
+})
+/** Exporting a class @exports AddEditUserComponent */
+export class AddEditUserComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** FormGroup user Edit Account added to the form @ html @public */
+ public userForm: FormGroup;
+
+ /** Form submission Add */
+ public submitted: boolean = false;
+
+ /** Input contains Modal dialog component Instance @public */
+ @Input() public userTitle: string;
+
+ /** Input contains Modal dialog component Instance @public */
+ @Input() public userType: string;
+
+ /** Input contains Modal dialog component Instance @public */
+ @Input() public userID: string;
+
+ /** Input contains Modal dialog component Instance @public */
+ @Input() public userName: string;
+
+ /** Check the loading results for loader status @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Holds list of domains @public */
+ public domains: {}[] = [];
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** ModalData instance of modal @private */
+ private modalData: MODALCLOSERESPONSEDATA;
+
+ /** Utilizes auth service for any auth operations @private */
+ private authService: AuthenticationService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.formBuilder = this.injector.get(FormBuilder);
+ this.restService = this.injector.get(RestService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ this.authService = this.injector.get(AuthenticationService);
+
+ /** Initializing Form Action */
+ this.userForm = this.formBuilder.group({
+ userName: ['', Validators.required],
+ password: [null, [Validators.required, Validators.pattern(this.sharedService.REGX_PASSWORD_PATTERN)]],
+ password2: [null, Validators.required],
+ domain_name: [null]
+ });
+ }
+
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.userForm.controls; }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ if (this.userType === 'add') {
+ this.getDomainName();
+ } else if (this.userType === 'editUserName') {
+ this.userForm.patchValue({ userName: this.userName });
+ }
+ }
+
+ /** On modal submit users acction will called @public */
+ public userAction(userType: string): void {
+ if (userType === 'editPassword') {
+ this.getFormControl('userName').setValidators([]);
+ this.getFormControl('userName').updateValueAndValidity();
+ } else if (userType === 'editUserName') {
+ this.getFormControl('password').setValidators([]);
+ this.getFormControl('password').updateValueAndValidity();
+ this.getFormControl('password2').setValidators([]);
+ this.getFormControl('password2').updateValueAndValidity();
+ }
+ this.submitted = true;
+ this.modalData = {
+ message: 'Done'
+ };
+ this.sharedService.cleanForm(this.userForm);
+ if (!this.userForm.invalid) {
+ if (this.userForm.value.password !== this.userForm.value.password2) {
+ this.notifierService.notify('error', this.translateService.instant('PAGE.USERS.PASSWORDCONFLICT'));
+ return;
+ }
+ if (userType === 'add') {
+ this.addUser();
+ } else if (userType === 'editUserName' || userType === 'editPassword') {
+ this.editUser();
+ }
+ }
+ }
+
+ /** Add user @public */
+ public addUser(): void {
+ this.isLoadingResults = true;
+ const payLoad: {} = JSON.stringify({
+ username: (this.userForm.value.userName).toLowerCase(),
+ password: (this.userForm.value.password),
+ domain_name: !isNullOrUndefined(this.userForm.value.domain_name) ? this.userForm.value.domain_name : undefined
+ });
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.USERS_URL,
+ httpOptions: { headers: this.headers }
+ };
+ this.restService.postResource(apiURLHeader, payLoad).subscribe((result: {}) => {
+ this.activeModal.close(this.modalData);
+ this.isLoadingResults = false;
+ this.notifierService.notify('success', this.translateService.instant('PAGE.USERS.CREATEDSUCCESSFULLY'));
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'post');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** Edit user @public */
+ public editUser(): void {
+ this.isLoadingResults = true;
+ const payLoad: { username?: string, password?: string } = {};
+ if (this.userType === 'editPassword') {
+ payLoad.password = (this.userForm.value.password);
+ } else {
+ payLoad.username = this.userForm.value.userName.toLowerCase();
+ }
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.USERS_URL + '/' + this.userID,
+ httpOptions: { headers: this.headers }
+ };
+ this.restService.patchResource(apiURLHeader, payLoad).subscribe((result: {}) => {
+ this.checkUsername(payLoad);
+ this.activeModal.close(this.modalData);
+ this.isLoadingResults = false;
+ this.notifierService.notify('success', this.translateService.instant('PAGE.USERS.EDITEDSUCCESSFULLY'));
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'put');
+ this.isLoadingResults = false;
+ });
+ }
+ /** Get domain name @private */
+ private getDomainName(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.DOMAIN_URL).subscribe((domains: { project_domain_name: string, user_domain_name: string }) => {
+ let domainNames: string[] = [];
+ if (!isNullOrUndefined(domains.project_domain_name)) {
+ domainNames = domainNames.concat(domains.project_domain_name.split(','));
+ }
+ if (!isNullOrUndefined(domains.user_domain_name)) {
+ domainNames = domainNames.concat(domains.user_domain_name.split(','));
+ }
+ domainNames = Array.from(new Set(domainNames));
+ this.checkDomainNames(domainNames);
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** Check the domain names and create modal for domain select @private */
+ private checkDomainNames(domainNames: string[]): void {
+ if (domainNames.length > 0) {
+ domainNames.forEach((domainName: string) => {
+ if (!domainName.endsWith(':ro')) {
+ this.domains.push({ id: domainName, text: domainName });
+ }
+ });
+ }
+ }
+
+ /** Used to get the AbstractControl of controlName passed @private */
+ private getFormControl(controlName: string): AbstractControl {
+ return this.userForm.controls[controlName];
+ }
+
+ /** Method to check loggedin username and update @private */
+ private checkUsername(payLoad: { username?: string }): void {
+ const logUsername: string = localStorage.getItem('username');
+ if (this.userType === 'editUserName' && logUsername === this.userName) {
+ this.authService.userName.next(payLoad.username);
+ localStorage.setItem('username', payLoad.username);
+ }
+ }
+}
diff --git a/src/app/users/project-role/ProjectRoleComponent.html b/src/app/users/project-role/ProjectRoleComponent.html
new file mode 100644
index 0000000..c093f37
--- /dev/null
+++ b/src/app/users/project-role/ProjectRoleComponent.html
@@ -0,0 +1,60 @@
+<!--
+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)
+-->
+<div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{userTitle}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+</div>
+<form [formGroup]="projectRoleForm" (ngSubmit)="addProjectRole()">
+ <div class="modal-body" *ngIf="userDetails" formArrayName="project_role_mappings">
+ <div class="form-group row p-2 bg-light text-white projects-roles-head text-white justify-content-end">
+ <div class="col-4">
+ <button type="button" class="btn btn-primary" (click)="addMapping()">
+ <i class="fas fa-plus-circle"></i> {{'PAGE.USERS.ADDMAPPINGS' | translate}}</button>
+ </div>
+ </div>
+ <label class="col-sm-12 col-form-label mandatory-label" [ngClass]="{'text-danger': projectRoleForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <div *ngFor="let params of getControls(); let i = index;" [formGroupName]="i">
+ <div class="form-group row">
+ <label class="col-sm-2 col-form-label" for="project_{{i}}">{{'PROJECT' | translate}}*</label>
+ <div class="col-sm-3">
+ <ng-select placeholder="{{'SELECT' | translate}}" [items]="projects" bindLabel="name"
+ bindValue="name" formControlName="project_name" id="project_{{i}}"
+ [ngClass]="{ 'is-invalid': submitted && params.controls.project_name.errors }"></ng-select>
+ </div>
+ <label class="col-sm-2 col-form-label" for="roles_{{i}}">{{'ROLES' | translate}}*</label>
+ <div class="col-sm-4">
+ <ng-select placeholder="{{'SELECT' | translate}}" [items]="roles" bindLabel="name" bindValue="name"
+ formControlName="role_name" id="roles_{{i}}"
+ [ngClass]="{ 'is-invalid': submitted && params.controls.role_name.errors }"></ng-select>
+ </div>
+ <div class="col-sm-1">
+ <button type="button" class="btn btn-sm btn-danger remove-mapping" (click)="removeMapping(i)">
+ <i class="fas fa-times-circle"></i>
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'APPLY' | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/users/project-role/ProjectRoleComponent.scss b/src/app/users/project-role/ProjectRoleComponent.scss
new file mode 100644
index 0000000..031e56e
--- /dev/null
+++ b/src/app/users/project-role/ProjectRoleComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/users/project-role/ProjectRoleComponent.ts b/src/app/users/project-role/ProjectRoleComponent.ts
new file mode 100644
index 0000000..f5bb772
--- /dev/null
+++ b/src/app/users/project-role/ProjectRoleComponent.ts
@@ -0,0 +1,236 @@
+/*
+ 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 Role Component.
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, Injector, Input, OnInit } from '@angular/core';
+import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { environment } from 'environment';
+import { ProjectData } from 'ProjectModel';
+import { ProjectService } from 'ProjectService';
+import { RestService } from 'RestService';
+import { RoleData } from 'RolesModel';
+import { ProjectRoleMappings, UserDetail, UserRoleMap } from 'UserModel';
+
+/**
+ * Creating component
+ * @Component takes ProjectRole.html as template url
+ */
+@Component({
+ templateUrl: './ProjectRoleComponent.html',
+ styleUrls: ['./ProjectRoleComponent.scss']
+})
+/** Exporting a class @exports ProjectRoleComponent */
+export class ProjectRoleComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** FormGroup user Edit Account added to the form @ html @public */
+ public projectRoleForm: FormGroup;
+
+ /** Form submission Add */
+ public submitted: boolean = false;
+
+ /** Input contains Modal dialog component Instance @private */
+ @Input() public userTitle: string;
+
+ /** Input contains Modal dialog component Instance @private */
+ @Input() public userID: string;
+
+ /** Contains user details information @public */
+ public userDetails: UserDetail;
+
+ /** Project Role Mapping @public */
+ public projectRoleMap: UserRoleMap = {};
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Contains project information @public */
+ public projects: ProjectData[] = [];
+
+ /** Contains roles information @public */
+ public roles: RoleData[] = [];
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Project Role Form array @private */
+ private projectRoleFormArray: FormArray;
+
+ /** Holds all project details @private */
+ private projectService: ProjectService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.formBuilder = this.injector.get(FormBuilder);
+ this.restService = this.injector.get(RestService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.projectService = this.injector.get(ProjectService);
+ this.initializeForm();
+ }
+
+ /** Generate primitive params @public */
+ get projectRoleParamsBuilder(): FormGroup {
+ return this.formBuilder.group({
+ project_name: [null, [Validators.required]],
+ role_name: [null, [Validators.required]]
+ });
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ this.getProjects();
+ this.generateData();
+ }
+
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.projectRoleForm.controls; }
+
+ /** Initializing Form Action @public */
+ public initializeForm(): void {
+ this.projectRoleForm = this.formBuilder.group({
+ project_role_mappings: this.formBuilder.array([])
+ });
+ }
+
+ /** Handle FormArray Controls @public */
+ public getControls(): AbstractControl[] {
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ return (this.projectRoleForm.get('project_role_mappings') as FormArray).controls;
+ }
+
+ /** Fetching the data from server to Load in the smarttable @public */
+ public generateData(): void {
+ if (this.userID !== '') {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.USERS_URL + '/' + this.userID).subscribe((userDetails: UserDetail) => {
+ this.userDetails = userDetails;
+ this.loadMapping();
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'get');
+ });
+ }
+ }
+ /** Fetching the projects information @public */
+ public getProjects(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.PROJECTS_URL).subscribe((projectsData: ProjectData[]) => {
+ this.projects = projectsData;
+ this.getRoles();
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'get');
+ });
+ }
+
+ /** Fetching the Roles information @public */
+ public getRoles(): void {
+ this.restService.getResource(environment.ROLES_URL).subscribe((rolesData: RoleData[]) => {
+ this.roles = rolesData;
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'get');
+ });
+ }
+
+ /** Set all roles and project values to the form @public */
+ public loadMapping(): void {
+ this.userDetails.project_role_mappings.forEach((data: ProjectRoleMappings) => {
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ this.projectRoleFormArray = this.projectRoleForm.get('project_role_mappings') as FormArray;
+ this.projectRoleFormArray.push(this.projectRoleParamsBuilder);
+ });
+ this.projectRoleForm.patchValue(this.userDetails);
+ }
+
+ /** Remove project and roles from the list @public */
+ public removeMapping(index: number): void {
+ this.projectRoleFormArray.removeAt(index);
+ }
+
+ /** Submit project and roles @public */
+ public addProjectRole(): void {
+ this.submitted = true;
+ const modalData: MODALCLOSERESPONSEDATA = {
+ message: 'Done'
+ };
+ if (this.projectRoleForm.invalid) { return; }
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.USERS_URL + '/' + this.userID
+ };
+ this.projectRoleMap.project_role_mappings = [];
+ this.projectRoleForm.value.project_role_mappings.forEach((res: ProjectRoleMappings) => {
+ this.projectRoleMap.project_role_mappings.push({ project: res.project_name, role: res.role_name });
+ });
+ if (this.projectRoleMap.project_role_mappings.length !== 0) {
+ this.isLoadingResults = true;
+ this.restService.patchResource(apiURLHeader, this.projectRoleMap).subscribe((result: {}) => {
+ this.isLoadingResults = false;
+ this.activeModal.close(modalData);
+ this.projectService.setHeaderProjects();
+ this.notifierService.notify('success', this.translateService.instant('PAGE.USERS.EDITEDSUCCESSFULLY'));
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'patch');
+ });
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('PAGE.USERS.EDITPROJECTROLEERROR'));
+ }
+ }
+
+ /** Add extra mapping and set empty project and roles @public */
+ public addMapping(): void {
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ this.projectRoleFormArray = this.projectRoleForm.get('project_role_mappings') as FormArray;
+ this.projectRoleFormArray.push(this.projectRoleParamsBuilder);
+ }
+}
diff --git a/src/app/users/user-details/UserDetailsComponent.html b/src/app/users/user-details/UserDetailsComponent.html
new file mode 100644
index 0000000..9d11186
--- /dev/null
+++ b/src/app/users/user-details/UserDetailsComponent.html
@@ -0,0 +1,36 @@
+<!--
+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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'PAGE.DASHBOARD.USERS' | translate}}</div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" (click)="composeUser()" placement="top" container="body"
+ ngbTooltip="{{'PAGE.USERS.CREATEUSER' | translate}}">
+ <i class="fas fa-plus-circle" aria-hidden="true"></i>
+ {{'PAGE.USERS.CREATEUSER' | translate}}
+ </button>
+ </span>
+</div>
+<div class="row mt-2 mb-0 form-group justify-content-end list-utilites-actions">
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/users/user-details/UserDetailsComponent.scss b/src/app/users/user-details/UserDetailsComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/users/user-details/UserDetailsComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/users/user-details/UserDetailsComponent.ts b/src/app/users/user-details/UserDetailsComponent.ts
new file mode 100644
index 0000000..a4bedd5
--- /dev/null
+++ b/src/app/users/user-details/UserDetailsComponent.ts
@@ -0,0 +1,214 @@
+/*
+ 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 users details Component.
+ */
+import { Component, Injector, OnDestroy, OnInit } from '@angular/core';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { AddEditUserComponent } from 'AddEditUserComponent';
+import { ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { LocalDataSource } from 'ng2-smart-table';
+import { ProjectService } from 'ProjectService';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+import { UserData, UserDetail } from 'UserModel';
+import { UsersActionComponent } from 'UsersActionComponent';
+
+/**
+ * Creating component
+ * @Component takes UserDetailsComponent.html as template url
+ */
+@Component({
+ templateUrl: './UserDetailsComponent.html',
+ styleUrls: ['./UserDetailsComponent.scss']
+})
+/** Exporting a class @exports UserDetailsComponent */
+export class UserDetailsComponent implements OnInit, OnDestroy {
+ /** Data of smarttable populate through LocalDataSource @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Settings for smarttable to populate the table with columns @public */
+ public settings: object = {};
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Formation of appropriate Data for LocalDatasource @private */
+ private userData: {}[] = [];
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Holds all project details */
+ private projectService: ProjectService;
+
+ /** holds the project information @private */
+ private projectList: {}[] = [];
+
+ /** Columns list of the smart table @public */
+ private columnLists: object = {};
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.sharedService = this.injector.get(SharedService);
+ this.modalService = this.injector.get(NgbModal);
+ this.projectService = this.injector.get(ProjectService);
+ this.translateService = this.injector.get(TranslateService);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.projectService.getAllProjects().subscribe((projects: {}[]) => {
+ this.projectList = projects;
+ });
+ this.generateColumns();
+ this.generateSettings();
+ this.generateData();
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** smart table Header Colums @public */
+ public generateColumns(): void {
+ this.columnLists = {
+ username: { title: this.translateService.instant('NAME'), width: '20%', sortDirection: 'asc' },
+ projects: { title: this.translateService.instant('PAGE.DASHBOARD.PROJECTS'), width: '25%' },
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' },
+ modified: { title: this.translateService.instant('MODIFIED'), width: '15%' },
+ created: { title: this.translateService.instant('CREATED'), width: '15%' },
+ Actions: {
+ name: 'Action', width: '5%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom',
+ valuePrepareFunction: (cell: UserData, row: UserData): UserData => row,
+ renderComponent: UsersActionComponent
+ }
+ };
+ }
+
+ /** smart table Data Settings @public */
+ public generateSettings(): void {
+ this.settings = {
+ edit: { editButtonContent: '<i class="fa fa-edit" title="Edit"></i>', confirmSave: true },
+ delete: { deleteButtonContent: '<i class="far fa-trash-alt" title="delete"></i>', confirmDelete: true },
+ columns: this.columnLists,
+ actions: { add: false, edit: false, delete: false, position: 'right' },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** on Navigate to Composer Page @public */
+ public composeUser(): void {
+ const modalRef: NgbModalRef = this.modalService.open(AddEditUserComponent, { backdrop: 'static' });
+ modalRef.componentInstance.userTitle = this.translateService.instant('PAGE.USERS.NEWUSER');
+ modalRef.componentInstance.userType = 'add';
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /** smart table listing manipulation @private */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** OnUserRowSelect function @private */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'users' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Set up user details @public */
+ public setUserDetails(userData: UserDetail): void {
+ const userDataObj: UserData = {
+ username: userData.username,
+ modified: this.sharedService.convertEpochTime(userData._admin.modified),
+ created: this.sharedService.convertEpochTime(userData._admin.created),
+ projects: userData.projectListName,
+ identifier: userData._id
+ };
+ this.userData.push(userDataObj);
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+
+ /** Fetching the data from server to Load in the smarttable @protected */
+ protected generateData(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.USERS_URL).subscribe((usersData: UserDetail[]) => {
+ this.userData = [];
+ usersData.forEach((userData: UserDetail) => {
+ if (userData.projects.length > 0) {
+ userData.projectListName = userData.projects.join(', ');
+ } else {
+ userData.projectListName = '';
+ }
+ this.setUserDetails(userData);
+ });
+ if (this.userData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.userData).then((data: {}) => {
+ this.isLoadingResults = false;
+ }).catch();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+}
diff --git a/src/app/utilities/clone-package/ClonePackageComponent.html b/src/app/utilities/clone-package/ClonePackageComponent.html
new file mode 100644
index 0000000..0a75c3a
--- /dev/null
+++ b/src/app/utilities/clone-package/ClonePackageComponent.html
@@ -0,0 +1,33 @@
+<!--
+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)
+-->
+<div class="modal-header">
+ <h4 class="modal-title" id="modal-clone-title">
+ {{'CLONE' | translate}}
+ </h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+</div>
+<div class="modal-body">
+ <span>{{'CLONECONFIRMPOPUPMESSAGE' | translate}} <b>{{ params.name }}</b> ?</span>
+</div>
+<div class="modal-footer">
+ <button (click)="activeModal.close()" class="btn btn-danger">{{'CANCEL' | translate}}</button>
+ <button (click)="clonePackageInfo();" class="btn btn-primary">{{'OK' | translate }}</button>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/utilities/clone-package/ClonePackageComponent.scss b/src/app/utilities/clone-package/ClonePackageComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/utilities/clone-package/ClonePackageComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/clone-package/ClonePackageComponent.ts b/src/app/utilities/clone-package/ClonePackageComponent.ts
new file mode 100644
index 0000000..94e0920
--- /dev/null
+++ b/src/app/utilities/clone-package/ClonePackageComponent.ts
@@ -0,0 +1,180 @@
+/*
+ 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 Clone Package Model
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, Injector, Input, OnInit } from '@angular/core';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, GETAPIURLHEADER, MODALCLOSERESPONSEDATA, URLPARAMS } from 'CommonModel';
+import { environment } from 'environment';
+import * as jsyaml from 'js-yaml';
+import { NSDDetails } from 'NSDModel';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes ClonePackageComponent.html as template url
+ */
+
+@Component({
+ selector: 'app-clone-package',
+ templateUrl: './ClonePackageComponent.html',
+ styleUrls: ['./ClonePackageComponent.scss']
+})
+/** Exporting a class @exports ClonePackageComponent */
+export class ClonePackageComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Input contains component objects @public */
+ @Input() public params: URLPARAMS;
+
+ /** To handle loader status for API call @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Contains cloned package name instance @private */
+ private packageName: string = '';
+
+ /** Contains API end point for package creation @private */
+ private endPoint: string;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.sharedService = this.injector.get(SharedService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ }
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ // Empty Block
+ }
+ /**
+ * Get package information based on type
+ */
+ public clonePackageInfo(): void {
+ let apiUrl: string = '';
+ const httpOptions: GETAPIURLHEADER = this.getHttpoptions();
+ apiUrl = this.params.page === 'nsd' ? apiUrl = environment.NSDESCRIPTORS_URL + '/' + this.params.id + '/nsd' :
+ apiUrl = environment.VNFPACKAGES_URL + '/' + this.params.id + '/vnfd';
+ this.isLoadingResults = true;
+ this.restService.getResource(apiUrl, httpOptions)
+ .subscribe((nsData: NSDDetails[]) => {
+ this.modifyContent(nsData);
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ error.error = typeof error.error === 'string' ? jsyaml.load(error.error) : error.error;
+ this.restService.handleError(error, 'get');
+ });
+ }
+ /**
+ * Get HTTP header options
+ */
+ private getHttpoptions(): GETAPIURLHEADER {
+ const apiHeaders: HttpHeaders = new HttpHeaders({
+ 'Content-Type': 'application/json',
+ Accept: 'text/plain',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ return {
+ headers: apiHeaders,
+ responseType: 'text'
+ };
+ }
+ /**
+ * Get and modify package information based on type
+ */
+ private modifyContent(packageInfo: NSDDetails[]): void {
+ const packageContent: string = jsyaml.load(packageInfo.toString());
+ if (this.params.page === 'nsd') {
+ this.packageName = 'clone_' + packageContent['nsd:nsd-catalog'].nsd[0].name;
+ this.endPoint = environment.NSDESCRIPTORSCONTENT_URL;
+ packageContent['nsd:nsd-catalog'].nsd.forEach((nsd: NSDDetails) => {
+ nsd.id = 'clone_' + nsd.id;
+ nsd.name = 'clone_' + nsd.name;
+ nsd['short-name'] = 'clone_' + nsd['short-name'];
+ });
+ } else {
+ this.packageName = 'clone_' + packageContent['vnfd:vnfd-catalog'].vnfd[0].name;
+ this.endPoint = environment.VNFPACKAGESCONTENT_URL;
+ packageContent['vnfd:vnfd-catalog'].vnfd.forEach((vnfd: NSDDetails) => {
+ vnfd.id = 'clone_' + vnfd.id;
+ vnfd.name = 'clone_' + vnfd.name;
+ vnfd['short-name'] = 'clone_' + vnfd['short-name'];
+ });
+ }
+ this.clonePackage(packageContent);
+ }
+ /**
+ * Create clone package and upload as TAR.GZ file
+ */
+ private clonePackage(packageContent: string): void {
+ const descriptorInfo: string = jsyaml.dump(packageContent);
+ const apiHeader: HttpHeaders = new HttpHeaders({
+ 'Content-Type': 'application/gzip',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ const modalData: MODALCLOSERESPONSEDATA = {
+ message: 'Done'
+ };
+ this.sharedService.targzFile({ packageType: this.params.page, id: this.params.id, descriptor: descriptorInfo })
+ .then((content: ArrayBuffer): void => {
+ const apiURLHeader: APIURLHEADER = {
+ url: this.endPoint,
+ httpOptions: { headers: apiHeader }
+ };
+ this.restService.postResource(apiURLHeader, content).subscribe((result: { id: string }) => {
+ this.activeModal.close(modalData);
+ this.isLoadingResults = false;
+ this.notifierService.notify('success', this.translateService.instant('CLONESUCCESSFULLY'));
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'post');
+ });
+ }).catch((): void => {
+ this.isLoadingResults = false;
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ });
+ }
+}
diff --git a/src/app/utilities/compose-packages/ComposePackages.html b/src/app/utilities/compose-packages/ComposePackages.html
new file mode 100644
index 0000000..0d66e0f
--- /dev/null
+++ b/src/app/utilities/compose-packages/ComposePackages.html
@@ -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)
+-->
+<form [formGroup]="packagesForm" (ngSubmit)="createPackages()" autocomplete="off">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'CREATEPACKAGE' | translate}}</h4>
+ <button class="button-xs" type="button" class="close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </div>
+ <div class="modal-body">
+ <div class="form-group row">
+ <label class="col-sm-12 col-form-label mandatory-label" [ngClass]="{'text-danger': packagesForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <label class="col-sm-6 col-form-label">{{'PACKAGE' | translate}} {{'NAME' | translate}}*</label>
+ <div class="col-sm-6">
+ <input type="text" class="form-control" placeholder="{{'PACKAGE' | translate}} {{'NAME' | translate}}"
+ formControlName="name" id="name" [ngClass]="{ 'is-invalid': submitted && f.name.errors }" required>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'CREATE' | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/utilities/compose-packages/ComposePackages.scss b/src/app/utilities/compose-packages/ComposePackages.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/utilities/compose-packages/ComposePackages.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/compose-packages/ComposePackages.ts b/src/app/utilities/compose-packages/ComposePackages.ts
new file mode 100644
index 0000000..9567b43
--- /dev/null
+++ b/src/app/utilities/compose-packages/ComposePackages.ts
@@ -0,0 +1,237 @@
+/*
+ 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 Info Compose Package Model
+ */
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { Component, Injector, Input, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { Router } from '@angular/router';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, URLPARAMS } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import * as jsyaml from 'js-yaml';
+import * as pako from 'pako';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+
+/** This is added globally by the tar.js library */
+// tslint:disable-next-line: no-any
+declare const Tar: any;
+
+/**
+ * Creating component
+ * @Component takes ComposePackages.html as template url
+ */
+@Component({
+ templateUrl: './ComposePackages.html',
+ styleUrls: ['./ComposePackages.scss']
+})
+/** Exporting a class @exports ComposePackages */
+export class ComposePackages implements OnInit {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** dataService to pass the data from one component to another @public */
+ public dataService: DataService;
+
+ /** Varaibles to hold http client @public */
+ public httpClient: HttpClient;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** FormGroup instance added to the form @ html @public */
+ public packagesForm: FormGroup;
+
+ /** Form submission Add */
+ public submitted: boolean = false;
+
+ /** To handle loader status for API call @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Create URL holds the end point of any packages @private */
+ private createURL: string;
+
+ /** Input contains component objects @private */
+ @Input() private params: URLPARAMS;
+
+ /** Holds the end point @private */
+ private endPoint: string;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.dataService = this.injector.get(DataService);
+ this.restService = this.injector.get(RestService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.notifierService = this.injector.get(NotifierService);
+ this.formBuilder = this.injector.get(FormBuilder);
+ this.router = this.injector.get(Router);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/gzip',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ this.initializeForm();
+ }
+
+ /** initialize Forms @public */
+ public initializeForm(): void {
+ this.packagesForm = this.formBuilder.group({
+ name: ['', [Validators.required]]
+ });
+ }
+
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.packagesForm.controls; }
+
+ /** Create packages @public */
+ public createPackages(): void {
+ this.submitted = true;
+ this.sharedService.cleanForm(this.packagesForm);
+ if (!this.packagesForm.invalid) {
+ this.isLoadingResults = true;
+ if (this.params.page === 'ns-package') {
+ this.endPoint = environment.NSDESCRIPTORSCONTENT_URL;
+ } else if (this.params.page === 'vnf-package') {
+ this.endPoint = environment.VNFPACKAGESCONTENT_URL;
+ }
+ const descriptor: string = this.packageYaml(this.params.page);
+ try {
+ // tslint:disable-next-line: no-any
+ const tar: any = new Tar();
+ const out: Uint8Array = tar.append(this.packagesForm.value.name + '/' + this.packagesForm.value.name + '.yaml',
+ descriptor, { type: '0' });
+ const gzipContent: Uint8Array = pako.gzip(out);
+ this.createPackageApi(gzipContent.buffer);
+ } catch (e) {
+ this.isLoadingResults = false;
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ }
+ }
+ /** Create packages @public */
+ private createPackageApi(packageContent: ArrayBuffer | SharedArrayBuffer): void {
+ const apiURLHeader: APIURLHEADER = {
+ url: this.endPoint,
+ httpOptions: { headers: this.headers }
+ };
+ this.restService.postResource(apiURLHeader, packageContent).subscribe((result: { id: string }) => {
+ this.isLoadingResults = false;
+ this.activeModal.close();
+ this.composeNSPackages(result.id);
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'post');
+ });
+ }
+ /** Compose NS Packages @private */
+ private composeNSPackages(id: string): void {
+ let packageUrl: string;
+ if (this.params.page === 'ns-package') {
+ packageUrl = '/packages/ns/compose/';
+ this.notifierService.notify('success', this.packagesForm.value.name + ' ' +
+ this.translateService.instant('PAGE.NSPACKAGE.CREATEDSUCCESSFULLY'));
+ } else if (this.params.page === 'vnf-package') {
+ packageUrl = '/packages/vnf/compose/';
+ this.notifierService.notify('success', this.packagesForm.value.name + ' ' +
+ this.translateService.instant('PAGE.VNFPACKAGE.CREATEDSUCCESSFULLY'));
+ }
+ this.router.navigate([packageUrl, id]).catch(() => {
+ // Catch Navigation Error
+ });
+ }
+ /** Deafult template for NS and VNF Packages @private */
+ private packageYaml(descriptorType: string): string {
+ let packageYaml: {} = {};
+ if (descriptorType === 'ns-package') {
+ packageYaml = {
+ 'nsd:nsd-catalog': {
+ nsd: [
+ {
+ 'short-name': this.packagesForm.value.name,
+ vendor: 'OSM Composer',
+ description: this.packagesForm.value.name + ' descriptor',
+ vld: [],
+ 'constituent-vnfd': [],
+ version: '1.0',
+ id: this.packagesForm.value.name,
+ name: this.packagesForm.value.name
+ }
+ ]
+ }
+ };
+ } else {
+ packageYaml = {
+ 'vnfd:vnfd-catalog': {
+ vnfd: [
+ {
+ 'short-name': this.packagesForm.value.name,
+ vdu: [],
+ description: '',
+ 'mgmt-interface': {
+ cp: ''
+ },
+ id: this.packagesForm.value.name,
+ version: '1.0',
+ 'internal-vld': [],
+ 'connection-point': [],
+ name: this.packagesForm.value.name
+ }
+ ]
+ }
+ };
+ }
+ return jsyaml.dump(packageYaml);
+ }
+}
diff --git a/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.html b/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.html
new file mode 100644
index 0000000..03f5564
--- /dev/null
+++ b/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.html
@@ -0,0 +1,81 @@
+<!--
+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)
+-->
+<div *ngIf="topologyType === 'Add'">
+ <form [formGroup]="addConfirmationForm" (ngSubmit)="addConfirmation()" autocomplete="off">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">
+ {{'CREATE' | translate}}
+ {{ topologytitle | translate}}
+ </h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </div>
+ <div class="modal-body">
+ <div *ngIf="topologyType === 'Add'">
+ <p [innerHTML]="topologyname"></p>
+ <ng-select placeholder="{{'SELECT' | translate}}" formControlName="cpName" [items]="cpDetails" bindLabel="name"
+ bindValue="name" [(ngModel)]="connectionPointInput"
+ [ngClass]="{ 'is-invalid': submitted && f.cpName.errors }"></ng-select>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'OK' | translate }}</button>
+ </div>
+ </form>
+</div>
+<div *ngIf="topologyType !== 'Add'">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">
+ {{(topologyType === 'Delete' ? 'DELETE' : (topologyType === 'Add' ? 'CREATE' : '')) | translate}}
+ {{ topologytitle | translate}}
+ </h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </div>
+ <div class="modal-body">
+ <span *ngIf="topologyType === 'Delete'">{{'DELETECONFIRMPOPUPMESSAGE' | translate}} {{ topologyname }} ?</span>
+ <ul *ngIf="topologyType === 'Info'">
+ <li>
+ <p><b>{{'PAGE.TOPOLOGY.HELPINFO.CREATEEDGE' | translate}}</b>:
+ {{'PAGE.TOPOLOGY.HELPINFO.CREATEEDGEFIRSTSETENCE' | translate}} <span class="help-key">Shift</span> + <span
+ class="help-key">Drag</span> {{'PAGE.TOPOLOGY.HELPINFO.CREATEEDGESECONDSETENCE' | translate}}</p>
+ </li>
+ <li>
+ <p><b>{{'PAGE.TOPOLOGY.HELPINFO.DELETEEDGEVERTEX' | translate}}</b>:
+ {{'PAGE.TOPOLOGY.HELPINFO.DELETEEDGEVERTEXSENTENCE' | translate}}</p>
+ </li>
+ <li>
+ <p><b>{{'PAGE.TOPOLOGY.HELPINFO.SPREADEDGE' | translate}}</b>:
+ {{'PAGE.TOPOLOGY.HELPINFO.SPREADEDGESENTENCE' | translate}} <span class="help-key">ctrl</span> + <span
+ class="help-key">Drag</span></p>
+ </li>
+ <li>
+ <p><b>{{'PAGE.TOPOLOGY.HELPINFO.EDGEINFO' | translate}}</b>:
+ {{'PAGE.TOPOLOGY.HELPINFO.EDGEINFOSENTENCE' | translate}}</p>
+ </li>
+ </ul>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="button" *ngIf="topologyType !== 'Info'" (click)="confirmation();"
+ class="btn btn-primary">{{'OK' | translate }}</button>
+ </div>
+</div>
\ No newline at end of file
diff --git a/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.scss b/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.scss
new file mode 100644
index 0000000..1ff404c
--- /dev/null
+++ b/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.scss
@@ -0,0 +1,24 @@
+/*
+ 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)
+*/
+.help-key {
+ border: 1px solid #ddd;
+ padding: 4px;
+ border-radius: 3px;
+ background: #f6f6f6;
+ box-shadow: #999 1px 1px 1px;
+}
\ No newline at end of file
diff --git a/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.ts b/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.ts
new file mode 100644
index 0000000..a1a79b9
--- /dev/null
+++ b/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.ts
@@ -0,0 +1,102 @@
+/*
+ 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 Delete Topology Model
+ */
+import { Component, Injector, Input, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { MODALCLOSERESPONSEWITHCP } from 'CommonModel';
+/**
+ * Creating component
+ * @Component takes ConfirmationTopologyComponent.html as template url
+ */
+@Component({
+ selector: 'app-confirmation-topology',
+ templateUrl: './ConfirmationTopologyComponent.html',
+ styleUrls: ['./ConfirmationTopologyComponent.scss']
+})
+/** Exporting a class @exports ConfirmationTopologyComponent */
+export class ConfirmationTopologyComponent implements OnInit {
+ /** Form valid on submit trigger @public */
+ public submitted: boolean = false;
+ /** To inject services @public */
+ public injector: Injector;
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+ /** FormGroup instance added to the form @ html @public */
+ public addConfirmationForm: FormGroup;
+ /** Input contains Modal dialog componentInstance @private */
+ @Input() public topologytitle: string;
+ /** Input contains Modal dialog componentInstance @private */
+ @Input() public topologyname: string;
+ /** Input contains Modal dialog componentInstance @private */
+ @Input() public topologyType: string;
+ /** Input contains Modal dialog componentInstance @private */
+ @Input() public cpDetails: {}[];
+
+ /** Contains connectionpoint @public */
+ public connectionPointInput: string;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.translateService = this.injector.get(TranslateService);
+ this.formBuilder = this.injector.get(FormBuilder);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.initializeForm();
+ }
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.addConfirmationForm.controls; }
+
+ /** initialize Forms @public */
+ public initializeForm(): void {
+ this.addConfirmationForm = this.formBuilder.group({
+ cpName: [null, [Validators.required]]
+ });
+ }
+
+ /** add confirmation to be handled in this function @public */
+ public addConfirmation(): void {
+ this.submitted = true;
+ if (this.addConfirmationForm.invalid) { return; } // Proceed, onces form is valid
+ const modalData: MODALCLOSERESPONSEWITHCP = {
+ message: 'Done',
+ connection_point: this.connectionPointInput
+ };
+ this.activeModal.close(modalData);
+ }
+ /** confirmation to be handled in this function @public */
+ public confirmation(): void {
+ const modalData: MODALCLOSERESPONSEWITHCP = {
+ message: 'Done'
+ };
+ this.activeModal.close(modalData);
+ }
+
+}
diff --git a/src/app/utilities/delete/DeleteComponent.html b/src/app/utilities/delete/DeleteComponent.html
new file mode 100644
index 0000000..a39cb61
--- /dev/null
+++ b/src/app/utilities/delete/DeleteComponent.html
@@ -0,0 +1,33 @@
+<!--
+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)
+-->
+<div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">
+ {{'DELETE' | translate}}
+ </h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+</div>
+<div class="modal-body">
+ <span>{{'DELETECONFIRMPOPUPMESSAGE' | translate}} <b>{{ this.title }}</b> ?</span>
+</div>
+<div class="modal-footer">
+ <button (click)="activeModal.close()" class="btn btn-danger">{{'CANCEL' | translate}}</button>
+ <button (click)="deleteData();" class="btn btn-primary">{{'OK' | translate }}</button>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/utilities/delete/DeleteComponent.scss b/src/app/utilities/delete/DeleteComponent.scss
new file mode 100644
index 0000000..cdd37f2
--- /dev/null
+++ b/src/app/utilities/delete/DeleteComponent.scss
@@ -0,0 +1,20 @@
+/*
+ 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)
+*/
+.loadermessage-column {
+ min-height: 150px;
+}
\ No newline at end of file
diff --git a/src/app/utilities/delete/DeleteComponent.ts b/src/app/utilities/delete/DeleteComponent.ts
new file mode 100644
index 0000000..4baee64
--- /dev/null
+++ b/src/app/utilities/delete/DeleteComponent.ts
@@ -0,0 +1,199 @@
+/*
+ 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 Delete Model
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, Injector, Input } from '@angular/core';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { DELETEPARAMS, ERRORDATA, MODALCLOSERESPONSEDATA, URLPARAMS } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { RestService } from 'RestService';
+
+/**
+ * Creating component
+ * @Component takes DeleteComponent.html as template url
+ */
+@Component({
+ selector: 'app-delete',
+ templateUrl: './DeleteComponent.html',
+ styleUrls: ['./DeleteComponent.scss']
+})
+/** Exporting a class @exports DeleteComponent */
+export class DeleteComponent {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Instance of the modal service @public */
+ public title: string;
+
+ /** Show the Delete Ok button to trigger the terminate and delete */
+ public forceDelete: boolean = false;
+
+ /** Check the loading results @public */
+ public isLoadingResults: Boolean = false;
+
+ /** Give the message for the loading @public */
+ public notifyMessage: string = 'DELETELOADERMESSAGE';
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** DataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** Instance of the modal service @private */
+ private id: string;
+
+ /** Variables holds url to be delete @private */
+ private deleteURL: string;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Input contains component objects @private */
+ @Input() private params: URLPARAMS;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ this.dataService.currentMessage.subscribe((data: DELETEPARAMS) => {
+ if (data.identifier !== undefined || data.identifier !== '' || data.identifier !== null) {
+ this.id = data.identifier;
+ }
+ this.createTitleandID(data);
+ this.createDeleteUrl(data);
+ });
+ }
+ /** Generate Title and Id from data @public */
+ public createTitleandID(data: DELETEPARAMS): void {
+ this.title = '';
+ if (data.name !== undefined) {
+ this.title = data.name;
+ } else if (data.shortName !== undefined) {
+ this.title = data.shortName;
+ } else if (data.projectName !== undefined) {
+ this.title = data.projectName;
+ this.id = this.title;
+ } else if (data.userName !== undefined) {
+ this.title = data.userName;
+ } else if (data.username !== undefined) {
+ this.title = data.username;
+ }
+ }
+ /** Generate Delete url from data @public */
+ public createDeleteUrl(data: DELETEPARAMS): void {
+ this.deleteURL = '';
+ if (data.page === 'ns-instance') {
+ this.deleteURL = environment.NSINSTANCESCONTENT_URL;
+ this.forceDelete = this.params.forceDeleteType;
+ } else if (data.page === 'ns-package') {
+ this.deleteURL = environment.NSDESCRIPTORSCONTENT_URL;
+ this.notifyMessage = 'DELETEDSUCCESSFULLY';
+ } else if (data.page === 'vnf-package') {
+ this.deleteURL = environment.VNFPACKAGESCONTENT_URL;
+ this.notifyMessage = 'DELETEDSUCCESSFULLY';
+ } else if (data.page === 'vim-account') {
+ this.deleteURL = environment.VIMACCOUNTS_URL;
+ this.notifyMessage = 'DELETEDSUCCESSFULLY';
+ } else if (data.page === 'wim-account') {
+ this.deleteURL = environment.WIMACCOUNTS_URL;
+ this.notifyMessage = 'DELETEDSUCCESSFULLY';
+ } else if (data.page === 'projects') {
+ this.deleteURL = environment.PROJECTS_URL;
+ this.notifyMessage = 'DELETEDSUCCESSFULLY';
+ this.id = data.id;
+ } else if (data.page === 'users') {
+ this.deleteURL = environment.USERS_URL;
+ this.notifyMessage = 'DELETEDSUCCESSFULLY';
+ } else if (data.page === 'network-slice') {
+ this.deleteURL = environment.NETWORKSLICETEMPLATECONTENT_URL;
+ this.notifyMessage = 'DELETEDSUCCESSFULLY';
+ } else if (data.page === 'net-slice-instance') {
+ this.deleteURL = environment.NETWORKSLICEINSTANCESCONTENT_URL;
+ this.forceDelete = this.params.forceDeleteType;
+ } else if (data.page === 'roles') {
+ this.deleteURL = environment.ROLES_URL;
+ this.notifyMessage = 'DELETEDSUCCESSFULLY';
+ } else if (data.page === 'pdu-instances') {
+ this.deleteURL = environment.PDUINSTANCE_URL;
+ } else if (data.page === 'sdn-controller') {
+ this.deleteURL = environment.SDNCONTROLLER_URL;
+ this.notifyMessage = 'DELETEDSUCCESSFULLY';
+ } else if (data.page === 'k8-cluster') {
+ this.deleteURL = environment.K8SCLUSTER_URL;
+ this.notifyMessage = 'DELETEDSUCCESSFULLY';
+ } else if (data.page === 'k8-repo') {
+ this.deleteURL = environment.K8REPOS_URL;
+ this.notifyMessage = 'DELETEDSUCCESSFULLY';
+ }
+ }
+ /** Generate Data function @public */
+ public deleteData(): void {
+ this.isLoadingResults = true;
+ const modalData: MODALCLOSERESPONSEDATA = {
+ message: 'Done'
+ };
+ let deletingURl: string = '';
+ if (this.forceDelete) {
+ deletingURl = this.deleteURL + '/' + this.id + '?FORCE=true';
+ this.notifyMessage = 'DELETEDSUCCESSFULLY';
+ } else {
+ deletingURl = this.deleteURL + '/' + this.id;
+ }
+ this.restService.deleteResource(deletingURl).subscribe((res: {}) => {
+ this.activeModal.close(modalData);
+ this.notifierService.notify('success', this.translateService.instant(this.notifyMessage, { title: this.title}));
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'delete');
+ }, () => {
+ this.isLoadingResults = false;
+ });
+ }
+}
diff --git a/src/app/utilities/dragDropUpload/DragDirective.ts b/src/app/utilities/dragDropUpload/DragDirective.ts
new file mode 100644
index 0000000..735ac7c
--- /dev/null
+++ b/src/app/utilities/dragDropUpload/DragDirective.ts
@@ -0,0 +1,87 @@
+/*
+ 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 Drag and Drop feature.
+ */
+import { Directive, EventEmitter, HostBinding, HostListener, Output } from '@angular/core';
+import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
+
+/** Interface for FileHandle */
+export interface FileHandle {
+ file: File;
+ url: SafeUrl;
+}
+
+/**
+ * Creating Directive
+ * @Directive for handling the files.
+ */
+// tslint:disable-next-line:export-name
+@Directive({
+ selector: '[appDrag]'
+})
+/** Exporting a class @exports DragDirective */
+export class DragDirective {
+ /** To publish the details of files @public */
+ @Output() public files: EventEmitter<FileList> = new EventEmitter();
+
+ /** To set the background of drag and drop region @public */
+ @HostBinding('style.background') private background: string = '#e6f3fe';
+
+ /** To set the background of drag and drop region @public */
+ @HostBinding('style.color') private color: string = '#6a7a8c';
+
+ /** To trust the SecurityURL @public */
+ private sanitizer: DomSanitizer;
+
+ constructor(sanitizer: DomSanitizer) {
+ this.sanitizer = sanitizer;
+ }
+
+ /** To handle the Drag over Event @public */
+ @HostListener('dragover', ['$event']) public onDragOver(evt: DragEvent): void {
+ evt.preventDefault();
+ evt.stopPropagation();
+ this.background = '#087add';
+ this.color = '#fff';
+ }
+ /** To handle Drag leave Event @public */
+ @HostListener('dragleave', ['$event']) public onDragLeave(evt: DragEvent): void {
+ evt.preventDefault();
+ evt.stopPropagation();
+ this.background = '#e6f3fe';
+ this.color = '#6a7a8c';
+ }
+ /** To handle Drop Event @public */
+ @HostListener('drop', ['$event']) public onDrop(evt: DragEvent): void {
+ evt.preventDefault();
+ evt.stopPropagation();
+ this.background = '#e6f3fe';
+ this.color = '#6a7a8c';
+
+ const files: FileHandle[] = [];
+ Array.from(evt.dataTransfer.files).forEach((listFiles: File, index: number) => {
+ const file: File = listFiles;
+ const url: SafeUrl = this.sanitizer.bypassSecurityTrustUrl(window.URL.createObjectURL(file));
+ files.push({ file, url });
+ });
+ if (files.length > 0) {
+ this.files.emit(evt.dataTransfer.files);
+ }
+ }
+}
diff --git a/src/app/utilities/edit-packages/EditPackagesComponent.html b/src/app/utilities/edit-packages/EditPackagesComponent.html
new file mode 100644
index 0000000..2defc33
--- /dev/null
+++ b/src/app/utilities/edit-packages/EditPackagesComponent.html
@@ -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)
+-->
+<div class="form-group row">
+ <div class="d-flex align-items-center header-style">{{'EDIT' | translate}} <span
+ class="text-uppercase">{{pacakgeType}} </span>{{'DESCRIPTOR' | translate}}</div>
+</div>
+<form *ngIf="defaults[mode]">
+ <div class="row">
+ <div class="col-2">
+ <div class="form-group">
+ <select class="form-control custom-select" name="state" [(ngModel)]="mode" (ngModelChange)="changeMode()">
+ <option *ngFor="let types of fileTypes;" [value]="types.value">
+ {{types.viewValue}}</option>
+ </select>
+ </div>
+ </div>
+ <div class="col-4">
+ <div class="btn-group-toggle mb-1 mr-1 float-left">
+ <label class="btn btn-light" [class.active]="readOnly">
+ <input type="checkbox" [(ngModel)]="readOnly" name="readOnly" autocomplete="off">
+ {{'READONLYMODE' | translate}} ({{'CURRENTLY' | translate}} {{ (readOnly ? 'ON' : 'OFF') | translate }})
+ </label>
+ </div>
+ </div>
+ <div class="col-6 text-right">
+ <button type="button" class="btn btn-primary mr-2" routerLink="/packages/{{navigatePath}}/compose/{{paramsID}}"
+ [hidden]="navigatePath==='netslice'">
+ <i class="fa fa-sitemap" aria-hidden="true"></i> {{'SHOWGRAPH' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary mr-2" (click)="update(true)" [hidden]="navigatePath==='netslice'">
+ <i class="fa fa-save" aria-hidden="true"></i> {{'UPDATESHOWGRAPH' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary" (click)="update(false)">
+ <i class="fa fa-save" aria-hidden="true"></i> {{'UPDATE' | translate}}
+ </button>
+ </div>
+ </div>
+</form>
+<div class="ngx-codemirror" *ngIf="defaults[mode] else noData">
+ <ngx-codemirror [options]="options" [ngModel]="defaults[mode]" [disabled]="readOnly" [autoFocus]="true"
+ (ngModelChange)="handleChange($event)"></ngx-codemirror>
+</div>
+<ng-template #noData>
+ {{'NODATAERROR' | translate}}
+</ng-template>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/utilities/edit-packages/EditPackagesComponent.scss b/src/app/utilities/edit-packages/EditPackagesComponent.scss
new file mode 100644
index 0000000..5a9c483
--- /dev/null
+++ b/src/app/utilities/edit-packages/EditPackagesComponent.scss
@@ -0,0 +1,20 @@
+/*
+ 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)
+*/
+.ngx-codemirror {
+ font-size: 14px;
+}
\ No newline at end of file
diff --git a/src/app/utilities/edit-packages/EditPackagesComponent.ts b/src/app/utilities/edit-packages/EditPackagesComponent.ts
new file mode 100644
index 0000000..befafb8
--- /dev/null
+++ b/src/app/utilities/edit-packages/EditPackagesComponent.ts
@@ -0,0 +1,318 @@
+/*
+ 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 Edit Actions Component
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, Injector, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import 'codemirror/addon/dialog/dialog';
+import 'codemirror/addon/display/autorefresh';
+import 'codemirror/addon/display/fullscreen';
+import 'codemirror/addon/edit/closebrackets';
+import 'codemirror/addon/edit/matchbrackets';
+import 'codemirror/addon/fold/brace-fold';
+import 'codemirror/addon/fold/foldcode';
+import 'codemirror/addon/fold/foldgutter';
+import 'codemirror/addon/search/search';
+import 'codemirror/addon/search/searchcursor';
+import 'codemirror/keymap/sublime';
+import 'codemirror/lib/codemirror';
+import 'codemirror/mode/javascript/javascript';
+import 'codemirror/mode/markdown/markdown';
+import 'codemirror/mode/yaml/yaml';
+import { APIURLHEADER, ERRORDATA, GETAPIURLHEADER } from 'CommonModel';
+import { environment } from 'environment';
+import * as HttpStatus from 'http-status-codes';
+import * as jsyaml from 'js-yaml';
+import { NSDDetails } from 'NSDModel';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes EditPackagesComponent.html as template url
+ */
+@Component({
+ selector: 'app-edit-packages',
+ templateUrl: './EditPackagesComponent.html',
+ styleUrls: ['./EditPackagesComponent.scss']
+})
+
+/** Exporting a class @exports EditPackagesComponent */
+export class EditPackagesComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** dataService to pass the data from one component to another @public */
+ public identifier: {} = {};
+
+ /** readOnly @public */
+ public readOnly: boolean = false;
+
+ /** Handle the formate Change @public */
+ public defaults: {} = {
+ 'text/x-yaml': '',
+ 'text/json': ''
+ };
+
+ /** Get & Update URL VNFD & NSD */
+ public getUpdateURL: string;
+
+ /** Pass the type of VNFD & NSD for fetching text */
+ public getFileContentType: string;
+
+ /** Pass the type of VNFD & NSD for fileUpdate */
+ public updateFileContentType: string;
+
+ /** To Set Mode @public */
+ public mode: string = 'text/x-yaml';
+
+ /** To Set Mode @public */
+ public modeDefault: string = 'yaml';
+
+ /** options @public */
+ public options: {} = {
+ mode: this.modeDefault,
+ showCursorWhenSelecting: true,
+ autofocus: true,
+ autoRefresh: true,
+ lineNumbers: true,
+ lineWrapping: true,
+ foldGutter: true,
+ gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
+ autoCloseBrackets: true,
+ matchBrackets: true,
+ theme: 'neat',
+ keyMap: 'sublime'
+ };
+
+ /** Ymal Url for the VNFD & NSD */
+ public ymalUrl: string;
+
+ /** json Url for the VNFD & NSD */
+ public jsonUrl: string;
+
+ /** Navigation Path for the VNFD & NSD */
+ public navigatePath: string;
+
+ /** Package type */
+ public pacakgeType: string;
+
+ /** variables contains paramsID @public */
+ public paramsID: string;
+
+ /** Controls the File Type List form @public */
+ public fileTypes: { value: string; viewValue: string; }[] = [];
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private activatedRoute: ActivatedRoute;
+
+ /** Data @private */
+ private data: string = '';
+
+ /** contains http options @private */
+ private httpOptions: HttpHeaders;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.activatedRoute = this.injector.get(ActivatedRoute);
+ this.router = this.injector.get(Router);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/json',
+ Accept: 'text/plain',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.paramsID = this.activatedRoute.snapshot.paramMap.get('id');
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.pacakgeType = this.activatedRoute.snapshot.paramMap.get('type');
+ this.generateURLPath();
+ }
+
+ /** generate ymalURL, JSONURL, navigation Path */
+ public generateURLPath(): void {
+ if (this.pacakgeType === 'vnf') {
+ this.getUpdateURL = environment.VNFPACKAGES_URL;
+ this.getFileContentType = 'vnfd';
+ this.updateFileContentType = 'package_content';
+ this.navigatePath = 'vnf';
+ this.fileTypes = [{ value: 'text/x-yaml', viewValue: 'yaml' }, { value: 'text/json', viewValue: 'json' }];
+ this.httpOptions = this.getHeadersWithContentAccept('application/zip', 'application/json');
+ this.getEditFileData();
+ } else if (this.pacakgeType === 'netslice') {
+ this.getUpdateURL = environment.NETWORKSLICETEMPLATE_URL;
+ this.getFileContentType = 'nst';
+ this.updateFileContentType = 'nst_content';
+ this.navigatePath = 'netslice';
+ this.fileTypes = [{ value: 'text/x-yaml', viewValue: 'yaml' }];
+ this.httpOptions = this.getHeadersWithContentAccept('application/yaml', 'application/json');
+ this.getEditFileData();
+ } else {
+ this.getUpdateURL = environment.NSDESCRIPTORS_URL;
+ this.getFileContentType = 'nsd';
+ this.updateFileContentType = 'nsd_content';
+ this.pacakgeType = 'nsd';
+ this.navigatePath = 'ns';
+ this.fileTypes = [{ value: 'text/x-yaml', viewValue: 'yaml' }, { value: 'text/json', viewValue: 'json' }];
+ this.httpOptions = this.getHeadersWithContentAccept('application/zip', 'application/json');
+ this.getEditFileData();
+ }
+ }
+
+ /** Get the headers based on the type @public */
+ public getHeadersWithContentAccept(contentType: string, acceptType: string): HttpHeaders {
+ this.headers = new HttpHeaders({
+ 'Content-Type': contentType,
+ Accept: acceptType,
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ return this.headers;
+ }
+
+ /** ChangeMode function @public */
+ public changeMode(): void {
+ if (this.mode === 'text/x-yaml') {
+ this.modeDefault = 'yaml';
+ } else {
+ this.modeDefault = 'javascript';
+ }
+ this.options = {
+ ...this.options,
+ mode: this.modeDefault
+ };
+ this.data = '';
+ }
+
+ /** HandleChange function @public */
+ public handleChange($event: string): void {
+ this.data = $event;
+ }
+
+ /** Update function @public */
+ public update(showgraph: boolean): void {
+ if (this.data === '') {
+ this.notifierService.notify('warning', this.translateService.instant('PAGE.TOPOLOGY.DATAEMPTY'));
+ } else {
+ this.updateCheck(showgraph);
+ }
+ }
+ /** Update the file Data @public */
+ public updateFileData(urlHeader: APIURLHEADER, fileData: string | ArrayBuffer, showgraph: boolean, packageType: string): void {
+ this.restService.putResource(urlHeader, fileData).subscribe(() => {
+ this.isLoadingResults = false;
+ this.notifierService.notify('success', this.translateService.instant(
+ (packageType !== 'netslice') ? 'PAGE.NSPACKAGE.EDITPACKAGES.UPDATEDSUCCESSFULLY' : 'PAGE.NETSLICE.UPDATEDSUCCESSFULLY'));
+ if (showgraph) {
+ if (packageType === 'nsd') {
+ this.router.navigate(['/packages/ns/compose/' + this.paramsID]).catch();
+ } else if (packageType === 'vnf') {
+ this.router.navigate(['/packages/vnf/compose/' + this.paramsID]).catch();
+ }
+ }
+ this.getEditFileData();
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'put');
+ });
+ }
+ /** Update method for NS, VNF and net-slice template */
+ private updateCheck(showgraph: boolean): void {
+ this.isLoadingResults = true;
+ const apiURLHeader: APIURLHEADER = {
+ url: this.getUpdateURL + '/' + this.paramsID + '/' + this.updateFileContentType,
+ httpOptions: { headers: this.httpOptions }
+ };
+ let descriptorInfo: string = '';
+ if (this.mode === 'text/json') {
+ descriptorInfo = jsyaml.dump(JSON.parse(this.data), {sortKeys: true});
+ } else {
+ descriptorInfo = this.data;
+ }
+ if (this.getFileContentType !== 'nst') {
+ this.sharedService.targzFile({ packageType: this.pacakgeType, id: this.paramsID, descriptor: descriptorInfo })
+ .then((content: ArrayBuffer): void => {
+ this.updateFileData(apiURLHeader, content, showgraph, this.pacakgeType);
+ }).catch((): void => {
+ this.isLoadingResults = false;
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ });
+ } else {
+ this.updateFileData(apiURLHeader, descriptorInfo, showgraph, this.pacakgeType);
+ }
+ }
+ /** Get the YAML content response as a plain/text and convert to JSON Format @private */
+ private getEditFileData(): void {
+ this.isLoadingResults = true;
+ const gethttpOptions: HttpHeaders = this.getHeadersWithContentAccept('application/json', 'text/plain');
+ const httpOptions: GETAPIURLHEADER = {
+ headers: gethttpOptions,
+ responseType: 'text'
+ };
+ this.restService.getResource(this.getUpdateURL + '/' + this.paramsID + '/' + this.getFileContentType, httpOptions)
+ .subscribe((nsData: NSDDetails[]) => {
+ const getJson: string = jsyaml.load(nsData.toString(), { json: true });
+ //tslint:disable-next-line:no-string-literal
+ this.defaults['text/x-yaml'] = nsData.toString();
+ this.defaults['text/json'] = JSON.stringify(getJson, null, '\t');
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ error.error = typeof error.error === 'string' ? jsyaml.load(error.error) : error.error;
+ if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED ) {
+ this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
+ } else {
+ this.restService.handleError(error, 'get');
+ }
+ this.isLoadingResults = false;
+ });
+ }
+}
diff --git a/src/app/utilities/loader/LoaderComponent.html b/src/app/utilities/loader/LoaderComponent.html
new file mode 100644
index 0000000..30ce264
--- /dev/null
+++ b/src/app/utilities/loader/LoaderComponent.html
@@ -0,0 +1,27 @@
+<!--
+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)
+-->
+<div class="loader-overlay">
+ <div class="loader-content loader-center text-center">
+ <div class="loader">{{'LOADING' | translate}}...</div>
+ <p class="loader-text">
+ <strong>{{getMessage | translate}}
+ <span class="loader__dot" *ngFor="let index of [0,1,2]">.</span>
+ </strong>
+ </p>
+ </div>
+</div>
\ No newline at end of file
diff --git a/src/app/utilities/loader/LoaderComponent.scss b/src/app/utilities/loader/LoaderComponent.scss
new file mode 100644
index 0000000..6bfd321
--- /dev/null
+++ b/src/app/utilities/loader/LoaderComponent.scss
@@ -0,0 +1,89 @@
+/*
+ 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 '../../../assets/scss/mixins/mixin';
+@import '../../../assets/scss/variable';
+.loader-overlay {
+ -ms-opacity: 0.9;
+ @include background(null, $white, null, null, null);
+ @include position_value(absolute, 0, null, null, 0);
+ @include wh-value(100%, 100%);
+ opacity: 0.9;
+ vertical-align: middle;
+ z-index: 100000;
+ .loader-content {
+ @include wh-value(50%, null);
+ margin-left: auto;
+ margin-top: auto;
+ .loader-text {
+ color: $body-color;
+ }
+ }
+ .loader-center {
+ @include position_value(absolute, 50%, null, null, 50%);
+ @include flexbox(block, null, null, null, null, null);
+ -moz-transform: translate(-50%, -50%);
+ -ms-transform: translate(-50%, -50%);
+ -o-transform: translate(-50%, -50%);
+ -webkit-transform: translate(-50%, -50%);
+ transform: translate(-50%, -55%);
+ }
+}
+.loader,
+.loader:after {
+ @include roundedCornersPercentage(50%);
+ @include wh-value(10em, 10em);
+}
+.loader {
+ @include font(null, 3px, null);
+ @include position_value(relative, null, null, null, null);
+ @include border(top, 2, solid, rgba(5, 76, 140, 0.28));
+ @include border(right, 2, solid, rgba(5, 76, 140, 0.28));
+ @include border(bottom, 2, solid, rgba(5, 76, 140, 0.28));
+ @include border(left, 2, solid, $primary);
+ margin: 0 auto;
+ text-indent: -9999em;
+ -webkit-transform: translateZ(0);
+ -ms-transform: translateZ(0);
+ transform: translateZ(0);
+ -webkit-animation: load8 1.1s infinite linear;
+ animation: load8 1.1s infinite linear;
+}
+@-webkit-keyframes load8 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+@keyframes load8 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+@keyframes blink {50% { color: transparent }}
+.loader__dot { animation: 1s blink infinite }
+.loader__dot:nth-child(2) { animation-delay: 250ms }
+.loader__dot:nth-child(3) { animation-delay: 500ms }
\ No newline at end of file
diff --git a/src/app/utilities/loader/LoaderComponent.ts b/src/app/utilities/loader/LoaderComponent.ts
new file mode 100644
index 0000000..ee8cab8
--- /dev/null
+++ b/src/app/utilities/loader/LoaderComponent.ts
@@ -0,0 +1,50 @@
+/*
+ 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 Delete Model
+ */
+import { Component, Input, OnInit } from '@angular/core';
+/**
+ * Creating component
+ * @Component takes LoaderComponent.html as template url
+ */
+@Component({
+ selector: 'app-loader',
+ templateUrl: './LoaderComponent.html',
+ styleUrls: ['./LoaderComponent.scss']
+})
+/** Exporting a class @exports LoaderComponent */
+export class LoaderComponent implements OnInit {
+ /** Variables declared to get the message from parents @public */
+ @Input() public waitingMessage: string;
+ /** Variables declared to get the message of loader @public */
+ public getMessage: string;
+
+ constructor() {
+ // Empty block
+ }
+
+ public ngOnInit(): void {
+ if (this.waitingMessage !== '') {
+ this.getMessage = this.waitingMessage;
+ } else {
+ this.getMessage = '';
+ }
+ }
+
+}
diff --git a/src/app/utilities/loader/LoaderModule.ts b/src/app/utilities/loader/LoaderModule.ts
new file mode 100644
index 0000000..ba81b6a
--- /dev/null
+++ b/src/app/utilities/loader/LoaderModule.ts
@@ -0,0 +1,41 @@
+/*
+ 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 Loader Module.
+ */
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { TranslateModule } from '@ngx-translate/core';
+import { LoaderComponent } from 'LoaderComponent';
+/**
+ * Creating @NgModule component for Modules
+ */
+@NgModule({
+ imports: [CommonModule, TranslateModule],
+ declarations: [LoaderComponent],
+ exports: [LoaderComponent]
+})
+/** Exporting a class @exports LoaderModule */
+export class LoaderModule {
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ // Empty Block
+ }
+}
diff --git a/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.html b/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.html
new file mode 100644
index 0000000..7fcc812
--- /dev/null
+++ b/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.html
@@ -0,0 +1,42 @@
+<!--
+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)
+-->
+<div class="btn-group list action" role="group">
+ <button type="button" class="btn btn-primary" (click)="infoNetSliceInstance()" placement="top" container="body"
+ ngbTooltip="{{'INFO' | translate}}">
+ <i class="fas fa-info icons"></i>
+ </button>
+ <button type="button" class="btn btn-primary" (click)="deleteNetSliceInstance(false)" placement="top"
+ container="body" ngbTooltip="{{'DELETE' | translate}}">
+ <i class="far fa-trash-alt icons"></i>
+ </button>
+ <div ngbDropdown class="btn-group">
+ <button type="button" class="btn btn-primary dropdown-toggle action-button" ngbDropdownToggle>
+ {{'ACTION' | translate}}
+ </button>
+ <div class="dropdown-menu" ngbDropdownMenu>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="historyOfOperations()" placement="left"
+ container="body" ngbTooltip="{{'HISTORYOFOPERATIONS' | translate}}">
+ <i class="fas fa-history"></i> {{'HISTORYOFOPERATIONS' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary dropdown-item text-danger" (click)="deleteNetSliceInstance(true)" placement="left"
+ container="body" ngbTooltip="{{'FORCEDELETE' | translate}}">
+ <i class="fas fa-trash-alt icons text-danger"></i> {{'FORCEDELETE' | translate}}
+ </button>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.scss b/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.ts b/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.ts
new file mode 100644
index 0000000..0e528a8
--- /dev/null
+++ b/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.ts
@@ -0,0 +1,95 @@
+/*
+ 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 Netslice InstancesAction Component
+ */
+import { Component, Injector } from '@angular/core';
+import { Router } from '@angular/router';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DeleteComponent } from 'DeleteComponent';
+import { NSTInstanceData } from 'NetworkSliceModel';
+import { SharedService } from 'SharedService';
+import { ShowInfoComponent } from 'ShowInfoComponent';
+/**
+ * Creating component
+ * @Component takes NetsliceInstancesActionComponent.html as template url
+ */
+@Component({
+ templateUrl: './NetsliceInstancesActionComponent.html',
+ styleUrls: ['./NetsliceInstancesActionComponent.scss']
+})
+/** Exporting a class @exports NetsliceInstancesActionComponent */
+export class NetsliceInstancesActionComponent {
+ /** To get the value from the vnfpackage via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: NSTInstanceData;
+
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Contains instance ID @private */
+ private instanceID: string;
+
+ /** Service holds the router information @private */
+ private router: Router;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.modalService = this.injector.get(NgbModal);
+ this.router = this.injector.get(Router);
+ this.sharedService = this.injector.get(SharedService);
+ }
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.instanceID = this.value.identifier;
+ }
+
+ /** Shows information using modalservice @public */
+ public infoNetSliceInstance(): void {
+ this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = {
+ id: this.instanceID,
+ page: 'net-slice-instance',
+ titleName: 'PAGE.NETSLICETEMPLATE.NETSLICETEMPLATEDETAILS'
+ };
+ }
+
+ /** Delete NetSlice Instance packages @public */
+ public deleteNetSliceInstance(forceAction: boolean): void {
+ const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' });
+ modalRef.componentInstance.params = {forceDeleteType: forceAction};
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+ /** History of operations for an Instanace @public */
+ public historyOfOperations(): void {
+ this.router.navigate(['/instances/netslice/history-operations/', this.instanceID]).catch(() => {
+ // Catch Navigation Error
+ });
+ }
+}
diff --git a/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.html b/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.html
new file mode 100644
index 0000000..d47d65e
--- /dev/null
+++ b/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.html
@@ -0,0 +1,34 @@
+<!--
+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)
+-->
+<div class="btn-group list action" role="group">
+ <button type="button" class="btn btn-primary" (click)="instantiateNetSlice()" placement="top" container="body"
+ ngbTooltip="{{'INSTANTIATE' | translate}} NS">
+ <i class="fa fa-paper-plane icons"></i>
+ </button>
+ <button type="button" class="btn btn-primary" (click)="infoNetSlice()" placement="top" container="body"
+ ngbTooltip="{{'INFO' | translate}}">
+ <i class="fas fa-info icons"></i>
+ </button>
+ <button type="button" class="btn btn-primary" (click)="netSliceEdit()" placement="top" container="body" ngbTooltip="{{'EDIT' | translate}}">
+ <i class="fa fa-edit icons"></i>
+ </button>
+ <button type="button" class="btn btn-primary" (click)="deleteNetSliceTemplate()" placement="top" container="body"
+ ngbTooltip="{{'DELETE' | translate}}">
+ <i class="far fa-trash-alt icons"></i>
+ </button>
+</div>
\ No newline at end of file
diff --git a/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.scss b/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.ts b/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.ts
new file mode 100644
index 0000000..8be5f50
--- /dev/null
+++ b/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.ts
@@ -0,0 +1,102 @@
+/*
+ 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 Netslice-packagesAction Component
+ */
+import { Component, Injector } from '@angular/core';
+import { Router } from '@angular/router';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DeleteComponent } from 'DeleteComponent';
+import { InstantiateNetSliceTemplateComponent } from 'InstantiateNetSliceTemplate';
+import { NetworkSliceData } from 'NetworkSliceModel';
+import { SharedService } from 'SharedService';
+import { ShowInfoComponent } from 'ShowInfoComponent';
+
+/**
+ * Creating component
+ * @Component takes NetslicePackagesActionComponent.html as template url
+ */
+@Component({
+ selector: 'app-netslice-packages-action',
+ templateUrl: './NetslicePackagesActionComponent.html',
+ styleUrls: ['./NetslicePackagesActionComponent.scss']
+})
+/** Exporting a class @exports NetslicePackagesActionComponent */
+export class NetslicePackagesActionComponent {
+ /** To get the value from the vnfpackage via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: NetworkSliceData;
+
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Contains instance ID @private */
+ private instanceID: string;
+
+ /** Service holds the router information @private */
+ private router: Router;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.modalService = this.injector.get(NgbModal);
+ this.router = this.injector.get(Router);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.instanceID = this.value.identifier;
+ }
+
+ /** Delete NetSliceTemplate packages @public */
+ public deleteNetSliceTemplate(): void {
+ const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, {backdrop: 'static'});
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /** Shows information using modalservice @public */
+ public infoNetSlice(): void {
+ this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = {
+ id: this.instanceID,
+ page: 'net-slice-package',
+ titleName: 'PAGE.NETSLICETEMPLATE.NETSLICETEMPLATEDETAILS'
+ };
+ }
+
+ /** Set Edit for the @public */
+ public netSliceEdit(): void {
+ this.router.navigate(['/packages/netslice/edit/', this.instanceID]).catch(() => {
+ // Catch Navigation Error
+ });
+ }
+
+ /** Instantiate Net Slice using modalservice @public */
+ public instantiateNetSlice(): void {
+ this.modalService.open(InstantiateNetSliceTemplateComponent, { backdrop: 'static' });
+ }
+}
diff --git a/src/app/utilities/ns-instances-action/NSInstancesActionComponent.html b/src/app/utilities/ns-instances-action/NSInstancesActionComponent.html
new file mode 100644
index 0000000..41a58f5
--- /dev/null
+++ b/src/app/utilities/ns-instances-action/NSInstancesActionComponent.html
@@ -0,0 +1,56 @@
+<!--
+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)
+-->
+<div class="btn-group list action" role="group">
+ <button type="button" class="btn btn-primary" (click)="metrics()" placement="top" container="body"
+ [disabled]="operationalStatus == 'failed' || configStatus == 'failed'" ngbTooltip="{{'METRICS' | translate}}">
+ <i class="fas fa-chart-bar icons"></i>
+ </button>
+ <button type="button" class="btn btn-primary" [disabled]="operationalStatus != 'running'" (click)="nsTopology()"
+ placement="top" container="body" ngbTooltip="{{'TOPOLOGY' | translate}}">
+ <i class="fa fa-sitemap fa-fw icons"></i>
+ </button>
+ <button type="button" class="btn btn-primary" (click)="deleteNSInstance(false)" placement="top" container="body"
+ ngbTooltip="{{'DELETE' | translate}}">
+ <i class="far fa-trash-alt icons"></i>
+ </button>
+ <div ngbDropdown class="btn-group">
+ <button type="button" class="btn btn-primary dropdown-toggle action-button" ngbDropdownToggle>
+ {{'ACTION' | translate}}
+ </button>
+ <div class="dropdown-menu" ngbDropdownMenu>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="infoNs()" placement="left"
+ container="body" ngbTooltip="{{'INFO' | translate}}">
+ <i class="fas fa-info icons list" title="info"></i> {{'INFO' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="execNSPrimitiveModal()"
+ placement="left" container="body" ngbTooltip="{{'EXECNSPRIMITIVE' | translate}}"
+ [disabled]="operationalStatus == 'failed' || configStatus == 'failed'">
+ <i class="fas fa-magic"></i> {{'EXECNSPRIMITIVE' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="historyOfOperations()" placement="left"
+ container="body" ngbTooltip="{{'HISTORYOFOPERATIONS' | translate}}">
+ <i class="fas fa-history"></i> {{'HISTORYOFOPERATIONS' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary dropdown-item text-danger" (click)="deleteNSInstance(true)"
+ placement="left" container="body" ngbTooltip="{{'FORCEDELETE' | translate}}">
+ <i class="fas fa-trash-alt icons text-danger"></i> {{'FORCEDELETE' | translate}}
+ </button>
+ </div>
+ </div>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingMetricsResult"></app-loader>
\ No newline at end of file
diff --git a/src/app/utilities/ns-instances-action/NSInstancesActionComponent.scss b/src/app/utilities/ns-instances-action/NSInstancesActionComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/utilities/ns-instances-action/NSInstancesActionComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/ns-instances-action/NSInstancesActionComponent.ts b/src/app/utilities/ns-instances-action/NSInstancesActionComponent.ts
new file mode 100644
index 0000000..8bf43ce
--- /dev/null
+++ b/src/app/utilities/ns-instances-action/NSInstancesActionComponent.ts
@@ -0,0 +1,181 @@
+/*
+ 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 NS InstancesAction Component
+ */
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector } from '@angular/core';
+import { Router } from '@angular/router';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DeleteComponent } from 'DeleteComponent';
+import { environment } from 'environment';
+import { NSDDetails } from 'NSDModel';
+import { NSDInstanceData } from 'NSInstanceModel';
+import { NSPrimitiveComponent } from 'NSPrimitiveComponent';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { ShowInfoComponent } from 'ShowInfoComponent';
+/**
+ * Creating component
+ * @Component takes NSInstancesActionComponent.html as template url
+ */
+@Component({
+ templateUrl: './NSInstancesActionComponent.html',
+ styleUrls: ['./NSInstancesActionComponent.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+/** Exporting a class @exports NSInstancesActionComponent */
+export class NSInstancesActionComponent {
+ /** To get the value from the nspackage via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: NSDInstanceData;
+
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** Instance of the modal service @public */
+ public restService: RestService;
+
+ /** Config Status Check @public */
+ public configStatus: string;
+
+ /** Operational Status Check @public */
+ public operationalStatus: string;
+
+ /** Check the loading results for loader status @public */
+ public isLoadingMetricsResult: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ /** Contains instance ID @private */
+ private instanceID: string;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Detect changes for the User Input */
+ private cd: ChangeDetectorRef;
+
+ /** Set timeout @private */
+ private timeOut: number = 1000;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.modalService = this.injector.get(NgbModal);
+ this.restService = this.injector.get(RestService);
+ this.router = this.injector.get(Router);
+ this.sharedService = this.injector.get(SharedService);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.cd = this.injector.get(ChangeDetectorRef);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.configStatus = this.value.ConfigStatus;
+ this.operationalStatus = this.value.OperationalStatus;
+ this.instanceID = this.value.identifier;
+ }
+
+ /** Shows information using modalservice @public */
+ public infoNs(): void {
+ this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = {
+ id: this.instanceID,
+ page: 'ns-instance',
+ titleName: 'INSTANCEDETAILS'
+ };
+ }
+
+ /** Delete NS Instanace @public */
+ public deleteNSInstance(forceAction: boolean): void {
+ const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' });
+ modalRef.componentInstance.params = { forceDeleteType: forceAction };
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /** History of operations for an Instanace @public */
+ public historyOfOperations(): void {
+ this.router.navigate(['/instances/ns/history-operations/', this.instanceID]).catch(() => {
+ // Catch Navigation Error
+ });
+ }
+
+ /** NS Topology */
+ public nsTopology(): void {
+ this.router.navigate(['/instances/ns/', this.instanceID]).catch(() => {
+ // Catch Navigation Error
+ });
+ }
+
+ /** Exec NS Primitive @public */
+ public execNSPrimitiveModal(): void {
+ this.modalService.open(NSPrimitiveComponent).componentInstance.params = {
+ memberIndex: this.value.memberIndex,
+ nsConfig: this.value.nsConfig
+ };
+ }
+
+ /** Redirect to Grafana Metrics @public */
+ public metrics(): void {
+ this.isLoadingMetricsResult = true;
+ this.restService.getResource(environment.NSDINSTANCES_URL + '/' + this.instanceID).subscribe((nsData: NSDDetails[]) => {
+ nsData['vnfd-id'].forEach((vnfdID: string[]) => {
+ this.restService.getResource(environment.VNFPACKAGES_URL + '/' + vnfdID)
+ .subscribe((vnfd: {}[]) => {
+ if (vnfd['monitoring-param'] !== undefined && vnfd['monitoring-param'].length > 0) {
+ this.isLoadingMetricsResult = false;
+ const location: string = environment.GRAFANA_URL + '/' + this.instanceID + '/osm-ns-metrics-metrics';
+ window.open(location);
+ } else {
+ this.isLoadingMetricsResult = false;
+ this.notifierService.notify('error', this.translateService.instant('GRAFANA.METRICSERROR'));
+ }
+ setTimeout(() => {
+ this.cd.detectChanges();
+ }, this.timeOut);
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingMetricsResult = false;
+ });
+ });
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingMetricsResult = false;
+ });
+ }
+}
diff --git a/src/app/utilities/ns-packages-action/NsPackagesActionComponent.html b/src/app/utilities/ns-packages-action/NsPackagesActionComponent.html
new file mode 100644
index 0000000..6e33d0d
--- /dev/null
+++ b/src/app/utilities/ns-packages-action/NsPackagesActionComponent.html
@@ -0,0 +1,55 @@
+<!--
+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)
+-->
+<div class="btn-group list action" role="group">
+ <button type="button" class="btn btn-primary" (click)="instantiateNS()" placement="top" container="body"
+ ngbTooltip="{{'INSTANTIATE' | translate}} NS">
+ <i class="fa fa-paper-plane icons"></i>
+ </button>
+ <button type="button" class="btn btn-primary" (click)="composeNSPackages()" placement="top" container="body"
+ ngbTooltip="{{'TOPOLOGY' | translate}}">
+ <i class="fa fa-sitemap fa-fw Icon"></i>
+ </button>
+ <button type="button" class="btn btn-primary" (click)="deleteNSPackage()" placement="top" container="body"
+ ngbTooltip="{{'DELETE' | translate}}">
+ <i class="far fa-trash-alt icons"></i>
+ </button>
+ <div ngbDropdown class="btn-group">
+ <button type="button" class="btn btn-primary dropdown-toggle action-button" ngbDropdownToggle>
+ {{'ACTION' | translate}}
+ </button>
+ <div class="dropdown-menu" ngbDropdownMenu>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="nsdEdit()" placement="left"
+ container="body" ngbTooltip="{{'EDIT' | translate}}">
+ <i class="fa fa-edit icons"></i> {{'EDIT' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="downloadNSPackage()" placement="left"
+ container="body" ngbTooltip="{{'DOWNLOAD' | translate}}">
+ <i class="fas fa-download icons"></i> {{'DOWNLOAD' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="cloneNSPackage()" placement="left"
+ container="body" ngbTooltip="{{'CLONE' | translate}}">
+ <i class="fa fa-clone icons"></i> {{'CLONE' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="showContent()" placement="left"
+ container="body" ngbTooltip="{{'CONTENT' | translate}}">
+ <i class="far fa-folder-open icons"></i> {{'CONTENT' | translate}}
+ </button>
+ </div>
+ </div>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingDownloadResult"></app-loader>
\ No newline at end of file
diff --git a/src/app/utilities/ns-packages-action/NsPackagesActionComponent.scss b/src/app/utilities/ns-packages-action/NsPackagesActionComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/utilities/ns-packages-action/NsPackagesActionComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/ns-packages-action/NsPackagesActionComponent.ts b/src/app/utilities/ns-packages-action/NsPackagesActionComponent.ts
new file mode 100644
index 0000000..6e7fbfb
--- /dev/null
+++ b/src/app/utilities/ns-packages-action/NsPackagesActionComponent.ts
@@ -0,0 +1,193 @@
+/*
+ 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 NS PackagesAction Component
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { ChangeDetectorRef, Component, Injector } from '@angular/core';
+import { Router } from '@angular/router';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { ClonePackageComponent } from 'ClonePackage';
+import { ERRORDATA, GETAPIURLHEADER, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DeleteComponent } from 'DeleteComponent';
+import { environment } from 'environment';
+import { InstantiateNsComponent } from 'InstantiateNs';
+import { NSData } from 'NSDModel';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { ShowContentComponent } from 'ShowContent';
+
+/**
+ * Creating component
+ * @Component takes NsPackagesActionComponent.html as template url
+ */
+@Component({
+ selector: 'app-ns-packages-action',
+ templateUrl: './NsPackagesActionComponent.html',
+ styleUrls: ['./NsPackagesActionComponent.scss']
+})
+
+/** Exporting a class @exports NsPackagesActionComponent */
+export class NsPackagesActionComponent {
+ /** To get the value from the nspackage via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: NSData;
+
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Check the loading results for loader status @public */
+ public isLoadingDownloadResult: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Variables holds NS ID @private */
+ private nsdID: string;
+
+ /** Variables holds NS name @private */
+ private nsdName: string;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Detect changes for the User Input */
+ private cd: ChangeDetectorRef;
+
+ /** Set timeout @private */
+ private timeOut: number = 1000;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.sharedService = this.injector.get(SharedService);
+ this.modalService = this.injector.get(NgbModal);
+ this.router = this.injector.get(Router);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.cd = this.injector.get(ChangeDetectorRef);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.headers = new HttpHeaders({
+ Accept: 'application/zip, application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ this.nsdID = this.value.identifier;
+ this.nsdName = this.value.shortName;
+ }
+
+ /** Instantiate NS using modalservice @public */
+ public instantiateNS(): void {
+ this.modalService.open(InstantiateNsComponent, { backdrop: 'static' });
+ }
+
+ /** Delete NS Package @public */
+ public deleteNSPackage(): void {
+ const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' });
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /** Set instance for NSD Edit @public */
+ public nsdEdit(): void {
+ this.router.navigate(['/packages/ns/edit/', this.nsdID]).catch(() => {
+ // Catch Navigation Error
+ });
+ }
+
+ /** list out all the file content of a descriptors @public */
+ public showContent(): void {
+ this.modalService.open(ShowContentComponent, { backdrop: 'static' }).componentInstance.params = { id: this.nsdID, page: 'nsd' };
+ }
+
+ /** Download NS Package @public */
+ public downloadNSPackage(): void {
+ this.isLoadingDownloadResult = true;
+ const httpOptions: GETAPIURLHEADER = {
+ headers: this.headers,
+ responseType: 'blob'
+ };
+ this.restService.getResource(environment.NSDESCRIPTORS_URL + '/' + this.nsdID + '/nsd_content', httpOptions)
+ .subscribe((response: Blob) => {
+ const binaryData: Blob[] = [];
+ binaryData.push(response);
+ this.sharedService.downloadFiles(this.nsdName, binaryData, response.type);
+ this.isLoadingDownloadResult = false;
+ this.changeDetactionforDownload();
+ }, (error: ERRORDATA) => {
+ this.isLoadingDownloadResult = false;
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ this.changeDetactionforDownload();
+ if (typeof error.error === 'object') {
+ error.error.text().then((data: string): void => {
+ error.error = JSON.parse(data);
+ this.restService.handleError(error, 'getBlob');
+ });
+ }
+ });
+ }
+
+ /** Compose NS Packages @public */
+ public composeNSPackages(): void {
+ this.router.navigate(['/packages/ns/compose/', this.nsdID]).catch(() => {
+ // Catch Navigation Error
+ });
+ }
+
+ /** Change the detaction @public */
+ public changeDetactionforDownload(): void {
+ setTimeout(() => {
+ this.cd.detectChanges();
+ }, this.timeOut);
+ }
+
+ /** Clone NS Packages @public */
+ public cloneNSPackage(): void {
+ const cloneModal: NgbModalRef = this.modalService.open(ClonePackageComponent, { backdrop: 'static' });
+ cloneModal.componentInstance.params = { id: this.nsdID, page: 'nsd', name: this.nsdName };
+ cloneModal.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+}
diff --git a/src/app/utilities/page-per-row/PagePerRow.html b/src/app/utilities/page-per-row/PagePerRow.html
new file mode 100644
index 0000000..a63ebb4
--- /dev/null
+++ b/src/app/utilities/page-per-row/PagePerRow.html
@@ -0,0 +1,26 @@
+<!--
+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)
+-->
+<div class="page-per-row">
+ <label class="mr-2 mt-2">
+ <b>{{'ENTRIES' | translate}}</b>
+ </label>
+ <select class="form-control custom-select" (change)="onSelectRow($event.target.value)">
+ <option *ngFor="let count of pageCount; let i = index" [value]="count.value" [selected]="count.value === getDefaultSelected">{{count.viewValue}}
+ </option>
+ </select>
+</div>
\ No newline at end of file
diff --git a/src/app/utilities/page-per-row/PagePerRow.scss b/src/app/utilities/page-per-row/PagePerRow.scss
new file mode 100644
index 0000000..06c069e
--- /dev/null
+++ b/src/app/utilities/page-per-row/PagePerRow.scss
@@ -0,0 +1,23 @@
+/*
+ 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)
+*/
+.page-per-row {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+}
\ No newline at end of file
diff --git a/src/app/utilities/page-per-row/PagePerRow.ts b/src/app/utilities/page-per-row/PagePerRow.ts
new file mode 100644
index 0000000..6794bfc
--- /dev/null
+++ b/src/app/utilities/page-per-row/PagePerRow.ts
@@ -0,0 +1,80 @@
+/*
+ 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 PagePerRow Model
+ */
+import { Component, EventEmitter, Injector, Output } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { PAGERSMARTTABLE } from 'CommonModel';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes PagePerRow.html as template url
+ */
+@Component({
+ selector: 'page-per-row',
+ templateUrl: './PagePerRow.html',
+ styleUrls: ['./PagePerRow.scss']
+})
+/** Exporting a class @exports PagePerRow */
+export class PagePerRow {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** get the pagaintion default select value @public */
+ public getDefaultSelected: number;
+
+ /** Controls the pagination List Count form @public */
+ public pageCount: { value: number; viewValue: number; }[] =
+ [
+ { value: 10, viewValue: 10 },
+ { value: 25, viewValue: 25 },
+ { value: 50, viewValue: 50 },
+ { value: 100, viewValue: 100 }
+ ];
+
+ /** Contains all methods related to shared @private */
+ public sharedService: SharedService;
+
+ /** Event emitter to emit selected page number @public */
+ @Output() public pagePerRow: EventEmitter<number> = new EventEmitter();
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ const getPaginationValues: PAGERSMARTTABLE = this.sharedService.paginationPagerConfig();
+ this.getDefaultSelected = getPaginationValues.perPage;
+ }
+
+ /** Handles select event @public */
+ public onSelectRow(e: number): void {
+ this.pagePerRow.emit(e);
+ }
+}
diff --git a/src/app/utilities/page-per-row/PagePerRowModule.ts b/src/app/utilities/page-per-row/PagePerRowModule.ts
new file mode 100644
index 0000000..e5b8a29
--- /dev/null
+++ b/src/app/utilities/page-per-row/PagePerRowModule.ts
@@ -0,0 +1,42 @@
+/*
+ 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 Page Per Row Module.
+ */
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { TranslateModule } from '@ngx-translate/core';
+import { PagePerRow } from 'PagePerRow';
+
+/**
+ * Creating @NgModule component for Modules
+ */
+@NgModule({
+ imports: [CommonModule, TranslateModule],
+ declarations: [PagePerRow],
+ exports: [PagePerRow]
+})
+/** Exporting a class @exports PagePerRowModule */
+export class PagePerRowModule {
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ // Empty Block
+ }
+}
diff --git a/src/app/utilities/page-reload/PageReload.html b/src/app/utilities/page-reload/PageReload.html
new file mode 100644
index 0000000..7d03e52
--- /dev/null
+++ b/src/app/utilities/page-reload/PageReload.html
@@ -0,0 +1,21 @@
+<!--
+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)
+-->
+<button class="btn btn-sm btn-primary border border-radius-default" placement="top" container="body"
+ ngbTooltip="{{'REFRESH' | translate}}" (click)="reloadPage()">
+ <i class="fas fa-sync"></i>
+</button>
\ No newline at end of file
diff --git a/src/app/utilities/page-reload/PageReload.scss b/src/app/utilities/page-reload/PageReload.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/utilities/page-reload/PageReload.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/page-reload/PageReload.ts b/src/app/utilities/page-reload/PageReload.ts
new file mode 100644
index 0000000..0ea3c3c
--- /dev/null
+++ b/src/app/utilities/page-reload/PageReload.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 Page Reload Component
+ */
+import { Component, Injector } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { SharedService } from 'SharedService';
+/**
+ * Creating component
+ * @Component takes PageReload.html as template url
+ */
+@Component({
+ selector: 'page-reload',
+ templateUrl: './PageReload.html',
+ styleUrls: ['./PageReload.scss']
+})
+/** Exporting a class @exports PageReload */
+export class PageReload {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ // Empty Block
+ }
+
+ /** Handles select event @public */
+ public reloadPage(): void {
+ this.sharedService.dataEvent.emit();
+ }
+}
diff --git a/src/app/utilities/page-reload/PageReloadModule.ts b/src/app/utilities/page-reload/PageReloadModule.ts
new file mode 100644
index 0000000..56c35fd
--- /dev/null
+++ b/src/app/utilities/page-reload/PageReloadModule.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 Page Per Row Module.
+ */
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateModule } from '@ngx-translate/core';
+import { PageReload } from 'PageReload';
+
+/**
+ * Creating @NgModule component for Modules
+ */
+@NgModule({
+ imports: [CommonModule, TranslateModule, NgbModule],
+ declarations: [PageReload],
+ exports: [PageReload]
+})
+/** Exporting a class @exports PageReloadModule */
+export class PageReloadModule {
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ // Empty Block
+ }
+}
diff --git a/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.html b/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.html
new file mode 100644
index 0000000..351cf9a
--- /dev/null
+++ b/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.html
@@ -0,0 +1,25 @@
+<!--
+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)
+-->
+<div class="btn-group list action" role="group">
+ <button type="button" class="btn btn-primary" (click)="showInfo()" placement="top" container="body" ngbTooltip="{{'INFO' | translate}}">
+ <i class="fas fa-info icons"></i>
+ </button>
+ <button type="button" class="btn btn-primary" (click)="deletePDUInstance()" placement="top" container="body" ngbTooltip="{{'DELETE' | translate}}">
+ <i class="far fa-trash-alt icons"></i>
+ </button>
+</div>
\ No newline at end of file
diff --git a/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.scss b/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.ts b/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.ts
new file mode 100644
index 0000000..8cfb6cf
--- /dev/null
+++ b/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.ts
@@ -0,0 +1,88 @@
+/*
+ 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 PDUInstancesActionComponent Component
+ */
+import { Component, Injector } from '@angular/core';
+import { Router } from '@angular/router';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DeleteComponent } from 'DeleteComponent';
+import { PDUInstanceDetails } from 'PDUInstanceModel';
+import { SharedService } from 'SharedService';
+import { ShowInfoComponent } from 'ShowInfoComponent';
+
+/**
+ * Creating component
+ * @Component takes PDUInstancesActionComponent.html as template url
+ */
+@Component({
+ templateUrl: './PDUInstancesActionComponent.html',
+ styleUrls: ['./PDUInstancesActionComponent.scss']
+})
+/** Exporting a class @exports PDUInstancesActionComponent */
+export class PDUInstancesActionComponent {
+ /** To get the value from the PDU Instances via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: PDUInstanceDetails;
+
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Contains PDU Instance ID @private */
+ private pduInstanceID: string;
+
+ /** Service holds the router information @private */
+ private router: Router;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.modalService = this.injector.get(NgbModal);
+ this.router = this.injector.get(Router);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.pduInstanceID = this.value.identifier;
+ }
+
+ /** Delete PDU Instances @public */
+ public deletePDUInstance(): void {
+ const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, {backdrop: 'static'});
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /** Shows PDU Instances information using modalservice @public */
+ public showInfo(): void {
+ this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = {
+ id: this.pduInstanceID,
+ page: 'pdu-instances',
+ titleName: 'INSTANCEDETAILS'
+ };
+ }
+}
diff --git a/src/app/utilities/project-link/ProjectLinkComponent.html b/src/app/utilities/project-link/ProjectLinkComponent.html
new file mode 100644
index 0000000..6d29f0b
--- /dev/null
+++ b/src/app/utilities/project-link/ProjectLinkComponent.html
@@ -0,0 +1,25 @@
+<!--
+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)
+-->
+<a *ngIf="isPresent" class="link" [ngClass]="value.projectName === selectedProject ? 'activeProject' : ''"
+ href="javascript:void(0);"
+ (click)="value.projectName === selectedProject ? '' : this.projectService.switchProjectModal(value)" placement="right" container="body" ngbTooltip="{{ (value.projectName === selectedProject ? 'CURRENTPROJECT' : 'SWITCHPROJECT') | translate}}">
+ <span>{{value.projectName}}</span>
+ <i *ngIf="value.projectName === selectedProject" class="fas fa-check-circle text-success"></i>
+ <i *ngIf="value.projectName !== selectedProject" class="fas fa-exchange-alt text-danger"></i>
+</a>
+<span *ngIf="!isPresent">{{value.projectName}}</span>
\ No newline at end of file
diff --git a/src/app/utilities/project-link/ProjectLinkComponent.scss b/src/app/utilities/project-link/ProjectLinkComponent.scss
new file mode 100644
index 0000000..152cf8d
--- /dev/null
+++ b/src/app/utilities/project-link/ProjectLinkComponent.scss
@@ -0,0 +1,28 @@
+/*
+ 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 '../../../assets/scss/mixins/mixin';
+@import '../../../assets/scss/variable';
+.link{
+ text-decoration: none !important;
+ &.activeProject{
+ border-radius: 3px;
+ @include padding-value(2, 2, 2, 2);
+ text-decoration: none !important;
+ cursor: default;
+ }
+}
\ No newline at end of file
diff --git a/src/app/utilities/project-link/ProjectLinkComponent.ts b/src/app/utilities/project-link/ProjectLinkComponent.ts
new file mode 100644
index 0000000..35c5b2c
--- /dev/null
+++ b/src/app/utilities/project-link/ProjectLinkComponent.ts
@@ -0,0 +1,81 @@
+/*
+ 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 Link Component.
+ */
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnInit } from '@angular/core';
+import { environment } from 'environment';
+import { ProjectData } from 'ProjectModel';
+import { ProjectService } from 'ProjectService';
+import { RestService } from 'RestService';
+import { UserDetail } from 'UserModel';
+/**
+ * Creating component
+ * @Component takes ProjectLinkComponent.html as template url
+ */
+@Component({
+ selector: 'app-project-link',
+ templateUrl: './ProjectLinkComponent.html',
+ styleUrls: ['./ProjectLinkComponent.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+/** Exporting a class @exports ProjectLinkComponent */
+export class ProjectLinkComponent implements OnInit {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+ /** Variables holds the selected project @public */
+ public selectedProject: string;
+ /** To get the value from the nspackage via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: ProjectData;
+ /** Variables holds all the projects @public */
+ public projectList: {}[] = [];
+ /** Check the project is present for the user @public */
+ public isPresent: boolean = false;
+ /** Set timeout @private */
+ private timeOut: number = 10;
+ /** Instance of the rest service @private */
+ private restService: RestService;
+ /** Holds all project details @private */
+ private projectService: ProjectService;
+ /** Detect changes for the User Input */
+ private cd: ChangeDetectorRef;
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.projectService = this.injector.get(ProjectService);
+ this.restService = this.injector.get(RestService);
+ this.cd = this.injector.get(ChangeDetectorRef);
+ }
+
+ public ngOnInit(): void {
+ this.selectedProject = localStorage.getItem('project');
+ this.getAdminProjects();
+ }
+
+ /** Get the admin projects to be selectable @public */
+ public getAdminProjects(): void {
+ const username: string = localStorage.getItem('username');
+ this.restService.getResource(environment.USERS_URL + '/' + username).subscribe((projects: UserDetail) => {
+ this.projectList = projects.project_role_mappings;
+ this.isPresent = this.projectList.some((item: ProjectData) => item.project === this.value.project);
+ setTimeout(() => {
+ this.cd.detectChanges();
+ }, this.timeOut);
+ });
+ }
+
+}
diff --git a/src/app/utilities/projects-action/ProjectsActionComponent.html b/src/app/utilities/projects-action/ProjectsActionComponent.html
new file mode 100644
index 0000000..a5d4d4e
--- /dev/null
+++ b/src/app/utilities/projects-action/ProjectsActionComponent.html
@@ -0,0 +1,34 @@
+<!--
+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)
+-->
+<div class="btn-group list action" role="group">
+ <div ngbDropdown class="btn-group">
+ <button type="button" class="btn btn-primary dropdown-toggle action-button" ngbDropdownToggle>
+ {{'ACTION' | translate}}
+ </button>
+ <div class="dropdown-menu" ngbDropdownMenu>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="projectEdit()" placement="left"
+ container="body" ngbTooltip="{{'RENAME' | translate}}">
+ <i class="fa fa-edit icons"></i> {{'RENAME' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="projectDelete()" placement="left"
+ container="body" ngbTooltip="{{'DELETE' | translate}}">
+ <i class="fas fa-trash-alt icons"></i> {{'DELETE' | translate}}
+ </button>
+ </div>
+ </div>
+</div>
diff --git a/src/app/utilities/projects-action/ProjectsActionComponent.scss b/src/app/utilities/projects-action/ProjectsActionComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/utilities/projects-action/ProjectsActionComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/projects-action/ProjectsActionComponent.ts b/src/app/utilities/projects-action/ProjectsActionComponent.ts
new file mode 100644
index 0000000..4ac0051
--- /dev/null
+++ b/src/app/utilities/projects-action/ProjectsActionComponent.ts
@@ -0,0 +1,78 @@
+/*
+ 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 Projects Action Component
+ */
+import { Component, Injector } from '@angular/core';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DeleteComponent } from 'DeleteComponent';
+import { ProjectCreateUpdateComponent } from 'ProjectCreateUpdate';
+import { ProjectData } from 'ProjectModel';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes ProjectsActionComponent.html as template url
+ */
+@Component({
+ selector: 'app-projects-action',
+ templateUrl: './ProjectsActionComponent.html',
+ styleUrls: ['./ProjectsActionComponent.scss']
+})
+/** Exporting a class @exports ProjectsActionComponent */
+export class ProjectsActionComponent {
+ /** To get the value from the nspackage via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: ProjectData;
+
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.modalService = this.injector.get(NgbModal);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /** Delete project @public */
+ public projectDelete(): void {
+ const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' });
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /** Edit project @public */
+ public projectEdit(): void {
+ const modalRef: NgbModalRef = this.modalService.open(ProjectCreateUpdateComponent, { backdrop: 'static' });
+ modalRef.componentInstance.projectType = 'Edit';
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+}
diff --git a/src/app/utilities/roles-action/RolesActionComponent.html b/src/app/utilities/roles-action/RolesActionComponent.html
new file mode 100644
index 0000000..5a7453d
--- /dev/null
+++ b/src/app/utilities/roles-action/RolesActionComponent.html
@@ -0,0 +1,34 @@
+<!--
+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)
+-->
+<div class="btn-group list action" role="group">
+ <div ngbDropdown class="btn-group">
+ <button type="button" class="btn btn-primary dropdown-toggle action-button" ngbDropdownToggle>
+ {{'ACTION' | translate}}
+ </button>
+ <div class="dropdown-menu" ngbDropdownMenu>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="editRole()" placement="left"
+ container="body" ngbTooltip="{{'EDIT' | translate}}">
+ <i class="fa fa-edit icons"></i> {{'EDIT' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="deleteRole()" placement="left"
+ container="body" ngbTooltip="{{'DELETE' | translate}}">
+ <i class="fas fa-trash-alt icons"></i> {{'DELETE' | translate}}
+ </button>
+ </div>
+ </div>
+</div>
diff --git a/src/app/utilities/roles-action/RolesActionComponent.scss b/src/app/utilities/roles-action/RolesActionComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/utilities/roles-action/RolesActionComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/roles-action/RolesActionComponent.ts b/src/app/utilities/roles-action/RolesActionComponent.ts
new file mode 100644
index 0000000..5b4d85b
--- /dev/null
+++ b/src/app/utilities/roles-action/RolesActionComponent.ts
@@ -0,0 +1,79 @@
+/*
+ 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 Roles Action Component
+ */
+import { Component, Injector } from '@angular/core';
+import { Router } from '@angular/router';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DeleteComponent } from 'DeleteComponent';
+import { RoleData } from 'RolesModel';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes RolesActionComponent.html as template url
+ */
+@Component({
+ selector: 'app-roles-action',
+ templateUrl: './RolesActionComponent.html',
+ styleUrls: ['./RolesActionComponent.scss']
+})
+/** Exporting a class @exports RolesActionComponent */
+export class RolesActionComponent {
+ /** To get the role data via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: RoleData;
+
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Holds the instance of roter service @private */
+ private router: Router;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.modalService = this.injector.get(NgbModal);
+ this.sharedService = this.injector.get(SharedService);
+ this.router = this.injector.get(Router);
+ }
+
+ /** Delete Role click handler @public */
+ public deleteRole(): void {
+ const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' });
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /** Edit Role click handler @public */
+ public editRole(): void {
+ this.router.navigate(['/roles/edit', this.value.identifier]).catch(() => {
+ // Catch Navigation Error
+ });
+ }
+
+}
diff --git a/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.html b/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.html
new file mode 100644
index 0000000..454527e
--- /dev/null
+++ b/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.html
@@ -0,0 +1,27 @@
+<!--
+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)
+-->
+<div class="btn-group list action" role="group">
+ <button type="button" class="btn btn-primary" (click)="showSDNControllerInfo()" placement="top" container="body"
+ ngbTooltip="{{'INFO' | translate}}">
+ <i class="fas fa-info icons"></i>
+ </button>
+ <button type="button" class="btn btn-primary" (click)="deleteSDNController()" placement="top" container="body"
+ ngbTooltip="{{'DELETE' | translate}}">
+ <i class="far fa-trash-alt icons"></i>
+ </button>
+</div>
\ No newline at end of file
diff --git a/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.scss b/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.ts b/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.ts
new file mode 100644
index 0000000..cbf3fc1
--- /dev/null
+++ b/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.ts
@@ -0,0 +1,83 @@
+/*
+ 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 SDN Controller Action Component
+ */
+import { Component, Injector } from '@angular/core';
+import { Router } from '@angular/router';
+import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DeleteComponent } from 'DeleteComponent';
+import { SDNControllerInfoComponent } from 'SDNControllerInfoComponent';
+import { SDNControllerList } from 'SDNControllerModel';
+import { SharedService } from 'SharedService';
+
+/**
+ * Creating component
+ * @Component takes SDNControllerActionComponent.html as template url
+ */
+@Component({
+ templateUrl: './SDNControllerActionComponent.html',
+ styleUrls: ['./SDNControllerActionComponent.scss']
+})
+/** Exporting a class @exports SDNControllerActionComponent */
+export class SDNControllerActionComponent {
+ /** To get the value from the vnfpackage via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: SDNControllerList;
+
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Variables holds SDN ID @private */
+ private sdnID: string;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.modalService = this.injector.get(NgbModal);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.sdnID = this.value.identifier;
+ }
+
+ /** Show SDN Controller Information @public */
+ public showSDNControllerInfo(): void {
+ this.modalService.open(SDNControllerInfoComponent, { backdrop: 'static' }).componentInstance.params = {
+ id: this.sdnID,
+ page: 'sdn-controller'
+ };
+ }
+
+ /** Delete SDN Controller @public */
+ public deleteSDNController(): void {
+ const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' });
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+}
diff --git a/src/app/utilities/show-info/ShowInfoComponent.html b/src/app/utilities/show-info/ShowInfoComponent.html
new file mode 100644
index 0000000..7fff58d
--- /dev/null
+++ b/src/app/utilities/show-info/ShowInfoComponent.html
@@ -0,0 +1,35 @@
+<!--
+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)
+-->
+<div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{titleName | translate}}</h4>
+ <button class="button-xs" type="button" class="close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+</div>
+<div class="modal-body">
+ <div class="ngx-codemirror mb-2" *ngIf="defaults[mode] else noData">
+ <ngx-codemirror [options]="options" [ngModel]="defaults[mode]" [disabled]="readOnly" [autoFocus]="true">
+ </ngx-codemirror>
+ </div>
+ <ng-template #noData>{{'NODATAERROR' | translate}}
+ </ng-template>
+</div>
+<div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/utilities/show-info/ShowInfoComponent.scss b/src/app/utilities/show-info/ShowInfoComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/utilities/show-info/ShowInfoComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/show-info/ShowInfoComponent.ts b/src/app/utilities/show-info/ShowInfoComponent.ts
new file mode 100644
index 0000000..0fb22e7
--- /dev/null
+++ b/src/app/utilities/show-info/ShowInfoComponent.ts
@@ -0,0 +1,216 @@
+/*
+ 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 Info Ns Model
+ */
+import { HttpClient } from '@angular/common/http';
+import { Component, Injector, Input, OnInit } from '@angular/core';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import 'codemirror/addon/dialog/dialog';
+import 'codemirror/addon/display/autorefresh';
+import 'codemirror/addon/display/fullscreen';
+import 'codemirror/addon/edit/closebrackets';
+import 'codemirror/addon/edit/matchbrackets';
+import 'codemirror/addon/fold/brace-fold';
+import 'codemirror/addon/fold/foldcode';
+import 'codemirror/addon/fold/foldgutter';
+import 'codemirror/addon/search/search';
+import 'codemirror/addon/search/searchcursor';
+import 'codemirror/keymap/sublime';
+import 'codemirror/lib/codemirror';
+import 'codemirror/mode/javascript/javascript';
+import 'codemirror/mode/markdown/markdown';
+import 'codemirror/mode/yaml/yaml';
+import { ERRORDATA, URLPARAMS } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { NSDDetails } from 'NSDModel';
+import { RestService } from 'RestService';
+/** Set defaults json as type in information modal @constant */
+const defaults: {} = {
+ 'text/json': ''
+};
+/**
+ * Creating component
+ * @Component takes ShowInfoComponent.html as template url
+ */
+@Component({
+ templateUrl: './ShowInfoComponent.html',
+ styleUrls: ['./ShowInfoComponent.scss']
+})
+/** Exporting a class @exports ShowInfoComponent */
+export class ShowInfoComponent implements OnInit {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** dataService to pass the data from one component to another @public */
+ public dataService: DataService;
+
+ /** Default variables holds NS data @public */
+ public defaults: {} = defaults;
+
+ /** Varaibles to hold http client @public */
+ public httpClient: HttpClient;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** variables readOnly holds boolean @public */
+ public readOnly: boolean = true;
+
+ /** variables to hold mode changes of editor @public */
+ public mode: string = 'text/json';
+
+ /** To Set Mode @public */
+ public modeDefault: string = 'javascript';
+
+ /** variables to hold options of editor @public */
+ public options: {} = {
+ mode: this.modeDefault,
+ showCursorWhenSelecting: true,
+ autofocus: true,
+ lineNumbers: true,
+ lineWrapping: true,
+ foldGutter: true,
+ gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
+ autoCloseBrackets: true,
+ matchBrackets: true,
+ theme: 'neat',
+ keyMap: 'sublime'
+ };
+
+ /** Reading the page Name @public */
+ public titleName: string;
+
+ /** Check the loading results @public */
+ public isLoadingResults: Boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Input contains component objects @private */
+ @Input() private params: URLPARAMS;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.dataService = this.injector.get(DataService);
+ this.restService = this.injector.get(RestService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.isLoadingResults = true;
+ this.defaults['text/json'] = '';
+ this.titleName = this.params.titleName;
+ // Checks page and assign URL
+ if (this.params.page === 'ns-instance') {
+ this.restService.getResource(environment.NSINSTANCESCONTENT_URL + '/' + this.params.id).subscribe((nsData: NSDDetails[]) => {
+ this.defaults['text/json'] = JSON.stringify(nsData, null, '\t');
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'get');
+ }, () => {
+ this.isLoadingResults = false;
+ });
+ } else if (this.params.page === 'ns-history-operation') {
+ this.restService.getResource(environment.NSHISTORYOPERATIONS_URL + '/' +
+ this.params.id).subscribe((nsHistoryOpn: {}[]) => {
+ this.defaults['text/json'] = JSON.stringify(nsHistoryOpn, null, '\t');
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'get');
+ }, () => {
+ this.isLoadingResults = false;
+ });
+ } else if (this.params.page === 'vnf-instance') {
+ this.restService.getResource(environment.VNFINSTANCES_URL + '/' + this.params.id).subscribe((vnfData: {}[]) => {
+ this.defaults['text/json'] = JSON.stringify(vnfData, null, '\t');
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'get');
+ }, () => {
+ this.isLoadingResults = false;
+ });
+ } else if (this.params.page === 'net-slice-package') {
+ this.restService.getResource(environment.NETWORKSLICETEMPLATECONTENT_URL + '/' + this.params.id).subscribe((netSliceData: {}[]) => {
+ this.defaults['text/json'] = JSON.stringify(netSliceData, null, '\t');
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'get');
+ }, () => {
+ this.isLoadingResults = false;
+ });
+ } else if (this.params.page === 'net-slice-instance') {
+ this.restService.getResource(environment.NETWORKSLICEINSTANCESCONTENT_URL + '/' + this.params.id)
+ .subscribe((netSliceInstanceData: {}[]) => {
+ this.defaults['text/json'] = JSON.stringify(netSliceInstanceData, null, '\t');
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'get');
+ }, () => {
+ this.isLoadingResults = false;
+ });
+ } else if (this.params.page === 'nst-history-operation') {
+ this.restService.getResource(environment.NSTHISTORYOPERATIONS_URL + '/' +
+ this.params.id).subscribe((nstHistoryOpn: {}[]) => {
+ this.defaults['text/json'] = JSON.stringify(nstHistoryOpn, null, '\t');
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'get');
+ }, () => {
+ this.isLoadingResults = false;
+ });
+ } else if (this.params.page === 'pdu-instances') {
+ this.restService.getResource(environment.PDUINSTANCE_URL + '/' +
+ this.params.id).subscribe((pduInstanceOpn: {}[]) => {
+ this.defaults['text/json'] = JSON.stringify(pduInstanceOpn, null, '\t');
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'get');
+ }, () => {
+ this.isLoadingResults = false;
+ });
+ } else if (this.params.page === 'k8s-cluster') {
+ this.restService.getResource(environment.K8SCLUSTER_URL + '/' +
+ this.params.id).subscribe((k8sclusterOpn: {}[]) => {
+ this.defaults['text/json'] = JSON.stringify(k8sclusterOpn, null, '\t');
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'get');
+ }, () => {
+ this.isLoadingResults = false;
+ });
+ } else if (this.params.page === 'k8s-repo') {
+ this.restService.getResource(environment.K8REPOS_URL + '/' +
+ this.params.id).subscribe((k8srepoOpn: {}[]) => {
+ this.defaults['text/json'] = JSON.stringify(k8srepoOpn, null, '\t');
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'get');
+ }, () => {
+ this.isLoadingResults = false;
+ });
+ }
+ }
+}
diff --git a/src/app/utilities/switch-project/SwitchProjectComponent.html b/src/app/utilities/switch-project/SwitchProjectComponent.html
new file mode 100644
index 0000000..948591a
--- /dev/null
+++ b/src/app/utilities/switch-project/SwitchProjectComponent.html
@@ -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)
+-->
+<form [formGroup]="switchProjectForm" (ngSubmit)="switchProject()" autocomplete="off">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'SWITCHPROJECT' | translate}}</h4>
+ <button class="button-xs" type="button" class="close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </div>
+ <div class="modal-body">
+ <div class="form-group row">
+ <label class="col-sm-12 col-form-label mandatory-label" [ngClass]="{'text-danger': switchProjectForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <label class="col-sm-6 col-form-label">{{'ENTER' | translate}} {{'PASSWORD' | translate}}*</label>
+ <div class="col-sm-6">
+ <input autocomplete="off" type="password" class="form-control" placeholder="{{'ENTER' | translate}} {{'PASSWORD' | translate}}" formControlName="password"
+ id="password" [ngClass]="{ 'is-invalid': submitted && f.password.errors }" required>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'SUBMIT' | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/utilities/switch-project/SwitchProjectComponent.scss b/src/app/utilities/switch-project/SwitchProjectComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/utilities/switch-project/SwitchProjectComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/switch-project/SwitchProjectComponent.ts b/src/app/utilities/switch-project/SwitchProjectComponent.ts
new file mode 100644
index 0000000..1df6a16
--- /dev/null
+++ b/src/app/utilities/switch-project/SwitchProjectComponent.ts
@@ -0,0 +1,139 @@
+/*
+ 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 Switch Project Component
+ */
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { Component, Injector, Input, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { APIURLHEADER, ERRORDATA, LOCALSTORAGE, URLPARAMS } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { RestService } from 'RestService';
+
+/**
+ * Creating component
+ * @Component takes SwitchProjectComponent.html as template url
+ */
+@Component({
+ templateUrl: './SwitchProjectComponent.html',
+ styleUrls: ['./SwitchProjectComponent.scss']
+})
+/** Exporting a class @exports SwitchProjectComponent */
+export class SwitchProjectComponent implements OnInit {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** dataService to pass the data from one component to another @public */
+ public dataService: DataService;
+
+ /** Varaibles to hold http client @public */
+ public httpClient: HttpClient;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** FormGroup instance added to the form @ html @public */
+ public switchProjectForm: FormGroup;
+
+ /** Form submission Add */
+ public submitted: boolean = false;
+
+ /** Check the Projects loading results @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Input contains component objects @private */
+ @Input() private params: URLPARAMS;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.dataService = this.injector.get(DataService);
+ this.restService = this.injector.get(RestService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.formBuilder = this.injector.get(FormBuilder);
+ }
+
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.switchProjectForm.controls; }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.initializeForm();
+ }
+
+ /** initialize Forms @public */
+ public initializeForm(): void {
+ this.switchProjectForm = this.formBuilder.group({
+ password: ['', [Validators.required]]
+ });
+ }
+
+ /** Switch project @public */
+ public switchProject(): void {
+ this.submitted = true;
+ if (!this.switchProjectForm.invalid) {
+ this.isLoadingResults = true;
+ this.headers = new HttpHeaders({
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ const payLoad: {} = JSON.stringify({
+ username: this.params.username,
+ password: this.switchProjectForm.value.password,
+ project_id: this.params.projectID
+ });
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.GENERATETOKEN_URL,
+ httpOptions: { headers: this.headers }
+ };
+ this.restService.postResource(apiURLHeader, payLoad).subscribe((data: LOCALSTORAGE) => {
+ if (data) {
+ localStorage.setItem('id_token', data.id);
+ localStorage.setItem('project_id', this.params.projectID);
+ localStorage.setItem('expires', data.expires.toString());
+ localStorage.setItem('username', data.username);
+ localStorage.setItem('project', data.project_name);
+ localStorage.setItem('token_state', data.id);
+ this.activeModal.close();
+ location.reload();
+ this.isLoadingResults = false;
+ }
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ this.restService.handleError(error, 'post');
+ this.activeModal.close();
+ });
+ }
+ }
+}
diff --git a/src/app/utilities/users-action/UsersActionComponent.html b/src/app/utilities/users-action/UsersActionComponent.html
new file mode 100644
index 0000000..9ce546e
--- /dev/null
+++ b/src/app/utilities/users-action/UsersActionComponent.html
@@ -0,0 +1,42 @@
+<!--
+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)
+-->
+<div class="btn-group list action" role="group">
+ <div ngbDropdown class="btn-group">
+ <button type="button" class="btn btn-primary dropdown-toggle action-button" ngbDropdownToggle>
+ {{'ACTION' | translate}}
+ </button>
+ <div class="dropdown-menu" ngbDropdownMenu>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="editUserModal('editPassword')" placement="left" container="body"
+ ngbTooltip="{{'PAGE.USERS.EDITCREDENTIALS' | translate}}">
+ <i class="fa fa-edit icons"></i> {{'PAGE.USERS.EDITCREDENTIALS' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="editUserModal('editUserName')" placement="left" container="body"
+ ngbTooltip="{{'PAGE.USERS.EDITUSERNAME' | translate}}">
+ <i class="fas fa-user-edit"></i> {{'PAGE.USERS.EDITUSERNAME' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="projectRolesModal()" placement="left" container="body"
+ ngbTooltip="{{'PAGE.USERS.PROJECTSROLES' | translate}}">
+ <i class="fas fa-user-check"></i> {{'PAGE.USERS.PROJECTSROLES' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="deleteUser()" placement="left" container="body"
+ ngbTooltip="{{'DELETE' | translate}}">
+ <i class="far fa-trash-alt icons" title="delete"></i> {{'DELETE' | translate}}
+ </button>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/src/app/utilities/users-action/UsersActionComponent.scss b/src/app/utilities/users-action/UsersActionComponent.scss
new file mode 100644
index 0000000..fdec4ed
--- /dev/null
+++ b/src/app/utilities/users-action/UsersActionComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+ */
diff --git a/src/app/utilities/users-action/UsersActionComponent.ts b/src/app/utilities/users-action/UsersActionComponent.ts
new file mode 100644
index 0000000..1d8e895
--- /dev/null
+++ b/src/app/utilities/users-action/UsersActionComponent.ts
@@ -0,0 +1,101 @@
+/*
+ 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 Users Action Component
+ */
+import { Component, Injector } from '@angular/core';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { AddEditUserComponent } from 'AddEditUserComponent';
+import { MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DeleteComponent } from 'DeleteComponent';
+import { ProjectRoleComponent } from 'ProjectRoleComponent';
+import { SharedService } from 'SharedService';
+import { UserData } from 'UserModel';
+/**
+ * Creating component
+ * @Component takes UsersActionComponent.html as template url
+ */
+@Component({
+ templateUrl: './UsersActionComponent.html',
+ styleUrls: ['./UsersActionComponent.scss']
+})
+/** Exporting a class @exports UsersActionComponent */
+export class UsersActionComponent {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** To get the value from the Users action via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: UserData;
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.modalService = this.injector.get(NgbModal);
+ this.sharedService = this.injector.get(SharedService);
+ this.translateService = this.injector.get(TranslateService);
+ }
+
+ /** Delete User Account @public */
+ public deleteUser(): void {
+ const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' });
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /** Edit User Account @public */
+ public editUserModal(editType: string): void {
+ const modalRef: NgbModalRef = this.modalService.open(AddEditUserComponent, { backdrop: 'static' });
+ modalRef.componentInstance.userID = this.value.identifier;
+ if (editType === 'editPassword') {
+ modalRef.componentInstance.userTitle = this.translateService.instant('PAGE.USERS.EDITCREDENTIALS');
+ } else {
+ modalRef.componentInstance.userTitle = this.translateService.instant('PAGE.USERS.EDITUSERNAME');
+ }
+ modalRef.componentInstance.userType = editType;
+ modalRef.componentInstance.userName = this.value.username;
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /** Edit User Account @public */
+ public projectRolesModal(): void {
+ const modalRef: NgbModalRef = this.modalService.open(ProjectRoleComponent, { backdrop: 'static' });
+ modalRef.componentInstance.userID = this.value.identifier;
+ modalRef.componentInstance.userTitle = this.translateService.instant('PAGE.USERS.EDITPROJECTROLEMAPPING');
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+}
diff --git a/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.html b/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.html
new file mode 100644
index 0000000..7491d59
--- /dev/null
+++ b/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.html
@@ -0,0 +1,46 @@
+<!--
+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)
+-->
+<div class="btn-group list action" role="group">
+ <ng-template #popTitle>
+ <span class="text-primary"><strong>{{'PAGE.DASHBOARD.RUNNINGINSTANCES' | translate}}</strong></span>
+ <button class="button-xs close" type="button" (click)="p.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </ng-template>
+ <ng-template #popContent>
+ <ul class="list-group">
+ <li class="list-group-item text-left p-1 border-0" *ngFor="let instanceDetails of showInstanceDetails">
+ <a class="d-block text-truncate" target="_parent"
+ routerLink="/instances/ns/{{instanceDetails._id}}"><i class="fa-sitemap fas icons"></i> {{instanceDetails.name}}</a>
+ </li>
+ </ul>
+ </ng-template>
+ <button type="button" class="btn btn-primary" (click)="vimInfo()" placement="top" container="body"
+ ngbTooltip="{{'INFO' | translate}}">
+ <i class="fas fa-info icons"></i>
+ </button>
+ <button type="button" class="btn btn-primary" (click)="deleteVIMAccount()" placement="top" container="body"
+ ngbTooltip="{{'DELETE' | translate}}">
+ <i class="far fa-trash-alt icons"></i>
+ </button>
+ <button type="button" placement="left" container="body" [ngbPopover]="popContent" triggers="manual" #p="ngbPopover"
+ (click)="p.open()" [autoClose]="'outside'" [popoverTitle]="popTitle" class="btn btn-primary"
+ popoverClass="runninginstances" [disabled]="!showMapIcon">
+ <i class="fas fa-layer-group"></i>
+ </button>
+</div>
\ No newline at end of file
diff --git a/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.scss b/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.ts b/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.ts
new file mode 100644
index 0000000..57f1f64
--- /dev/null
+++ b/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.ts
@@ -0,0 +1,104 @@
+/*
+ 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 Vim AccountsAction Component
+ */
+import { Component, Injector, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DeleteComponent } from 'DeleteComponent';
+import { NSInstanceDetails } from 'NSInstanceModel';
+import { SharedService } from 'SharedService';
+import { VIMData } from 'VimAccountModel';
+
+/**
+ * Creating component
+ * @Component takes VimAccountsActionComponent.html as template url
+ */
+@Component({
+ selector: 'app-vim-accounts-action',
+ templateUrl: './VimAccountsActionComponent.html',
+ styleUrls: ['./VimAccountsActionComponent.scss']
+})
+/** Exporting a class @exports VimAccountsActionComponent */
+export class VimAccountsActionComponent implements OnInit {
+ /** To get the value from the vimAccounts via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: VIMData;
+
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** To show Instances running @public */
+ public showMapIcon: boolean = false;
+
+ /** To show Details Instances running @public */
+ public showInstanceDetails: {}[];
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ /** Variables holds NS ID @private */
+ private vimID: string;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.modalService = this.injector.get(NgbModal);
+ this.router = this.injector.get(Router);
+ this.sharedService = this.injector.get(SharedService);
+ }
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.getInstancesDetails();
+ }
+
+ /** Delete VIM Account @public */
+ public deleteVIMAccount(): void {
+ const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, {backdrop: 'static'});
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /** On navigate to Info VimAccount @public */
+ public vimInfo(): void {
+ this.vimID = this.value.identifier;
+ this.router.navigate(['/vim/info', this.vimID]).catch(() => {
+ // Catch Navigation Error
+ });
+ }
+
+ /** To show the Instances Info for the particular VimAccount @public */
+ public getInstancesDetails(): void {
+ this.showInstanceDetails = [];
+ this.value.instancesData.filter((item: NSInstanceDetails) => {
+ if (item.datacenter === this.value.identifier) {
+ this.showMapIcon = true;
+ this.showInstanceDetails.push(item);
+ }
+ });
+ }
+}
diff --git a/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.html b/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.html
new file mode 100644
index 0000000..333beea
--- /dev/null
+++ b/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.html
@@ -0,0 +1,23 @@
+<!--
+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)
+-->
+<div class="btn-group list action" role="group">
+ <button type="button" (click)="infoVNF()" class="btn btn-primary" placement="top" container="body"
+ ngbTooltip="{{'VNFR' | translate}}">
+ <i class="fas fa-info icons"></i>
+ </button>
+</div>
\ No newline at end of file
diff --git a/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.scss b/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.scss
new file mode 100644
index 0000000..8c2b739
--- /dev/null
+++ b/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
diff --git a/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.ts b/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.ts
new file mode 100644
index 0000000..d3a95f2
--- /dev/null
+++ b/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.ts
@@ -0,0 +1,67 @@
+/*
+ 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 VNF-instancesAction Component
+ */
+import { Component, Injector } from '@angular/core';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { ShowInfoComponent } from 'ShowInfoComponent';
+import { VNFInstanceData } from 'VNFInstanceModel';
+/**
+ * Creating component
+ * @Component takes VNFInstancesActionComponent.html as template url
+ */
+@Component({
+ templateUrl: './VNFInstancesActionComponent.html',
+ styleUrls: ['./VNFInstancesActionComponent.scss']
+})
+/** Exporting a class @exports VNFInstancesActionComponent */
+export class VNFInstancesActionComponent {
+ /** To get the value from the vnfpackage via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: VNFInstanceData;
+
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Contains instance ID @private */
+ private instanceID: string;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.modalService = this.injector.get(NgbModal);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.instanceID = this.value.identifier;
+ }
+
+ /** Shows information using modalservice @public */
+ public infoVNF(): void {
+ this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = {
+ id: this.instanceID,
+ page: 'vnf-instance',
+ titleName: 'INSTANCEDETAILS'
+ };
+ }
+}
diff --git a/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.html b/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.html
new file mode 100644
index 0000000..df3aa52
--- /dev/null
+++ b/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.html
@@ -0,0 +1,51 @@
+<!--
+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)
+-->
+<div class="btn-group list action" role="group">
+ <button type="button" class="btn btn-primary" placement="top" container="body" (click)="composeVNFPackages()"
+ ngbTooltip="{{'TOPOLOGY' | translate}}">
+ <i class="fa fa-sitemap fa-fw Icon"></i>
+ </button>
+ <button type="button" class="btn btn-primary" (click)="deleteVNFPackage()" placement="top" container="body"
+ ngbTooltip="{{'DELETE' | translate}}">
+ <i class="far fa-trash-alt icons"></i>
+ </button>
+ <div ngbDropdown class="btn-group">
+ <button type="button" class="btn btn-primary dropdown-toggle action-button" ngbDropdownToggle>
+ {{'ACTION' | translate}}
+ </button>
+ <div class="dropdown-menu" ngbDropdownMenu>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="vnfdEdit()" placement="left"
+ container="body" ngbTooltip="{{'EDIT' | translate}}">
+ <i class="fa fa-edit icons"></i> {{'EDIT' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="downloadVNFPackage()" placement="left"
+ container="body" ngbTooltip="{{'DOWNLOAD' | translate}}">
+ <i class="fas fa-download icons"></i> {{'DOWNLOAD' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="cloneVNFPackage()" placement="left"
+ container="body" ngbTooltip="{{'CLONE' | translate}}">
+ <i class="fa fa-clone icons"></i> {{'CLONE' | translate}}
+ </button>
+ <button type="button" class="btn btn-primary dropdown-item" (click)="showContent()" placement="left"
+ container="body" ngbTooltip="{{'CONTENT' | translate}}">
+ <i class="far fa-folder-open icons"></i> {{'CONTENT' | translate}}
+ </button>
+ </div>
+ </div>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingDownloadResult"></app-loader>
\ No newline at end of file
diff --git a/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.scss b/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.ts b/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.ts
new file mode 100644
index 0000000..f9d1955
--- /dev/null
+++ b/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.ts
@@ -0,0 +1,187 @@
+/*
+ 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 VNF-packagesAction Component
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { ChangeDetectorRef, Component, Injector } from '@angular/core';
+import { Router } from '@angular/router';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { ClonePackageComponent } from 'ClonePackage';
+import { ERRORDATA, GETAPIURLHEADER, MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DeleteComponent } from 'DeleteComponent';
+import { environment } from 'environment';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { ShowContentComponent } from 'ShowContent';
+import { VNFData } from 'VNFDModel';
+
+/**
+ * Creating component
+ * @Component takes VNFPackagesActionComponent.html as template url
+ */
+@Component({
+ templateUrl: './VNFPackagesActionComponent.html',
+ styleUrls: ['./VNFPackagesActionComponent.scss']
+})
+/** Exporting a class @exports VNFPackagesActionComponent */
+export class VNFPackagesActionComponent {
+ /** To get the value from the vnfpackage via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: VNFData;
+
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Check the loading results for loader status @public */
+ public isLoadingDownloadResult: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Variables holds NS ID @private */
+ private vnfID: string;
+
+ /** Variables holds NS name @private */
+ private vnfName: string;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Detect changes for the User Input */
+ private cd: ChangeDetectorRef;
+
+ /** Set timeout @private */
+ private timeOut: number = 1000;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.sharedService = this.injector.get(SharedService);
+ this.modalService = this.injector.get(NgbModal);
+ this.router = this.injector.get(Router);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.cd = this.injector.get(ChangeDetectorRef);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.headers = new HttpHeaders({
+ Accept: 'application/zip, application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ this.vnfID = this.value.identifier;
+ this.vnfName = this.value.shortName;
+ }
+
+ /** Delete VNF packages @public */
+ public deleteVNFPackage(): void {
+ const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' });
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /** Set instance for NSD Edit @public */
+ public vnfdEdit(): void {
+ this.router.navigate(['/packages/vnf/edit/', this.vnfID]).then((nav: {}) => {
+ // Navigated Successfully
+ }, (error: Error) => {
+ // Navigation Error Handler
+ });
+ }
+
+ /** list out all the file content of a descriptors @public */
+ public showContent(): void {
+ this.modalService.open(ShowContentComponent, { backdrop: 'static' }).componentInstance.params = { id: this.vnfID, page: 'vnfd' };
+ }
+
+ /** Download VNF Package @public */
+ public downloadVNFPackage(): void {
+ this.isLoadingDownloadResult = true;
+ const httpOptions: GETAPIURLHEADER = {
+ headers: this.headers,
+ responseType: 'blob'
+ };
+ this.restService.getResource(environment.VNFPACKAGES_URL + '/' + this.vnfID + '/package_content', httpOptions)
+ .subscribe((response: Blob) => {
+ this.isLoadingDownloadResult = false;
+ this.changeDetactionforDownload();
+ const binaryData: Blob[] = [];
+ binaryData.push(response);
+ this.sharedService.downloadFiles(this.vnfName, binaryData, response.type);
+ }, (error: ERRORDATA) => {
+ this.isLoadingDownloadResult = false;
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ this.changeDetactionforDownload();
+ if (typeof error.error === 'object') {
+ error.error.text().then((data: string): void => {
+ error.error = JSON.parse(data);
+ this.restService.handleError(error, 'getBlob');
+ });
+ }
+ });
+ }
+
+ /** Compose VNF Packages @public */
+ public composeVNFPackages(): void {
+ this.router.navigate(['/packages/vnf/compose/', this.vnfID]).catch();
+ }
+
+ /** Change the detaction @public */
+ public changeDetactionforDownload(): void {
+ setTimeout(() => {
+ this.cd.detectChanges();
+ }, this.timeOut);
+ }
+
+ /** Clone NS Packages @public */
+ public cloneVNFPackage(): void {
+ const cloneModal: NgbModalRef = this.modalService.open(ClonePackageComponent, { backdrop: 'static' });
+ cloneModal.componentInstance.params = { id: this.vnfID, page: 'vnfd', name: this.vnfName };
+ cloneModal.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+}
diff --git a/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.html b/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.html
new file mode 100644
index 0000000..902bb42
--- /dev/null
+++ b/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.html
@@ -0,0 +1,27 @@
+<!--
+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)
+-->
+<div class="btn-group list action" role="group">
+ <button type="button" class="btn btn-primary" (click)="showWIMAccountInfo()" placement="top" container="body"
+ ngbTooltip="{{'INFO' | translate}}">
+ <i class="fas fa-info icons"></i>
+ </button>
+ <button type="button" class="btn btn-primary" (click)="deleteWIMAccount()" placement="top" container="body"
+ ngbTooltip="{{'DELETE' | translate}}">
+ <i class="far fa-trash-alt icons"></i>
+ </button>
+</div>
\ No newline at end of file
diff --git a/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.scss b/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.scss
new file mode 100644
index 0000000..031e56e
--- /dev/null
+++ b/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.ts b/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.ts
new file mode 100644
index 0000000..0769863
--- /dev/null
+++ b/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.ts
@@ -0,0 +1,83 @@
+/*
+ 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 WIM AccountsAction Component
+ */
+import { Component, Injector } from '@angular/core';
+import { Router } from '@angular/router';
+import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { MODALCLOSERESPONSEDATA } from 'CommonModel';
+import { DeleteComponent } from 'DeleteComponent';
+import { SharedService } from 'SharedService';
+import { WIMAccountInfoComponent } from 'WIMAccountInfo';
+import { WIMValue } from 'WIMAccountModel';
+
+/**
+ * Creating component
+ * @Component takes WIMAccountsActionComponent.html as template url
+ */
+@Component({
+ templateUrl: './WIMAccountsActionComponent.html',
+ styleUrls: ['./WIMAccountsActionComponent.scss']
+})
+/** Exporting a class @exports WIMAccountsActionComponent */
+export class WIMAccountsActionComponent {
+ /** To get the value from the vnfpackage via valuePrepareFunction default Property of ng-smarttable @public */
+ public value: WIMValue;
+
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Variables holds WIM ID @private */
+ private wimID: string;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.modalService = this.injector.get(NgbModal);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.wimID = this.value.identifier;
+ }
+
+ /** Show WIM account information @public */
+ public showWIMAccountInfo(): void {
+ this.modalService.open(WIMAccountInfoComponent, { backdrop: 'static' }).componentInstance.params = {
+ id: this.wimID,
+ page: 'wim-accounts'
+ };
+ }
+
+ /** Delete WIM Account @public */
+ public deleteWIMAccount(): void {
+ const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' });
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+}
diff --git a/src/app/vim-accounts/VimAccountsComponent.html b/src/app/vim-accounts/VimAccountsComponent.html
new file mode 100644
index 0000000..3f96ff8
--- /dev/null
+++ b/src/app/vim-accounts/VimAccountsComponent.html
@@ -0,0 +1,18 @@
+<!--
+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)
+-->
+<router-outlet></router-outlet>
\ No newline at end of file
diff --git a/src/app/vim-accounts/VimAccountsComponent.scss b/src/app/vim-accounts/VimAccountsComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/vim-accounts/VimAccountsComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/vim-accounts/VimAccountsComponent.ts b/src/app/vim-accounts/VimAccountsComponent.ts
new file mode 100644
index 0000000..9951cfc
--- /dev/null
+++ b/src/app/vim-accounts/VimAccountsComponent.ts
@@ -0,0 +1,56 @@
+/*
+ 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 Vim Account Component.
+ */
+import { Component, Injector } from '@angular/core';
+import { Router, RouterEvent } from '@angular/router';
+
+/**
+ * Creating component
+ * @Component takes VimAccountsComponent.html as template url
+ */
+@Component({
+ selector: 'app-vim-accounts',
+ templateUrl: './VimAccountsComponent.html',
+ styleUrls: ['./VimAccountsComponent.scss']
+})
+/** Exporting a class @exports VimAccountsComponent */
+export class VimAccountsComponent {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ // creates packages component
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.router = this.injector.get(Router);
+ this.router.events.subscribe((event: RouterEvent) => {
+ this.redirectToList(event.url);
+ });
+ }
+
+ /** Return to list NS Package List */
+ public redirectToList(getURL: string): void {
+ if (getURL === '/vim') {
+ this.router.navigate(['/vim/details']).catch();
+ }
+ }
+}
diff --git a/src/app/vim-accounts/VimAccountsModule.ts b/src/app/vim-accounts/VimAccountsModule.ts
new file mode 100644
index 0000000..3424bc4
--- /dev/null
+++ b/src/app/vim-accounts/VimAccountsModule.ts
@@ -0,0 +1,92 @@
+/*
+ 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 Vim Account module.
+ */
+import { CommonModule } from '@angular/common';
+import { HttpClientModule } from '@angular/common/http';
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterModule, Routes } from '@angular/router';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { TranslateModule } from '@ngx-translate/core';
+import { DataService } from 'DataService';
+import { InfoVimComponent } from 'InfoVim';
+import { LoaderModule } from 'LoaderModule';
+import { NewVimaccountComponent } from 'NewVimaccount';
+import { Ng2SmartTableModule } from 'ng2-smart-table';
+import { PagePerRowModule } from 'PagePerRowModule';
+import { PageReloadModule } from 'PageReloadModule';
+import { VimAccountDetailsComponent } from 'VimAccountDetails';
+import { VimAccountsComponent } from 'VimAccountsComponent';
+
+/** To halndle project information */
+const projectInfo: {} = { title: '{project}', url: '/' };
+
+/** const values for dashboard Routes */
+const routes: Routes = [
+ {
+ path: '',
+ component: VimAccountsComponent,
+ children: [
+ {
+ path: 'details',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'VIMACCOUNTS', url: null }]
+ },
+ component: VimAccountDetailsComponent
+ },
+ {
+ path: 'info/:id',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'VIMACCOUNTS', url: '/vim/details' }, { title: '{id}', url: null }]
+ },
+ component: InfoVimComponent
+ },
+ {
+ path: 'new',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'VIMACCOUNTS', url: '/vim/details' }, { title: 'PAGE.VIMDETAILS.NEWVIM', url: null }]
+ },
+ component: NewVimaccountComponent
+ }
+ ]
+ }
+];
+
+/**
+ * Creating @NgModule component for Modules
+ */
+@NgModule({
+ imports: [ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }), FormsModule, CommonModule,
+ HttpClientModule, NgSelectModule, Ng2SmartTableModule, TranslateModule, RouterModule.forChild(routes), NgbModule,
+ PagePerRowModule, LoaderModule, PageReloadModule],
+ declarations: [VimAccountsComponent, InfoVimComponent, VimAccountDetailsComponent, NewVimaccountComponent],
+ providers: [DataService],
+ entryComponents: [InfoVimComponent]
+})
+/** Exporting a class @exports VimAccountsModule */
+export class VimAccountsModule {
+ /** Variables declared to avoid state-less class */
+ private vimModule: string;
+}
diff --git a/src/app/vim-accounts/info-vim/InfoVimComponent.html b/src/app/vim-accounts/info-vim/InfoVimComponent.html
new file mode 100644
index 0000000..2eac832
--- /dev/null
+++ b/src/app/vim-accounts/info-vim/InfoVimComponent.html
@@ -0,0 +1,47 @@
+<!--
+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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'PAGE.VIMDETAILS.VIMACCOUNTDETAILS' | translate}}</div>
+</div>
+<div class="context-style bg-white mt-2 vim-details">
+ <div class="row">
+ <div class="col-sm-6 text-dark mb-2 font-weight-bold" *ngFor="let details of vimDetails">
+ <label class="col-sm-5 col-form-label">{{ details.title | translate}}</label>
+ <span class="col-sm-5">{{details.value}}</span>
+ </div>
+ </div>
+</div>
+<div class="col-12">
+ <button type="button" class="btn btn-block border-0 bg-light text-dark" (click)="isCollapsed = !isCollapsed"
+ [attr.aria-expanded]="!isCollapsed">
+ {{'PAGE.VIMDETAILS.CONFIGPARAMETERS' | translate}}
+ </button>
+</div>
+<div id="demo" class="collapse context-style p-2" [ngbCollapse]="isCollapsed">
+ <div class="row">
+ <div class="col-sm-6 text-dark mb-2 font-weight-bold" *ngFor="let details of configParams">
+ <label class="col-sm-5 col-form-label">{{ details.title | translate}}:</label>
+ <span class="col-sm-5"> {{(details.value !== undefined)?details.value : '--'}} </span>
+ </div>
+ </div>
+</div>
+<div class="modal-footer list">
+ <button (click)="onVimAccountBack()"
+ class="btn btn-danger pull-right">{{'PAGE.VIMDETAILS.BACKTOVIMACCOUNTS' | translate}}</button>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/vim-accounts/info-vim/InfoVimComponent.scss b/src/app/vim-accounts/info-vim/InfoVimComponent.scss
new file mode 100644
index 0000000..8c2b739
--- /dev/null
+++ b/src/app/vim-accounts/info-vim/InfoVimComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
diff --git a/src/app/vim-accounts/info-vim/InfoVimComponent.ts b/src/app/vim-accounts/info-vim/InfoVimComponent.ts
new file mode 100644
index 0000000..d40b696
--- /dev/null
+++ b/src/app/vim-accounts/info-vim/InfoVimComponent.ts
@@ -0,0 +1,445 @@
+/*
+ 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 Info VIM Page
+ */
+import { Component, Injector, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { ERRORDATA } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import * as HttpStatus from 'http-status-codes';
+import { RestService } from 'RestService';
+import { isNullOrUndefined } from 'util';
+import { CONFIG, VimAccountDetails, VIMData } from 'VimAccountModel';
+
+/**
+ * Creating component
+ * @Component InfoVimComponent.html as template url
+ */
+@Component({
+ selector: 'app-info-vim',
+ templateUrl: './InfoVimComponent.html',
+ styleUrls: ['./InfoVimComponent.scss']
+})
+/** Exporting a class @exports InfoVimComponent */
+export class InfoVimComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** vimAccountDetails to populate in InfoVIM Page @private */
+ public vimAccountDetails: VimAccountDetails;
+
+ /** Information Top Left @public */
+ public configParams: {}[] = [];
+
+ /** Showing more details of collapase */
+ public isCollapsed: boolean = true;
+
+ /** Contains vim details @public */
+ public vimDetails: {}[];
+
+ /** Check the Projects loading results @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** variables contains paramsID @private */
+ private paramsID: string;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** Holds the instance of router class @private */
+ private router: Router;
+
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** vimId to populate in InfoVIM Page @private */
+ private vimId: string;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private activatedRoute: ActivatedRoute;
+
+ /** Utilizes modal service for any modal operations @private */
+ private modalService: NgbModal;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.activatedRoute = this.injector.get(ActivatedRoute);
+ this.modalService = this.injector.get(NgbModal);
+ this.router = this.injector.get(Router);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ // tslint:disable-next-line:no-backbone-get-set-outside-model
+ this.paramsID = this.activatedRoute.snapshot.paramMap.get('id');
+ this.dataService.currentMessage.subscribe((data: VIMData) => {
+ this.vimId = data.identifier;
+ });
+ this.generateData();
+ }
+
+ /** Routing to VIM Account Details Page @public */
+ public onVimAccountBack(): void {
+ this.router.navigate(['vim/details']).catch(() => {
+ // Error Cached
+ });
+ }
+
+ /** Generate Data function @public */
+ public generateData(): void {
+ this.isLoadingResults = true;
+ this.restService.getResource(environment.VIMACCOUNTS_URL + '/' + this.paramsID)
+ .subscribe((vimAccountsData: VimAccountDetails) => {
+ this.showDetails(vimAccountsData);
+ if (vimAccountsData.config !== undefined) {
+ if (vimAccountsData.vim_type === 'openstack') {
+ this.showOpenstackConfig(vimAccountsData.config);
+ } else if (vimAccountsData.vim_type === 'aws') {
+ this.awsConfig(vimAccountsData.config);
+ } else if (vimAccountsData.vim_type === 'openvim' || vimAccountsData.vim_type === 'opennebula') {
+ this.openVIMOpenNebulaConfig(vimAccountsData.config);
+ } else if (vimAccountsData.vim_type === 'vmware') {
+ this.vmwareConfig(vimAccountsData.config);
+ } else if (vimAccountsData.vim_type === 'azure') {
+ this.azureConfig(vimAccountsData.config);
+ }
+ }
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.isLoadingResults = false;
+ if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
+ this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
+ } else {
+ this.restService.handleError(error, 'get');
+ }
+ });
+ }
+
+ /** show general vim detailed information @public */
+ public showDetails(vimAccountsData: VimAccountDetails): void {
+ this.vimDetails = [
+ {
+ title: 'PAGE.VIMDETAILS.NAME',
+ value: vimAccountsData.name
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VIMUSERNAME',
+ value: vimAccountsData.vim_user
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VIMURL',
+ value: vimAccountsData.vim_url
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VIMTYPE',
+ value: vimAccountsData.vim_type
+ },
+ {
+ title: 'PAGE.VIMDETAILS.TENANTNAME',
+ value: vimAccountsData.vim_tenant_name
+ },
+ {
+ title: 'PAGE.VIMDETAILS.DESCRIPTION',
+ value: vimAccountsData.description
+ },
+ {
+ title: 'PAGE.VIMDETAILS.SCHEMATYPE',
+ value: vimAccountsData.schema_type
+ },
+ {
+ title: 'PAGE.VIMDETAILS.SCHEMAVERSION',
+ value: vimAccountsData.schema_version
+ }
+ ];
+ }
+
+ /** Openstack Config @public */
+ public showOpenstackConfig(config: CONFIG): void {
+ if (!isNullOrUndefined(config)) {
+ Object.keys(config).forEach((key: string) => {
+ if (Array.isArray(config[key])) {
+ config[key] = JSON.stringify(config[key]);
+ }
+ });
+ }
+ let location: string = config.location;
+ if (!isNullOrUndefined(location)) {
+ const locationArr: string[] = config.location.split(',');
+ if (Array.isArray(locationArr)) {
+ location = locationArr[0];
+ }
+ }
+
+ this.configParams = [
+ {
+ title: 'PAGE.VIMDETAILS.SDNCONTROLLER',
+ value: config.sdn_controller
+ },
+ {
+ title: 'PAGE.VIMDETAILS.SDNPORTMAPPING',
+ value: config.sdn_port_mapping
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VIMNETWORKNAME',
+ value: config.vim_network_name
+ },
+ {
+ title: 'PAGE.VIMDETAILS.SECURITYGROUPS',
+ value: config.security_groups
+ },
+ {
+ title: 'PAGE.VIMDETAILS.AVAILABILITYZONE',
+ value: config.availabilityZone
+ },
+ {
+ title: 'PAGE.VIMDETAILS.REGIONALNAME',
+ value: config.region_name
+ },
+ {
+ title: 'PAGE.VIMDETAILS.INSECURE',
+ value: config.insecure
+ },
+ {
+ title: 'PAGE.VIMDETAILS.USEEXISTINGFLAVOURS',
+ value: config.use_existing_flavors
+ },
+ {
+ title: 'PAGE.VIMDETAILS.USEINTERNALENDPOINT',
+ value: config.use_internal_endpoint
+ },
+ {
+ title: 'PAGE.VIMDETAILS.ADDITIONALCONFIG',
+ value: config.additional_conf
+ },
+ {
+ title: 'PAGE.VIMDETAILS.APIVERSION',
+ value: config.APIversion
+ },
+ {
+ title: 'PAGE.VIMDETAILS.PROJECTDOMAINID',
+ value: config.project_domain_id
+ },
+ {
+ title: 'PAGE.VIMDETAILS.PROJECTDOMAINNAME',
+ value: config.project_domain_name
+ },
+ {
+ title: 'PAGE.VIMDETAILS.USERDOMAINID',
+ value: config.user_domain_id
+ },
+ {
+ title: 'PAGE.VIMDETAILS.USERDOMAINUSER',
+ value: config.user_domain_name
+ },
+ {
+ title: 'PAGE.VIMDETAILS.KEYPAIR',
+ value: config.keypair
+ },
+ {
+ title: 'PAGE.VIMDETAILS.DATAPLANEPHYSICALNET',
+ value: config.dataplane_physical_net
+ },
+ {
+ title: 'PAGE.VIMDETAILS.USEFLOATINGIP',
+ value: config.use_floating_ip
+ },
+ {
+ title: 'PAGE.VIMDETAILS.MICROVERSION',
+ value: config.microversion
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VIMLOCATION',
+ value: location
+ }
+ ];
+ }
+
+ /** AWS Config @public */
+ public awsConfig(config: CONFIG): void {
+ this.configParams = [
+ {
+ title: 'PAGE.VIMDETAILS.SDNCONTROLLER',
+ value: config.sdn_controller
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VPCCIDRBLOCK',
+ value: config.vpc_cidr_block
+ },
+ {
+ title: 'PAGE.VIMDETAILS.SDNPORTMAPPING',
+ value: config.sdn_port_mapping
+ },
+ {
+ title: 'PAGE.VIMDETAILS.SECURITYGROUPS',
+ value: config.security_groups
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VIMNETWORKNAME',
+ value: config.vim_network_name
+ },
+ {
+ title: 'PAGE.VIMDETAILS.KEYPAIR',
+ value: config.keypair
+ },
+ {
+ title: 'PAGE.VIMDETAILS.REGIONALNAME',
+ value: config.region_name
+ },
+ {
+ title: 'PAGE.VIMDETAILS.FLAVORIINFO',
+ value: config.flavor_info
+ },
+ {
+ title: 'PAGE.VIMDETAILS.ADDITIONALCONFIG',
+ value: config.additional_conf
+ }
+ ];
+ }
+
+ /** Open vim and open nebula config @public */
+ public openVIMOpenNebulaConfig(config: CONFIG): void {
+ this.configParams = [
+ {
+ title: 'PAGE.VIMDETAILS.SDNCONTROLLER',
+ value: config.sdn_controller
+ },
+ {
+ title: 'PAGE.VIMDETAILS.SDNPORTMAPPING',
+ value: config.sdn_port_mapping
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VIMNETWORKNAME',
+ value: config.vim_network_name
+ },
+ {
+ title: 'PAGE.VIMDETAILS.ADDITIONALCONFIG',
+ value: config.additional_conf
+ }
+ ];
+ }
+
+ /** vmware config @public */
+ public vmwareConfig(config: CONFIG): void {
+ this.configParams = [
+ {
+ title: 'PAGE.VIMDETAILS.SDNCONTROLLER',
+ value: config.sdn_controller
+ },
+ {
+ title: 'PAGE.VIMDETAILS.ORGNAME',
+ value: config.orgname
+ },
+ {
+ title: 'PAGE.VIMDETAILS.SDNPORTMAPPING',
+ value: config.sdn_port_mapping
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VCENTERIP',
+ value: config.vcenter_ip
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VIMNETWORKNAME',
+ value: config.vim_network_name
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VCENTERPORT',
+ value: config.vcenter_port
+ },
+ {
+ title: 'PAGE.VIMDETAILS.ADMINUSERNAME',
+ value: config.admin_username
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VCENTERUSER',
+ value: config.vcenter_user
+ },
+ {
+ title: 'PAGE.VIMDETAILS.ADMINPASSWORD',
+ value: config.admin_password
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VCENTERPASSWORD',
+ value: config.vcenter_password
+ },
+ {
+ title: 'PAGE.VIMDETAILS.NSXMANAGER',
+ value: config.nsx_manager
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VROPSSITE',
+ value: config.vrops_site
+ },
+ {
+ title: 'PAGE.VIMDETAILS.NSXUSER',
+ value: config.nsx_user
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VROPSUSER',
+ value: config.vrops_user
+ },
+ {
+ title: 'PAGE.VIMDETAILS.NSXPASSWORD',
+ value: config.nsx_password
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VROPSPASSWORD',
+ value: config.vrops_password
+ },
+ {
+ title: 'PAGE.VIMDETAILS.ADDITIONALCONFIG',
+ value: config.additional_conf
+ }
+ ];
+ }
+
+ /** Azure Config @public */
+ public azureConfig(config: CONFIG): void {
+ this.configParams = [
+ {
+ title: 'PAGE.VIMDETAILS.SUBSCRIPTIONID',
+ value: config.subscription_id
+ },
+ {
+ title: 'PAGE.VIMDETAILS.REGIONALNAME',
+ value: config.region_name
+ },
+ {
+ title: 'PAGE.VIMDETAILS.RESOURCEGROUP',
+ value: config.resource_group
+ },
+ {
+ title: 'PAGE.VIMDETAILS.VNETNAME',
+ value: config.vnet_name
+ },
+ {
+ title: 'PAGE.VIMDETAILS.FLAVORSPATTERN',
+ value: config.flavors_pattern
+ }
+ ];
+ }
+}
diff --git a/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.html b/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.html
new file mode 100644
index 0000000..cc56017
--- /dev/null
+++ b/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.html
@@ -0,0 +1,661 @@
+<!--
+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)
+-->
+<form [formGroup]="vimNewAccountForm" (ngSubmit)="newVimAccountSubmit()" autocomplete="off">
+ <div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'PAGE.VIMDETAILS.NEWVIMACCOUNT' | translate}} </div>
+ </div>
+ <div class="context-style bg-white p-3">
+ <div class="row form-group content-style">
+ <label class="col-sm-12 col-form-label mandatory-label"
+ [ngClass]="{'text-danger': vimNewAccountForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <div class="col-sm-3">
+ <label for="name">{{'PAGE.VIMDETAILS.NAME' | translate}}*</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control" placeholder="{{'PAGE.VIMDETAILS.NAME' | translate}}"
+ type="text" formControlName="name" id="name"
+ [ngClass]="{ 'is-invalid': submitted && f.name.errors }">
+ </div>
+ <div class="col-sm-3">
+ <label for="vim_tenant_name">{{'PAGE.VIMDETAILS.TENANTNAME' | translate}}*</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.TENANTNAME' | translate}}" type="text"
+ formControlName="vim_tenant_name" id="vim_tenant_name"
+ [ngClass]="{ 'is-invalid': submitted && f.vim_tenant_name.errors }">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="vim_type">{{'PAGE.VIMDETAILS.VIMTYPE' | translate}}*</label>
+ </div>
+ <div class="col-sm-3">
+ <ng-select bindLabel="title" bindValue="value" [items]="vimType" placeholder="{{'SELECT' | translate}}"
+ formControlName="vim_type" id="vim_type" [(ngModel)]="selectedVimType"
+ [ngClass]="{ 'is-invalid': submitted && f.vim_type.errors }">
+ </ng-select>
+ </div>
+ <div class="col-sm-3">
+ <label for="description">{{'PAGE.VIMDETAILS.DESCRIPTION' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <textarea class="form-control" placeholder="{{'PAGE.VIMDETAILS.DESCRIPTION' | translate}}" type="text"
+ formControlName="description" id="description"></textarea>
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="vim_url">{{'PAGE.VIMDETAILS.VIMURL' | translate}}*</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control" placeholder="{{'PAGE.VIMDETAILS.VIMURL' | translate}}"
+ type="url" formControlName="vim_url" id="vim_url"
+ [ngClass]="{ 'is-invalid': submitted && f.vim_url.errors }">
+ <div *ngIf="vimNewAccountForm.invalid" class="invalid-feedback">
+ <div *ngIf="f.vim_url.errors && f.vim_url.value">{{'DOMVALIDATIONS.INVALIDURL' | translate}}</div>
+ </div>
+ </div>
+ <div class="col-sm-3">
+ <label for="schema_type">{{'PAGE.VIMDETAILS.SCHEMATYPE' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.SCHEMATYPE' | translate}}" type="text" formControlName="schema_type"
+ id="schema_type">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="vim_user">{{'PAGE.VIMDETAILS.VIMUSERNAME' | translate}}*</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.VIMUSERNAME' | translate}}" type="text" formControlName="vim_user"
+ id="vim_user" [ngClass]="{ 'is-invalid': submitted && f.vim_user.errors }">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="vim_password">{{'PAGE.VIMDETAILS.VIMPASSWORD' | translate}}*</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control" placeholder="VIM Password" type="password"
+ formControlName="vim_password" id="vim_password"
+ [ngClass]="{ 'is-invalid': submitted && f.vim_password.errors }">
+ </div>
+ </div>
+ <div class="row" [hidden]="!selectedVimType">
+ <div class="col-12">
+ <button type="button" class="btn btn-block border-0 bg-light text-dark"
+ (click)="isCollapsed = !isCollapsed"
+ [attr.aria-expanded]="!isCollapsed">{{'PAGE.VIMDETAILS.CONFIGPARAMETERS' | translate}}</button>
+ </div>
+ </div>
+ <div formGroupName="vimconfig" id="configurationparameters" class="collapse mt-3" [ngbCollapse]="isCollapsed">
+ <div class="row" [hidden]="!selectedVimType">
+ <div class="col-sm-3">
+ <label>{{'UPLOADCONFIG' | translate}}</label>
+ </div>
+ <div class="col-sm-3 form-group">
+ <div class="custom-file">
+ <input type="file" #fileInput class="custom-file-input"
+ (change)="filesDropped($event.target.files)" id="customFile">
+ <label class="custom-file-label" #fileInputLabel
+ for="customFile">{{'CHOOSEFILE' | translate}}</label>
+ </div>
+ </div>
+ </div>
+ <div *ngIf="selectedVimType == 'openstack'">
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="sdn_controller">{{'PAGE.VIMDETAILS.SDNCONTROLLER' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.SDNCONTROLLER' | translate}}" type="text"
+ formControlName="sdn_controller" id="sdn_controller">
+ </div>
+ <div class="col-sm-3">
+ <label for="APIversion">{{'PAGE.VIMDETAILS.APIVERSION' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.APIVERSION' | translate}}" type="text"
+ formControlName="APIversion" id="APIversion">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="sdn_port_mapping">{{'PAGE.VIMDETAILS.SDNPORTMAPPING' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.SDNPORTMAPPING' | translate}}" type="text"
+ formControlName="sdn_port_mapping" id="sdn_port_mapping">
+ </div>
+ <div class="col-sm-3">
+ <label for="project_domain_id">{{'PAGE.VIMDETAILS.PROJECTDOMAINID' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.PROJECTDOMAINID' | translate}}" type="text"
+ formControlName="project_domain_id" id="project_domain_id">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="vim_network_name">{{'PAGE.VIMDETAILS.VIMNETWORKNAME' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.VIMNETWORKNAME' | translate}}" type="text"
+ formControlName="vim_network_name" id="vim_network_name">
+ </div>
+ <div class="col-sm-3">
+ <label for="project_domain_name">{{'PAGE.VIMDETAILS.PROJECTDOMAINNAME' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.PROJECTDOMAINNAME' | translate}}" type="text"
+ formControlName="project_domain_name" id="project_domain_name">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="config_vim_ype">{{'PAGE.VIMDETAILS.VIMTYPE' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.VIM_TYPE' | translate}}" type="text"
+ formControlName="config_vim_ype" id="config_vim_ype">
+ </div>
+ <div class="col-sm-3">
+ <label for="user_domain_id">{{'PAGE.VIMDETAILS.USERDOMAINID' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.USERDOMAINID' | translate}}" type="text"
+ formControlName="user_domain_id" id="user_domain_id">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="security_groups">{{'PAGE.VIMDETAILS.SECURITYGROUPS' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.SECURITYGROUPS' | translate}}" type="text"
+ formControlName="security_groups" id="security_groups">
+ </div>
+ <div class="col-sm-3">
+ <label for="user_domain_name">{{'PAGE.VIMDETAILS.USERDOMAINUSER' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.USERDOMAINUSER' | translate}}" type="text"
+ formControlName="user_domain_name" id="user_domain_name">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="availabilityZone">{{'PAGE.VIMDETAILS.AVAILABILITYZONE' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.AVAILABILITYZONE' | translate}}" type="text"
+ formControlName="availabilityZone" id="availabilityZone">
+ </div>
+ <div class="col-sm-3">
+ <label for="keypair">{{'PAGE.VIMDETAILS.KEYPAIR' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.KEYPAIR' | translate}}" type="text"
+ formControlName="keypair" id="keypair">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="region_name">{{'PAGE.VIMDETAILS.REGIONALNAME' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.REGIONALNAME' | translate}}" type="text"
+ formControlName="region_name" id="region_name">
+ </div>
+ <div class="col-sm-3">
+ <label
+ for="dataplane_physical_net">{{'PAGE.VIMDETAILS.DATAPLANEPHYSICALNET' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.DATAPLANEPHYSICALNET' | translate}}" type="text"
+ formControlName="dataplane_physical_net" id="dataplane_physical_net">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="insecure">{{'PAGE.VIMDETAILS.INSECURE' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <ng-select [items]="boolValue" bindLabel="name" bindValue="id"
+ placeholder="{{'PAGE.VIMDETAILS.INSECURE' | translate}}" formControlName="insecure"
+ id="insecure">
+ </ng-select>
+ </div>
+ <div class="col-sm-3">
+ <label for="use_floating_ip">{{'PAGE.VIMDETAILS.USEFLOATINGIP' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <ng-select [items]="boolValue" bindLabel="name" bindValue="id" id="use_floating_ip"
+ placeholder="{{'PAGE.VIMDETAILS.USEFLOATINGIP' | translate}}"
+ formControlName="use_floating_ip">
+ </ng-select>
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="use_internal_endpoint">{{'PAGE.VIMDETAILS.USEINTERNALENDPOINT' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <ng-select [items]="boolValue" bindLabel="name" bindValue="id"
+ placeholder="{{'PAGE.VIMDETAILS.USEINTERNALENDPOINT' | translate}}"
+ formControlName="use_internal_endpoint" id="use_internal_endpoint">
+ </ng-select>
+ </div>
+ <div class="col-sm-3">
+ <label for="microversion">{{'PAGE.VIMDETAILS.MICROVERSION' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.MICROVERSION' | translate}}" type="text"
+ formControlName="microversion" id="microversion">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="additional_conf">{{'PAGE.VIMDETAILS.ADDITIONALCONFIG' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.ADDITIONALCONFIGPLACEHOLDER' | translate}}" type="text"
+ formControlName="additional_conf" id="additional_conf">
+ </div>
+ <div class="col-sm-3">
+ <label for="use_existing_flavors">{{'PAGE.VIMDETAILS.USEEXISTINGFLAVOURS' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <ng-select [items]="boolValue" bindLabel="name" bindValue="id"
+ placeholder="{{'PAGE.VIMDETAILS.USEEXISTINGFLAVOURS' | translate}}"
+ formControlName="use_existing_flavors" id="use_existing_flavors">
+ </ng-select>
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="location">{{'PAGE.VIMDETAILS.VIMLOCATION' | translate}}</label>
+ </div>
+ <div class="col-sm-9">
+ <ng-select [items]="getVIMLocation" bindLabel="label" bindValue="value"
+ placeholder="{{'PAGE.VIMDETAILS.VIMLOCATION' | translate}}" formControlName="location"
+ id="location" (keydown.enter)="fetchLocationLatLong($event.target.value)">
+ </ng-select>
+ <small>{{'PAGE.VIM.LOACTIONINFO' | translate}}</small>
+ </div>
+ </div>
+ </div>
+ <div *ngIf="selectedVimType == 'openvim' || selectedVimType == 'opennebula'">
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="sdn_controller">{{'PAGE.VIMDETAILS.SDNCONTROLLER' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.SDNCONTROLLER' | translate}}" type="text"
+ formControlName="sdn_controller" id="sdn_controller">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="sdn_port_mapping">{{'PAGE.VIMDETAILS.SDNPORTMAPPING' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.SDNPORTMAPPING' | translate}}" type="text"
+ formControlName="sdn_port_mapping" id="sdn_port_mapping">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="vim_network_name">{{'PAGE.VIMDETAILS.VIMNETWORKNAME' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.VIMNETWORKNAME' | translate}}" type="text"
+ formControlName="vim_network_name" id="vim_network_name">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="additional_conf">{{'PAGE.VIMDETAILS.ADDITIONALCONFIG' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.ADDITIONALCONFIGPLACEHOLDER' | translate}}" type="text"
+ formControlName="additional_conf" id="additional_conf">
+ </div>
+ </div>
+ </div>
+ <div *ngIf="selectedVimType == 'vmware'">
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="sdn_controller">{{'PAGE.VIMDETAILS.SDNCONTROLLER' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.SDNCONTROLLER' | translate}}" type="text"
+ formControlName="sdn_controller" id="sdn_controller">
+ </div>
+ <div class="col-sm-3">
+ <label for="orgname">{{'PAGE.VIMDETAILS.ORGNAME' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.ORGNAME' | translate}}" type="text"
+ formControlName="orgname" id="orgname">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="sdn_port_mapping">{{'PAGE.VIMDETAILS.SDNPORTMAPPING' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.SDNPORTMAPPING' | translate}}" type="text"
+ formControlName="sdn_port_mapping" id="sdn_port_mapping">
+ </div>
+ <div class="col-sm-3">
+ <label for="vcenter_ip">{{'PAGE.VIMDETAILS.VCENTERIP' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.VCENTERIP' | translate}}" type="text"
+ formControlName="vcenter_ip" id="vcenter_ip">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="vim_network_name">{{'PAGE.VIMDETAILS.VIMNETWORKNAME' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.VIMNETWORKNAME' | translate}}" type="text"
+ formControlName="vim_network_name" id="vim_network_name">
+ </div>
+ <div class="col-sm-3">
+ <label for="vcenter_port">{{'PAGE.VIMDETAILS.VCENTERPORT' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.VCENTERPORT' | translate}}" type="text"
+ formControlName="vcenter_port" id="vcenter_port">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="admin_username">{{'PAGE.VIMDETAILS.ADMINUSERNAME' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.ADMINUSERNAME' | translate}}" type="text"
+ formControlName="admin_username" id="admin_username">
+ </div>
+ <div class="col-sm-3">
+ <label for="vcenter_user">{{'PAGE.VIMDETAILS.VCENTERUSER' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.VCENTERUSER' | translate}}" type="text"
+ formControlName="vcenter_user" id="vcenter_user">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="admin_password">{{'PAGE.VIMDETAILS.ADMINPASSWORD' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.ADMINPASSWORD' | translate}}" type="text"
+ formControlName="admin_password" id="admin_password">
+ </div>
+ <div class="col-sm-3">
+ <label for="vcenter_password">{{'PAGE.VIMDETAILS.VCENTERPASSWORD' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.VCENTERPASSWORD' | translate}}" type="text"
+ formControlName="vcenter_password" id="vcenter_password">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="nsx_manager">{{'PAGE.VIMDETAILS.NSXMANAGER' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.NSXMANAGER' | translate}}" type="text"
+ formControlName="nsx_manager" id="nsx_manager">
+ </div>
+ <div class="col-sm-3">
+ <label for="vrops_site">{{'PAGE.VIMDETAILS.VROPSSITE' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.VROPSSITE' | translate}}" type="text"
+ formControlName="vrops_site" id="vrops_site">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="nsx_user">{{'PAGE.VIMDETAILS.NSXUSER' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.NSXUSER' | translate}}" type="text"
+ formControlName="nsx_user" id="nsx_user">
+ </div>
+ <div class="col-sm-3">
+ <label for="vrops_user">{{'PAGE.VIMDETAILS.VROPSUSER' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.VROPSUSER' | translate}}" type="text"
+ formControlName="vrops_user" id="vrops_user">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="nsx_password">{{'PAGE.VIMDETAILS.NSXPASSWORD' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.NSXPASSWORD' | translate}}" type="text"
+ formControlName="nsx_password" id="nsx_password">
+ </div>
+ <div class="col-sm-3">
+ <label for="vrops_password">{{'PAGE.VIMDETAILS.VROPSPASSWORD' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.VROPSPASSWORD' | translate}}" type="text"
+ formControlName="vrops_password" id="vrops_password">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="additional_conf">{{'PAGE.VIMDETAILS.ADDITIONALCONFIG' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.ADDITIONALCONFIGPLACEHOLDER' | translate}}" type="text"
+ formControlName="additional_conf" id="additional_conf">
+ </div>
+ </div>
+ </div>
+ <div *ngIf="selectedVimType == 'aws'">
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="sdn_controller">{{'PAGE.VIMDETAILS.SDNCONTROLLER' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.SDNCONTROLLER' | translate}}" type="text"
+ formControlName="sdn_controller" id="sdn_controller">
+ </div>
+ <div class="col-sm-3">
+ <label for="vpc_cidr_block">{{'PAGE.VIMDETAILS.VPCCIDRBLOCK' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.VPCCIDRBLOCK' | translate}}" type="text"
+ formControlName="vpc_cidr_block" id="vpc_cidr_block">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="sdn_port_mapping">{{'PAGE.VIMDETAILS.SDNPORTMAPPING' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.SDNPORTMAPPING' | translate}}" type="text"
+ formControlName="sdn_port_mapping" id="sdn_port_mapping">
+ </div>
+ <div class="col-sm-3">
+ <label for="security_groups">{{'PAGE.VIMDETAILS.SECURITYGROUPS' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.SECURITYGROUPS' | translate}}" type="text"
+ formControlName="security_groups" id="security_groups">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="vim_network_name">{{'PAGE.VIMDETAILS.VIMNETWORKNAME' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.VIMNETWORKNAME' | translate}}" type="text"
+ formControlName="vim_network_name" id="vim_network_name">
+ </div>
+ <div class="col-sm-3">
+ <label for="keypair">{{'PAGE.VIMDETAILS.KEYPAIR' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.KEYPAIR' | translate}}" type="text"
+ formControlName="keypair" id="keypair">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="region_name">{{'PAGE.VIMDETAILS.REGIONALNAME' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.REGIONALNAME' | translate}}" type="text"
+ formControlName="region_name" id="region_name">
+ </div>
+ <div class="col-sm-3">
+ <label for="flavor_info">{{'PAGE.VIMDETAILS.FLAVORIINFO' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.FLAVORIINFO' | translate}}" type="text"
+ formControlName="flavor_info" id="flavor_info">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="additional_conf">{{'PAGE.VIMDETAILS.ADDITIONALCONFIG' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.ADDITIONALCONFIGPLACEHOLDER' | translate}}" type="text"
+ formControlName="additional_conf" id="additional_conf">
+ </div>
+ </div>
+ </div>
+ <div *ngIf="selectedVimType == 'azure'">
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="subscription_id">{{'PAGE.VIMDETAILS.SUBSCRIPTIONID' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.SUBSCRIPTIONID' | translate}}" type="text"
+ formControlName="subscription_id" id="subscription_id">
+ </div>
+ <div class="col-sm-3">
+ <label for="region_name">{{'PAGE.VIMDETAILS.REGIONALNAME' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.REGIONALNAME' | translate}}" type="text"
+ formControlName="region_name" id="region_name">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="resource_group">{{'PAGE.VIMDETAILS.RESOURCEGROUP' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.RESOURCEGROUP' | translate}}" type="text"
+ formControlName="resource_group" id="resource_group">
+ </div>
+ <div class="col-sm-3">
+ <label for="vnet_name">{{'PAGE.VIMDETAILS.VNETNAME' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.VNETNAME' | translate}}" type="text"
+ formControlName="vnet_name" id="vnet_name">
+ </div>
+ </div>
+ <div class="row form-group content-style">
+ <div class="col-sm-3">
+ <label for="flavors_pattern">{{'PAGE.VIMDETAILS.FLAVORSPATTERN' | translate}}</label>
+ </div>
+ <div class="col-sm-3">
+ <input autocomplete="off" class="form-control"
+ placeholder="{{'PAGE.VIMDETAILS.FLAVORSPATTERN' | translate}}" type="text"
+ formControlName="flavors_pattern" id="flavors_pattern">
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" (click)="onVimAccountBack()"
+ class="btn btn-danger">{{'PAGE.VIMDETAILS.BACKTOVIMACCOUNTS' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'CREATE' | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLocationLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.scss b/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.scss
new file mode 100644
index 0000000..d750ccc
--- /dev/null
+++ b/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.ts b/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.ts
new file mode 100644
index 0000000..bc6fd1e
--- /dev/null
+++ b/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.ts
@@ -0,0 +1,321 @@
+/*
+ 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 Vim Account Component.
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { Router } from '@angular/router';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, TYPESECTION, VIM_TYPES } from 'CommonModel';
+import { environment } from 'environment';
+import * as jsyaml from 'js-yaml';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+import { FEATURES, VIMLOCATION, VIMLOCATIONDATA } from 'VimAccountModel';
+
+/**
+ * Creating component
+ * @Component takes NewVimaccountComponent.html as template url
+ */
+@Component({
+ selector: 'app-new-vimaccount',
+ templateUrl: './NewVimaccountComponent.html',
+ styleUrls: ['./NewVimaccountComponent.scss']
+})
+/** Exporting a class @exports NewVimaccountComponent */
+export class NewVimaccountComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** FormGroup vim New Account added to the form @ html @public */
+ public vimNewAccountForm: FormGroup;
+
+ /** Supported Vim type for the dropdown */
+ public vimType: TYPESECTION[];
+
+ /** Supported Vim type for the dropdown */
+ public selectedVimType: string;
+
+ /** Supported true and false value for the dropdown */
+ public boolValue: {}[];
+
+ /** Form submission Add */
+ public submitted: boolean = false;
+
+ /** Showing more details of collapase */
+ public isCollapsed: boolean = false;
+
+ /** Vim location values @public */
+ public getVIMLocation: VIMLOCATIONDATA[] = [];
+
+ /** Check the Projects loading results @public */
+ public isLocationLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** set the longitude value of the selected place @public */
+ public setLong: number;
+
+ /** set the latitude value of the selected place @public */
+ public setLat: number;
+
+ /** Element ref for fileInput @public */
+ @ViewChild('fileInput', { static: true }) public fileInput: ElementRef;
+
+ /** Element ref for fileInput @public */
+ @ViewChild('fileInputLabel', { static: true }) public fileInputLabel: ElementRef;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** Holds the instance of router class @private */
+ private router: Router;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Contains configuration key variables @private */
+ private configKeys: string[] = [];
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.formBuilder = this.injector.get(FormBuilder);
+ this.router = this.injector.get(Router);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.sharedService = this.injector.get(SharedService);
+
+ /** Initializing Form Action */
+ this.vimNewAccountForm = this.formBuilder.group({
+ name: [null, Validators.required],
+ vim_type: [null, Validators.required],
+ vim_tenant_name: [null, Validators.required],
+ description: [null],
+ vim_url: [null, [Validators.required, Validators.pattern(this.sharedService.REGX_URL_PATTERN)]],
+ schema_type: [''],
+ vim_user: [null, Validators.required],
+ vim_password: [null, Validators.required],
+ vimconfig: this.paramsBuilder()
+ });
+ }
+
+ /** Generate params for config @public */
+ public paramsBuilder(): FormGroup {
+ return this.formBuilder.group({
+ use_existing_flavors: [null],
+ location: [null],
+ sdn_controller: [null],
+ APIversion: [null],
+ sdn_port_mapping: [null],
+ project_domain_id: [null],
+ vim_network_name: [null],
+ project_domain_name: [null],
+ config_vim_ype: [null],
+ user_domain_id: [null],
+ security_groups: [null],
+ user_domain_name: [null],
+ availabilityZone: [null],
+ keypair: [null],
+ region_name: [null],
+ dataplane_physical_net: [null],
+ insecure: [null],
+ use_floating_ip: [null],
+ microversion: [null],
+ use_internal_endpoint: [null],
+ additional_conf: [null],
+ orgname: [null],
+ vcenter_ip: [null],
+ vcenter_port: [null],
+ admin_username: [null],
+ vcenter_user: [null],
+ admin_password: [null],
+ vcenter_password: [null],
+ nsx_manager: [null],
+ vrops_site: [null],
+ nsx_user: [null],
+ vrops_user: [null],
+ nsx_password: [null],
+ vrops_password: [null],
+ vpc_cidr_block: [null],
+ flavor_info: [null],
+ subscription_id: [null],
+ resource_group: [null],
+ vnet_name: [null],
+ flavors_pattern: [null]
+ });
+ }
+
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.vimNewAccountForm.controls; }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.vimType = VIM_TYPES;
+ this.boolValue = [
+ { id: '', name: 'None' },
+ { id: true, name: 'True' },
+ { id: false, name: 'False' }
+ ];
+ this.headers = new HttpHeaders({
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ }
+
+ /** On modal submit newVimAccountSubmit will called @public */
+ public newVimAccountSubmit(): void {
+ this.submitted = true;
+ if (!this.vimNewAccountForm.invalid) {
+ this.isLocationLoadingResults = true;
+ this.configKeys.forEach((key: string) => {
+ this.vimNewAccountForm.controls.vimconfig.get(key).setValue(JSON.parse(this.vimNewAccountForm.controls.vimconfig.get(key).value));
+ });
+ this.sharedService.cleanForm(this.vimNewAccountForm);
+ Object.keys(this.vimNewAccountForm.value.vimconfig).forEach((key: string) => {
+ if (this.vimNewAccountForm.value.vimconfig[key] === undefined || this.vimNewAccountForm.value.vimconfig[key] === null ||
+ this.vimNewAccountForm.value.vimconfig[key] === '') {
+ delete this.vimNewAccountForm.value.vimconfig[key];
+ }
+ });
+ this.vimNewAccountForm.value.config = this.vimNewAccountForm.value.vimconfig;
+ delete this.vimNewAccountForm.value.vimconfig;
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.VIMACCOUNTS_URL,
+ httpOptions: { headers: this.headers }
+ };
+ this.restService.postResource(apiURLHeader, this.vimNewAccountForm.value)
+ .subscribe((result: {}) => {
+ this.notifierService.notify('success', this.translateService.instant('PAGE.VIM.CREATEDSUCCESSFULLY'));
+ this.isLocationLoadingResults = false;
+ this.router.navigate(['vim/details']).catch(() => {
+ // Error Cached;
+ });
+ // Post the New Vim data and reflect in the VIM Details Page.
+ }, (error: ERRORDATA) => {
+ this.configKeys.forEach((key: string) => {
+ this.vimNewAccountForm.controls.vimconfig.get(key)
+ .setValue(JSON.stringify(this.vimNewAccountForm.controls.vimconfig.get(key).value));
+ });
+ this.restService.handleError(error, 'post');
+ this.isLocationLoadingResults = false;
+ });
+ }
+ }
+
+ /** Routing to VIM Account Details Page @public */
+ public onVimAccountBack(): void {
+ this.router.navigate(['vim/details']).catch(() => {
+ // Error Cached
+ });
+ }
+
+ /** Fetching the location with name,latitude,longitude @public */
+ public fetchLocationLatLong(value: string): void {
+ this.isLocationLoadingResults = true;
+ const newVIMLocation: VIMLOCATIONDATA[] = [];
+ const locationTrack: string = environment.MAPLATLONGAPI_URL;
+ const locationAPIURL: string = locationTrack.replace('{value}', value);
+ this.restService.getResource(locationAPIURL).subscribe((result: VIMLOCATION) => {
+ result.features.forEach((getFeturesResult: FEATURES) => {
+ if ('extent' in getFeturesResult.properties) {
+ getFeturesResult.properties.extent.forEach((extentResult: number, index: number) => {
+ if (index === 0) {
+ this.setLong = extentResult;
+ }
+ if (index === 1) {
+ this.setLat = extentResult;
+ }
+ });
+ } else {
+ getFeturesResult.geometry.coordinates.forEach((coordinateResult: number, index: number) => {
+ if (index === 0) {
+ this.setLong = coordinateResult;
+ }
+ if (index === 1) {
+ this.setLat = coordinateResult;
+ }
+ });
+ }
+ newVIMLocation.push({
+ label: getFeturesResult.properties.name + ',' + getFeturesResult.properties.state + ', ' + getFeturesResult.properties.country,
+ value: getFeturesResult.properties.name + ',' + this.setLong + ',' + this.setLat
+ });
+ });
+ this.getVIMLocation = newVIMLocation;
+ this.isLocationLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLocationLoadingResults = false;
+ });
+ }
+
+ /** Drag and drop feature and fetchind the details of files @private */
+ public filesDropped(files: FileList): void {
+ this.configKeys = [];
+ if (files && files.length === 1) {
+ this.sharedService.getFileString(files, 'yaml').then((fileContent: string): void => {
+ const getJson: string = jsyaml.load(fileContent, { json: true });
+ Object.keys(getJson).forEach((item: string) => {
+ if (!isNullOrUndefined(this.vimNewAccountForm.controls.vimconfig.get(item))) {
+ if (typeof getJson[item] === 'object') {
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.vimNewAccountForm.controls.vimconfig.get(item).setValue(JSON.stringify(getJson[item]));
+ this.configKeys.push(item);
+ } else {
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.vimNewAccountForm.controls.vimconfig.get(item).setValue(getJson[item]);
+ }
+ }
+ });
+ }).catch((err: string): void => {
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ this.fileInputLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
+ this.fileInput.nativeElement.value = null;
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ this.fileInputLabel.nativeElement.innerText = files[0].name;
+ this.fileInput.nativeElement.value = null;
+ }
+}
diff --git a/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.html b/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.html
new file mode 100644
index 0000000..7feabe2
--- /dev/null
+++ b/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.html
@@ -0,0 +1,67 @@
+<!--
+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)
+-->
+<div class="row d-flex align-items-center justify-content-between">
+ <div class="col-sm-4">
+ <div class="d-flex align-items-center header-style">{{'VIMACCOUNTS' | translate}}</div>
+ </div>
+ <div class="col-sm-8 text-right">
+ <div class="btn-group list mr-2" role="group">
+ <button class="btn btn-primary" type="button" (click)="mapView()" placement="top" container="body"
+ ngbTooltip="{{'MAPVIEW' | translate}}" [hidden]="showList" [disabled]="vimData.length == 0">
+ <i class="fas fa-map-marker-alt"></i>
+ {{'MAPVIEW' | translate}}
+ </button>
+ <button class="btn btn-primary" type="button" (click)="listView()" placement="top" container="body"
+ ngbTooltip="{{'LISTVIEW' | translate}}" [hidden]="showMap">
+ <i class="fas fa-list"></i>
+ {{'LISTVIEW' | translate}}
+ </button>
+ </div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" placement="top" container="body"
+ ngbTooltip="{{'PAGE.VIMDETAILS.NEWVIM' | translate}}" (click)="composeVIM()">
+ <i class="fas fa-plus-circle" aria-hidden="true"></i>
+ {{'PAGE.VIMDETAILS.NEWVIM' | translate}}
+ </button>
+ </span>
+ </div>
+</div>
+<div class="row mt-2 mb-0 list-utilites-actions" [hidden]="showList">
+ <div class="col-auto mr-auto">
+ <nav class="custom-items-config">
+ <span><i class="fas fa-clock text-warning"></i>{{operationalStateFirstStep}}</span>
+ <span><i class="fas fa-check-circle text-success"></i>{{operationalStateSecondStep}}</span>
+ <span><i class="fas fa-times-circle text-danger"></i>{{operationalStateThirdStep}}</span>
+ </nav>
+ </div>
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1" [hidden]="showList">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<br>
+<div id="map" class="row map" [hidden]="showMap"></div>
+<div id="popup" class="ol-popup" [hidden]="!popupShow">
+ <button id="popup-closer" class="button-xs close m-1" type="button" aria-label="Close">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ <div id="popup-content" [innerHTML]="popupData"></div>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.scss b/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.scss
new file mode 100644
index 0000000..aeb086e
--- /dev/null
+++ b/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.scss
@@ -0,0 +1,73 @@
+/*
+ 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 '../../../assets/scss/mixins/mixin';
+@import '../../../assets/scss/variable';
+.map {
+ height: 60vh;
+ width: 100%;
+ .ol-popup {
+ @include background(null, $white, null, null, null);
+ -webkit-filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2));
+ filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2));
+ @include border(all, 1, solid, $gray-80);
+ @include position_value(absolute, $top: null, $right: null, $bottom: 12px, $left: -50px);
+ @include wh-value(280px, null);
+ @include font-style(normal);
+ @include font(null, .765625rem, 400);
+ @include line-height(1.5);
+ text-align: start;
+ text-decoration: none;
+ letter-spacing: normal;
+ word-break: normal;
+ word-spacing: normal;
+ white-space: normal;
+ line-break: auto;
+ word-wrap: break-word;
+ @include roundedCorners(2px);
+ &:after, &:before{
+ top: 100%;
+ @include border(all, 1, solid, transparent);
+ content: " ";
+ height: 0;
+ width: 0;
+ position: absolute;
+ pointer-events: none;
+ }
+ &:after {
+ border-top-color: $white;
+ border-width: 10px;
+ left: 48px;
+ margin-left: -10px;
+ }
+ &:before {
+ border-top-color: $gray-80;
+ border-width: 11px;
+ left: 48px;
+ margin-left: -11px;
+ }
+ }
+ .ol-popup-closer {
+ text-decoration: none;
+ position: absolute;
+ top: 2px;
+ right: 8px;
+ &:after {
+ content: "✖";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.ts b/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.ts
new file mode 100644
index 0000000..71455f5
--- /dev/null
+++ b/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.ts
@@ -0,0 +1,476 @@
+/*
+ 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 Vim Account Component.
+ */
+import { Component, Injector, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { TranslateService } from '@ngx-translate/core';
+import { CONFIGCONSTANT, CONSTANTNUMBER, ERRORDATA, VIM_TYPES } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { LocalDataSource } from 'ng2-smart-table';
+import { NSInstanceDetails } from 'NSInstanceModel';
+import Feature from 'ol/Feature';
+import Point from 'ol/geom/Point';
+import { defaults as defaultInteractions } from 'ol/interaction';
+import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';
+import Map from 'ol/Map';
+import Overlay from 'ol/Overlay';
+import { fromLonLat } from 'ol/proj.js';
+import OSM from 'ol/source/OSM';
+import VectorSource from 'ol/source/Vector';
+import { Icon, Style } from 'ol/style';
+import View from 'ol/View';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+import { VimAccountDetails, VIMData } from 'VimAccountModel';
+import { VimAccountsActionComponent } from 'VimAccountsAction';
+/**
+ * Creating component
+ * @Component takes VimAccountDetailsComponent.html as template url
+ */
+@Component({
+ selector: 'app-vim-account-details',
+ templateUrl: './VimAccountDetailsComponent.html',
+ styleUrls: ['./VimAccountDetailsComponent.scss']
+})
+/** Exporting a class @exports VimAccountDetailsComponent */
+export class VimAccountDetailsComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+ /** handle translate @public */
+ public translateService: TranslateService;
+ /** initially show the map container@public */
+ public showMap: boolean;
+ /** hide and show popup @public */
+ public popupShow: boolean = false;
+ /** Data of smarttable populate through LocalDataSource @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+ /** Columns list of the smart table @public */
+ public columnLists: object = {};
+ /** Settings for smarttable to populate the table with columns @public */
+ public settings: object = {};
+ /** initially hide the list@private */
+ public showList: boolean;
+ /** to store locations name @public */
+ public getLocation: GetLocation[];
+ /** Contains content for map popup @public */
+ public popupData: string;
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+ /** Formation of appropriate Data for LocalDatasource @public */
+ public vimData: VIMData[];
+ /** operational State init data @public */
+ public operationalStateFirstStep: string = CONFIGCONSTANT.vimOperationalStateFirstStep;
+ /** operational State running data @public */
+ public operationalStateSecondStep: string = CONFIGCONSTANT.vimOperationalStateStateSecondStep;
+ /** operational State failed data @public */
+ public operationalStateThirdStep: string = CONFIGCONSTANT.vimOperationalStateThirdStep;
+ /** NS Instances operational State failed data @public */
+ public nsinstancesoperationalStateRunning: string = CONFIGCONSTANT.operationalStateSecondStep;
+ /** Instance of the rest service @private */
+ private restService: RestService;
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+ /** Holds the instance of router class @private */
+ private router: Router;
+ /** ns INstance Data @private */
+ private nsData: NSInstanceDetails[];
+ /** map object @private */
+ private map: Map;
+ /** used to bind marker @private */
+ private vectorSource: VectorSource;
+ /** used to bind vectorSource @private */
+ private vectorLayer: VectorLayer;
+ /** marker @private */
+ private marker: Feature;
+ /** latitude @private */
+ private lat: number;
+ /** longitude @private */
+ private lng: number;
+ /** each vector layer of marker is pushed to layers array @private */
+ private layers: VectorLayer[] = [];
+ /** locationData @private */
+ private locationData: VimAccountDetails[];
+ /** popup array @private */
+ private overLay: Overlay[] = [];
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.sharedService = this.injector.get(SharedService);
+ this.router = this.injector.get(Router);
+ this.translateService = this.injector.get(TranslateService);
+ }
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.osmMapView();
+ this.listView();
+ this.generateColumns();
+ this.generateSettings();
+ this.generateData();
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** smart table Header Colums @public */
+ public generateColumns(): void {
+ this.columnLists = {
+ name: { title: this.translateService.instant('NAME'), width: '15%', sortDirection: 'asc' },
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '25%' },
+ type: {
+ title: this.translateService.instant('TYPE'), width: '15%',
+ filter: {
+ type: 'list',
+ config: {
+ selectText: 'Select',
+ list: VIM_TYPES
+ }
+ }
+ },
+ operationalState: {
+ title: this.translateService.instant('OPERATIONALSTATUS'), width: '15%', type: 'html',
+ filter: {
+ type: 'list',
+ config: {
+ selectText: 'Select',
+ list: [
+ { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep },
+ { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep },
+ { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep }
+ ]
+ }
+ },
+ valuePrepareFunction: (cell: VIMData, row: VIMData): string => {
+ if (row.operationalState === this.operationalStateFirstStep) {
+ return `<span class="icon-label" title="${row.operationalState}">
+ <i class="fas fa-clock text-warning"></i>
+ </span>`;
+ } else if (row.operationalState === this.operationalStateSecondStep) {
+ return `<span class="icon-label" title="${row.operationalState}">
+ <i class="fas fa-check-circle text-success"></i>
+ </span>`;
+ } else if (row.operationalState === this.operationalStateThirdStep) {
+ return `<span class="icon-label" title="${row.operationalState}">
+ <i class="fas fa-times-circle text-danger"></i>
+ </span>`;
+ } else {
+ return `<span>${row.operationalState}</span>`;
+ }
+ }
+ },
+ description: { title: this.translateService.instant('DESCRIPTION'), width: '25%' },
+ Actions: {
+ name: 'Action', width: '5%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom',
+ valuePrepareFunction: (cell: VIMData, row: VIMData): VIMData => row,
+ renderComponent: VimAccountsActionComponent
+ }
+ };
+ }
+
+ /** smart table Data Settings @public */
+ public generateSettings(): void {
+ this.settings = {
+ edit: {
+ editButtonContent: '<i class="fa fa-edit" title="Edit"></i>',
+ confirmSave: true
+ },
+ delete: {
+ deleteButtonContent: '<i class="far fa-trash-alt" title="delete"></i>',
+ confirmDelete: true
+ },
+ columns: this.columnLists,
+ actions: {
+ add: false,
+ edit: false,
+ delete: false,
+ position: 'right'
+ },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** smart table listing manipulation @public */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** smart table listing manipulation @public */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'vim-account' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** on Navigate to Composer Page @public */
+ public composeVIM(): void {
+ this.router.navigate(['vim/new']).catch(() => {
+ //empty block
+ });
+ }
+
+ /** To show map conatainer @public */
+ public mapView(): void {
+ this.showList = true;
+ this.showMap = false;
+ }
+ /** To show listview @public */
+ public listView(): void {
+ this.showMap = true;
+ this.showList = false;
+ }
+ /** Load the datasource appropriatetype @public */
+ public loadDatasource(getdata: VIMData[]): void {
+ if (getdata.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(getdata).then((data: boolean) => {
+ //empty block
+ }).catch(() => {
+ //empty block
+ });
+ }
+
+ /** Generate generateVIMData object from loop and return for the datasource @public */
+ public generateVIMData(vimAccountData: VimAccountDetails): VIMData {
+ return {
+ name: vimAccountData.name,
+ identifier: vimAccountData._id,
+ type: vimAccountData.vim_type,
+ operationalState: vimAccountData._admin.operationalState,
+ description: vimAccountData.description,
+ instancesData: this.nsData
+ };
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+
+ /** Fetching the data from server to Load in the smarttable @protected */
+ private generateData(): void {
+ this.isLoadingResults = true;
+ this.vimData = [];
+ this.getNSData().then((): void => {
+ this.restService.getResource(environment.VIMACCOUNTS_URL).subscribe((vimAccountsData: VimAccountDetails[]) => {
+ this.locationData = vimAccountsData;
+ vimAccountsData.forEach((vimAccountData: VimAccountDetails) => {
+ const vimDataObj: VIMData = this.generateVIMData(vimAccountData);
+ this.vimData.push(vimDataObj);
+ });
+ this.loadDatasource(this.vimData);
+ this.removeLayersOverLay();
+ this.arrayOfLocation();
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }).catch((error: ERRORDATA): void => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+
+ /** fetching the nsdata @private */
+ private async getNSData(): Promise<Boolean> {
+ return new Promise<Boolean>((resolve: Function, reject: Function): void => {
+ this.nsData = [];
+ this.restService.getResource(environment.NSDINSTANCES_URL).subscribe((nsdInstancesData: NSInstanceDetails[]) => {
+ const nsRunningInstancesData: NSInstanceDetails[] = nsdInstancesData.filter((instancesData: NSInstanceDetails) =>
+ instancesData['operational-status'] === this.nsinstancesoperationalStateRunning);
+ this.nsData = nsRunningInstancesData;
+ resolve(true);
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ resolve(true);
+ });
+ });
+ }
+
+ /** create map view @private */
+ private osmMapView(): void {
+ this.map = new Map({
+ target: 'map',
+ layers: [new TileLayer({
+ source: new OSM()
+ })],
+ interactions: defaultInteractions({
+ mouseWheelZoom: true
+ }),
+ view: new View({
+ center: fromLonLat([CONSTANTNUMBER.osmapviewlong, CONSTANTNUMBER.osmapviewlat]),
+ zoom: 3
+ })
+ });
+ }
+
+ /** remove the layers and overlay @private */
+ private removeLayersOverLay(): void {
+ this.layers.forEach((layer: VectorLayer) => {
+ this.map.removeLayer(layer);
+ });
+ this.overLay.forEach((lay: Overlay) => {
+ this.map.removeOverlay(lay);
+ });
+ }
+
+ /** filter locations from vimaccounts @private */
+ private arrayOfLocation(): void {
+ this.getLocation = [];
+ this.locationData.filter((item: VimAccountDetails) => {
+ if (item.hasOwnProperty('config')) {
+ if (item.config.hasOwnProperty('location')) {
+ this.getLocation.push({ name: item.name, location: item.config.location, id: item._id });
+ }
+ }
+ });
+ if (this.getLocation !== []) {
+ this.getLocation.filter((loc: GetLocation) => {
+ if (loc.location !== '') {
+ const getLatLong: string[] = loc.location.split(',');
+ this.lng = +getLatLong[CONSTANTNUMBER.splitLongitude];
+ this.lat = +getLatLong[CONSTANTNUMBER.splitLatitude];
+ this.addMarker(getLatLong[0], loc.id, loc.name);
+ }
+ });
+ }
+ }
+ /** add markers on map @private */
+ private addMarker(loc: string, id: string, name: string): void {
+ const container: HTMLElement = document.getElementById('popup');
+ const closer: HTMLElement = document.getElementById('popup-closer');
+ this.popupShow = true;
+ const overlay: Overlay = this.addOverlay(container);
+ this.marker = this.addFeature(loc, id, name);
+ this.setStyleMarker();
+ this.setVectorSource();
+ this.setVectorLayer();
+ this.map.addLayer(this.vectorLayer);
+ this.layers.push(this.vectorLayer);
+ if (this.layers.length === 1) {
+ this.markerClickEvent(closer, overlay);
+ }
+ }
+ /** Create an overlay to anchor the popup to the map @private */
+ private addOverlay(container: HTMLElement): Overlay {
+ return new Overlay({
+ element: container,
+ autoPan: true,
+ autoPanAnimation: {
+ duration: 250
+ }
+ });
+ }
+ /** Return the Feature of creating a marker in the map @private */
+ private addFeature(loc: string, id: string, name: string): Feature {
+ return new Feature({
+ geometry: new Point(fromLonLat([this.lng, this.lat])),
+ location: loc,
+ Id: id,
+ vimName: name
+ });
+ }
+ /** Set the style of the marker @private */
+ private setStyleMarker(): void {
+ this.marker.setStyle(new Style({
+ image: new Icon(({
+ crossOrigin: 'anonymous',
+ src: 'assets/images/map-icon.png'
+ }))
+ }));
+ }
+ /** Set the map vector source @private */
+ private setVectorSource(): void {
+ this.vectorSource = new VectorSource({
+ features: [this.marker]
+ });
+ }
+ /** Set the map vector layer @private */
+ private setVectorLayer(): void {
+ this.vectorLayer = new VectorLayer({
+ source: this.vectorSource
+ });
+ }
+ /** Add a click handler to the map to render the popup. @private */
+ private markerClickEvent(closer: HTMLElement, overlay: Overlay): void {
+ // tslint:disable-next-line: no-any
+ this.map.on('singleclick', (evt: any) => {
+ const feature: Feature = this.map.forEachFeatureAtPixel(evt.pixel,
+ (f: Feature) => {
+ return f;
+ });
+ if (feature) {
+ this.setCoordinates(feature, overlay);
+ } else {
+ this.map.removeOverlay(overlay);
+ }
+ });
+ /** Handle close event for overlay */
+ closer.onclick = (): boolean => {
+ overlay.setPosition(undefined);
+ closer.blur();
+ return false;
+ };
+ }
+ /** Set the coordinates point if the feature is available @private */
+ // tslint:disable-next-line: no-any
+ private setCoordinates(feature: any, overlay: Overlay): void {
+ this.popupData = '';
+ this.popupData += '<h3 class="popover-header">' + feature.values_.vimName + '</h3>';
+ this.popupData += '<ul class="list-unstyled m-2">';
+ const instnaceData: NSInstanceDetails[] = this.nsData.filter((item: NSInstanceDetails) => {
+ if (item.datacenter === feature.values_.Id) {
+ this.popupData += '<li class="m-2"><a class="d-block text-truncate" target="_parent" href="instances/ns/' + item._id + '">'
+ + item.name + '</a></li>';
+ return item;
+ }
+ });
+ if (instnaceData.length === 0) {
+ this.popupData += '<li class="m-2">' + this.translateService.instant('PAGE.DASHBOARD.NOINSTANCES') + '</li>';
+ }
+ this.popupData += '</ul>';
+ const coordinates: number[] = feature.getGeometry().getCoordinates();
+ overlay.setPosition(coordinates);
+ this.overLay.push(overlay);
+ this.map.addOverlay(overlay);
+ }
+}
+
+/** interface for get location */
+interface GetLocation {
+ location: string;
+ id: string;
+ name?: string;
+}
diff --git a/src/app/wim-accounts/WIMAccountsComponent.html b/src/app/wim-accounts/WIMAccountsComponent.html
new file mode 100644
index 0000000..3f96ff8
--- /dev/null
+++ b/src/app/wim-accounts/WIMAccountsComponent.html
@@ -0,0 +1,18 @@
+<!--
+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)
+-->
+<router-outlet></router-outlet>
\ No newline at end of file
diff --git a/src/app/wim-accounts/WIMAccountsComponent.scss b/src/app/wim-accounts/WIMAccountsComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/wim-accounts/WIMAccountsComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/wim-accounts/WIMAccountsComponent.ts b/src/app/wim-accounts/WIMAccountsComponent.ts
new file mode 100644
index 0000000..5914f30
--- /dev/null
+++ b/src/app/wim-accounts/WIMAccountsComponent.ts
@@ -0,0 +1,55 @@
+/*
+ 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 WIM Account Component.
+ */
+import { Component, Injector } from '@angular/core';
+import { Router, RouterEvent } from '@angular/router';
+
+/**
+ * Creating component
+ * @Component takes WIMAccountsComponent.html as template url
+ */
+@Component({
+ templateUrl: './WIMAccountsComponent.html',
+ styleUrls: ['./WIMAccountsComponent.scss']
+})
+/** Exporting a class @exports WIMAccountsComponent */
+export class WIMAccountsComponent {
+ /** Invoke service injectors @public */
+ public injector: Injector;
+
+ /** Holds teh instance of AuthService class of type AuthService @private */
+ private router: Router;
+
+ // creates packages component
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.router = this.injector.get(Router);
+ this.router.events.subscribe((event: RouterEvent) => {
+ this.redirectToList(event.url);
+ });
+ }
+
+ /** Return to list NS Package List */
+ public redirectToList(getURL: string): void {
+ if (getURL === '/wim') {
+ this.router.navigate(['/wim/details']).catch();
+ }
+ }
+}
diff --git a/src/app/wim-accounts/WIMAccountsModule.ts b/src/app/wim-accounts/WIMAccountsModule.ts
new file mode 100644
index 0000000..088502f
--- /dev/null
+++ b/src/app/wim-accounts/WIMAccountsModule.ts
@@ -0,0 +1,77 @@
+/*
+ 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 WIM Account module.
+ */
+import { CommonModule } from '@angular/common';
+import { HttpClientModule } from '@angular/common/http';
+import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterModule, Routes } from '@angular/router';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { TranslateModule } from '@ngx-translate/core';
+import { DataService } from 'DataService';
+import { LoaderModule } from 'LoaderModule';
+import { NewWIMAccountComponent } from 'NewWIMAccount';
+import { Ng2SmartTableModule } from 'ng2-smart-table';
+import { PagePerRowModule } from 'PagePerRowModule';
+import { PageReloadModule } from 'PageReloadModule';
+import { WIMAccountDetailsComponent } from 'WIMAccountDetails';
+import { WIMAccountInfoComponent } from 'WIMAccountInfo';
+import { WIMAccountsComponent } from 'WIMAccountsComponent';
+
+/** To halndle project information */
+const projectInfo: {} = { title: '{project}', url: '/' };
+
+/** const values for dashboard Routes */
+const routes: Routes = [
+ {
+ path: '',
+ component: WIMAccountsComponent,
+ children: [
+ {
+ path: 'details',
+ data: {
+ breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' },
+ projectInfo, { title: 'WIMACCOUNTS', url: null }]
+ },
+ component: WIMAccountDetailsComponent
+ }
+ ]
+ }
+];
+
+/**
+ * Creating @NgModule component for Modules
+ */
+@NgModule({
+ imports: [ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }), FormsModule, CommonModule,
+ HttpClientModule, NgSelectModule, Ng2SmartTableModule, TranslateModule, RouterModule.forChild(routes), NgbModule,
+ PagePerRowModule, LoaderModule, PageReloadModule],
+ declarations: [WIMAccountsComponent, WIMAccountInfoComponent, WIMAccountDetailsComponent, NewWIMAccountComponent],
+ providers: [DataService],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
+ entryComponents: [WIMAccountInfoComponent, NewWIMAccountComponent]
+})
+/** Exporting a class @exports WIMAccountsModule */
+export class WIMAccountsModule {
+ /** Variables declared to avoid state-less class */
+ private wimModule: string;
+}
diff --git a/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.html b/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.html
new file mode 100644
index 0000000..a97107f
--- /dev/null
+++ b/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.html
@@ -0,0 +1,97 @@
+<!--
+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)
+-->
+<form [formGroup]="wimNewAccountForm" (ngSubmit)="newWimAccountSubmit()" autocomplete="off">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'PAGE.WIMACCOUNTS.NEWWIM' | translate}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+ </div>
+ <div class="modal-body modal-body-custom-height">
+ <div class="form-group row">
+ <label class="col-sm-12 col-form-label mandatory-label"
+ [ngClass]="{'text-danger': wimNewAccountForm.invalid === true && submitted === true}">{{'MANDATORYCHECK' | translate}}</label>
+ <label class="col-sm-4 col-form-label" for="name">{{'NAME' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control" placeholder="{{'NAME' | translate}}" type="text"
+ formControlName="name" id="name" [ngClass]="{ 'is-invalid': submitted && f.name.errors }" required>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="wim_type">{{'TYPE' | translate}}*</label>
+ <div class="col-sm-8">
+ <ng-select bindLabel="title" bindValue="value" [items]="wimType" placeholder="{{'SELECT' | translate}}"
+ formControlName="wim_type" id="wim_type" [(ngModel)]="wimTypeMod"
+ [ngClass]="{ 'is-invalid': submitted && f.wim_type.errors }" required>
+ </ng-select>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="wim_url">{{'URL' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control" placeholder="{{'URL' | translate}}" type="url"
+ formControlName="wim_url" id="wim_url" [ngClass]="{ 'is-invalid': submitted && f.wim_url.errors }"
+ required>
+ <div *ngIf="wimNewAccountForm.invalid" class="invalid-feedback">
+ <div *ngIf="f.wim_url.errors && f.wim_url.value">{{'DOMVALIDATIONS.INVALIDURL' | translate}}</div>
+ </div>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="user">{{'PAGE.WIMACCOUNTS.USERNAME' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control" placeholder="{{'PAGE.WIMACCOUNTS.USERNAME' | translate}}"
+ type="text" formControlName="user" id="user"
+ [ngClass]="{ 'is-invalid': submitted && f.user.errors }" required>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="password">{{'PAGE.WIMACCOUNTS.PASSWORD' | translate}}*</label>
+ <div class="col-sm-8">
+ <input autocomplete="off" class="form-control" placeholder="{{'PAGE.WIMACCOUNTS.PASSWORD' | translate}}"
+ type="password" formControlName="password" id="password"
+ [ngClass]="{ 'is-invalid': submitted && f.password.errors }" required>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="description">{{'DESCRIPTION' | translate}}</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'DESCRIPTION' | translate}}" type="text"
+ formControlName="description" id="description"></textarea>
+ </div>
+ </div>
+ <div class="form-group row">
+ <label class="col-sm-4 col-form-label" for="config">{{'CONFIG' | translate}}</label>
+ <div class="col-sm-8">
+ <textarea class="form-control" placeholder="{{'YAMLCONFIG' | translate}}" formControlName="config"
+ id="config"></textarea>
+ <div class="fileupload-text mt-1 mb-1">{{'FILEUPLOADLABEL' | translate}}</div>
+ <div class="custom-file">
+ <input type="file" #fileInputConfig class="custom-file-input"
+ (change)="configFile($event.target.files)" id="customFile">
+ <label class="custom-file-label" #fileInputConfigLabel for="customFile">{{'CHOOSEFILE' | translate}}</label>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+ <button type="submit" class="btn btn-primary">{{'CREATE' | translate}}</button>
+ </div>
+</form>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.scss b/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.ts b/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.ts
new file mode 100644
index 0000000..7587b58
--- /dev/null
+++ b/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.ts
@@ -0,0 +1,189 @@
+/*
+ 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 WIM Account Component.
+ */
+import { HttpHeaders } from '@angular/common/http';
+import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
+import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { NotifierService } from 'angular-notifier';
+import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA, TYPESECTION, WIM_TYPES } from 'CommonModel';
+import { environment } from 'environment';
+import * as jsyaml from 'js-yaml';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { isNullOrUndefined } from 'util';
+
+/**
+ * Creating component
+ * @Component takes NewWIMAccountComponent.html as template url
+ */
+@Component({
+ templateUrl: './NewWIMAccountComponent.html',
+ styleUrls: ['./NewWIMAccountComponent.scss']
+})
+/** Exporting a class @exports NewWIMAccountComponent */
+export class NewWIMAccountComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Setting wim types in array @public */
+ public wimType: TYPESECTION[];
+
+ /** Set WIM Type select to empty @public */
+ public wimTypeMod: string = null;
+
+ /** New WIM account form controls using formgroup @public */
+ public wimNewAccountForm: FormGroup;
+
+ /** Form submission Add */
+ public submitted: boolean = false;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Check the loading results for loader status @public */
+ public isLoadingResults: boolean = false;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Element ref for fileInputConfig @public */
+ @ViewChild('fileInputConfig', { static: true }) public fileInputConfig: ElementRef;
+
+ /** Element ref for fileInputConfig @public */
+ @ViewChild('fileInputConfigLabel', { static: true }) public fileInputConfigLabel: ElementRef;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** Controls the header form @private */
+ private headers: HttpHeaders;
+
+ /** FormBuilder instance added to the formBuilder @private */
+ private formBuilder: FormBuilder;
+
+ /** Notifier service to popup notification @private */
+ private notifierService: NotifierService;
+
+ /** Contains tranlsate instance @private */
+ private translateService: TranslateService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.formBuilder = this.injector.get(FormBuilder);
+ this.notifierService = this.injector.get(NotifierService);
+ this.translateService = this.injector.get(TranslateService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.sharedService = this.injector.get(SharedService);
+
+ /** Initializing Form Action */
+ this.wimNewAccountForm = this.formBuilder.group({
+ name: ['', Validators.required],
+ wim_type: ['', Validators.required],
+ wim_url: ['', [Validators.required, Validators.pattern(this.sharedService.REGX_URL_PATTERN)]],
+ user: ['', Validators.required],
+ password: ['', Validators.required],
+ description: [null],
+ config: [null]
+ });
+ }
+
+ /** convenience getter for easy access to form fields */
+ get f(): FormGroup['controls'] { return this.wimNewAccountForm.controls; }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.wimType = WIM_TYPES;
+ this.headers = new HttpHeaders({
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
+ });
+ }
+
+ /** On modal submit newWimAccountSubmit will called @public */
+ public newWimAccountSubmit(): void {
+ this.submitted = true;
+ const modalData: MODALCLOSERESPONSEDATA = {
+ message: 'Done'
+ };
+ this.sharedService.cleanForm(this.wimNewAccountForm);
+ if (this.wimNewAccountForm.value.description === '') {
+ this.wimNewAccountForm.value.description = null;
+ }
+ if (isNullOrUndefined(this.wimNewAccountForm.value.config) || this.wimNewAccountForm.value.config === '') {
+ delete this.wimNewAccountForm.value.config;
+ } else {
+ const validJSON: boolean = this.sharedService.checkJson(this.wimNewAccountForm.value.config);
+ if (validJSON) {
+ this.wimNewAccountForm.value.config = JSON.parse(this.wimNewAccountForm.value.config);
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('INVALIDCONFIG'));
+ return;
+ }
+ }
+ if (!this.wimNewAccountForm.invalid) {
+ this.isLoadingResults = true;
+ const apiURLHeader: APIURLHEADER = {
+ url: environment.WIMACCOUNTS_URL,
+ httpOptions: { headers: this.headers }
+ };
+ this.restService.postResource(apiURLHeader, this.wimNewAccountForm.value)
+ .subscribe((result: {}) => {
+ this.activeModal.close(modalData);
+ this.isLoadingResults = false;
+ this.notifierService.notify('success', this.translateService.instant('PAGE.WIMACCOUNTS.CREATEDSUCCESSFULLY'));
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'post');
+ this.isLoadingResults = false;
+ });
+ }
+ }
+
+ /** Config file process @private */
+ public configFile(files: FileList): void {
+ if (files && files.length === 1) {
+ this.sharedService.getFileString(files, 'yaml').then((fileContent: string): void => {
+ const getConfigJson: string = jsyaml.load(fileContent, { json: true });
+ // tslint:disable-next-line: no-backbone-get-set-outside-model
+ this.wimNewAccountForm.get('config').setValue(JSON.stringify(getConfigJson));
+ }).catch((err: string): void => {
+ if (err === 'typeError') {
+ this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR'));
+ } else {
+ this.notifierService.notify('error', this.translateService.instant('ERROR'));
+ }
+ this.fileInputConfigLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE');
+ this.fileInputConfig.nativeElement.value = null;
+ });
+ } else if (files && files.length > 1) {
+ this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION'));
+ }
+ this.fileInputConfigLabel.nativeElement.innerText = files[0].name;
+ this.fileInputConfig.nativeElement.value = null;
+ }
+}
diff --git a/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.html b/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.html
new file mode 100644
index 0000000..2c2d839
--- /dev/null
+++ b/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.html
@@ -0,0 +1,42 @@
+<!--
+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)
+-->
+<div class="row d-flex flex-row justify-content-between">
+ <div class="d-flex align-items-center header-style">{{'WIMACCOUNTS' | translate}}</div>
+ <span class="button">
+ <button class="btn btn-primary" type="button" placement="top" container="body" ngbTooltip="{{'PAGE.WIMACCOUNTS.NEWWIM' | translate}}"
+ (click)="composeWIM()">
+ <i class="fas fa-plus-circle" aria-hidden="true"></i> {{'PAGE.WIMACCOUNTS.NEWWIM' | translate}}
+ </button>
+ </span>
+</div>
+<div class="row mt-2 mb-0 list-utilites-actions">
+ <div class="col-auto mr-auto">
+ <nav class="custom-items-config">
+ <span><i class="fas fa-clock text-warning"></i>{{operationalStateFirstStep}}</span>
+ <span><i class="fas fa-check-circle text-success"></i>{{operationalStateSecondStep}}</span>
+ <span><i class="fas fa-times-circle text-danger"></i>{{operationalStateThirdStep}}</span>
+ </nav>
+ </div>
+ <page-per-row class="mr-2" (pagePerRow)="onChange($event)"></page-per-row>
+ <page-reload></page-reload>
+</div>
+<div class="smarttable-style bg-white mt-1">
+ <ng2-smart-table [ngClass]="checkDataClass" [settings]="settings" [source]="dataSource" (userRowSelect)="onUserRowSelect($event)">
+ </ng2-smart-table>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
\ No newline at end of file
diff --git a/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.scss b/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.ts b/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.ts
new file mode 100644
index 0000000..b1d16f7
--- /dev/null
+++ b/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.ts
@@ -0,0 +1,246 @@
+/*
+ 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 WIM Account Component.
+ */
+import { Component, Injector, OnDestroy, OnInit } from '@angular/core';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+import { CONFIGCONSTANT, ERRORDATA, MODALCLOSERESPONSEDATA, WIM_TYPES } from 'CommonModel';
+import { DataService } from 'DataService';
+import { environment } from 'environment';
+import { NewWIMAccountComponent } from 'NewWIMAccount';
+import { LocalDataSource } from 'ng2-smart-table';
+import { RestService } from 'RestService';
+import { Subscription } from 'rxjs';
+import { SharedService } from 'SharedService';
+import { WIMAccountData, WIMAccountModel } from 'WIMAccountModel';
+import { WIMAccountsActionComponent } from 'WIMAccountsAction';
+/**
+ * Creating component
+ * @Component takes WIMAccountDetailsComponent.html as template url
+ */
+@Component({
+ templateUrl: './WIMAccountDetailsComponent.html',
+ styleUrls: ['./WIMAccountDetailsComponent.scss']
+})
+/** Exporting a class @exports WIMAccountDetailsComponent */
+export class WIMAccountDetailsComponent implements OnInit, OnDestroy {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** handle translate @public */
+ public translateService: TranslateService;
+
+ /** Data of smarttable populate through LocalDataSource @public */
+ public dataSource: LocalDataSource = new LocalDataSource();
+
+ /** Columns list of the smart table @public */
+ public columnLists: object = {};
+
+ /** Settings for smarttable to populate the table with columns @public */
+ public settings: object = {};
+
+ /** Check the loading results @public */
+ public isLoadingResults: boolean = true;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Class for empty and present data @public */
+ public checkDataClass: string;
+
+ /** operational State init data @public */
+ public operationalStateFirstStep: string = CONFIGCONSTANT.wimOperationalStateFirstStep;
+
+ /** operational State running data @public */
+ public operationalStateSecondStep: string = CONFIGCONSTANT.wimOperationalStateStateSecondStep;
+
+ /** operational State failed data @public */
+ public operationalStateThirdStep: string = CONFIGCONSTANT.wimOperationalStateThirdStep;
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** dataService to pass the data from one component to another @private */
+ private dataService: DataService;
+
+ /** Formation of appropriate Data for LocalDatasource @private */
+ private wimData: {}[] = [];
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ /** Instance of the modal service @private */
+ private modalService: NgbModal;
+
+ /** Instance of subscriptions @private */
+ private generateDataSub: Subscription;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.dataService = this.injector.get(DataService);
+ this.sharedService = this.injector.get(SharedService);
+ this.translateService = this.injector.get(TranslateService);
+ this.modalService = this.injector.get(NgbModal);
+ }
+ /** Lifecyle Hooks the trigger before component is instantiate @public */
+ public ngOnInit(): void {
+ this.generateColumns();
+ this.generateSettings();
+ this.generateData();
+ this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); });
+ }
+
+ /** smart table Header Colums @public */
+ public generateColumns(): void {
+ this.columnLists = {
+ name: { title: this.translateService.instant('NAME'), width: '20%', sortDirection: 'asc' },
+ identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' },
+ type: {
+ title: this.translateService.instant('TYPE'), width: '15%',
+ filter: {
+ type: 'list',
+ config: {
+ selectText: 'Select',
+ list: WIM_TYPES
+ }
+ }
+ },
+ operationalState: {
+ title: this.translateService.instant('OPERATIONALSTATUS'), width: '15%', type: 'html',
+ filter: {
+ type: 'list',
+ config: {
+ selectText: 'Select',
+ list: [
+ { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep },
+ { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep },
+ { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep }
+ ]
+ }
+ },
+ valuePrepareFunction: (cell: WIMAccountData, row: WIMAccountData): string => {
+ if (row.operationalState === this.operationalStateFirstStep) {
+ return `<span class="icon-label" title="${row.operationalState}">
+ <i class="fas fa-clock text-warning"></i>
+ </span>`;
+ } else if (row.operationalState === this.operationalStateSecondStep) {
+ return `<span class="icon-label" title="${row.operationalState}">
+ <i class="fas fa-check-circle text-success"></i>
+ </span>`;
+ } else if (row.operationalState === this.operationalStateThirdStep) {
+ return `<span class="icon-label" title="${row.operationalState}">
+ <i class="fas fa-times-circle text-danger"></i>
+ </span>`;
+ } else {
+ return `<span>${row.operationalState}</span>`;
+ }
+ }
+ },
+ description: { title: this.translateService.instant('DESCRIPTION'), width: '25%' },
+ Actions: {
+ name: 'Action', width: '5%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom',
+ valuePrepareFunction: (cell: WIMAccountData, row: WIMAccountData): WIMAccountData => row,
+ renderComponent: WIMAccountsActionComponent
+ }
+ };
+ }
+
+ /** smart table Data Settings @public */
+ public generateSettings(): void {
+ this.settings = {
+ edit: {
+ editButtonContent: '<i class="fa fa-edit" title="Edit"></i>', confirmSave: true
+ },
+ delete: {
+ deleteButtonContent: '<i class="far fa-trash-alt" title="delete"></i>', confirmDelete: true
+ },
+ columns: this.columnLists,
+ actions: {
+ add: false, edit: false, delete: false, position: 'right'
+ },
+ attr: this.sharedService.tableClassConfig(),
+ pager: this.sharedService.paginationPagerConfig(),
+ noDataMessage: this.translateService.instant('NODATAMSG')
+ };
+ }
+
+ /** smart table listing manipulation @public */
+ public onChange(perPageValue: number): void {
+ this.dataSource.setPaging(1, perPageValue, true);
+ }
+
+ /** smart table listing manipulation @public */
+ public onUserRowSelect(event: MessageEvent): void {
+ Object.assign(event.data, { page: 'wim-account' });
+ this.dataService.changeMessage(event.data);
+ }
+
+ /** Compose new WIM Accounts @public */
+ public composeWIM(): void {
+ const modalRef: NgbModalRef = this.modalService.open(NewWIMAccountComponent, { backdrop: 'static' });
+ modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
+ if (result) {
+ this.sharedService.callData();
+ }
+ }).catch();
+ }
+
+ /** Generate generateWIMData object from loop and return for the datasource @public */
+ public generateWIMData(wimAccountData: WIMAccountModel): WIMAccountData {
+ return {
+ name: wimAccountData.name,
+ identifier: wimAccountData._id,
+ type: wimAccountData.wim_type,
+ operationalState: wimAccountData._admin.operationalState,
+ description: wimAccountData.description
+ };
+ }
+
+ /**
+ * Lifecyle hook which get trigger on component destruction
+ */
+ public ngOnDestroy(): void {
+ this.generateDataSub.unsubscribe();
+ }
+
+ /** Fetching the data from server to Load in the smarttable @protected */
+ protected generateData(): void {
+ this.isLoadingResults = true;
+ this.wimData = [];
+ this.restService.getResource(environment.WIMACCOUNTS_URL).subscribe((wimAccountsDetails: {}[]) => {
+ wimAccountsDetails.forEach((wimAccountsData: WIMAccountModel) => {
+ const wimDataObj: WIMAccountData = this.generateWIMData(wimAccountsData);
+ this.wimData.push(wimDataObj);
+ });
+ if (this.wimData.length > 0) {
+ this.checkDataClass = 'dataTables_present';
+ } else {
+ this.checkDataClass = 'dataTables_empty';
+ }
+ this.dataSource.load(this.wimData).then((data: {}) => {
+ this.isLoadingResults = false;
+ }).catch();
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+}
diff --git a/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.html b/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.html
new file mode 100644
index 0000000..25126b8
--- /dev/null
+++ b/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.html
@@ -0,0 +1,84 @@
+<!--
+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)
+-->
+<div class="modal-header">
+ <h4 class="modal-title" id="modal-basic-title">{{'PAGE.WIMACCOUNTS.WIMDETAILS' | translate}}</h4>
+ <button class="button-xs" type="button" class="close" aria-label="Close" (click)="activeModal.close()">
+ <i class="fas fa-times-circle text-danger"></i>
+ </button>
+</div>
+<div class="modal-body modal-body-custom-height p-0">
+ <table class="table table-striped table-layout-fixed mb-0" *ngIf="wimDetails">
+ <tr>
+ <td colspan="2">
+ <b>{{'ID' | translate}}:</b> {{(wimDetails._id)?wimDetails._id:''}}
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>{{'DETAILEDSTATUS' | translate}}:</b> {{(wimDetails._admin['detailed-status'])?wimDetails._admin['detailed-status']:''}}
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <b>{{'OPERATIONALSTATE' | translate}}:</b><span class="ml-1 badge" [ngClass]="{'badge-info':wimDetails._admin.operationalState === operationalStateFirstStep,
+ 'badge-success':wimDetails._admin.operationalState === operationalStateSecondStep,
+ 'badge-danger':wimDetails._admin.operationalState === operationalStateThirdStep}">
+ {{(wimDetails._admin.operationalState)?wimDetails._admin.operationalState:''}}
+ </span>
+ </td>
+ <td>
+ <b>{{'PAGE.WIMACCOUNTS.SCHEMAVERSION' | translate}}:</b> {{(wimDetails.schema_version)?wimDetails.schema_version:''}}
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <b>{{'NAME' | translate}}:</b> {{(wimDetails.name)?wimDetails.name:''}}
+ </td>
+ <td>
+ <b>{{'TYPE' | translate}}:</b> {{(wimDetails.wim_type)?wimDetails.wim_type:''}}
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <b>{{'URL' | translate}}:</b> {{(wimDetails.wim_url)?wimDetails.wim_url:''}}
+ </td>
+ <td>
+ <b>{{'DESCRIPTION' | translate}}:</b> {{(wimDetails.description)?wimDetails.description:''}}
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <b>{{'CREATED' | translate}}:</b> {{(wimDetails._admin.created)?this.sharedService.convertEpochTime(wimDetails._admin.created):''}}
+ </td>
+ <td>
+ <b>{{'MODIFIED' | translate}}:</b> {{(wimDetails._admin.modified)?this.sharedService.convertEpochTime(wimDetails._admin.modified):''}}
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <b>{{'DEPLOYED' | translate}}</b> <br>
+ <b>{{'PAGE.WIMACCOUNTS.RO' | translate}}:</b> {{(wimDetails._admin.deployed.RO)?wimDetails._admin.deployed.RO:''}} <br>
+ <b>{{'PAGE.WIMACCOUNTS.ROACCOUNT' | translate}}:</b> {{(wimDetails._admin.deployed['RO-account'])?wimDetails._admin.deployed['RO-account']:''}}
+ </td>
+ </tr>
+ </table>
+</div>
+<div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="activeModal.close()">{{'CANCEL' | translate}}</button>
+</div>
+<app-loader [waitingMessage]="message" *ngIf="isLoadingResults"></app-loader>
diff --git a/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.scss b/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.scss
new file mode 100644
index 0000000..021d205
--- /dev/null
+++ b/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.scss
@@ -0,0 +1,17 @@
+/*
+ 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)
+*/
\ No newline at end of file
diff --git a/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.ts b/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.ts
new file mode 100644
index 0000000..1e7be9f
--- /dev/null
+++ b/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.ts
@@ -0,0 +1,96 @@
+/*
+ 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 Info WIM Page
+ */
+import { Component, Injector, Input, OnInit } from '@angular/core';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { CONFIGCONSTANT, ERRORDATA, URLPARAMS } from 'CommonModel';
+import { environment } from 'environment';
+import { RestService } from 'RestService';
+import { SharedService } from 'SharedService';
+import { WIMAccountModel } from 'WIMAccountModel';
+
+/**
+ * Creating component
+ * @Component takes WIMAccountInfoComponent.html as template url
+ */
+@Component({
+ templateUrl: './WIMAccountInfoComponent.html',
+ styleUrls: ['./WIMAccountInfoComponent.scss']
+})
+/** Exporting a class @exports WIMAccountInfoComponent */
+export class WIMAccountInfoComponent implements OnInit {
+ /** To inject services @public */
+ public injector: Injector;
+
+ /** Input contains component objects @public */
+ @Input() public params: URLPARAMS;
+
+ /** Contains WIM details @public */
+ public wimDetails: WIMAccountModel;
+
+ /** Instance for active modal service @public */
+ public activeModal: NgbActiveModal;
+
+ /** Check the loading results for loader status @public */
+ public isLoadingResults: boolean = true;
+
+ /** operational State init data @public */
+ public operationalStateFirstStep: string = CONFIGCONSTANT.wimOperationalStateFirstStep;
+
+ /** operational State running data @public */
+ public operationalStateSecondStep: string = CONFIGCONSTANT.wimOperationalStateStateSecondStep;
+
+ /** operational State failed data @public */
+ public operationalStateThirdStep: string = CONFIGCONSTANT.wimOperationalStateThirdStep;
+
+ /** Give the message for the loading @public */
+ public message: string = 'PLEASEWAIT';
+
+ /** Instance of the rest service @private */
+ private restService: RestService;
+
+ /** Contains all methods related to shared @private */
+ private sharedService: SharedService;
+
+ constructor(injector: Injector) {
+ this.injector = injector;
+ this.restService = this.injector.get(RestService);
+ this.activeModal = this.injector.get(NgbActiveModal);
+ this.sharedService = this.injector.get(SharedService);
+ }
+
+ /**
+ * Lifecyle Hooks the trigger before component is instantiate
+ */
+ public ngOnInit(): void {
+ this.generateData();
+ }
+
+ /** Generate Data function @public */
+ public generateData(): void {
+ this.restService.getResource(environment.WIMACCOUNTS_URL + '/' + this.params.id).subscribe((wimDetails: WIMAccountModel) => {
+ this.wimDetails = wimDetails;
+ this.isLoadingResults = false;
+ }, (error: ERRORDATA) => {
+ this.restService.handleError(error, 'get');
+ this.isLoadingResults = false;
+ });
+ }
+}