diff options
author | Sonsino, Ofir (os0695) <os0695@intl.att.com> | 2018-07-10 15:57:37 +0300 |
---|---|---|
committer | Sonsino, Ofir (os0695) <os0695@intl.att.com> | 2018-07-10 15:57:37 +0300 |
commit | ff76b5ed0aa91d5fdf9dc4f95e8b20f91ed9d072 (patch) | |
tree | aae42404a93fdffdd16ff050eaa28129959f7577 /vid-webpack-master/src/app | |
parent | c72d565bb58226b20625b2bce5f0019046bee649 (diff) |
New Angular UI from 1806
Change-Id: I39c160db0e0a6ec2e587ccf007ee1b23c6a08666
Issue-ID: VID-208
Signed-off-by: Sonsino, Ofir (os0695) <os0695@intl.att.com>
Diffstat (limited to 'vid-webpack-master/src/app')
202 files changed, 14582 insertions, 0 deletions
diff --git a/vid-webpack-master/src/app/app.component.e2e-spec.js b/vid-webpack-master/src/app/app.component.e2e-spec.js new file mode 100644 index 00000000..9ec9d23d --- /dev/null +++ b/vid-webpack-master/src/app/app.component.e2e-spec.js @@ -0,0 +1,27 @@ +describe('App', function () { + + beforeEach(function () { + browser.get('/'); + }); + + it('should have a title', function () { + expect(browser.getTitle()).toEqual("Angular 2 App | ng2-webpack"); + }); + + it('should have <header>', function () { + expect(element(by.css('my-app header')).isPresent()).toEqual(true); + }); + + it('should have <main>', function () { + expect(element(by.css('my-app main')).isPresent()).toEqual(true); + }); + + it('should have a main title', function () { + expect(element(by.css('main h1')).getText()).toEqual('Hello from Angular 2!'); + }); + + it('should have <footer>', function () { + expect(element(by.css('my-app footer')).getText()).toEqual("Webpack Angular 2 Starter"); + }); + +}); diff --git a/vid-webpack-master/src/app/app.component.html b/vid-webpack-master/src/app/app.component.html new file mode 100644 index 00000000..3768d0c5 --- /dev/null +++ b/vid-webpack-master/src/app/app.component.html @@ -0,0 +1,6 @@ +<main> + <router-outlet> + <message-box></message-box> + <spinner-component></spinner-component> + </router-outlet> +</main> diff --git a/vid-webpack-master/src/app/app.component.scss b/vid-webpack-master/src/app/app.component.scss new file mode 100644 index 00000000..9149aefc --- /dev/null +++ b/vid-webpack-master/src/app/app.component.scss @@ -0,0 +1,24 @@ +// styles applied on :host are applied on the current component, "app" in this case +:host { + display: block; +} + +header { + background-color: #fff; + padding: 16px; + position: fixed; + top: 0; + left: 0; + width: 100%; + box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.5); +} + +main { + font-family: Arial, Helvetica, sans-serif; + display: block; +} + +footer { + text-align: center; + font-size: 0.8em; +} diff --git a/vid-webpack-master/src/app/app.component.ts b/vid-webpack-master/src/app/app.component.ts new file mode 100644 index 00000000..528c7d7a --- /dev/null +++ b/vid-webpack-master/src/app/app.component.ts @@ -0,0 +1,28 @@ +import {Component, Inject} from '@angular/core'; + +import { ApiService } from './shared'; + +import '../style/app.scss'; +import {NgRedux} from "@angular-redux/store"; +import {AppState} from "./store/reducers"; +import { LogService } from './shared/utils/log/log.service'; + +@Component({ + selector: 'vid-app', // <vid-app></vid-app> + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'], +}) +export class AppComponent { + url = 'https://github.com/preboot/angular2-webpack'; + title: string; + + + constructor(private api: ApiService, private store: NgRedux<AppState>, private _logService : LogService) { + this.title = this.api.title; + store.subscribe(()=>{ + sessionStorage.setItem('reduxState', JSON.stringify(store.getState())); + }); + + this._logService.info("testing new log service"); + } +} diff --git a/vid-webpack-master/src/app/app.module.ts b/vid-webpack-master/src/app/app.module.ts new file mode 100644 index 00000000..a87ffb5b --- /dev/null +++ b/vid-webpack-master/src/app/app.module.ts @@ -0,0 +1,79 @@ +import { ApplicationRef, NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { AppComponent } from './app.component'; +import { HomeComponent } from './home/home.component'; +import { ApiService } from './shared'; +import { routing } from './app.routing'; +import { createNewHosts, removeNgStyles } from '@angularclass/hmr'; +import { BrowseSdcModule } from './browseSdc/browseSdc.module'; +import { VlanTaggingModule } from './vlanTagging/vlan-tagging.module' +import { BootstrapModalModule } from 'ng2-bootstrap-modal'; +import { HashLocationStrategy, LocationStrategy } from "@angular/common"; +import { InstantiationStatusModule } from './instantiationStatus/InstantiationStatus.module'; +import { SharedModule } from './shared/shared.module'; +import { AngularSvgIconModule } from 'angular-svg-icon'; +import { NgReduxModule } from '@angular-redux/store'; +import { StoreModule } from "./store/module"; +import { HttpInterceptorService } from './shared/utils/httpInterceptor/httpInterceptor.service'; +import { DrawingBoardModule } from './drawingBoard/drawingBoard.module'; +import { HealthStatusComponent } from './healthStatus/health-status.component'; +import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to'; +import { LogService } from './shared/utils/log/log.service'; + +@NgModule({ + imports: [ + BrowserModule, + HttpClientModule, + FormsModule, + routing, + SharedModule.forRoot(), + ScrollToModule.forRoot(), + DrawingBoardModule, + VlanTaggingModule, + InstantiationStatusModule, + BrowseSdcModule, + BootstrapModalModule, + BrowseSdcModule, + AngularSvgIconModule, + ReactiveFormsModule, + NgReduxModule, + StoreModule, + ], + declarations: [ + AppComponent, + HomeComponent, + HealthStatusComponent + ], + providers: [ + ApiService, + LogService, + { provide: LocationStrategy, useClass: HashLocationStrategy }, + { provide: HTTP_INTERCEPTORS, useClass: HttpInterceptorService, multi: true } + ], + bootstrap: [AppComponent] +}) +export class AppModule { + private static CustomLoader: any; + + constructor(public appRef: ApplicationRef) { + //for ng2-bootstrap-modal in angualar 5 + Object.defineProperty(appRef, '_rootComponents', { get: () => appRef['components'] }); + } + hmrOnInit(store) { + console.log('HMR store', store); + } + hmrOnDestroy(store) { + let cmpLocation = this.appRef.components.map(cmp => cmp.location.nativeElement); + // recreate elements + store.disposeOldHosts = createNewHosts(cmpLocation); + // remove styles + removeNgStyles(); + } + hmrAfterDestroy(store) { + // display new elements + store.disposeOldHosts(); + delete store.disposeOldHosts; + } +} diff --git a/vid-webpack-master/src/app/app.routing.ts b/vid-webpack-master/src/app/app.routing.ts new file mode 100644 index 00000000..e6f42f9e --- /dev/null +++ b/vid-webpack-master/src/app/app.routing.ts @@ -0,0 +1,40 @@ +import {RouterModule, Routes} from '@angular/router'; + +import {HomeComponent} from './home/home.component'; +import {BrowseSdcComponent} from './browseSdc/browseSdc.component'; +import {ServicePlanningComponent, ServicePlanningEmptyComponent} from './drawingBoard/service-planning/service-planning.component'; +import {VlanTaggingComponent} from './vlanTagging/vlan-tagging.component'; +import {ServicePopupComponent} from './components/service-popup/service-popup.component'; +import { InstantiationStatusComponent } from './instantiationStatus/instantiationStatus.component'; +import {HealthStatusComponent} from "./healthStatus/health-status.component"; +import {FlagsResolve} from "./services/flags.resolve"; + +const routes: Routes = [ + { path: '', component: HomeComponent, pathMatch: 'full' ,resolve : { + flags : FlagsResolve, + }}, + { path: 'browseSdc', component: BrowseSdcComponent, resolve : { + flags : FlagsResolve, + }}, + { path: 'servicePlanning', component: ServicePlanningComponent, resolve : { + flags : FlagsResolve, + }}, + { path: 'servicePlanningEmpty', component: ServicePlanningEmptyComponent, resolve : { + flags : FlagsResolve, + }}, + { path: 'servicePopup', component: ServicePopupComponent, resolve : { + flags : FlagsResolve, + }}, + { path :'vlan', component : VlanTaggingComponent, resolve : { + flags : FlagsResolve, + }}, + { path: 'instantiationStatus', component: InstantiationStatusComponent, resolve : { + flags : FlagsResolve, + }}, + { path: 'healthStatus', component: HealthStatusComponent, resolve : { + flags : FlagsResolve, + }} + +]; + +export const routing = RouterModule.forRoot(routes); diff --git a/vid-webpack-master/src/app/browseSdc/browseSdc.component.html b/vid-webpack-master/src/app/browseSdc/browseSdc.component.html new file mode 100644 index 00000000..7ffd7931 --- /dev/null +++ b/vid-webpack-master/src/app/browseSdc/browseSdc.component.html @@ -0,0 +1,26 @@ +<div> + + <span class="statusLine" [hidden]="!isSpinnerVisible"> + <img src="./img/spinner.gif"/> + </span> + <span class="statusLine" [hidden]="!isProgressVisible"> + <label>Status:</label><span class="status"><span [hidden]="!error"><font color='red'><b>Error: </b></font></span>{{status}}</span> + </span> + <br><br> + <div> + <input class="form-control search-query" placeholder="Filter..." data-tests-id="browseFilter" type="text" [(ngModel)]="filterQuery" size="20" style="width: 250px;"> + </div> + + <h1 class="heading1" style="margin-top:20px;">Browse SDC Service Models</h1> + <div style="margin-top:30px"> + <button (click)="deploy()">deploy</button> + <vid-table + [options]="tableOptions" + [filterQuery]="filterQuery" + (clickEvent)="clickAction($event)"> + Loading table... + </vid-table> + </div> +</div> + + diff --git a/vid-webpack-master/src/app/browseSdc/browseSdc.component.scss b/vid-webpack-master/src/app/browseSdc/browseSdc.component.scss new file mode 100644 index 00000000..0d1678a9 --- /dev/null +++ b/vid-webpack-master/src/app/browseSdc/browseSdc.component.scss @@ -0,0 +1,59 @@ +div[ng-controller=ServiceModelController] .popupContents { + text-align: left; +} + +div[ng-controller=ServiceModelController] .statusLine { + vertical-align: center; +} + +div[ng-controller=ServiceModelController] .statusLine img { + width: 20px; + margin: 10px; +} + +div[ng-controller=ServiceModelController] .status { + margin-left: 5px; +} + +div[ng-controller=ServiceModelController] .feedback { + height: 35px; + vertical-align: center; +} + +div[ng-controller=ServiceModelController] .progress { + margin: 0px 10px; + font-weight: bold; +} + +div[ng-controller=ServiceModelController] .error { + width: 630px; + font-weight: bold; + font-size: 16px; + color: red; + margin: 0px 10px 0px 45px; +} + +div[ng-controller=ServiceModelController] .error img { + width: 25px; + margin: 0px 10px; +} + +div[ng-controller=ServiceModelController] .buttonRow { + text-align: center; + margin-bottom: 10px; +} + +div[ng-controller=ServiceModelController] .log { + width: 700px; + height: 500px; + border: solid black 1px; + text-align: left !important; + margin: 10px; + padding: 10px; + overflow: auto; +} + + +.previous-versions-button { + width: 113px; +} diff --git a/vid-webpack-master/src/app/browseSdc/browseSdc.component.ts b/vid-webpack-master/src/app/browseSdc/browseSdc.component.ts new file mode 100644 index 00000000..46f37dc2 --- /dev/null +++ b/vid-webpack-master/src/app/browseSdc/browseSdc.component.ts @@ -0,0 +1,176 @@ +import {Component, Input, OnInit} from '@angular/core'; +import * as _ from 'lodash'; +import {SdcService} from '../services/sdc.service'; +import {DialogService} from 'ng2-bootstrap-modal'; +import {Constants} from '../shared/utils/constants'; +import {CustomTableColumnDefinition, CustomTableOptions} from './vid-table/vid-table.component'; +import {ServicePopupComponent} from "../components/service-popup/service-popup.component"; +import { PreviousVersionsComponent } from './previous-versions/previous-versions.component'; + +@Component({ + selector: 'browse-sdc', + templateUrl: './browseSdc.component.html', + styleUrls: ['./browseSdc.component.scss'] +}) + + +export class BrowseSdcComponent implements OnInit { + + isSpinnerVisible = false; + isProgressVisible = false; + error: boolean; + status: string; + // table + + private basicColumns: CustomTableColumnDefinition[]; + @Input () filterQuery = ''; + tableOptions: CustomTableOptions; + private wholeData: any[]; + + constructor(private _sdcService: SdcService, private dialogService: DialogService) {} + + initTableOptions() { + this.basicColumns = [ + { displayName: 'Action', key: 'action', type: 'button' , text: 'deploy', action: 'deploy' }, + { displayName: 'UUID', key: 'uuid', filter: 'text'}, + { displayName: 'invariantUUID', key: 'invariantUUID', filter: 'text'}, + { displayName: 'Name', key: 'name', filter: 'text'}, + { displayName: 'Version', key: 'version', filter: 'text'}, + { displayName: 'Category', key: 'category', filter: 'text'}, + { displayName: 'Distribution Status', key: 'distributionStatus', filter: 'text'}, + { displayName: 'Last Updated By', key: 'lastUpdaterUserId', filter: 'text'}, + { displayName: 'Tosca Model', key: 'toscaModelUrl', filter: 'text'} + ]; + + let columns: CustomTableColumnDefinition[] = this.basicColumns.concat( + {displayName: 'Action', key: 'actions', type: 'button', text: 'Previous Versions', + showCondition: 'hasPreviousVersion', action: 'loadPreviousData' } + ); + + this.tableOptions = { + data: [], + columns: columns, + config: { + sortBy: 'name', + sortOrder: 'asc', + pageSize: 10, + pageNumber: 1, + totalCount: 0, + totalPages: 0, + maxSize: 10, + showSelectCheckbox: true, + showSelectAll: true, + showSort: true, + clientSort: true, + clientPaging: true, + // displayPager: true, + // displayPageSize: true, + stickyHeader: true, + stickyHeaderOffset: 0, + stickyContainer: '.table1-container' + }, + }; + } + private deploy(service: any): void { + if (service) { + console.log('this row uuid:' + service.uuid); + } + + this.dialogService.addDialog(ServicePopupComponent, { + }) + } + + private filterDataWithHigherVersion(serviceData) { + let delimiter = '$$'; + let filterDataServices = {}; + for (let i = 0; i < serviceData.length; i++) { + let index = serviceData[i].invariantUUID.trim() + delimiter + serviceData[i].name.trim(); + if (!filterDataServices[index]) { + filterDataServices[index] = { + service: serviceData[i], + hasPreviousVersion: false + }; + } else { + filterDataServices[index].hasPreviousVersion = true; + if (parseFloat(serviceData[i].version.trim()) > parseFloat(filterDataServices[index].service.version.trim())) { + filterDataServices[index].service = serviceData[i]; + } + } + } + return Object.keys(filterDataServices).map(function (key) { + let service = filterDataServices[key].service; + service.hasPreviousVersion = filterDataServices[key].hasPreviousVersion; + return service; + }); + } + + private initData() { + this.isProgressVisible = true; + this.isSpinnerVisible = true; + console.log('getServicesModels: '); + this._sdcService.getServicesModels().subscribe( + // onNext() function + value => this.getServiceCallback(value), + // onError() function + error => this.getServiceOnError(error), + // onComplete() function + () => console.log('completed') + ); + } + + private getServiceCallback(responseBody: any): void { + console.log('response is ' , responseBody); + this.wholeData = responseBody.services; + this.tableOptions.data = this.filterDataWithHigherVersion(responseBody.services); + this.isSpinnerVisible = false; + this.isProgressVisible = false; + } + private getServiceOnError(error: any): void { + console.log('error is ' , error); + this.status = Constants.Status.FAILED_SERVICE_MODELS_ASDC; + this.error = true; + this.isSpinnerVisible = false; + } + + private loadPreviousVersionData(service): void { + let previousVersionData: any[] = _.filter(this.wholeData, function(item) { + return item.invariantUUID === service.invariantUUID && item.name === service.name && service.version !== item.version; + }); + + let modalTableOptions: CustomTableOptions = { + data: previousVersionData, + columns: this.basicColumns, + config: { + sortBy: 'version', + sortOrder: 'desc'} + }; + // open modal + this.dialogService.addDialog(PreviousVersionsComponent, { + title: service.name + ' - Previous Version', + tableOptions: modalTableOptions + }) + .subscribe( service => { + if (service) { + this.deploy(service); + } + }); + } + + + public clickAction(row) { + switch (row.actionName) { + case 'loadPreviousData': + this.loadPreviousVersionData(row); + break; + case 'deploy': + this.deploy(row); + break; + } + } + + ngOnInit() { + console.log('Browse SDC Service Models'); + this.initTableOptions(); + this.initData(); + } +} diff --git a/vid-webpack-master/src/app/browseSdc/browseSdc.module.ts b/vid-webpack-master/src/app/browseSdc/browseSdc.module.ts new file mode 100644 index 00000000..b7dd78e5 --- /dev/null +++ b/vid-webpack-master/src/app/browseSdc/browseSdc.module.ts @@ -0,0 +1,35 @@ + +import { NgModule } from '@angular/core'; +import {BrowseSdcComponent} from './browseSdc.component'; +import {VidTableComponent} from './vid-table/vid-table.component'; +import {SdcService} from '../services/sdc.service'; +import {CommonModule} from '@angular/common'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {DataTableModule} from 'angular2-datatable'; +import {BootstrapModalModule} from 'ng2-bootstrap-modal'; +import {PreviousVersionsComponent} from './previous-versions/previous-versions.component'; +import {ServicePopupComponent} from '../components/service-popup/service-popup.component'; +import {ServiceInstanceDetailsComponent} from "../components/service-popup/service-instance-details/service-instance-details.component"; +import {TooltipModule} from 'ngx-tooltip'; +import {InputsModule} from "../modules/inputs.module"; +import {VnfPopupComponent} from "../components/vnf-popup/vnf-popup.components"; +import {VnfInstanceDetailsComponent} from "../components/vnf-popup/vnf-instance-details/vnf-instance-details.component"; +import { VnfInstanceDetailsService } from '../components/vnf-popup/vnf-instance-details/vnf-instance-details.service'; +import { SharedModule } from '../shared/shared.module'; +import { ServiceInstanceDetailsService } from '../components/service-popup/service-instance-details/service-instance-details.service'; +import { ServicePopupService } from '../components/service-popup/service-popup.service'; +import { DataFilterPipe } from '../shared/pipes/data-filter.pipe'; + + +@NgModule({ + imports: [CommonModule, FormsModule, ReactiveFormsModule, BootstrapModalModule, DataTableModule, TooltipModule, InputsModule, SharedModule.forRoot()], + providers: [SdcService, VnfInstanceDetailsService, ServiceInstanceDetailsService, ServicePopupService], + declarations: [BrowseSdcComponent, VidTableComponent, DataFilterPipe, PreviousVersionsComponent, + ServicePopupComponent, ServiceInstanceDetailsComponent, VnfPopupComponent, VnfInstanceDetailsComponent + ], + entryComponents: [BrowseSdcComponent, PreviousVersionsComponent, ServicePopupComponent, VnfPopupComponent], + exports: [ ] + +}) + +export class BrowseSdcModule { } diff --git a/vid-webpack-master/src/app/browseSdc/previous-versions/previous-versions.component.ts b/vid-webpack-master/src/app/browseSdc/previous-versions/previous-versions.component.ts new file mode 100644 index 00000000..8faf04d6 --- /dev/null +++ b/vid-webpack-master/src/app/browseSdc/previous-versions/previous-versions.component.ts @@ -0,0 +1,29 @@ +import { Component } from '@angular/core'; +import { DialogComponent, DialogService } from 'ng2-bootstrap-modal'; +import { CustomTableOptions } from '../vid-table/vid-table.component'; + +export interface PreviousVersionsModel { + + title: string; + tableOptions: CustomTableOptions; +} + +@Component({ + selector: 'previous-versions', + templateUrl: 'previous-versions.html', + styleUrls: ['previous-versions.css'] +}) +export class PreviousVersionsComponent extends DialogComponent<PreviousVersionsModel, boolean> implements PreviousVersionsModel { + + title: string; + tableOptions: CustomTableOptions; + + constructor(dialogService: DialogService) { + super(dialogService); + } + + public deploy(row) { + this.result = row; + this.close(); + } +} diff --git a/vid-webpack-master/src/app/browseSdc/previous-versions/previous-versions.css b/vid-webpack-master/src/app/browseSdc/previous-versions/previous-versions.css new file mode 100644 index 00000000..92bcbd10 --- /dev/null +++ b/vid-webpack-master/src/app/browseSdc/previous-versions/previous-versions.css @@ -0,0 +1,4 @@ +.modal-dialog { + width: 1145px; + font-size: 11px; +} diff --git a/vid-webpack-master/src/app/browseSdc/previous-versions/previous-versions.html b/vid-webpack-master/src/app/browseSdc/previous-versions/previous-versions.html new file mode 100644 index 00000000..68209b8e --- /dev/null +++ b/vid-webpack-master/src/app/browseSdc/previous-versions/previous-versions.html @@ -0,0 +1,18 @@ +<div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" (click)="close()" >×</button> + <h4 class="modal-title">{{title}}</h4> + </div> + <div class="modal-body"> + <vid-table + [options]="tableOptions" + (clickEvent)="deploy($event)"> + Loading table... + </vid-table> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-default" (click)="close()" >Cancel</button> + </div> + </div> +</div> diff --git a/vid-webpack-master/src/app/browseSdc/vid-table/vid-table.component.html b/vid-webpack-master/src/app/browseSdc/vid-table/vid-table.component.html new file mode 100644 index 00000000..5650e5d3 --- /dev/null +++ b/vid-webpack-master/src/app/browseSdc/vid-table/vid-table.component.html @@ -0,0 +1,26 @@ +<table class="table table-hover table-striped table-sortable" [mfData]="options.data | dataFilter : filterQuery" #mf="mfDataTable" [(mfSortBy)]="options.config.sortBy" [(mfSortOrder)]="options.config.sortOrder" [mfRowsOnPage]="10"> + <thead> + <tr> + <th *ngFor="let column of options.columns"> + <mfDefaultSorter by="{{column.key}}"> {{column.displayName}}</mfDefaultSorter> + </th> + </tr> + </thead> + <tbody> + <tr *ngFor="let row of mf.data"> + <td *ngFor="let column of options.columns" > + <button class="btn btn-primary" *ngIf="column.type==='button' && (!column.showCondition || row[column.showCondition])" title="{{column.text}}" (click)="clickBtn(row, column.action)"> + {{column.text}} + </button> + {{row[column.key]}} + </td> + </tr> + </tbody> + <tfoot> + <tr> + <td colspan="5"> + <mfBootstrapPaginator [rowsOnPageSet]="[10,15,20]"></mfBootstrapPaginator> + </td> + </tr> + </tfoot> +</table> diff --git a/vid-webpack-master/src/app/browseSdc/vid-table/vid-table.component.scss b/vid-webpack-master/src/app/browseSdc/vid-table/vid-table.component.scss new file mode 100644 index 00000000..f3488798 --- /dev/null +++ b/vid-webpack-master/src/app/browseSdc/vid-table/vid-table.component.scss @@ -0,0 +1,40 @@ +table > thead > tr > th { + cursor: pointer; + position: relative; + background-image: none; + color: #fff; + background-color: #666; + a{ + color: #fff; + } +} + +.table-sortable > thead > tr > th:after, +.table-sortable > thead > tr > th.sort-false:after, +.table-sortable > thead > tr > th.sort-true:after { + font-family: FontAwesome; + padding-left: 5px; +} + +.table-sortable > thead > tr > th:after { + content: "\f0dc"; + color: #ddd; +} +.table-sortable > thead > tr > th.sort-false:after { + content: "\f0de"; + color: #767676; +} +.table-sortable > thead > tr > th.sort-true:after { + content: "\f0dd"; + color: #767676; +} +//.table-sortable > tbody > td > +.table-sortable { + tbody{ + td{ + .btn-primary{ + min-width: 100px; + } + } + } +} diff --git a/vid-webpack-master/src/app/browseSdc/vid-table/vid-table.component.ts b/vid-webpack-master/src/app/browseSdc/vid-table/vid-table.component.ts new file mode 100644 index 00000000..aa203090 --- /dev/null +++ b/vid-webpack-master/src/app/browseSdc/vid-table/vid-table.component.ts @@ -0,0 +1,59 @@ +/** + * Created by cp2122 on 1/2/2018. + */ +import { Component, Input, EventEmitter, Output } from '@angular/core'; +export class CustomTableColumnDefinition { + public displayName = ''; + public key = ''; + public type? = 'text'; + public text? = ''; // for button + public action? = ''; // for button - callback + public showCondition? = ''; // for button + // public binding = ''; + public filter? = ''; + // public computedClass: any; + // public isComputed = false; + // public isAnchor = false; + // public srefBinding = ''; +} + +export class CustomTableConfig { + public sortBy = ''; + public sortOrder = 'desc'; + public pageSize? = 10; + public pageNumber? = 1; + public totalCount? = 0; + public totalPages? = 0; + public maxSize? = 10; + public showSelectCheckbox? = false; + public showSelectAll? = true; + public showSort? = true; + public clientSort? = true; + public clientPaging? = true; + public stickyHeader? = true; + public stickyHeaderOffset? = 0; + public stickyContainer? = ''; +} + +export class CustomTableOptions { + public data: any[]; + public columns: Array<CustomTableColumnDefinition>; + public config: CustomTableConfig; + // public callbacks: any; +} +@Component({ + selector: 'vid-table', + styleUrls: ['./vid-table.component.scss'], + templateUrl: './vid-table.component.html' +}) + +export class VidTableComponent { + @Input() options: CustomTableOptions; + @Input() filterQuery = ''; + + @Output() clickEvent = new EventEmitter<any>(); + clickBtn(row, actionName) { + row.actionName = actionName; + this.clickEvent.emit(row); + } +} diff --git a/vid-webpack-master/src/app/components/dynamic-inputs/dynamic-inputs.component.ts b/vid-webpack-master/src/app/components/dynamic-inputs/dynamic-inputs.component.ts new file mode 100644 index 00000000..921c923d --- /dev/null +++ b/vid-webpack-master/src/app/components/dynamic-inputs/dynamic-inputs.component.ts @@ -0,0 +1,80 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {FormControl, FormGroup, Validators} from "@angular/forms"; +import {DynamicInput, DynamicMultiSelect, DynamicNumber, DynamicSelect} from "../../shared/models/dynamicInput"; + +@Component({ + selector: 'dynamic-inputs', + templateUrl: './dynamic-inputs.html', + styleUrls: ['./dynamic-inputs.scss'] +}) + +export class DynamicInputsComponent implements OnInit{ + @Input() public list:any[] = []; + @Input() public group: FormGroup; + + private dynamicList:DynamicInput<any>[] = []; + + isDynamicNumber(item: any): item is DynamicNumber { + return item; + } + + buildValidators(item: DynamicInput<any>) { + let validatorArr = []; + item.maxLength && validatorArr.push(Validators.maxLength(item.maxLength)); + item.minLength && validatorArr.push(Validators.minLength(item.minLength)); + if(this.isDynamicNumber(item)) { + item.max && validatorArr.push(Validators.max(item.max)); + item.min && validatorArr.push(Validators.min(item.min)); + } + return Validators.compose(validatorArr); + } + + ngOnInit(): void { + this.list.forEach((item)=> { + let dynamicItem: DynamicInput<any>; + switch (item.type) { + case 'multi_select': + item.optionList.forEach(function(option) { option.id = option.id||option.name; + option.itemName = option.name;}); + item.settings = { + disabled: !item.isEnabled, + text: item.prompt, + }; + dynamicItem = new DynamicMultiSelect(item); + break; + case 'select': + let defaultValue:any = item.optionList.find((option) => option.isDataLoading && option.name); + item.value = defaultValue && defaultValue.id; + dynamicItem = new DynamicSelect(item); + break; + case 'boolean': + item.value = item.value || false; + item.optionList = [{name: true, isPermitted: true, isDataLoading: item.value}, {name: false, isPermitted: true, isDataLoading: !item.value}]; + + dynamicItem = new DynamicSelect(item); + break; + case 'number': + dynamicItem = new DynamicNumber(item); + break; + case 'file': + dynamicItem = new DynamicInput<any>(item); + break; + case 'checkbox': + dynamicItem = new DynamicInput<boolean>(item); + break; + case 'map': + item.prompt = "{<key1>: <value1>,\.\.\.,<keyN>: <valueN>}"; + dynamicItem = new DynamicInput<string>(item); + break; + case 'list': + item.prompt = "[<value1>,...,<valueN>]"; + dynamicItem = new DynamicInput<string>(item); + break; + default: dynamicItem = new DynamicInput<string>(item); + } + this.dynamicList.push(dynamicItem); + this.group.addControl(dynamicItem.name, new FormControl({value: dynamicItem.value, disabled: !dynamicItem.isEnabled}, this.buildValidators(dynamicItem))); + }) + } + +} diff --git a/vid-webpack-master/src/app/components/dynamic-inputs/dynamic-inputs.html b/vid-webpack-master/src/app/components/dynamic-inputs/dynamic-inputs.html new file mode 100644 index 00000000..a933364e --- /dev/null +++ b/vid-webpack-master/src/app/components/dynamic-inputs/dynamic-inputs.html @@ -0,0 +1,22 @@ +<div *ngFor="let item of dynamicList"> + <div id="{{item.id}}" class="details-item" [ngSwitch]="item.type" [formGroup]="group" [hidden]="!item.isVisible"> + <label>{{item.name | dynamicInputLabel }}</label> + <select *ngSwitchCase="item.type === 'select'|| item.type === 'boolean'? item.type: ''" name="{{item.name}}" class="form-control input-text" [formControlName]="item.name" title="{{item.description}}" [required]="item.isRequired && item.isVisible" maxlength="{{item.maxLength}}" minlength="{{item.minLength}}"> + <option value="null" [selected]="item.value == null || item.value == undefined" hidden disabled>{{item.prompt}}</option> + <option *ngFor="let option of item.optionList" [ngValue]="option.id || option.name" [disabled]="!option.isPermitted" [selected]="option.isDataLoading">{{option.name}}</option> + </select> + <angular2-multiselect *ngSwitchCase="'multi_select'" [formControlName]="item.name" [(ngModel)]="item.selectedItems" [data]="item.optionList" [settings]="item.settings" title="{{item.description}}" [required]="item.isRequired && item.isVisible"></angular2-multiselect> + <input *ngSwitchCase="'number'" name="{{item.name}}" class="form-control input-text" [formControlName]="item.name" type="number" placeholder="{{item.prompt}}" title="{{item.description}}" min="{{item.min}}" max="{{item.max}}" [readonly]="item.isReadOnly" [required]="item.isRequired && item.isVisible"> + <div *ngSwitchCase="'file'"> + + <label class="dynamicFileName" for="dynamicFileInput-{{item.name}}"> + <input id="dynamicFileInput-{{item.name}}" name="{{item.name}}" class="form-control input-text" [formControlName]="item.name" type="file" placeholder="{{item.prompt}}" [readonly]="item.isReadOnly" [required]="item.isRequired && item.isVisible"> + <label for="dynamicFileInput-{{item.name}}" class="labelForImage"> + <span class="icon-browse"></span> + </label> + </label> + </div> + <input *ngSwitchCase="'checkbox'" name="{{item.name}}" [formControlName]="item.name" type="checkbox" data-toggle="toggle" title="{{item.description}}" [readonly]="item.isReadOnly" [required]="item.isRequired && item.isVisible"> + <input *ngSwitchDefault name="{{item.name}}" class="form-control input-text" [formControlName]="item.name" placeholder="{{item.prompt}}" title="{{item.description}}" maxlength="{{item.maxLength}}" minlength="{{item.minLength}}" [readonly]="item.isReadOnly" [required]="item.isRequired && item.isVisible"/> + </div> +</div> diff --git a/vid-webpack-master/src/app/components/dynamic-inputs/dynamic-inputs.scss b/vid-webpack-master/src/app/components/dynamic-inputs/dynamic-inputs.scss new file mode 100644 index 00000000..11a14142 --- /dev/null +++ b/vid-webpack-master/src/app/components/dynamic-inputs/dynamic-inputs.scss @@ -0,0 +1,58 @@ +input[type=file] { + opacity: 0; + position: relative; + z-index: 1; + width: 0.5px; + height: 0.5px; + display: inline-block; + input { + display: none; + } +} + +.dynamicFileName { + width: 100%; + height: 34px; + background: #FFFFFF; + border: 1px solid #D2D2D2; + border-radius: 2px; + display: inline-block; + line-height: 34px; + font-weight: normal !important; + padding-left: 3px; + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} + +.labelForImage { + background: #F2F2F2; + border-left: 1px solid #D2D2D2; + float: right; + height: 32px; +} + +.icon-browse:before { + content: "\e90d"; + color: #5A5A5A; + font-size: 15px; + cursor: pointer; + width: 34px; + height: 100%; + line-height: 34px; + text-align: center; + display: inline-block; + vertical-align: super; + border-radius: 2px; + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} + +.icon-browse:hover::before { + background-color: #E6F6FB; + color: #009FDB; +} + +.icon-browse:active::before { + background-color: #E6F6FB; + color: #009FDB; +} diff --git a/vid-webpack-master/src/app/components/form-async/form-async.component.ts b/vid-webpack-master/src/app/components/form-async/form-async.component.ts new file mode 100644 index 00000000..e71c4446 --- /dev/null +++ b/vid-webpack-master/src/app/components/form-async/form-async.component.ts @@ -0,0 +1,80 @@ +import {Component, Input, OnInit, ViewChild} from '@angular/core'; +import { NgRedux, select } from '@angular-redux/store'; +import { Observable } from "rxjs/Observable"; +import { updateProductFamilies } from "../../service.actions"; +import { AppState } from "../../store/reducers"; +import { + loadProductFamiliesAction, loadLcpTenant, loadAicZones, + loadCategoryParameters +} from '../../services/aaiService/aai.actions'; +import { LcpRegionsAndTenants } from "../../shared/models/lcpRegionsAndTenants"; +import {NgForm} from "@angular/forms"; +import {SelectOption} from "../../shared/models/selectOption"; +import {VNFModel} from "../../shared/models/vnfModel"; + +@Component({ + selector: "formasync", + templateUrl: "form-async.template.html", + styleUrls: ["form-async.style.scss"], + +}) + +export class formasync implements OnInit { + + constructor(private store: NgRedux<AppState>) { } + + @ViewChild('form') form: NgForm; + + @Input() + set model(model: VNFModel) { + if (model) { + this.isUserProvidedNaming = model.isUserProvidedNaming; + } + }; + + @select(['service', 'productFamilies']) + readonly productFamilies: Observable<any>; + + @select(['service', 'lcpRegionsAndTenants']) + readonly lcpRegionsAndTenants: Observable<any>; + + @select(['service', 'lcpRegionsAndTenants', 'lcpRegionList']) + readonly lcpRegions: Observable<any>; + + @select(['service', 'aicZones']) + readonly aicZones: Observable<any>; + + @select(['service', 'categoryParameters', 'platformList']) + readonly platformList: Observable<any>; + + @select(['service', 'categoryParameters', 'lineOfBusinessList']) + readonly lineOfBusinessList: Observable<any>; + + rollbackOnFailure = [ + new SelectOption({id: 'true', name: 'Rollback'}), + new SelectOption({id: 'false', name: 'Don\'t Rollback'}) + ]; + tenants = []; + + serviceInstance: any = { + rollback:'true' + }; + + isUserProvidedNaming: boolean = false; + + onLcpSelect(newValue: string) { + let value: LcpRegionsAndTenants = undefined; + this.lcpRegionsAndTenants.subscribe(data => value = data); + this.tenants = value.lcpRegionsTenantsMap[newValue]; + } + + ngOnInit() { + this.store.dispatch(loadProductFamiliesAction()); + this.store.dispatch(loadLcpTenant()); + this.store.dispatch(loadAicZones()); + this.store.dispatch(loadCategoryParameters()); + } +} + + + diff --git a/vid-webpack-master/src/app/components/form-async/form-async.style.scss b/vid-webpack-master/src/app/components/form-async/form-async.style.scss new file mode 100644 index 00000000..e6c89bca --- /dev/null +++ b/vid-webpack-master/src/app/components/form-async/form-async.style.scss @@ -0,0 +1,15 @@ +.form-wrapper{ + width:640px; + padding-left: 50px; + padding-top: 50px; + .details-item{ + padding-bottom: 30px; + } + .details-item { + select { + font-family: OpenSans-Italic; + font-size: 14px; + color: #959595 !important; + } + } +} diff --git a/vid-webpack-master/src/app/components/form-async/form-async.template.html b/vid-webpack-master/src/app/components/form-async/form-async.template.html new file mode 100644 index 00000000..c63a7bee --- /dev/null +++ b/vid-webpack-master/src/app/components/form-async/form-async.template.html @@ -0,0 +1,67 @@ +<div class="content"> + <form #form="ngForm" name="networkNodeForm" class="form-wrapper"> + <div *ngIf="isUserProvidedNaming" class="details-item"> + <label class="placeholder">Instance name*</label> + <input [attr.data-tests-id]="'instanceName'" id="instance-name" [(ngModel)]="serviceInstance.instanceName" name="instance-name" + class="form-control input-text" placeholder="Type Instance Name" type="text" required> + </div> + + <div class="details-item"> + <label>Product family</label> + <select class="form-control input-text" [(ngModel)]="serviceInstance.productFamily" data-tests-id="productFamily" id="product-family-select" + name="product-family-select"> + <option class="placeholder" [value]="undefined" disabled>Select Product Family</option> + <option *ngFor="let productFamily of productFamilies | async" [value]="productFamily.id" [disabled]="!productFamily.isPermitted">{{productFamily.name}}</option> + </select> + </div> + + <div class="details-item"> + <label>LCP region:*</label> + <select (change)="onLcpSelect($event.target.value)" class="form-control input-text" [(ngModel)]="serviceInstance.lcpRegion" + name="lcpRegion" id="lcpRegion-select" data-tests-id="lcpRegion" required> + <option class="placeholder1" [value]="undefined" disabled>Select LCP Region</option> + <option *ngFor="let lcpRegion of lcpRegions | async" [value]="lcpRegion.id" [disabled]="!lcpRegion.isPermitted" class="lcpRegionOption">{{lcpRegion.id}}</option> + </select> + </div> + <div class="details-item"> + <label>Tenant:*</label> + <select class="form-control input-text" [(ngModel)]="serviceInstance.tenantId" name="tenant" id="tenant-select" data-tests-id="tenant" + required> + <option class="placeholder1" [value]="undefined" disabled>Select Tenant</option> + <option *ngFor="let tenant of tenants" [value]="tenant.id" [disabled]="!tenant.isPermitted">{{tenant.name}}</option> + </select> + </div> + + <div class="details-item"> + <label>AIC Zone:</label> + <select class="form-control input-text" name="aicZone" id="aicZone-select" data-tests-id="aic_zone" [(ngModel)]="serviceInstance.aicZone"> + <option class="placeholder1" [value]="undefined" disabled>Select AIC Zone</option> + <option class="aicZoneOption" *ngFor="let aicZone of aicZones | async" [value]="aicZone.id">{{aicZone.name}}</option> + </select> + </div> + <div class="details-item"> + <label>Platform:</label> + <select data-tests-id="platform" class="form-control input-text" name="platform" id="platform" [(ngModel)]="serviceInstance.platformName"> + <option class="placeholder1" [value]="undefined" disabled>Select Platform</option> + <option *ngFor="let platform of platformList | async" [value]="platform.id">{{platform.name}}</option> + </select> + </div> + + <div class="details-item"> + <label>Line Of Business:*</label> + <select data-tests-id="lineOfBusiness" class="form-control input-text" [(ngModel)]="serviceInstance.lineOfBusiness" + name="owningEntity" id="owningEntity" required> + <option class="placeholder1" [value]="undefined" disabled>Select Line Of Business</option> + <option *ngFor="let lineOfBusiness of lineOfBusinessList | async" [value]="lineOfBusiness.id">{{lineOfBusiness.name}}</option> + </select> + </div> + <div class="details-item"> + <label>Rollback On Failure:</label> + <select data-tests-id="suppressRollback" class="form-control input-text" name="rollbackOnFailure" id="rollbackOnFailure" [(ngModel)]="serviceInstance.rollback"> + <option *ngFor="let option of rollbackOnFailure" [value]="option.id">{{option.name}}</option> + </select> + </div> + </form> + +</div> + diff --git a/vid-webpack-master/src/app/components/instance-popup/instance-popup.components.ts b/vid-webpack-master/src/app/components/instance-popup/instance-popup.components.ts new file mode 100644 index 00000000..b8ce613d --- /dev/null +++ b/vid-webpack-master/src/app/components/instance-popup/instance-popup.components.ts @@ -0,0 +1,9 @@ +import {ModelInformationItem} from "../../shared/components/model-information/model-information.component"; + + +export interface InstancePopup { + onCancelClick():void; + createModelInformationItems(): Array<ModelInformationItem>; + getModelName():string; +} + diff --git a/vid-webpack-master/src/app/components/service-popup/service-instance-details/service-instance-details.component.ts b/vid-webpack-master/src/app/components/service-popup/service-instance-details/service-instance-details.component.ts new file mode 100644 index 00000000..b6a2e396 --- /dev/null +++ b/vid-webpack-master/src/app/components/service-popup/service-instance-details/service-instance-details.component.ts @@ -0,0 +1,238 @@ +import {Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild} from '@angular/core'; +import {FormControl, FormGroup, Validators} from "@angular/forms"; +import {ServicePopupDataModel} from './servicePopupDataModel'; +import {AaiService} from '../../../services/aaiService/aai.service'; +import {updateServiceInstance} from "../../../service.actions"; +import * as _ from 'lodash'; +import {ServiceModel} from "../../../shared/models/serviceModel"; +import {ModelInfo} from "../../../shared/models/modelInfo"; +import {loadProductFamiliesAction} from "../../../services/aaiService/aai.actions"; +import {Observable} from "rxjs/Observable"; +import {SelectOptionInterface} from "../../../shared/models/selectOption"; +import {NgRedux, select} from "@angular-redux/store"; +import {AppState} from "../../../store/reducers"; +import {isNullOrUndefined} from 'util'; +import {ServiceInstanceDetailsService} from './service-instance-details.service'; +import {NumbersLettersUnderscoreValidator} from '../../../shared/components/validators/numbersLettersUnderscore/numbersLettersUnderscore.validator'; +import {DefaultDataGeneratorService} from '../../../shared/services/defaultDataServiceGenerator/default.data.generator.service'; + + +@Component({ + selector: 'service-instance-details', + templateUrl: 'service-instance-details.html', + styleUrls: ['service-instance-details.scss'], + providers: [AaiService] +}) + +export class ServiceInstanceDetailsComponent implements OnInit, OnChanges { + ngOnChanges(changes: SimpleChanges): void { + if (changes["serviceInstance"] !== undefined && changes["serviceInstance"].currentValue !== changes["serviceInstance"].previousValue && changes["serviceInstance"].currentValue !== null) { + this.oldServiceInstance = Object.assign({}, this.serviceInstance); + } + } + _serviceModel: ServiceModel; + @Input () serviceInstance: any; + @Input () dynamicInputs; + @Input () servicesQty: number; + @Input () + set serviceModel(serviceModel: ServiceModel) { + this._serviceModel = serviceModel; + this.updateFormGroupControlsWithServiceModel(serviceModel); + } + @ViewChild('serviceForm') serviceForm: 'ServiceForm'; + @Output() closePopup : EventEmitter<any> = new EventEmitter<any>(); + @Output() onDataChanged: EventEmitter<any> = new EventEmitter<any>(); + oldServiceInstance = {}; + + //todo: implement Epics and use @select to fetch the rest of the form's data as done with productFamilies. + //that way we can loose the updateFormData function and the subscription to store in the constructor. + @select(['service','productFamilies']) + readonly productFamilies : Observable<SelectOptionInterface[]>; + serviceDetails:any = { + + }; + servicePopupDataModel: ServicePopupDataModel = new ServicePopupDataModel(); + serviceInstanceDetailsFormGroup: FormGroup; + serviceInstanceDetailsService : ServiceInstanceDetailsService; + + constructor(private _aaiService: AaiService, private store: NgRedux<AppState>, private _serviceInstanceDetailsService : ServiceInstanceDetailsService, private _defaultDataGeneratorService : DefaultDataGeneratorService) { + this.store.subscribe(() => {this.updateFormData()}); + this.serviceInstanceDetailsService = this._serviceInstanceDetailsService; + this.serviceInstanceDetailsFormGroup = this.createFormGroup(); + + this.serviceInstanceDetailsFormGroup.valueChanges.subscribe(()=> { + this.onDataChanged.next(); + }) + } + + ngOnInit() { + this.subscribeToFormChanges(); + this._aaiService.getSubscribers().subscribe(); + this._aaiService.getCategoryParameters(null).subscribe(); + this._aaiService.getAicZones().subscribe(); + this.store.dispatch(loadProductFamiliesAction()); + } + + + createFormGroup(): FormGroup { + const formGroup = new FormGroup({ + globalSubscriberId: new FormControl( + Validators.compose([Validators.required]) + ), + productFamilyId: new FormControl(), + subscriptionServiceType: new FormControl({value: null, disabled: true}, Validators.compose([Validators.required])), + lcpCloudRegionId: new FormControl({value: null, disabled: true}, Validators.compose([Validators.required])), + tenantId: new FormControl({value: null, disabled: true}, Validators.compose([Validators.required])), + aicZoneId: new FormControl(), + projectName: new FormControl(), + owningEntityId: new FormControl(Validators.compose([Validators.required])), + rollbackOnFailure: new FormControl(null, Validators.required), + }); + + return formGroup; + } + + updateFormGroupControlsWithServiceModel(serviceModel: ServiceModel) { + this.serviceInstanceDetailsFormGroup.markAsUntouched(); + + if (serviceModel) { + this.serviceDetails.isUserProvidedNaming = serviceModel.isUserProvidedNaming; + if (serviceModel.isUserProvidedNaming) { + this.serviceInstanceDetailsFormGroup.addControl('instanceName', new FormControl('', Validators.compose([Validators.required, NumbersLettersUnderscoreValidator.valid]))) + }else{ + this.serviceInstanceDetailsFormGroup.removeControl('instanceName'); + } + + if (serviceModel.isMultiStepDesign) { + this.serviceInstanceDetailsFormGroup.addControl('pause', new FormControl(true)); + }else{ + this.serviceInstanceDetailsFormGroup.removeControl('pause'); + } + } + } + + updateFormData() { + let service = this.store.getState().service; + this.servicePopupDataModel.subscribers = service.subscribers; + this.servicePopupDataModel.serviceTypes = service.serviceTypes[this.servicePopupDataModel.globalCustomerId]; + this.servicePopupDataModel.lcpRegions = service.lcpRegionsAndTenants.lcpRegionList; + if (this.serviceInstance) { + this.servicePopupDataModel.tenants = service.lcpRegionsAndTenants.lcpRegionsTenantsMap[this.serviceInstance.lcpCloudRegionId]; + } + this.servicePopupDataModel.aicZones = service.aicZones; + this.servicePopupDataModel.owningEntities = _.get(service.categoryParameters, 'owningEntityList'); + this.servicePopupDataModel.projects = _.get(service.categoryParameters, 'projectList'); + this.onDataChanged.next(); + } + + subscribeToFormChanges(): void { + this.serviceInstanceDetailsFormGroup.get('globalSubscriberId').valueChanges.subscribe(val => { + this.updateServiceTypes(val); + this.setDisabledState(val, 'subscriptionServiceType'); + + }); + this.serviceInstanceDetailsFormGroup.get('subscriptionServiceType').valueChanges.subscribe(val => { + this.getTenants(val); + this.setDisabledState(val, 'lcpCloudRegionId'); + + }); + this.serviceInstanceDetailsFormGroup.get('lcpCloudRegionId').valueChanges.subscribe(val => { + this.setDisabledState(val, 'tenantId'); + this.updateTenantList(val); + + }); + + this.serviceInstanceDetailsFormGroup.get('tenantId').valueChanges.subscribe(val => { + this.serviceDetails.tenantName = this.getNameFromListById(this.servicePopupDataModel.tenants, val); + this.onDataChanged.next(); + }); + + this.serviceInstanceDetailsFormGroup.get('aicZoneId').valueChanges.subscribe(val => { + this.serviceDetails.aicZoneName = this.getNameFromListById(this.servicePopupDataModel.aicZones, val); + this.onDataChanged.next(); + }); + } + + getNameFromListById(list, id:string ) { + if(list && id) { + let filterItem = list.filter(item => { + return item.id == id; + }) + return filterItem && filterItem[0].name; + } + return null; + } + + setDisabledState(val, field: string): void { + if(val) { + this.serviceInstanceDetailsFormGroup.controls[field].enable(); + } else { + this.serviceInstanceDetailsFormGroup.controls[field].disable(); + } + } + + isShowingNotificationArea(): boolean { + return this.servicesQty > 1; + } + + updateServiceTypes(subscriberId) { + if (subscriberId) { + this.servicePopupDataModel.globalCustomerId = subscriberId; + this._aaiService.getServiceTypes(subscriberId).subscribe(() => { + this.updateFormData(); + this.onDataChanged.next(); + }, (error) => { + + }); + } + } + + updateTenantList(cloudRegionId) { + this.servicePopupDataModel.tenants = this.store.getState().service.lcpRegionsAndTenants.lcpRegionsTenantsMap[cloudRegionId]; + this.onDataChanged.next(); + } + + getTenants(serviceType) { + if (serviceType) { + this._aaiService.getLcpRegionsAndTenants(this.servicePopupDataModel.globalCustomerId, serviceType).subscribe(()=>{ + this.onDataChanged.next(); + }); + } + } + + onSubmit(formValues): void { + formValues.bulkSize = this.servicesQty; + let dynamicFields: { [dynamicField: string] : string; }; + dynamicFields = {}; + this.dynamicInputs.map(function (x) { + let dynamicField: string = x.id; + dynamicFields[dynamicField] = formValues[dynamicField]; + delete formValues[dynamicField]; + }); + formValues.instanceParams = []; + formValues.instanceParams.push(dynamicFields); + formValues.modelInfo = new ModelInfo(this._serviceModel); + Object.assign(formValues, this.serviceDetails); + this.store.dispatch(updateServiceInstance(formValues, this._serviceModel.uuid)); + if (this.store.getState().global.flags['FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD']){ + this._defaultDataGeneratorService.updateReduxOnFirstSet(this._serviceModel.uuid,formValues); + } + window.parent.postMessage( { + eventId: 'submitIframe', + data: { + serviceModelId: this._serviceModel.uuid + } + }, "*"); + this.closePopup.emit(this._serviceModel.uuid); + } + + hasApiError(controlName : string, data : Array<any>){ + if(!isNullOrUndefined(this.servicePopupDataModel) && !isNullOrUndefined(data)){ + if(!this.serviceInstanceDetailsFormGroup.controls[controlName].disabled && data.length === 0){ + return true; + } + } + return false; + } + +} diff --git a/vid-webpack-master/src/app/components/service-popup/service-instance-details/service-instance-details.html b/vid-webpack-master/src/app/components/service-popup/service-instance-details/service-instance-details.html new file mode 100644 index 00000000..3d632bd8 --- /dev/null +++ b/vid-webpack-master/src/app/components/service-popup/service-instance-details/service-instance-details.html @@ -0,0 +1,168 @@ + +<div id="service-instance-details"> + <form id="serviceForm" #serviceForm="ngForm" (ngSubmit)="onSubmit(serviceForm.value)" [formGroup]="serviceInstanceDetailsFormGroup"> + <!--We can't use [hidden] since bootstrap.css label has display: inline-block. --> + <!--see https://stackoverflow.com/questions/34650410/angular-2-hidden-does-not-seem-to-be-working--> + <label id="notification-area" *ngIf="isShowingNotificationArea()">Data entered will apply to all service instances</label> + + <div class="details-item" *ngIf="serviceInstanceDetailsFormGroup.get('instanceName')"> + <label class="required">Instance name:</label> + <input patternInput + pattern="^[a-zA-Z0-9_]*$" + [attr.data-tests-id]="'instanceName'" + id="instance-name" + name="instance-name" + [ngClass]="{'error-style' :(serviceInstance?.instanceName != '' && serviceInstanceDetailsFormGroup.controls['instanceName']?.touched && serviceInstanceDetailsFormGroup.controls['instanceName']?.errors?.pattern !== null)}" + [formControlName]="'instanceName'" + class="form-control input-text" + placeholder="Type Instance Name" + type="text" + [(ngModel)]="serviceInstance.instanceName" required> + <form-control-error + *ngIf="serviceInstance?.instanceName != '' && serviceInstanceDetailsFormGroup.controls['instanceName']?.touched && serviceInstanceDetailsFormGroup.controls['instanceName']?.errors?.pattern !== null" + [message]="'Instance name may include only alphanumeric characters and underscore.'"></form-control-error> + + </div> + + <div class="details-item"> + <label class="required">Subscriber name:</label> + <select class="subscriber form-control input-text" id="subscriber-name-select" data-tests-id="subscriberName" + name="subscriber-name-select" [formControlName]="'globalSubscriberId'" + [ngClass]="{'error-style' :serviceInstanceDetailsService.hasApiError('globalSubscriberId',servicePopupDataModel?.subscribers, serviceInstanceDetailsFormGroup)}" + [(ngModel)]="serviceInstance.globalSubscriberId" + required> + <option [value]="undefined" disabled>Select Subscriber Name</option> + <option class="subscriberNameOption" *ngFor="let subscriber of servicePopupDataModel.subscribers" + [value]="subscriber.id" [disabled]="!subscriber.isPermitted">{{subscriber.name}}</option> + </select> + <form-control-error *ngIf="serviceInstanceDetailsService.hasApiError('globalSubscriberId',servicePopupDataModel?.subscribers, serviceInstanceDetailsFormGroup)" [message]="'No results for this request. Please change criteria.'"></form-control-error> + + </div> + + <div class="details-item"> + <label class="required">Service type:</label> + <select class="form-control input-text" + [(ngModel)]="serviceInstance.subscriptionServiceType" + [formControlName]="'subscriptionServiceType'" + [ngClass]="{'error-style' :serviceInstanceDetailsService.hasApiError('subscriptionServiceType',servicePopupDataModel?.serviceTypes, serviceInstanceDetailsFormGroup)}" + data-tests-id="serviceType" id="service-type-select" + name="service-type" required> + <option [value]="undefined" disabled>Select Service Type</option> + <option *ngFor="let serviceType of servicePopupDataModel.serviceTypes" class="serviceTypeOption" [value]="serviceType.name" [disabled]="!serviceType.isPermitted">{{serviceType.name}}</option> + </select> + <form-control-error *ngIf="serviceInstanceDetailsService.hasApiError('subscriptionServiceType',servicePopupDataModel?.serviceTypes, serviceInstanceDetailsFormGroup)" [message]="'No results for this request. Please change criteria.'"></form-control-error> + </div> + + <div class="details-item"> + <label class="required">Product family:</label> + <select class="form-control input-text" + data-tests-id="productFamily" + id="product-family-select" + [ngClass]="{'error-style' :serviceInstanceDetailsService.hasApiError('productFamilyId',productFamilies, serviceInstanceDetailsFormGroup)}" + [formControlName]="'productFamilyId'" + [(ngModel)]="serviceInstance.productFamilyId" + name="product-family-select" required> + <option [value]="undefined" disabled>Select Product Family</option> + <option *ngFor="let productFamily of productFamilies | async" [value]="productFamily.id" + [disabled]="!productFamily.isPermitted">{{productFamily.name}}</option> + </select> + <form-control-error *ngIf="serviceInstanceDetailsService.hasApiError('productFamilyId',productFamilies, serviceInstanceDetailsFormGroup)" [message]="'No results for this request. Please change criteria.'"></form-control-error> + </div> + + <div class="details-item"> + <label class="required">LCP region:</label> + <select + class="form-control input-text" + [formControlName]="'lcpCloudRegionId'" + [(ngModel)]="serviceInstance.lcpCloudRegionId" + [ngClass]="{'error-style ' : serviceInstanceDetailsService.hasApiError('lcpCloudRegionId', servicePopupDataModel?.lcpRegions, serviceInstanceDetailsFormGroup)}" + name="lcpRegion" + id="lcpRegion-select" + data-tests-id="lcpRegion" + required> + <option [value]="undefined" disabled>Select LCP Region</option> + <option *ngFor="let lcpRegion of servicePopupDataModel.lcpRegions" [value]="lcpRegion.id" [disabled]="!lcpRegion.isPermitted" class="lcpRegionOption">{{lcpRegion.id}}</option> + </select> + <form-control-error *ngIf="serviceInstanceDetailsService.hasApiError('lcpCloudRegionId', servicePopupDataModel?.lcpRegions, serviceInstanceDetailsFormGroup)" [message]="'No results for this request. Please change criteria.'"></form-control-error> + </div> + + <div class="details-item"> + <label class="required">Tenant:</label> + <select class="form-control input-text" + [formControlName]="'tenantId'" + [(ngModel)]="serviceInstance.tenantId" + name="tenant" id="tenant-select" + [ngClass]="{'error-style ' : serviceInstanceDetailsService.hasApiError('tenantId',servicePopupDataModel?.tenants ,serviceInstanceDetailsFormGroup)}" + data-tests-id="tenant" required> + <option [value]="undefined" disabled>Select Tenant</option> + <option *ngFor="let tenant of servicePopupDataModel.tenants" [value]="tenant.id" [disabled]="!tenant.isPermitted">{{tenant.name}}</option> + </select> + <form-control-error *ngIf="serviceInstanceDetailsService.hasApiError('tenantId',servicePopupDataModel?.tenants ,serviceInstanceDetailsFormGroup)" [message]="'No results for this request. Please change criteria.'"></form-control-error> + </div> + + <div class="details-item"> + <label>AIC Zone:</label> + <select + class="form-control input-text" + name="aicZone" id="aicZone-select" + data-tests-id="aic_zone" + [formControlName]="'aicZoneId'" + [ngClass]="{'error-style ' : servicePopupDataModel?.aicZones?.length == 0 && serviceInstanceDetailsFormGroup.controls['aicZoneId'].disabled == false}" + [(ngModel)]="serviceInstance.aicZoneId" > + <option [value]="undefined" disabled>Select AIC Zone</option> + <option class="aicZoneOption" *ngFor="let aicZone of servicePopupDataModel.aicZones" [value]="aicZone.id">{{aicZone.name}}</option> + </select> + <form-control-error *ngIf="serviceInstanceDetailsService.hasApiError('aicZoneId',servicePopupDataModel?.aicZones ,serviceInstanceDetailsFormGroup)" [message]="'No results for this request. Please change criteria.'"></form-control-error> + + </div> + + <div class="details-item"> + <label>Project:</label> + <select + [attr.data-tests-id]="'project'" + class="form-control input-text" + [ngClass]="{'error-style ' : servicePopupDataModel?.projects?.length == 0 && serviceInstanceDetailsFormGroup.controls['projectName'].disabled == false}" + name="project" id="project" + [formControlName]="'projectName'" + [(ngModel)]="serviceInstance.projectName" > + <option [value]="undefined" disabled>Select Project</option> + <option *ngFor="let project of servicePopupDataModel.projects" [value]="project.id">{{project.name}}</option> + </select> + <form-control-error *ngIf="serviceInstanceDetailsService.hasApiError('projectName',servicePopupDataModel?.projects ,serviceInstanceDetailsFormGroup)" [message]="'No results for this request. Please change criteria.'"></form-control-error> + + </div> + + <div class="details-item"> + <label class="required">Owning entity:</label> + <select [attr.data-tests-id]="'owningEntity'" + class="form-control input-text" + [formControlName]="'owningEntityId'" + [(ngModel)]="serviceInstance.owningEntityId" + name="owningEntity" id="owningEntity" + [ngClass]="{'error-style ' : servicePopupDataModel?.owningEntities?.length == 0 && serviceInstanceDetailsFormGroup.controls['owningEntityId'].disabled == false}" + required> + <option [value]="undefined" disabled>Select Owning Entity</option> + <option *ngFor="let owningEntity of servicePopupDataModel.owningEntities" [value]="owningEntity.id">{{owningEntity.name}}</option> + </select> + <form-control-error *ngIf="serviceInstanceDetailsService.hasApiError('owningEntityId',servicePopupDataModel?.owningEntities ,serviceInstanceDetailsFormGroup)" [message]="'No results for this request. Please change criteria.'"></form-control-error> + </div> + <div class="details-item"> + <label class="required">Rollback On Failure:</label> + <select [attr.data-tests-id]="'rollback'" + [ngClass]="{'error-style' :serviceInstanceDetailsService.hasApiError('rollbackOnFailure',servicePopupDataModel?.rollbackOnFailure, serviceInstanceDetailsFormGroup)}" + class="form-control input-text" + [(ngModel)]="serviceInstance.rollbackOnFailure" + [formControlName]="'rollbackOnFailure'" name="rollbackOnFailure" id="rollbackOnFailure"> + <option *ngFor="let option of servicePopupDataModel.rollbackOnFailure" [value]="option.id">{{option.name}}</option> + </select> + <form-control-error *ngIf="serviceInstanceDetailsService.hasApiError('rollbackOnFailure',servicePopupDataModel?.rollbackOnFailure, serviceInstanceDetailsFormGroup)" [message]="'No results for this request. Please change criteria.'"></form-control-error> + </div> + <div class="details-item" *ngIf="serviceInstanceDetailsFormGroup.get('pause')"> + <input #pause id="pause" [formControlName]="'pause'" [(ngModel)]="serviceInstance.pause" type="checkbox" name="pause" data-toggle="toggle"> + <label class="checkbox-label" for="pause">Pause on pause points:</label> + </div> + + + <dynamic-inputs *ngIf="dynamicInputs != undefined && dynamicInputs.length>0" [group]="serviceInstanceDetailsFormGroup" [list]="dynamicInputs"></dynamic-inputs> + </form> +</div> diff --git a/vid-webpack-master/src/app/components/service-popup/service-instance-details/service-instance-details.scss b/vid-webpack-master/src/app/components/service-popup/service-instance-details/service-instance-details.scss new file mode 100644 index 00000000..928343d4 --- /dev/null +++ b/vid-webpack-master/src/app/components/service-popup/service-instance-details/service-instance-details.scss @@ -0,0 +1,64 @@ +#service-instance-details { + position: relative; + + #notification-area { + color: #959595; + font-size: 12px; + position: absolute; + top: 3px; + left: 30px; + } + + height: 100%; + overflow: auto; + padding: 30px; + + /deep/ { + .form-control { + border-radius: 2px; + box-shadow: none; + border-color: #D2D2D2; + } + + label { + font-family: OpenSans-Semibold; + font-size: 12px; + } + + select { + @extend .form-control; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: url('../../../../assets/img/chevron.svg') 0 0 no-repeat; + background-size: 24px; + background-position-x: right; + background-position-y: center; + font-family: OpenSans-Italic; + font-size: 14px; + color: #959595; + height: 38px; + } + + input:not([type='checkbox']) { + @extend .form-control; + height: 38px; + } + + .form-control[disabled], fieldset[disabled] .form-control { + opacity: 0.5; + } + .input-text { + border: 1px solid #D2D2D2; + border-radius: 2px; + } + + .details-item { + margin-bottom: 20px; + } + } + + .checkbox-label { + font-family: OpenSans-Regular; + } +} diff --git a/vid-webpack-master/src/app/components/service-popup/service-instance-details/service-instance-details.service.spec.ts b/vid-webpack-master/src/app/components/service-popup/service-instance-details/service-instance-details.service.spec.ts new file mode 100644 index 00000000..605653bd --- /dev/null +++ b/vid-webpack-master/src/app/components/service-popup/service-instance-details/service-instance-details.service.spec.ts @@ -0,0 +1,26 @@ +import { getTestBed, TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { ServiceInstanceDetailsService } from './service-instance-details.service'; +import { NgRedux } from '@angular-redux/store'; + +export class MockAppStore<T> {} + +describe('Service instance details service', () => { + let injector; + let service: ServiceInstanceDetailsService; + let httpMock: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ServiceInstanceDetailsService, + {provide: NgRedux, useClass: MockAppStore}] + }); + + injector = getTestBed(); + service = injector.get(ServiceInstanceDetailsService); + httpMock = injector.get(HttpTestingController); + }); +}); + + diff --git a/vid-webpack-master/src/app/components/service-popup/service-instance-details/service-instance-details.service.ts b/vid-webpack-master/src/app/components/service-popup/service-instance-details/service-instance-details.service.ts new file mode 100644 index 00000000..99b390d2 --- /dev/null +++ b/vid-webpack-master/src/app/components/service-popup/service-instance-details/service-instance-details.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { isNullOrUndefined } from 'util'; +import { FormGroup } from '@angular/forms'; +import * as _ from 'lodash'; +import { createVFModuleInstance, updateVFModuleInstance, updateVNFInstance } from '../../../service.actions'; +import { NgRedux } from '@angular-redux/store'; +import { AppState } from '../../../store/reducers'; + +@Injectable() +export class ServiceInstanceDetailsService { + static controlsFieldsStatus = {}; + + constructor(private store: NgRedux<AppState>) { } + hasApiError(controlName: string, data: Array<any>, serviceInstanceDetailsFormGroup: FormGroup) { + if (!isNullOrUndefined(data)) { + if (!serviceInstanceDetailsFormGroup.controls[controlName].disabled && data.length === 0) { + return true; + } + } + return false; + } +} diff --git a/vid-webpack-master/src/app/components/service-popup/service-instance-details/servicePopupDataModel.ts b/vid-webpack-master/src/app/components/service-popup/service-instance-details/servicePopupDataModel.ts new file mode 100644 index 00000000..c7894e2c --- /dev/null +++ b/vid-webpack-master/src/app/components/service-popup/service-instance-details/servicePopupDataModel.ts @@ -0,0 +1,32 @@ +import {SelectOption, SelectOptionInterface} from "../../../shared/models/selectOption"; + +export class ServicePopupDataModel { + subscribers: SelectOptionInterface[]; + serviceTypes: SelectOptionInterface[]; + aicZones: SelectOptionInterface[]; + lcpRegions: SelectOptionInterface[]; + productFamilies: SelectOptionInterface[]; + lcpRegionsTenantsMap: object; + tenants: SelectOptionInterface[]; + projects: SelectOptionInterface[]; + owningEntities: SelectOptionInterface[]; + globalCustomerId: string; + rollbackOnFailure: SelectOptionInterface[]; + + + constructor(){ + this.subscribers = null; + this.serviceTypes = null; + this.aicZones = null; + this.lcpRegions = null; + this.lcpRegionsTenantsMap = {}; + this.tenants = null; + this.productFamilies = null; + this.projects = null; + this.owningEntities = null; + this.rollbackOnFailure = [ + new SelectOption({id: 'true', name: 'Rollback'}), + new SelectOption({id: 'false', name: 'Don\'t Rollback'}) + ]; + } +} diff --git a/vid-webpack-master/src/app/components/service-popup/service-popup.component.ts b/vid-webpack-master/src/app/components/service-popup/service-popup.component.ts new file mode 100644 index 00000000..908ae4ad --- /dev/null +++ b/vid-webpack-master/src/app/components/service-popup/service-popup.component.ts @@ -0,0 +1,144 @@ +import {Component, ViewChild} from '@angular/core'; +import {DialogComponent, DialogService} from 'ng2-bootstrap-modal'; +import {ServiceModel} from '../../shared/models/serviceModel'; +import {Constants} from '../../shared/utils/constants'; +import {ServiceInstanceDetailsComponent} from './service-instance-details/service-instance-details.component'; +import {ActivatedRoute} from "@angular/router"; +import {AaiService} from "../../services/aaiService/aai.service"; +import {Utils} from "../../utils/utils"; +import {ServicePlanningService} from "../../services/service-planning.service"; +import * as _ from 'lodash'; +import {ModelInformationItem} from '../../shared/components/model-information/model-information.component'; +import {deleteServiceInstance} from '../../service.actions'; + +import {InstancePopup} from "../instance-popup/instance-popup.components"; +import {NgRedux} from "@angular-redux/store"; +import {AppState} from "../../store/reducers"; +import {ServicePopupService} from './service-popup.service'; +import {IframeService} from "../../shared/utils/iframe.service"; + +export interface ServicePopupModel { + serviceModel: ServiceModel +} + +@Component({ + selector: 'service-popup', + templateUrl: 'service-popup.html', + styleUrls: ['service-popup.scss'], + providers: [AaiService, ServicePopupService] +}) + +export class ServicePopupComponent extends DialogComponent<ServicePopupModel, boolean> + implements ServicePopupModel, InstancePopup{ + @ViewChild(ServiceInstanceDetailsComponent) serviceInstanceDetails: ServiceInstanceDetailsComponent; + + serviceModel: ServiceModel; + serviceModelId: string; + serviceInstance: any = { + 'rollbackOnFailure' : 'false' + }; + title: string = Constants.ServicePopup.TITLE; + dynamicInputs: any[] = null; + + maxServiceQty:number = 50; + minServiceQty:number = 1; + servicesQty = 1; //default + quantityOptions = this.getQuantityOptions(); + + modelInformationItems: Array<ModelInformationItem> = []; + hasGeneralApiError : boolean = false; + parentElementClassName = 'content'; + + constructor(dialogService: DialogService, private route: ActivatedRoute, private _aaiService: AaiService, + private _iframeService : IframeService, + private _servicePlanningService: ServicePlanningService, private store: NgRedux<AppState>, private _servicePopupService : ServicePopupService) { + super(dialogService); + this.title = Constants.ServicePopup.TITLE; + } + + updateGeneralErrorSection() : void { + this.hasGeneralApiError = this._servicePopupService.onControlError(this.serviceInstanceDetails, this.serviceInstanceDetails.serviceInstanceDetailsFormGroup); + } + + + ngOnInit() { + this.route + .queryParams + .subscribe(params => { + this.serviceModelId = params['serviceModelId']; + if(params['isCreate']=="true") { + this.store.dispatch(deleteServiceInstance(this.serviceModelId)); + } + this.updateServiceModelById(this.serviceModelId); + this.updateInstanceFromStore(); + }); + } + + updateInstanceFromStore() { + let serviceInstance; + if (_.has(this.store.getState().service.serviceInstance, this.serviceModelId)) { + serviceInstance = Object.assign({}, this.store.getState().service.serviceInstance[this.serviceModelId]); + } + + this.serviceInstance = serviceInstance ? serviceInstance : this.serviceInstance; + this.servicesQty = serviceInstance ? serviceInstance.bulkSize : 1; + if (serviceInstance && serviceInstance.instanceParams && serviceInstance.instanceParams[0]) { + this.dynamicInputs = this.dynamicInputs.map(function (x) { + x.value = (serviceInstance.instanceParams[0][x.id]) ? serviceInstance.instanceParams[0][x.id] : x.value; + return x; + }); + } + } + + updateServiceModelById(serviceModelId) { + this._aaiService.getServiceModelById(serviceModelId).subscribe( + value => { + const convertedModel = Utils.convertModel(value); + this.serviceModel = new ServiceModel(convertedModel); + let displayInputs = Object.assign({},convertedModel.service.inputs); + this.dynamicInputs = _.isEmpty(displayInputs)? [] : this._servicePlanningService.getArbitraryInputs(displayInputs); + this.modelInformationItems = this.createModelInformationItems(); + }, + error => {console.log('error is ', error)}, + () => {console.log('completed')} + ); + } + + createModelInformationItems() : Array<ModelInformationItem> { + return [ + new ModelInformationItem("Model version", "modelVersion", [this.serviceModel.version], "", true), + new ModelInformationItem("Description", "description", [this.serviceModel.description]), + new ModelInformationItem("Category", "category", [this.serviceModel.category]), + new ModelInformationItem("UUID", "uuid", [this.serviceModel.uuid], Constants.ServicePopup.TOOLTIP_UUID, true), + new ModelInformationItem("Invariant UUID", "invariantUuid", [this.serviceModel.invariantUuid], Constants.ServicePopup.TOOLTIP_INVARIANT_UUID, true), + new ModelInformationItem("Service type", "serviceType", [this.serviceModel.serviceType]), + new ModelInformationItem("Service role", "serviceRole", [this.serviceModel.serviceRole]) + ]; + } + + onCancelClick() { + this._iframeService.removeClassCloseModal(this.parentElementClassName); + this.dialogService.removeDialog(this); + this.serviceInstance = this.serviceInstanceDetails.oldServiceInstance; + + this._servicePopupService.resetDynamicInputs(this.serviceInstanceDetails, this.dynamicInputs); + // Delaying the iframe close in few milliseconds. + // This should workaround a problem in Selenium tests' that + // blocks after click because the iframe goes out before + // the driver understands it was clicked. Similar bug is + // described here: + // - https://github.com/mozilla/geckodriver/issues/611 + // - https://bugzilla.mozilla.org/show_bug.cgi?id=1223277 + setTimeout(() => { + window.parent.postMessage("closeIframe", "*"); + }, 15); + } + + getModelName(): string { + return (this.serviceModel && this.serviceModel.name) || ""; + } + + getQuantityOptions(){ + return _.range(this.minServiceQty, this.maxServiceQty + 1); + } +} diff --git a/vid-webpack-master/src/app/components/service-popup/service-popup.html b/vid-webpack-master/src/app/components/service-popup/service-popup.html new file mode 100644 index 00000000..e967daa3 --- /dev/null +++ b/vid-webpack-master/src/app/components/service-popup/service-popup.html @@ -0,0 +1,52 @@ +<div id="service-popup" class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" (click)="onCancelClick()" >×</button> + <span [attr.data-tests-id]="'create-modal-title'" class="modal-title">{{title}}</span> + </div> + <div class="modal-body popup-content"> + + <div class="header-left"> + <div>SERVICE MODEL: <span>"{{getModelName()}}"</span></div> + </div> + + <div class="header-right"> + Service Instance Details + </div> + + <label class="quantity-label">Qty:</label> + <div class="quantity"> + <select class="quantity-select" [(ngModel)]="servicesQty" name="quantity" id="quantity-select" required> + <option *ngFor="let qty of quantityOptions" [value]="qty">{{qty}}</option> + </select> + </div> + + <div class="service-model"> + + <model-information [modelInformationItems]="modelInformationItems"></model-information> + </div> + + <div class="service-instance"> + <service-instance-details [dynamicInputs]="dynamicInputs" + [serviceInstance]="serviceInstance" + [serviceModel]="serviceModel" + [servicesQty]="servicesQty" + (onDataChanged)="updateGeneralErrorSection()" + (closePopup)="onCancelClick($event)"></service-instance-details> + </div> + + </div> + <div class="modal-footer row" style="padding: 0"> + <div class="col-md-6"> + <div *ngIf="hasGeneralApiError == true"> + <form-general-error [message]="'Page contains errors. Please see details next to the relevant fields.'"></form-general-error> + </div> + </div> + <div class="col-md-6" style="padding: 15px;padding-right: 35px;"> + <button [attr.data-tests-id]="'cancelButton'" type="button" class="btn btn-default cancel" (click)="onCancelClick()"><span>Cancel</span></button> + <input type="submit" value="Set" form="serviceForm" data-tests-id="service-form-set" + class="btn btn-success submit" [disabled]="!serviceInstanceDetails?.serviceForm?.valid"> + </div> + </div> + </div> +</div> diff --git a/vid-webpack-master/src/app/components/service-popup/service-popup.scss b/vid-webpack-master/src/app/components/service-popup/service-popup.scss new file mode 100644 index 00000000..aa4552d2 --- /dev/null +++ b/vid-webpack-master/src/app/components/service-popup/service-popup.scss @@ -0,0 +1,185 @@ +$grid-border: 1px #d2d2d2 solid; + +#service-popup { + color: #191919; + + .left-panel { + background: #f2f2f2; + border-right: $grid-border; + } + + .header-common { + height: 100%; + align-items: center; + display: flex; + font-family: OpenSans-Semibold; + font-size: 12px; + } + + .header-text { + padding-left: 30px; + @extend .header-common; + } + + .header-left { + grid-area: header-left; + @extend .header-text; + @extend .left-panel; + border-bottom: $grid-border; + + span { + font-family: OpenSans-Regular; + font-size: 14px; + }; + } + + .header-right { + grid-area: header-right; + + @extend .header-text; + border-bottom: $grid-border; + } + + .quantity-label { + grid-area: quantity-label; + @extend .header-common; + border-bottom: $grid-border; + height: 100%; + font-family: OpenSans-Regular; + } + + .quantity { + grid-area: quantity; + border-left: $grid-border; + border-bottom: $grid-border; + border-top-style: none; + font-family: OpenSans-Semibold; + text-align: start; + text-indent: 10px; + } + + .quantity-select { + width: 78px; + height: 100%; + border: 0; + background: white; + outline: none; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: url('../../../assets/img/chevron.svg') 0 0 no-repeat; + background-size: 24px; + background-position-x: right; + background-position-y: center; + } + input[type="number"]:hover::-webkit-inner-spin-button { + height: 20px; + } + + .service-model { + grid-area: service-model; + + padding: 30px; + overflow: auto; + @extend .left-panel; + } + + .service-instance { + grid-area: service-instance; + } + + .popup-content { + display: grid; + grid-template-columns: 400px auto 30px 93px; + grid-template-rows: 50px calc(100vh - 180px); + grid-template-areas: + "header-left header-right quantity-label quantity" + "service-model service-instance service-instance service-instance"; + padding: 0; + } +} + +.modal { + background-color: #191919; + opacity: 0.8; +} + +.modal-dialog { + position: relative; + width: auto; + margin: 0; +} +@media (min-width: 1150px) { + .popup-content { + grid-template-rows: 30px 680px; + } +} + +.modal-content { + border-radius: 0; + box-shadow: none; + border: none; +} + +.modal-footer { + .cancel { + width: 120px; + height: 36px; + background: #ffffff; + border: 1px solid #009fdb; + border-radius: 2px; + span { + font-family: OpenSans-Regular; + font-size: 14px; + color: #009fdb; + line-height: 16px; + } + } + + .submit { + width: 120px; + height: 36px; + background: #009fdb; + border-radius: 2px; + border-color: #009fdb; + span { + font-family: OpenSans-Regular; + font-size: 14px; + color: #FFFFFF; + line-height: 16px; + } + } +} + +.modal-header { + background-color: #009fdb; + + padding-bottom: 13px; + padding-top: 13px; + padding-left: 29px; + padding-right: 21px; + + .close { + font-size: 32px; + font-weight: 200; + color: #d8d8d8; + text-shadow: none; + filter: none; + opacity: 1; + } + + .modal-title { + font-family: OpenSans-Regular; + font-size: 24px; + color: #fff; + line-height: 34px; + } +} +// +//@media (min-width: 1200px) { +// .service-model, +// .service-instance { +// width: 1050px; +// margin: 30px auto; +// } +//} diff --git a/vid-webpack-master/src/app/components/service-popup/service-popup.service.spec.ts b/vid-webpack-master/src/app/components/service-popup/service-popup.service.spec.ts new file mode 100644 index 00000000..cddc6400 --- /dev/null +++ b/vid-webpack-master/src/app/components/service-popup/service-popup.service.spec.ts @@ -0,0 +1,138 @@ + +import { TestBed, getTestBed} from '@angular/core/testing'; +import { + HttpClientTestingModule, + HttpTestingController +} from '@angular/common/http/testing'; +import { ServicePopupService } from './service-popup.service'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { NumbersLettersUnderscoreValidator } from '../../shared/components/validators/numbersLettersUnderscore/numbersLettersUnderscore.validator'; + +describe('Service Popup Service', () => { + let injector; + let service: ServicePopupService; + let httpMock: HttpTestingController; + let form : FormGroup; + let servicePopupDataModel; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ServicePopupService] + }); + + injector = getTestBed(); + service = injector.get(ServicePopupService); + httpMock = injector.get(HttpTestingController); + + form = generateFormGroup(); + servicePopupDataModel = generateServicePopupDataModel(); + }); + + describe('#resetDynamicInputs', () => { + it('resetDynamicInputs should reset dymanic fields',(done: DoneFn) => { + const dynamicInputs = generateDynamicInputs(); + let serviceForm = generateFormGroup(); + serviceForm.addControl(dynamicInputs[0].name, new FormControl({value: dynamicInputs[0].value, disabled: false})); + + serviceForm.controls[dynamicInputs[0].name].setValue("diffValue"); + service.resetDynamicInputs({ + serviceInstanceDetailsFormGroup : serviceForm, + dynamicInputs : dynamicInputs + }, dynamicInputs); + + expect(serviceForm.controls[dynamicInputs[0].name].value).toEqual(dynamicInputs[0].value); + done(); + }) + }); + + describe('#onControlError', () => { + + it('should return true if instanceName is illegal', (done: DoneFn) => { + form.controls['instanceName'].setValue("illegal - illegal"); + + let result : boolean = service.onControlError(<any>servicePopupDataModel, form); + expect(result).toBeTruthy(); + done(); + }); + + it('should return false if instanceName is legal', (done: DoneFn) => { + + form.controls['instanceName'].setValue("legal"); + let result = service.onControlError(<any>servicePopupDataModel, form); + expect(result).toBeFalsy(); + done(); + }); + + it('should return false if lcpRegions is empty and is disabled', (done: DoneFn) => { + servicePopupDataModel.servicePopupDataModel['lcpRegions'] = []; + let result = service.onControlError(<any>servicePopupDataModel, form); + expect(result).toBeFalsy(); + done(); + }); + + it('should return true if lcpRegions is empty', (done: DoneFn) => { + servicePopupDataModel.servicePopupDataModel['lcpRegions'] = []; + form.controls['lcpCloudRegionId'].enable(); + let result = service.onControlError(<any>servicePopupDataModel, form); + expect(result).toBeTruthy(); + done() + }); + }); + + + function generateDynamicInputs(){ + return JSON.parse('[{"id":"2017488_adiodvpe0_ASN","type":"string","name":"2017488_adiodvpe0_ASN","value":"AV_vPE","isRequired":true,"description":"AV/PE"}]'); + } + + + function generateServicePopupDataModel() { + return { + "servicePopupDataModel" : JSON.parse('{"subscribers":[{"id":"a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb","name":"Mobility","isPermitted":false},{"id":"a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fc","name":"PACKET CORE","isPermitted":false},{"id":"e433710f-9217-458d-a79d-1c7aff376d89","name":"USP VOICE","isPermitted":true}],"serviceTypes":[{"id":"0","name":"vFlowLogic","isPermitted":false},{"id":"1","name":"VIRTUAL USP","isPermitted":true},{"id":"2","name":"Mobility","isPermitted":false},{"id":"3","name":"vBNG","isPermitted":false},{"id":"4","name":"vVoiceMail","isPermitted":false},{"id":"5","name":"Nimbus","isPermitted":false},{"id":"6","name":"vSEGW","isPermitted":false},{"id":"7","name":"vVM","isPermitted":false},{"id":"8","name":"vOTA","isPermitted":false},{"id":"9","name":"vMME","isPermitted":false},{"id":"10","name":"vMNS","isPermitted":false},{"id":"11","name":"vSCP","isPermitted":false},{"id":"12","name":"VPMS","isPermitted":false},{"id":"13","name":"vMMSC","isPermitted":false},{"id":"14","name":"SSD","isPermitted":false},{"id":"15","name":"vMOG","isPermitted":false},{"id":"16","name":"FIRSTNET","isPermitted":false},{"id":"17","name":"ACTIVE_CHARGE","isPermitted":false},{"id":"18","name":"vHSS","isPermitted":false}],"aicZones":[{"id":"NFT1","name":"NFTJSSSS-NFT1"},{"id":"JAG1","name":"YUDFJULP-JAG1"},{"id":"YYY1","name":"UUUAIAAI-YYY1"},{"id":"BAN1","name":"VSDKYUTP-BAN1"},{"id":"DKJ1","name":"DKJSJDKA-DKJ1"},{"id":"MCS1","name":"ASACMAMS-MCS1"},{"id":"UIO1","name":"uioclli1-UIO1"},{"id":"RAJ1","name":"YGBIJNLQ-RAJ1"},{"id":"OPA1","name":"opaclli1-OPA1"},{"id":"SDE1","name":"ZXCVBNMA-SDE1"},{"id":"VEN2","name":"FGHJUHIL-VEN2"},{"id":"ORL1","name":"ORLDFLMA-ORL1"},{"id":"JAD1","name":"JADECLLI-JAD1"},{"id":"ZXL1","name":"LWLWCANN-ZXL1"},{"id":"CKL1","name":"CLKSKCKK-CKL1"},{"id":"SDF1","name":"sdfclli1-SDF1"},{"id":"RAD1","name":"RADICAL1-RAD1"},{"id":"KIT1","name":"BHYJFGLN-KIT1"},{"id":"REL1","name":"INGERFGT-REL1"},{"id":"JNL1","name":"CJALSDAC-JNL1"},{"id":"OLK1","name":"OLKOLKLS-OLK1"},{"id":"CHI1","name":"CHILLIWE-CHI1"},{"id":"UUU4","name":"UUUAAAUU-UUU4"},{"id":"TUF1","name":"TUFCLLI1-TUF1"},{"id":"KJN1","name":"CKALDKSA-KJN1"},{"id":"SAM1","name":"SNDGCA64-SAN1"},{"id":"SCK1","name":"SCKSCKSK-SCK1"},{"id":"HJH1","name":"AOEEQQQD-HJH1"},{"id":"HGD1","name":"SDFQWHGD-HGD1"},{"id":"KOR1","name":"HYFLNBVT-KOR1"},{"id":"ATL43","name":"AICLOCID-ATL43"},{"id":"ATL54","name":"AICFTAAI-ATL54"},{"id":"ATL66","name":"CLLIAAII-ATL66"},{"id":"VEL1","name":"BNMLKUIK-VEL1"},{"id":"ICC1","name":"SANJITAT-ICC1"},{"id":"MNT11","name":"WSXEFBTH-MNT11"},{"id":"DEF2","name":"WSBHGTYL-DEF2"},{"id":"MAD11","name":"SDFQWGKL-MAD11"},{"id":"OLG1","name":"OLHOLHOL-OLG1"},{"id":"GAR1","name":"NGFVSJKO-GAR1"},{"id":"SAN22","name":"GNVLSCTL-SAN22"},{"id":"HRG1","name":"HRGHRGGS-HRG1"},{"id":"JCS1","name":"JCSJSCJS-JCS1"},{"id":"DHA12","name":"WSXEDECF-DHA12"},{"id":"HJE1","name":"AOEEWWWD-HJE1"},{"id":"NCA1","name":"NCANCANN-NCA1"},{"id":"IOP1","name":"iopclli1-IOP1"},{"id":"RTY1","name":"rtyclli1-RTY1"},{"id":"KAP1","name":"HIOUYTRQ-KAP1"},{"id":"ZEN1","name":"ZENCLLI1-ZEN1"},{"id":"HKA1","name":"JAKHLASS-HKA1"},{"id":"CQK1","name":"CQKSCAKK-CQK1"},{"id":"SAI1","name":"UBEKQLPD-SAI1"},{"id":"ERT1","name":"ertclli1-ERT1"},{"id":"IBB1","name":"PLMKOIJU-IBB1"},{"id":"TIR2","name":"PLKINHYI-TIR2"},{"id":"HSD1","name":"CHASKCDS-HSD1"},{"id":"SLF78","name":"SDCTLFN1-SLF78"},{"id":"SEE78","name":"SDCTEEE4-SEE78"},{"id":"SAN13","name":"TOKYJPFA-SAN13"},{"id":"SAA78","name":"SDCTAAA1-SAA78"},{"id":"LUC1","name":"ATLDFGYC-LUC1"},{"id":"AMD13","name":"MEMATLAN-AMD13"},{"id":"TOR1","name":"TOROONXN-TOR1"},{"id":"QWE1","name":"QWECLLI1-QWE1"},{"id":"ZOG1","name":"ZOGASTRO-ZOG1"},{"id":"CAL33","name":"CALIFORN-CAL33"},{"id":"SHH78","name":"SDIT1HHH-SHH78"},{"id":"DSA1","name":"LKJHGFDS-DSA1"},{"id":"CLG1","name":"CLGRABAD-CLG1"},{"id":"BNA1","name":"BNARAGBK-BNA1"},{"id":"ATL84","name":"CANTTCOC-ATL84"},{"id":"APP1","name":"WBHGTYUI-APP1"},{"id":"RJN1","name":"RJNRBZAW-RJN1"},{"id":"EHH78","name":"SDCSHHH5-EHH78"},{"id":"mac10","name":"PKGTESTF-mac10"},{"id":"SXB78","name":"SDCTGXB1-SXB78"},{"id":"SAX78","name":"SDCTAXG1-SAX78"},{"id":"SYD1","name":"SYDNAUBV-SYD1"},{"id":"TOK1","name":"TOKYJPFA-TOK1"},{"id":"KGM2","name":"KGMTNC20-KGM2"},{"id":"DCC1b","name":"POIUYTGH-DCC1b"},{"id":"SKK78","name":"SDCTKKK1-SKK78"},{"id":"SGG78","name":"SDCTGGG1-SGG78"},{"id":"SJJ78","name":"SDCTJJJ1-SJJ78"},{"id":"SBX78","name":"SDCTBXG1-SBX78"},{"id":"LAG1","name":"LARGIZON-LAG1"},{"id":"IAA1","name":"QAZXSWED-IAA1"},{"id":"POI1","name":"PLMNJKIU-POI1"},{"id":"LAG1a","name":"LARGIZON-LAG1a"},{"id":"PBL1","name":"PBLAPBAI-PBL1"},{"id":"LAG45","name":"LARGIZON-LAG1a"},{"id":"MAR1","name":"MNBVCXZM-MAR1"},{"id":"HST70","name":"HSTNTX70-HST70"},{"id":"DCC1a","name":"POIUYTGH-DCC1a"},{"id":"TOL1","name":"TOLDOH21-TOL1"},{"id":"LON1","name":"LONEENCO-LON1"},{"id":"SJU78","name":"SDIT1JUB-SJU78"},{"id":"STN27","name":"HSTNTX01-STN27"},{"id":"SSW56","name":"ss8126GT-SSW56"},{"id":"SBB78","name":"SDIT1BBB-SBB78"},{"id":"DCC3","name":"POIUYTGH-DCC3"},{"id":"GNV1","name":"GNVLSCTL-GNV1"},{"id":"WAS1","name":"WASHDCSW-WAS1"},{"id":"TOY1","name":"TORYONNZ-TOY1"},{"id":"STT1","name":"STTLWA02-STT1"},{"id":"STG1","name":"STTGGE62-STG1"},{"id":"SLL78","name":"SDCTLLL1-SLL78"},{"id":"SBU78","name":"SDIT1BUB-SBU78"},{"id":"ATL2","name":"ATLNGANW-ATL2"},{"id":"BOT1","name":"BOTHWAKY-BOT1"},{"id":"SNG1","name":"SNGPSIAU-SNG1"},{"id":"NYC1","name":"NYCMNY54-NYC1"},{"id":"LAG1b","name":"LARGIZON-LAG1b"},{"id":"AMD15","name":"AMDFAA01-AMD15"},{"id":"SNA1","name":"SNANTXCA-SNA1"},{"id":"PLT1","name":"PLTNCA60-PLT1"},{"id":"TLP1","name":"TLPNXM18-TLP1"},{"id":"SDD81","name":"SAIT1DD6-SDD81"},{"id":"DCC1","name":"POIUYTGH-DCC1"},{"id":"DCC2","name":"POIUYTGH-DCC2"},{"id":"OKC1","name":"OKCBOK55-OKC1"},{"id":"PAR1","name":"PARSFRCG-PAR1"},{"id":"TES36","name":"ABCEETES-TES36"},{"id":"COM1","name":"PLMKOPIU-COM1"},{"id":"ANI1","name":"ATLNGTRE-ANI1"},{"id":"SDG78","name":"SDIT1BDG-SDG78"},{"id":"mac20","name":"PKGTESTF-mac20"},{"id":"DSF45","name":"DSFBG123-DSF45"},{"id":"HST25","name":"HSTNTX01-HST25"},{"id":"AMD18","name":"AUDIMA01-AMD18"},{"id":"SAA80","name":"SAIT9AA3-SAA80"},{"id":"SSA56","name":"SSIT2AA7-SSA56"},{"id":"SDD82","name":"SAIT1DD9-SDD82"},{"id":"JCV1","name":"JCVLFLBW-JCV1"},{"id":"SUL2","name":"WERTYUJK-SUL2"},{"id":"PUR1","name":"purelyde-PUR1"},{"id":"FDE55","name":"FDERT555-FDE55"},{"id":"SITE","name":"LONEENCO-SITE"},{"id":"ATL1","name":"ATLNGAMA-ATL1"},{"id":"JUL1","name":"ZXCVBNMM-JUL1"},{"id":"TAT34","name":"TESAAISB-TAT34"},{"id":"XCP12","name":"CHKGH123-XCP12"},{"id":"RAI1","name":"poiuytre-RAI1"},{"id":"HPO1","name":"ATLNGAUP-HPO1"},{"id":"KJF12","name":"KJFDH123-KJF12"},{"id":"SCC80","name":"SAIT9CC3-SCC80"},{"id":"SAA12","name":"SAIT9AF8-SAA12"},{"id":"SAA14","name":"SAIT1AA9-SAA14"},{"id":"ATL35","name":"TTESSAAI-ATL35"},{"id":"CWY1","name":"CWYMOWBS-CWY1"},{"id":"ATL76","name":"TELEPAAI-ATL76"},{"id":"DSL12","name":"DSLFK242-DSL12"},{"id":"ATL53","name":"AAIATLTE-ATL53"},{"id":"SAA11","name":"SAIT9AA2-SAA11"},{"id":"ATL62","name":"TESSASCH-ATL62"},{"id":"AUG1","name":"ASDFGHJK-AUG1"},{"id":"POI22","name":"POIUY123-POI22"},{"id":"SAA13","name":"SAIT1AA9-SAA13"},{"id":"BHY17","name":"BHYTFRF3-BHY17"},{"id":"LIS1","name":"HOSTPROF-LIS1"},{"id":"SIP1","name":"ZXCVBNMK-SIP1"},{"id":"ATL99","name":"TEESTAAI-ATL43"},{"id":"ATL64","name":"FORLOAAJ-ATL64"},{"id":"TAT33","name":"TESAAISA-TAT33"},{"id":"RAD10","name":"INDIPUNE-RAD10"},{"id":"RTW5","name":"BHYTFRY4-RTW5"},{"id":"JGS1","name":"KSJKKKKK-JGS1"},{"id":"ATL98","name":"TEESTAAI-ATL43"},{"id":"WAN1","name":"LEIWANGW-WAN1"},{"id":"ATL44","name":"ATLSANAB-ATL44"},{"id":"RTD2","name":"BHYTFRk4-RTD2"},{"id":"NIR1","name":"ORFLMANA-NIR1"},{"id":"ATL75","name":"SANAAIRE-ATL75"},{"id":"NUM1","name":"QWERTYUI-NUM1"},{"id":"MTN32","name":"MDTWNJ21-MTN32"},{"id":"RTZ4","name":"BHYTFRZ6-RTZ4"},{"id":"ATL56","name":"ATLSANAC-ATL56"},{"id":"AMS1","name":"AMSTNLBW-AMS1"},{"id":"RCT1","name":"AMSTERNL-RCT1"},{"id":"JAN1","name":"ORFLMATT-JAN1"},{"id":"ABC14","name":"TESAAISA-ABC14"},{"id":"TAT37","name":"TESAAISD-TAT37"},{"id":"MIC54","name":"MICHIGAN-MIC54"},{"id":"ABC11","name":"ATLSANAI-ABC11"},{"id":"AMF11","name":"AMDOCS01-AMF11"},{"id":"ATL63","name":"ATLSANEW-ATL63"},{"id":"ABC12","name":"ATLSECIA-ABC12"},{"id":"MTN20","name":"MDTWNJ21-MTN20"},{"id":"ABC15","name":"AAITESAN-ABC15"},{"id":"AVT1","name":"AVTRFLHD-AVT1"},{"id":"ATL34","name":"ATLSANAI-ATL34"}],"lcpRegions":[{"id":"AAIAIC25","name":"AAIAIC25","isPermitted":true},{"id":"mtn6","name":"mtn6","isPermitted":true}],"lcpRegionsTenantsMap":{},"tenants":[{"id":"bae71557c5bb4d5aac6743a4e5f1d054","name":"AIN Web Tool-15-D-testgamma","isPermitted":true},{"id":"229bcdc6eaeb4ca59d55221141d01f8e","name":"AIN Web Tool-15-D-STTest2","isPermitted":true},{"id":"1178612d2b394be4834ad77f567c0af2","name":"AIN Web Tool-15-D-SSPtestcustome","isPermitted":true},{"id":"19c5ade915eb461e8af52fb2fd8cd1f2","name":"AIN Web Tool-15-D-UncheckedEcopm","isPermitted":true},{"id":"de007636e25249238447264a988a927b","name":"AIN Web Tool-15-D-dfsdf","isPermitted":true},{"id":"62f29b3613634ca6a3065cbe0e020c44","name":"AIN/SMS-16-D-Multiservices1","isPermitted":true},{"id":"649289e30d3244e0b48098114d63c2aa","name":"AIN Web Tool-15-D-SSPST66","isPermitted":true},{"id":"3f21eeea6c2c486bba31dab816c05a32","name":"AIN Web Tool-15-D-ASSPST47","isPermitted":true},{"id":"f60ce21d3ee6427586cff0d22b03b773","name":"CESAR-100-D-sspjg67246","isPermitted":true},{"id":"8774659e425f479895ae091bb5d46560","name":"CESAR-100-D-sspjg68359","isPermitted":true},{"id":"624eb554b0d147c19ff8885341760481","name":"AINWebTool-15-D-iftach","isPermitted":true},{"id":"214f55f5fc414c678059c383b03e4962","name":"CESAR-100-D-sspjg612401","isPermitted":true},{"id":"c90666c291664841bb98e4d981ff1db5","name":"CESAR-100-D-sspjg621340","isPermitted":true},{"id":"ce5b6bc5c7b348e1bf4b91ac9a174278","name":"sspjg621351cloned","isPermitted":true},{"id":"b386b768a3f24c8e953abbe0b3488c02","name":"AINWebTool-15-D-eteancomp","isPermitted":true},{"id":"dc6c4dbfd225474e9deaadd34968646c","name":"AINWebTool-15-T-SPFET","isPermitted":true},{"id":"02cb5030e9914aa4be120bd9ed1e19eb","name":"AINWebTool-15-X-eeweww","isPermitted":true},{"id":"f2f3830e4c984d45bcd00e1a04158a79","name":"CESAR-100-D-spjg61909","isPermitted":true},{"id":"05b91bd5137f4929878edd965755c06d","name":"CESAR-100-D-sspjg621512cloned","isPermitted":true},{"id":"7002fbe8482d4a989ddf445b1ce336e0","name":"AINWebTool-15-X-vdr","isPermitted":true},{"id":"4008522be43741dcb1f5422022a2aa0b","name":"AINWebTool-15-D-ssasa","isPermitted":true},{"id":"f44e2e96a1b6476abfda2fa407b00169","name":"AINWebTool-15-D-PFNPT","isPermitted":true},{"id":"b69a52bec8a84669a37a1e8b72708be7","name":"AINWebTool-15-X-vdre","isPermitted":true},{"id":"fac7d9fd56154caeb9332202dcf2969f","name":"AINWebTool-15-X-NONPODECOMP","isPermitted":true},{"id":"2d34d8396e194eb49969fd61ffbff961","name":"DN5242-Nov16-T5","isPermitted":true},{"id":"cb42a77ff45b48a8b8deb83bb64acc74","name":"ro-T11","isPermitted":true},{"id":"fa45ca53c80b492fa8be5477cd84fc2b","name":"ro-T112","isPermitted":true},{"id":"4914ab0ab3a743e58f0eefdacc1dde77","name":"DN5242-Nov21-T1","isPermitted":true},{"id":"d0a3e3f2964542259d155a81c41aadc3","name":"test-mtn6-09","isPermitted":true},{"id":"cbb99fe4ada84631b7baf046b6fd2044","name":"DN5242-Nov16-T3","isPermitted":true}],"productFamilies":null,"projects":[{"id":"DFW","name":"DFW"},{"id":"x1","name":"x1"},{"id":"yyy1","name":"yyy1"}],"owningEntities":[{"id":"aaa1","name":"aaa1"},{"id":"d61e6f2d-12fa-4cc2-91df-7c244011d6fc","name":"MetroPacketCore"},{"id":"Wireline","name":"Wireline"}],"globalCustomerId":"e433710f-9217-458d-a79d-1c7aff376d89"}')} + } + + function generateFormGroup(){ + return new FormGroup({ + globalSubscriberId: new FormControl( + Validators.compose([Validators.required]) + ), + productFamilyId: new FormControl(), + subscriptionServiceType: new FormControl({value: null, disabled: true}, Validators.compose([Validators.required])), + lcpCloudRegionId: new FormControl({value: null, disabled: true}, Validators.compose([Validators.required])), + tenantId: new FormControl({value: null, disabled: true}, Validators.compose([Validators.required])), + aicZoneId: new FormControl(), + projectName: new FormControl(), + owningEntityId: new FormControl(Validators.compose([Validators.required])), + instanceName : new FormControl({value: null}, Validators.compose([Validators.required, NumbersLettersUnderscoreValidator.valid])) + }); + } + + + function generateServiceInstanceDetails(){ + return { + servicePopupDataModel : { + "productFamilies" : [] + }, + serviceInstanceDetailsFormGroup : { + controls : { + productFamilyId : { + disabled : false + } + } + } + } + } + + function generateLegalServiceInstance(){ + return { + instanceName : "legalInstanceName" + } + } + + function generateIllegalServiceInstance(){ + return { + instanceName : "illegalInstanceName" + } + } + +}); diff --git a/vid-webpack-master/src/app/components/service-popup/service-popup.service.ts b/vid-webpack-master/src/app/components/service-popup/service-popup.service.ts new file mode 100644 index 00000000..f6efd353 --- /dev/null +++ b/vid-webpack-master/src/app/components/service-popup/service-popup.service.ts @@ -0,0 +1,33 @@ +import {Injectable} from '@angular/core'; +import {isNullOrUndefined} from "util"; +import {NumbersLettersUnderscoreValidator} from '../../shared/components/validators/numbersLettersUnderscore/numbersLettersUnderscore.validator'; +import {ServiceInstanceDetailsComponent} from './service-instance-details/service-instance-details.component'; +import {FormGroup} from '@angular/forms'; +import * as _ from "lodash"; + +@Injectable() +export class ServicePopupService { + onControlError(serviceInstanceDetails : ServiceInstanceDetailsComponent, serviceInstanceDetailsFormGroup : FormGroup) : boolean{ + if(!isNullOrUndefined(serviceInstanceDetailsFormGroup) && !isNullOrUndefined(serviceInstanceDetailsFormGroup.controls['instanceName']) && NumbersLettersUnderscoreValidator.valid(serviceInstanceDetailsFormGroup.controls['instanceName'].value) && serviceInstanceDetailsFormGroup.controls['instanceName'].value != null && serviceInstanceDetailsFormGroup.controls['instanceName'].value.length > 0){ + return true; + } + + const controlName : Array<string> = ['productFamilyId', 'lcpCloudRegionId', 'tenantId', 'owningEntityId', 'projectName', 'aicZoneId', 'subscriptionServiceType', 'globalSubscriberId', 'rollbackOnFailure']; + const selectDataName : Array<string> = ['productFamilies', 'lcpRegions', 'tenants', 'owningEntities', 'projects', 'aicZones', 'serviceTypes', 'subscribers', 'rollbackOnFailure']; + for(let i = 0 ; i < controlName.length ; i++){ + if (!isNullOrUndefined(serviceInstanceDetails.servicePopupDataModel) && !isNullOrUndefined(serviceInstanceDetails.servicePopupDataModel[selectDataName[i]])) { + if (!serviceInstanceDetailsFormGroup.controls[controlName[i]].disabled && serviceInstanceDetails.servicePopupDataModel[selectDataName[i]].length === 0) { + return true; + } + } + } + return false; + } + + resetDynamicInputs(serviceInstance : any, defaultDynamicInputs : any) : void { + for(let dynamicInput of serviceInstance.dynamicInputs){ + const defaultDymanicInput = _.find(defaultDynamicInputs, {name:dynamicInput.name}); + serviceInstance.serviceInstanceDetailsFormGroup.controls[dynamicInput.name].setValue(defaultDymanicInput.value); + } + } +} diff --git a/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnf-instance-details.component.ts b/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnf-instance-details.component.ts new file mode 100644 index 00000000..725e4429 --- /dev/null +++ b/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnf-instance-details.component.ts @@ -0,0 +1,275 @@ +import {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core'; +import {FormControl, FormGroup, Validators} from "@angular/forms"; +import {VNFPopupDataModel} from './vnfPopupDataModel'; +import {AaiService} from '../../../services/aaiService/aai.service'; +import { createVFModuleInstance, updateVFModuleInstance, updateVNFInstance } from '../../../service.actions'; +import {VnfInstance} from "../../../shared/models/vnfInstance"; +import {ServiceInstance} from "../../../shared/models/serviceInstance"; +import {VNFModel} from "../../../shared/models/vnfModel"; +import {InputType} from "../../../shared/models/inputTypes"; +import {ModelInfo} from "../../../shared/models/modelInfo"; +import {VfModuleInstance} from "../../../shared/models/vfModuleInstance"; +import {NgRedux, select} from "@angular-redux/store"; +import {AppState} from "../../../store/reducers"; +import {SelectOptionInterface} from "../../../shared/models/selectOption"; +import {Observable} from "rxjs/Observable"; +import {loadProductFamiliesAction} from "../../../services/aaiService/aai.actions"; +import {VnfInstanceDetailsService} from "./vnf-instance-details.service"; +import {isNullOrUndefined} from 'util'; +import {NumbersLettersUnderscoreValidator} from '../../../shared/components/validators/numbersLettersUnderscore/numbersLettersUnderscore.validator'; +import * as _ from "lodash"; +import {ServiceNodeTypes} from "../../../shared/models/ServiceNodeTypes"; + +@Component({ + selector: 'vnf-instance-details', + templateUrl: 'vnf-instance-details.html', + styleUrls: ['vnf-instance-details.scss'], + providers: [AaiService] +}) + +export class VnfInstanceDetailsComponent implements OnInit { + @ViewChild('vnfForm') vnfForm: 'VnfForm'; + _vnfModel: VNFModel; + @Input () + set vnfModel(vnfModel: VNFModel) { + this._vnfModel = vnfModel; + this.updateFormGroupControlsFromVNFModel(); + } + @Input() vnfInstance: any; + @Input() serviceInstance: ServiceInstance; + @Input() dynamicInputs; + @Input() modelName: string; + @Input() serviceUuid: string; + @Input() userProvidedNaming: boolean; + _modelType: string; + @Input() + set modelType(modelType: string) { + this._modelType = modelType; + this.updateFormGroupControlsFromVNFModel(); + } + + @Input() parentModelName: string; + @Input() isNewVfModule : boolean; + + + @Output() onSubmitClick: EventEmitter<any> = new EventEmitter<any>(); + @Output() onServiceInstanceNameChanged : EventEmitter<boolean> = new EventEmitter<boolean>(); + @Output() onVolumeGroupNameChanged : EventEmitter<boolean> = new EventEmitter<boolean>(); + +@Output() onDataChanged: EventEmitter<any> = new EventEmitter<any>(); + @select(['service','productFamilies']) + readonly productFamilies : Observable<SelectOptionInterface[]>; + + vnfPopupDataModel: VNFPopupDataModel = new VNFPopupDataModel(); + lcpRegionsThatEnableLegacyRegionField = ['AAIAIC25', 'rdm3', 'rdm5a']; + shouldShowLegacyRegion: boolean; + instanceFormGroup: FormGroup = null; + inputType = InputType; + isNotUniqueInstanceName : boolean = false; + isNotUniqueVolumeGroupName : boolean = false; + + constructor(private _aaiService: AaiService, private store: NgRedux<AppState>, + private _vnfInstanceDetailsService : VnfInstanceDetailsService) { + this.store.subscribe(() => { + this.updateFormData() + }); + } + + ngOnInit() { + this.updateFormGroup(); + this.subscribeToFormChanges(); + this._aaiService.getCategoryParameters(null).subscribe(); + this._aaiService.getLcpRegionsAndTenants(this.serviceInstance.globalSubscriberId, this.serviceInstance.subscriptionServiceType).subscribe(); + this.updateLegacyRegionVisibility(); + this.store.dispatch(loadProductFamiliesAction()); + } + + isInputShouldBeShown(inputType: any) { + let vnfInputs = [InputType.LCP_REGION, InputType.LOB, InputType.TENANT, InputType.PRODUCT_FAMILY, InputType.PLATFORM, InputType.ROLLBACK]; + let vfInputs = [InputType.VG]; + let exist = false; + if (this._modelType === 'VF') { + exist = vnfInputs.indexOf(inputType) > -1; + } + else { + exist = vfInputs.indexOf(inputType) > -1; + } + return exist; + } + + updateFormGroupControlsFromVNFModel() { + if (this._vnfModel && this._modelType) { + if (this._modelType === ServiceNodeTypes.VF) { + const vnfInstance = <VnfInstance>this.vnfInstance; + if (this.instanceFormGroup && this.userProvidedNaming + && !this.instanceFormGroup.get('instanceName')) { + const initialInstanceName = vnfInstance.instanceName || (!isNullOrUndefined(this._vnfModel.name) ? this._vnfModel.name.replace(/[-]/g, "") : this._vnfModel.name); + this.instanceFormGroup.addControl('instanceName', new FormControl(initialInstanceName, Validators.compose([Validators.required, NumbersLettersUnderscoreValidator.valid]))) + } + } + else if (this._modelType === ServiceNodeTypes.VFmodule) { + const vfInstance = <VfModuleInstance>this.vnfInstance; + if (this.instanceFormGroup && this.userProvidedNaming && !this.instanceFormGroup.get('instanceName')) { + this.instanceFormGroup.addControl('instanceName', new FormControl(vfInstance.instanceName, Validators.required)); + + let vfModule = this.extractVfAccordingToVfModuleUuid(this.store.getState(), this._vnfModel.uuid); + if (vfModule.volumeGroupAllowed && !this.instanceFormGroup.get('volumeGroupName')) { + this.instanceFormGroup.addControl('volumeGroupName', new FormControl(vfInstance.volumeGroupName)); + } + } + } + } + } + + updateFormGroup() { + const tenantDisabled = !this.vnfInstance.lcpCloudRegionId; + + if (this._modelType === ServiceNodeTypes.VF) { + const vnfInstance = <VnfInstance>this.vnfInstance; + this.instanceFormGroup = new FormGroup({ + productFamilyId: new FormControl(vnfInstance.productFamilyId), + lcpCloudRegionId: new FormControl(vnfInstance.lcpCloudRegionId, Validators.required), + tenantId: new FormControl({value: vnfInstance.tenantId, disabled: tenantDisabled}, Validators.required), + legacyRegion: new FormControl(vnfInstance.legacyRegion), + lineOfBusiness: new FormControl(vnfInstance.lineOfBusiness), + platformName: new FormControl(vnfInstance.platformName, Validators.required), + }); + } + else if (this._modelType === ServiceNodeTypes.VFmodule) { + const vfInstance = <VfModuleInstance>this.vnfInstance; + this.instanceFormGroup = new FormGroup({ + }); + } + + this.instanceFormGroup.valueChanges.subscribe(()=> { + this.checkForUniqueInstanceName(); + this.onDataChanged.next(); + }); + + this.updateFormGroupControlsFromVNFModel(); + } + + private getParentVnfModel(): VNFModel { + const rawModel = _.get(this.store.getState().service.serviceHierarchy[this.serviceUuid], ['vnfs', this.parentModelName]); + return new VNFModel(rawModel); + } + + extractVfAccordingToVfModuleUuid(state : any,vfModuleUuid : string) { + const vnfs = this.store.getState().service.serviceHierarchy[this.serviceUuid].vnfs; + const vnfsArray = Object.values(vnfs); + for (let i = 0; i<vnfsArray.length;i++){ + let vfModules = Object.values(vnfsArray[i].vfModules); + for (let j = 0; j<vfModules.length;j++){ + if (vfModules[j].uuid === vfModuleUuid){ + return vfModules[j]; + } + } + } + } + + updateFormData() { + let service = this.store.getState().service; + this.vnfPopupDataModel.lcpRegions = service.lcpRegionsAndTenants.lcpRegionList; + if (this.vnfInstance && this.vnfInstance.lcpCloudRegionId) { + this.vnfPopupDataModel.tenants = service.lcpRegionsAndTenants.lcpRegionsTenantsMap[this.vnfInstance.lcpCloudRegionId]; + console.log('setting vnf instances tenant: ' + JSON.stringify(this.vnfPopupDataModel.tenants)); + } + this.vnfPopupDataModel.platforms = service.categoryParameters.platformList; + this.vnfPopupDataModel.lineOfBusinesses = service.categoryParameters.lineOfBusinessList; + this.onDataChanged.next(); + } + + subscribeToFormChanges(): void { + if (this.instanceFormGroup.get('lcpCloudRegionId') !== null) { + this.instanceFormGroup.get('lcpCloudRegionId').valueChanges.subscribe(val => { + this.setDisabledState(val, 'tenantId'); + this.updateTenantList(val); + this.updateLegacyRegionVisibility(); + this.onDataChanged.next(); + }); + } + } + + setDisabledState(val, field: string): void { + if (val) { + this.instanceFormGroup.controls[field].enable(); + } + } + + updateLegacyRegionVisibility() { + if (this.instanceFormGroup.get('lcpCloudRegionId') !== null) { + this.shouldShowLegacyRegion = this.lcpRegionsThatEnableLegacyRegionField.indexOf(this.instanceFormGroup.get('lcpCloudRegionId').value) > -1; + if (!this.shouldShowLegacyRegion) { + this.instanceFormGroup.controls.legacyRegion.setValue(undefined); + } + } + } + + updateTenantList(cloudRegionId) { + this.resetTenantSelection(); + const tenantsForCloudRegionId = this.store.getState().service.lcpRegionsAndTenants.lcpRegionsTenantsMap[cloudRegionId]; + console.log('tenants for selected cloud region id: ' + JSON.stringify(tenantsForCloudRegionId)); + this.vnfPopupDataModel.tenants = tenantsForCloudRegionId; + } + + resetTenantSelection() { + this.instanceFormGroup.controls.tenantId.setValue(undefined); + } + + checkForUniqueInstanceName() { + let currentName = !isNullOrUndefined(this.instanceFormGroup.get('instanceName')) ? this.instanceFormGroup.get('instanceName').value : null; + + if(currentName && !this._vnfInstanceDetailsService.isUnique(this.store.getState().service.serviceInstance, this.serviceUuid, currentName, currentName === this.serviceInstance.instanceName) && this.userProvidedNaming){ + this.isNotUniqueInstanceName = true; + this.onServiceInstanceNameChanged.emit(true); + }else { + this.isNotUniqueInstanceName = false; + this.onServiceInstanceNameChanged.emit(false); + } + } + + checkForUniqueGroupName(){ + let currentName = this.instanceFormGroup.get('volumeGroupName').value; + if( !this._vnfInstanceDetailsService.isUnique(this.store.getState().service.serviceInstance, this.serviceUuid, currentName, currentName === this.serviceInstance['volumeGroupName'])){ + this.isNotUniqueVolumeGroupName = true; + this.onVolumeGroupNameChanged.emit(true); + }else { + this.isNotUniqueVolumeGroupName = false; + this.onVolumeGroupNameChanged.emit(false); + } + } + + onSubmit(formValues): void { + formValues.modelInfo = new ModelInfo(this._vnfModel); + if (this._modelType === 'VFmodule') { + let dynamicFields: { [dynamicField: string]: string; }; + dynamicFields = {}; + if(!_.isEmpty(this.dynamicInputs)) { + this.dynamicInputs.map(function (x) { + let dynamicField: string = x.id; + dynamicFields[dynamicField] = formValues[dynamicField]; + delete formValues[dynamicField]; + }); + } + formValues.instanceParams = []; + formValues.instanceParams.push(dynamicFields); + if(this.isNewVfModule){ + this.store.dispatch(createVFModuleInstance(formValues, this.modelName, this.serviceUuid)); + }else { + this.store.dispatch(updateVFModuleInstance(formValues, this.modelName, this.serviceUuid)); + } + + } + else { + formValues.isUserProvidedNaming = this.userProvidedNaming; + this.store.dispatch(updateVNFInstance(formValues, this.modelName, this.serviceUuid)); + } + window.parent.postMessage({ + eventId: 'submitIframe', + data: { + serviceModelId: this.serviceUuid + } + }, "*"); + this.onSubmitClick.emit(this.serviceUuid); + } +} diff --git a/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnf-instance-details.html b/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnf-instance-details.html new file mode 100644 index 00000000..ccdaac53 --- /dev/null +++ b/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnf-instance-details.html @@ -0,0 +1,114 @@ + +<div id="vnf-instance-details"> + <form id="vnfForm" #vnfForm="ngForm" (ngSubmit)="onSubmit(vnfForm.value)" [formGroup]="instanceFormGroup"> + + <div class="details-item" *ngIf="instanceFormGroup.get('instanceName')"> + <label class="required">Instance name:</label> + <input patternInput + pattern="^[a-zA-Z0-9_]*$" + [attr.data-tests-id]="'instanceName'" + [ngClass]="{'error-style' : _vnfInstanceDetailsService.hasInstanceNameError(instanceFormGroup) || _vnfInstanceDetailsService.hasUniqueError(isNotUniqueInstanceName)}" + id="instance-name" name="instance-name" + [formControlName]="'instanceName'" + class="form-control input-text" + placeholder="Type Instance Name" + type="text" + (blur)="checkForUniqueInstanceName()"> + <form-control-error *ngIf="_vnfInstanceDetailsService.hasUniqueError(isNotUniqueInstanceName)" [message]="'Instance name is already in use, please pick another name.'"></form-control-error> + <form-control-error *ngIf="_vnfInstanceDetailsService.hasInstanceNameError(instanceFormGroup)" [message]="'Instance name may include only alphanumeric characters and underscore.'"></form-control-error> + </div> + + <div *ngIf="isInputShouldBeShown(inputType.PRODUCT_FAMILY)" class="details-item"> + <label>Product family:</label> + <select class="form-control input-text" + [ngClass]="{'error-style' :_vnfInstanceDetailsService.hasApiError('productFamilyId',productFamilies, instanceFormGroup)}" + data-tests-id="productFamily" + id="product-family-select" + [formControlName]="'productFamilyId'" + name="product-family-select" > + <option [value]="null" disabled>Select Product Family</option> + <option *ngFor="let productFamily of productFamilies | async" [value]="productFamily.id" + [disabled]="!productFamily.isPermitted">{{productFamily.name}}</option> + </select> + <form-control-error *ngIf="_vnfInstanceDetailsService.hasApiError('productFamilyId',productFamilies, instanceFormGroup)" [message]="'No results for this request. Please change criteria.'"></form-control-error> + </div> + + <div *ngIf="isInputShouldBeShown(inputType.LCP_REGION)" class="details-item"> + <label class="required">LCP region:</label> + <select + [ngClass]="{'error-style' :_vnfInstanceDetailsService.hasApiError('lcpCloudRegionId',vnfPopupDataModel?.lcpRegions, instanceFormGroup)}" + class="form-control input-text" + [formControlName]="'lcpCloudRegionId'" + name="lcpRegion" id="lcpRegion-select" + data-tests-id="lcpRegion"> + <option [value]="null" disabled>Select LCP Region</option> + <option *ngFor="let lcpRegion of vnfPopupDataModel.lcpRegions" [value]="lcpRegion.id" [disabled]="!lcpRegion.isPermitted" class="lcpRegionOption">{{lcpRegion.id}}</option> + </select> + <form-control-error *ngIf="_vnfInstanceDetailsService.hasApiError('lcpCloudRegionId',vnfPopupDataModel?.lcpRegions, instanceFormGroup)" [message]="'No results for this request. Please change criteria.'"></form-control-error> + </div> + + <div class="details-item" *ngIf="shouldShowLegacyRegion"> + <label>Legacy Region:</label> + <input + [attr.data-tests-id]="'lcpRegionText'" + id="legacy-region" + name="legacy-region" + [formControlName]="'legacyRegion'" + class="form-control input-text" + placeholder="Type Legacy Region" type="text"> + </div> + + <div *ngIf="isInputShouldBeShown(inputType.TENANT)" class="details-item"> + <label class="required">Tenant:</label> + <select class="form-control input-text" + [ngClass]="{'error-style' :_vnfInstanceDetailsService.hasApiError('tenantId',vnfPopupDataModel?.tenants, instanceFormGroup)}" + [formControlName]="'tenantId'" + name="tenant" id="tenant-select" data-tests-id="tenant"> + <option [value]="undefined" disabled>Select Tenant</option> + <option *ngFor="let tenant of vnfPopupDataModel.tenants" [value]="tenant.id" [disabled]="!tenant.isPermitted">{{tenant.name}}</option> + </select> + <form-control-error *ngIf="_vnfInstanceDetailsService.hasApiError('tenantId',vnfPopupDataModel?.tenants, instanceFormGroup)" [message]="'No results for this request. Please change criteria.'"></form-control-error> + </div> + + <div *ngIf="isInputShouldBeShown(inputType.LOB)" class="details-item"> + <label>Line of business:</label> + <select [attr.data-tests-id]="'lineOfBusiness'" + class="form-control input-text" + [ngClass]="{'error-style' :_vnfInstanceDetailsService.hasApiError('lineOfBusiness',vnfPopupDataModel?.lineOfBusinesses, instanceFormGroup)}" + name="lineOfBusiness" id="lineOfBusiness" + [formControlName]="'lineOfBusiness'"> + <option [value]="null" disabled>Select Line Of Business</option> + <option *ngFor="let project of vnfPopupDataModel.lineOfBusinesses" [value]="project.id">{{project.name}}</option> + </select> + <form-control-error *ngIf="_vnfInstanceDetailsService.hasApiError('lineOfBusiness',vnfPopupDataModel?.lineOfBusinesses, instanceFormGroup)" [message]="'No results for this request. Please change criteria.'"></form-control-error> + </div> + + <div *ngIf="isInputShouldBeShown(inputType.PLATFORM)" class="details-item"> + <label class="required">Platform:</label> + <select + [attr.data-tests-id]="'platform'" + [ngClass]="{'error-style' :_vnfInstanceDetailsService.hasApiError('platformName',vnfPopupDataModel?.platforms, instanceFormGroup)}" + class="form-control input-text" + [formControlName]="'platformName'" + name="platform" id="platform"> + <option [value]="null" disabled>Select Platform</option> + <option *ngFor="let platform of vnfPopupDataModel.platforms" [value]="platform.id">{{platform.name}}</option> + </select> + <form-control-error *ngIf="_vnfInstanceDetailsService.hasApiError('platformName',vnfPopupDataModel?.platforms, instanceFormGroup)" [message]="'No results for this request. Please change criteria.'"></form-control-error> + </div> + + + <div *ngIf="isInputShouldBeShown(inputType.VG) && instanceFormGroup.get('volumeGroupName')" class="details-item" > + <label class="required">Volume Group Name:</label> + <input [attr.data-tests-id]="'volumeGroupName'" + id="vgName" name="vgName" + [ngClass]="{'error-style' :isNotUniqueVolumeGroupName}" + [formControlName]="'volumeGroupName'" + class="form-control input-text" + placeholder="Type Instance Name" type="text" (blur)="checkForUniqueGroupName()"> + <form-control-error *ngIf="isNotUniqueVolumeGroupName" [message]="'Volume Group instance name is already in use, please pick another name.'"></form-control-error> + </div> + + <dynamic-inputs *ngIf="dynamicInputs != undefined && dynamicInputs.length>0" [group]="instanceFormGroup" [list]="dynamicInputs"></dynamic-inputs> + </form> +</div> diff --git a/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnf-instance-details.scss b/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnf-instance-details.scss new file mode 100644 index 00000000..080540a5 --- /dev/null +++ b/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnf-instance-details.scss @@ -0,0 +1,64 @@ +#vnf-instance-details { + position: relative; + + #notification-area { + color: #959595; + font-size: 12px; + position: absolute; + top: 3px; + left: 30px; + } + + height: 100%; + overflow: auto; + padding: 30px; + + /deep/ { + .form-control { + border-radius: 2px; + box-shadow: none; + border-color: #D2D2D2; + } + + label { + font-family: OpenSans-Semibold; + font-size: 12px; + } + + select { + @extend .form-control; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: url('../../../../assets/img/chevron.svg') 0 0 no-repeat; + background-size: 24px; + background-position-x: right; + background-position-y: center; + font-family: OpenSans-Italic; + font-size: 14px; + color: #959595; + height: 38px; + } + + input:not([type='checkbox']) { + @extend .form-control; + height: 38px; + } + + .form-control[disabled], fieldset[disabled] .form-control { + opacity: 0.5; + } + .input-text { + border: 1px solid #D2D2D2; + border-radius: 2px; + } + + .details-item { + margin-bottom: 20px; + } + } + + .checkbox-label { + font-family: OpenSans-Regular; + } +} diff --git a/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnf-instance-details.service.spec.ts b/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnf-instance-details.service.spec.ts new file mode 100644 index 00000000..41ddb437 --- /dev/null +++ b/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnf-instance-details.service.spec.ts @@ -0,0 +1,241 @@ +import { TestBed, getTestBed } from '@angular/core/testing'; +import { + HttpClientTestingModule, + HttpTestingController +} from '@angular/common/http/testing'; +import { VnfInstanceDetailsService } from './vnf-instance-details.service'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { NumbersLettersUnderscoreValidator } from '../../../shared/components/validators/numbersLettersUnderscore/numbersLettersUnderscore.validator'; + +describe('Vnf Instance Details Service', () => { + let injector; + let service: VnfInstanceDetailsService; + let httpMock: HttpTestingController; + + let SERVICE_ID: string = '1a80c596-27e5-4ca9-b5bb-e03a7fd4c0fd'; + let serviceHierarchy; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [VnfInstanceDetailsService] + }); + + injector = getTestBed(); + service = injector.get(VnfInstanceDetailsService); + httpMock = injector.get(HttpTestingController); + serviceHierarchy = getServiceServiceHierarchy(); + }); + + + describe('#hasInstanceNameError', ()=> { + it('hasInstanceNameError should return true if instanceName is illegal and enabled', (done: DoneFn) => { + let form = generateFormGroup(); + form.controls['instanceName'].setValue('----'); + form.controls['instanceName'].setErrors({ + pattern : true + }); + form.controls['instanceName'].markAsTouched(); + let result = service.hasInstanceNameError(form); + expect(result).toBeTruthy(); + done(); + }); + + it('hasInstanceNameError should return false if instanceName is illegal and enabled and pattern is ok', (done: DoneFn) => { + let form = generateFormGroup(); + form.controls['instanceName'].setValue('----'); + form.controls['instanceName'].setErrors({ + otherError : true + }); + form.controls['instanceName'].markAsTouched(); + let result = service.hasInstanceNameError(form); + expect(result).toBeFalsy(); + done(); + }); + }); + + describe('#isUnique', () => { + it('Create Mode: should return false if instanceName exist', (done: DoneFn) => { + serviceHierarchy = getServiceServiceHierarchy(); + let result = service.isUnique(serviceHierarchy, SERVICE_ID, 'uniqueInstanceName', false); + expect(result).toBeFalsy(); + done(); + }); + + it('Update Mode: should return true if instanceName exist once', (done: DoneFn) => { + let result = service.isUnique(serviceHierarchy, SERVICE_ID, 'uniqueInstanceName', true); + expect(result).toBeTruthy() + done(); + }); + + it('Create Mode: should return true if instanceName not exist', (done: DoneFn) => { + let result = service.isUnique(serviceHierarchy, SERVICE_ID, 'uniqueInstanceNameNotExist', false); + expect(result).toBeTruthy(); + done(); + }); + + it('Create Mode: should return false if instanceName exist inside vf modules', (done: DoneFn) => { + let result = service.isUnique(serviceHierarchy, SERVICE_ID, 'uniqueInstanceNameVfModule', false); + expect(result).toBeFalsy(); + done(); + }); + + it('Update Mode: should return true if instanceName exist once inside vf modules', (done: DoneFn) => { + let result = service.isUnique(serviceHierarchy, SERVICE_ID, 'uniqueInstanceNameVfModule', true); + expect(result).toBeTruthy(); + done(); + }); + + it('Create Mode: should return true if instanceName is not exist at vf modules and vnfs', (done: DoneFn) => { + let result = service.isUnique(serviceHierarchy, SERVICE_ID, 'uniqueInstanceNameVfModuleNotExist', false); + expect(result).toBeTruthy(); + done(); + }); + + it('Create Mode: should return false if instanceName exist service name', (done: DoneFn) => { + let result = service.isUnique(serviceHierarchy, SERVICE_ID, 'Instance-Name', false); + expect(result).toBeFalsy(); + done(); + }); + + it('Create Mode: should return false if volumeGroupName exist service name', (done: DoneFn) => { + let result = service.isUnique(serviceHierarchy, SERVICE_ID, 'volumeGroupNameExist', false); + expect(result).toBeFalsy(); + done(); + }); + + it('Create Mode: should return true if volumeGroupName not exist service name', (done: DoneFn) => { + let result = service.isUnique(serviceHierarchy, SERVICE_ID, 'volumeGroupNameNotExist', false); + expect(result).toBeTruthy(); + done(); + }); + }); + + function getServiceServiceHierarchy() { + return JSON.parse(JSON.stringify( + { + "1a80c596-27e5-4ca9-b5bb-e03a7fd4c0fd": { + "vnfs": { + "2017-388_ADIOD-vPE 1": { + "rollbackOnFailure": "true", + "vfModules": {}, + "instanceParams": [ + {} + ], + "productFamilyId": "a4f6f2ae-9bf5-4ed7-b904-06b2099c4bd7", + "lcpCloudRegionId": "AAIAIC25", + "tenantId": "092eb9e8e4b7412e8787dd091bc58e86", + "lineOfBusiness": "zzz1", + "platformName": "platform", + "instanceName": "uniqueInstanceName", + "modelInfo": { + "modelInvariantId": "00beb8f9-6d39-452f-816d-c709b9cbb87d", + "modelVersionId": "0903e1c0-8e03-4936-b5c2-260653b96413", + "modelName": "2017-388_ADIOD-vPE", + "modelVersion": "1.0", + "modelCustomizationId": "280dec31-f16d-488b-9668-4aae55d6648a", + "modelCustomizationName": "2017-388_ADIOD-vPE 1" + }, + "isUserProvidedNaming": true + }, + "2017-388_ADIOD-vPE 0": { + "rollbackOnFailure": "true", + "vfModules": {}, + "instanceParams": [ + {} + ], + "productFamilyId": null, + "lcpCloudRegionId": "mtn6", + "tenantId": "1178612d2b394be4834ad77f567c0af2", + "lineOfBusiness": "ECOMP", + "platformName": "xxx1", + "instanceName": "blaaa", + "modelInfo": { + "modelInvariantId": "72e465fe-71b1-4e7b-b5ed-9496118ff7a8", + "modelVersionId": "afacccf6-397d-45d6-b5ae-94c39734b168", + "modelName": "2017-388_ADIOD-vPE", + "modelVersion": "4.0", + "modelCustomizationId": "b3c76f73-eeb5-4fb6-9d31-72a889f1811c", + "modelCustomizationName": "2017-388_ADIOD-vPE 0" + }, + "isUserProvidedNaming": true + }, + "2017488_ADIODvPE 0": { + "rollbackOnFailure": "true", + "vfModules": { + "2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vRE_BV..module-1": { + "2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vRE_BV..module-1": { + "rollbackOnFailure": "true", + "instanceName": "uniqueInstanceNameVfModule", + "volumeGroupName": "volumeGroupNameExist", + "modelInfo": { + "modelInvariantId": "7253ff5c-97f0-4b8b-937c-77aeb4d79aa1", + "modelVersionId": "25284168-24bb-4698-8cb4-3f509146eca5", + "modelName": "2017488AdiodVpe..ADIOD_vRE_BV..module-1", + "modelVersion": "6" + } + } + } + }, + "instanceParams": [ + {} + ], + "productFamilyId": "ebc3bc3d-62fd-4a3f-a037-f619df4ff034", + "lcpCloudRegionId": "mtn6", + "tenantId": "19c5ade915eb461e8af52fb2fd8cd1f2", + "lineOfBusiness": "zzz1", + "platformName": "platform", + "instanceName": "2017488_ADIODvPE", + "modelInfo": { + "modelInvariantId": "72e465fe-71b1-4e7b-b5ed-9496118ff7a8", + "modelVersionId": "69e09f68-8b63-4cc9-b9ff-860960b5db09", + "modelName": "2017488_ADIODvPE", + "modelVersion": "5.0", + "modelCustomizationId": "1da7b585-5e61-4993-b95e-8e6606c81e45", + "modelCustomizationName": "2017488_ADIODvPE 0" + }, + "isUserProvidedNaming": true + } + }, + "instanceParams": [ + {} + ], + "globalSubscriberId": "e433710f-9217-458d-a79d-1c7aff376d89", + "productFamilyId": "17cc1042-527b-11e6-beb8-9e71128cae77", + "subscriptionServiceType": "VIRTUAL USP", + "lcpCloudRegionId": "AAIAIC25", + "tenantId": "092eb9e8e4b7412e8787dd091bc58e86", + "aicZoneId": "DKJ1", + "projectName": "DFW", + "owningEntityId": "aaa1", + "instanceName": "Instance-Name", + "bulkSize": 1, + "modelInfo": { + "modelInvariantId": "e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0", + "modelVersionId": "1a80c596-27e5-4ca9-b5bb-e03a7fd4c0fd", + "modelName": "action-data", + "modelVersion": "1.0" + }, + "tenantName": "USP-SIP-IC-24335-T-01", + "aicZoneName": "DKJSJDKA-DKJ1", + "isUserProvidedNaming": true + } + } + )); + } + + function generateFormGroup() { + return new FormGroup({ + productFamilyId: new FormControl(), + lcpCloudRegionId: new FormControl(Validators.required), + tenantId: new FormControl({value: null, disabled: false}, Validators.required), + legacyRegion: new FormControl(), + lineOfBusiness: new FormControl(), + platformName: new FormControl(Validators.required), + rollbackOnFailure: new FormControl(Validators.required), + instanceName: new FormControl({value: null}, Validators.compose([Validators.required, NumbersLettersUnderscoreValidator.valid])) + + }); + } + +}); diff --git a/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnf-instance-details.service.ts b/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnf-instance-details.service.ts new file mode 100644 index 00000000..677895e7 --- /dev/null +++ b/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnf-instance-details.service.ts @@ -0,0 +1,64 @@ +import { Injectable } from '@angular/core'; +import { isNullOrUndefined } from "util"; +import { FormGroup } from '@angular/forms'; +@Injectable() +export class VnfInstanceDetailsService { + isUnique(serviceInstance : any, serviceId : string, name: string, isEqualToOriginalInstanceName : boolean) : boolean { + const service = serviceInstance[serviceId]; + let countInstanceName = 0; + let countVolumeGroupName = 0; + if(service){ + if(service.instanceName === name) return false; + if(service.vnfs){ + for(let key in service.vnfs){ + if(service.vnfs[key].instanceName === name) { + countInstanceName++; + if((isEqualToOriginalInstanceName && countInstanceName > 1) || (!isEqualToOriginalInstanceName)) return false; + } + if(service.vnfs[key].vfModules){ + for(let vfModule in service.vnfs[key].vfModules){ + if(service.vnfs[key].vfModules[vfModule]) { + for(let module in service.vnfs[key].vfModules[vfModule]){ + if(service.vnfs[key].vfModules[vfModule][module].instanceName === name ) { + countInstanceName++; + if((isEqualToOriginalInstanceName && countInstanceName > 1) || (!isEqualToOriginalInstanceName)) return false; + } + + if(service.vnfs[key].vfModules[vfModule][module].volumeGroupName === name ) { + countVolumeGroupName++; + if((isEqualToOriginalInstanceName && countVolumeGroupName > 1) || (!isEqualToOriginalInstanceName)) return false; + } + } + } + } + } + + } + } + } + return true; + } + + hasApiError(controlName: string, data: Array<any>, form: FormGroup) { + if (!isNullOrUndefined(data)) { + if (!form.controls[controlName].disabled && data.length === 0) { + return true; + } + } + return false; + } + + hasInstanceNameError(form : FormGroup) : boolean { + if(!isNullOrUndefined(form) && !isNullOrUndefined(form.controls['instanceName'])){ + if (form.controls['instanceName'].touched && form.controls['instanceName'].errors && form.controls['instanceName'].errors.pattern) { + return true; + } + } + return false; + } + + hasUniqueError(isNotUniqueInstanceName) : boolean { + return isNotUniqueInstanceName; + } + +} diff --git a/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnfPopupDataModel.ts b/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnfPopupDataModel.ts new file mode 100644 index 00000000..9a2694c7 --- /dev/null +++ b/vid-webpack-master/src/app/components/vnf-popup/vnf-instance-details/vnfPopupDataModel.ts @@ -0,0 +1,26 @@ +import {Project} from "../../../shared/models/project"; +import {LcpRegion} from "../../../shared/models/lcpRegion"; +import {Tenant} from "../../../shared/models/tenant"; +import {ProductFamily} from "../../../shared/models/productFamily"; +import {SelectOption, SelectOptionInterface} from "../../../shared/models/selectOption"; + +export class VNFPopupDataModel { + productFamilies: ProductFamily[]; + lcpRegions: LcpRegion[]; + lcpRegionsTenantsMap: any; + tenants: Tenant[]; + projects: Project[]; + lineOfBusinesses: SelectOption[]; + platforms: SelectOptionInterface[]; + globalCustomerId: string; + + + constructor(){ + this.lcpRegions = []; + this.lcpRegionsTenantsMap = {}; + this.tenants = []; + this.productFamilies = []; + this.lineOfBusinesses = []; + this.platforms = []; + } +} diff --git a/vid-webpack-master/src/app/components/vnf-popup/vnf-popup-service.ts b/vid-webpack-master/src/app/components/vnf-popup/vnf-popup-service.ts new file mode 100644 index 00000000..7dbe9b11 --- /dev/null +++ b/vid-webpack-master/src/app/components/vnf-popup/vnf-popup-service.ts @@ -0,0 +1,55 @@ +import {Injectable} from "@angular/core"; +import {ServiceNodeTypeToModelKeyMapper} from "../../shared/models/serviceNodeTypeToModelKeyMapper"; +import {ServiceNodeTypes} from "../../shared/models/ServiceNodeTypes"; +import {VfModule} from "../../shared/models/vfModule"; +import {ServicePlanningService} from "../../services/service-planning.service"; +import {VNFModel} from "../../shared/models/vnfModel"; +import * as _ from 'lodash'; +import {isNullOrUndefined} from "util"; +import {NumbersLettersUnderscoreValidator} from '../../shared/components/validators/numbersLettersUnderscore/numbersLettersUnderscore.validator'; +import {FormGroup} from '@angular/forms'; +import {VnfInstanceDetailsComponent} from './vnf-instance-details/vnf-instance-details.component'; +import {VnfInstanceDetailsService} from './vnf-instance-details/vnf-instance-details.service'; + +@Injectable() +export class VnfPopupService { + + constructor(private _servicePlanningService : ServicePlanningService, private _vnfInstanceDetailsService : VnfInstanceDetailsService) { + } + + public getModelFromResponse(result : any, modelType : string, modelName:string) { + let model = null; + let rawModel = _.get(result, [ServiceNodeTypeToModelKeyMapper[modelType], modelName]); + if (!rawModel) return; + + if (modelType === ServiceNodeTypes.VFmodule) { + model = new VfModule(rawModel); + } + else { + model = new VNFModel(rawModel); + } + return model; + } + + onControlError(servicePopupDataModel : VnfInstanceDetailsComponent, serviceInstanceDetailsFormGroup : FormGroup, isNotUniqueInstanceName : boolean, isNotUniqueVolumeGroupName : boolean) : boolean{ + if(this._vnfInstanceDetailsService.hasUniqueError(isNotUniqueInstanceName) || isNotUniqueVolumeGroupName){ + return true; + } + if(!isNullOrUndefined(serviceInstanceDetailsFormGroup.controls['instanceName']) && NumbersLettersUnderscoreValidator.valid(serviceInstanceDetailsFormGroup.controls['instanceName'].value) && serviceInstanceDetailsFormGroup.controls['instanceName'].value != null && serviceInstanceDetailsFormGroup.controls['instanceName'].value.length > 0){ + return true; + } + + const controlName : Array<string> = ['lcpCloudRegionId', 'tenantId', 'lineOfBusiness', 'platformName']; + const selectDataName : Array<string> = ['lcpRegions', 'tenants', 'lineOfBusinesses', 'platforms', 'projects']; + + for(let i = 0 ; i < controlName.length ; i++){ + if (!isNullOrUndefined(servicePopupDataModel.vnfPopupDataModel) && !isNullOrUndefined(servicePopupDataModel.vnfPopupDataModel[selectDataName[i]])) { + if (!isNullOrUndefined(serviceInstanceDetailsFormGroup.controls[controlName[i]]) && !serviceInstanceDetailsFormGroup.controls[controlName[i]].disabled && servicePopupDataModel.vnfPopupDataModel[selectDataName[i]].length === 0) { + return true; + } + } + } + return false; + } + +} diff --git a/vid-webpack-master/src/app/components/vnf-popup/vnf-popup.components.ts b/vid-webpack-master/src/app/components/vnf-popup/vnf-popup.components.ts new file mode 100644 index 00000000..26e667d4 --- /dev/null +++ b/vid-webpack-master/src/app/components/vnf-popup/vnf-popup.components.ts @@ -0,0 +1,190 @@ +import {Component, OnInit, ViewChild} from "@angular/core"; +import {AaiService} from "../../services/aaiService/aai.service"; +import {ModelInformationItem} from "../../shared/components/model-information/model-information.component"; +import {ActivatedRoute} from "@angular/router"; +import {DialogComponent, DialogService} from "ng2-bootstrap-modal"; +import {InstancePopup} from "../instance-popup/instance-popup.components"; +import {ServiceModel} from "../../shared/models/serviceModel"; +import {Constants} from "../../shared/utils/constants"; +import * as _ from "lodash"; +import {VnfInstance} from "../../shared/models/vnfInstance"; +import {ServiceInstance} from "../../shared/models/serviceInstance"; +import {VnfInstanceDetailsComponent} from "./vnf-instance-details/vnf-instance-details.component"; +import {Subscriber} from "../../shared/models/subscriber"; +import {ServiceNodeTypes} from "../../shared/models/ServiceNodeTypes"; +import {AppState} from "../../store/reducers"; +import {NgRedux} from "@angular-redux/store"; +import {VfModuleInstance} from "../../shared/models/vfModuleInstance"; +import {VnfPopupService} from './vnf-popup-service'; +import {IframeService} from "../../shared/utils/iframe.service"; + +export interface VnfPopupModel { + serviceModelId: string; + modelName: string; + parentModelName: string; + modelType: string; + dynamicInputs: any; + userProvidedNaming: boolean; + isNewVfModule : boolean; +} + +@Component({ + selector: 'vnf-popup', + templateUrl: 'vnf-popup.html', + styleUrls: ['vnf-popup.scss'], + providers: [AaiService, VnfPopupService] +}) + +export class VnfPopupComponent extends DialogComponent<VnfPopupModel, boolean> implements VnfPopupModel, InstancePopup, OnInit { + + @ViewChild(VnfInstanceDetailsComponent) vnfInstanceDetails: VnfInstanceDetailsComponent; + + serviceModelId: string; + modelName: string; + parentModelName: string; + modelType: string; + isNewVfModule : boolean; + model: any; + serviceModel: ServiceModel; + popupTypeName: string; + serviceInstance: ServiceInstance; + vnfInstance: VnfInstance; + dynamicInputs; + userProvidedNaming: boolean; + typeMapperForTitle = { + VF: "VNF", + VFmodule: "Module (Heat stack)" + }; + + modelInformationItems: Array<ModelInformationItem> = []; + isNotUniqueInstanceName : boolean = false; + isNotUniqueVolumeGroupName : boolean = false; + hasGeneralApiError : boolean = false; + + parentElementClassName = 'content'; + + constructor(dialogService: DialogService, protected route: ActivatedRoute, protected _aaiService: AaiService, + private store: NgRedux<AppState>, + private _iframeService : IframeService, + private _vnfPopupService: VnfPopupService) { + super(dialogService); + this.vnfInstance = new VnfInstance(); + } + + updateGeneralErrorSection() : void { + this.hasGeneralApiError = this._vnfPopupService.onControlError( + this.vnfInstanceDetails, + this.vnfInstanceDetails.instanceFormGroup, + this.vnfInstanceDetails.isNotUniqueInstanceName, + this.vnfInstanceDetails.isNotUniqueVolumeGroupName); + } + + ngOnInit(): void { + this.updateServiceModelById(); + this.popupTypeName = this.getModelTypeForPopupTitle(); + this.updateServiceModelById(); + this.updateInstanceFromStore(); + } + + onCancelClick() { + this._iframeService.removeClassCloseModal(this.parentElementClassName); + super.close(); + } + + onServiceInstanceNameChanged(isNotUniqueInstanceName: boolean) : void { + this.isNotUniqueInstanceName = isNotUniqueInstanceName; + } + + onVolumeGroupNameChanged(isNotUniqueVolumeGroupName: boolean) : void { + this.isNotUniqueVolumeGroupName = isNotUniqueVolumeGroupName; + } + + onSetClick() { + this._iframeService.removeClassCloseModal(this.parentElementClassName); + this.result = true; + super.close(); + } + + updateServiceModelById() { + this._aaiService.getServiceModelById(this.serviceModelId).subscribe( + result => { + this.serviceModel = new ServiceModel(result); + this.model = this._vnfPopupService.getModelFromResponse(result, this.modelType, this.modelName); + this.modelInformationItems = this.createModelInformationItems(); + }, + error => { + console.log('error is ', error) + } + ); + } + + updateInstanceFromStore() { + let instance; + const serviceInstance = this.store.getState().service.serviceInstance[this.serviceModelId]; + if (this.modelType === ServiceNodeTypes.VF) { + instance = serviceInstance.vnfs[this.modelName] || new VnfInstance(); + } else { + instance = new VfModuleInstance(); + } + + if (instance.instanceParams && instance.instanceParams[0]) { + this.dynamicInputs = this.dynamicInputs.map(x => { + x.value = (instance.instanceParams[0][x.id]) ? instance.instanceParams[0][x.id] : x.value; + return x; + }); + } + this.vnfInstance = instance; + } + + getModelName(): string { + return this.modelName; + } + + getModelTypeForPopupTitle(): string { + if (_.has(this.typeMapperForTitle, this.modelType)) { + return this.typeMapperForTitle[this.modelType]; + } + return this.modelType; + } + + extractSubscriberNameBySubscriberId(subsriberId: string) { + var result: string = null; + var filteredArray: any = _.filter(this.store.getState().service.subscribers, function (o: Subscriber) { + return o.id === subsriberId + }) + if (filteredArray.length > 0) { + result = filteredArray[0].name; + } + return result; + } + + createModelInformationItems(): Array<ModelInformationItem> { + var serviceInstance = this.store.getState().service.serviceInstance[this.serviceModelId]; + + let items = [ + new ModelInformationItem("Subscriber Name", "subscriberName", [this.extractSubscriberNameBySubscriberId(serviceInstance.globalSubscriberId)], "", true), + new ModelInformationItem("Service Name", "serviceModelName", [this.serviceModel.name], "", true), + + new ModelInformationItem("Service Instance Name", "serviceName", [serviceInstance.instanceName], "", false), + new ModelInformationItem("Model Name", "modelName", [this.model.name], "", true), + new ModelInformationItem("Model version", "modelVersion", [this.model.version], "", true), + new ModelInformationItem("Description", "description", [this.model.description]), + new ModelInformationItem("Category", "category", [this.model.category]), + new ModelInformationItem("Sub Category", "subCategory",[this.model.subCategory]), + new ModelInformationItem("UUID", "uuid", [this.model.uuid], Constants.ServicePopup.TOOLTIP_UUID, true), + new ModelInformationItem("Invariant UUID", "invariantUuid", [this.model.invariantUuid], Constants.ServicePopup.TOOLTIP_INVARIANT_UUID, true), + new ModelInformationItem("Service type", "serviceType", [this.serviceModel.serviceType]), + new ModelInformationItem("Service role", "serviceRole", [this.serviceModel.serviceRole]), + + + ]; + if (this.modelType === 'VFmodule') { + items.push(new ModelInformationItem("Minimum to instantiate", "min", [this.model.min], "", true), + new ModelInformationItem("Maximum to instantiate", "max", this.model.max == undefined ? [1] : [this.model.max], "", true), + new ModelInformationItem("Recommended to instantiate", "initial", [this.model.initial])); + + } + + return items; + } +} diff --git a/vid-webpack-master/src/app/components/vnf-popup/vnf-popup.html b/vid-webpack-master/src/app/components/vnf-popup/vnf-popup.html new file mode 100644 index 00000000..d2e043b1 --- /dev/null +++ b/vid-webpack-master/src/app/components/vnf-popup/vnf-popup.html @@ -0,0 +1,54 @@ +<div id="instance-popup" class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" (click)="onCancelClick()" >×</button> + <span [attr.data-tests-id]="'create-modal-title'" class="modal-title">Set a new {{popupTypeName}}</span> + </div> + <div class="modal-body popup-content"> + + <div class="header-left"> + <div>MODEL: <span>"{{popupTypeName}}"</span></div> + </div> + + <div class="header-right"> + {{getModelTypeForPopupTitle()}} Instance Details + </div> + + <div class="model-information"> + <model-information [modelInformationItems]="modelInformationItems"></model-information> + </div> + + <div class="instance-form"> + <vnf-instance-details [dynamicInputs]="dynamicInputs" + [vnfModel]="model" + [modelType]="modelType" + [modelName]="modelName" + [parentModelName]="parentModelName" + [isNewVfModule]="isNewVfModule" + [serviceInstance]="vnfInstance" + [vnfInstance]="vnfInstance" + [serviceUuid]="serviceModelId" + [userProvidedNaming]="userProvidedNaming" + (onSubmitClick)="onSetClick($event)" + (onDataChanged)="updateGeneralErrorSection()" + (onServiceInstanceNameChanged)="onServiceInstanceNameChanged($event)" + (onVolumeGroupNameChanged)="onVolumeGroupNameChanged($event)" + + ></vnf-instance-details> + </div> + + </div> + <div class="modal-footer row" style="padding: 0"> + <div class="col-md-6"> + <div *ngIf="hasGeneralApiError == true"> + <form-general-error [message]="'Page contains errors. Please see details next to the relevant fields.'"></form-general-error> + </div> + </div> + <div class="col-md-6" style="padding: 15px;padding-right: 35px;"> + <button [attr.data-tests-id]="'cancelButton'" type="button" class="btn btn-default cancel" (click)="onCancelClick()"><span>Cancel</span></button> + <input type="submit" value="Set" form="vnfForm" data-tests-id="vnf-form-set" + class="btn btn-success submit" [disabled]="!vnfInstanceDetails?.vnfForm?.valid || isNotUniqueInstanceName || isNotUniqueVolumeGroupName"> + </div> + </div> + </div> +</div> diff --git a/vid-webpack-master/src/app/components/vnf-popup/vnf-popup.scss b/vid-webpack-master/src/app/components/vnf-popup/vnf-popup.scss new file mode 100644 index 00000000..6e606dbb --- /dev/null +++ b/vid-webpack-master/src/app/components/vnf-popup/vnf-popup.scss @@ -0,0 +1,185 @@ +$grid-border: 1px #d2d2d2 solid; + +#instance-popup { + color: #191919; + + .left-panel { + background: #f2f2f2; + border-right: $grid-border; + } + + .header-common { + height: 100%; + align-items: center; + display: flex; + font-family: OpenSans-Semibold; + font-size: 12px; + } + + .header-text { + padding-left: 30px; + @extend .header-common; + } + + .header-left { + grid-area: header-left; + @extend .header-text; + @extend .left-panel; + border-bottom: $grid-border; + + span { + font-family: OpenSans-Regular; + font-size: 14px; + }; + } + + .header-right { + grid-area: header-right; + + @extend .header-text; + border-bottom: $grid-border; + } + + .quantity-label { + grid-area: quantity-label; + @extend .header-common; + border-bottom: $grid-border; + height: 100%; + font-family: OpenSans-Regular; + } + + .quantity { + grid-area: quantity; + border-left: $grid-border; + border-bottom: $grid-border; + border-top-style: none; + font-family: OpenSans-Semibold; + text-align: start; + text-indent: 10px; + } + + .quantity-select { + width: 78px; + height: 100%; + border: 0; + background: white; + outline: none; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: url('../../../assets/img/chevron.svg') 0 0 no-repeat; + background-size: 24px; + background-position-x: right; + background-position-y: center; + } + input[type="number"]:hover::-webkit-inner-spin-button { + height: 20px; + } + + .model-information { + grid-area: model-information; + + padding: 30px; + overflow: auto; + @extend .left-panel; + } + + .instance-form { + grid-area: instance-form; + } + + .popup-content { + display: grid; + grid-template-columns: 400px auto 30px 93px; + grid-template-rows: 50px calc(100vh - 180px); + grid-template-areas: + "header-left header-right header-right header-right" + "model-information instance-form instance-form instance-form"; + padding: 0; + } +} + +.modal { + background-color: #191919; + opacity: 0.8; +} + +.modal-dialog { + position: relative; + width: auto; + margin: 0; +} +@media (min-width: 1150px) { + .popup-content { + grid-template-rows: 30px 680px; + } +} + +.modal-content { + border-radius: 0; + box-shadow: none; + border: none; +} + +.modal-footer { + .cancel { + width: 120px; + height: 36px; + background: #ffffff; + border: 1px solid #009fdb; + border-radius: 2px; + span { + font-family: OpenSans-Regular; + font-size: 14px; + color: #009fdb; + line-height: 16px; + } + } + + .submit { + width: 120px; + height: 36px; + background: #009fdb; + border-radius: 2px; + border-color: #009fdb; + span { + font-family: OpenSans-Regular; + font-size: 14px; + color: #FFFFFF; + line-height: 16px; + } + } +} + +.modal-header { + background-color: #009fdb; + + padding-bottom: 13px; + padding-top: 13px; + padding-left: 29px; + padding-right: 21px; + + .close { + font-size: 32px; + font-weight: 200; + color: #d8d8d8; + text-shadow: none; + filter: none; + opacity: 1; + } + + .modal-title { + font-family: OpenSans-Regular; + font-size: 24px; + color: #fff; + line-height: 34px; + } +} +// +//@media (min-width: 1200px) { +// .service-model, +// .service-instance { +// width: 1050px; +// margin: 30px auto; +// } +//} diff --git a/vid-webpack-master/src/app/components/vnf-popup/vnf-popup.service.spec.ts b/vid-webpack-master/src/app/components/vnf-popup/vnf-popup.service.spec.ts new file mode 100644 index 00000000..02296f72 --- /dev/null +++ b/vid-webpack-master/src/app/components/vnf-popup/vnf-popup.service.spec.ts @@ -0,0 +1,827 @@ +import {VnfPopupService} from './vnf-popup-service'; +import {ServicePlanningService} from '../../services/service-planning.service'; +import {ServiceNodeTypes} from '../../shared/models/ServiceNodeTypes'; +import {NgRedux} from '@angular-redux/store'; +import {VNFModel} from '../../shared/models/vnfModel'; +import {VfModule} from '../../shared/models/vfModule'; +import {FormControl, FormGroup, Validators} from '@angular/forms'; +import {NumbersLettersUnderscoreValidator} from '../../shared/components/validators/numbersLettersUnderscore/numbersLettersUnderscore.validator'; +import {VnfInstanceDetailsService} from './vnf-instance-details/vnf-instance-details.service'; +import {ReflectiveInjector} from '@angular/core'; + +export class MockAppStore<T> { +} + +describe('Vnf popup service', () => { + let injector; + let service: VnfPopupService; + let fg: FormGroup; + let data = generateModelData(); + let servicePopupDataModel = generateServicePopupDataModel(); + let form: FormGroup = generateFormGroup(); + beforeEach(() => { + + let injector = ReflectiveInjector.resolveAndCreate([ + VnfPopupService, + ServicePlanningService, + VnfInstanceDetailsService, + {provide: FormGroup, useClass: MockAppStore}, + {provide: NgRedux, useClass: MockAppStore} + ]); + + service = injector.get(VnfPopupService); + fg = injector.get(FormGroup); + }); + + describe('#updateVnfDataFromModel', () => { + it('update vnf data from model should return new vnf', (done: DoneFn) => { + let vnf: VNFModel = service.getModelFromResponse(data, ServiceNodeTypes.VF, '2017-388_ADIOD-vPE 1'); + + expect(vnf).toEqual(jasmine.any(VNFModel)); + done(); + }); + + it('update wrong vnf data from model should be undefined', (done: DoneFn) => { + let vnf: VNFModel = service.getModelFromResponse(data, ServiceNodeTypes.VF, '2017-388_ADIOD-vPE 3'); + + expect(vnf).toBeUndefined(); + done(); + }); + + it('update vfModule data from model should return new vfModule', (done: DoneFn) => { + let vfModule: VfModule = service.getModelFromResponse(data, ServiceNodeTypes.VFmodule, '2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vRE_BV..module-1'); + + expect(vfModule).toEqual(jasmine.any(VfModule)); + done(); + }); + + }); + + + describe('#onControlError', () => { + it('onControlError should return true if instanceName is not legal', (done: DoneFn) => { + form.controls['instanceName'].setValue('aaaa - aaa'); + + let result: boolean = service.onControlError(<any>servicePopupDataModel, form, false, false); + expect(result).toBeTruthy(); + done(); + }); + + it('onControlError should return false if instanceName is legal', (done: DoneFn) => { + + form.controls['instanceName'].setValue('aaaa'); + let result = service.onControlError(<any>servicePopupDataModel, form, false, false); + expect(result).toBeFalsy(); + done(); + }); + + it('onControlError should return true if instanceName is not unique', (done: DoneFn) => { + + form.controls['instanceName'].setValue('aaaa'); + let result = service.onControlError(<any>servicePopupDataModel, form, true, false); + expect(result).toBeTruthy(); + done(); + }); + + it('onControlError should return true if lcpRegions is empty', (done: DoneFn) => { + servicePopupDataModel.vnfPopupDataModel['lcpRegions'] = []; + let result = service.onControlError(<any>servicePopupDataModel, form, true, false); + expect(result).toBeTruthy(); + done(); + }); + + it('onControlError should return true if isNotUniqueVolumeGroupName is true', (done: DoneFn) => { + let result = service.onControlError(<any>servicePopupDataModel, form, true, true); + expect(result).toBeTruthy(); + done(); + }) + }); + + + function generateServicePopupDataModel() { + return { + 'vnfPopupDataModel': JSON.parse('{"tenants" : [1,2,3],"lcpRegions":[1,2,3],"lcpRegionsTenantsMap":{},"productFamilies":[1,2,3],"lineOfBusinesses":[{"id":"ECOMP","name":"ECOMP"},{"id":"zzz1","name":"zzz1"}],"platforms":[{"id":"platform","name":"platform"},{"id":"xxx1","name":"xxx1"}],"rollbackOnFailure":[{"id":"true","name":"Rollback"}]}') + } + } + + function generateFormGroup() { + return new FormGroup({ + productFamilyId: new FormControl(), + lcpCloudRegionId: new FormControl(Validators.required), + tenantId: new FormControl({value: null, disabled: false}, Validators.required), + legacyRegion: new FormControl(), + lineOfBusiness: new FormControl(), + platformName: new FormControl(Validators.required), + instanceName: new FormControl({value: null}, Validators.compose([Validators.required, NumbersLettersUnderscoreValidator.valid])) + }); + } + + function generateModelData() { + return JSON.parse(JSON.stringify( + { + 'service': { + 'uuid': '2f80c596-27e5-4ca9-b5bb-e03a7fd4c0fd', + 'invariantUuid': 'e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0', + 'name': 'action-data', + 'version': '1.0', + 'toscaModelURL': null, + 'category': '', + 'serviceType': '', + 'serviceRole': '', + 'description': '', + 'serviceEcompNaming': 'true', + 'instantiationType': 'ClientConfig', + 'inputs': { + '2017488_adiodvpe0_ASN': { + 'type': 'string', + 'description': 'AV/PE', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'AV_vPE' + }, + 'adiodvpe0_bandwidth': { + 'type': 'string', + 'description': 'Requested VPE bandwidth', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': '10' + }, + '2017488_adiodvpe0_vnf_instance_name': { + 'type': 'string', + 'description': 'The hostname assigned to the vpe.', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'mtnj309me6' + }, + '2017488_adiodvpe0_vnf_config_template_version': { + 'type': 'string', + 'description': 'VPE Software Version', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': '17.2' + }, + '2017488_adiodvpe0_AIC_CLLI': { + 'type': 'string', + 'description': 'AIC Site CLLI', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'ATLMY8GA' + }, + 'adiodvpe0_bandwidth_units': { + 'type': 'string', + 'description': 'Units of bandwidth', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'Gbps' + } + } + }, + 'vnfs': { + '2017-388_ADIOD-vPE 1': { + 'uuid': '0903e1c0-8e03-4936-b5c2-260653b96413', + 'invariantUuid': '00beb8f9-6d39-452f-816d-c709b9cbb87d', + 'description': 'Name ADIOD vPE Description The provider edge function for the ADIOD service supported by the Junipers VMX product Category Router Vendor Juniper Vendor Release Code 17.2 Owners Mary Fragale. Updated 9-25 to use v8.0 of the Juniper Valid 2 VLM', + 'name': '2017-388_ADIOD-vPE', + 'version': '1.0', + 'customizationUuid': '280dec31-f16d-488b-9668-4aae55d6648a', + 'inputs': { + 'vnf_config_template_version': { + 'type': 'string', + 'description': 'VPE Software Version', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': '17.2' + }, + 'bandwidth_units': { + 'type': 'string', + 'description': 'Units of bandwidth', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'Gbps' + }, + 'bandwidth': { + 'type': 'string', + 'description': 'Requested VPE bandwidth', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': '10' + }, + 'AIC_CLLI': { + 'type': 'string', + 'description': 'AIC Site CLLI', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'ATLMY8GA' + }, + 'ASN': { + 'type': 'string', + 'description': 'AV/PE', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'AV_vPE' + }, + 'vnf_instance_name': { + 'type': 'string', + 'description': 'The hostname assigned to the vpe.', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'mtnj309me6' + } + }, + 'commands': { + 'vnf_config_template_version': { + 'displayName': 'vnf_config_template_version', + 'command': 'get_input', + 'inputName': '2017488_adiodvpe0_vnf_config_template_version' + }, + 'bandwidth_units': { + 'displayName': 'bandwidth_units', + 'command': 'get_input', + 'inputName': 'adiodvpe0_bandwidth_units' + }, + 'bandwidth': {'displayName': 'bandwidth', 'command': 'get_input', 'inputName': 'adiodvpe0_bandwidth'}, + 'AIC_CLLI': {'displayName': 'AIC_CLLI', 'command': 'get_input', 'inputName': '2017488_adiodvpe0_AIC_CLLI'}, + 'ASN': {'displayName': 'ASN', 'command': 'get_input', 'inputName': '2017488_adiodvpe0_ASN'}, + 'vnf_instance_name': { + 'displayName': 'vnf_instance_name', + 'command': 'get_input', + 'inputName': '2017488_adiodvpe0_vnf_instance_name' + } + }, + 'properties': { + 'vmxvre_retype': 'RE-VMX', + 'vnf_config_template_version': 'get_input:2017488_adiodvpe0_vnf_config_template_version', + 'sriov44_net_id': '48d399b3-11ee-48a8-94d2-f0ea94d6be8d', + 'int_ctl_net_id': '2f323477-6936-4d01-ac53-d849430281d9', + 'vmxvpfe_sriov41_0_port_mac': '00:11:22:EF:AC:DF', + 'int_ctl_net_name': 'VMX-INTXI', + 'vmx_int_ctl_prefix': '128.0.0.0', + 'sriov43_net_id': 'da349ca1-6de9-4548-be88-2d88e99bfef5', + 'sriov42_net_id': '760669ba-013d-4d9b-b0e7-4151fe2e6279', + 'sriov41_net_id': '25ad52d5-c165-40f8-b3b0-ddfc2373280a', + 'nf_type': 'vPE', + 'vmxvpfe_int_ctl_ip_1': '128.0.0.16', + 'is_AVPN_service': 'false', + 'vmx_RSG_name': 'vREXI-affinity', + 'vmx_int_ctl_forwarding': 'l2', + 'vmxvre_oam_ip_0': '10.40.123.5', + 'vmxvpfe_sriov44_0_port_mac': '00:11:22:EF:AC:DF', + 'vmxvpfe_sriov41_0_port_vlanstrip': 'false', + 'vmxvpfe_sriov42_0_port_vlanfilter': '4001', + 'vmxvpfe_sriov44_0_port_unknownunicastallow': 'true', + 'vmxvre_image_name_0': 'VRE-ENGINE_17.2-S2.1.qcow2', + 'vmxvre_instance': '0', + 'vmxvpfe_sriov43_0_port_mac': '00:11:22:EF:AC:DF', + 'vmxvre_flavor_name': 'ns.c1r16d32.v5', + 'vmxvpfe_volume_size_0': '40.0', + 'vmxvpfe_sriov43_0_port_vlanfilter': '4001', + 'nf_naming': '{ecomp_generated_naming=true}', + 'nf_naming_code': 'Navneet', + 'vmxvre_name_0': 'vREXI', + 'vmxvpfe_sriov42_0_port_vlanstrip': 'false', + 'vmxvpfe_volume_name_0': 'vPFEXI_FBVolume', + 'vmx_RSG_id': 'bd89a33c-13c3-4a04-8fde-1a57eb123141', + 'vmxvpfe_image_name_0': 'VPE_ROUTING-ENGINE_17.2R1-S2.1.qcow2', + 'vmxvpfe_sriov43_0_port_unknownunicastallow': 'true', + 'vmxvpfe_sriov44_0_port_unknownmulticastallow': 'true', + 'vmxvre_console': 'vidconsole', + 'vmxvpfe_sriov44_0_port_vlanfilter': '4001', + 'vmxvpfe_sriov42_0_port_mac': '00:11:22:EF:AC:DF', + 'vmxvpfe_volume_id_0': '47cede15-da2f-4397-a101-aa683220aff3', + 'vmxvpfe_sriov42_0_port_unknownmulticastallow': 'true', + 'vmxvpfe_sriov44_0_port_vlanstrip': 'false', + 'vf_module_id': '123', + 'nf_function': 'JAI', + 'vmxvpfe_sriov43_0_port_unknownmulticastallow': 'true', + 'vmxvre_int_ctl_ip_0': '128.0.0.1', + 'AIC_CLLI': 'get_input:2017488_adiodvpe0_AIC_CLLI', + 'vnf_name': 'mtnj309me6vre', + 'vmxvpfe_sriov41_0_port_unknownunicastallow': 'true', + 'vmxvre_volume_type_1': 'HITACHI', + 'vmxvpfe_sriov44_0_port_broadcastallow': 'true', + 'vmxvre_volume_type_0': 'HITACHI', + 'vmxvpfe_volume_type_0': 'HITACHI', + 'vmxvpfe_sriov43_0_port_broadcastallow': 'true', + 'bandwidth_units': 'get_input:adiodvpe0_bandwidth_units', + 'vnf_id': '123', + 'vmxvre_oam_prefix': '24', + 'availability_zone_0': 'mtpocfo-kvm-az01', + 'ASN': 'get_input:2017488_adiodvpe0_ASN', + 'vmxvre_chassis_i2cid': '161', + 'vmxvpfe_name_0': 'vPFEXI', + 'bandwidth': 'get_input:adiodvpe0_bandwidth', + 'availability_zone_max_count': '1', + 'vmxvre_volume_size_0': '45.0', + 'vmxvre_volume_size_1': '50.0', + 'vmxvpfe_sriov42_0_port_broadcastallow': 'true', + 'vmxvre_oam_gateway': '10.40.123.1', + 'vmxvre_volume_name_1': 'vREXI_FAVolume', + 'vmxvre_ore_present': '0', + 'vmxvre_volume_name_0': 'vREXI_FBVolume', + 'vmxvre_type': '0', + 'vnf_instance_name': 'get_input:2017488_adiodvpe0_vnf_instance_name', + 'vmxvpfe_sriov41_0_port_unknownmulticastallow': 'true', + 'oam_net_id': 'b95eeb1d-d55d-4827-abb4-8ebb94941429', + 'vmx_int_ctl_len': '24', + 'vmxvpfe_sriov43_0_port_vlanstrip': 'false', + 'vmxvpfe_sriov41_0_port_broadcastallow': 'true', + 'vmxvre_volume_id_1': '6e86797e-03cd-4fdc-ba72-2957119c746d', + 'vmxvpfe_sriov41_0_port_vlanfilter': '4001', + 'nf_role': 'Testing', + 'vmxvre_volume_id_0': 'f4eacb79-f687-4e9d-b760-21847c8bb15a', + 'vmxvpfe_sriov42_0_port_unknownunicastallow': 'true', + 'vmxvpfe_flavor_name': 'ns.c20r16d25.v5' + }, + 'type': 'VF', + 'modelCustomizationName': '2017-388_ADIOD-vPE 1', + 'vfModules': {}, + 'volumeGroups': {} + }, + '2017-388_ADIOD-vPE 0': { + 'uuid': 'afacccf6-397d-45d6-b5ae-94c39734b168', + 'invariantUuid': '72e465fe-71b1-4e7b-b5ed-9496118ff7a8', + 'description': 'Name ADIOD vPE Description The provider edge function for the ADIOD service supported by the Junipers VMX product Category Router Vendor Juniper Vendor Release Code 17.2 Owners Mary Fragale. Updated 9-25 to use v8.0 of the Juniper Valid 2 VLM', + 'name': '2017-388_ADIOD-vPE', + 'version': '4.0', + 'customizationUuid': 'b3c76f73-eeb5-4fb6-9d31-72a889f1811c', + 'inputs': { + 'vnf_config_template_version': { + 'type': 'string', + 'description': 'VPE Software Version', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': '17.2' + }, + 'bandwidth_units': { + 'type': 'string', + 'description': 'Units of bandwidth', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'Gbps' + }, + 'bandwidth': { + 'type': 'string', + 'description': 'Requested VPE bandwidth', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': '10' + }, + 'AIC_CLLI': { + 'type': 'string', + 'description': 'AIC Site CLLI', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'ATLMY8GA' + }, + 'ASN': { + 'type': 'string', + 'description': 'AV/PE', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'AV_vPE' + }, + 'vnf_instance_name': { + 'type': 'string', + 'description': 'The hostname assigned to the vpe.', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'mtnj309me6' + } + }, + 'commands': { + 'vnf_config_template_version': { + 'displayName': 'vnf_config_template_version', + 'command': 'get_input', + 'inputName': '2017488_adiodvpe0_vnf_config_template_version' + }, + 'bandwidth_units': { + 'displayName': 'bandwidth_units', + 'command': 'get_input', + 'inputName': 'adiodvpe0_bandwidth_units' + }, + 'bandwidth': {'displayName': 'bandwidth', 'command': 'get_input', 'inputName': 'adiodvpe0_bandwidth'}, + 'AIC_CLLI': {'displayName': 'AIC_CLLI', 'command': 'get_input', 'inputName': '2017488_adiodvpe0_AIC_CLLI'}, + 'ASN': {'displayName': 'ASN', 'command': 'get_input', 'inputName': '2017488_adiodvpe0_ASN'}, + 'vnf_instance_name': { + 'displayName': 'vnf_instance_name', + 'command': 'get_input', + 'inputName': '2017488_adiodvpe0_vnf_instance_name' + } + }, + 'properties': { + 'vmxvre_retype': 'RE-VMX', + 'vnf_config_template_version': 'get_input:2017488_adiodvpe0_vnf_config_template_version', + 'sriov44_net_id': '48d399b3-11ee-48a8-94d2-f0ea94d6be8d', + 'int_ctl_net_id': '2f323477-6936-4d01-ac53-d849430281d9', + 'vmxvpfe_sriov41_0_port_mac': '00:11:22:EF:AC:DF', + 'int_ctl_net_name': 'VMX-INTXI', + 'vmx_int_ctl_prefix': '128.0.0.0', + 'sriov43_net_id': 'da349ca1-6de9-4548-be88-2d88e99bfef5', + 'sriov42_net_id': '760669ba-013d-4d9b-b0e7-4151fe2e6279', + 'sriov41_net_id': '25ad52d5-c165-40f8-b3b0-ddfc2373280a', + 'nf_type': 'vPE', + 'vmxvpfe_int_ctl_ip_1': '128.0.0.16', + 'is_AVPN_service': 'false', + 'vmx_RSG_name': 'vREXI-affinity', + 'vmx_int_ctl_forwarding': 'l2', + 'vmxvre_oam_ip_0': '10.40.123.5', + 'vmxvpfe_sriov44_0_port_mac': '00:11:22:EF:AC:DF', + 'vmxvpfe_sriov41_0_port_vlanstrip': 'false', + 'vmxvpfe_sriov42_0_port_vlanfilter': '4001', + 'vmxvpfe_sriov44_0_port_unknownunicastallow': 'true', + 'vmxvre_image_name_0': 'VRE-ENGINE_17.2-S2.1.qcow2', + 'vmxvre_instance': '0', + 'vmxvpfe_sriov43_0_port_mac': '00:11:22:EF:AC:DF', + 'vmxvre_flavor_name': 'ns.c1r16d32.v5', + 'vmxvpfe_volume_size_0': '40.0', + 'vmxvpfe_sriov43_0_port_vlanfilter': '4001', + 'nf_naming': '{ecomp_generated_naming=true}', + 'nf_naming_code': 'Navneet', + 'vmxvre_name_0': 'vREXI', + 'vmxvpfe_sriov42_0_port_vlanstrip': 'false', + 'vmxvpfe_volume_name_0': 'vPFEXI_FBVolume', + 'vmx_RSG_id': 'bd89a33c-13c3-4a04-8fde-1a57eb123141', + 'vmxvpfe_image_name_0': 'VPE_ROUTING-ENGINE_17.2R1-S2.1.qcow2', + 'vmxvpfe_sriov43_0_port_unknownunicastallow': 'true', + 'vmxvpfe_sriov44_0_port_unknownmulticastallow': 'true', + 'vmxvre_console': 'vidconsole', + 'vmxvpfe_sriov44_0_port_vlanfilter': '4001', + 'vmxvpfe_sriov42_0_port_mac': '00:11:22:EF:AC:DF', + 'vmxvpfe_volume_id_0': '47cede15-da2f-4397-a101-aa683220aff3', + 'vmxvpfe_sriov42_0_port_unknownmulticastallow': 'true', + 'vmxvpfe_sriov44_0_port_vlanstrip': 'false', + 'vf_module_id': '123', + 'nf_function': 'JAI', + 'vmxvpfe_sriov43_0_port_unknownmulticastallow': 'true', + 'vmxvre_int_ctl_ip_0': '128.0.0.1', + 'AIC_CLLI': 'get_input:2017488_adiodvpe0_AIC_CLLI', + 'vnf_name': 'mtnj309me6vre', + 'vmxvpfe_sriov41_0_port_unknownunicastallow': 'true', + 'vmxvre_volume_type_1': 'HITACHI', + 'vmxvpfe_sriov44_0_port_broadcastallow': 'true', + 'vmxvre_volume_type_0': 'HITACHI', + 'vmxvpfe_volume_type_0': 'HITACHI', + 'vmxvpfe_sriov43_0_port_broadcastallow': 'true', + 'bandwidth_units': 'get_input:adiodvpe0_bandwidth_units', + 'vnf_id': '123', + 'vmxvre_oam_prefix': '24', + 'availability_zone_0': 'mtpocfo-kvm-az01', + 'ASN': 'get_input:2017488_adiodvpe0_ASN', + 'vmxvre_chassis_i2cid': '161', + 'vmxvpfe_name_0': 'vPFEXI', + 'bandwidth': 'get_input:adiodvpe0_bandwidth', + 'availability_zone_max_count': '1', + 'vmxvre_volume_size_0': '45.0', + 'vmxvre_volume_size_1': '50.0', + 'vmxvpfe_sriov42_0_port_broadcastallow': 'true', + 'vmxvre_oam_gateway': '10.40.123.1', + 'vmxvre_volume_name_1': 'vREXI_FAVolume', + 'vmxvre_ore_present': '0', + 'vmxvre_volume_name_0': 'vREXI_FBVolume', + 'vmxvre_type': '0', + 'vnf_instance_name': 'get_input:2017488_adiodvpe0_vnf_instance_name', + 'vmxvpfe_sriov41_0_port_unknownmulticastallow': 'true', + 'oam_net_id': 'b95eeb1d-d55d-4827-abb4-8ebb94941429', + 'vmx_int_ctl_len': '24', + 'vmxvpfe_sriov43_0_port_vlanstrip': 'false', + 'vmxvpfe_sriov41_0_port_broadcastallow': 'true', + 'vmxvre_volume_id_1': '6e86797e-03cd-4fdc-ba72-2957119c746d', + 'vmxvpfe_sriov41_0_port_vlanfilter': '4001', + 'nf_role': 'Testing', + 'vmxvre_volume_id_0': 'f4eacb79-f687-4e9d-b760-21847c8bb15a', + 'vmxvpfe_sriov42_0_port_unknownunicastallow': 'true', + 'vmxvpfe_flavor_name': 'ns.c20r16d25.v5' + }, + 'type': 'VF', + 'modelCustomizationName': '2017-388_ADIOD-vPE 0', + 'vfModules': {}, + 'volumeGroups': {} + }, + '2017488_ADIODvPE 0': { + 'uuid': '69e09f68-8b63-4cc9-b9ff-860960b5db09', + 'invariantUuid': '72e465fe-71b1-4e7b-b5ed-9496118ff7a8', + 'description': 'Name ADIOD vPE Description The provider edge function for the ADIOD service supported by the Junipers VMX product Category Router Vendor Juniper Vendor Release Code 17.2 Owners Mary Fragale. Updated 9-25 to use v8.0 of the Juniper Valid 2 VLM', + 'name': '2017488_ADIODvPE', + 'version': '5.0', + 'customizationUuid': '1da7b585-5e61-4993-b95e-8e6606c81e45', + 'inputs': { + 'vnf_config_template_version': { + 'type': 'string', + 'description': 'VPE Software Version', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': '17.2' + }, + 'bandwidth_units': { + 'type': 'string', + 'description': 'Units of bandwidth', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'Gbps' + }, + 'bandwidth': { + 'type': 'string', + 'description': 'Requested VPE bandwidth', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': '10' + }, + 'AIC_CLLI': { + 'type': 'string', + 'description': 'AIC Site CLLI', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'ATLMY8GA' + }, + 'ASN': { + 'type': 'string', + 'description': 'AV/PE', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'AV_vPE' + }, + 'vnf_instance_name': { + 'type': 'string', + 'description': 'The hostname assigned to the vpe.', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'mtnj309me6' + } + }, + 'commands': { + 'vnf_config_template_version': { + 'displayName': 'vnf_config_template_version', + 'command': 'get_input', + 'inputName': '2017488_adiodvpe0_vnf_config_template_version' + }, + 'bandwidth_units': { + 'displayName': 'bandwidth_units', + 'command': 'get_input', + 'inputName': 'adiodvpe0_bandwidth_units' + }, + 'bandwidth': {'displayName': 'bandwidth', 'command': 'get_input', 'inputName': 'adiodvpe0_bandwidth'}, + 'AIC_CLLI': {'displayName': 'AIC_CLLI', 'command': 'get_input', 'inputName': '2017488_adiodvpe0_AIC_CLLI'}, + 'ASN': {'displayName': 'ASN', 'command': 'get_input', 'inputName': '2017488_adiodvpe0_ASN'}, + 'vnf_instance_name': { + 'displayName': 'vnf_instance_name', + 'command': 'get_input', + 'inputName': '2017488_adiodvpe0_vnf_instance_name' + } + }, + 'properties': { + 'vmxvre_retype': 'RE-VMX', + 'vnf_config_template_version': 'get_input:2017488_adiodvpe0_vnf_config_template_version', + 'sriov44_net_id': '48d399b3-11ee-48a8-94d2-f0ea94d6be8d', + 'int_ctl_net_id': '2f323477-6936-4d01-ac53-d849430281d9', + 'vmxvpfe_sriov41_0_port_mac': '00:11:22:EF:AC:DF', + 'int_ctl_net_name': 'VMX-INTXI', + 'vmx_int_ctl_prefix': '128.0.0.0', + 'sriov43_net_id': 'da349ca1-6de9-4548-be88-2d88e99bfef5', + 'sriov42_net_id': '760669ba-013d-4d9b-b0e7-4151fe2e6279', + 'sriov41_net_id': '25ad52d5-c165-40f8-b3b0-ddfc2373280a', + 'nf_type': 'vPE', + 'vmxvpfe_int_ctl_ip_1': '128.0.0.16', + 'is_AVPN_service': 'false', + 'vmx_RSG_name': 'vREXI-affinity', + 'vmx_int_ctl_forwarding': 'l2', + 'vmxvre_oam_ip_0': '10.40.123.5', + 'vmxvpfe_sriov44_0_port_mac': '00:11:22:EF:AC:DF', + 'vmxvpfe_sriov41_0_port_vlanstrip': 'false', + 'vmxvpfe_sriov42_0_port_vlanfilter': '4001', + 'vmxvpfe_sriov44_0_port_unknownunicastallow': 'true', + 'vmxvre_image_name_0': 'VRE-ENGINE_17.2-S2.1.qcow2', + 'vmxvre_instance': '0', + 'vmxvpfe_sriov43_0_port_mac': '00:11:22:EF:AC:DF', + 'vmxvre_flavor_name': 'ns.c1r16d32.v5', + 'vmxvpfe_volume_size_0': '40.0', + 'vmxvpfe_sriov43_0_port_vlanfilter': '4001', + 'nf_naming': '{ecomp_generated_naming=true}', + 'nf_naming_code': 'Navneet', + 'vmxvre_name_0': 'vREXI', + 'vmxvpfe_sriov42_0_port_vlanstrip': 'false', + 'vmxvpfe_volume_name_0': 'vPFEXI_FBVolume', + 'vmx_RSG_id': 'bd89a33c-13c3-4a04-8fde-1a57eb123141', + 'vmxvpfe_image_name_0': 'VPE_ROUTING-ENGINE_17.2R1-S2.1.qcow2', + 'vmxvpfe_sriov43_0_port_unknownunicastallow': 'true', + 'vmxvpfe_sriov44_0_port_unknownmulticastallow': 'true', + 'vmxvre_console': 'vidconsole', + 'vmxvpfe_sriov44_0_port_vlanfilter': '4001', + 'vmxvpfe_sriov42_0_port_mac': '00:11:22:EF:AC:DF', + 'vmxvpfe_volume_id_0': '47cede15-da2f-4397-a101-aa683220aff3', + 'vmxvpfe_sriov42_0_port_unknownmulticastallow': 'true', + 'vmxvpfe_sriov44_0_port_vlanstrip': 'false', + 'vf_module_id': '123', + 'nf_function': 'JAI', + 'vmxvpfe_sriov43_0_port_unknownmulticastallow': 'true', + 'vmxvre_int_ctl_ip_0': '128.0.0.1', + 'AIC_CLLI': 'get_input:2017488_adiodvpe0_AIC_CLLI', + 'vnf_name': 'mtnj309me6vre', + 'vmxvpfe_sriov41_0_port_unknownunicastallow': 'true', + 'vmxvre_volume_type_1': 'HITACHI', + 'vmxvpfe_sriov44_0_port_broadcastallow': 'true', + 'vmxvre_volume_type_0': 'HITACHI', + 'vmxvpfe_volume_type_0': 'HITACHI', + 'vmxvpfe_sriov43_0_port_broadcastallow': 'true', + 'bandwidth_units': 'get_input:adiodvpe0_bandwidth_units', + 'vnf_id': '123', + 'vmxvre_oam_prefix': '24', + 'availability_zone_0': 'mtpocfo-kvm-az01', + 'ASN': 'get_input:2017488_adiodvpe0_ASN', + 'vmxvre_chassis_i2cid': '161', + 'vmxvpfe_name_0': 'vPFEXI', + 'bandwidth': 'get_input:adiodvpe0_bandwidth', + 'availability_zone_max_count': '1', + 'vmxvre_volume_size_0': '45.0', + 'vmxvre_volume_size_1': '50.0', + 'vmxvpfe_sriov42_0_port_broadcastallow': 'true', + 'vmxvre_oam_gateway': '10.40.123.1', + 'vmxvre_volume_name_1': 'vREXI_FAVolume', + 'vmxvre_ore_present': '0', + 'vmxvre_volume_name_0': 'vREXI_FBVolume', + 'vmxvre_type': '0', + 'vnf_instance_name': 'get_input:2017488_adiodvpe0_vnf_instance_name', + 'vmxvpfe_sriov41_0_port_unknownmulticastallow': 'true', + 'oam_net_id': 'b95eeb1d-d55d-4827-abb4-8ebb94941429', + 'vmx_int_ctl_len': '24', + 'vmxvpfe_sriov43_0_port_vlanstrip': 'false', + 'vmxvpfe_sriov41_0_port_broadcastallow': 'true', + 'vmxvre_volume_id_1': '6e86797e-03cd-4fdc-ba72-2957119c746d', + 'vmxvpfe_sriov41_0_port_vlanfilter': '4001', + 'nf_role': 'Testing', + 'vmxvre_volume_id_0': 'f4eacb79-f687-4e9d-b760-21847c8bb15a', + 'vmxvpfe_sriov42_0_port_unknownunicastallow': 'true', + 'vmxvpfe_flavor_name': 'ns.c20r16d25.v5' + }, + 'type': 'VF', + 'modelCustomizationName': '2017488_ADIODvPE 0', + 'vfModules': { + '2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vRE_BV..module-1': { + 'uuid': '25284168-24bb-4698-8cb4-3f509146eca5', + 'invariantUuid': '7253ff5c-97f0-4b8b-937c-77aeb4d79aa1', + 'customizationUuid': 'f7e7c365-60cf-49a9-9ebf-a1aa11b9d401', + 'description': null, + 'name': '2017488AdiodVpe..ADIOD_vRE_BV..module-1', + 'version': '6', + 'modelCustomizationName': '2017488AdiodVpe..ADIOD_vRE_BV..module-1', + 'properties': {'minCountInstances': 0, 'maxCountInstances': null, 'initialCount': 0}, + 'commands': {}, + 'volumeGroupAllowed': true, + 'inputs': { + '2017488_adiodvpe0_vnf_config_template_version': { + 'type': 'string', + 'description': 'VPE Software Version', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': '17.2' + }, + '2017488_adiodvpe0_AIC_CLLI': { + 'type': 'string', + 'description': 'AIC Site CLLI', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'ATLMY8GA' + } + } + }, + '2017488_adiodvpe0..2017488AdiodVpe..ADIOD_base_vPE_BV..module-0': { + 'uuid': 'f8360508-3f17-4414-a2ed-6bc71161e8db', + 'invariantUuid': 'b34833bb-6aa9-4ad6-a831-70b06367a091', + 'customizationUuid': 'a55961b2-2065-4ab0-a5b7-2fcee1c227e3', + 'description': null, + 'name': '2017488AdiodVpe..ADIOD_base_vPE_BV..module-0', + 'version': '5', + 'modelCustomizationName': '2017488AdiodVpe..ADIOD_base_vPE_BV..module-0', + 'properties': {'minCountInstances': 1, 'maxCountInstances': 1, 'initialCount': 1}, + 'commands': {}, + 'volumeGroupAllowed': false, + 'inputs': { + '2017488_adiodvpe0_ASN': { + 'type': 'string', + 'description': 'AV/PE', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'AV_vPE' + }, + 'adiodvpe0_bandwidth': { + 'type': 'string', + 'description': 'Requested VPE bandwidth', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': '10' + } + } + }, + '2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vPFE_BV..module-2': { + 'uuid': '0a0dd9d4-31d3-4c3a-ae89-a02f383e6a9a', + 'invariantUuid': 'eff8cc59-53a1-4101-aed7-8cf24ecf8339', + 'customizationUuid': '3cd946bb-50e0-40d8-96d3-c9023520b557', + 'description': null, + 'name': '2017488AdiodVpe..ADIOD_vPFE_BV..module-2', + 'version': '6', + 'modelCustomizationName': '2017488AdiodVpe..ADIOD_vPFE_BV..module-2', + 'properties': {'minCountInstances': 0, 'maxCountInstances': null, 'initialCount': 0}, + 'commands': {}, + 'volumeGroupAllowed': true, + 'inputs': {} + } + }, + 'volumeGroups': { + '2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vRE_BV..module-1': { + 'uuid': '25284168-24bb-4698-8cb4-3f509146eca5', + 'invariantUuid': '7253ff5c-97f0-4b8b-937c-77aeb4d79aa1', + 'customizationUuid': 'f7e7c365-60cf-49a9-9ebf-a1aa11b9d401', + 'description': null, + 'name': '2017488AdiodVpe..ADIOD_vRE_BV..module-1', + 'version': '6', + 'modelCustomizationName': '2017488AdiodVpe..ADIOD_vRE_BV..module-1', + 'properties': {'minCountInstances': 0, 'maxCountInstances': null, 'initialCount': 0} + }, + '2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vPFE_BV..module-2': { + 'uuid': '0a0dd9d4-31d3-4c3a-ae89-a02f383e6a9a', + 'invariantUuid': 'eff8cc59-53a1-4101-aed7-8cf24ecf8339', + 'customizationUuid': '3cd946bb-50e0-40d8-96d3-c9023520b557', + 'description': null, + 'name': '2017488AdiodVpe..ADIOD_vPFE_BV..module-2', + 'version': '6', + 'modelCustomizationName': '2017488AdiodVpe..ADIOD_vPFE_BV..module-2', + 'properties': {'minCountInstances': 0, 'maxCountInstances': null, 'initialCount': 0} + } + } + } + }, + 'vfModules': { + '2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vRE_BV..module-1': { + 'uuid': '25284168-24bb-4698-8cb4-3f509146eca5', + 'invariantUuid': '7253ff5c-97f0-4b8b-937c-77aeb4d79aa1', + 'customizationUuid': 'f7e7c365-60cf-49a9-9ebf-a1aa11b9d401', + 'description': null, + 'name': '2017488AdiodVpe..ADIOD_vRE_BV..module-1', + 'version': '6', + 'modelCustomizationName': '2017488AdiodVpe..ADIOD_vRE_BV..module-1', + 'properties': {'minCountInstances': 0, 'maxCountInstances': null, 'initialCount': 0}, + 'commands': {}, + 'volumeGroupAllowed': true + }, + '2017488_adiodvpe0..2017488AdiodVpe..ADIOD_base_vPE_BV..module-0': { + 'uuid': 'f8360508-3f17-4414-a2ed-6bc71161e8db', + 'invariantUuid': 'b34833bb-6aa9-4ad6-a831-70b06367a091', + 'customizationUuid': 'a55961b2-2065-4ab0-a5b7-2fcee1c227e3', + 'description': null, + 'name': '2017488AdiodVpe..ADIOD_base_vPE_BV..module-0', + 'version': '5', + 'modelCustomizationName': '2017488AdiodVpe..ADIOD_base_vPE_BV..module-0', + 'properties': {'minCountInstances': 1, 'maxCountInstances': 1, 'initialCount': 1}, + 'commands': {}, + 'volumeGroupAllowed': false + }, + '2017488_adiodvpe0..2017488AdiodVpe..ADIOD_vPFE_BV..module-2': { + 'uuid': '0a0dd9d4-31d3-4c3a-ae89-a02f383e6a9a', + 'invariantUuid': 'eff8cc59-53a1-4101-aed7-8cf24ecf8339', + 'customizationUuid': '3cd946bb-50e0-40d8-96d3-c9023520b557', + 'description': null, + 'name': '2017488AdiodVpe..ADIOD_vPFE_BV..module-2', + 'version': '6', + 'modelCustomizationName': '2017488AdiodVpe..ADIOD_vPFE_BV..module-2', + 'properties': {'minCountInstances': 0, 'maxCountInstances': null, 'initialCount': 0}, + 'commands': {}, + 'volumeGroupAllowed': true + } + }, + 'networks': {}, + 'collectionResource': {}, + 'configurations': {}, + 'serviceProxies': {}, + 'pnfs': {} + } + )) + } + +}); diff --git a/vid-webpack-master/src/app/configuration/vid.configuration.ts b/vid-webpack-master/src/app/configuration/vid.configuration.ts new file mode 100644 index 00000000..2430d3ba --- /dev/null +++ b/vid-webpack-master/src/app/configuration/vid.configuration.ts @@ -0,0 +1,98 @@ +export class VidConfiguration { + + public static VNF_STATUS_CHECK_ENABLED = false; + + /* + * UPLOAD_SUPPLEMENTARY_STATUS_CHECK_ENABLED: Determines the Property to Govern Presence of + * Upload Supplementary File on Volume Group Screen. + * Set to false, to disable the check. + */ + public static UPLOAD_SUPPLEMENTARY_STATUS_CHECK_ENABLED = false; + + /* + * List of valid VNF status combinations + */ + public static VNF_VALID_STATUS_LIST = [ + { + 'provStatus': 'preprov', + 'orchestrationStatus': 'pending-create', + 'inMaint': false, + 'operationalStatus': null + }, + { + 'provStatus': 'preprov', + 'orchestrationStatus': 'created', + 'inMaint': false, + 'operationalStatus': null + }, + { + 'provStatus': 'preprov', + 'orchestrationStatus': 'active', + 'inMaint': false, + 'operationalStatus': null + }, + { + 'provStatus': 'nvtprov', + 'orchestrationStatus': 'active', + 'inMaint': false, + 'operationalStatus': null + }, + { + 'provStatus': 'prov', + 'orchestrationStatus': 'active', + 'inMaint': false, + 'operationalStatus': 'out-of-service-path' + }, + { + 'provStatus': 'prov', + 'orchestrationStatus': 'activated', + 'inMaint': false, + 'operationalStatus': 'out-of-service-path' + } + ]; + + /* + * The model status VID uses to query SDC for a list of models. The possible values are: + * DISTRIBUTION_NOT_APPROVED, + * DISTRIBUTION_APPROVED, + * DISTRIBUTED, + * DISTRIBUTION_REJECTED, + * ALL, + * In the production env, this should always be set to DISTRIBUTED + */ + public static ASDC_MODEL_STATUS = 'DISTRIBUTED'; + + /* + * Max number of times that VID will poll MSO for a given request status + */ + public static MSO_MAX_POLLS = 10; + + /* + * Number of msecs that VID will wait between MSO polls. + */ + public static MSO_POLLING_INTERVAL_MSECS = 10000; + + public static SCHEDULER_POLLING_INTERVAL_MSECS = 10000; + + public static SCHEDULER_MAX_POLLS = 10; + + public static COMPONENT_LIST_NAMED_QUERY_ID = '0367193e-c785-4d5f-9cb8-7bc89dc9ddb7'; + + /* + * List of all service model invariant UUIDs that need macro instantiation. + * Example: + * MACRO_SERVICES : ['3cf30cbb-5fe7-4fb3-b049-559a4997b221', 'b135a703-bab5-4295-a37f-580a4f2d0961'] + * + */ + public static MACRO_SERVICES = ['c9514b73-3dfe-4d7e-9146-b318d48655d9', '93150ffa-00c6-4ea0-85f2-3536ca46ebd2', + '2b54297f-72e7-4a94-b451-72df88d0be0b', + 'd27e42cf-087e-4d31-88ac-6c4b7585f800', + 'ec0c4bab-c272-4dab-b087-875031bb0c9f', '0311f998-9268-4fd6-bbba-afff15087b72', + '43596836-ae36-4608-a987-6608ede10dac', '306caa85-74c7-48a9-aa22-7e3a564b957a', + 'e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0']; + + public static SCHEDULER_CALLBACK_URL = 'https://vid-web-ete.ecomp.cci.att.com:8000/vid/change-management/workflow/'; + + public static SCHEDULER_PORTAL_URL = 'http://www.ecomp.att.com'; + +} diff --git a/vid-webpack-master/src/app/drawingBoard/available-models-tree/available-models-tree.component.html b/vid-webpack-master/src/app/drawingBoard/available-models-tree/available-models-tree.component.html new file mode 100644 index 00000000..5eb97732 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/available-models-tree/available-models-tree.component.html @@ -0,0 +1,35 @@ +<div class="available-models-tree"> + <h5> + <span>SERVICE MODEL:</span> + <span id="service-model-name">{{service | serviceInfo: _store: serviceModelId : 'name'}}</span> + </h5> + <div class="available-models-content-wrapper"> + <div class="search-container"> + <input [attr.data-tests-id]="'search-left-tree'" #filter (keyup)="searchTree(filter.value, $event)" placeholder="Filter..."/> + <span class="icon-search"></span> + </div> + + <tree-root #tree [attr.data-tests-id]="'available-models-tree'" [nodes]="nodes" [options]="options"> + <ng-template #treeNodeTemplate let-node let-index="index"> + <div [attr.data-tests-id]="'node-'+node.data.name" (click)="selectNode(node)" [ngClass]="{'selected': index , 'isParent': node.data.type !== 'VFmodule' , 'isChild': node.data.type === 'VFmodule' }"> + <span class="vf-type">{{node.data.type.substring(0,1)}}</span> + <span class="span-name" [innerHTML]=" isFilterEnabled ? (node.data.name | highlight : filter.value) : (node.data.name)"></span> + <span class="actions"> + <span class="number-button" *ngIf="isShowNodeCount(node)"> + <span>{{getNodeCount(node)}}</span> + </span> + <span class="icon-v" *ngIf="isShowIconV(node)"> + <span ></span> + </span> + <span class="icon-plus" *ngIf="isShowIconAdd(node)"> + <span tooltip="Add" [attr.data-tests-id]="'node-'+node.data.name+'-add-btn'" (click)="onClickAdd($event,node)"> + <i class="fa fa-plus-circle" aria-hidden="true"></i> + </span> + </span> + </span> + </div> + </ng-template> + </tree-root> + + </div> +</div> diff --git a/vid-webpack-master/src/app/drawingBoard/available-models-tree/available-models-tree.component.scss b/vid-webpack-master/src/app/drawingBoard/available-models-tree/available-models-tree.component.scss new file mode 100644 index 00000000..44f94109 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/available-models-tree/available-models-tree.component.scss @@ -0,0 +1,398 @@ +available-models-tree { + .available-models-tree { + height: 100%; + display: flex; + flex-direction: column; + line-height: 14px; + border-right: #D2D2D2 1.5px solid; + min-width: 340px; + h5 { + font-family: OpenSans-Semibold; + color: #5A5A5A; + background-color: #F2F2F2; + margin: 0; + padding: 15px; + padding-left: 20px; + span { + vertical-align: middle; + &:first-child { + font-size: 12px; + color: #191919; + } + } + } + .available-models-content-wrapper { + flex: 1; + display: flex; + flex-direction: column; + padding: 20px; + background-color: #F2F2F2; + .search-container { + margin-bottom: 30px; + width: 100%; + display: flex; + background: #FFFFFF; + border: 1px solid #D2D2D2; + border-radius: 2px; + height: 40px; + min-width: 40px; + font-family: OpenSans-Italic; + color: #959595; + input { + flex: 1; + border: 0; + padding-left: 10px; + outline: 0; + } + .icon-search { + display: flex; + width: 40px; + &:after { + content: "\e92e"; + cursor: pointer; + font-size: 20px; + font-weight: 600; + text-align: center; + display: inline-block; + flex: auto; + align-self: center; + } + } + } + tree-root { + flex: 1; + display: flex; + } + tree-viewport { + flex: 1; + height: auto; + overflow: auto; + padding-top: 5px; + .tree-node { + color: #5A5A5A; + font-size: 13px; + white-space: normal; + word-break: break-all; + tree-node-drop-slot { + .node-drop-slot { + display: none; + } + } + &.tree-node-disabled { + color: #D2D2D2; + cursor: default; + pointer-events: none; + } + &:not(.tree-node-disabled) { + >tree-node-wrapper { + .node-wrapper:hover { + color: #009FDB; + .node-content-wrapper { + tree-node-content { + > div { + span.actions { + .number-button { + span { + //background-color: #009FDB; + } + } + .icon-plus span:before { + display: inline-block; + color: #5A5A5A; + } + } + } + } + } + } + } + } + &.tree-node-focused:not(.tree-node-disabled) { + & > tree-node-wrapper { + .node-wrapper { + color: #009FDB; + .node-content-wrapper-focused, + .node-content-wrapper:hover { + background: none; + box-shadow: none; + tree-node-content { + > div { + span.actions { + .number-button { + span { + //background-color: #009FDB; + } + } + } + } + } + } + } + } + } + tree-node-wrapper { + .node-wrapper { + height: 36px; + tree-node-expander { + font-family: 'icomoon' !important; + height: 100%; + .toggle-children-wrapper { + padding: 0; + display: block; + height: 100%; + span.toggle-children { + display: flex; + width: 20px; + top: 0; + height: inherit; + background-image: none; + &:before { + content: "\e900"; + font-weight: 600; + text-align: center; + display: inline-block; + flex: auto; + align-self: center; + font-size: 20px; + } + } + } + .toggle-children-wrapper-expanded { + span.toggle-children { + transform: none; + &:before { + content: "\e930"; + } + } + } + .toggle-children-placeholder { + width: 20px; + } + } + .node-content-wrapper { + padding: 0; + background: none; + box-shadow: none; + height: 100%; + flex: 1; + min-width: 0; + border-left: 1px solid #D2D2D2; + tree-node-content { + > div { + height: 100%; + display: flex; + align-items: center; + justify-content: space-between; + padding-left: 10px; + span { + &.actions { + height: 100%; + display: flex; + justify-content: space-between; + align-items: center; + >span { + width: 45px; + max-width: 45px; + text-align: center; + } + .number-button { + width: 30px; + padding-left: 0; + text-align: center; + span { + display: block; + font-size: 11px; + } + } + .icon-v { + width: 45px; + span:before { + content: "\e932"; + color: #5A5A5A; + font-size: 16px; + text-align: center; + display: inline-block; + vertical-align: baseline; + } + } + .icon-plus { + width: 45px; + span { + &:before { + //content: "\e901"; + //fill:#009FDB; + //color: #009FDB; + //font-size: 16px; + //text-align: center; + //display: none; + //vertical-align: baseline; + } + &:hover:before { + //color: #009FDB !important; + + } + } + } + } + } + } + } + } + + } + } + tree-node-children { + .tree-children { + padding-left: 20px; + } + } + } + } + + } + } +} +.highlight { + background-color: #9DD9EF; +} + +#drawing-board-tree{ + .tree-node.tree-node-expanded.tree-node-focused { + border: 1px solid #009FDB; + } + +} + +available-models-tree { + + .tree-root { + margin-top: 35px; + } + + tree-node-expander { + background: #FFFFFF; + border: 1px solid #D2D2D2; + border-right: none; + width: 45px; + padding-left: 12px; + } + + .node-content-wrapper { + border: none; + } + + tree-node-wrapper tree-node-expander{ + background: none !important; + border: none !important; + } + + tree-node-content div { + background: white; + } + + .node-wrapper { + height: 45px !important; + background: #FFFFFF; + border: 1px solid #D2D2D2; + } + + tree-node-collection div { + margin-top: 0px; + } + + .tree-node-leaf .node-wrapper tree-node-expander { + display: none; + } + + .tree-children { + padding: 20px; + } + + .tree-node.tree-node-expanded.tree-node-focused { + border: 1px solid #009FDB; + } + + .tree-node.tree-node-expanded { + border: 1px solid rgba(128, 128, 128, 0.72); + margin-bottom: 10px; + } + + .tree-children { + padding-left: 0; + } + + tree-node-content .actions .number-button { + height: 45px; + padding-top: 14px; + border: 1px solid #D2D2D2; + padding-left: 0; + span { + background: none; + font-size: 11px; + color: #5A5A5A; + } + } + + + + .node-content-wrapper.node-content-wrapper-focused tree-node-content div{ + background: #009FDB !important; + color: white; + + .isParent { + border-left: 1px solid #009FDB; + } + + .number-button span{ + color: white !important; + } + + .icon-v span:before{ + color: white !important; + } + } + + .vf-type { + width: 20px; + height: 45px; + padding-top: 16px; + border-right: 1px solid #D2D2D2; + + } + + .isParent { + width: 100%; + padding-left: 5px; + } + + .tree-node-expanded .isChild .vf-type { + display: none; + } + + .isParent .span-name { + width: 100%; + padding-left: 10px; + } + + .toggle-children-wrapper.toggle-children-wrapper-expanded { + .toggle-children:before { + color: #009FDB; + } + } + + .tree-node.tree-node-expanded .tree-children { + border: 1px solid rgba(128, 128, 128, 0.72); + } + + .tree-node.tree-node-expanded.tree-node-focused .tree-children { + border: 1px solid #009fdb; + } + + .tree-node-leaf .node-wrapper{ + margin-left: 45px; + border-left: none; + } +} + + + diff --git a/vid-webpack-master/src/app/drawingBoard/available-models-tree/available-models-tree.component.ts b/vid-webpack-master/src/app/drawingBoard/available-models-tree/available-models-tree.component.ts new file mode 100644 index 00000000..4e5819e4 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/available-models-tree/available-models-tree.component.ts @@ -0,0 +1,166 @@ +import {Component, EventEmitter, Output, ViewChild} from '@angular/core'; +import {ITreeOptions, TreeComponent} from 'angular-tree-component'; +import '../../../../node_modules/angular-tree-component/dist/angular-tree-component.css'; +import {IDType, ITreeNode} from 'angular-tree-component/dist/defs/api'; +import {DialogService} from 'ng2-bootstrap-modal'; +import {AvailableModelsTreeService} from './available-models-tree.service'; +import {NgRedux} from "@angular-redux/store"; +import {ActivatedRoute} from '@angular/router'; +import {AppState} from '../../store/reducers'; +import {AaiService} from '../../services/aaiService/aai.service'; +import {ServicePlanningService} from '../../services/service-planning.service'; +import {VnfPopupComponent} from '../../components/vnf-popup/vnf-popup.components'; +import {ServiceNodeTypes} from '../../shared/models/ServiceNodeTypes'; +import {VfModuleMap} from '../../shared/models/vfModulesMap'; +import {IframeService} from "../../shared/utils/iframe.service"; +import {createVFModuleInstance} from "../../service.actions"; +import {DefaultDataGeneratorService} from "../../shared/services/defaultDataServiceGenerator/default.data.generator.service"; + + +@Component({ + selector: 'available-models-tree', + templateUrl: './available-models-tree.component.html', + styleUrls: ['./available-models-tree.component.scss'] +}) + + +export class AvailableModelsTreeComponent{ + + serviceModelId: string; + serviceHierarchy; + parentElementClassName = 'content'; + _store : NgRedux<AppState>; + constructor(private _servicePlanningService: ServicePlanningService, + private _iframeService: IframeService, + private _aaiService: AaiService, + private route: ActivatedRoute, + private dialogService: DialogService, + private _availableModelsTreeService: AvailableModelsTreeService, + private _defaultDataGeneratorService: DefaultDataGeneratorService, + private store: NgRedux<AppState>) { + this._store = store; + this.route + .queryParams + .subscribe(params => { + this.serviceModelId = params['serviceModelId']; + this._aaiService.getServiceModelById(this.serviceModelId).subscribe( + value => { + this.serviceHierarchy = value; + this.nodes = this._servicePlanningService.convertServiceModelToTreeNodes(this.serviceHierarchy); + }, + error => { + console.log('error is ', error) + } + ); + }); + + } + + @Output() + highlightInstances: EventEmitter<number> = new EventEmitter<number>(); + @ViewChild('tree') tree: TreeComponent; + + nodes = []; + service = {name: ''}; + isFilterEnabled: boolean = false; + + options: ITreeOptions = { + nodeHeight: 36, + dropSlotHeight: 0, + nodeClass: (node: ITreeNode) => { + if(node.data.type === ServiceNodeTypes.VFmodule && !this.getNodeCount(node.parent)) + { + node.data.disabled = true; + return 'tree-node tree-node-disabled'; + } + node.data.disabled = false; + return 'tree-node'; + } + }; + + expandParentByNodeId(id: IDType): void { + this.tree.treeModel.getNodeById(id).parent.expand(); + } + + searchTree(searchText: string, event: KeyboardEvent): void { + if (searchText === '') { + return; + } + this.isFilterEnabled = event.key === 'Delete' || event.key === 'Backspace' || searchText.length > 1; + if (this.isFilterEnabled) { + let __this = this; + let results: ITreeNode[] = []; + this.nodes.forEach(function (node) { + __this.searchTreeNode(node, searchText, results); + }); + results.forEach(function (result) { + __this.expandParentByNodeId(result.id) + }); + } + } + + searchTreeNode(node, searchText: string, results): void { + if (node.name.toLowerCase().indexOf(searchText.toLowerCase()) != -1) { + results.push(node); + } + if (node.children != null) { + for (let i = 0; i < node.children.length; i++) { + this.searchTreeNode(node.children[i], searchText, results); + } + } + } + + selectNode(node: ITreeNode): void { + node.expand(); + this.highlightInstances.emit(node.data.id); + } + + onClickAdd(e: MouseEvent, node: ITreeNode): void { + let data = node.data; + let dynamicInputs = data.dynamicInputs; + let userProvidedNaming:boolean = data.userProvidedNaming; + let type:string = data.type; + if(!this.store.getState().global.flags['FLAG_SETTING_DEFAULTS_IN_DRAWING_BOARD']|| node.data.type === ServiceNodeTypes.VF || this._availableModelsTreeService.shouldOpenDialog(type, dynamicInputs, userProvidedNaming)) { + this._iframeService.addClassOpenModal(this.parentElementClassName); + this.dialogService.addDialog(VnfPopupComponent, { + serviceModelId: this.serviceModelId, + parentModelName: node.parent && node.parent.data.name, + modelName: data.name, + modelType: type, + dynamicInputs: dynamicInputs, + userProvidedNaming: userProvidedNaming, + isNewVfModule : true + }); + } + else { + let vfModule = this._defaultDataGeneratorService.generateVFModule(this.serviceHierarchy, node.parent.data.name, node.data.name); + this.store.dispatch(createVFModuleInstance(vfModule, node.data.name, this.serviceModelId)); + } + e.preventDefault(); + e.stopPropagation(); + } + + getNodeCount(node: ITreeNode): number { + let modelName: string = node.data.name; + if (ServicePlanningService.isVfModule(node)) { + let parentVnfModelName = node.parent.data.name; + let vfModuleMap: VfModuleMap = this._servicePlanningService.getVfModuleMap(this.serviceModelId, parentVnfModelName, modelName); + return vfModuleMap ? Object.keys(vfModuleMap).length : 0; + } else if (ServicePlanningService.isVnf(node)) { + let vnfInstance = this._servicePlanningService.getVnfInstance(this.serviceModelId, modelName); + return vnfInstance ? 1 : 0; + } + } + + isShowIconV(node: ITreeNode): boolean { + return this.getNodeCount(node) > 0; + } + + isShowNodeCount(node: ITreeNode): boolean { + return this.getNodeCount(node) > 0; + } + + isShowIconAdd(node: ITreeNode): boolean { + return this._availableModelsTreeService.shouldShowAddIcon(node, this.store.getState().service.serviceHierarchy, this.serviceModelId, this.getNodeCount(node)); + } +} diff --git a/vid-webpack-master/src/app/drawingBoard/available-models-tree/available-models-tree.service.spec.ts b/vid-webpack-master/src/app/drawingBoard/available-models-tree/available-models-tree.service.spec.ts new file mode 100644 index 00000000..10cbb0d8 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/available-models-tree/available-models-tree.service.spec.ts @@ -0,0 +1,450 @@ +import {TestBed, getTestBed} from '@angular/core/testing'; +import { + HttpClientTestingModule, + HttpTestingController +} from '@angular/common/http/testing'; +import {AvailableModelsTreeService} from './available-models-tree.service'; +import {ServicePlanningService} from "../../services/service-planning.service"; +import {ServiceNodeTypes} from "../../shared/models/ServiceNodeTypes"; +import {NgRedux} from "@angular-redux/store"; +import {MockAppStore} from "../../services/service-planning.service.spec"; + +describe('Available Models Tree Service', () => { + let injector; + let service: AvailableModelsTreeService; + let httpMock: HttpTestingController; + + beforeEach(() => { + + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [AvailableModelsTreeService, + ServicePlanningService, + {provide: NgRedux, useClass: MockAppStore}] + }); + + injector = getTestBed(); + service = injector.get(AvailableModelsTreeService); + httpMock = injector.get(HttpTestingController); + }); + + describe('#shouldShowAddIcon', () => { + it('should return true if number of current vnf modules is under the max', (done: DoneFn) => { + let treeNode = { + data: { + children: [], + name: 'vf_vmee0..VfVmee..base_vmme..module-0' + } + }; + + let serviceHierarchy = getSericeServiceHierarchy(); + let result = service.shouldShowAddIcon(treeNode, serviceHierarchy, '6e59c5de-f052-46fa-aa7e-2fca9d674c44', 0); + expect(result).toBeTruthy(); + done(); + }); + + it('should return false if number of current vnf modules are more than max', (done: DoneFn) => { + let treeNode = { + data: { + children: [], + name: 'vf_vmee0..VfVmee..base_vmme..module-0' + } + }; + + let serviceHierarchy = getSericeServiceHierarchy(); + let result = service.shouldShowAddIcon(treeNode, serviceHierarchy, '6e59c5de-f052-46fa-aa7e-2fca9d674c44', 2); + expect(result).toBeFalsy(); + done(); + }); + + it('should return true if number of current vnf modules are more than max and max is null', (done: DoneFn) => { + let treeNode = { + data: { + children: [], + name: 'vf_vmee0..VfVmee..base_vmme..module-0' + } + }; + + let serviceHierarchy = getSericeServiceHierarchy(); + let result = service.shouldShowAddIcon(treeNode, serviceHierarchy, '6e59c5de-f052-46fa-aa7e-2fca9d674c44', 0); + expect(result).toBeTruthy(); + done(); + }); + }); + + describe('#shouldOpenModalDialogOnAddInstance', () => { + let serviceHierarchy = getSericeServiceHierarchy(); + + it('should open popup on add instance', (done: DoneFn) => { + // add vnf should return true + let result = service.shouldOpenDialog(ServiceNodeTypes.VF, [], false); + expect(result).toBeTruthy(); + + // add vfModule with user provided naming should return true + result = service.shouldOpenDialog(ServiceNodeTypes.VFmodule, [], true); + expect(result).toBeTruthy(); + + // add vfModule with dynamicInputs without defaultValues should return true + result = service.shouldOpenDialog(ServiceNodeTypes.VFmodule, [{ + id: '2017488_adiodvpe0_vnf_config_template_version', + type: 'string', + name: '2017488_adiodvpe0_vnf_config_template_version', + isRequired: true, + description: 'VPE Software Version' + }], false); + expect(result).toBeTruthy(); + + // add vfModule with dynamicInputs with defaultValues should return false + result = service.shouldOpenDialog(ServiceNodeTypes.VFmodule, [{ + id: '2017488_adiodvpe0_vnf_config_template_version', + type: 'string', + name: '2017488_adiodvpe0_vnf_config_template_version', + value: '17.2', + isRequired: true, + description: 'VPE Software Version' + }], false); + expect(result).toBeFalsy(); + done(); + }); + }); + + function getSericeServiceHierarchy() { + return JSON.parse(JSON.stringify( + { + '6e59c5de-f052-46fa-aa7e-2fca9d674c44': { + 'service': { + 'uuid': '6e59c5de-f052-46fa-aa7e-2fca9d674c44', + 'invariantUuid': 'e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0', + 'name': 'ComplexService', + 'version': '1.0', + 'toscaModelURL': null, + 'category': 'Mobility', + 'serviceType': '', + 'serviceRole': '', + 'description': 'ComplexService', + 'serviceEcompNaming': 'true', + 'instantiationType': 'Macro', + 'inputs': {} + }, + 'vnfs': { + 'VF_vMee 0': { + 'uuid': 'd6557200-ecf2-4641-8094-5393ae3aae60', + 'invariantUuid': '4160458e-f648-4b30-a176-43881ffffe9e', + 'description': 'VSP_vMee', + 'name': 'VF_vMee', + 'version': '2.0', + 'customizationUuid': '91415b44-753d-494c-926a-456a9172bbb9', + 'inputs': {}, + 'commands': {}, + 'properties': { + 'gpb2_Internal2_mac': '00:80:37:0E:02:22', + 'sctp-b-ipv6-egress_src_start_port': '0', + 'sctp-a-ipv6-egress_rule_application': 'any', + 'Internal2_allow_transit': 'true', + 'sctp-b-IPv6_ethertype': 'IPv6', + 'sctp-a-egress_rule_application': 'any', + 'sctp-b-ingress_action': 'pass', + 'sctp-b-ingress_rule_protocol': 'icmp', + 'ncb2_Internal1_mac': '00:80:37:0E:0F:12', + 'sctp-b-ipv6-ingress-src_start_port': '0.0', + 'ncb1_Internal2_mac': '00:80:37:0E:09:12', + 'fsb_volume_size_0': '320.0', + 'sctp-b-egress_src_addresses': 'local', + 'sctp-a-ipv6-ingress_ethertype': 'IPv4', + 'sctp-a-ipv6-ingress-dst_start_port': '0', + 'sctp-b-ipv6-ingress_rule_application': 'any', + 'domain_name': 'default-domain', + 'sctp-a-ingress_rule_protocol': 'icmp', + 'sctp-b-egress-src_start_port': '0.0', + 'sctp-a-egress_src_addresses': 'local', + 'sctp-b-display_name': 'epc-sctp-b-ipv4v6-sec-group', + 'sctp-a-egress-src_start_port': '0.0', + 'sctp-a-ingress_ethertype': 'IPv4', + 'sctp-b-ipv6-ingress-dst_end_port': '65535', + 'sctp-b-dst_subnet_prefix_v6': '::', + 'nf_naming': '{ecomp_generated_naming=true}', + 'sctp-a-ipv6-ingress_src_subnet_prefix': '0.0.0.0', + 'sctp-b-egress-dst_start_port': '0.0', + 'ncb_flavor_name': 'nv.c20r64d1', + 'gpb1_Internal1_mac': '00:80:37:0E:01:22', + 'sctp-b-egress_dst_subnet_prefix_len': '0.0', + 'Internal2_net_cidr': '169.255.0.0', + 'sctp-a-ingress-dst_start_port': '0.0', + 'sctp-a-egress-dst_start_port': '0.0', + 'fsb1_Internal2_mac': '00:80:37:0E:0B:12', + 'sctp-a-egress_ethertype': 'IPv4', + 'vlc_st_service_mode': 'in-network-nat', + 'sctp-a-ipv6-egress_ethertype': 'IPv4', + 'sctp-a-egress-src_end_port': '65535.0', + 'sctp-b-ipv6-egress_rule_application': 'any', + 'sctp-b-egress_action': 'pass', + 'sctp-a-ingress-src_subnet_prefix_len': '0.0', + 'sctp-b-ipv6-ingress-src_end_port': '65535.0', + 'sctp-b-name': 'epc-sctp-b-ipv4v6-sec-group', + 'fsb2_Internal1_mac': '00:80:37:0E:0D:12', + 'sctp-a-ipv6-ingress-src_start_port': '0.0', + 'sctp-b-ipv6-egress_ethertype': 'IPv4', + 'Internal1_net_cidr': '169.253.0.0', + 'sctp-a-egress_dst_subnet_prefix': '0.0.0.0', + 'fsb_flavor_name': 'nv.c20r64d1', + 'sctp_rule_protocol': '132', + 'sctp-b-ipv6-ingress_src_subnet_prefix_len': '0', + 'sctp-a-ipv6-ingress_rule_application': 'any', + 'sctp-a-IPv6_ethertype': 'IPv6', + 'vlc2_Internal1_mac': '00:80:37:0E:02:12', + 'vlc_st_virtualization_type': 'virtual-machine', + 'sctp-b-ingress-dst_start_port': '0.0', + 'sctp-b-ingress-dst_end_port': '65535.0', + 'sctp-a-ipv6-ingress-src_end_port': '65535.0', + 'sctp-a-display_name': 'epc-sctp-a-ipv4v6-sec-group', + 'sctp-b-ingress_rule_application': 'any', + 'int2_sec_group_name': 'int2-sec-group', + 'vlc_flavor_name': 'nd.c16r64d1', + 'sctp-b-ipv6-egress_src_addresses': 'local', + 'vlc_st_interface_type_int1': 'other1', + 'sctp-b-egress-src_end_port': '65535.0', + 'sctp-a-ipv6-egress-dst_start_port': '0', + 'vlc_st_interface_type_int2': 'other2', + 'sctp-a-ipv6-egress_rule_protocol': 'any', + 'Internal2_shared': 'false', + 'sctp-a-ipv6-egress_dst_subnet_prefix_len': '0', + 'Internal2_rpf': 'disable', + 'vlc1_Internal1_mac': '00:80:37:0E:01:12', + 'sctp-b-ipv6-egress_src_end_port': '65535', + 'sctp-a-ipv6-egress_src_addresses': 'local', + 'sctp-a-ingress-dst_end_port': '65535.0', + 'sctp-a-ipv6-egress_src_end_port': '65535', + 'Internal1_forwarding_mode': 'l2', + 'Internal2_dhcp': 'false', + 'sctp-a-dst_subnet_prefix_v6': '::', + 'pxe_image_name': 'MME_PXE-Boot_16ACP04_GA.qcow2', + 'vlc_st_interface_type_gtp': 'other0', + 'ncb1_Internal1_mac': '00:80:37:0E:09:12', + 'sctp-b-src_subnet_prefix_v6': '::', + 'sctp-a-egress_dst_subnet_prefix_len': '0.0', + 'int1_sec_group_name': 'int1-sec-group', + 'Internal1_dhcp': 'false', + 'sctp-a-ipv6-egress_dst_end_port': '65535', + 'Internal2_forwarding_mode': 'l2', + 'fsb2_Internal2_mac': '00:80:37:0E:0D:12', + 'sctp-b-egress_dst_subnet_prefix': '0.0.0.0', + 'Internal1_net_cidr_len': '17', + 'gpb2_Internal1_mac': '00:80:37:0E:02:22', + 'sctp-b-ingress-src_subnet_prefix_len': '0.0', + 'sctp-a-ingress_dst_addresses': 'local', + 'sctp-a-egress_action': 'pass', + 'fsb_volume_type_0': 'SF-Default-SSD', + 'ncb2_Internal2_mac': '00:80:37:0E:0F:12', + 'vlc_st_interface_type_sctp_a': 'left', + 'vlc_st_interface_type_sctp_b': 'right', + 'sctp-a-src_subnet_prefix_v6': '::', + 'vlc_st_version': '2', + 'sctp-b-egress_ethertype': 'IPv4', + 'sctp-a-ingress_rule_application': 'any', + 'gpb1_Internal2_mac': '00:80:37:0E:01:22', + 'instance_ip_family_v6': 'v6', + 'sctp-a-ipv6-egress_src_start_port': '0', + 'sctp-b-ingress-src_start_port': '0.0', + 'sctp-b-ingress_dst_addresses': 'local', + 'fsb1_Internal1_mac': '00:80:37:0E:0B:12', + 'vlc_st_interface_type_oam': 'management', + 'multi_stage_design': 'false', + 'oam_sec_group_name': 'oam-sec-group', + 'Internal2_net_gateway': '169.255.0.3', + 'sctp-a-ipv6-ingress-dst_end_port': '65535', + 'sctp-b-ipv6-egress-dst_start_port': '0', + 'Internal1_net_gateway': '169.253.0.3', + 'sctp-b-ipv6-egress_rule_protocol': 'any', + 'gtp_sec_group_name': 'gtp-sec-group', + 'sctp-a-ipv6-egress_dst_subnet_prefix': '0.0.0.0', + 'sctp-b-ipv6-egress_dst_subnet_prefix_len': '0', + 'sctp-a-ipv6-ingress_dst_addresses': 'local', + 'sctp-a-egress_rule_protocol': 'icmp', + 'sctp-b-ipv6-egress_action': 'pass', + 'sctp-a-ipv6-egress_action': 'pass', + 'Internal1_shared': 'false', + 'sctp-b-ipv6-ingress_rule_protocol': 'any', + 'Internal2_net_cidr_len': '17', + 'sctp-a-name': 'epc-sctp-a-ipv4v6-sec-group', + 'sctp-a-ingress-src_end_port': '65535.0', + 'sctp-b-ipv6-ingress_src_subnet_prefix': '0.0.0.0', + 'sctp-a-egress-dst_end_port': '65535.0', + 'sctp-a-ingress_action': 'pass', + 'sctp-b-egress_rule_protocol': 'icmp', + 'sctp-b-ipv6-ingress_action': 'pass', + 'vlc_st_service_type': 'firewall', + 'sctp-b-ipv6-egress_dst_end_port': '65535', + 'sctp-b-ipv6-ingress-dst_start_port': '0', + 'vlc2_Internal2_mac': '00:80:37:0E:02:12', + 'vlc_st_availability_zone': 'true', + 'fsb_volume_image_name_1': 'MME_FSB2_16ACP04_GA.qcow2', + 'sctp-b-ingress-src_subnet_prefix': '0.0.0.0', + 'sctp-a-ipv6-ingress_src_subnet_prefix_len': '0', + 'Internal1_allow_transit': 'true', + 'gpb_flavor_name': 'nv.c20r64d1', + 'availability_zone_max_count': '1', + 'fsb_volume_image_name_0': 'MME_FSB1_16ACP04_GA.qcow2', + 'sctp-b-ipv6-ingress_dst_addresses': 'local', + 'sctp-b-ipv6-egress_dst_subnet_prefix': '0.0.0.0', + 'sctp-b-ipv6-ingress_ethertype': 'IPv4', + 'vlc1_Internal2_mac': '00:80:37:0E:01:12', + 'sctp-a-ingress-src_subnet_prefix': '0.0.0.0', + 'sctp-a-ipv6-ingress_action': 'pass', + 'Internal1_rpf': 'disable', + 'sctp-b-ingress_ethertype': 'IPv4', + 'sctp-b-egress_rule_application': 'any', + 'sctp-b-ingress-src_end_port': '65535.0', + 'sctp-a-ipv6-ingress_rule_protocol': 'any', + 'sctp-a-ingress-src_start_port': '0.0', + 'sctp-b-egress-dst_end_port': '65535.0' + }, + 'type': 'VF', + 'modelCustomizationName': 'VF_vMee 0', + 'vfModules': { + 'vf_vmee0..VfVmee..vmme_vlc..module-1': { + 'uuid': '522159d5-d6e0-4c2a-aa44-5a542a12a830', + 'invariantUuid': '98a7c88b-b577-476a-90e4-e25a5871e02b', + 'customizationUuid': '55b1be94-671a-403e-a26c-667e9c47d091', + 'description': null, + 'name': 'VfVmee..vmme_vlc..module-1', + 'version': '2', + 'modelCustomizationName': 'VfVmee..vmme_vlc..module-1', + 'properties': {'minCountInstances': 0, 'maxCountInstances': null, 'initialCount': 0}, + 'commands': {}, + 'volumeGroupAllowed': false + }, + 'vf_vmee0..VfVmee..vmme_gpb..module-2': { + 'uuid': '41708296-e443-4c71-953f-d9a010f059e1', + 'invariantUuid': '1cca90b8-3490-495e-87da-3f3e4c57d5b9', + 'customizationUuid': '6add59e0-7fe1-4bc4-af48-f8812422ae7c', + 'description': null, + 'name': 'VfVmee..vmme_gpb..module-2', + 'version': '2', + 'modelCustomizationName': 'VfVmee..vmme_gpb..module-2', + 'properties': {'minCountInstances': 0, 'maxCountInstances': null, 'initialCount': 0}, + 'commands': {}, + 'volumeGroupAllowed': false + }, + 'vf_vmee0..VfVmee..base_vmme..module-0': { + 'uuid': 'a27f5cfc-7f12-4f99-af08-0af9c3885c87', + 'invariantUuid': 'a6f9e51a-2b35-416a-ae15-15e58d61f36d', + 'customizationUuid': 'f8c040f1-7e51-4a11-aca8-acf256cfd861', + 'description': null, + 'name': 'VfVmee..base_vmme..module-0', + 'version': '2', + 'modelCustomizationName': 'VfVmee..base_vmme..module-0', + 'properties': {'minCountInstances': 1, 'maxCountInstances': 1, 'initialCount': 1}, + 'commands': {}, + 'volumeGroupAllowed': true + } + }, + 'volumeGroups': { + 'vf_vmee0..VfVmee..base_vmme..module-0': { + 'uuid': 'a27f5cfc-7f12-4f99-af08-0af9c3885c87', + 'invariantUuid': 'a6f9e51a-2b35-416a-ae15-15e58d61f36d', + 'customizationUuid': 'f8c040f1-7e51-4a11-aca8-acf256cfd861', + 'description': null, + 'name': 'VfVmee..base_vmme..module-0', + 'version': '2', + 'modelCustomizationName': 'VfVmee..base_vmme..module-0', + 'properties': {'minCountInstances': 1, 'maxCountInstances': 1, 'initialCount': 1} + } + } + } + }, + 'networks': { + 'ExtVL 0': { + 'uuid': 'ddc3f20c-08b5-40fd-af72-c6d14636b986', + 'invariantUuid': '379f816b-a7aa-422f-be30-17114ff50b7c', + 'description': 'ECOMP generic virtual link (network) base type for all other service-level and global networks', + 'name': 'ExtVL', + 'version': '37.0', + 'customizationUuid': '94fdd893-4a36-4d70-b16a-ec29c54c184f', + 'inputs': {}, + 'commands': {}, + 'properties': { + 'network_assignments': '{is_external_network=false, ipv4_subnet_default_assignment={min_subnets_count=1}, ecomp_generated_network_assignment=false, ipv6_subnet_default_assignment={min_subnets_count=1}}', + 'exVL_naming': '{ecomp_generated_naming=true}', + 'network_flows': '{is_network_policy=false, is_bound_to_vpn=false}', + 'network_homing': '{ecomp_selected_instance_node_target=false}' + }, + 'type': 'VL', + 'modelCustomizationName': 'ExtVL 0' + } + }, + 'configurations': { + 'Port Mirroring Configuration By Policy 0': { + 'uuid': 'b4398538-e89d-4f13-b33d-ca323434ba50', + 'invariantUuid': '6ef0ca40-f366-4897-951f-abd65d25f6f7', + 'description': 'A port mirroring configuration by policy object', + 'name': 'Port Mirroring Configuration By Policy', + 'version': '27.0', + 'customizationUuid': '3c3b7b8d-8669-4b3b-8664-61970041fad2', + 'inputs': {}, + 'commands': {}, + 'properties': {}, + 'type': 'Configuration', + 'modelCustomizationName': 'Port Mirroring Configuration By Policy 0', + 'sourceNodes': [], + 'collectorNodes': null, + 'configurationByPolicy': false + } + }, + 'serviceProxies': {}, + 'vfModules': { + 'vf_vmee0..VfVmee..vmme_vlc..module-1': { + 'uuid': '522159d5-d6e0-4c2a-aa44-5a542a12a830', + 'invariantUuid': '98a7c88b-b577-476a-90e4-e25a5871e02b', + 'customizationUuid': '55b1be94-671a-403e-a26c-667e9c47d091', + 'description': null, + 'name': 'VfVmee..vmme_vlc..module-1', + 'version': '2', + 'modelCustomizationName': 'VfVmee..vmme_vlc..module-1', + 'properties': {'minCountInstances': 0, 'maxCountInstances': null, 'initialCount': 0}, + 'commands': {}, + 'volumeGroupAllowed': false + }, + 'vf_vmee0..VfVmee..vmme_gpb..module-2': { + 'uuid': '41708296-e443-4c71-953f-d9a010f059e1', + 'invariantUuid': '1cca90b8-3490-495e-87da-3f3e4c57d5b9', + 'customizationUuid': '6add59e0-7fe1-4bc4-af48-f8812422ae7c', + 'description': null, + 'name': 'VfVmee..vmme_gpb..module-2', + 'version': '2', + 'modelCustomizationName': 'VfVmee..vmme_gpb..module-2', + 'properties': {'minCountInstances': 0, 'maxCountInstances': null, 'initialCount': 0}, + 'commands': {}, + 'volumeGroupAllowed': false + }, + 'vf_vmee0..VfVmee..base_vmme..module-0': { + 'uuid': 'a27f5cfc-7f12-4f99-af08-0af9c3885c87', + 'invariantUuid': 'a6f9e51a-2b35-416a-ae15-15e58d61f36d', + 'customizationUuid': 'f8c040f1-7e51-4a11-aca8-acf256cfd861', + 'description': null, + 'name': 'VfVmee..base_vmme..module-0', + 'version': '2', + 'modelCustomizationName': 'VfVmee..base_vmme..module-0', + 'properties': {'minCountInstances': 1, 'maxCountInstances': 1, 'initialCount': 1}, + 'commands': {}, + 'volumeGroupAllowed': true + } + }, + 'volumeGroups': { + 'vf_vmee0..VfVmee..base_vmme..module-0': { + 'uuid': 'a27f5cfc-7f12-4f99-af08-0af9c3885c87', + 'invariantUuid': 'a6f9e51a-2b35-416a-ae15-15e58d61f36d', + 'customizationUuid': 'f8c040f1-7e51-4a11-aca8-acf256cfd861', + 'description': null, + 'name': 'VfVmee..base_vmme..module-0', + 'version': '2', + 'modelCustomizationName': 'VfVmee..base_vmme..module-0', + 'properties': {'minCountInstances': 1, 'maxCountInstances': 1, 'initialCount': 1} + } + }, + 'pnfs': {} + } + } + )); + } + +}); diff --git a/vid-webpack-master/src/app/drawingBoard/available-models-tree/available-models-tree.service.ts b/vid-webpack-master/src/app/drawingBoard/available-models-tree/available-models-tree.service.ts new file mode 100644 index 00000000..57dc4b40 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/available-models-tree/available-models-tree.service.ts @@ -0,0 +1,36 @@ +import {Injectable} from '@angular/core'; +import * as _ from "lodash"; +import {ServicePlanningService} from "../../services/service-planning.service"; + +@Injectable() +export class AvailableModelsTreeService { + constructor(private _servicePlanningService: ServicePlanningService) { + } + + shouldShowAddIcon(node: any, serviceHierarchy: any, serviceModelId: string, currentNodeCount: number): boolean { + let maxNodes: number = 1; + if (node.data.children !== null && node.data.children.length == 0) { + let vnfModules = serviceHierarchy[serviceModelId].vfModules; + if (vnfModules[node.data.name]) { + maxNodes = vnfModules[node.data.name].properties.maxCountInstances || 1; + } + } + return !node.data.disabled && currentNodeCount < maxNodes + } + + shouldOpenDialog(type: string, dynamicInputs: any, userProvidedNaming: boolean): boolean { + if (userProvidedNaming || this._servicePlanningService.requiredFields[type].length > 0) { + return true; + } + + if (dynamicInputs) { + for(let input of dynamicInputs) { + if (input.isRequired && _.isEmpty(input.value)) { + return true; + } + } + } + return false; + } + +} diff --git a/vid-webpack-master/src/app/drawingBoard/drawing-board-header/drawing-board-header.component.html b/vid-webpack-master/src/app/drawingBoard/drawing-board-header/drawing-board-header.component.html new file mode 100644 index 00000000..7d0f7f45 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/drawing-board-header/drawing-board-header.component.html @@ -0,0 +1,33 @@ +<div class="drawing-board-header"> + <div class="left-header"> + <span [attr.data-tests-id]="'backBtn'" class="icon-back" (click)="closePage()"></span> + <span [attr.data-tests-id]="'serviceInstance'" class="service-instance-label">Service instance:</span> + <span [attr.data-tests-id]="'serviceName'" class="service-instance-name">{{serviceName}}</span> + <span class="quantity-container" style=" padding: 10px;font-size: 13px;" tooltip="Number of services to instantiate including all their objects as defined below"> + <span [attr.data-tests-id]="'quantityLabel'" class="quantity-label" >Scale Times:</span> + <span [attr.data-tests-id]="'servicesQuantity'" class="quantity" style="font-family: OpenSans-Semibold;font-size: 14px;"> {{numServicesToDeploy}} </span> + </span> + <span class="service-instance-label">status:</span> + <span [attr.data-tests-id]="'serviceStatus'" class="status">{{status}}</span> + </div> + <div class="right-header"> + <span class="menu-container"> + <span [attr.data-tests-id]="'openMenuBtn'" class="icon-browse" (click)="onContextMenu($event)"></span> + <context-menu> + <ng-template contextMenuItem (execute)="editService()"> + <div [attr.data-tests-id]="'context-menu-header-edit-item'"> + <span class="icon-edit"></span> + Edit + </div> + </ng-template> + <ng-template contextMenuItem (execute)="closePage()"> + <div [attr.data-tests-id]="'context-menu-header-delete-item'"> + <span class="icon-trash"></span> + Delete + </div> + </ng-template> + </context-menu> + </span> + <button [disabled]="false" [attr.data-tests-id]="'deployBtn'" (click)="deployMacroservice()" class="deploy-btn">DEPLOY</button> + </div> +</div> diff --git a/vid-webpack-master/src/app/drawingBoard/drawing-board-header/drawing-board-header.component.scss b/vid-webpack-master/src/app/drawingBoard/drawing-board-header/drawing-board-header.component.scss new file mode 100644 index 00000000..29b7711b --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/drawing-board-header/drawing-board-header.component.scss @@ -0,0 +1,95 @@ +.drawing-board-header { + height: 56px; + margin-bottom: 4px; + position: relative; + font-family: OpenSans-Regular; + display: flex; + justify-content: space-between; + font-size: 14px; + box-shadow: 2px 2px 6px #D2D2D2; + color: #191919; + [class^="icon-"] { + height: 56px; + width: 56px; + display: flex; + align-items: center; + text-align: center; + color: #5A5A5A; + cursor: pointer; + &:before { + font-size: 18px; + width: 100%; + } + &:hover:before { + color: #009FDB; + } + } + .left-header { + display: flex; + align-items: center; + .icon-back { + border-right: 1px solid #EAEAEA; + &:before { + content: "\e906"; + font-size: 24px; + } + } + .service-instance-label { + padding: 0 5px; + font-family: OpenSans-Regular; + font-size: 13px; + color: #191919; + } + .service-instance-name { + padding-right: 20px; + color: #191919; + font-family: OpenSans-Semibold; + background-color: white; + font-size: 16px + } + .status { + font-family: OpenSans-Semibold; + line-height: 14px; + font-size: 14px; + } + } + .right-header { + display: flex; + align-items: center; + .quantity-container { + .quantity-label { + padding-left: 10px; + font-family: OpenSans-Semibold; + font-size: 12px; + } + .quantity { + padding: 5px 10px 5px 0; + font-family: OpenSans-Semibold; + font-size: 18px; + } + } + [class^="icon-"] { + border-left: 1px solid #EAEAEA; + } + .menu-container { + height: 100%; + display: flex; + background: none; + border: none; + padding: 0; + outline: none; + } + .icon-browse:before { + content: '\e924'; + display: inline-block; + font-size: 24px; + } + .deploy-btn { + color: #FFFFFF ; + background: #009fdb; + width: 128px; + height: 100%; + border: none; + } + } +} diff --git a/vid-webpack-master/src/app/drawingBoard/drawing-board-header/drawing-board-header.component.ts b/vid-webpack-master/src/app/drawingBoard/drawing-board-header/drawing-board-header.component.ts new file mode 100644 index 00000000..38284e21 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/drawing-board-header/drawing-board-header.component.ts @@ -0,0 +1,119 @@ +import {Component, ViewChild} from '@angular/core'; +import {ContextMenuComponent, ContextMenuService} from 'ngx-contextmenu'; +import {DialogService} from 'ng2-bootstrap-modal'; +import {ServicePopupComponent} from '../../components/service-popup/service-popup.component'; +import {MsoService} from '../../services/msoService/mso.service' +import * as _ from 'lodash'; +import {ActivatedRoute} from '@angular/router'; +import {ServiceInstance} from "../../shared/models/serviceInstance"; +import {OwningEntity} from "../../shared/models/owningEntity"; +import {MessageBoxData, ModalSize, ModalType} from "../../shared/components/messageBox/messageBox.data"; +import {MessageBoxService} from "../../shared/components/messageBox/messageBox.service"; +import {NgRedux} from "@angular-redux/store"; +import {AppState} from "../../store/reducers"; +import {IframeService} from "../../shared/utils/iframe.service"; + +@Component({ + selector: 'drawing-board-header', + providers: [MsoService], + templateUrl: './drawing-board-header.component.html', + styleUrls: ['./drawing-board-header.component.scss'] +}) + +export class DrawingBoardHeader { + serviceName: string; + numServicesToDeploy: number; + status: string = 'Designing a new service'; + serviceModelId: string; + parentElementClassName = 'content'; + + constructor(private _contextMenuService: ContextMenuService, private dialogService: DialogService, + private _iframeService : IframeService, + private route: ActivatedRoute, private msoService: MsoService, + private store: NgRedux<AppState>) { + this.route + .queryParams + .subscribe(params => { + this.serviceModelId = params['serviceModelId']; + if (_.has(this.store.getState().service.serviceHierarchy, this.serviceModelId)) { + this.setValuesFromStore(); + this.store.subscribe(() => { + this.setValuesFromStore(); + }); + } + }); + } + + + @ViewChild(ContextMenuComponent) public contextMenu: ContextMenuComponent; + + public onContextMenu($event: MouseEvent, item: any): void { + this._contextMenuService.show.next({ + contextMenu: this.contextMenu, + event: $event, + item: item, + }); + $event.preventDefault(); + $event.stopPropagation(); + } + + private setValuesFromStore() { + const serviceInstance = this.store.getState().service.serviceInstance[this.serviceModelId]; + this.numServicesToDeploy = serviceInstance.bulkSize; + this.serviceName = serviceInstance.instanceName || '<Automatically Assigned>'; + + } + + public editService(): void { + this._iframeService.addClassOpenModal(this.parentElementClassName); + this.dialogService.addDialog(ServicePopupComponent, {}) + + } + + + extractOwningEntityNameAccordingtoId(id:String): string { + let owningEntityName; + _.forEach(this.store.getState().service.categoryParameters.owningEntityList,function(owningEntity: OwningEntity) { + if (owningEntity.id === id) { + owningEntityName = owningEntity.name; + + }}) + + return owningEntityName; + } + + extractServiceFields(): any { + let instanceFields : ServiceInstance; + instanceFields = this.store.getState().service.serviceInstance[Object.keys(this.store.getState().service.serviceInstance)[0]]; + instanceFields.subscriberName = this.store.getState().service.subscribers.find(sub => sub.id === instanceFields.globalSubscriberId).name; + instanceFields.owningEntityName = this.extractOwningEntityNameAccordingtoId(instanceFields.owningEntityId); + return instanceFields; + } + + public deployMacroservice(): void { + var instanceFields = this.extractServiceFields(); + instanceFields.rollbackOnFailure = instanceFields.rollbackOnFailure === 'true'; + this.msoService.submitMsoTask(instanceFields).subscribe((result) => { + window.parent.postMessage("navigateToInstantiationStatus", '*'); + }) + } + + closePage() { + let messageBoxData : MessageBoxData = new MessageBoxData( + "Delete Instantiation", // modal title + "You are about to stop the instantiation process of this service. \nAll data will be lost. Are you sure you want to stop?", + + ModalType.alert, + ModalSize.medium, + [ + {text:"Stop Instantiation", size:"large", callback: this.navigate.bind(this), closeModal:true}, + {text:"Cancel", size:"medium", closeModal:true} + ]); + + MessageBoxService.openModal.next(messageBoxData); + } + + navigate(){ + window.parent.postMessage("navigateTo", "*"); + } +} diff --git a/vid-webpack-master/src/app/drawingBoard/drawing-board-header/tmp_instansiate_request.ts b/vid-webpack-master/src/app/drawingBoard/drawing-board-header/tmp_instansiate_request.ts new file mode 100644 index 00000000..7accc3a9 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/drawing-board-header/tmp_instansiate_request.ts @@ -0,0 +1,52 @@ +export default + { + "modelInfo": { + "modelType": "service", + "modelInvariantId": "5d48acb5-097d-4982-aeb2-f4a3bd87d31b", + "modelVersionId": "3c40d244-808e-42ca-b09a-256d83d19d0a", + "modelName": "MOW AVPN vMX BV vPE 1 Service", + "modelVersion": "10.0" + }, + "owningEntityId": "038d99af-0427-42c2-9d15-971b99b9b489", + "owningEntityName": "PACKET CORE", + "projectName": "{some project name}", + "globalSubscriberId": "{some subscriber id}", + "productFamilyId": "a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb", + "instanceName": "vPE_Service", + "subscriptionServiceType": "VMX", + "lcpCloudRegionId": "mdt1", + "tenantId": "88a6ca3ee0394ade9403f075db23167e", + "vnfs": [ + { + "modelInfo": { + "modelName": "2016-73_MOW-AVPN-vPE-BV-L", + "modelVersionId": "7f40c192-f63c-463e-ba94-286933b895f8", + "modelCustomizationName": "2016-73_MOW-AVPN-vPE-BV-L 0", + "modelCustomizationId": "ab153b6e-c364-44c0-bef6-1f2982117f04" + }, + "lcpCloudRegionId": "mdt1", + "tenantId": "88a6ca3ee0394ade9403f075db23167e", + "platformName": "test", + "productFamilyId": "a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb", + "instanceName": "vmxnjr001", + "instanceParams": [], + "vfModules": [ + { + "modelInfo": { + "modelType": "vfModule", + "modelName": "201673MowAvpnVpeBvL..AVPN_base_vPE_BV..module-0", + "modelVersionId": "4c75f813-fa91-45a4-89d0-790ff5f1ae79", + "modelCustomizationId": "a25e8e8c-58b8-4eec-810c-97dcc1f5cb7f" + }, + "instanceName": "vmxnjr001_AVPN_base_vPE_BV_base_001", + "instanceParams": [ + { + "vmx_int_net_len": "24" + } + ] + } + ] + } + ] + } + diff --git a/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.component.ts b/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.component.ts new file mode 100644 index 00000000..6b717a93 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.component.ts @@ -0,0 +1,133 @@ +import {AfterViewInit, Component, EventEmitter, OnInit, Output, ViewChild} from '@angular/core'; +import { ContextMenuService } from 'ngx-contextmenu'; +import { Constants } from '../../shared/utils/constants'; +import {ServicePlanningService} from "../../services/service-planning.service"; +import {ITreeNode} from "angular-tree-component/dist/defs/api"; +import {ITreeOptions, TreeComponent} from "angular-tree-component"; +import {VnfPopupComponent} from "../../components/vnf-popup/vnf-popup.components"; +import {DialogService} from "ng2-bootstrap-modal"; +import {ActivatedRoute} from "@angular/router"; +import {NgRedux} from "@angular-redux/store"; +import {AppState} from "../../store/reducers"; +import { MessageBoxData, ModalSize, ModalType } from '../../shared/components/messageBox/messageBox.data'; +import { MessageBoxService } from '../../shared/components/messageBox/messageBox.service'; +import { deleteVnfInstance, deleteVfModuleInstance } from '../../service.actions'; +import { isNullOrUndefined } from 'util'; +import {IframeService} from "../../shared/utils/iframe.service"; + + +@Component({ + selector: 'drawing-board-tree', + templateUrl: './drawing-board-tree.html', + styleUrls : ['./drawing-board-tree.scss'] +}) + + +export class DrawingBoardTreeComponent implements OnInit, AfterViewInit { + constructor(private _contextMenuService: ContextMenuService, + private _servicePlanningService: ServicePlanningService, + private _iframeService : IframeService, + private dialogService: DialogService, + private store: NgRedux<AppState>, + private route: ActivatedRoute) { + this.route + .queryParams + .subscribe(params => { + this.serviceModelId = params['serviceModelId']; + }); + } + + @Output() + highlightNode : EventEmitter<number> = new EventEmitter<number>(); + + @ViewChild('tree') tree: TreeComponent; + missingDataTooltip: string = Constants.Error.MISSING_VNF_DETAILS; + currentNode: ITreeNode = null; // + nodes = []; + serviceModelId: string; + options: ITreeOptions = { + nodeHeight: 45, + dropSlotHeight: 1 + }; + parentElementClassName = 'content'; + + ngOnInit(): void { + this.store.subscribe(() => {this.updateTree()}); + this.updateTree() + } + + updateTree() { + const serviceInstance = this.store.getState().service.serviceInstance[this.serviceModelId]; + this.nodes = this._servicePlanningService.convertServiceInstanceToTreeData(serviceInstance, this.serviceModelId); + } + + ngAfterViewInit():void { + // Expand drawing tree on init. + this.tree.treeModel.expandAll(); + } + + public onContextMenu($event: MouseEvent, node: ITreeNode): void { + this.currentNode = node; + node.focus(); + node.setActiveAndVisible(false); + this.selectNode(node); + this._contextMenuService.show.next({ + event: <any>$event, + item: node, + }); + $event.preventDefault(); + $event.stopPropagation(); + } + + public editItem(node: ITreeNode): void { + node = this.currentNode; + this._iframeService.addClassOpenModal(this.parentElementClassName); + this.dialogService.addDialog(VnfPopupComponent, { + serviceModelId: this.serviceModelId, + modelName: node.data.modelName, + modelType: node.data.type, + parentModelName: node.parent.data.modelName, + isNewVfModule : false + }) + } + + public deleteItem(node: ITreeNode): void { + if(this.currentNode.data.type === 'VF'){ + if(!isNullOrUndefined(this.currentNode.data.children) && this.currentNode.data.children.length === 0){ + this.store.dispatch(deleteVnfInstance(this.currentNode.data.modelName, this.serviceModelId)); + }else { + let messageBoxData : MessageBoxData = new MessageBoxData( + "Remove VNF", // modal title + "You are about to remove this VNF and all its children from this service. Are you sure you want to remove it?", + + ModalType.alert, + ModalSize.medium, + [ + {text:"Remove VNF", size:"large", callback: this.removeVnf.bind(this), closeModal:true}, + {text:"Don’t Remove", size:"medium", closeModal:true} + ]); + + MessageBoxService.openModal.next(messageBoxData); + } + }else { + this.store.dispatch(deleteVfModuleInstance(this.currentNode.data.modelName, this.serviceModelId, node.parent.data.modelName)); + } + } + + removeVnf() { + this.store.dispatch(deleteVnfInstance(this.currentNode.data.modelName, this.serviceModelId)); + } + + public selectNode(node: ITreeNode): void { + node.expand(); + this.highlightNode.emit(node.data.modelId); + } + + isDataMissing(node: ITreeNode) { + //todo: currently not showing the alert icon. will be implemented in upcoming story. + return false; + } + +} + + diff --git a/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.html b/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.html new file mode 100644 index 00000000..c4061db9 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.html @@ -0,0 +1,42 @@ +<div class="drawing-board-tree"> + <div *ngIf="nodes?.length == 0" style="text-align: center; margin-top: 50px;"> + <no-content-message-and-icon class="span-over" + data-title="Please add objects (VNFs, network, modules etc.) from the left tree to design the service instance" + subtitle="Once done, click Deploy to start instantiation" + iconPath="./assets/img/UPLOAD.svg" + iconClass="upload-icon-service-planing"></no-content-message-and-icon> + </div> + <tree-root [attr.data-tests-id]="'drawing-board-tree'" #tree [nodes]="nodes" [options]="options" id="drawing-board-tree"> + <ng-template #treeNodeTemplate let-node let-index="index"> + <div [attr.data-tests-id]="'node-'+node.data.modelId +'-' +node.data.modelName" (click)="selectNode(node)"> + <div class="model-info"> + <span> + <span class="property-name">{{node.data.type}}{{node.data.name ? ': ': ''}}<span class="auto-name">{{node.data.name? node.data.name: ''}}</span></span> + </span> + </div> + <div class="model-actions"> + <span class="icon-browse" [attr.data-tests-id]="'node-'+node.data.modelId +'-' +node.data.modelName+'-menu-btn'" (click)="onContextMenu($event, node)" > + <context-menu> + <ng-template contextMenuItem (execute)="editItem(node)"> + <div [attr.data-tests-id]="'context-menu-item'"> + <span class="icon-edit"></span> + Edit + </div> + </ng-template> + <ng-template contextMenuItem (execute)="deleteItem(node)"> + <div> + <span class="icon-trash"></span> + Remove + </div> + </ng-template> + </context-menu> + </span> + <span *ngIf="isDataMissing(node)" class="icon-alert" tooltip="{{ missingDataTooltip }}" tooltipPlacement="left" [attr.data-tests-id]="'node-'+node.data.modelId +'-' +node.data.modelName+'-alert-icon'"></span> + </div> + </div> + + </ng-template> + </tree-root> +</div> + + diff --git a/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.scss b/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.scss new file mode 100644 index 00000000..fed9ead1 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/drawing-board-tree/drawing-board-tree.scss @@ -0,0 +1,274 @@ +@mixin highlight($background-color, $color) { + background: none; + padding: 0; + background-color: $background-color; + border: $color 1px solid; + color: $color; +} +@mixin highlight-toggle-children { + tree-node-expander { + .toggle-children-wrapper { + span.toggle-children { + @include highlight(white, #009FDB); + border-right: none; + } + } + } +} + +@mixin highlight-tree-node-content { + tree-node-content { + > div { + .model-actions { + .icon-browse:before { + display: inline-block; + } + } + } + } +} + +drawing-board-tree { + + .toggle-children-wrapper.toggle-children-wrapper-expanded { + .toggle-children:before { + color: #009FDB; + } + } + + overflow: auto; + flex: 1; + color: #5A5A5A; + line-height: 14px; + .drawing-board-tree { + width: 100%; + } + tree-viewport { + padding: 50px 3.5% 1% 6%; + tree-node { + tree-node-drop-slot { + .node-drop-slot { + display: none; + } + } + & .tree-node-focused, + & .tree-node-active { + & > tree-node-wrapper { + .node-wrapper { + @include highlight-toggle-children; + .node-content-wrapper-focused, + .node-content-wrapper-active + { + @include highlight(#E6F6FB, #009FDB); + .property-name { + color: #009FDB; + } + .auto-name { + font-family: OpenSans-Regular !important; + } + .icon-browse:before { + color: #5A5A5A; + } + @include highlight-tree-node-content; + } + } + } + } + & .tree-node-expanded { + > tree-node-wrapper .node-wrapper { + box-shadow: 0 0px 2px rgba(90,90,90,0.24); + } + } + + .tree-node-active .tree-children { + border: 1px solid #009FDB; + padding: 20px; + } + + + + .tree-node.tree-node-active.tree-node-expanded { + border: 1px solid #009FDB; + } + + .tree-node-leaf .node-wrapper{ + margin-left: -45px; + } + + tree-node-wrapper { + .node-wrapper { + height: 45px; + &:hover { + @include highlight-toggle-children; + .node-content-wrapper { + @include highlight(#E6F6FB, #009FDB); + .property-name { + color: #009FDB; + } + .icon-browse:before { + color: #5A5A5A; + } + @include highlight-tree-node-content; + } + } + tree-node-expander { + font-family: 'icomoon' !important; + height: 100%; + .toggle-children-wrapper { + padding: 0; + display: block; + height: 100%; + span.toggle-children { + display: flex; + width: 45px; + padding: 0; + top: 0; + height: inherit; + background-image: none; + background-color: white; + border: 1px solid #D2D2D2; + border-right: none; + &:before { + content: "\e900"; + font-size: 20px; + font-weight: 600; + text-align: center; + display: inline-block; + flex: auto; + align-self: center; + } + } + } + .toggle-children-wrapper-expanded { + span.toggle-children { + transform: none; + &:before { + content: "\e930"; + } + } + } + .toggle-children-placeholder { + width:45px; + } + } + .node-content-wrapper { + padding: 0; + background: none; + box-shadow: none; + border-radius: 0; + border: 1px solid #D2D2D2; + height: 100%; + flex: 1; + tree-node-content { + > div { + height: 100%; + display: flex; + align-items: center; + justify-content: space-between; + .model-info { + flex: 1; + display: flex; + justify-content: space-between; + align-items: center; + padding-left: 8px; + > span { + flex: 1; + padding: 0 8px; + span:nth-child(2) { + display: block; + } + } + } + .model-actions { + display: flex; + align-items: center; + padding-right: 12px; + .icon-browse { + padding: 0; + width: 30px; + &:before { + content: "\e924"; + font-size: 24px; + display: none; + } + &:hover:before { + color: #009FDB; + } + &:focus:before, + &:active:before { + color: #009FDB; + } + } + + .icon-alert { + padding-left: 10px; + &:before { + content: "\e904"; + font-size: 16px; + color: #ffb81c; + } + } + } + } + } + .property-name { + font-family: OpenSans-Semibold; + font-size: 12px; + line-height: 12px; + color: #191919; + text-transform: capitalize; + } + } + + + } + } + tree-node-children { + .tree-children { + padding: 20px; + .model-info span:first-child { + flex: 1.1 !important; + } + } + } + } + } +} +.cdk-overlay-pane.ngx-contextmenu { + ul.dropdown-menu { + width: 200px; + box-shadow: none; + padding: 0; + padding-top: 10px; + margin: 0; + border: 1px solid #D2D2D2; + border-top: 3px solid #009FDB; + box-shadow: 0 0px 2px rgba(90,90,90,0.24); + border-radius: 2px; + li { + height: 40px; + a { + font-family: OpenSans-Regular; + display: flex; + align-items: center; + height: 100%; + padding: 12px; + color: #5A5A5A; + &:hover { + background: #E6F6FB; + color: #009FDB; + } + span { + padding-right: 12px; + &.icon-edit:before { + content: '\e917'; + } + &.icon-trash:before { + content: '\e937'; + } + } + } + } + } +} + diff --git a/vid-webpack-master/src/app/drawingBoard/drawingBoard.module.ts b/vid-webpack-master/src/app/drawingBoard/drawingBoard.module.ts new file mode 100644 index 00000000..41efbe0e --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/drawingBoard.module.ts @@ -0,0 +1,41 @@ +import { AvailableModelsTreeService } from './available-models-tree/available-models-tree.service'; +import { NgModule } from '@angular/core'; +import { HighlightPipe } from '../shared/pipes/highlight-filter.pipe'; +import { TreeModule } from 'angular-tree-component'; +import { BrowserModule } from '@angular/platform-browser'; +import { TooltipModule } from 'ngx-tooltip'; +import { AvailableModelsTreeComponent } from './available-models-tree/available-models-tree.component'; +import { ServicePlanningService } from '../services/service-planning.service'; +import { AaiService } from '../services/aaiService/aai.service'; +import { DrawingBoardTreeComponent } from './drawing-board-tree/drawing-board-tree.component'; +import { SharedModule } from '../shared/shared.module'; +import { ContextMenuModule, ContextMenuService } from 'ngx-contextmenu'; +import { CommonModule } from '@angular/common'; +import { DrawingBoardHeader } from './drawing-board-header/drawing-board-header.component'; +import { ServicePlanningComponent, ServicePlanningEmptyComponent } from './service-planning/service-planning.component'; + +@NgModule({ + imports: [ + TreeModule, + BrowserModule, + ContextMenuModule, + TooltipModule, + CommonModule, + SharedModule.forRoot()], + providers: [ + ServicePlanningService, + AaiService, + AvailableModelsTreeService , + ContextMenuService, + ServicePlanningService], + declarations: [ + AvailableModelsTreeComponent, + HighlightPipe, + DrawingBoardTreeComponent, + DrawingBoardHeader, + ServicePlanningComponent, + ServicePlanningEmptyComponent], + exports: [ AvailableModelsTreeComponent, DrawingBoardTreeComponent, DrawingBoardHeader] +}) + +export class DrawingBoardModule { } diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/service-planning.component.html b/vid-webpack-master/src/app/drawingBoard/service-planning/service-planning.component.html new file mode 100644 index 00000000..5b2f22d5 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/service-planning.component.html @@ -0,0 +1,13 @@ +<div class="service-planning-header"> + <drawing-board-header></drawing-board-header> +</div> +<div class="service-planning-content row"> + <available-models-tree class="left-side col-md-6" (highlightInstances)="highlightInstancesBySelectingNode($event)"></available-models-tree> + <!--<no-content-message-and-icon *ngIf="!isShowTree()" class="span-over"--> + <!--data-title="Please add objects (VNFs, network, modules etc.) from the left tree to design the service instance"--> + <!--subtitle="Once done, click Deploy to start instantiation"--> + <!--iconPath="./img/UPLOAD.svg"--> + <!--iconClass="upload-icon-service-planing"></no-content-message-and-icon>--> + <drawing-board-tree *ngIf="isShowTree()" class="span-over col-md-6" (highlightNode)="highlightNodeBySelectingInstance($event)"></drawing-board-tree> +</div> + diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/service-planning.component.scss b/vid-webpack-master/src/app/drawingBoard/service-planning/service-planning.component.scss new file mode 100644 index 00000000..69546a6c --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/service-planning.component.scss @@ -0,0 +1,16 @@ + +.service-planning-content { + display: flex; + flex: 1; +} + +.span-over { + display: flex; + flex: 1; +} + +//css for the icon. :host ::ng-deep are needed for applying css to child component +:host ::ng-deep .upload-icon-service-planing { + height: 117px; + margin-top: 32px; +} diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/service-planning.component.ts b/vid-webpack-master/src/app/drawingBoard/service-planning/service-planning.component.ts new file mode 100644 index 00000000..1ce0e810 --- /dev/null +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/service-planning.component.ts @@ -0,0 +1,77 @@ +import {Component, ViewChild} from '@angular/core'; +import {DrawingBoardTreeComponent} from "../drawing-board-tree/drawing-board-tree.component"; +import {AvailableModelsTreeComponent} from "../available-models-tree/available-models-tree.component"; +import {ITreeNode} from "angular-tree-component/dist/defs/api"; +import {TreeComponent} from 'angular-tree-component'; + +@Component({ + selector: 'service-planning', + templateUrl: './service-planning.component.html', + styleUrls: ['./service-planning.component.scss'] +}) + +export class ServicePlanningComponent { + + @ViewChild(DrawingBoardTreeComponent) drawingModelTree; + @ViewChild(AvailableModelsTreeComponent) availableModelTree; + + isShowTree(): boolean { + return true; + } + + public highlightNodeBySelectingInstance(modelId: number): void { + this.availableModelTree.tree.treeModel.getNodeBy((node: ITreeNode) => node.data.id === modelId) + .setActiveAndVisible().expand(); + } + + public highlightInstancesBySelectingNode(id: number): void { + if(this.isShowTree()) { + let _this = this; + let matchInstances = _this.searchTree(id); + if (!matchInstances.length) + _this.clearSelectionInTree(_this.drawingModelTree.tree); + matchInstances.forEach(function (instance, index) { + let multi : boolean = !!index; + _this.drawingModelTree.tree.treeModel.getNodeById(instance.id) + .setActiveAndVisible(multi).expand(); + }); + + } + } + + clearSelectionInTree(tree: TreeComponent): void { + let activateNode = tree.treeModel.getActiveNode(); + activateNode ? activateNode.toggleActivated().blur() : null; + } + + searchTree(modelId: number) { + let _this = this; + let results = []; + let nodes = _this.drawingModelTree.nodes; + nodes.forEach(function (node) { + _this.searchTreeNode(node, modelId, results); + }); + return results; + } + + searchTreeNode(node, modelId: number, results): void { + if(node.modelId === modelId){ + results.push(node); + } + if (node.children != null){ + for(let i = 0; i < node.children.length; i++){ + this.searchTreeNode(node.children[i], modelId, results); + } + } + } + + +} + +export class ServicePlanningEmptyComponent extends ServicePlanningComponent { + isShowTree() : boolean { + return false; + } +} + + diff --git a/vid-webpack-master/src/app/factories/models/requestDetails.model.ts b/vid-webpack-master/src/app/factories/models/requestDetails.model.ts new file mode 100644 index 00000000..4ccc4781 --- /dev/null +++ b/vid-webpack-master/src/app/factories/models/requestDetails.model.ts @@ -0,0 +1,126 @@ +export class RequestInfo { + instanceName: string; + source: string; + suppressRollback: boolean; + requestorId: string; + productFamilyId: string; +} + +export class ModelInfo { + modelType: string; + modelInvariantId: string; + modelVersionId: string; + modelName: string; + modelVersion: string; + modelCustomizationId: string; + modelCustomizationName: string; +} + +export class RequestParameters { + userParams: any[]; + testApi: string; +} + +export class CloudConfiguration { + lcpCloudRegionId: string; + tenantId: string; +} + +export class LineOfBusiness { + lineOfBusinessName: string; +} + +export class Platform { + platformName: string; +} + +export class VfcModel { + uuid: string; + invariantUuid: string; + name: string; + version: string; + vfcInstanceGroupProperties : any; +} + +export class RelatedInstance { + instanceId: string; + modelInfo: ModelInfo; +} + +export class RelatedInstanceList { + relatedInstance: RelatedInstance; +} + +export class RequestDetails { + requestInfo: RequestInfo; + modelInfo: ModelInfo; + requestParameters: RequestParameters; + cloudConfiguration: CloudConfiguration; + lineOfBusiness: LineOfBusiness; + platform: Platform; + relatedInstanceList: RelatedInstanceList[]; +} + +export class RootObject { + requestDetails: RequestDetails; +} +// { +// "requestDetails": { +// "modelInfo": { +// “modelType”: “vnf”, +// “modelInvariantId”: “ff5256d1-5a33-55df-13ab-12abad84e7ff”, +// “modelVersionId”: “fe042c22-ba82-43c6-b2f6-8f1fc4164091”, +// “modelName”: “vSAMP12”, +// "modelVersion": "1.0", +// “modelCustomizationName”: “vSAMP12 1”, +// “modelCustomizationId”: “a7f1d08e-b02d-11e6-80f5-76304dec7eb7” +// }, +// “cloudConfiguration”: { +// “lcpCloudRegionId”: “mdt1”, +// “tenantId”: “88a6ca3ee0394ade9403f075db23167e” +// }, +// "requestInfo": { +// “instanceName”: “MSOTEST103a”, +// “productFamilyId”: “a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb”, +// “source”: “VID”, +// “suppressRollback”: false, +// “requestorId”: “az2016” +// }, +// "platform": { +// "platformName": "{some platformName}" +// }, +// "lineOfBusiness": { +// "lineOfBusinessName": "{some string}" +// }, +// "relatedInstanceList": [ +// { +// “relatedInstance”: { +// “instanceId”: “{serviceInstanceId}”, +// “modelInfo”: { +// “modelType”: “service”, +// “modelInvariantId”: “ff3514e3-5a33-55df-13ab-12abad84e7ff”, +// “modelVersionId”: “fe6985cd-ea33-3346-ac12-ab121484a3fe”, +// “modelName”: “{parent service model name}”, +// "modelVersion": "1.0" +// } +// } +// }, +// { +// “relatedInstance”: { +// “instanceId”: “{instanceGroupId}”, +// “modelInfo”: { +// “modelType”: “networkCollection”, +// “modelInvariantId”: “9ea660dc-155f-44d3-b45c-cc7648b4f31c”, +// “modelVersionId”: “bb07aad1-ce2d-40c1-85cb-5392f76bb1ef”, +// “modelName”: “{network collection model name}”, +// "modelVersion": "1.0" +// } +// } +// } + +// ], +// “requestParameters”: { +// “userParams”: [] +// } +// } +// } diff --git a/vid-webpack-master/src/app/factories/mso.factory.spec.ts b/vid-webpack-master/src/app/factories/mso.factory.spec.ts new file mode 100644 index 00000000..16efd297 --- /dev/null +++ b/vid-webpack-master/src/app/factories/mso.factory.spec.ts @@ -0,0 +1,319 @@ +import {createRequest} from './mso.factory'; + +sessionStorage.setItem("msoRequestParametersTestApiValue","GR_API"); +describe('Vlantagging', () => { + it('should create a correct request', (done: DoneFn) => { + let userInputs_withEcompGeneratedNaming = { + "productFamily": "e433710f-9217-458d-a79d-1c7aff376d89", + "lcpRegion": "AAIAIC25", + "tenantId": "092eb9e8e4b7412e8787dd091bc58e86", + "aicZone": "YYY1", + "platformName": "plat1", + "lineOfBusiness": "ecomp" + }; + let userInputs_withoutEcompGeneratedNaming = { + "instanceName": "New Name", + "productFamily": "e433710f-9217-458d-a79d-1c7aff376d89", + "lcpRegion": "AAIAIC25", + "tenantId": "092eb9e8e4b7412e8787dd091bc58e86", + "aicZone": "YYY1", + "platformName": "plat1", + "lineOfBusiness": "ecomp" + }; + let service = { + "service": { + "uuid": "6bce7302-70bd-4057-b48e-8d5b99e686ca", + "invariantUuid": "9aa04749-c02c-432d-a90c-18caa361c833", + "name": "vDBE_srv", + "version": "1.0", + "toscaModelURL": null, + "category": "Network L4+", + "serviceType": "", + "serviceRole": "", + "description": "vDBE_srv", + "serviceEcompNaming": "true", + "instantiationType": "A-La-Carte", + "inputs": {} + }, + "vnfs": { + "vDBE 0": { + "uuid": "61535073-2e50-4141-9000-f66fea69b433", + "invariantUuid": "fcdf49ce-6f0b-4ca2-b676-a484e650e734", + "description": "vDBE", + "name": "vDBE", + "version": "0.2", + "customizationUuid": "1", + "inputs": {}, + "commands": {}, + "properties": { + "nf_naming": "{ecomp_generated_naming=true}", + "multi_stage_design": "false", + "oam_vfc_instance_group_function": "oambbb", + "availability_zone_max_count": "1", + "oam_network_collection_function": "oamaaa", + "ecomp_generated_naming": "true", + "untr_vfc_instance_group_function": "untrbbb", + "untr_network_collection_function": "untraaa" + }, + "type": "VF", + "modelCustomizationName": "vDBE 0", + "vfModules": { + "vdbe0..Vdbe..main..module-0": { + "uuid": "25a4d009-2f5a-44b4-b02a-62c584c15912", + "invariantUuid": "614afb1a-3e7e-44e9-90ab-424d0070c781", + "customizationUuid": "3443b341-7b0b-498c-a84a-a7ee736cba7e", + "description": null, + "name": "Vdbe..main..module-0", + "version": "1", + "modelCustomizationName": "Vdbe..main..module-0", + "properties": { + "minCountInstances": 1, + "maxCountInstances": 1, + "initialCount": 1, + "vfModuleLabel": "main" + }, + "inputs": {}, + "volumeGroupAllowed": false + } + }, + "volumeGroups": {}, + "vfcInstanceGroups": { + "untr_group": { + "uuid": "5fca04e2-a889-4579-8338-f60f1bf285fa", + "invariantUuid": "fb1e384b-117a-46ae-9ad1-bf2f1ee1e49f", + "name": "untr_group", + "version": "1", + "vfcInstanceGroupProperties": { + "vfcParentPortRole": "untr", + "networkCollectionFunction": "untraaa", + "vfcInstanceGroupFunction": null, + "subinterfaceRole": "untr" + } + }, + "oam_group": { + "uuid": "a0efd5fc-f7be-4502-936a-a6c6392b958f", + "invariantUuid": "9384abf9-1231-4da4-bd8d-89e4d2f8a749", + "name": "oam_group", + "version": "1", + "vfcInstanceGroupProperties": { + "vfcParentPortRole": "untr", + "networkCollectionFunction": "untraaa", + "vfcInstanceGroupFunction": null, + "subinterfaceRole": "untr" + } + } + } + } + }, + "networks": {}, + "collectionResource": {}, + "configurations": {}, + "serviceProxies": {}, + "vfModules": { + "vdbe0..Vdbe..main..module-0": { + "uuid": "25a4d009-2f5a-44b4-b02a-62c584c15912", + "invariantUuid": "614afb1a-3e7e-44e9-90ab-424d0070c781", + "customizationUuid": "3443b341-7b0b-498c-a84a-a7ee736cba7e", + "description": null, + "name": "Vdbe..main..module-0", + "version": "1", + "modelCustomizationName": "Vdbe..main..module-0", + "properties": { + "minCountInstances": 1, + "maxCountInstances": 1, + "initialCount": 1, + "vfModuleLabel": "main" + }, + "inputs": {}, + "volumeGroupAllowed": false + } + }, + "volumeGroups": {}, + "pnfs": {} + }; + let serviceInstanceId: string = "6bce7302-70bd-4057-b48e-8d5b99e686ca"; + let networkInstanceGroups = { + "untr_group": { + "instance-group": { + "instance-group-role": "JZmha7QSS4tJ", + "model-invariant-id": "model-id3", + "model-version-id": "a0efd5fc-f7be-4502-936a-a6c6392b958f", + "id": "AAI-12002-test3-vm230w", + "description": "a9DEa0kpY", + "instance-group-type": "type", + "resource-version": "1520888659539", + "instance-group-name": "wKmBXiO1xm8bK", + "instance-group-function": "testfunction2", + "relationship-list": { + "relationship": [ + { + "relationDataList": [ + { + "relationship-key": "cloud-region.cloud-owner", + "relationship-value": "AAI-12002-vm230w" + }, + { + "relationship-key": "cloud-region.cloud-region-id", + "relationship-value": "AAI-region-vm230w" + } + ], + "relatedToPropertyList": [ + { + "property-key": "cloud-region.owner-defined-type", + "property-value": null + } + ], + "related-to": "cloud-region", + "related-link": "/aai/v13/cloud-infrastructure/cloud-regions/cloud-region/AAI-12002-vm230w/AAI-region-vm230w", + "relationship-label": "org.onap.relationships.inventory.Uses", + "relationship-data": [ + { + "relationship-key": "cloud-region.cloud-owner", + "relationship-value": "AAI-12002-vm230w" + }, + { + "relationship-key": "cloud-region.cloud-region-id", + "relationship-value": "AAI-region-vm230w" + } + ], + "related-to-property": [ + { + "property-key": "cloud-region.owner-defined-type", + "property-value": null + } + ] + } + ] + } + } + }, + "oam_group": { + "instance-group": { + "instance-group-role": "JZmha7QSS4tJ", + "model-invariant-id": "model-id3", + "model-version-id": "a0efd5fc-f7be-4502-936a-a6c6392b958f", + "id": "AAI-12002-test3-vm230w", + "description": "a9DEa0kpY", + "instance-group-type": "type", + "resource-version": "1520888659539", + "instance-group-name": "wKmBXiO1xm8bK", + "instance-group-function": "testfunction2", + "relationship-list": { + "relationship": [ + { + "relationDataList": [ + { + "relationship-key": "cloud-region.cloud-owner", + "relationship-value": "AAI-12002-vm230w" + }, + { + "relationship-key": "cloud-region.cloud-region-id", + "relationship-value": "AAI-region-vm230w" + } + ], + "relatedToPropertyList": [ + { + "property-key": "cloud-region.owner-defined-type", + "property-value": null + } + ], + "related-to": "cloud-region", + "related-link": "/aai/v13/cloud-infrastructure/cloud-regions/cloud-region/AAI-12002-vm230w/AAI-region-vm230w", + "relationship-label": "org.onap.relationships.inventory.Uses", + "relationship-data": [ + { + "relationship-key": "cloud-region.cloud-owner", + "relationship-value": "AAI-12002-vm230w" + }, + { + "relationship-key": "cloud-region.cloud-region-id", + "relationship-value": "AAI-region-vm230w" + } + ], + "related-to-property": [ + { + "property-key": "cloud-region.owner-defined-type", + "property-value": null + } + ] + } + ] + } + } + } + }; + let expectedResult = { + "requestInfo": { + "productFamilyId": "e433710f-9217-458d-a79d-1c7aff376d89", + "source": "VID", + "requestorId": "az2016", + "suppressRollback": false + }, + "lineOfBusiness": Object({ lineOfBusinessName: "ecomp" }), + "cloudConfiguration": { + "lcpCloudRegionId": "AAIAIC25", + "tenantId": "092eb9e8e4b7412e8787dd091bc58e86" + }, + "platform": Object({ platformName: "plat1" }), + "modelInfo": { + modelCustomizationId :'1', + "modelVersionId": "61535073-2e50-4141-9000-f66fea69b433", + "modelCustomizationName": "vDBE 0", + "modelName": "vDBE", + "modelInvariantId": "fcdf49ce-6f0b-4ca2-b676-a484e650e734", + "modelType": "vnf", + "modelVersion": "0.2" + }, + "requestParameters": { + "userParams": [], + "testApi": "GR_API" + }, + "relatedInstanceList": [ + { + "relatedInstance": { + "instanceId": "6bce7302-70bd-4057-b48e-8d5b99e686ca", + "modelInfo": { + "modelVersionId": "6bce7302-70bd-4057-b48e-8d5b99e686ca", + "modelName": "vDBE_srv", + "modelInvariantId": "9aa04749-c02c-432d-a90c-18caa361c833", + "modelType": "service", + "modelVersion": "1.0" + } + } + }, + { + "relatedInstance": { + "instanceId": "AAI-12002-test3-vm230w", + "modelInfo": { + "modelName": "oam_group", + "modelType": "networkCollection", + "modelVersion": "1", + "modelVersionId": "a0efd5fc-f7be-4502-936a-a6c6392b958f", + "modelInvariantId": "9384abf9-1231-4da4-bd8d-89e4d2f8a749" + } + } + }, + { + "relatedInstance": { + "instanceId": "AAI-12002-test3-vm230w", + "modelInfo": { + "modelName": "oam_group", + "modelType": "networkCollection", + "modelVersion": "1", + "modelVersionId": "a0efd5fc-f7be-4502-936a-a6c6392b958f", + "modelInvariantId": "9384abf9-1231-4da4-bd8d-89e4d2f8a749" + } + } + } + ] + }; + + + let actualResult_withEcompGeneratedNaming = <any>createRequest("az2016",userInputs_withEcompGeneratedNaming, service, serviceInstanceId, networkInstanceGroups,'vDBE 0','1'); + expect(actualResult_withEcompGeneratedNaming).toEqual(expectedResult); + expectedResult["requestInfo"]["instanceName"] = "New Name"; + let actualResult_withoutEcompGeneratedNaming = <any>createRequest("az2016",userInputs_withoutEcompGeneratedNaming, service, serviceInstanceId, networkInstanceGroups,'vDBE 0','1'); + expect(actualResult_withoutEcompGeneratedNaming).toEqual(expectedResult); + done(); + }); +}); diff --git a/vid-webpack-master/src/app/factories/mso.factory.ts b/vid-webpack-master/src/app/factories/mso.factory.ts new file mode 100644 index 00000000..c26296ed --- /dev/null +++ b/vid-webpack-master/src/app/factories/mso.factory.ts @@ -0,0 +1,92 @@ +import { RequestInfo, RequestDetails, ModelInfo, CloudConfiguration, LineOfBusiness, Platform, RelatedInstanceList, VfcModel } from "./models/requestDetails.model"; +import * as _ from "lodash"; + +function extractModelInfoFromNodeTemplate(node: any, type: string) { + let modelInfo: ModelInfo = <ModelInfo>{}; + if (node["customizationUuid"] !== undefined) { + modelInfo.modelCustomizationId = node["customizationUuid"]; + } + if (node["modelCustomizationName"] !== undefined) { + modelInfo.modelCustomizationName = node["modelCustomizationName"]; + } + modelInfo.modelVersionId = node["uuid"]; + modelInfo.modelName = node["name"]; + modelInfo.modelInvariantId = node["invariantUuid"]; + modelInfo.modelType = type; + modelInfo.modelVersion = node["version"]; + return modelInfo; +} + +function extractRequestInfo(userInputs: any, userId: string): RequestInfo { + let requestInfo: RequestInfo = <RequestInfo>{}; + if (userInputs["instanceName"] !== undefined) requestInfo.instanceName = userInputs["instanceName"]; + requestInfo.productFamilyId = userInputs["productFamily"]; + requestInfo.source = "VID"; + requestInfo.suppressRollback = userInputs["rollback"] === "false"; + requestInfo.requestorId = userId; + return requestInfo; +} + +function extractPlatform(userInputs: any): Platform { + let platform: Platform = <Platform>{}; + platform.platformName = userInputs["platformName"]; + return platform; +} + +function extractVfcGroupModelAccordingToUuid(vnfModel: any, vfcUuid: string) { + return _.find(vnfModel.vfcInstanceGroups, { uuid: vfcUuid }); +} + +function extractLineOfBusiness(userInputs: any) { + let lob: LineOfBusiness = <LineOfBusiness>{}; + lob.lineOfBusinessName = userInputs["lineOfBusiness"]; + return lob; +} + +function extractCloudConfiguration(userInputs: any) { + let cloudConfig: CloudConfiguration = <CloudConfiguration>{}; + cloudConfig.lcpCloudRegionId = userInputs["lcpRegion"]; + cloudConfig.tenantId = userInputs["tenantId"]; + return cloudConfig; +} + +function extractModelInfoFromVfcNode(vfcModel: VfcModel): ModelInfo { + let modelinfo: ModelInfo = <ModelInfo>{}; + modelinfo.modelName = vfcModel.name; + modelinfo.modelType = "networkCollection"; + modelinfo.modelVersion = vfcModel.version; + modelinfo.modelVersionId = vfcModel.uuid; + modelinfo.modelInvariantId = vfcModel.invariantUuid; + return modelinfo; +} + +export function createRequest(userId: string, userInputs: any, service: any, serviceInstanceId: string, networkInstanceGroups: any, vnfCustomizationName: string, vnfCustomizationId: string) { + let request: RequestDetails = <RequestDetails>{}; + request.requestInfo = extractRequestInfo(userInputs, userId); + request.lineOfBusiness = extractLineOfBusiness(userInputs); + request.cloudConfiguration = extractCloudConfiguration(userInputs); + request.platform = extractPlatform(userInputs); + request.modelInfo = extractModelInfoFromNodeTemplate(service.vnfs[vnfCustomizationName], "vnf"); + request.requestParameters = { userParams: [], testApi: sessionStorage.getItem("msoRequestParametersTestApiValue")}; + request.relatedInstanceList = []; + let serviceRelatedInstance: RelatedInstanceList = { + relatedInstance: { + instanceId: serviceInstanceId, + modelInfo: extractModelInfoFromNodeTemplate(service.service, "service") + } + }; + request.relatedInstanceList.push(serviceRelatedInstance); + _.forOwn(networkInstanceGroups, function(group) { + let modelUuid = group["instance-group"]["model-version-id"]; + let vfcModel = extractVfcGroupModelAccordingToUuid(service.vnfs[vnfCustomizationName], modelUuid); + let networkInstanceGroup: RelatedInstanceList = { + relatedInstance: { + instanceId: group["instance-group"].id, + modelInfo: extractModelInfoFromVfcNode(vfcModel) + } + }; + request.relatedInstanceList.push(networkInstanceGroup); + }); + + return request; +} diff --git a/vid-webpack-master/src/app/global.actions.ts b/vid-webpack-master/src/app/global.actions.ts new file mode 100644 index 00000000..46575fb3 --- /dev/null +++ b/vid-webpack-master/src/app/global.actions.ts @@ -0,0 +1,23 @@ +import {Action, ActionCreator} from "redux"; +export const UPDATE_NAME= '[NAME] Update'; +export const UPDATE_FLAGS= '[FLAGS] Update'; + +export interface UpdateGlobalAction extends Action { + name?: string; +} + +export interface UpdateFlagsAction extends Action { + flags?: any; +} + +export const updateName: ActionCreator<UpdateGlobalAction> = + (name) => ({ + type: UPDATE_NAME, + name: name + }); + +export const updateFlags: ActionCreator<UpdateFlagsAction> = + (flags) => ({ + type: UPDATE_FLAGS, + flags: flags + }); diff --git a/vid-webpack-master/src/app/global.reducer.ts b/vid-webpack-master/src/app/global.reducer.ts new file mode 100644 index 00000000..cffd3460 --- /dev/null +++ b/vid-webpack-master/src/app/global.reducer.ts @@ -0,0 +1,29 @@ +/* tslint:disable no-switch-case-fall-through */ +import {Action} from 'redux'; +import {UPDATE_FLAGS, UPDATE_NAME, UpdateFlagsAction, UpdateGlobalAction} from "./global.actions"; + + + +export interface GlobalState { + name : string; + flags : { [key: string]: boolean }; +} + +const initialState: GlobalState = { + name : null, + flags : null +}; + + +export const GlobalReducer = + function (state: GlobalState = initialState, action: Action): GlobalState { + switch (action.type) { + case UPDATE_NAME: + return Object.assign(state, state, (<UpdateGlobalAction>action)); + case UPDATE_FLAGS: + Object.assign(state, (<UpdateFlagsAction>action)); + return Object.assign({}, state); + default: + return state; + } + }; diff --git a/vid-webpack-master/src/app/healthStatus/health-status.component.html b/vid-webpack-master/src/app/healthStatus/health-status.component.html new file mode 100644 index 00000000..600c873b --- /dev/null +++ b/vid-webpack-master/src/app/healthStatus/health-status.component.html @@ -0,0 +1,36 @@ +<div class="row"> + <div class="health-status-header"> + <div> + <div class="row" style="margin-left: 0;"> + <div> + <span class="title">Health Status </span> + </div> + <div class="lastUpdate"> + <div style="float: left;margin-top: 3px;"><span>Last update: {{lastUpdatedDate | date:'MMM. dd, yyyy | HH:mm'}}</span> + </div> + <div class="refresh-btn" [ngClass]="{'spin' : !dataIsReady}" (click)="refreshData()"> + <span class="icon-refresh"></span> + </div> + </div> + </div> + </div> + </div> +</div> +<div class="row"> + <table id="health-status-table" class="table table-bordered"> + <tbody> + <tr class="row spaceUnder" *ngFor="let componentStatus of componentStatuses" [attr.data-tests-id]="'component-'+componentStatus?.component"> + <td class="col-md-1 col-xs-1 component-name" + [attr.data-tests-id]="'component-name-'+componentStatus?.component" + [ngClass]="isAvailable(componentStatus) ? 'border-is-ok': 'border-not-ok'"> + {{componentStatus?.component}} + </td> + <td class="col-md-11 col-xs-3 component-metadata-cell" + [attr.data-tests-id]="'component-metadata-'+componentStatus?.component" + [ngClass]="isAvailable(componentStatus) ? 'border-is-ok': 'border-not-ok'"> + <pre class="component-metadata">{{getMetadata(componentStatus) | json}}</pre> + </td> + </tr> + </tbody> + </table> +</div> diff --git a/vid-webpack-master/src/app/healthStatus/health-status.component.scss b/vid-webpack-master/src/app/healthStatus/health-status.component.scss new file mode 100644 index 00000000..0dda6b21 --- /dev/null +++ b/vid-webpack-master/src/app/healthStatus/health-status.component.scss @@ -0,0 +1,187 @@ +div.dataTables_wrapper { + width: 800px; + margin: 0 auto; +} + +.row { + margin-left: 15px; + margin-right: 15px; +} + +.health-status-header { + margin-top: 30px; + .title { + font-family: OpenSans-Semibold; + font-size: 24px; + color: #4A4A4A; + float: left; + } + + .info { + width: 18px; + height: 18px; + border: 0; + background-size: auto 100%; + cursor: pointer; + float: left; + margin: 10px; + } + + .refresh-btn { + float: left; + margin-top: 6px; + margin-left: 10px; + cursor: pointer; + } + + .lastUpdate { + margin-top: 5px; + padding-top: 0px; + font-size: 15px; + border-left: 1px solid black; + float: left; + padding-left: 10px; + color: gray; + height: 26px; + margin-left: 10px; + } + + .refreshBtn { + width: 18px; + height: 18px; + border: 0; + background-size: auto 100%; + outline: none; + margin-left: 10px; + background: transparent; + } + + svg-icon use { + fill: #0000ff !important; + } + + //.sub-title { + // font-family: OpenSans-Semibold; + // font-size: 14px; + // color: #4A4A4A; + // margin-left: 0; + //} +} + +.loader { + border: 5px solid #f3f3f3; + border-radius: 50%; + border-top: 5px solid #3498db; + width: 170px; + height: 170px; + -webkit-animation: spin 2s linear infinite; + animation: spin 2s linear infinite; + position: absolute; + left: 50%; + right: 50%; + top: 50%; +} + +/* Safari */ +@-webkit-keyframes spin { + 0% { -webkit-transform: rotate(0deg); } + 100% { -webkit-transform: rotate(360deg); } +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + + +.spin { + -webkit-animation: spin .4s infinite linear; + -moz-animation: spin .4s infinite linear; + -o-animation: spin .4s infinite linear; + animation: spin .4s infinite linear; + -webkit-transform-origin: 50% 44%; + transform-origin:50% 44%; + -ms-transform-origin:50% 44% /* IE 9 */ +} + +@-moz-keyframes spin { + from { + -moz-transform: rotate(0deg); + } + to { + -moz-transform: rotate(360deg); + } +} + +@-webkit-keyframes spin { + from { + -webkit-transform: rotate(0deg); + } + to { + -webkit-transform: rotate(360deg); + } +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.icon-refresh:before { + font-family: icomoon; + content: '\e936'; +} + +.row{ + max-width: 1500px; +} + +.red { + color: red; +} + +.border-not-ok { + border : 2px solid red; +} + +.border-is-ok { + border : 2px solid #2fcc66; +} + + +.component-name { + min-width: 100px; + vertical-align: middle; + text-align: center; + background: #f5f5f5; + border-right: unset; +} + +.component-metadata-cell { + border-left: unset; +} + +.component-metadata { + white-space: pre-wrap; + background-color: unset; + border: unset; + font-family: OpenSans-Semibold; + font-size: 12px; +} + + +.table-bordered { + margin-top: 10px; + font-family: OpenSans-Semibold; + font-size: 12px; + overflow-x: auto; + display: block; + color: #5A5A5A; + border: none; + border-spacing: 0px 3px; + border-collapse: separate; +} diff --git a/vid-webpack-master/src/app/healthStatus/health-status.component.ts b/vid-webpack-master/src/app/healthStatus/health-status.component.ts new file mode 100644 index 00000000..6a9ddfdd --- /dev/null +++ b/vid-webpack-master/src/app/healthStatus/health-status.component.ts @@ -0,0 +1,42 @@ +import { Component, OnInit } from '@angular/core'; +import {ExternalComponentStatus} from "../shared/models/externalComponentStatus"; +import {HealthStatusService} from "../shared/server/healthStatusService/health-status.service"; + +@Component({ + selector: 'app-health-status', + templateUrl: './health-status.component.html', + styleUrls: ['./health-status.component.scss'] +}) +export class HealthStatusComponent implements OnInit { + private componentStatuses: Array<ExternalComponentStatus> = []; + private dataIsReady: boolean; + private lastUpdatedDate: Date; + + constructor(private _healthStatusService: HealthStatusService) { + } + + + ngOnInit() { + this.refreshData(); + } + + refreshData(): void { + this.dataIsReady = false; + this._healthStatusService.getProbe() + .subscribe((res: Array<ExternalComponentStatus>) => { + this.componentStatuses = res; + this.dataIsReady = true; + this.lastUpdatedDate = new Date(); + }) + } + + getMetadata(status : ExternalComponentStatus):string { + let metadata = status.metadata; + delete metadata.rawData; + return metadata; + } + + isAvailable(componentStatus: ExternalComponentStatus) { + return componentStatus.available; + } +} diff --git a/vid-webpack-master/src/app/home/home.component.e2e-spec.js b/vid-webpack-master/src/app/home/home.component.e2e-spec.js new file mode 100644 index 00000000..41203f9d --- /dev/null +++ b/vid-webpack-master/src/app/home/home.component.e2e-spec.js @@ -0,0 +1,13 @@ +describe('Home', function () { + + beforeEach(function () { + browser.get('/'); + }); + + it('should have <my-home>', function () { + var home = element(by.css('vid-app my-home')); + expect(home.isPresent()).toEqual(true); + expect(home.getText()).toEqual("Home Works!"); + }); + +}); diff --git a/vid-webpack-master/src/app/home/home.component.html b/vid-webpack-master/src/app/home/home.component.html new file mode 100644 index 00000000..604d9e1d --- /dev/null +++ b/vid-webpack-master/src/app/home/home.component.html @@ -0,0 +1,3 @@ +<p> + Home Works! +</p> diff --git a/vid-webpack-master/src/app/home/home.component.scss b/vid-webpack-master/src/app/home/home.component.scss new file mode 100644 index 00000000..5db1e17a --- /dev/null +++ b/vid-webpack-master/src/app/home/home.component.scss @@ -0,0 +1,4 @@ +// component styles are encapsulated and only applied to their components +* { + color: #FFEF00; +} diff --git a/vid-webpack-master/src/app/home/home.component.ts b/vid-webpack-master/src/app/home/home.component.ts new file mode 100644 index 00000000..1adaf0eb --- /dev/null +++ b/vid-webpack-master/src/app/home/home.component.ts @@ -0,0 +1,30 @@ +import { Component, OnInit } from '@angular/core'; +import { SdcService } from '../services/sdc.service'; +import { DataService } from '../services/data.service'; + +@Component({ + selector: 'my-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.scss'], + providers: [SdcService, DataService] +}) +export class HomeComponent implements OnInit { + + constructor(private _sdcService: SdcService) { + // Do stuff + } + + ngOnInit() { + console.log('Hello Home'); + console.log('getServicesModels: '); + this._sdcService.getServicesModels().subscribe( + // onNext() function + value => console.log('value is ', value), + // onError() function + error => console.log('error is ', error), + // onComplete() function + () => console.log('completed') + ); + } + +} diff --git a/vid-webpack-master/src/app/instantiationStatus/InstantiationStatus.module.ts b/vid-webpack-master/src/app/instantiationStatus/InstantiationStatus.module.ts new file mode 100644 index 00000000..01db0f18 --- /dev/null +++ b/vid-webpack-master/src/app/instantiationStatus/InstantiationStatus.module.ts @@ -0,0 +1,33 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { InputsModule } from '../modules/inputs.module'; +import { DataTableModule } from 'angular2-datatable'; +import { BootstrapModalModule } from 'ng2-bootstrap-modal'; +import { TooltipModule } from 'ngx-tooltip'; +import { InstantiationStatusComponent } from './instantiationStatus.component'; +import { InstantiationStatusComponentService } from './instantiationStatus.component.service'; +import { SharedModule } from '../shared/shared.module'; +import { AngularSvgIconModule } from 'angular-svg-icon'; +import { ContextMenuModule, ContextMenuService } from 'ngx-contextmenu'; +import {ModalModule, PopoverModule} from 'ngx-bootstrap'; +import {AuditInfoModalComponent} from "./auditInfoModal/auditInfoModal.component"; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + BootstrapModalModule, + DataTableModule, + TooltipModule, + ModalModule, + InputsModule, + AngularSvgIconModule, + ContextMenuModule, + SharedModule.forRoot(), + PopoverModule.forRoot()], + declarations: [InstantiationStatusComponent, AuditInfoModalComponent], + providers: [InstantiationStatusComponentService, ContextMenuService] +}) +export class InstantiationStatusModule { } diff --git a/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.html b/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.html new file mode 100644 index 00000000..9386af34 --- /dev/null +++ b/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.html @@ -0,0 +1,84 @@ +<div class="modal fade" bsModal #auditInfoModal="bs-modal" [config]="{backdrop: 'static'}" + tabindex="-1" role="dialog" aria-labelledby="dialog-static-name"> + <div id="audit-info-modal" class=""> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" (click)="onCancelClick()">×</button> + <span [attr.data-tests-id]="'audit-info-title'" class="modal-title">{{title}}</span> + </div> + <div class="modal-body row"> + <div class="col-md-4 left-panel"> + <div id="service-model-name" class="row">SERVICE MODEL: {{serviceModelName}}</div> + <div class="row service-model"> + <model-information [modelInformationItems]="modelInfoItems"></model-information> + </div> + </div> + <div class="col-md-8 right-panel"> + <div class="row"><span class="table-title">VID status</span></div> + <div class="row"> + <table id="service-instantiation-audit-info-vid" class="table table-bordered"> + <thead class="thead-dark"> + <tr class="row"> + <th class="col-md-4" scope="col">Status</th> + <th class="col-md-4" scope="col">Status time</th> + <th class="col-md-4" scope="col">Final</th> + </tr> + </thead> + <tbody> + <tr class="row" *ngFor="let data of vidInfoData"> + <td class="col-md-4" id="vidJobStatus" [attr.data-tests-id]="'vidJobStatus'"> + <custom-ellipsis [id]="data?.jobStatus" [value]="data?.jobStatus | capitalizeAndFormat"></custom-ellipsis> + </td> + <td class="col-md-4" id="vidStatusTime"> + <custom-ellipsis [id]="data?.vidCreated" + [value]="data?.createdDate | date:'MMM dd, yyyy HH:mm'"></custom-ellipsis> + </td> + <td class="col-md-4" id="vidFinalStatus"> + <custom-ellipsis [id]="data?.final" + [value]="data?.final ? 'Yes' : 'No'"></custom-ellipsis> + </td> + </tr> + </tbody> + </table> + <div class="no-result" *ngIf="!isLoading && vidInfoData?.length == 0">There is no data.</div> + </div> + + <div class="row"><span class="table-title">MSO status</span></div> + <table id="service-instantiation-audit-info-mso" class="table table-bordered"> + <thead class="thead-dark row"> + <tr class="row"> + <th class="col-md-3" scope="col">Request ID</th> + <th class="col-md-3" scope="col">Status</th> + <th class="col-md-3" scope="col">Status time</th> + <th class="col-md-3" scope="col">Additional info</th> + </tr> + </thead> + <tbody> + <tr class="row" *ngFor="let data of msoInfoData"> + <td class="col-md-3" id="msoRequestId"> + <custom-ellipsis [id]="data?.requestId" [value]="data?.requestId"></custom-ellipsis> + </td> + <td class="col-md-3" id="msoJobStatus"> + <custom-ellipsis [id]="data?.jobStatus" [value]="data?.jobStatus | capitalizeAndFormat"></custom-ellipsis> + </td> + <td class="col-md-3" id="msoStatusTime"> + <custom-ellipsis [id]="data?.vidCreated" + [value]="data?.createdDate | date:'MMM dd, yyyy HH:mm'"></custom-ellipsis> + </td> + <td class="col-md-3" id="msoAdditionalInfo"> + <custom-ellipsis [id]="data?.additionalInfo" [value]="data?.additionalInfo"></custom-ellipsis> + </td> + </tr> + </tbody> + </table> + <div class="no-result" *ngIf="!isLoading && msoInfoData?.length == 0">There is no data.</div> + </div> + </div> + <div class="modal-footer row"> + <button id="cancelButton" type="button" class="btn btn-default cancel" (click)="onCancelClick()"> + Close + </button> + </div> + </div> + </div> +</div> diff --git a/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.scss b/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.scss new file mode 100644 index 00000000..27b27149 --- /dev/null +++ b/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.scss @@ -0,0 +1,159 @@ +.templatebody.modal-open{ + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; + opacity: 0.5; +} +.modal{ + + #audit-info-modal { + + .modal-content{ + border-radius: 0px; + border: none; + .modal-header{ + background: #009FDB; + font-size: 24px; + color: #ffffff; + .close{ + font-size: 32px; + font-weight: 200; + color: #ffffff; + text-shadow: none; + filter: none; + opacity: 1; + &:hover{ + color: #d2d2d2; + } + } + .modal-title{ + + } + } + .modal-body{ + padding: 0px; + margin: 0px; + display: flex; + .left-panel{ + background: #f2f2f2; + border-right: 1px solid #D2D2D2; + padding-right: 0px; + .row:first-child{ + border-bottom: 1px solid #D2D2D2; + height: 50px; + font-size: 12px; + line-height: 50px; + padding-left: 30px; + font-weight: 700; + margin-right: 0px; + } + .service-model{ + padding-left: 30px; + padding-top: 15px; + } + } + .right-panel{ + padding: 30px 30px 15px 30px; + .row{ + margin: 0px; + } + .table-title{ + font-size: 12px; + text-transform: uppercase; + font-weight: bold; + } + .no-result{ + margin-bottom: 20px; + text-align: center; + border: 1px solid #d2d2d2; + padding: 20px; + margin-top: -23px; + } + + .table-bordered{ + width: 100%; + margin-top: 10px; + font-family: OpenSans-Semibold; + font-size: 12px; + overflow-x: auto; + display: block; + color: #5A5A5A; + + thead { + position: sticky; + top: 0; + z-index: 100; + display: block; + background: rgb(242, 242, 242); + border-bottom: 1px solid #d2d2d2; + tr { + display: flex; + th { + flex-grow: 1; + border-right: 1px solid #d2d2d2; + &:last-child{ + border-right: none; + } + } + } + } + + tbody { + border: none !important; + max-height: 152px; + display: block; + + tr { + display: flex; + border-bottom: 1px solid #d2d2d2; + &:last-child{ + border-bottom: none; + } + td { + border: none; + border-right: 1px solid #d2d2d2; + &:last-child{ + border-right: none; + } + } + } + } + + th { + background: #f2f2f2; + font-family: OpenSans-Semibold; + color: #000000; + font-weight: bold; + border: none; + } + + tr.odd { + background-color: rgb(242, 242, 242); + } + + tr:hover { + background: #e1e1e1; + } + } + } + } + .modal-footer{ + margin: 0px; + .cancel{ + width: 120px; + height: 36px; + background: #009fdb; + border-radius: 2px; + font-family: OpenSans-Regular; + font-size: 14px; + color: #ffffff; + line-height: 16px; + } + } + } + } +} diff --git a/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.service.ts b/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.service.ts new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.service.ts diff --git a/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.ts b/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.ts new file mode 100644 index 00000000..1cff97f5 --- /dev/null +++ b/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.ts @@ -0,0 +1,83 @@ +import {Component, ViewChild} from '@angular/core'; +import {Subject} from 'rxjs/Subject'; +import {ModalDirective} from 'ngx-bootstrap' +import {Constants} from '../../shared/utils/constants'; +import {ModelInformationItem} from '../../shared/components/model-information/model-information.component'; +import {ServiceModel} from '../../shared/models/serviceModel'; +import {ServiceInfoService} from '../../shared/server/serviceInfo/serviceInfo.service'; +import {ServiceInfoModel} from '../../shared/server/serviceInfo/serviceInfo.model'; +import {AuditStatus} from '../../shared/server/serviceInfo/AuditStatus.model'; +import {IframeService} from "../../shared/utils/iframe.service"; + +@Component({ + selector: 'audit-info-modal', + templateUrl: './auditInfoModal.component.html', + styleUrls: ['./auditInfoModal.component.scss'] +}) +export class AuditInfoModalComponent { + static openModal: Subject<ServiceInfoModel> = new Subject<ServiceInfoModel>(); + @ViewChild('auditInfoModal') public auditInfoModal: ModalDirective; + title: string = Constants.AuditInfoModal.TITLE; + modelInfoItems: Array<ModelInformationItem> = []; + serviceModel: ServiceModel; + serviceModelName: string; + vidInfoData: Array<AuditStatus> = []; + msoInfoData: Array<AuditStatus> = []; + parentElementClassName = 'content'; + isLoading = true; + + constructor(private _serviceInfoService: ServiceInfoService, private _iframeService : IframeService) { + AuditInfoModalComponent.openModal.subscribe((jobData: ServiceInfoModel) => { + this.initializeProperties(); + if (jobData) { + this.openAuditInfoModal(jobData); + _iframeService.addClassOpenModal(this.parentElementClassName); + this.serviceModelName = jobData.serviceModelName ? jobData.serviceModelName : ''; + this.auditInfoModal.show(); + } else { + _iframeService.removeClassCloseModal(this.parentElementClassName); + this.auditInfoModal.hide(); + } + }) + } + + initializeProperties() : void { + this.modelInfoItems = null; + this.vidInfoData = []; + this.msoInfoData = []; + this.isLoading = true; + } + + openAuditInfoModal(jobData: ServiceInfoModel): void { + this.modelInfoItems = this.createModelInformationItems(jobData); + this.initAuditInfoData(jobData['jobId']); + this.auditInfoModal.show(); + } + + initAuditInfoData(jobId: string) { + this._serviceInfoService.getJobAuditStatus(jobId) + .subscribe((res: Array<Array<AuditStatus>>) => { + this.vidInfoData = res[0]; + this.msoInfoData = res[1]; + this.isLoading = false; + }); + } + + createModelInformationItems(serviceModel: ServiceInfoModel): Array<ModelInformationItem> { + return [ + new ModelInformationItem('Subscriber name', 'subscriberName', [serviceModel.subscriberName]), + new ModelInformationItem('Service type', 'serviceType', [serviceModel.serviceType]), + new ModelInformationItem('Service model version', 'serviceModelVersion', [serviceModel.serviceModelVersion]), + new ModelInformationItem('Service instance name', 'serviceInstanceName', [serviceModel.serviceInstanceName], '', true), + new ModelInformationItem('Service instance ID', 'serviceInstanceId', [serviceModel.serviceInstanceId]), + new ModelInformationItem('Requestor User ID', 'userId', [serviceModel.userId]), + ]; + } + + onCancelClick() { + this._iframeService.removeClassCloseModal(this.parentElementClassName); + this.initializeProperties(); + this.auditInfoModal.hide(); + } +} + diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.html b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.html new file mode 100644 index 00000000..e0641d03 --- /dev/null +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.html @@ -0,0 +1,100 @@ +<div class="row"> + <div class="instantiation-status-header"> + <div> + <div class="row" style="margin-left: 0;"> + <div> + <span class="title">Instantiation Status</span> + <span class="icon-info" + triggers="mouseenter:mouseleave" + popover="This table presents all the instantiation requests you made that are waiting, during or finished instantiating. You may see others requests by removing the Show only my requests checkmark." + placement="bottom"></span> + + </div> + <div class="lastUpdate"> + <div style="float: left;margin-top: 3px;"><span>Last update: {{lastUpdatedDate | date:'MMM. dd, yyyy | HH:mm'}}</span></div> + <div id="refresh-btn" class="refresh-btn" [ngClass]="{'spin' : !dataIsReady}" (click)="deactivateInterval(); refreshData(); activateInterval();"> + <span class="icon-refresh"></span> + </div> + </div> + </div> + </div> + </div> + <div class="instantiation-status-data table-responsive"> + <table id="instantiation-status" class="table table-bordered"> + <thead class="thead-dark"> + <tr> + <th scope="col" class="smallTd">User ID</th> + <th scope="col" class="normal">Model Name</th> + <th scope="col" class="normal">Instance Name</th> + <th scope="col" class="smallTd">Model version</th> + <th scope="col" class="normal">Subscriber</th> + <th scope="col" class="mediumTd">Service Type</th> + <th scope="col" class="normal">Region</th> + <th scope="col" class="mediumTd">Tenant</th> + <th scope="col" class="mediumTd">AIC Zone</th> + <th scope="col" class="mediumTd">Project</th> + <th scope="col" class="mediumTd">Owning entity</th> + <th scope="col" class="smallTd">Pause</th> + <th scope="col" class="mediumTd">Date</th> + <th scope="col" class="last">Status</th> + </tr> + </thead> + <tbody > + <tr *ngFor="let data of serviceInfoData; let i = index" [ngClass]="{'odd' : data.serviceIndex%2 == 1}" [id]="data.jobId" (mouseenter)="currentJobId = data?.jobId"> + <td class="smallTd" id="userId"><custom-ellipsis [id]="data.userId" [value]="data.userId"></custom-ellipsis></td> + <td class="normal" id="serviceModelName"><custom-ellipsis [id]="data.serviceModelName" [value]="data.serviceModelName"></custom-ellipsis></td> + <td class="normal" id="serviceInstanceName"><custom-ellipsis [id]="data.serviceInstanceName" [value]="data.serviceInstanceName"></custom-ellipsis></td> + <td class="smallTd" id="serviceModelVersion"><custom-ellipsis [id]="data.serviceModelVersion" [value]="data.serviceModelVersion"></custom-ellipsis></td> + <td class="normal" id="subscriberName"><custom-ellipsis [id]="data.subscriberName" [value]="data.subscriberName"></custom-ellipsis></td> + <td class="mediumTd" id="serviceType"><custom-ellipsis [id]="data.serviceType" [value]="data.serviceType"></custom-ellipsis></td> + <td class="normal" id="regionId"><custom-ellipsis [id]="data.regionId" [value]="data.regionId"></custom-ellipsis></td> + <td class="mediumTd" id="tenantName"><custom-ellipsis [id]="data.tenantName" [value]="data.tenantName"></custom-ellipsis></td> + <td class="mediumTd" id="aicZoneName"><custom-ellipsis [id]="data.aicZoneName" [value]="data.aicZoneName"></custom-ellipsis></td> + <td class="mediumTd" id="project"><custom-ellipsis [id]="data.project" [value]="data.project"></custom-ellipsis></td> + <td class="mediumTd" id="owningEntityName"><custom-ellipsis [id]="data.owningEntityName" [value]="data.owningEntityName"></custom-ellipsis></td> + <td class="smallTd" id="pause"><custom-ellipsis [id]="data.pause" [value]="data.pause"></custom-ellipsis></td> + <td class="mediumTd" id="created"><custom-ellipsis [id]="data.created" [value]="data.created | date:'MMM. dd, yyyy HH:mm'"></custom-ellipsis></td> + <td class="last" id="jobStatus" [ngClass]="data.jobStatus"> + <custom-popover [value]="data.serviceStatus.tooltip" style="float: left;"> + <svg-icon id="jobStatusIcon-{{i}}" (click)="auditInfo(data)" svg-directive [fill]="data.serviceStatus.color" [widthViewBox]="27" [heightViewBox]="27" + src="./assets/img/{{data.serviceStatus.iconClassName}}.svg"></svg-icon> + </custom-popover> + <div class="menu-div" (click)="onContextMenu($event, data); currentJobId = data.jobId"> + <span class="icon-menu"></span> + <context-menu> + <ng-template contextMenuItem (execute)="open($event?.item)" [enabled]="isOpenVisible"> + <div [attr.data-tests-id]="'context-menu-open'"> + <span class="context-menu-icon"><i class="fa fa-external-link" aria-hidden="true"></i></span> + Open + </div> + </ng-template> + <ng-template contextMenuItem (execute)="auditInfo($event?.item)"> + <div [attr.data-tests-id]="'context-menu-audit-info'"> + <span class="context-menu-icon audit-icon"><i class="fa fa-info-circle" aria-hidden="true"></i></span> + Audit info + </div> + </ng-template> + <ng-template contextMenuItem let-item (execute)="deleteItem($event?.item)" [enabled]="isDeleteEnabled"> + <div [attr.data-tests-id]="'context-menu-delete'"> + <span class="context-menu-icon"><i class="fa fa-trash-o" aria-hidden="true"></i></span> + Delete + </div> + </ng-template> + <ng-template contextMenuItem let-item (execute)="hideItem($event?.item)" [enabled]="isHideEnabled"> + <div [attr.data-tests-id]="'context-menu-hide'"> + <span class="context-menu-icon"><i class="fa fa-eye-slash" aria-hidden="true"></i></span> + Hide request + </div> + </ng-template> + </context-menu> + </div> + </td> + </tr> + </tbody> + </table> + </div> + <audit-info-modal></audit-info-modal> +</div> + + + diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.scss b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.scss new file mode 100644 index 00000000..65c2400a --- /dev/null +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.scss @@ -0,0 +1,277 @@ + +.last { + position: sticky; + background: #f8f8f8; + right: 0; + padding-left: 15px; + padding-right: 15px; + width: 100px !important; + max-width: 100px !important; + min-width: 85px; +} + +div.dataTables_wrapper { + width: 800px; + margin: 0 auto; +} + +.row { + margin-left: 15px; + margin-right: 15px; +} + +.instantiation-status-header { + margin-top: 30px; + .title { + font-family: OpenSans-Semibold; + font-size: 24px; + color: #4A4A4A; + float: left; + } + + .info { + width: 18px; + height: 18px; + border: 0; + background-size: auto 100%; + cursor: pointer; + float: left; + margin: 10px; + } + + .refresh-btn { + float: left; + margin-top: 6px; + margin-left: 10px; + cursor: pointer; + } + + .lastUpdate { + margin-top: 5px; + padding-top: 0px; + font-size: 15px; + border-left: 1px solid black; + float: left; + padding-left: 10px; + color: gray; + height: 26px; + } + + .refreshBtn { + width: 18px; + height: 18px; + border: 0; + background-size: auto 100%; + outline: none; + margin-left: 10px; + background: transparent; + } + + svg-icon use { + fill: #0000ff !important; + } + + .sub-title { + font-family: OpenSans-Semibold; + font-size: 14px; + color: #4A4A4A; + margin-left: 0; + } +} + +.instantiation-status-data { + table { + width: 100%; + margin-top: 30px; + font-family: OpenSans-Semibold; + font-size: 12px; + overflow-x: auto; + display: block; + color: #5A5A5A; + + } + + thead { + position: sticky; + top: 0; + z-index: 100; + display: block; + } + + thead th.normal, tbody td.normal { + min-width: 200px !important; + max-width: 200px; + } + + thead th.smallTd ,tbody td.smallTd { + max-width: 100px !important; + min-width: 100px !important; + } + + thead th.mediumTd ,tbody td.mediumTd { + max-width: 150px !important; + min-width: 150px !important; + } + + tbody { + border: none !important; + max-height: 500px; + display: block; + } + + th { + background: #f2f2f2; + font-family: OpenSans-Semibold; + color: #5A5A5A; + font-weight: bold; + } + .menu-div { + float: right; + border-left: 1px solid gray; + height: 23px; + } + + tr.odd { + background-color: rgb(242, 242, 242); + } + + tr:hover { + background: #e1e1e1; + } + + thead { + background: rgb(242, 242, 242); + } + + td#jobStatus { + cursor: pointer; + box-shadow: -2px 1px 5px -2px #aaa; + } +} + + +.loader { + border: 5px solid #f3f3f3; + border-radius: 50%; + border-top: 5px solid #3498db; + width: 170px; + height: 170px; + -webkit-animation: spin 2s linear infinite; + animation: spin 2s linear infinite; + position: absolute; + left: 50%; + right: 50%; + top: 50%; +} + +/* Safari */ +@-webkit-keyframes spin { + 0% { -webkit-transform: rotate(0deg); } + 100% { -webkit-transform: rotate(360deg); } +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + + +.spin { + -webkit-animation: spin .4s infinite linear; + -moz-animation: spin .4s infinite linear; + -o-animation: spin .4s infinite linear; + animation: spin .4s infinite linear; + -webkit-transform-origin: 50% 44%; + transform-origin:50% 44%; + -ms-transform-origin:50% 44% /* IE 9 */ +} + +@-moz-keyframes spin { + from { + -moz-transform: rotate(0deg); + } + to { + -moz-transform: rotate(360deg); + } +} + +@-webkit-keyframes spin { + from { + -webkit-transform: rotate(0deg); + } + to { + -webkit-transform: rotate(360deg); + } +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.icon-refresh:before { + font-family: icomoon; + content: '\e936'; +} + +.icon-info { + float: left; + margin-top: 10px; + margin-left: 10px; + font-size: 17px; + margin-right: 10px +} + +.icon-info:before { + font-family: icomoon; + content: '\e91f'; +} + +.context-menu-icon{ + width: 25px; + float: left; +} + +.icon-x:before { + font-family: icomoon; + content: '\e93d'; +} + +.icon-inprogress:before { + font-family: icomoon; + content: '\e899'; +} + +.icon-success_o:before { + font-family: icomoon; + content: '\e934'; +} + +.icon-menu:before { + font-family: icomoon; + content: '\e924'; + font-size: 22px; +} + +.icon-X_o:before { + font-family: icomoon; + content: '\e93d'; + color: #D02B2B; +} + + +.icon-inprogress:before { + font-family: icomoon; + content: '\e941'; + color: #009FDB; +} + +.status-icon { + font-size: 20px; + margin-top: 0px; + height: 0; +} diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.spec.ts b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.spec.ts new file mode 100644 index 00000000..c9f434e9 --- /dev/null +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.spec.ts @@ -0,0 +1,278 @@ +import {getTestBed, TestBed} from '@angular/core/testing'; +import { + INPROGRESS, + InstantiationStatusComponentService, + PAUSE, + PENDING, + ServiceStatus, + STOPED, + SUCCESS_CIRCLE, + X_O +} from './instantiationStatus.component.service'; +import {ServiceInfoModel} from '../shared/server/serviceInfo/serviceInfo.model'; +import { Observable } from 'rxjs/Rx'; + +describe('Instantiation Status Service', () => { + let injector; + let service: InstantiationStatusComponentService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [], + providers: [InstantiationStatusComponentService] + }); + + injector = getTestBed(); + service = injector.get(InstantiationStatusComponentService); + }); + + it('generateServiceInfoDataMapping should return mapping of arrays', (done: DoneFn) => { + let data : Array<ServiceInfoModel> = generateServiceInfoData(); + let result = service.generateServiceInfoDataMapping(data); + + expect(result['1']).toBeDefined(); + expect(result['2']).toBeDefined(); + expect(result['3']).toBeDefined(); + + expect(result['1'].length).toEqual(2); + expect(result['2'].length).toEqual(2); + expect(result['3'].length).toEqual(1); + done(); + }); + + it('generateServiceInfoDataMapping if array is empty should return empty object', (done: DoneFn) => { + let result = service.generateServiceInfoDataMapping([]); + + expect(result['1']).not.toBeDefined(); + expect(result['2']).not.toBeDefined(); + expect(result['3']).not.toBeDefined(); + done(); + }); + + it('convertObjectToArray', (done: DoneFn) => { + + spyOn(service, 'convertObjectToArray').and.returnValue( + Observable.of([]) + ); + + let data : Array<ServiceInfoModel> = generateServiceInfoData(); + service.convertObjectToArray(data).subscribe((result) => { + expect(result).toBeDefined(); + done(); + }); + }); + + it('getStatusTooltip should return status popover', (done: DoneFn) => { + let result : ServiceStatus = service.getStatus('pending'); + expect(result.tooltip).toEqual('Pending: The service will automatically be sent for instantiation as soon as possible.'); + + result = service.getStatus('IN_PROGRESS'); + expect(result.tooltip).toEqual('In-progress: the service is in process of instantiation.'); + + result = service.getStatus('PAUSED'); + expect(result.tooltip).toEqual('Paused: Service has paused and waiting for your action.\n Select actions from the menu to the right.'); + + result = service.getStatus('FAILED'); + expect(result.tooltip).toEqual('Failed: Service instantiation has failed, load the service to see the error returned.'); + + result = service.getStatus('COMPLETED'); + expect(result.tooltip).toEqual('Completed successfully: Service is successfully instantiated.'); + + result = service.getStatus('STOPPED'); + expect(result.tooltip).toEqual('Stopped: Due to previous failure, will not be instantiated.'); + done(); + }); + + it('getStatusTooltip should return correct icon per job status', (done: DoneFn) => { + let result : ServiceStatus = service.getStatus('pending'); + expect(result.iconClassName).toEqual(PENDING); + + result = service.getStatus('IN_PROGRESS'); + expect(result.iconClassName).toEqual(INPROGRESS); + + result = service.getStatus('PAUSED'); + expect(result.iconClassName).toEqual(PAUSE); + + result = service.getStatus('FAILED'); + expect(result.iconClassName).toEqual(X_O); + + result = service.getStatus('COMPLETED'); + expect(result.iconClassName).toEqual(SUCCESS_CIRCLE); + + result = service.getStatus('STOPPED'); + expect(result.iconClassName).toEqual(STOPED); + done(); + }); + + + function generateServiceInfoData(){ + return JSON.parse(JSON.stringify([ + { + "created": 1519956533000, + "modified": 1521727738000, + "createdId": null, + "modifiedId": null, + "rowNum": null, + "auditUserId": null, + "auditTrail": null, + "jobId": "6748648484", + "userId": "2222", + "jobStatus": "FAILED", + "pause": false, + "owningEntityId": "1234", + "owningEntityName": null, + "project": null, + "aicZoneId": null, + "aicZoneName": null, + "tenantId": null, + "tenantName": null, + "regionId": null, + "regionName": null, + "serviceType": null, + "subscriberName": null, + "serviceInstanceId": "1", + "serviceInstanceName": null, + "serviceModelId": null, + "serviceModelName": null, + "serviceModelVersion": null, + "createdBulkDate": 1519956533000, + "statusModifiedDate": 1520042933000, + "templateId": "1", + "hidden": false + }, + { + "created": 1519956533000, + "modified": 1521727738000, + "createdId": null, + "modifiedId": null, + "rowNum": null, + "auditUserId": null, + "auditTrail": null, + "jobId": "6748648484", + "userId": "2222", + "jobStatus": "FAILED", + "pause": false, + "owningEntityId": "1234", + "owningEntityName": null, + "project": null, + "aicZoneId": null, + "aicZoneName": null, + "tenantId": null, + "tenantName": null, + "regionId": null, + "regionName": null, + "serviceType": null, + "subscriberName": null, + "serviceInstanceId": "1", + "serviceInstanceName": null, + "serviceModelId": null, + "serviceModelName": null, + "serviceModelVersion": null, + "createdBulkDate": 1519956533000, + "statusModifiedDate": 1520042933000, + "templateId": "1", + "hidden": false + }, + { + "created": 1519956533000, + "modified": 1521727738000, + "createdId": null, + "modifiedId": null, + "rowNum": null, + "auditUserId": null, + "auditTrail": null, + "jobId": "6748648484", + "userId": "2222", + "jobStatus": "FAILED", + "pause": false, + "owningEntityId": "1234", + "owningEntityName": null, + "project": null, + "aicZoneId": null, + "aicZoneName": null, + "tenantId": null, + "tenantName": null, + "regionId": null, + "regionName": null, + "serviceType": null, + "subscriberName": null, + "serviceInstanceId": "2", + "serviceInstanceName": null, + "serviceModelId": null, + "serviceModelName": null, + "serviceModelVersion": null, + "createdBulkDate": 1519956533000, + "statusModifiedDate": 1520042933000, + "templateId": "2", + "hidden": false + }, + { + "created": 1519956533000, + "modified": 1521727738000, + "createdId": null, + "modifiedId": null, + "rowNum": null, + "auditUserId": null, + "auditTrail": null, + "jobId": "6748648484", + "userId": "2222", + "jobStatus": "FAILED", + "pause": false, + "owningEntityId": "1234", + "owningEntityName": null, + "project": null, + "aicZoneId": null, + "aicZoneName": null, + "tenantId": null, + "tenantName": null, + "regionId": null, + "regionName": null, + "serviceType": null, + "subscriberName": null, + "serviceInstanceId": "2", + "serviceInstanceName": null, + "serviceModelId": null, + "serviceModelName": null, + "serviceModelVersion": null, + "createdBulkDate": 1519956533000, + "statusModifiedDate": 1520042933000, + "templateId": "2", + "hidden": false + }, + { + "created": 1519956533000, + "modified": 1521727738000, + "createdId": null, + "modifiedId": null, + "rowNum": null, + "auditUserId": null, + "auditTrail": null, + "jobId": "6748648484", + "userId": "2222", + "jobStatus": "FAILED", + "pause": false, + "owningEntityId": "1234", + "owningEntityName": null, + "project": null, + "aicZoneId": null, + "aicZoneName": null, + "tenantId": null, + "tenantName": null, + "regionId": null, + "regionName": null, + "serviceType": null, + "subscriberName": null, + "serviceInstanceId": "3", + "serviceInstanceName": null, + "serviceModelId": null, + "serviceModelName": null, + "serviceModelVersion": null, + "createdBulkDate": 1519956533000, + "statusModifiedDate": 1520042933000, + "templateId": "3", + "hidden": false + } + ])); + } + +}); diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.ts b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.ts new file mode 100644 index 00000000..293397cc --- /dev/null +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.ts @@ -0,0 +1,75 @@ +import {Injectable} from '@angular/core'; +import {ServiceInfoModel, ServiceInfoUiModel} from '../shared/server/serviceInfo/serviceInfo.model'; +import {isNullOrUndefined} from "util"; +import { Observable } from 'rxjs/Observable'; +import 'rxjs/observable/of'; + +export let PENDING : string = "pending"; +export let INPROGRESS : string = "inprogress"; +export let PAUSE : string = "pause"; +export let X_O : string = "X_o"; +export let SUCCESS_CIRCLE : string = "success+Circle"; +export let STOPED : string = "stoped"; + + +@Injectable() +export class InstantiationStatusComponentService { + generateServiceInfoDataMapping(arr: Array<ServiceInfoModel>) : { [serviceInstanceId: string]: Array<ServiceInfoModel>}{ + let serviceInfoData: { [serviceInstanceId: string]: Array<ServiceInfoModel>; } = {}; + for(let item of arr){ + if(isNullOrUndefined(serviceInfoData[item.templateId])){ + serviceInfoData[item.templateId] = [item]; + }else { + serviceInfoData[item.templateId].push(item); + } + } + return serviceInfoData; + } + + convertObjectToArray(arr: Array<ServiceInfoModel>) : Observable<Array<ServiceInfoUiModel>>{ + const obj = this.generateServiceInfoDataMapping(arr); + let index:number = 0; + let result = []; + for(let item in obj) { + obj[item].map(item => { + item['serviceStatus'] = this.getStatus(item.jobStatus); + item['serviceIndex'] = index; + }); + index++; + result = result.concat(obj[item]); + } + + console.log(result); + return Observable.of(result); + } + + getStatus(status : string) : ServiceStatus { + switch(status.toUpperCase()) { + case 'PENDING' : + return new ServiceStatus(PENDING, '#009FDB', 'Pending: The service will automatically be sent for instantiation as soon as possible.'); + case 'IN_PROGRESS' : + return new ServiceStatus(INPROGRESS, '#009FDB', 'In-progress: the service is in process of instantiation.'); + case 'PAUSED' : + return new ServiceStatus(PAUSE, '#009FDB', 'Paused: Service has paused and waiting for your action.\n Select actions from the menu to the right.'); + case 'FAILED' : + return new ServiceStatus(X_O, '#D02B2B', 'Failed: Service instantiation has failed, load the service to see the error returned.'); + case 'COMPLETED' : + return new ServiceStatus(SUCCESS_CIRCLE, '#53AD15', 'Completed successfully: Service is successfully instantiated.'); + case 'STOPPED' : + return new ServiceStatus(STOPED, '#D02B2B', 'Stopped: Due to previous failure, will not be instantiated.'); + } + } +} + + +export class ServiceStatus { + iconClassName : string; + color : string; + tooltip : string; + + constructor(_iconClassName : string, _color : string, _tooltip : string){ + this.iconClassName = _iconClassName; + this.color = _color; + this.tooltip = _tooltip; + } +} diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.spec.ts b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.spec.ts new file mode 100644 index 00000000..00b6a994 --- /dev/null +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.spec.ts @@ -0,0 +1,88 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import {InstantiationStatusComponent} from './instantiationStatus.component'; +import {ServiceInfoService} from '../shared/server/serviceInfo/serviceInfo.service'; +import {InstantiationStatusComponentService} from './instantiationStatus.component.service'; +import { ContextMenuModule, ContextMenuService } from 'ngx-contextmenu'; +import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to'; +import { ConfigurationService } from '../services/configuration.service'; +import { LogService } from '../shared/utils/log/log.service'; + +describe('Instantiation Status Component', () => { + let component: InstantiationStatusComponent; + let fixture: ComponentFixture<InstantiationStatusComponent>; + let enableDeleteItems = [ + { jobStatus:"PENDING" }, + { jobStatus:"STOPPED" }]; + let disableDeleteItems = [ + { jobStatus:"COMPLETED" }, + { jobStatus:"FAILED" }, + {jobStatus:"IN_PROGRESS"}, + {jobStatus:"UnknownOne"}]; + + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, ContextMenuModule, ScrollToModule.forRoot()], + providers: [ServiceInfoService, InstantiationStatusComponentService, ContextMenuService, ConfigurationService, LogService], + declarations: [InstantiationStatusComponent], + schemas: [ CUSTOM_ELEMENTS_SCHEMA ] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(InstantiationStatusComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('component should initialize basic parameters', (done: DoneFn) => { + component.TIMER_TIME_IN_SECONDS = 2; + expect(component.TIMER_TIME_IN_SECONDS).toEqual(2); + expect(component.dataIsReady).toBeFalsy(); + expect(component.lastUpdatedDate).toBeNull(); + done(); + }); + + it('component constructor should call activateInterval and ngOnInit', (done: DoneFn) => { + component.refreshData(); + expect(component.dataIsReady).toBeFalsy(); + done(); + }); + + it('stopped and pending status isDeleteEnabled button should be enabled, not allowed delete statuses isDeleteEnabled button should be disabled', (done: DoneFn) => { + enableDeleteItems.forEach((item) => { + let isDeleteEnabled: boolean = component.isDeleteEnabled(item); + expect(isDeleteEnabled).toBeTruthy(); + }); + + disableDeleteItems.forEach((item) => { + let isDeleteEnabled: boolean = component.isDeleteEnabled(item); + expect(isDeleteEnabled).toBeFalsy(); + }); + done(); + }); + + it('[COMPLETED, FAILED, STOPPED] status isHideEnable button should be enabled, [IN_PROGRESS, PAUSE, PENDING] status isHideEnable button should be disabled', (done: DoneFn) => { + const enableHideItems = [ + { jobStatus:"COMPLETED" }, + { jobStatus:"FAILED" }, + { jobStatus:"STOPPED" }]; + enableHideItems.forEach((item) => { + let isDeleteEnabled: boolean = component.isHideEnabled(item); + expect(isDeleteEnabled).toBeTruthy(); + }); + + const disableHideItems = [ + { jobStatus:"IN_PROGRESS" }, + { jobStatus:"PAUSE" }, + { jobStatus:"PENDING" }, + { jobStatus:"NOT_MATTER"}]; + disableHideItems.forEach((item) => { + let isDeleteEnabled: boolean = component.isHideEnabled(item); + expect(isDeleteEnabled).toBeFalsy(); + }); + done(); + }); +}); diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.ts b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.ts new file mode 100644 index 00000000..ed45ce43 --- /dev/null +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.ts @@ -0,0 +1,145 @@ +import {AfterViewChecked, Component, ViewChild} from '@angular/core'; +import {ServiceInfoService} from '../shared/server/serviceInfo/serviceInfo.service'; +import {ServiceInfoModel} from '../shared/server/serviceInfo/serviceInfo.model'; +import {InstantiationStatusComponentService} from './instantiationStatus.component.service'; +import {ContextMenuComponent, ContextMenuService} from 'ngx-contextmenu'; +import {AuditInfoModalComponent} from "./auditInfoModal/auditInfoModal.component"; +import * as _ from 'lodash'; +import {ScrollToConfigOptions, ScrollToService} from '@nicky-lenaers/ngx-scroll-to'; +import {ConfigurationService} from "../services/configuration.service"; +import {LogService} from '../shared/utils/log/log.service'; + + +@Component({ + selector : 'instantiation-status', + templateUrl : './instantiationStatus.component.html', + styleUrls : ['./instantiationStatus.component.scss'] +}) +export class InstantiationStatusComponent implements AfterViewChecked{ + + + TIMER_TIME_IN_SECONDS : number = 0; + timer = null; + dataIsReady : boolean = false; + scroll : boolean = false; + lastUpdatedDate: Date = null; + currentJobId: string = null; + instantiationStatusComponentService: InstantiationStatusComponentService; + configurationService : ConfigurationService; + serviceInfoData: Array<ServiceInfoModel> = null; + @ViewChild(ContextMenuComponent) public contextMenu: ContextMenuComponent; + + constructor(private _serviceInfoService: ServiceInfoService, + private _instantiationStatusComponentService : InstantiationStatusComponentService, + private _contextMenuService: ContextMenuService, + private _configurationService : ConfigurationService, + private _scrollToService: ScrollToService, + private _logService : LogService) { + this.instantiationStatusComponentService = _instantiationStatusComponentService; + this.configurationService = this._configurationService; + this.configurationService.getConfiguration("refreshTimeInstantiationDashboard").subscribe(response => { + this.TIMER_TIME_IN_SECONDS = _.isNumber(response) ? response : 0; + this.activateInterval(); + this.refreshData(); + }); + } + + activateInterval() { + if (this.TIMER_TIME_IN_SECONDS > 0) { + this.timer = setInterval(() => { + this.refreshData(); + }, this.TIMER_TIME_IN_SECONDS * 1000); + } + } + + deactivateInterval() { + clearInterval(this.timer); + } + + refreshData(): void { + this.dataIsReady = false; + this._serviceInfoService.getServicesJobInfo(true) + .subscribe((res: Array<ServiceInfoModel>) => { + this._instantiationStatusComponentService.convertObjectToArray(res).subscribe((res) => { + this._logService.info('refresh instantiation status table', res); + this.dataIsReady = true; + this.lastUpdatedDate = new Date(); + if (!_.isEqual(this.serviceInfoData, res)) { + this.serviceInfoData = res; + this.scroll = true; + } + }); + }) + } + + ngAfterViewChecked(){ + if (this.scroll) { + this.scrollToElement(); + this.scroll = false; + } + } + + + + isDeleteEnabled(item):boolean { + return _.includes(['PENDING', 'STOPPED'], item.jobStatus); + } + + deleteItem(item): void { + this._serviceInfoService.deleteJob(item.jobId).subscribe(() => { + this.refreshData(); + }); + } + + hideItem(item): void { + this._serviceInfoService.hideJob(item.jobId).subscribe(() => { + this.refreshData(); + }); + } + + auditInfo(jobData : ServiceInfoModel): void { + AuditInfoModalComponent.openModal.next(jobData); + + } + + isOpenVisible(item):boolean { + return _.includes(['COMPLETED', 'PAUSE'], item.jobStatus); + } + + open(item): void { + let query = + `subscriberId=${item['subscriberName']}&` + + `serviceType=${item['serviceType']}&` + + `serviceInstanceId=${item['serviceInstanceId']}`; + + window.parent.location.assign('../../serviceModels.htm#/instantiate?' + query); + } + + public onContextMenu($event: MouseEvent, item: any): void { + this._contextMenuService.show.next({ + contextMenu: this.contextMenu, + event: $event, + item: item, + }); + $event.preventDefault(); + $event.stopPropagation(); + } + + getImagesSrc(imageName : string) : string { + return './' + imageName + '.svg'; + } + + isHideEnabled(item: any):boolean { + return _.includes(['COMPLETED', 'FAILED', 'STOPPED'], item.jobStatus); + } + scrollToElement() { + if(this.currentJobId){ + const config: ScrollToConfigOptions = { + target: this.currentJobId, + duration: 50, + offset: -35 //header height + }; + this._scrollToService.scrollTo(config); + } + } +} diff --git a/vid-webpack-master/src/app/modules/inputs.module.ts b/vid-webpack-master/src/app/modules/inputs.module.ts new file mode 100644 index 00000000..cb5f914c --- /dev/null +++ b/vid-webpack-master/src/app/modules/inputs.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { DynamicInputsComponent } from '../components/dynamic-inputs/dynamic-inputs.component'; +import { AngularMultiSelectModule } from "angular2-multiselect-dropdown"; +import { DynamicInputLabelPipe } from '../shared/pipes/dynamicInputLabel/dynamic-input-label.pipe'; + + +@NgModule({ + imports: [ + CommonModule, FormsModule, ReactiveFormsModule, AngularMultiSelectModule + ], + providers: [], + declarations: [ DynamicInputLabelPipe, DynamicInputsComponent ], + entryComponents: [], + exports: [ DynamicInputLabelPipe, DynamicInputsComponent ] + +}) + +export class InputsModule { } diff --git a/vid-webpack-master/src/app/service.actions.ts b/vid-webpack-master/src/app/service.actions.ts new file mode 100644 index 00000000..a62fec67 --- /dev/null +++ b/vid-webpack-master/src/app/service.actions.ts @@ -0,0 +1,197 @@ +import { Action, ActionCreator } from "redux"; +import { LcpRegionsAndTenants } from "./shared/models/lcpRegionsAndTenants"; +import { ServiceInstance } from "./shared/models/serviceInstance"; +import { SelectOptionInterface } from "./shared/models/selectOption"; +import { ServiceType } from "./shared/models/serviceType"; +import { VnfInstance } from "./shared/models/vnfInstance"; + +export const UPDATE_MODEL = "[MODEL] Update"; +export const UPDATE_SERVICE_INSTANCE = "[SERVICE INSTANCE] Update"; +export const UPDATE_VNF_INSTANCE = "[VNF INSTANCE] Update"; +export const UPDATE_VF_MODULE = "[VF MODULE] Update"; +export const CREATE_VF_MODULE = "[VF MODULE] Create"; + +export const UPDATE_LCP_REGIONS_AND_TENANTS = "[LCP_REGIONS_AND_TENANTS] Update"; +export const UPDATE_SUBSCRIBERS = "[SUBSCRIBERS] Update"; +export const UPDATE_PRODUCT_FAMILIES = "[PRODUCT_FAMILIES] Update"; +export const UPDATE_SERVICE_TYPES = "[SERVICE_TYPE] Update"; +export const UPDATE_AIC_ZONES = "[AIC_ZONES] Update"; +export const DELETE_SERVICE_INSTANCE = "[SERVICE_INSTANCE] Delete"; +export const DELETE_VNF_INSTANCE = "[VNF_INSTANCE] Delete"; +export const DELETE_VNF_MODULE_INSTANCE = "[VNF_MODULE_INSTANCE] Delete"; +export const UPDATE_CATEGORY_PARAMETERS = "[CATEGORY_PARAMETERS] Update"; +export const UPDATE_NETWORK_FUNCTION = "[UPDATE_NETWORK_FUNCTION] Update"; +export const UPDATE_USER_ID = "[UPDATE_USER_ID] Update"; + + +export interface UpdateServiceModelAction extends Action { + serviceHierarchy?: any; +} + +export interface UpdateNetworkCollectionFunction extends Action { + networksAccordingToNetworkCollection: any; + network_function: any; +} + +export interface UpdateServiceInstanceAction extends Action { + serviceUuid?: string; + serviceInstance?: ServiceInstance; +} + +export interface UpdateVFModuleInstanceAction extends Action { + vfInstance: any; + vfId: string; + serviceUuid: string; +} + +export interface CreateVFModuleInstanceAction extends Action { + vfInstance: any; + vfId: string; + serviceUuid: string; + index : number +} + +export interface UpdateUserIdAction extends Action { + userId: string; +} + +export interface UpdateVnfInstanceAction extends Action { + vnfInstance?: VnfInstance; + vnfModelName?: string; + serviceUuid?: string; +} + +export interface UpdateLcpRegionsAndTenantsAction extends Action { + lcpRegionsAndTenants?: LcpRegionsAndTenants; +} + +export interface UpdateSubscribersAction extends Action { + subscribers?: SelectOptionInterface[]; +} + +export interface UpdateProductFamiliesAction extends Action { + productFamilies?: SelectOptionInterface[]; +} + +export interface UpdateAicZonesAction extends Action { + aicZones?: SelectOptionInterface[]; +} + +export interface UpdateServiceTypesAction extends Action { + serviceTypes?: ServiceType[]; + subscriberId: string; +} + +export interface DeleteServiceInstanceAction extends Action { + serviceUuid?: string; +} + +export interface DeleteVnfInstanceAction extends Action { + modelName?: string; + serviceModelId: string; +} + +export interface DeleteVfModuleInstanceAction extends Action { + modelName?: string; + serviceModelId?: string; + vfName?: string; +} + +export interface UpdateCategoryParametersAction extends Action { + categoryParameters?: string; +} + +export const updateModel: ActionCreator<UpdateServiceModelAction> = serviceHierarchy => ({ + type: UPDATE_MODEL, + serviceHierarchy: serviceHierarchy +}); + +export const updateServiceInstance: ActionCreator<UpdateServiceInstanceAction> = (serviceInstance, serviceUuid) => ({ + type: UPDATE_SERVICE_INSTANCE, + serviceInstance: serviceInstance, + serviceUuid: serviceUuid +}); + +export const updateVFModuleInstance: ActionCreator<UpdateVFModuleInstanceAction> = (vfInstance, vfId, serviceUuid) => ({ + type: UPDATE_VF_MODULE, + vfInstance: vfInstance, + vfId: vfId, + serviceUuid: serviceUuid +}) + +export const createVFModuleInstance: ActionCreator<CreateVFModuleInstanceAction> = (vfInstance, vfId, serviceUuid, index) => ({ + type: CREATE_VF_MODULE, + vfInstance: vfInstance, + vfId: vfId, + serviceUuid: serviceUuid, + index : index +}) + + + +export const updateVNFInstance: ActionCreator<UpdateVnfInstanceAction> = (vnfInstance, vnfModelName, serviceUuid) => ({ + type: UPDATE_VNF_INSTANCE, + vnfInstance: vnfInstance, + vnfModelName: vnfModelName, + serviceUuid: serviceUuid +}); + +export const updateLcpRegionsAndTenants: ActionCreator<UpdateLcpRegionsAndTenantsAction> = lcpRegionsAndTenants => ({ + type: UPDATE_LCP_REGIONS_AND_TENANTS, + lcpRegionsAndTenants: lcpRegionsAndTenants +}); + +export const updateSubscribers: ActionCreator<UpdateSubscribersAction> = subscribers => ({ + type: UPDATE_SUBSCRIBERS, + subscribers: subscribers +}); + +export const updateProductFamilies: ActionCreator<UpdateProductFamiliesAction> = productFamilies => ({ + type: UPDATE_PRODUCT_FAMILIES, + productFamilies: productFamilies +}); + +export const updateAicZones: ActionCreator<UpdateAicZonesAction> = aicZones => ({ + type: UPDATE_AIC_ZONES, + aicZones: aicZones +}); + +export const updateUserId: ActionCreator<UpdateUserIdAction> = userId => ({ + type: UPDATE_USER_ID, + userId: userId +}); + +export const updateCategoryParameters: ActionCreator<UpdateCategoryParametersAction> = categoryParameters => ({ + type: UPDATE_CATEGORY_PARAMETERS, + categoryParameters: categoryParameters +}); + +export const updateServiceTypes: ActionCreator<UpdateServiceTypesAction> = (serviceTypes, subscriberId) => ({ + type: UPDATE_SERVICE_TYPES, + serviceTypes: serviceTypes, + subscriberId: subscriberId +}); + +export const updateNetworkCollectionFunction: ActionCreator<UpdateNetworkCollectionFunction> = (ncf, networksAccordingToNetworkCollection) => ({ + type: UPDATE_NETWORK_FUNCTION, + networksAccordingToNetworkCollection: networksAccordingToNetworkCollection["results"], + network_function: ncf +}); + +export const deleteServiceInstance: ActionCreator<DeleteServiceInstanceAction> = serviceUuid => ({ + type: DELETE_SERVICE_INSTANCE, + serviceUuid: serviceUuid +}); + +export const deleteVnfInstance: ActionCreator<DeleteVnfInstanceAction> = (modelName, serviceModelId) => ({ + type: DELETE_VNF_INSTANCE, + modelName: modelName, + serviceModelId: serviceModelId +}); + +export const deleteVfModuleInstance: ActionCreator<DeleteVfModuleInstanceAction> = (modelName, serviceModelId, vfName) => ({ + type: DELETE_VNF_MODULE_INSTANCE, + modelName: modelName, + serviceModelId: serviceModelId, + vfName: vfName +}); diff --git a/vid-webpack-master/src/app/service.reducer.spec.ts b/vid-webpack-master/src/app/service.reducer.spec.ts new file mode 100644 index 00000000..f734979a --- /dev/null +++ b/vid-webpack-master/src/app/service.reducer.spec.ts @@ -0,0 +1,286 @@ +import { LcpRegionsAndTenants } from './shared/models/lcpRegionsAndTenants'; +import { ServiceReducer, ServiceState } from './service.reducer'; +import { CategoryParams } from './shared/models/categoryParams'; +import { + DELETE_VNF_INSTANCE, DELETE_VNF_MODULE_INSTANCE, DeleteVfModuleInstanceAction, + DeleteVnfInstanceAction, + UPDATE_AIC_ZONES, + UPDATE_LCP_REGIONS_AND_TENANTS, + UPDATE_PRODUCT_FAMILIES, + UPDATE_SERVICE_INSTANCE, + UPDATE_SUBSCRIBERS, + UPDATE_USER_ID, + UPDATE_VNF_INSTANCE, + UpdateAicZonesAction, + UpdateProductFamiliesAction, UpdateServiceInstanceAction, + UpdateSubscribersAction, UpdateUserIdAction, + UpdateVnfInstanceAction +} from './service.actions'; +import { VnfInstance } from './shared/models/vnfInstance'; +import { ServiceInstance } from './shared/models/serviceInstance'; +import { LcpRegion } from './shared/models/lcpRegion'; +import { Tenant } from './shared/models/tenant'; +import { SelectOption } from './shared/models/selectOption'; + + +const initialState: ServiceState = { + serviceHierarchy: {}, + serviceInstance: {}, + lcpRegionsAndTenants: new LcpRegionsAndTenants(), + subscribers: null, + productFamilies: null, + serviceTypes: {}, + aicZones: null, + categoryParameters: new CategoryParams() +}; + + +describe('service reducer', () => { + const userId: string = 'userId'; + it('should handle initial state', () => { + expect(ServiceReducer(undefined, <any>{})).toEqual(initialState); + }); + + it('#UPDATE_USER_ID : should update userId ', (done: DoneFn) => { + expect(ServiceReducer(<any>{}, + <UpdateUserIdAction>{ + type: UPDATE_USER_ID, + userId: userId + } + )['userId']).toEqual(userId); + done(); + }); + + it('#UPDATE_SERVICE_INSTANCE : should update service instance with service id ', (done: DoneFn) => { + const serviceUuid:string = 'serviceUuid'; + + let serviceInstanceObject : ServiceInstance = { + instanceName: 'instanceName', + isUserProvidedNaming: false, + globalSubscriberId: 'globalSubscriberId', + productFamilyId: 'productFamilyId', + subscriptionServiceType: 'subscriptionServiceType', + lcpCloudRegionId: 'lcpCloudRegionId', + tenantId: 'tenantId', + tenantName: 'tenantName', + aicZoneId: 'aicZoneId', + aicZoneName: 'aicZoneName', + projectName: 'projectName', + owningEntityId: 'owningEntityId', + owningEntityName: 'owningEntityName', + pause: false, + bulkSize: 1, + vnfs: {}, + instanceParams : {}, + rollbackOnFailure: false, + subscriberName: 'subscriberName' + }; + + let serviceState = ServiceReducer(<any>{serviceInstance : {}}, + <UpdateServiceInstanceAction>{ + type: UPDATE_SERVICE_INSTANCE, + serviceUuid: serviceUuid, + serviceInstance : serviceInstanceObject + }).serviceInstance['serviceUuid']; + + expect(serviceState.instanceName).toEqual(serviceInstanceObject.instanceName); + expect(serviceState.isUserProvidedNaming).toEqual(serviceInstanceObject.isUserProvidedNaming); + expect(serviceState.globalSubscriberId).toEqual(serviceInstanceObject.globalSubscriberId); + expect(serviceState.productFamilyId).toEqual(serviceInstanceObject.productFamilyId); + expect(serviceState.subscriptionServiceType).toEqual(serviceInstanceObject.subscriptionServiceType); + expect(serviceState.lcpCloudRegionId).toEqual(serviceInstanceObject.lcpCloudRegionId); + expect(serviceState.tenantId).toEqual(serviceInstanceObject.tenantId); + expect(serviceState.tenantName).toEqual(serviceInstanceObject.tenantName); + expect(serviceState.aicZoneId).toEqual(serviceInstanceObject.aicZoneId); + expect(serviceState.aicZoneName).toEqual(serviceInstanceObject.aicZoneName); + expect(serviceState.projectName).toEqual(serviceInstanceObject.projectName); + expect(serviceState.owningEntityId).toEqual(serviceInstanceObject.owningEntityId); + expect(serviceState.owningEntityName).toEqual(serviceInstanceObject.owningEntityName); + expect(serviceState.pause).toEqual(serviceInstanceObject.pause); + expect(serviceState.bulkSize).toEqual(serviceInstanceObject.bulkSize); + expect(serviceState.vnfs).toEqual(serviceInstanceObject.vnfs); + expect(serviceState.instanceParams).toEqual(serviceInstanceObject.instanceParams); + expect(serviceState.rollbackOnFailure).toEqual(serviceInstanceObject.rollbackOnFailure); + expect(serviceState.subscriberName).toEqual(serviceInstanceObject.subscriberName); + + done(); + }); + + it('#UPDATE_VNF_INSTANCE : should update vnf instance with service id and vnfModelName ', (done: DoneFn) => { + let vnfInstanceObj : VnfInstance = { + instanceName: 'instanceName', + isUserProvidedNaming: false, + productFamilyId: 'productFamilyId', + lcpCloudRegionId: 'lcpCloudRegionId', + legacyRegion: 'legacyRegion', + tenantId: 'tenantId', + platformName: 'platformName', + lineOfBusiness: 'lineOfBusiness', + rollbackOnFailure: 'false', + vfModules: {} + }; + + let vnfState = ServiceReducer(<any>{serviceInstance : { + 'serviceUuid' : { + vnfs : {} + } + }}, + <UpdateVnfInstanceAction>{ + type: UPDATE_VNF_INSTANCE, + serviceUuid : 'serviceUuid', + vnfInstance: vnfInstanceObj, + vnfModelName : 'vnfModelName' + }).serviceInstance['serviceUuid'].vnfs['vnfModelName']; + + expect(vnfState.instanceName).toEqual(vnfInstanceObj.instanceName); + expect(vnfState.isUserProvidedNaming).toEqual(vnfInstanceObj.isUserProvidedNaming); + expect(vnfState.productFamilyId).toEqual(vnfInstanceObj.productFamilyId); + expect(vnfState.lcpCloudRegionId).toEqual(vnfInstanceObj.lcpCloudRegionId); + expect(vnfState.legacyRegion).toEqual(vnfInstanceObj.legacyRegion); + expect(vnfState.tenantId).toEqual(vnfInstanceObj.tenantId); + expect(vnfState.platformName).toEqual(vnfInstanceObj.platformName); + expect(vnfState.lineOfBusiness).toEqual(vnfInstanceObj.lineOfBusiness); + expect(vnfState.vfModules).toEqual(vnfInstanceObj.vfModules); + expect(vnfState.rollbackOnFailure).toEqual(vnfInstanceObj.rollbackOnFailure); + + done(); + }); + + it('#UPDATE_LCP_REGIONS_AND_TENANTS : should update lcp region and tenants', (done: DoneFn) => { + let lcpRegionsAndTenantsObj = [ + { + lcpRegionList : [ + new LcpRegion({ + "cloudRegionID" : 'cloudRegionID', + "is-permitted" : "is-permitted" + }) + ], + lcpRegionsTenantsMap : { + "lcpRegion" : [new Tenant({ + "tenantID" : "tenantID", + "tenantName" : "tenantName", + "is-permitted" : true + })] + } + } + ]; + let lcpRegionsAndTenantsState = ServiceReducer(<any>{serviceInstance : {}}, + <any>{ + type: UPDATE_LCP_REGIONS_AND_TENANTS, + lcpRegionsAndTenants : lcpRegionsAndTenantsObj + })['lcpRegionsAndTenants']; + + expect(lcpRegionsAndTenantsState).toBeDefined(); + done(); + }); + + it('#UPDATE_SUBSCRIBERS : should update subscribers', (done: DoneFn) => { + let subscribersList = [ + new SelectOption({ + id : 'id', + name : 'name', + isPermitted : false + }) + ]; + let subscribersState = ServiceReducer(<any>{serviceInstance : {}}, + <UpdateSubscribersAction>{ + type: UPDATE_SUBSCRIBERS, + subscribers : subscribersList + })['subscribers']; + + expect(subscribersState).toBeDefined(); + expect(subscribersState[0].id).toEqual(subscribersList[0].id); + expect(subscribersState[0].isPermitted).toEqual(subscribersList[0].isPermitted); + expect(subscribersState[0].name).toEqual(subscribersList[0].name); + + done(); + }); + + it('#UpdateProductFamiliesAction : should update product families', (done: DoneFn) => { + let productFamiliesObj = [ + new SelectOption({ + id : 'id', + name : 'name', + isPermitted : false + }) + ]; + let productFamiliesState = ServiceReducer(<any>{serviceInstance : {}}, + <UpdateProductFamiliesAction>{ + type: UPDATE_PRODUCT_FAMILIES, + productFamilies : productFamiliesObj + })['productFamilies']; + + expect(productFamiliesState).toBeDefined(); + expect(productFamiliesState[0].id).toEqual(productFamiliesObj[0].id); + expect(productFamiliesState[0].isPermitted).toEqual(productFamiliesObj[0].isPermitted); + expect(productFamiliesState[0].name).toEqual(productFamiliesObj[0].name); + + done(); + }); + + it('#UPDATE_AIC_ZONES : should update aic zones', (done: DoneFn) => { + let aicZonesObj = [ + new SelectOption({ + id : 'id', + name : 'name', + isPermitted : false + }) + ]; + let aicZonesState = ServiceReducer(<any>{serviceInstance : {}}, + <UpdateAicZonesAction>{ + type: UPDATE_AIC_ZONES, + aicZones : aicZonesObj + })['aicZones']; + + expect(aicZonesState).toBeDefined(); + expect(aicZonesState[0].id).toEqual(aicZonesObj[0].id); + expect(aicZonesState[0].isPermitted).toEqual(aicZonesObj[0].isPermitted); + expect(aicZonesState[0].name).toEqual(aicZonesObj[0].name); + + done(); + }); + + it('#DELETE_VNF_INSTANCE : should delete existing vnf', (done: DoneFn) => { + let state = ServiceReducer(<any>{serviceInstance : { + 'serviceModelId' : { + vnfs : { + 'modelName' : {} + } + } + }}, + <DeleteVnfInstanceAction>{ + type: DELETE_VNF_INSTANCE, + modelName : 'modelName', + serviceModelId : 'serviceModelId' + }); + + expect(state).toBeDefined(); + expect(state.serviceInstance[ 'serviceModelId'].vnfs['modelName']).not.toBeDefined(); + done(); + }); + + it('#DELETE_VNF_MODULE_INSTANCE : should delete existing vnf module', (done: DoneFn) => { + let state = ServiceReducer(<any>{serviceInstance : { + 'serviceModelId' : { + vnfs : { + 'vfName' : { + vfModules : { + 'modelName' : {} + } + } + } + } + }}, + <DeleteVfModuleInstanceAction>{ + type: DELETE_VNF_MODULE_INSTANCE, + modelName : 'modelName', + vfName : 'vfName', + serviceModelId : 'serviceModelId' + }); + + expect(state).toBeDefined(); + expect(state.serviceInstance['serviceModelId'].vnfs['vfName'].vfModules['modelName']).not.toBeDefined(); + done(); + }); + +}); diff --git a/vid-webpack-master/src/app/service.reducer.ts b/vid-webpack-master/src/app/service.reducer.ts new file mode 100644 index 00000000..a11f31e8 --- /dev/null +++ b/vid-webpack-master/src/app/service.reducer.ts @@ -0,0 +1,214 @@ +import { Action } from 'redux'; +import { + CREATE_VF_MODULE, + CreateVFModuleInstanceAction, + DELETE_SERVICE_INSTANCE, + DELETE_VNF_INSTANCE, + DELETE_VNF_MODULE_INSTANCE, + DeleteServiceInstanceAction, + DeleteVfModuleInstanceAction, + DeleteVnfInstanceAction, + UPDATE_AIC_ZONES, + UPDATE_CATEGORY_PARAMETERS, + UPDATE_LCP_REGIONS_AND_TENANTS, + UPDATE_MODEL, + UPDATE_NETWORK_FUNCTION, + UPDATE_PRODUCT_FAMILIES, + UPDATE_SERVICE_INSTANCE, + UPDATE_SERVICE_TYPES, + UPDATE_SUBSCRIBERS, + UPDATE_USER_ID, + UPDATE_VF_MODULE, + UPDATE_VNF_INSTANCE, + UpdateAicZonesAction, + UpdateCategoryParametersAction, + UpdateLcpRegionsAndTenantsAction, + UpdateNetworkCollectionFunction, + UpdateProductFamiliesAction, + UpdateServiceInstanceAction, + UpdateServiceModelAction, + UpdateServiceTypesAction, + UpdateSubscribersAction, + UpdateUserIdAction, + UpdateVFModuleInstanceAction, + UpdateVnfInstanceAction, +} from './service.actions'; +import { LcpRegionsAndTenants } from './shared/models/lcpRegionsAndTenants'; +import * as _ from 'lodash'; +import { ServiceInstance } from './shared/models/serviceInstance'; +import { CategoryParams } from './shared/models/categoryParams'; +import { SelectOptionInterface } from './shared/models/selectOption'; +import { ServiceType } from './shared/models/serviceType'; +import { VnfInstance } from './shared/models/vnfInstance'; +import { VfModuleMap } from './shared/models/vfModulesMap'; + +export interface ServiceState { + serviceHierarchy: any; + serviceInstance: { [uuid: string]: ServiceInstance; }; + lcpRegionsAndTenants: LcpRegionsAndTenants; + subscribers: SelectOptionInterface[]; + productFamilies: any; + serviceTypes: { [subscriberId: string]: ServiceType[]; }; + aicZones: SelectOptionInterface[]; + categoryParameters: CategoryParams; +} + +const initialState: ServiceState = { + serviceHierarchy: {}, + serviceInstance: {}, + lcpRegionsAndTenants: new LcpRegionsAndTenants(), + subscribers: null, + productFamilies: null, + serviceTypes: {}, + aicZones: null, + categoryParameters: new CategoryParams() +}; + +export const ServiceReducer = + function (state: ServiceState = initialState, action: Action): ServiceState { + switch (action.type) { + case UPDATE_MODEL: { + let uuid = (<UpdateServiceModelAction>action).serviceHierarchy.service.uuid; + state.serviceHierarchy[uuid] = _.cloneDeep((<UpdateServiceModelAction>action).serviceHierarchy); + return Object.assign({}, state); + } + case UPDATE_SERVICE_INSTANCE: { + const updateServiceInstanceAction = <UpdateServiceInstanceAction>action; + const uuid = updateServiceInstanceAction.serviceUuid; + const newState = _.cloneDeep(state); + + const serviceInstance: ServiceInstance = newState.serviceInstance[uuid] || new ServiceInstance(); + + newState.serviceInstance[uuid] = Object.assign(serviceInstance, updateServiceInstanceAction.serviceInstance); + return newState; + } + case UPDATE_VNF_INSTANCE: { + const updateVnfInstanceAction = <UpdateVnfInstanceAction>action; + const serviceUuid = updateVnfInstanceAction.serviceUuid; + const vnfModelName = updateVnfInstanceAction.vnfModelName; + + const newState = _.cloneDeep(state); + const vnfInstance: VnfInstance = newState.serviceInstance[serviceUuid].vnfs[vnfModelName] || new VnfInstance(); + + newState.serviceInstance[serviceUuid].vnfs[vnfModelName] = Object.assign(vnfInstance, updateVnfInstanceAction.vnfInstance); + return newState; + } + + + case UPDATE_USER_ID: { + const updateUserId : UpdateUserIdAction = <UpdateUserIdAction>action; + // var newState2 = {...state,'userId':updateUserId.userId} + var newState = _.cloneDeep(state); + newState['userId'] = updateUserId.userId; + return newState; + + // state = (... {userId:action["userId"]},state]} + } + + case UPDATE_VF_MODULE: { + const updateVFModuleInstanceAction = <UpdateVFModuleInstanceAction>action; + const vfInstance = updateVFModuleInstanceAction.vfInstance; + const serviceUuid = updateVFModuleInstanceAction.serviceUuid; + const vfModuleId = updateVFModuleInstanceAction.vfId; + const newState = _.cloneDeep(state); + const vnfs = newState.serviceHierarchy[serviceUuid].vnfs; + const vnfId = getVfModuleParentVnfId(vnfs, vfModuleId); + let vfModulesMap = newState.serviceInstance[serviceUuid].vnfs[vnfId].vfModules[vfModuleId] || new VfModuleMap(); + vfModulesMap[vfModuleId] = vfInstance; + newState.serviceInstance[serviceUuid].vnfs[vnfId].vfModules[vfModuleId] = vfModulesMap; + return newState; + } + + case CREATE_VF_MODULE: { + const updateVFModuleInstanceAction = <CreateVFModuleInstanceAction>action; + const vfInstance = updateVFModuleInstanceAction.vfInstance; + const serviceUuid = updateVFModuleInstanceAction.serviceUuid; + const vfModuleId = updateVFModuleInstanceAction.vfId; + const index = updateVFModuleInstanceAction.index; + let newState = Object.assign({}, state); + const vnfs = newState.serviceHierarchy[serviceUuid].vnfs; + const vnfId = getVfModuleParentVnfId(vnfs, vfModuleId); + let vfModulesMap = newState.serviceInstance[serviceUuid].vnfs[vnfId].vfModules[vfModuleId] || new VfModuleMap(); + let randomId = generateId(); + vfModulesMap[vfModuleId + randomId] = vfInstance; + + newState.serviceInstance[serviceUuid].vnfs[vnfId].vfModules[vfModuleId] = vfModulesMap; + return newState; + } + + + case UPDATE_LCP_REGIONS_AND_TENANTS: { + Object.assign(state, (<UpdateLcpRegionsAndTenantsAction>action)); + return Object.assign({}, state); + } + case UPDATE_SUBSCRIBERS: { + Object.assign(state, (<UpdateSubscribersAction>action)); + return Object.assign({}, state); + } + case UPDATE_AIC_ZONES: { + Object.assign(state, (<UpdateAicZonesAction>action)); + return Object.assign({}, state); + } + case UPDATE_PRODUCT_FAMILIES: { + Object.assign(state, (<UpdateProductFamiliesAction>action)); + return Object.assign({}, state); + } + case UPDATE_NETWORK_FUNCTION: { + let networkFunctionReduxObj = state["networkFunctions"] == undefined ? {} : state["networkFunctions"]; + networkFunctionReduxObj[(<UpdateNetworkCollectionFunction>action).network_function] = (<UpdateNetworkCollectionFunction>action).networksAccordingToNetworkCollection; + Object.assign(state, {"networkFunctions":networkFunctionReduxObj}); + return Object.assign({}, state); + } + case UPDATE_SERVICE_TYPES: { + let subscriberId = (<UpdateServiceTypesAction>action).subscriberId; + let serviceTypes = (<UpdateServiceTypesAction>action).serviceTypes; + state.serviceTypes[subscriberId] = serviceTypes; + return Object.assign({}, state); + } + case UPDATE_CATEGORY_PARAMETERS: { + Object.assign(state, (<UpdateCategoryParametersAction>action)); + return Object.assign({}, state); + } + case DELETE_SERVICE_INSTANCE: { + const uuid = (<DeleteServiceInstanceAction>action).serviceUuid; + if (state.serviceHierarchy[uuid]) { + let newState = _.omit(state, ['serviceInstance[' + uuid + ']']); + return Object.assign({}, state, newState); + } + return Object.assign({}, state); + } + case DELETE_VNF_INSTANCE: { + const actionData =(<DeleteVnfInstanceAction>action); + if(state.serviceInstance[actionData.serviceModelId]){ + delete state.serviceInstance[actionData.serviceModelId].vnfs[actionData.modelName]; + } + return Object.assign({}, state); + } + + case DELETE_VNF_MODULE_INSTANCE: { + const actionData =(<DeleteVfModuleInstanceAction>action); + if(state.serviceInstance[actionData.serviceModelId]){ + delete state.serviceInstance[actionData.serviceModelId].vnfs[actionData.vfName].vfModules[actionData.modelName]; + } + return Object.assign({}, state); + } + default: + return Object.assign({}, state); + } + }; + +const generateId = () => { + return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5); +}; + + +const getVfModuleParentVnfId = (vnfs: object, vfModuleId: string) => { + let vnfId = undefined; + _.forOwn(vnfs, (value, key) => { + if (vnfs[key].vfModules && vnfs[key].vfModules[vfModuleId]) { + vnfId = vnfs[key].modelCustomizationName; + return false; + } + }); + return vnfId; +}; diff --git a/vid-webpack-master/src/app/services/aaiService/aai.actions.ts b/vid-webpack-master/src/app/services/aaiService/aai.actions.ts new file mode 100644 index 00000000..649fb145 --- /dev/null +++ b/vid-webpack-master/src/app/services/aaiService/aai.actions.ts @@ -0,0 +1,75 @@ +import {Action, ActionCreator} from "redux"; + +export const LOAD_PRODUCT_FAMILIES = '[PRODUCT_FAMILIES] Load'; + +export const LOAD_LCP_TENANT = '[LCP_TENANT] Load'; + +export const LOAD_AIC_ZONES = '[AIC_ZONES] Load'; + +export const LOAD_CATEGORY_PARAMETERS = '[LOAD_CATEGORY_PARAMETERS] Load'; + +export const LOAD_SERVICE_MDOEL_BY_UUID = '[LOAD_SERVICE_MDOEL_BY_UUID] Load'; + +export const LOAD_NETWORK_ACCORDING_TO_NF = '[LOAD_NETWORK_ACCORDING_TO_NF] Load' + +export const LOAD_USER_ID = '[LOAD_USER_ID] Load' + + +export interface LoadProductFamiliesAction extends Action {} + +interface LoadLcpTenant extends Action {} + +interface LoadAicZones extends Action {} + +interface LoadCategoryParameters extends Action {} + +interface LoadServiceModelByUuid extends Action {} + +interface LoadNetworkAccordingToNetworkCF extends Action{} + +interface LoadUserId extends Action{} + + +export const loadServiceAccordingToUuid : ActionCreator<LoadServiceModelByUuid> = + (uuid : string) =>({ + type : LOAD_SERVICE_MDOEL_BY_UUID, + modelId : uuid + }) + + +export const loadProductFamiliesAction: ActionCreator<LoadProductFamiliesAction> = + () => ({ + type: LOAD_PRODUCT_FAMILIES, + }); + + +export const loadUserId: ActionCreator<LoadUserId> = +() => ({ + type: LOAD_USER_ID, +}); + + + export const loadLcpTenant: ActionCreator<LoadLcpTenant> = + () => ({ + type: LOAD_LCP_TENANT, + }); + + +export const loadAicZones: ActionCreator<LoadAicZones> = + () => ({ + type: LOAD_AIC_ZONES, + }); + +export const loadCategoryParameters: ActionCreator<LoadCategoryParameters> = + () => ({ + type: LOAD_CATEGORY_PARAMETERS, + }); + + +export const loadAaiNetworkAccordingToNetworkCF: ActionCreator<LoadNetworkAccordingToNetworkCF> = + (networkFunction,cloudOwner,cloudRegionId) => ({ + type: LOAD_NETWORK_ACCORDING_TO_NF, + networkFunctions: networkFunction, + cloudOwner: cloudOwner, + cloudRegionId: cloudRegionId + }); diff --git a/vid-webpack-master/src/app/services/aaiService/aai.epics.ts b/vid-webpack-master/src/app/services/aaiService/aai.epics.ts new file mode 100644 index 00000000..5249cea8 --- /dev/null +++ b/vid-webpack-master/src/app/services/aaiService/aai.epics.ts @@ -0,0 +1,81 @@ +import {updateServiceInstance} from './../../service.actions'; +import {Injectable} from '@angular/core'; +import {createEpicMiddleware} from 'redux-observable'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/operator/map'; +import 'rxjs/add/operator/do'; +import 'rxjs/add/operator/startWith'; +import { + LOAD_PRODUCT_FAMILIES, + LOAD_LCP_TENANT, + LOAD_AIC_ZONES, + LOAD_CATEGORY_PARAMETERS, + LOAD_SERVICE_MDOEL_BY_UUID, + LOAD_NETWORK_ACCORDING_TO_NF, + LOAD_USER_ID +} from "./aai.actions"; +import {AaiService} from "./aai.service"; +import { + updateAicZones, updateCategoryParameters, updateLcpRegionsAndTenants, updateNetworkCollectionFunction, + updateProductFamilies, updateUserId +} from "../../service.actions"; +import {AppState} from "../../store/reducers"; + +const notFetchedAlready = (state: AppState): boolean => state.service.productFamilies !== null; + +@Injectable() +export class AAIEpics { + constructor(private aaiService: AaiService) { + } + + public createEpic() { + return [createEpicMiddleware(this.loadProductFamiliesEpic) + , createEpicMiddleware(this.loadLcpTenants) + , createEpicMiddleware(this.loadAicZones) + , createEpicMiddleware(this.loadCategoryParameters) + , createEpicMiddleware(this.loadServiceAccordingToUuid) + , createEpicMiddleware(this.loadNetworkAccordingToNetworkFunction) + , createEpicMiddleware(this.loadUserId) + ]; + } + + private loadLcpTenants = (action$, store) => + action$ + .ofType(LOAD_LCP_TENANT) + .switchMap(() => this + .aaiService + .getLcpRegionsAndTenants('e433710f-9217-458d-a79d-1c7aff376d89', 'VIRTUAL USP') + .map(data => updateLcpRegionsAndTenants(data))); + + private loadProductFamiliesEpic = (action$, store) => action$ + .ofType(LOAD_PRODUCT_FAMILIES) + .switchMap(() => this.aaiService.getProductFamilies().map(data => updateProductFamilies(data))); + + private loadCategoryParameters = (action$, store) => action$ + .ofType(LOAD_CATEGORY_PARAMETERS) + .switchMap(() => this.aaiService.getCategoryParameters(null).map(data => updateCategoryParameters(data))); + + + private loadNetworkAccordingToNetworkFunction = (action$, store) => action$ + .ofType(LOAD_NETWORK_ACCORDING_TO_NF) + .flatMap((action) => this.aaiService.getCRAccordingToNetworkFunctionId(action.networkFunctions, action.cloudOwner, action.cloudRegionId).map((res) => + updateNetworkCollectionFunction(action.networkFunctions, res))); + + private loadServiceAccordingToUuid = (action$, store) => action$ + .ofType(LOAD_SERVICE_MDOEL_BY_UUID) + .switchMap((action) => this.aaiService.getServiceModelById(action.modelId) + .map(data => updateServiceInstance(action.uuid, data))); + + private loadUserId = (action$, store) => action$ + .ofType(LOAD_USER_ID) + .switchMap(() => this.aaiService.getUserId() + .map(res => updateUserId(res))); + + + private loadAicZones = (action$, store) => action$ + .ofType(LOAD_AIC_ZONES) + .switchMap(() => this.aaiService.getAicZones().map(data => updateAicZones(data))); + // .catch(response => of(this.actions.loadFailed(status))) + // .startWith(this.actions.loadStarted())); + +} diff --git a/vid-webpack-master/src/app/services/aaiService/aai.service.ts b/vid-webpack-master/src/app/services/aaiService/aai.service.ts new file mode 100644 index 00000000..dd9d9fb2 --- /dev/null +++ b/vid-webpack-master/src/app/services/aaiService/aai.service.ts @@ -0,0 +1,189 @@ +import {Injectable} from '@angular/core'; +import {HttpClient, HttpHeaders} from '@angular/common/http'; +import { Constants } from '../../shared/utils/constants'; +import { ServiceType } from "../../shared/models/serviceType"; +import {GetSubDetailsResponse} from "./responseInterfaces/getSubDetailsResponseInterface"; +import {Observable} from "rxjs/Observable"; +import * as _ from 'lodash'; +import {CategoryParams} from "../../shared/models/categoryParams"; +import {GetCategoryParamsResponseInterface} from "./responseInterfaces/getCategoryParamsResponseInterface"; +import {Project} from "../../shared/models/project"; +import {OwningEntity} from "../../shared/models/owningEntity"; +import {GetServicesResponseInterface} from "./responseInterfaces/getServicesResponseInterface"; +import {Subscriber} from "../../shared/models/subscriber"; +import {GetSubscribersResponse} from "./responseInterfaces/getSubscribersResponseInterface"; +import {AicZone} from "../../shared/models/aicZone"; +import {GetAicZonesResponse} from "./responseInterfaces/getAicZonesResponseInterface"; +import {LcpRegionsAndTenants} from "../../shared/models/lcpRegionsAndTenants"; +import {LcpRegion} from "../../shared/models/lcpRegion"; +import {Tenant} from "../../shared/models/tenant"; +import {ProductFamily} from "../../shared/models/productFamily" +import { + updateAicZones, updateCategoryParameters, updateLcpRegionsAndTenants, updateModel, updateProductFamilies, + updateServiceTypes, updateSubscribers, updateUserId +} from '../../service.actions'; +import {SelectOption} from '../../shared/models/selectOption'; +import {NgRedux} from "@angular-redux/store"; +import {AppState} from "../../store/reducers"; +import {ResponseContentType, ResponseType} from "@angular/http"; +import 'rxjs/add/operator/do'; +import 'rxjs/add/observable/of'; +import 'rxjs/add/operator/catch'; + +@Injectable() +export class AaiService { + + constructor (private http: HttpClient, private store: NgRedux<AppState>) {} + + public getServiceModelById(serviceModelId: string): Observable<any> { + if (_.has(this.store.getState().service.serviceHierarchy,serviceModelId)){ + return Observable.of(<any> JSON.parse(JSON.stringify(this.store.getState().service.serviceHierarchy[serviceModelId]))); + } + let pathQuery: string = Constants.Path.SERVICES_PATH + serviceModelId; + return this.http.get(pathQuery).map(res => res ) + .do((res) => { + this.store.dispatch(updateModel(res)); + }); + } + + public getUserId() : Observable<any>{ + return this.http.get("../../getuserID",{responseType: 'text'}).do((res)=>this.store.dispatch(updateUserId(res))); + } + + + public getCRAccordingToNetworkFunctionId(networkCollectionFunction,cloudOwner,cloudRegionId){ + return this.http.get('../../aai_get_instance_groups_by_cloudregion/'+cloudOwner+'/'+cloudRegionId+'/' + networkCollectionFunction) + .map(res=>res).do((res)=>console.log(res)); + } + + public getCategoryParameters(familyName): Observable<CategoryParams> { + familyName = familyName || Constants.Path.PARAMETER_STANDARDIZATION_FAMILY; + let pathQuery: string = Constants.Path.GET_CATEGORY_PARAMETERS +"?familyName=" + familyName+ "&r=" + Math.random(); + + return this.http.get<GetCategoryParamsResponseInterface>(pathQuery) + .map(this.categoryParametersResponseToProductAndOwningEntity) + .do(res => { + this.store.dispatch(updateCategoryParameters(res)) + }); + } + + + + categoryParametersResponseToProductAndOwningEntity(res: GetCategoryParamsResponseInterface): CategoryParams { + if (res && res.categoryParameters) { + const owningEntityList = res.categoryParameters.owningEntity.map(owningEntity => new OwningEntity(owningEntity)); + const projectList = res.categoryParameters.project.map(project => new Project(project)); + const lineOfBusinessList = res.categoryParameters.lineOfBusiness.map(owningEntity => new SelectOption(owningEntity)); + const platformList = res.categoryParameters.platform.map(platform => new SelectOption(platform)); + + return new CategoryParams(owningEntityList, projectList, lineOfBusinessList, platformList); + } else { + return new CategoryParams(); + } + } + + public getProductFamilies(): Observable<ProductFamily[]> { + return this.getServices().map(res => res.service.map(service => new ProductFamily(service))); + } + + public getServices(): Observable<GetServicesResponseInterface> { + let pathQuery: string = Constants.Path.AAI_GET_SERVICES + Constants.Path.ASSIGN + Math.random(); + + return this.http.get<GetServicesResponseInterface>(pathQuery); + } + + public getSubscribers(): Observable<Subscriber[]> { + if (this.store.getState().service.subscribers){ + return Observable.of(<any> JSON.parse(JSON.stringify(this.store.getState().service.subscribers))); + } + + let pathQuery: string = Constants.Path.AAI_GET_SUBSCRIBERS + Constants.Path.ASSIGN + Math.random(); + + return this.http.get<GetSubscribersResponse>(pathQuery).map(res => + res.customer.map( subscriber => new Subscriber(subscriber))).do((res) => { + this.store.dispatch(updateSubscribers(res)); + }); + } + + public getAicZones(): Observable<AicZone[]> { + if (this.store.getState().service.aicZones){ + return Observable.of(<any> JSON.parse(JSON.stringify(this.store.getState().service.aicZones))); + } + + let pathQuery: string = Constants.Path.AAI_GET_AIC_ZONES + Constants.Path.ASSIGN + Math.random(); + + return this.http.get<GetAicZonesResponse>(pathQuery).map(res => + res.zone.map(aicZone => new AicZone(aicZone))).do((res) => { + this.store.dispatch(updateAicZones(res)); + }); + } + + public getLcpRegionsAndTenants(globalCustomerId, serviceType): Observable<LcpRegionsAndTenants> { + if (this.store.getState().service.lcpRegionsAndTenants.lcpRegionList.length !== 0){ + return Observable.of(<any> JSON.parse(JSON.stringify(this.store.getState().service.lcpRegionsAndTenants))); + } + let pathQuery: string = Constants.Path.AAI_GET_TENANTS + + globalCustomerId + Constants.Path.FORWARD_SLASH + serviceType + Constants.Path.ASSIGN + Math.random(); + + console.log("AaiService:getSubscriptionServiceTypeList: globalCustomerId: " + + globalCustomerId); + if (globalCustomerId != null) { + return this.http.get(pathQuery) + .map(this.tenantResponseToLcpRegionsAndTenants).do((res) => { + this.store.dispatch(updateLcpRegionsAndTenants(res)); + }); + } + } + + tenantResponseToLcpRegionsAndTenants(cloudRegionTenantList): LcpRegionsAndTenants { + + const lcpRegionsTenantsMap = {}; + + const lcpRegionList = _.uniqBy(cloudRegionTenantList, 'cloudRegionID').map((cloudRegionTenant) => { + return new LcpRegion(cloudRegionTenant) + }); + + lcpRegionList.forEach(region => { + lcpRegionsTenantsMap[region.id] = _.filter(cloudRegionTenantList, {'cloudRegionID' : region.id}) + .map((cloudRegionTenant) => { + return new Tenant(cloudRegionTenant) + }); + const reducer = (accumulator, currentValue) => { + accumulator.isPermitted = accumulator.isPermitted || currentValue.isPermitted; + + return accumulator; + }; + region.isPermitted = lcpRegionsTenantsMap[region.id].reduce(reducer).isPermitted; + }); + + return new LcpRegionsAndTenants(lcpRegionList, lcpRegionsTenantsMap); + } + + public getServiceTypes(subscriberId): Observable<ServiceType[]> { + console.log("AaiService:getSubscriptionServiceTypeList: globalCustomerId: " + subscriberId); + if (_.has(this.store.getState().service.serviceTypes, subscriberId)){ + return Observable.of(<any> JSON.parse(JSON.stringify(this.store.getState().service.serviceTypes[subscriberId]))); + } + + return this.getSubscriberDetails(subscriberId) + .map(this.subDetailsResponseToServiceTypes) + .do((res) => {this.store.dispatch(updateServiceTypes(res, subscriberId));}); + } + + public getSubscriberDetails(subscriberId): Observable<GetSubDetailsResponse> { + let pathQuery: string = Constants.Path.AAI_SUB_DETAILS_PATH + subscriberId + Constants.Path.ASSIGN + Math.random(); + + if (subscriberId != null) { + return this.http.get<GetSubDetailsResponse>(pathQuery); + } + } + + subDetailsResponseToServiceTypes(res: GetSubDetailsResponse): ServiceType[] { + if (res && res['service-subscriptions']) { + const serviceSubscriptions = res['service-subscriptions']['service-subscription']; + return serviceSubscriptions.map((subscription, index) => new ServiceType(String(index), subscription)) + } else { + return []; + } + } +} diff --git a/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getAicZonesResponseInterface.ts b/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getAicZonesResponseInterface.ts new file mode 100644 index 00000000..62581c9e --- /dev/null +++ b/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getAicZonesResponseInterface.ts @@ -0,0 +1,3 @@ +export interface GetAicZonesResponse { + zone: any[]; +} diff --git a/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getCategoryParamsResponseInterface.ts b/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getCategoryParamsResponseInterface.ts new file mode 100644 index 00000000..06398904 --- /dev/null +++ b/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getCategoryParamsResponseInterface.ts @@ -0,0 +1,10 @@ +interface CategoryParametersResponse { + owningEntity: any[], + project: any[] + lineOfBusiness: any[] + platform: any[] +} + +export interface GetCategoryParamsResponseInterface { + categoryParameters: CategoryParametersResponse; +} diff --git a/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getServiceModelResponseInterface.ts b/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getServiceModelResponseInterface.ts new file mode 100644 index 00000000..87671155 --- /dev/null +++ b/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getServiceModelResponseInterface.ts @@ -0,0 +1,5 @@ +import {ServiceModelResponseInterface} from "../../../shared/models/serviceModel"; + +export interface GetServiceModelResponseInterface { + service: ServiceModelResponseInterface +} diff --git a/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getServicesResponseInterface.ts b/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getServicesResponseInterface.ts new file mode 100644 index 00000000..ae04055e --- /dev/null +++ b/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getServicesResponseInterface.ts @@ -0,0 +1,9 @@ +export interface ServiceResponseInterface { + 'service-id': string, + 'service-description': string + 'is-permitted': boolean +} + +export interface GetServicesResponseInterface { + service: ServiceResponseInterface[]; +} diff --git a/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getSubDetailsResponseInterface.ts b/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getSubDetailsResponseInterface.ts new file mode 100644 index 00000000..dbfb695d --- /dev/null +++ b/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getSubDetailsResponseInterface.ts @@ -0,0 +1,12 @@ +export interface Subscription { + 'service-type': string; + 'is-permitted': boolean; +} + +interface ServiceSubscriptions { + 'service-subscription': Subscription[]; +} + +export interface GetSubDetailsResponse { + 'service-subscriptions': ServiceSubscriptions; +} diff --git a/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getSubscribersResponseInterface.ts b/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getSubscribersResponseInterface.ts new file mode 100644 index 00000000..065f66e2 --- /dev/null +++ b/vid-webpack-master/src/app/services/aaiService/responseInterfaces/getSubscribersResponseInterface.ts @@ -0,0 +1,5 @@ +import {Subscriber} from "../../../shared/models/subscriber"; + +export interface GetSubscribersResponse { + customer: Subscriber[]; +} diff --git a/vid-webpack-master/src/app/services/configuration.service.ts b/vid-webpack-master/src/app/services/configuration.service.ts new file mode 100644 index 00000000..4edd8ffb --- /dev/null +++ b/vid-webpack-master/src/app/services/configuration.service.ts @@ -0,0 +1,34 @@ +import {Injectable} from '@angular/core'; +import {HttpClient} from "@angular/common/http"; +import {Constants} from "../shared/utils/constants"; +import {Observable} from 'rxjs/Observable'; +import {updateFlags} from "../global.actions"; +import {NgRedux} from "@angular-redux/store"; +import {AppState} from "../store/reducers"; + +@Injectable() +export class ConfigurationService { + store : NgRedux<AppState>; + + constructor(private _http: HttpClient, _store : NgRedux<AppState>) { + this.store = _store; + } + + getConfiguration(key : string): Observable<any> { + let pathQuery = Constants.Path.CONFIGURATION_PATH; + pathQuery = pathQuery.replace("{name}",key); + return this._http.get(pathQuery).map(response => response); + } + + getFlags(): Observable<{[key: string] : boolean}> { + let flags = this.store.getState().global.flags; + if (flags) { + return Observable.of(flags); + } + let pathQuery = Constants.Path.FEATURES_FLAG_PATH; + return this._http.get<{[key: string] : boolean}>(pathQuery).map(response => { + this.store.dispatch(updateFlags(response)); + return response; + }); + } +} diff --git a/vid-webpack-master/src/app/services/data.service.ts b/vid-webpack-master/src/app/services/data.service.ts new file mode 100644 index 00000000..4f8bf362 --- /dev/null +++ b/vid-webpack-master/src/app/services/data.service.ts @@ -0,0 +1,528 @@ +import { Injectable } from '@angular/core'; + +@Injectable() +export class DataService { + + private static _availableVolumeGroupList; + private static _cloudRegionTenantList; + private static _globalCustomerId; + private static _customizationUUID; + private static _rescustomizationUUID; + private static _inventoryItem; + private static _modelId; + private static _modelInstanceName; + private static _modelInfo; + private static _networkInstanceId; + private static _serviceIdList; + private static _aicZones; + private static _aicZone; + private static _serviceInstanceId; + private static _serviceInstanceName; + private static _serviceName; + private static _serviceType; + private static _serviceUuid; + private static _serviceTypeName; + private static _createSubscriberName; + private static _uploadSupplementoryDataFile; + private static _supplementoryDataFile; + private static _subscriberId; + private static _loggedInUserId; + private static _subscriberName; + private static _subscribers; + private static _subscriptionServiceTypeList; + private static _userParams; + private static _userServiceInstanceName; + private static _vfModuleInstanceId; + private static _vnfInstanceId; + private static _vfModuleInstanceName; + private static _volumeGroupInstanceId; + private static _lcpRegion; + private static _tenant; + private static _treeHandle; + private static _serviceInstanceToCustomer; + private static _aLaCarte: boolean; + private static _macro: boolean; + private static _resources; + private static _syspropProvStatusList; + private static _updatedvnfProvStatus; + private static _arbitraryParameters; + private static _hideServiceFields; + private static _serviceProxies; + private static _sourceServiceProxies; + private static _collectorServiceProxies; + private static _configurationByPolicy; + private static _suppressRollback; + private static _portMirroningConfigFields; + private static _configurationInstanceId: string; + private static _configurationStatus: string; + private static _portStatus: string; + private static _portId: string; + private static _pnf; + private static _owningEntityProperties; + + static get availableVolumeGroupList() { + return this._availableVolumeGroupList; + } + + static set availableVolumeGroupList(value) { + this._availableVolumeGroupList = value; + } + + static get cloudRegionTenantList() { + return this._cloudRegionTenantList; + } + + static set cloudRegionTenantList(value) { + this._cloudRegionTenantList = value; + } + + static get globalCustomerId() { + return this._globalCustomerId; + } + + static set globalCustomerId(value) { + this._globalCustomerId = value; + } + + static get customizationUUID() { + return this._customizationUUID; + } + + static set customizationUUID(value) { + this._customizationUUID = value; + } + + static get rescustomizationUUID() { + return this._rescustomizationUUID; + } + + static set rescustomizationUUID(value) { + this._rescustomizationUUID = value; + } + + static get inventoryItem() { + return this._inventoryItem; + } + + static set inventoryItem(value) { + this._inventoryItem = value; + } + + static get modelId() { + return this._modelId; + } + + static set modelId(value) { + this._modelId = value; + } + + static get modelInstanceName() { + return this._modelInstanceName; + } + + static set modelInstanceName(value) { + this._modelInstanceName = value; + } + + static get modelInfo() { + return this._modelInfo; + } + + static set modelInfo(value) { + this._modelInfo = value; + } + + static getModelInfo(componentId) { + return this._modelInfo[componentId]; + } + + static setModelInfo(componentId, modelInfo) { + if (!this._modelInfo) { + this._modelInfo = {}; + } + this._modelInfo[componentId] = modelInfo; + } + + static get networkInstanceId() { + return this._networkInstanceId; + } + + static set networkInstanceId(value) { + this._networkInstanceId = value; + } + + static get serviceIdList() { + return this._serviceIdList; + } + + static set serviceIdList(value) { + this._serviceIdList = value; + } + + static get aicZones() { + return this._aicZones; + } + + static set aicZones(value) { + this._aicZones = value; + } + + static get aicZone() { + return this._aicZone; + } + + static set aicZone(value) { + this._aicZone = value; + } + + static get serviceInstanceId() { + return this._serviceInstanceId; + } + + static set serviceInstanceId(value) { + this._serviceInstanceId = value; + } + + static get serviceInstanceName() { + return this._serviceInstanceName; + } + + static set serviceInstanceName(value) { + this._serviceInstanceName = value; + } + + static get serviceName() { + return this._serviceName; + } + + static set serviceName(value) { + this._serviceName = value; + } + + static get serviceType() { + return this._serviceType; + } + + static set serviceType(value) { + this._serviceType = value; + } + + static get serviceUuid() { + return this._serviceUuid; + } + + static set serviceUuid(value) { + this._serviceUuid = value; + } + + static get serviceTypeName() { + return this._serviceTypeName; + } + + static set serviceTypeName(value) { + this._serviceTypeName = value; + } + + static get createSubscriberName() { + return this._createSubscriberName; + } + + static set createSubscriberName(value) { + this._createSubscriberName = value; + } + + static get uploadSupplementoryDataFile() { + return this._uploadSupplementoryDataFile; + } + + static set uploadSupplementoryDataFile(value) { + this._uploadSupplementoryDataFile = value; + } + + static get supplementoryDataFile() { + return this._supplementoryDataFile; + } + + static set supplementoryDataFile(value) { + this._supplementoryDataFile = value; + } + + static get subscriberId() { + return this._subscriberId; + } + + static set subscriberId(value) { + this._subscriberId = value; + } + + static get loggedInUserId() { + return this._loggedInUserId; + } + + static set loggedInUserId(value) { + this._loggedInUserId = value; + } + + static get subscriberName() { + return this._subscriberName; + } + + static set subscriberName(value) { + this._subscriberName = value; + } + + static get subscribers() { + return this._subscribers; + } + + static set subscribers(value) { + this._subscribers = value; + } + + static get subscriptionServiceTypeList() { + return this._subscriptionServiceTypeList; + } + + static set subscriptionServiceTypeList(value) { + this._subscriptionServiceTypeList = value; + } + + static get userParams() { + return this._userParams; + } + + static set userParams(value) { + this._userParams = value; + } + + static get userServiceInstanceName() { + return this._userServiceInstanceName; + } + + static set userServiceInstanceName(value) { + this._userServiceInstanceName = value; + } + + static get vfModuleInstanceId() { + return this._vfModuleInstanceId; + } + + static set vfModuleInstanceId(value) { + this._vfModuleInstanceId = value; + } + + static get vnfInstanceId() { + return this._vnfInstanceId; + } + + static set vnfInstanceId(value) { + this._vnfInstanceId = value; + } + + static get vfModuleInstanceName() { + return this._vfModuleInstanceName; + } + + static set vfModuleInstanceName(value) { + this._vfModuleInstanceName = value; + } + + static get volumeGroupInstanceId() { + return this._volumeGroupInstanceId; + } + + static set volumeGroupInstanceId(value) { + this._volumeGroupInstanceId = value; + } + + static get lcpRegion() { + return this._lcpRegion; + } + + static set lcpRegion(value) { + this._lcpRegion = value; + } + + static get tenant() { + return this._tenant; + } + + static set tenant(value) { + this._tenant = value; + } + + static get treeHandle() { + return this._treeHandle; + } + + static set treeHandle(value) { + this._treeHandle = value; + } + + static get serviceInstanceToCustomer() { + return this._serviceInstanceToCustomer; + } + + static set serviceInstanceToCustomer(value) { + this._serviceInstanceToCustomer = value; + } + + static get aLaCarte() { + if (!this._aLaCarte) { + return true; + } + return this._aLaCarte; + } + + static set aLaCarte(value: boolean) { + this._aLaCarte = value; + } + + static get macro() { + if (!this._macro) { + return false; + } + return this._macro; + } + + static set macro(value: boolean) { + this._macro = value; + } + + static get resources() { + return this._resources; + } + + static set resources(value) { + this._resources = value; + } + + static get syspropProvStatusList() { + return this._syspropProvStatusList; + } + + static set syspropProvStatusList(value) { + this._syspropProvStatusList = value; + } + + static get updatedvnfProvStatus() { + return this._updatedvnfProvStatus; + } + + static set updatedvnfProvStatus(value) { + this._updatedvnfProvStatus = value; + } + + static get arbitraryParameters() { + return this._arbitraryParameters; + } + + static set arbitraryParameters(value) { + this._arbitraryParameters = value; + } + + static get hideServiceFields() { + return this._hideServiceFields; + } + + static set hideServiceFields(value) { + this._hideServiceFields = value; + } + + static get serviceProxies() { + return this._serviceProxies; + } + + static set serviceProxies(value) { + this._serviceProxies = value; + } + + static get sourceServiceProxies() { + return this._sourceServiceProxies; + } + + static set sourceServiceProxies(value) { + this._sourceServiceProxies = value; + } + + static get collectorServiceProxies() { + return this._collectorServiceProxies; + } + + static set collectorServiceProxies(value) { + this._collectorServiceProxies = value; + } + + static get configurationByPolicy() { + return this._configurationByPolicy; + } + + static set configurationByPolicy(value) { + this._configurationByPolicy = value; + } + + static get suppressRollback() { + return this._suppressRollback; + } + + static set suppressRollback(value) { + this._suppressRollback = value; + } + + static get portMirroningConfigFields() { + return this._portMirroningConfigFields; + } + + static set portMirroningConfigFields(value) { + this._portMirroningConfigFields = value; + } + + static get configurationInstanceId(): string { + return this._configurationInstanceId; + } + + static set configurationInstanceId(value: string) { + this._configurationInstanceId = value; + } + + static get configurationStatus(): string { + return this._configurationStatus; + } + + static set configurationStatus(value: string) { + this._configurationStatus = value; + } + + static get portStatus(): string { + return this._portStatus; + } + + static set portStatus(value: string) { + this._portStatus = value; + } + + static get portId(): string { + return this._portId; + } + + static set portId(value: string) { + this._portId = value; + } + + static get pnf() { + return this._pnf; + } + + static set pnf(value) { + this._pnf = value; + } + + static get owningEntityProperties() { + return this._owningEntityProperties; + } + + static set owningEntityProperties(value) { + this._owningEntityProperties = value; + } + +} diff --git a/vid-webpack-master/src/app/services/flags.resolve.ts b/vid-webpack-master/src/app/services/flags.resolve.ts new file mode 100644 index 00000000..70449379 --- /dev/null +++ b/vid-webpack-master/src/app/services/flags.resolve.ts @@ -0,0 +1,14 @@ +import {ActivatedRouteSnapshot, Resolve} from "@angular/router"; +import {Injectable} from "@angular/core"; +import {ConfigurationService} from "./configuration.service"; +import {Observable} from "rxjs/Observable"; + +@Injectable() +export class FlagsResolve implements Resolve<Observable< { [key: string]: boolean }>> { + + constructor(private _configurationService: ConfigurationService) {} + + resolve(route: ActivatedRouteSnapshot) { + return this._configurationService.getFlags(); + } +} diff --git a/vid-webpack-master/src/app/services/msoService/mso.service.ts b/vid-webpack-master/src/app/services/msoService/mso.service.ts new file mode 100644 index 00000000..1402b50f --- /dev/null +++ b/vid-webpack-master/src/app/services/msoService/mso.service.ts @@ -0,0 +1,26 @@ +import {Injectable} from "@angular/core"; +import {HttpClient} from "@angular/common/http"; +import {Observable} from "rxjs/Observable"; +import {Constants} from "../../shared/utils/constants"; + +@Injectable() +export class MsoService { + httpClient: HttpClient; + + constructor(http: HttpClient) { + this.httpClient = http; + } + + + public submitMsoTask(instanceFields): Observable<any> { + let path = '../../asyncInstantiation/bulk'; + return this.httpClient.post(path, instanceFields); + } + public createVnf(requestDetails, serviceInstanceId): Observable<any> { + let pathQuery: string = Constants.Path.MSO_CREATE_VNF_INSTANCE + serviceInstanceId; + + return this.httpClient.post( pathQuery, { + requestDetails : requestDetails + }); + } +} diff --git a/vid-webpack-master/src/app/services/sdc.service.ts b/vid-webpack-master/src/app/services/sdc.service.ts new file mode 100644 index 00000000..d4eba260 --- /dev/null +++ b/vid-webpack-master/src/app/services/sdc.service.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import 'rxjs/add/operator/map'; +import { Constants } from '../shared/utils/constants'; +import { VidConfiguration } from '../configuration/vid.configuration'; + +@Injectable() +export class SdcService { + + + constructor (private http: HttpClient) { + } + + public getServicesModels(): any { + let pathQuery: string = Constants.Path.SERVICES_DIST_STATUS_PATH + VidConfiguration.ASDC_MODEL_STATUS; + + if ( VidConfiguration.ASDC_MODEL_STATUS === Constants.Status.ALL) { + pathQuery = Constants.Path.SERVICES_PATH; + } + + return this.http.get(pathQuery); + } + + getService(uuid: string) { + return this.http.get(Constants.Path.SERVICES_PATH + uuid); + } + +} diff --git a/vid-webpack-master/src/app/services/service-planning.service.spec.ts b/vid-webpack-master/src/app/services/service-planning.service.spec.ts new file mode 100644 index 00000000..5d60e29f --- /dev/null +++ b/vid-webpack-master/src/app/services/service-planning.service.spec.ts @@ -0,0 +1,467 @@ +import {ServicePlanningService} from "./service-planning.service"; +import {ReflectiveInjector} from "@angular/core"; +import {NgRedux} from "@angular-redux/store"; +import {ServiceNodeTypes} from "../shared/models/ServiceNodeTypes"; + +export class MockAppStore<T> { +} + +describe('Service planning service', () => { + let injector; + let service: ServicePlanningService; + + beforeEach(() => { + + let injector = ReflectiveInjector.resolveAndCreate([ + ServicePlanningService, + {provide: NgRedux, useClass: MockAppStore} + ]); + + service = injector.get(ServicePlanningService); + }); + + describe('#updateDynamicInputsVnfDataFromModel', () => { + it('get vfModule instance params', (done: DoneFn) => { + //get vfModule instance params + let dynamicInputs = service.updateDynamicInputsVnfDataFromModel(ServiceNodeTypes.VFmodule, generateVFModule()); + expect(dynamicInputs).toEqual([{ + id: '2017488_adiodvpe0_vnf_config_template_version', + type: 'string', + name: '2017488_adiodvpe0_vnf_config_template_version', + value: '17.2', + isRequired: true, + description: 'VPE Software Version' + }, { + id: '2017488_adiodvpe0_AIC_CLLI', + type: 'string', + name: '2017488_adiodvpe0_AIC_CLLI', + value: 'ATLMY8GA', + isRequired: true, + description: 'AIC Site CLLI' + }]); + + //get vfModule with no instance params should return empty array + dynamicInputs = service.updateDynamicInputsVnfDataFromModel(ServiceNodeTypes.VFmodule, generateVFModule2); + expect(dynamicInputs).toEqual([]); + + //get vf instance params should be undefined + dynamicInputs = service.updateDynamicInputsVnfDataFromModel(ServiceNodeTypes.VF, generateVNF()); + expect(dynamicInputs).toEqual([]); + done(); + }); + }); + + describe('#isUserProvidedNaming', () => { + it('get vfModule with generate ecompNaming should return userProvided false', (done: DoneFn) => { + //get vfModule with generate ecompNaming should return userProvided false + let isUserProvidedNaming = service.isUserProvidedNaming(ServiceNodeTypes.VFmodule, generateVFModule(), generateVNF()); + expect(isUserProvidedNaming).toBeFalsy(); + + //get vfModule without generate ecompNaming should return userProvided true + isUserProvidedNaming = service.isUserProvidedNaming(ServiceNodeTypes.VFmodule, generateVFModule(), generateVNF_ecompNamingFalse()); + expect(isUserProvidedNaming).toBeTruthy(); + + //get vnf with generate ecompNaming should return userProvided false + isUserProvidedNaming = service.isUserProvidedNaming(ServiceNodeTypes.VF, generateVNF(), null); + expect(isUserProvidedNaming).toBeFalsy(); + + //get vnf without generate ecompNaming should return userProvided true + isUserProvidedNaming = service.isUserProvidedNaming(ServiceNodeTypes.VF, generateVNF_ecompNamingFalse(), null); + expect(isUserProvidedNaming).toBeTruthy(); + done(); + }); + }); + + function generateVFModule() { + return { + 'uuid': '25284168-24bb-4698-8cb4-3f509146eca5', + 'invariantUuid': '7253ff5c-97f0-4b8b-937c-77aeb4d79aa1', + 'customizationUuid': 'f7e7c365-60cf-49a9-9ebf-a1aa11b9d401', + 'description': null, + 'name': '2017488AdiodVpe..ADIOD_vRE_BV..module-1', + 'version': '6', + 'modelCustomizationName': '2017488AdiodVpe..ADIOD_vRE_BV..module-1', + 'properties': {'minCountInstances': 0, 'maxCountInstances': null, 'initialCount': 0}, + 'commands': {}, + 'volumeGroupAllowed': true, + 'inputs': { + '2017488_adiodvpe0_vnf_config_template_version': { + 'type': 'string', + 'description': 'VPE Software Version', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': '17.2' + }, + '2017488_adiodvpe0_AIC_CLLI': { + 'type': 'string', + 'description': 'AIC Site CLLI', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'ATLMY8GA' + } + } + }; + } + + function generateVFModule2() { + return { + 'uuid': '0a0dd9d4-31d3-4c3a-ae89-a02f383e6a9a', + 'invariantUuid': 'eff8cc59-53a1-4101-aed7-8cf24ecf8339', + 'customizationUuid': '3cd946bb-50e0-40d8-96d3-c9023520b557', + 'description': null, + 'name': '2017488AdiodVpe..ADIOD_vPFE_BV..module-2', + 'version': '6', + 'modelCustomizationName': '2017488AdiodVpe..ADIOD_vPFE_BV..module-2', + 'properties': {'minCountInstances': 0, 'maxCountInstances': null, 'initialCount': 0}, + 'commands': {}, + 'volumeGroupAllowed': true, + 'inputs': {} + }; + } + + function generateVNF() { + return { + 'uuid': '0903e1c0-8e03-4936-b5c2-260653b96413', + 'invariantUuid': '00beb8f9-6d39-452f-816d-c709b9cbb87d', + 'description': 'Name ADIOD vPE Description The provider edge function for the ADIOD service supported by the Junipers VMX product Category Router Vendor Juniper Vendor Release Code 17.2 Owners Mary Fragale. Updated 9-25 to use v8.0 of the Juniper Valid 2 VLM', + 'name': '2017-388_ADIOD-vPE', + 'version': '1.0', + 'customizationUuid': '280dec31-f16d-488b-9668-4aae55d6648a', + 'inputs': { + 'vnf_config_template_version': { + 'type': 'string', + 'description': 'VPE Software Version', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': '17.2' + }, + 'bandwidth_units': { + 'type': 'string', + 'description': 'Units of bandwidth', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'Gbps' + }, + 'bandwidth': { + 'type': 'string', + 'description': 'Requested VPE bandwidth', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': '10' + }, + 'AIC_CLLI': { + 'type': 'string', + 'description': 'AIC Site CLLI', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'ATLMY8GA' + }, + 'ASN': { + 'type': 'string', + 'description': 'AV/PE', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'AV_vPE' + }, + 'vnf_instance_name': { + 'type': 'string', + 'description': 'The hostname assigned to the vpe.', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'mtnj309me6' + } + }, + 'commands': { + 'vnf_config_template_version': { + 'displayName': 'vnf_config_template_version', + 'command': 'get_input', + 'inputName': '2017488_adiodvpe0_vnf_config_template_version' + }, + 'bandwidth_units': { + 'displayName': 'bandwidth_units', + 'command': 'get_input', + 'inputName': 'adiodvpe0_bandwidth_units' + }, + 'bandwidth': {'displayName': 'bandwidth', 'command': 'get_input', 'inputName': 'adiodvpe0_bandwidth'}, + 'AIC_CLLI': {'displayName': 'AIC_CLLI', 'command': 'get_input', 'inputName': '2017488_adiodvpe0_AIC_CLLI'}, + 'ASN': {'displayName': 'ASN', 'command': 'get_input', 'inputName': '2017488_adiodvpe0_ASN'}, + 'vnf_instance_name': { + 'displayName': 'vnf_instance_name', + 'command': 'get_input', + 'inputName': '2017488_adiodvpe0_vnf_instance_name' + } + }, + 'properties': { + 'vmxvre_retype': 'RE-VMX', + 'vnf_config_template_version': 'get_input:2017488_adiodvpe0_vnf_config_template_version', + 'sriov44_net_id': '48d399b3-11ee-48a8-94d2-f0ea94d6be8d', + 'int_ctl_net_id': '2f323477-6936-4d01-ac53-d849430281d9', + 'vmxvpfe_sriov41_0_port_mac': '00:11:22:EF:AC:DF', + 'int_ctl_net_name': 'VMX-INTXI', + 'vmx_int_ctl_prefix': '128.0.0.0', + 'sriov43_net_id': 'da349ca1-6de9-4548-be88-2d88e99bfef5', + 'sriov42_net_id': '760669ba-013d-4d9b-b0e7-4151fe2e6279', + 'sriov41_net_id': '25ad52d5-c165-40f8-b3b0-ddfc2373280a', + 'nf_type': 'vPE', + 'vmxvpfe_int_ctl_ip_1': '128.0.0.16', + 'is_AVPN_service': 'false', + 'vmx_RSG_name': 'vREXI-affinity', + 'vmx_int_ctl_forwarding': 'l2', + 'vmxvre_oam_ip_0': '10.40.123.5', + 'vmxvpfe_sriov44_0_port_mac': '00:11:22:EF:AC:DF', + 'vmxvpfe_sriov41_0_port_vlanstrip': 'false', + 'vmxvpfe_sriov42_0_port_vlanfilter': '4001', + 'vmxvpfe_sriov44_0_port_unknownunicastallow': 'true', + 'vmxvre_image_name_0': 'VRE-ENGINE_17.2-S2.1.qcow2', + 'vmxvre_instance': '0', + 'vmxvpfe_sriov43_0_port_mac': '00:11:22:EF:AC:DF', + 'vmxvre_flavor_name': 'ns.c1r16d32.v5', + 'vmxvpfe_volume_size_0': '40.0', + 'vmxvpfe_sriov43_0_port_vlanfilter': '4001', + 'nf_naming': '{ecomp_generated_naming=true}', + 'nf_naming_code': 'Navneet', + 'vmxvre_name_0': 'vREXI', + 'vmxvpfe_sriov42_0_port_vlanstrip': 'false', + 'vmxvpfe_volume_name_0': 'vPFEXI_FBVolume', + 'vmx_RSG_id': 'bd89a33c-13c3-4a04-8fde-1a57eb123141', + 'vmxvpfe_image_name_0': 'VPE_ROUTING-ENGINE_17.2R1-S2.1.qcow2', + 'vmxvpfe_sriov43_0_port_unknownunicastallow': 'true', + 'vmxvpfe_sriov44_0_port_unknownmulticastallow': 'true', + 'vmxvre_console': 'vidconsole', + 'vmxvpfe_sriov44_0_port_vlanfilter': '4001', + 'vmxvpfe_sriov42_0_port_mac': '00:11:22:EF:AC:DF', + 'vmxvpfe_volume_id_0': '47cede15-da2f-4397-a101-aa683220aff3', + 'vmxvpfe_sriov42_0_port_unknownmulticastallow': 'true', + 'vmxvpfe_sriov44_0_port_vlanstrip': 'false', + 'vf_module_id': '123', + 'nf_function': 'JAI', + 'vmxvpfe_sriov43_0_port_unknownmulticastallow': 'true', + 'vmxvre_int_ctl_ip_0': '128.0.0.1', + 'AIC_CLLI': 'get_input:2017488_adiodvpe0_AIC_CLLI', + 'vnf_name': 'mtnj309me6vre', + 'vmxvpfe_sriov41_0_port_unknownunicastallow': 'true', + 'vmxvre_volume_type_1': 'HITACHI', + 'vmxvpfe_sriov44_0_port_broadcastallow': 'true', + 'vmxvre_volume_type_0': 'HITACHI', + 'vmxvpfe_volume_type_0': 'HITACHI', + 'vmxvpfe_sriov43_0_port_broadcastallow': 'true', + 'bandwidth_units': 'get_input:adiodvpe0_bandwidth_units', + 'vnf_id': '123', + 'vmxvre_oam_prefix': '24', + 'availability_zone_0': 'mtpocfo-kvm-az01', + 'ASN': 'get_input:2017488_adiodvpe0_ASN', + 'vmxvre_chassis_i2cid': '161', + 'vmxvpfe_name_0': 'vPFEXI', + 'bandwidth': 'get_input:adiodvpe0_bandwidth', + 'availability_zone_max_count': '1', + 'vmxvre_volume_size_0': '45.0', + 'vmxvre_volume_size_1': '50.0', + 'vmxvpfe_sriov42_0_port_broadcastallow': 'true', + 'vmxvre_oam_gateway': '10.40.123.1', + 'vmxvre_volume_name_1': 'vREXI_FAVolume', + 'vmxvre_ore_present': '0', + 'vmxvre_volume_name_0': 'vREXI_FBVolume', + 'vmxvre_type': '0', + 'vnf_instance_name': 'get_input:2017488_adiodvpe0_vnf_instance_name', + 'vmxvpfe_sriov41_0_port_unknownmulticastallow': 'true', + 'oam_net_id': 'b95eeb1d-d55d-4827-abb4-8ebb94941429', + 'vmx_int_ctl_len': '24', + 'vmxvpfe_sriov43_0_port_vlanstrip': 'false', + 'vmxvpfe_sriov41_0_port_broadcastallow': 'true', + 'vmxvre_volume_id_1': '6e86797e-03cd-4fdc-ba72-2957119c746d', + 'vmxvpfe_sriov41_0_port_vlanfilter': '4001', + 'nf_role': 'Testing', + 'vmxvre_volume_id_0': 'f4eacb79-f687-4e9d-b760-21847c8bb15a', + 'vmxvpfe_sriov42_0_port_unknownunicastallow': 'true', + 'vmxvpfe_flavor_name': 'ns.c20r16d25.v5' + }, + 'type': 'VF', + 'modelCustomizationName': '2017-388_ADIOD-vPE 1', + 'vfModules': {}, + 'volumeGroups': {} + }; + } + + function generateVNF_ecompNamingFalse() { + return { + 'uuid': '0903e1c0-8e03-4936-b5c2-260653b96413', + 'invariantUuid': '00beb8f9-6d39-452f-816d-c709b9cbb87d', + 'description': 'Name ADIOD vPE Description The provider edge function for the ADIOD service supported by the Junipers VMX product Category Router Vendor Juniper Vendor Release Code 17.2 Owners Mary Fragale. Updated 9-25 to use v8.0 of the Juniper Valid 2 VLM', + 'name': '2017-388_ADIOD-vPE', + 'version': '1.0', + 'customizationUuid': '280dec31-f16d-488b-9668-4aae55d6648a', + 'inputs': { + 'vnf_config_template_version': { + 'type': 'string', + 'description': 'VPE Software Version', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': '17.2' + }, + 'bandwidth_units': { + 'type': 'string', + 'description': 'Units of bandwidth', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'Gbps' + }, + 'bandwidth': { + 'type': 'string', + 'description': 'Requested VPE bandwidth', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': '10' + }, + 'AIC_CLLI': { + 'type': 'string', + 'description': 'AIC Site CLLI', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'ATLMY8GA' + }, + 'ASN': { + 'type': 'string', + 'description': 'AV/PE', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'AV_vPE' + }, + 'vnf_instance_name': { + 'type': 'string', + 'description': 'The hostname assigned to the vpe.', + 'entry_schema': null, + 'constraints': [], + 'required': true, + 'default': 'mtnj309me6' + } + }, + 'commands': { + 'vnf_config_template_version': { + 'displayName': 'vnf_config_template_version', + 'command': 'get_input', + 'inputName': '2017488_adiodvpe0_vnf_config_template_version' + }, + 'bandwidth_units': { + 'displayName': 'bandwidth_units', + 'command': 'get_input', + 'inputName': 'adiodvpe0_bandwidth_units' + }, + 'bandwidth': {'displayName': 'bandwidth', 'command': 'get_input', 'inputName': 'adiodvpe0_bandwidth'}, + 'AIC_CLLI': {'displayName': 'AIC_CLLI', 'command': 'get_input', 'inputName': '2017488_adiodvpe0_AIC_CLLI'}, + 'ASN': {'displayName': 'ASN', 'command': 'get_input', 'inputName': '2017488_adiodvpe0_ASN'}, + 'vnf_instance_name': { + 'displayName': 'vnf_instance_name', + 'command': 'get_input', + 'inputName': '2017488_adiodvpe0_vnf_instance_name' + } + }, + 'properties': { + 'ecomp_generated_naming': "false", + 'vmxvre_retype': 'RE-VMX', + 'vnf_config_template_version': 'get_input:2017488_adiodvpe0_vnf_config_template_version', + 'sriov44_net_id': '48d399b3-11ee-48a8-94d2-f0ea94d6be8d', + 'int_ctl_net_id': '2f323477-6936-4d01-ac53-d849430281d9', + 'vmxvpfe_sriov41_0_port_mac': '00:11:22:EF:AC:DF', + 'int_ctl_net_name': 'VMX-INTXI', + 'vmx_int_ctl_prefix': '128.0.0.0', + 'sriov43_net_id': 'da349ca1-6de9-4548-be88-2d88e99bfef5', + 'sriov42_net_id': '760669ba-013d-4d9b-b0e7-4151fe2e6279', + 'sriov41_net_id': '25ad52d5-c165-40f8-b3b0-ddfc2373280a', + 'nf_type': 'vPE', + 'vmxvpfe_int_ctl_ip_1': '128.0.0.16', + 'is_AVPN_service': 'false', + 'vmx_RSG_name': 'vREXI-affinity', + 'vmx_int_ctl_forwarding': 'l2', + 'vmxvre_oam_ip_0': '10.40.123.5', + 'vmxvpfe_sriov44_0_port_mac': '00:11:22:EF:AC:DF', + 'vmxvpfe_sriov41_0_port_vlanstrip': 'false', + 'vmxvpfe_sriov42_0_port_vlanfilter': '4001', + 'vmxvpfe_sriov44_0_port_unknownunicastallow': 'true', + 'vmxvre_image_name_0': 'VRE-ENGINE_17.2-S2.1.qcow2', + 'vmxvre_instance': '0', + 'vmxvpfe_sriov43_0_port_mac': '00:11:22:EF:AC:DF', + 'vmxvre_flavor_name': 'ns.c1r16d32.v5', + 'vmxvpfe_volume_size_0': '40.0', + 'vmxvpfe_sriov43_0_port_vlanfilter': '4001', + 'nf_naming': '{ecomp_generated_naming=false}', + 'nf_naming_code': 'Navneet', + 'vmxvre_name_0': 'vREXI', + 'vmxvpfe_sriov42_0_port_vlanstrip': 'false', + 'vmxvpfe_volume_name_0': 'vPFEXI_FBVolume', + 'vmx_RSG_id': 'bd89a33c-13c3-4a04-8fde-1a57eb123141', + 'vmxvpfe_image_name_0': 'VPE_ROUTING-ENGINE_17.2R1-S2.1.qcow2', + 'vmxvpfe_sriov43_0_port_unknownunicastallow': 'true', + 'vmxvpfe_sriov44_0_port_unknownmulticastallow': 'true', + 'vmxvre_console': 'vidconsole', + 'vmxvpfe_sriov44_0_port_vlanfilter': '4001', + 'vmxvpfe_sriov42_0_port_mac': '00:11:22:EF:AC:DF', + 'vmxvpfe_volume_id_0': '47cede15-da2f-4397-a101-aa683220aff3', + 'vmxvpfe_sriov42_0_port_unknownmulticastallow': 'true', + 'vmxvpfe_sriov44_0_port_vlanstrip': 'false', + 'vf_module_id': '123', + 'nf_function': 'JAI', + 'vmxvpfe_sriov43_0_port_unknownmulticastallow': 'true', + 'vmxvre_int_ctl_ip_0': '128.0.0.1', + 'AIC_CLLI': 'get_input:2017488_adiodvpe0_AIC_CLLI', + 'vnf_name': 'mtnj309me6vre', + 'vmxvpfe_sriov41_0_port_unknownunicastallow': 'true', + 'vmxvre_volume_type_1': 'HITACHI', + 'vmxvpfe_sriov44_0_port_broadcastallow': 'true', + 'vmxvre_volume_type_0': 'HITACHI', + 'vmxvpfe_volume_type_0': 'HITACHI', + 'vmxvpfe_sriov43_0_port_broadcastallow': 'true', + 'bandwidth_units': 'get_input:adiodvpe0_bandwidth_units', + 'vnf_id': '123', + 'vmxvre_oam_prefix': '24', + 'availability_zone_0': 'mtpocfo-kvm-az01', + 'ASN': 'get_input:2017488_adiodvpe0_ASN', + 'vmxvre_chassis_i2cid': '161', + 'vmxvpfe_name_0': 'vPFEXI', + 'bandwidth': 'get_input:adiodvpe0_bandwidth', + 'availability_zone_max_count': '1', + 'vmxvre_volume_size_0': '45.0', + 'vmxvre_volume_size_1': '50.0', + 'vmxvpfe_sriov42_0_port_broadcastallow': 'true', + 'vmxvre_oam_gateway': '10.40.123.1', + 'vmxvre_volume_name_1': 'vREXI_FAVolume', + 'vmxvre_ore_present': '0', + 'vmxvre_volume_name_0': 'vREXI_FBVolume', + 'vmxvre_type': '0', + 'vnf_instance_name': 'get_input:2017488_adiodvpe0_vnf_instance_name', + 'vmxvpfe_sriov41_0_port_unknownmulticastallow': 'true', + 'oam_net_id': 'b95eeb1d-d55d-4827-abb4-8ebb94941429', + 'vmx_int_ctl_len': '24', + 'vmxvpfe_sriov43_0_port_vlanstrip': 'false', + 'vmxvpfe_sriov41_0_port_broadcastallow': 'true', + 'vmxvre_volume_id_1': '6e86797e-03cd-4fdc-ba72-2957119c746d', + 'vmxvpfe_sriov41_0_port_vlanfilter': '4001', + 'nf_role': 'Testing', + 'vmxvre_volume_id_0': 'f4eacb79-f687-4e9d-b760-21847c8bb15a', + 'vmxvpfe_sriov42_0_port_unknownunicastallow': 'true', + 'vmxvpfe_flavor_name': 'ns.c20r16d25.v5' + }, + 'type': 'VF', + 'modelCustomizationName': '2017-388_ADIOD-vPE 1', + 'vfModules': {}, + 'volumeGroups': {} + }; + } + +}); + + + + diff --git a/vid-webpack-master/src/app/services/service-planning.service.ts b/vid-webpack-master/src/app/services/service-planning.service.ts new file mode 100644 index 00000000..75720c8a --- /dev/null +++ b/vid-webpack-master/src/app/services/service-planning.service.ts @@ -0,0 +1,282 @@ +import {Injectable} from '@angular/core'; +import {Constants} from "../shared/utils/constants"; +import {Utils} from "../utils/utils"; +import * as _ from 'lodash'; +import Parameter = Constants.Parameter; +import {ITreeNode} from "angular-tree-component/dist/defs/api"; +import {ServiceInstance} from "../shared/models/serviceInstance"; +import {VNFModel} from "../shared/models/vnfModel"; +import {ServiceNodeTypes} from "../shared/models/ServiceNodeTypes"; +import {VfModuleMap} from "../shared/models/vfModulesMap"; +import {VnfInstance} from "../shared/models/vnfInstance"; +import {VfModuleTreeNode} from "../shared/models/vfModuleTreeNode"; +import {VfModule} from "../shared/models/vfModule"; +import {VnfTreeNode} from "../shared/models/vnfTreeNode"; +import {NgRedux} from "@angular-redux/store"; +import {AppState} from "../store/reducers"; +import {InputType} from "../shared/models/inputTypes"; + + +@Injectable() +export class ServicePlanningService { + + modelDataTree: any[] = []; + drawingDataTree: any[] = []; + service: any = {name:'My Service'} ; + public requiredFields = { + VF: [InputType.LCP_REGION, InputType.TENANT, InputType.PLATFORM], + VFmodule: [] + }; + + constructor(private store: NgRedux<AppState>) {} + + + public getServiceName() :string{ + return this.service.name; + } + + public getServiceInstance(serviceModelId): ServiceInstance { + return this.store.getState().service.serviceInstance[serviceModelId]; + } + + public getVnfInstance(serviceModelId, vnfModelName): VnfInstance { + return this.getServiceInstance(serviceModelId).vnfs[vnfModelName]; + } + + public getVfModuleMap(serviceModelId, vnfModelName, vfModuleModelName): VfModuleMap { + let vnfInstance = this.getVnfInstance(serviceModelId, vnfModelName); + return _.get(vnfInstance, ['vfModules', vfModuleModelName]); + } + + public convertServiceModelToTreeNodes(serviceModel) { + let _this = this; + + _.forOwn(serviceModel.vnfs, function(item, key) { + _this.addFirstLevelModel(key, item, item.type, serviceModel); + }); + + _.forOwn(serviceModel.configurations, function(item, key) { + _this.addFirstLevelModel(key, item, ServiceNodeTypes.Configuration, serviceModel); + }); + + _.forOwn(serviceModel.networks, function(network, key) { + _this.addFirstLevelModel(key, network, ServiceNodeTypes.Network, serviceModel); + }); + + return this.modelDataTree; + } + + private addFirstLevelModel(key, value, valueType, serviceModel) { + + let node = this.convertItemToTreeNode(key, value, valueType, null, false); + let vnfInstance = this.getVnfInstance(serviceModel.service.uuid, key); + if(value.vfModules) { + node.children = Object.keys(value.vfModules).map((vmKey) => + this.convertItemToTreeNode(vmKey, value.vfModules[vmKey], ServiceNodeTypes.VFmodule, value, !vnfInstance)); + } + this.modelDataTree.push(node); + } + + private convertItemToTreeNode(key, value, valueType, parentModel , disabled) { + + return { + id: value.uuid, + name: key, + tooltip: valueType, + type: valueType, + count: value.count || 0, + max: value.max || 1, + children: [], + disabled: disabled, + dynamicInputs: this.updateDynamicInputsVnfDataFromModel(valueType, value), + userProvidedNaming: this.isUserProvidedNaming(valueType, value, parentModel) + } + } + + public convertServiceInstanceToTreeData(serviceInstance: ServiceInstance, modelId: string): any { + let drawingBoardData = []; + let _this = this; + _.forOwn(serviceInstance.vnfs, (vnfInstance, vnfModelName) => { + let vnfModel: VNFModel = _this.store.getState().service.serviceHierarchy[modelId].vnfs[vnfModelName]; + let vnfNode = new VnfTreeNode(vnfInstance, vnfModel); + + let vfModuleNodes = []; + _.forOwn(vnfInstance.vfModules, (vfModuleMap, vfModuleModelName) => { + _.forOwn(vfModuleMap, (vfModuleInstance, vfModuleInstsanceName) => { + let vfModule: VfModule = _this.store.getState().service.serviceHierarchy[modelId].vnfs[vnfModelName].vfModules[vfModuleModelName]; + let vfModuleNode: VfModuleTreeNode = new VfModuleTreeNode(vfModuleInstance, vfModule, vfModuleModelName); + vfModuleNodes.push(vfModuleNode); + }); + }); + vnfNode.children = vfModuleNodes; + drawingBoardData.push(vnfNode); + }); + + return drawingBoardData; + } + + public getArbitraryInputs(inputs) { + let parameter; + let parameterList = []; + for (let key in inputs) { + parameter = { + id : key, + type : Parameter.STRING, + name : key, + value : inputs[key][Parameter.DEFAULT], + isRequired : inputs[key][Parameter.REQUIRED], + description : inputs[key][Parameter.DESCRIPTION] + }; + switch (inputs[key][Parameter.TYPE]) { + case Parameter.INTEGER: + parameter.type = Parameter.NUMBER; + break; + case Parameter.BOOLEAN: + parameter.type = Parameter.BOOLEAN; + break; + case Parameter.RANGE: + break; + case Parameter.LIST: + parameter.type = Parameter.LIST; + break; + case Parameter.MAP: + parameter.type = Parameter.MAP; + break; + } + if ( Utils.hasContents(inputs[key][Parameter.CONSTRAINTS]) + && ( inputs[key][Parameter.CONSTRAINTS].length > 0 ) ) { + let constraintsArray = inputs[key][Parameter.CONSTRAINTS]; + this.addConstraintParameters (parameterList, constraintsArray, key, inputs, parameter); + } + else { + + parameterList.push(parameter); + } + } + return parameterList; + } + + private addConstraintParameters(parameterList, constraintsArray, key, inputs, parameter) { + // If there are constraints and the operator is "valid_values", + // use a select parameter type. + let i:number = constraintsArray.length; + let parameterPushed: boolean = false; + if ( i > 0 ) { + while ( (i--) && (!parameterPushed) ) { + let keys = Object.keys(constraintsArray[i]); + for ( let operator in keys ) { + switch (keys[operator]) { + case Parameter.VALID_VALUES: + let j: number = constraintsArray[i][Parameter.VALID_VALUES].length; + if ( j > 0 ) { + let oList = []; + let option; + while (j--) { + option = { + name: constraintsArray[i][Parameter.VALID_VALUES][j], + isDefault: false + }; + if ( (Utils.hasContents (inputs[key][Parameter.DEFAULT]) ) + && (inputs[key][Parameter.DEFAULT] === constraintsArray[i][Parameter.VALID_VALUES][j] ) ) { + option = { + name: constraintsArray[i][Parameter.VALID_VALUES][j], + isDefault: true + } + } + oList.push(option); + } + parameter.type = Parameter.SELECT; + parameter.optionList = oList; + parameterList.push(parameter); + parameterPushed = true; + } + break; + + case Parameter.EQUAL: + if ( constraintsArray[i][Parameter.EQUAL] != null ) { + //override parameter type + parameter.type = Parameter.STRING; + parameter.isReadOnly = true; + parameter.value = constraintsArray[i][Parameter.EQUAL]; + parameterList.push(parameter); + parameterPushed = true; + } + break; + + case Parameter.LENGTH: + if ( constraintsArray[i][Parameter.LENGTH] != null ) { + parameter.minLength = constraintsArray[i][Parameter.LENGTH]; + parameter.maxLength = constraintsArray[i][Parameter.LENGTH]; + parameterList.push(parameter); + parameterPushed = true; + } + break; + case Parameter.MAX_LENGTH: + if ( constraintsArray[i][Parameter.MAX_LENGTH] != null ) { + parameter.maxLength = constraintsArray[i][Parameter.MAX_LENGTH]; + parameterList.push(parameter); + parameterPushed = true; + } + break; + case Parameter.MIN_LENGTH: + if ( constraintsArray[i][Parameter.MIN_LENGTH] != null ) { + parameter.minLength = constraintsArray[i][Parameter.MIN_LENGTH]; + parameterList.push(parameter); + parameterPushed = true; + } + break; + case Parameter.IN_RANGE: + if ( constraintsArray[i][Parameter.IN_RANGE] != null ) { + if (constraintsArray[i][Parameter.IN_RANGE].length > 1 ) { + parameter.min = constraintsArray[i][Parameter.IN_RANGE][0]; + parameter.max = constraintsArray[i][Parameter.IN_RANGE][1]; + parameter.type = Parameter.NUMBER; + parameter.value = inputs[key][Parameter.DEFAULT]; + parameterList.push(parameter); + parameterPushed = true; + } + } + break; + case Parameter.GREATER_THAN: + if ( constraintsArray[i][Parameter.GREATER_THAN] != null ) { + parameter.type = Parameter.NUMBER; + parameter.min = constraintsArray[i][Parameter.GREATER_THAN]; + parameter.value = inputs[key][Parameter.DEFAULT]; + parameterList.push(parameter); + parameterPushed = true; + } + break; + }//switch + }//for + }//while + }//if + }; + + public static isVfModule(node:ITreeNode): boolean { + return node.data.type=='VFmodule'; + } + + public static isVnf(node:ITreeNode): boolean { + return node.data.type == ServiceNodeTypes.VF; + } + + updateDynamicInputsVnfDataFromModel(modelType: string, model: any): Array<any> { + let displayInputs; + if (modelType === ServiceNodeTypes.VFmodule) { + displayInputs = model.inputs; + } + return _.isEmpty(displayInputs) ? [] : this.getArbitraryInputs(displayInputs); + } + + isUserProvidedNaming(type: string, nodeModel: any, parentModel: any) : boolean { + let model; + if (type === ServiceNodeTypes.VFmodule) { + model = parentModel; + } + else { + model = nodeModel; + } + const ecompGeneratedNaming = model.properties.ecomp_generated_naming; + return ecompGeneratedNaming !== undefined && ecompGeneratedNaming === "false"; + } +} diff --git a/vid-webpack-master/src/app/shared/api.service.ts b/vid-webpack-master/src/app/shared/api.service.ts new file mode 100644 index 00000000..a69d91ea --- /dev/null +++ b/vid-webpack-master/src/app/shared/api.service.ts @@ -0,0 +1,6 @@ +import { Injectable } from '@angular/core'; + +@Injectable() +export class ApiService { + title = 'Angular 2'; +} diff --git a/vid-webpack-master/src/app/shared/components/ellipsis/ellipsis.component.ts b/vid-webpack-master/src/app/shared/components/ellipsis/ellipsis.component.ts new file mode 100644 index 00000000..fd58b650 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/ellipsis/ellipsis.component.ts @@ -0,0 +1,27 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector : 'custom-ellipsis', + template: ` + <span + class="ellipsis" + id="{{id}}" + tooltip="{{value}}">{{value}}</span>`, + styles : [ + ` + .ellipsis { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + width: 99%; + text-align: left; + } + ` + ] +}) +export class EllipsisComponent { + @Input() value : string; + @Input() id : string; + +} diff --git a/vid-webpack-master/src/app/shared/components/error/error.component.service.ts b/vid-webpack-master/src/app/shared/components/error/error.component.service.ts new file mode 100644 index 00000000..35b83f0b --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/error/error.component.service.ts @@ -0,0 +1,35 @@ +import {Injectable} from "@angular/core"; +import {Subject} from "rxjs/Subject"; +import { MessageBoxService } from '../messageBox/messageBox.service'; +import { MessageBoxData, ModalSize, ModalType } from '../messageBox/messageBox.data'; + +@Injectable() +export class ErrorService { + static showErrorWithMessage(error : ErrorMessage) : void { + setTimeout(()=>{ + let messageBoxData : MessageBoxData = new MessageBoxData( + error.title, // modal title + error.text, + + ModalType.error, + ModalSize.medium, + [ + {text:"Close", size:"large", closeModal:true} + ]); + MessageBoxService.openModal.next(messageBoxData); + } + ,500); + } +} + +export class ErrorMessage { + title : string; + text : string; + errorNumber : number; + + constructor( title : string, text : string,errorNumber : number){ + this.title = title; + this.text = text; + this.errorNumber = errorNumber; + } +} diff --git a/vid-webpack-master/src/app/shared/components/formControlError/formControlError.component.html b/vid-webpack-master/src/app/shared/components/formControlError/formControlError.component.html new file mode 100644 index 00000000..daa35e65 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/formControlError/formControlError.component.html @@ -0,0 +1,4 @@ +<div *ngIf="message"> + <span class="icon-alert"></span> + <span class="message">{{message}}</span> +</div> diff --git a/vid-webpack-master/src/app/shared/components/formControlError/formControlError.component.scss b/vid-webpack-master/src/app/shared/components/formControlError/formControlError.component.scss new file mode 100644 index 00000000..d174f511 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/formControlError/formControlError.component.scss @@ -0,0 +1,14 @@ +.icon-alert { + margin-top: 10px; + &:before { + content: "\e904"; + font-size: 16px; + color: #cf2a2a; + } +} + +.message { + font-size: 12px; + line-height: 14px; + color: #cf2a2a; +} diff --git a/vid-webpack-master/src/app/shared/components/formControlError/formControlError.component.ts b/vid-webpack-master/src/app/shared/components/formControlError/formControlError.component.ts new file mode 100644 index 00000000..a20b2603 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/formControlError/formControlError.component.ts @@ -0,0 +1,10 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector : 'form-control-error', + templateUrl : 'formControlError.component.html', + styleUrls : ['formControlError.component.scss'] +}) +export class FormControlErrorComponent { + @Input() message = null; +} diff --git a/vid-webpack-master/src/app/shared/components/formGeneralErrors/formGeneralErrors.component.html b/vid-webpack-master/src/app/shared/components/formGeneralErrors/formGeneralErrors.component.html new file mode 100644 index 00000000..4c794e68 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/formGeneralErrors/formGeneralErrors.component.html @@ -0,0 +1,10 @@ +<div *ngIf="message" class="row row-padding"> + <div class="col-md-2 icon-div"><span class="icon-alert"></span></div> + <div class="col-md-10 left-align parentbox"> + <div class="childbox"> + <span>{{message}}</span> + </div> + </div> +</div> + + diff --git a/vid-webpack-master/src/app/shared/components/formGeneralErrors/formGeneralErrors.component.scss b/vid-webpack-master/src/app/shared/components/formGeneralErrors/formGeneralErrors.component.scss new file mode 100644 index 00000000..5271cad4 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/formGeneralErrors/formGeneralErrors.component.scss @@ -0,0 +1,48 @@ +.icon-alert { + margin-top: 10px; + &:before { + content: "\e904"; + font-size: 30px; + color: #ffffff; + } +} + +.icon-div { + background: rgb(207,41,41); + text-align: center; + padding-top: 18px; + width: 77px; + height: 110px; + float: left; +} + + +.error-title { + color: #cf2a2a; +} + +.row-padding { + margin-left: 0 !important; + margin-right: 0 !important; +} + +.left-align { + text-align: left; +} + +.parentbox { + padding-right: 0; +} + +.parentbox:before { + content: ' '; + display: inline-block; + vertical-align: middle; + height: 100%; +} + +.childbox { + color: #cf2a2a; + display: inline-block; + vertical-align: middle; +} diff --git a/vid-webpack-master/src/app/shared/components/formGeneralErrors/formGeneralErrors.component.ts b/vid-webpack-master/src/app/shared/components/formGeneralErrors/formGeneralErrors.component.ts new file mode 100644 index 00000000..0c0516a4 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/formGeneralErrors/formGeneralErrors.component.ts @@ -0,0 +1,11 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector : 'form-general-error', + templateUrl : 'formGeneralErrors.component.html', + styleUrls : ['formGeneralErrors.component.scss'] +}) + +export class FormGeneralErrorsComponent { + @Input() message : string = null; +} diff --git a/vid-webpack-master/src/app/shared/components/messageBox/messageBox.component.ts b/vid-webpack-master/src/app/shared/components/messageBox/messageBox.component.ts new file mode 100644 index 00000000..08e199cf --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/messageBox/messageBox.component.ts @@ -0,0 +1,50 @@ +/************************************************************************************************ + * @Component: Message box component + * In order to use component you need to do a number of things: + * 1) Inside your component constructor you need to add listener to the button trigger. + * 2) Inside the listener you should write your callback logic. + * + * Example: + * @Component({ + * selector : 'some-component' + * ... + * }) + * + * export class SomeComponent { + * openModal() : void { + * let messageBoxData : MessageBoxData = new MessageBoxData( + * "title", // modal title + * "message", ModalType.alert, // modal type + * [ + {text:"Save", size:"'x-small'", callback: this.someFunction.bind(this), closeModal:true}, + {text:"Cancel", size:"'x-small'", closeModal:true} + ]); + * + * MessageBoxService.openModal.next(messageBoxData); // open modal + * } + * } + + ************************************************************************************************/ + + +import { Component } from '@angular/core'; +import { MessageBoxData} from './messageBox.data'; +import { MessageBoxService } from './messageBox.service'; +import { SdcUiComponents } from 'sdc-ui/lib/angular'; + +@Component({ + selector: 'message-box', + template: '<div id="message-box"></div>' +}) + +export class MessageBoxComponent { + modalService: SdcUiComponents.ModalService; + + constructor(modalService: SdcUiComponents.ModalService, private _messageBoxService : MessageBoxService) { + this.modalService = modalService; + MessageBoxService.openModal.subscribe((messageBoxData: MessageBoxData) => { + modalService.openModal(this._messageBoxService.setConfig(messageBoxData)) + }); + } +} + diff --git a/vid-webpack-master/src/app/shared/components/messageBox/messageBox.data.ts b/vid-webpack-master/src/app/shared/components/messageBox/messageBox.data.ts new file mode 100644 index 00000000..165140ba --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/messageBox/messageBox.data.ts @@ -0,0 +1,51 @@ +import { Subject } from 'rxjs/Subject'; + +export class MessageBoxData { + title?: string; + message?: string; + size : ModalSize; + type: ModalType; + buttons: Array<IModalButtonComponent>; + + constructor(title: string, message: string, type: ModalType, size : ModalSize, buttons: Array<IModalButtonComponent>) { + this.title = title; + this.message = message; + this.size = size; + this.type = type; + this.buttons = buttons; + } +} + +export interface IModalConfig { + size?: string; + title?: string; + message?: string; + buttons?: Array<IModalButtonComponent>; + type?: string; +} +export interface IButtonComponent { + text: string; + disabled?: boolean; + type?: string; + size?: string; +} +export interface IModalButtonComponent extends IButtonComponent { + callback?: Function; + closeModal?: boolean; +} +export enum ModalType { + alert = "alert", + error = "error", + standard = "info", + custom = "custom", +} +export enum ModalSize { + xlarge = "xl", + large = "l", + medium = "md", + small = "sm", + xsmall = "xsm", +} + + + diff --git a/vid-webpack-master/src/app/shared/components/messageBox/messageBox.service.spec.ts b/vid-webpack-master/src/app/shared/components/messageBox/messageBox.service.spec.ts new file mode 100644 index 00000000..89562ac5 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/messageBox/messageBox.service.spec.ts @@ -0,0 +1,49 @@ +import { TestBed, getTestBed } from '@angular/core/testing'; +import { + HttpClientTestingModule, + HttpTestingController +} from '@angular/common/http/testing'; + +import { MessageBoxService } from './messageBox.service'; +import {MessageBoxData, ModalSize, ModalType } from './messageBox.data'; + +describe('MessageBoxService', () => { + let injector; + let service: MessageBoxService; + let httpMock: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [MessageBoxService] + }); + + injector = getTestBed(); + service = injector.get(MessageBoxService); + httpMock = injector.get(HttpTestingController); + }); + + describe('#setConfig', () => { + it('should return <IModalConfig>', (done: DoneFn) => { + let title = "Delete Instantiation"; + let message = "You are about to stop the instantiation process of this service. \nAll data will be lost. Are you sure you want to stop?"; + let messageBoxData : MessageBoxData = new MessageBoxData( + title, + message, + ModalType.alert, + ModalSize.medium, + [ + {text:"Stop Instantiation", size:"large", closeModal:true}, + {text:"Cancel", size:"medium", closeModal:true} + ]); + + let result = service.setConfig(messageBoxData); + expect(result.title).toEqual(title); + expect(result.message).toEqual(message); + expect(result.buttons.length).toEqual(2); + expect(result.type).toEqual(ModalType.alert); + expect(result.size).toEqual(ModalSize.medium); + done(); + }); + }); +}); diff --git a/vid-webpack-master/src/app/shared/components/messageBox/messageBox.service.ts b/vid-webpack-master/src/app/shared/components/messageBox/messageBox.service.ts new file mode 100644 index 00000000..eaa012d3 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/messageBox/messageBox.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; +import { IModalConfig, MessageBoxData, ModalSize, ModalType } from './messageBox.data'; + +@Injectable() +export class MessageBoxService { + static openModal: Subject<MessageBoxData> = new Subject<MessageBoxData>(); + setConfig(messageBoxData: MessageBoxData) : IModalConfig{ + return { + size : ModalSize.medium, + title : messageBoxData.title, + type : messageBoxData.type, + message : messageBoxData.message, + buttons: messageBoxData.buttons + }; + } + +} diff --git a/vid-webpack-master/src/app/shared/components/model-information/model-information.component.ts b/vid-webpack-master/src/app/shared/components/model-information/model-information.component.ts new file mode 100644 index 00000000..fea4c44c --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/model-information/model-information.component.ts @@ -0,0 +1,42 @@ +import {Component, Input} from '@angular/core'; +import * as _ from 'lodash'; + +@Component({ + selector: 'model-information', + templateUrl: 'model-information.html', + styleUrls: ['model-information.scss'] +}) + +export class ModelInformationComponent { + private _modelInformationItems: Array<ModelInformationItem>; + + + get modelInformationItems(): Array<ModelInformationItem> { + return this._modelInformationItems; + } + + @Input() + set modelInformationItems(_modelInformationItems: Array<ModelInformationItem>) { + if (_modelInformationItems) { + this._modelInformationItems = _modelInformationItems.filter(x => x.mandatory || (!_.isEmpty(x.values) && !_.isEmpty(x.values[0]))); + } + } +} + + +export class ModelInformationItem { + label: string; + testsId: string; + values: Array<string>; + toolTipText: string; + mandatory: boolean; + + constructor(label: string, testsId: string, values: Array<any>, toolTipText: string = "", mandatory: boolean = false,nested:boolean=false) { + this.label = label; + this.testsId = testsId; + this.values = values; + this.toolTipText = toolTipText; + this.mandatory = mandatory; + } + +} diff --git a/vid-webpack-master/src/app/shared/components/model-information/model-information.html b/vid-webpack-master/src/app/shared/components/model-information/model-information.html new file mode 100644 index 00000000..456dfdee --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/model-information/model-information.html @@ -0,0 +1,12 @@ +<div id="model-information"> + <div *ngFor="let item of modelInformationItems" class="item" attr.data-tests-id="model-item-{{item.label}}"> + <tooltip-content #a> + <span> {{item.toolTipText}}</span> + </tooltip-content> + + <div class="wrapper" [tooltip]="a" [tooltipDisabled]="!item.toolTipText" tooltipPlacement="top" [tooltipAnimation]="false"> + <label attr.data-tests-id="model-item-label-{{item.testsId}}">{{item.label}}</label> + <div *ngFor="let value of item.values" attr.data-tests-id="model-item-value-{{item.testsId}}">{{value}}</div> + </div> + </div> +</div> diff --git a/vid-webpack-master/src/app/shared/components/model-information/model-information.scss b/vid-webpack-master/src/app/shared/components/model-information/model-information.scss new file mode 100644 index 00000000..cd42136e --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/model-information/model-information.scss @@ -0,0 +1,34 @@ +#model-information { + overflow: auto; + +} + +tooltip-content span { + font-family: OpenSans-Regular; + font-size: 12px; + color: #FFFFFF; + letter-spacing: 0; + line-height: 16px; +} + +.item { + display: block; + + .wrapper { + + display: inline-block; + margin-bottom: 25px; + + label { + font-family: OpenSans-Semibold; + font-size: 12px; + } + + div { + font-family: OpenSans-Regular; + font-size: 14px; + } + } +} + + diff --git a/vid-webpack-master/src/app/shared/components/no-content-message-and-icon/no-content-message-and-icon.component.html b/vid-webpack-master/src/app/shared/components/no-content-message-and-icon/no-content-message-and-icon.component.html new file mode 100644 index 00000000..bbe7bc78 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/no-content-message-and-icon/no-content-message-and-icon.component.html @@ -0,0 +1,5 @@ +<div class="width-100"> + <div class="text-title" [ngClass]="titleClass">{{title}}</div> + <div class="text-subtitle" [ngClass]="subtitleClass">{{subtitle}}</div> + <img id="not-node-img-id" src="{{iconPath}}" alt="" class="no-content-icon" [ngClass]="iconClass" data-tests-id="no-content-icon"> +</div> diff --git a/vid-webpack-master/src/app/shared/components/no-content-message-and-icon/no-content-message-and-icon.component.scss b/vid-webpack-master/src/app/shared/components/no-content-message-and-icon/no-content-message-and-icon.component.scss new file mode 100644 index 00000000..1320ef73 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/no-content-message-and-icon/no-content-message-and-icon.component.scss @@ -0,0 +1,26 @@ +.width-100 { + width: 100%; + margin-top: 126px; + +} + +.text-title { + font-family: OpenSans-Semibold; + font-size: 16px; + color: #4A4A4A; + text-align: center; +} + +.text-subtitle { + font-family: OpenSans-Regular; + font-size: 16px; + color: #959595; + text-align: center; + margin-top: 7px; +} + +.no-content-icon { + display: block; + vertical-align: middle; + margin: 30px auto; +} diff --git a/vid-webpack-master/src/app/shared/components/no-content-message-and-icon/no-content-message-and-icon.component.ts b/vid-webpack-master/src/app/shared/components/no-content-message-and-icon/no-content-message-and-icon.component.ts new file mode 100644 index 00000000..7f4e9829 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/no-content-message-and-icon/no-content-message-and-icon.component.ts @@ -0,0 +1,23 @@ +import {Component, Input} from '@angular/core'; + +@Component({ + selector: 'no-content-message-and-icon', + templateUrl: './no-content-message-and-icon.component.html', + styleUrls: ['./no-content-message-and-icon.component.scss'] +}) + + +export class NoContentMessageAndIconComponent { + constructor() {} + + @Input() title: string; + @Input() subtitle: string; + @Input() iconPath: string; + + @Input() titleClass: string=""; + @Input() subtitleClass: string=""; + @Input() iconClass: string=""; + +} + + diff --git a/vid-webpack-master/src/app/shared/components/popover/popover.component.html b/vid-webpack-master/src/app/shared/components/popover/popover.component.html new file mode 100644 index 00000000..c5515596 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/popover/popover.component.html @@ -0,0 +1,8 @@ +<div + triggers="mouseenter:mouseleave" + popover="{{value}}" + [hidden]="value == null" + container="body"> + <ng-content ></ng-content> +</div> + diff --git a/vid-webpack-master/src/app/shared/components/popover/popover.component.scss b/vid-webpack-master/src/app/shared/components/popover/popover.component.scss new file mode 100644 index 00000000..ca2800a2 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/popover/popover.component.scss @@ -0,0 +1,4 @@ +.popover.popover-top.top { + color : green !important; + font-size: 10px; +} diff --git a/vid-webpack-master/src/app/shared/components/popover/popover.component.ts b/vid-webpack-master/src/app/shared/components/popover/popover.component.ts new file mode 100644 index 00000000..d6a4c3ae --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/popover/popover.component.ts @@ -0,0 +1,17 @@ +import {Component, Input} from "@angular/core"; + +@Component({ + selector: 'custom-popover', + templateUrl: 'popover.component.html', + styles: [` + :host >>> .popover { + font-size: 13px; + text-align: left; + z-index: 10000; + } + `] +}) + +export class PopoverComponent { + @Input() value: String; +} diff --git a/vid-webpack-master/src/app/shared/components/spinner/spinner.component.html b/vid-webpack-master/src/app/shared/components/spinner/spinner.component.html new file mode 100644 index 00000000..cb11feea --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/spinner/spinner.component.html @@ -0,0 +1,2 @@ +<div *ngIf="show" + class="spinner"></div> diff --git a/vid-webpack-master/src/app/shared/components/spinner/spinner.component.scss b/vid-webpack-master/src/app/shared/components/spinner/spinner.component.scss new file mode 100644 index 00000000..d31dfed8 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/spinner/spinner.component.scss @@ -0,0 +1,87 @@ +.spinner { + height: 150px; + width: 150px; + margin: 0 auto; + -webkit-animation: rotation .6s infinite linear; + animation: rotation .6s infinite linear; + border: 6px solid rgba(0, 174, 239, 0.01); + border-radius: 100%; + position: absolute; + z-index: 1000; + left: 50%; + right: 50%; + top: 50%; + bottom: 50%; +} + + + +.spinner:before { + content: ""; + display: block; + position: absolute; + top: -6px; + height: 100%; + width: 100%; + border-top: 6px solid #009fdb; + border-left: 6px solid transparent; + border-bottom: 6px solid #c3161600; + border-right: 6px solid transparent; + border-radius: 100%; +} + +@-webkit-keyframes rotation { + from {-webkit-transform: rotate(0deg);} + to {-webkit-transform: rotate(359deg);} +} +@-moz-keyframes rotation { + from {-moz-transform: rotate(0deg);} + to {-moz-transform: rotate(359deg);} +} +@-o-keyframes rotation { + from {-o-transform: rotate(0deg);} + to {-o-transform: rotate(359deg);} +} +@keyframes rotation { + from {transform: rotate(0deg);} + to {transform: rotate(359deg);} +} + +.spinner-sm { + height:16px; + width:16px; +} + +.spinner-md { + height:40px; + width:40px; +} + +.spinner-lr { + height:150px; + width:150px; +} + +.spinner-red { + border:6px solid rgba(216, 27, 34, .15); +} + +.spinner-red:before { + border-top:6px solid rgba(216, 27, 34, 1); +} + +.spinner-green { + border:6px solid rgba(40, 183, 121, .15); +} + +.spinner-green:before { + border-top:6px solid rgba(40, 183, 121, 1); +} + +.spinner-grey { + border:6px solid rgba(139, 146, 154, .15); +} + +.spinner-grey:before { + border-top:6px solid rgba(139, 146, 154, 1); +} diff --git a/vid-webpack-master/src/app/shared/components/spinner/spinner.component.spec.ts b/vid-webpack-master/src/app/shared/components/spinner/spinner.component.spec.ts new file mode 100644 index 00000000..531ee8c6 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/spinner/spinner.component.spec.ts @@ -0,0 +1,42 @@ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import { SpinnerComponent } from './spinner.component'; + +describe('Spinner component', () => { + let component: SpinnerComponent; + let fixture: ComponentFixture<SpinnerComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [], + declarations: [SpinnerComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SpinnerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('component should be defined', () => { + expect(component).toBeDefined(); + }); + + + it('component constructor should subscribe of showSpinner event with true', ()=> { + SpinnerComponent.showSpinner.next(true); + expect(component.show).toBeTruthy(); + }); + + it('component constructor should subscribe of showSpinner event with false', ()=> { + SpinnerComponent.showSpinner.next(false); + expect(component.show).toBeFalsy(); + }); + + + + +}); diff --git a/vid-webpack-master/src/app/shared/components/spinner/spinner.component.ts b/vid-webpack-master/src/app/shared/components/spinner/spinner.component.ts new file mode 100644 index 00000000..0ce5d207 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/spinner/spinner.component.ts @@ -0,0 +1,18 @@ +import { Component, Input } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; + +@Component({ + selector : 'spinner-component', + templateUrl : './spinner.component.html', + styleUrls : ['./spinner.component.scss'] +}) +export class SpinnerComponent { + show : boolean = false; + static showSpinner: Subject<boolean> = new Subject<boolean>(); + + constructor(){ + SpinnerComponent.showSpinner.subscribe((status) => { + this.show = status; + }) + } +} diff --git a/vid-webpack-master/src/app/shared/components/validators/numbersLettersUnderscore/numbersLettersUnderscore.validator.spec.ts b/vid-webpack-master/src/app/shared/components/validators/numbersLettersUnderscore/numbersLettersUnderscore.validator.spec.ts new file mode 100644 index 00000000..ec9f3f73 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/validators/numbersLettersUnderscore/numbersLettersUnderscore.validator.spec.ts @@ -0,0 +1,39 @@ +import { ReflectiveInjector } from '@angular/core'; +import { NumbersLettersUnderscoreValidator } from './numbersLettersUnderscore.validator'; + +describe('Numbers letters underscore validator', () => { + let injector; + let service: NumbersLettersUnderscoreValidator; + + beforeEach(() => { + + let injector = ReflectiveInjector.resolveAndCreate([ + NumbersLettersUnderscoreValidator + ]); + + service = injector.get(NumbersLettersUnderscoreValidator); + }); + + + describe('#valid', () => { + it("'legal' should return null", ()=> { + let legalVal: string = "legal"; + let result = NumbersLettersUnderscoreValidator.valid(legalVal); + expect(result).toBeNull(); + }); + + it("'illegal' should return object with invalidNumberLettersUnderscore true", ()=> { + let illegalVal: string = "illegal-Val"; + let result = NumbersLettersUnderscoreValidator.valid(illegalVal); + expect(result.invalidNumberLettersUnderscore).toBeTruthy(); + }); + + it("'null' should return null", ()=> { + let nullVal: string = null + let result = NumbersLettersUnderscoreValidator.valid(nullVal); + expect(result).toBeNull(); + }); + + + }); +}); diff --git a/vid-webpack-master/src/app/shared/components/validators/numbersLettersUnderscore/numbersLettersUnderscore.validator.ts b/vid-webpack-master/src/app/shared/components/validators/numbersLettersUnderscore/numbersLettersUnderscore.validator.ts new file mode 100644 index 00000000..418bdfc4 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/validators/numbersLettersUnderscore/numbersLettersUnderscore.validator.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { isNullOrUndefined, isString } from 'util'; + +@Injectable() +export class NumbersLettersUnderscoreValidator { + static valid(control: any) { + let reg = /^[a-zA-Z0-9_]*$/; + + if(isNullOrUndefined(control)) return null; + let val = isString(control) ? control : control.value; + if (val === null) { + return {'invalidNumberLettersUnderscore': true}; + } + if (reg.test(val)) { + return null; + } else { + return {'invalidNumberLettersUnderscore': true}; + } + } +} diff --git a/vid-webpack-master/src/app/shared/directives/inputPrevention/inputPreventionPattern.directive.spec.ts b/vid-webpack-master/src/app/shared/directives/inputPrevention/inputPreventionPattern.directive.spec.ts new file mode 100644 index 00000000..846ff70f --- /dev/null +++ b/vid-webpack-master/src/app/shared/directives/inputPrevention/inputPreventionPattern.directive.spec.ts @@ -0,0 +1,66 @@ +import {TestBed, ComponentFixture} from '@angular/core/testing'; +import {Component, DebugElement} from "@angular/core"; +import {By} from "@angular/platform-browser"; +import { InputPreventionPatternDirective } from './inputPreventionPattern.directive'; + +@Component({ + template: `<input + patternInput + pattern="^[a-zA-Z0-9_]*$">` +}) +class TestHoverFocusComponent { +} + + +describe('InputPrevention Pattern Directive', () => { + + let component: TestHoverFocusComponent; + let fixture: ComponentFixture<TestHoverFocusComponent>; + let directiveInstance : InputPreventionPatternDirective; + let inputEl: DebugElement; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [TestHoverFocusComponent, InputPreventionPatternDirective] + }); + fixture = TestBed.createComponent(TestHoverFocusComponent); + component = fixture.componentInstance; + inputEl = fixture.debugElement.query(By.css('input')); + directiveInstance = inputEl.injector.get(InputPreventionPatternDirective); + }); + + it('directive should be defined', () => { + expect(directiveInstance).toBeDefined(); + }); + + it('pattern exists', () => { + expect(inputEl.nativeElement.pattern).toEqual('^[a-zA-Z0-9_]*$'); + }); + + it('kepress legal input', () => { + fixture.detectChanges(); + inputEl.nativeElement.value = "legalInput"; + expect(new RegExp(inputEl.nativeElement.pattern).test(inputEl.nativeElement.value)).toBeTruthy(); + }); + + it('kepress illegal input', () => { + inputEl.triggerEventHandler('kepress', " "); + fixture.detectChanges(); + expect(inputEl.nativeElement.value).toBe(''); + }); + + it('kepress event legal input should return event', () => { + const event = <any>{ key: 'A' }; + inputEl.nativeElement.value = "legalInput"; + let result = directiveInstance.onKeypress(event); + expect(result).toBe(event); + }); + + it('kepress event illegal input should prevent default', () => { + const event = <any>{key: '-', preventDefault : function () {} }; + spyOn(event, 'preventDefault'); + inputEl.nativeElement.value = "-"; + let result = directiveInstance.onKeypress(event); + expect(event.preventDefault).toHaveBeenCalled(); + }); +}); diff --git a/vid-webpack-master/src/app/shared/directives/inputPrevention/inputPreventionPattern.directive.ts b/vid-webpack-master/src/app/shared/directives/inputPrevention/inputPreventionPattern.directive.ts new file mode 100644 index 00000000..dada09be --- /dev/null +++ b/vid-webpack-master/src/app/shared/directives/inputPrevention/inputPreventionPattern.directive.ts @@ -0,0 +1,24 @@ +import {Directive, ElementRef} from '@angular/core'; + +@Directive({ + selector: '[patternInput]', + host: { + '(keypress)': 'onKeypress($event)' + } +}) +export class InputPreventionPatternDirective{ + inputElement : ElementRef; + constructor(el: ElementRef) { + this.inputElement = el; + } + + onKeypress(event: KeyboardEvent) { + const pattern = new RegExp(this.inputElement.nativeElement.pattern); + if(pattern){ + if(!pattern.test(event['key'])){ + event.preventDefault(); + } + } + return event; + } +} diff --git a/vid-webpack-master/src/app/shared/directives/svg/svg.directive.html b/vid-webpack-master/src/app/shared/directives/svg/svg.directive.html new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/vid-webpack-master/src/app/shared/directives/svg/svg.directive.html diff --git a/vid-webpack-master/src/app/shared/directives/svg/svg.directive.ts b/vid-webpack-master/src/app/shared/directives/svg/svg.directive.ts new file mode 100644 index 00000000..e4dc55a5 --- /dev/null +++ b/vid-webpack-master/src/app/shared/directives/svg/svg.directive.ts @@ -0,0 +1,34 @@ +import { AfterContentChecked, AfterViewInit, Directive, ElementRef, Input } from '@angular/core'; +import { isNullOrUndefined } from 'util'; + + +/* + Temporary + Changing svg color and size. + color changing according to fill attribute + size according to viewBox +*/ +@Directive({ + selector: '[svg-directive]' +}) +export class SvgDirective implements AfterContentChecked { + @Input('fill') fill: string = "black"; + @Input('widthViewBox') widthViewBox: string = null; + @Input('heightViewBox') heightViewBox: string = null; + + constructor(private elRef: ElementRef) {} + ngAfterContentChecked(): void { + if(this.elRef !== undefined && this.elRef.nativeElement.children !== undefined && this.elRef.nativeElement.children[0] !== undefined){ + this.elRef.nativeElement.children[0].children[1].children[0].style.fill = this.fill; + if(this.elRef.nativeElement.children[0].children[1].children.length > 1){ + this.elRef.nativeElement.children[0].children[1].children[1].style.fill = this.fill; + this.elRef.nativeElement.children[0].children[1].children[2].children[0].style.fill = this.fill; + } + + if(this.widthViewBox && this.heightViewBox){ + this.elRef.nativeElement.firstChild.setAttribute('viewBox', "1 1 " + this.widthViewBox + " " + this.heightViewBox) + } + + } + } +} diff --git a/vid-webpack-master/src/app/shared/index.ts b/vid-webpack-master/src/app/shared/index.ts new file mode 100644 index 00000000..68fada65 --- /dev/null +++ b/vid-webpack-master/src/app/shared/index.ts @@ -0,0 +1 @@ +export * from './api.service'; diff --git a/vid-webpack-master/src/app/shared/models/ServiceNodeTypes.ts b/vid-webpack-master/src/app/shared/models/ServiceNodeTypes.ts new file mode 100644 index 00000000..f72b32d8 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/ServiceNodeTypes.ts @@ -0,0 +1,8 @@ +export enum ServiceNodeTypes { + VF = "VF", + VFmodule = "VFmodule", + Network = "Network", + Configuration = "Configuration" +} + + diff --git a/vid-webpack-master/src/app/shared/models/aicZone.ts b/vid-webpack-master/src/app/shared/models/aicZone.ts new file mode 100644 index 00000000..7b9e1ed4 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/aicZone.ts @@ -0,0 +1,9 @@ +export class AicZone { + id: string; + name: string; + + constructor(serviceJson){ + this.id = serviceJson["zone-id"]; + this.name = serviceJson["zone-name"]; + } +} diff --git a/vid-webpack-master/src/app/shared/models/categoryParams.ts b/vid-webpack-master/src/app/shared/models/categoryParams.ts new file mode 100644 index 00000000..9e005297 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/categoryParams.ts @@ -0,0 +1,15 @@ +import {SelectOptionInterface} from "./selectOption"; + +export class CategoryParams { + owningEntityList: SelectOptionInterface[]; + projectList: SelectOptionInterface[]; + lineOfBusinessList: SelectOptionInterface[]; + platformList: SelectOptionInterface[]; + + constructor(owningEntityList=[], projectList=[], lob=[], platform=[]){ + this.owningEntityList = owningEntityList; + this.projectList = projectList; + this.lineOfBusinessList = lob; + this.platformList = platform; + } +} diff --git a/vid-webpack-master/src/app/shared/models/dynamicInput.ts b/vid-webpack-master/src/app/shared/models/dynamicInput.ts new file mode 100644 index 00000000..a08cdfc7 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/dynamicInput.ts @@ -0,0 +1,123 @@ +export class DynamicInput<T>{ + + id: string; + name: string; + type: string; + description: string; + value: T; + prompt: any; + maxLength: number; + minLength: number; + isVisible: boolean; + isRequired: boolean; + isEnabled: boolean; + isReadOnly: boolean; + + + constructor(options: { + id?: string, + name?: string, + type: string, + description?: string, + value?: T, + prompt?: any, + maxLength?: number, + minLength?: number, + isVisible?: boolean, + isRequired?: boolean, + isEnabled?: boolean, + isReadOnly?: boolean, + }) { + this.id = options.id; + this.name = options.name || ''; + this.type = options.type; + this.description = options.description || ''; + this.value = options.value; + this.prompt = options.prompt; + this.maxLength = options.maxLength; + this.minLength = options.minLength; + this.isVisible = options.isVisible == false? options.isVisible: true; + this.isEnabled = options.isEnabled == false? options.isEnabled: true; + this.isRequired = options.isRequired == null? false: options.isRequired; + this.isReadOnly = options.isReadOnly == null? false: options.isReadOnly; + } +} + +export class DynamicNumber extends DynamicInput<number> { + + max: number; + min: number; + + constructor(options: { + id?: string, + name?: string, + type: string, + description?: string, + value?: number, + prompt?: any, + maxLength?: number, + minLength?: number, + isVisible?: boolean, + isRequired?: boolean, + isEnabled?: boolean, + isReadOnly?: boolean, + max?: number, + min?: number + }){ + super(options); + this.max = options.max; + this.min = options.min; + } + +} + +export class DynamicSelect extends DynamicInput<any> { + optionList: any[]; + + constructor(options: { + id?: string, + name?: string, + type: string, + description?: string, + value?: any, + prompt?: any, + maxLength?: number, + minLength?: number, + isVisible?: boolean, + isRequired?: boolean, + isEnabled?: boolean, + isReadOnly?: boolean, + optionList?: any[] + }) { + super(options); + this.optionList = options.optionList || []; + } +} + +export class DynamicMultiSelect extends DynamicSelect { + selectedItems: any[]; + settings: any; + + constructor(options: { + id?: string, + name?: string, + type: string, + description?: string, + value?: any, + prompt?: any, + maxLength?: number, + minLength?: number, + isVisible?: boolean, + isRequired?: boolean, + isEnabled?: boolean, + isReadOnly?: boolean, + settings?: any, + optionList?: any[], + selectedItems?: any[] + }) { + super(options); + this.settings = options.settings || {}; + this.selectedItems = options.selectedItems || []; + } +} + diff --git a/vid-webpack-master/src/app/shared/models/externalComponentStatus.ts b/vid-webpack-master/src/app/shared/models/externalComponentStatus.ts new file mode 100644 index 00000000..21ef61a8 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/externalComponentStatus.ts @@ -0,0 +1,11 @@ +export class ExternalComponentStatus { + component: string; + available: boolean; + metadata: any; + + constructor(component: string, available: boolean, metadata: any) { + this.component = component; + this.available = available; + this.metadata = metadata; + } +} diff --git a/vid-webpack-master/src/app/shared/models/inputTypes.ts b/vid-webpack-master/src/app/shared/models/inputTypes.ts new file mode 100644 index 00000000..1f7222f5 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/inputTypes.ts @@ -0,0 +1,12 @@ +export enum InputType { + LCP_REGION = "LCP_REGION", + TENANT = "TENANT", + LOB = "LOB", + PLATFORM = "PLATFORM", + ROLLBACK = "ROLLBACK", + PRODUCT_FAMILY = "PRODUCT_FAMILY", + VG = "VG" + +} + + diff --git a/vid-webpack-master/src/app/shared/models/lcpRegion.ts b/vid-webpack-master/src/app/shared/models/lcpRegion.ts new file mode 100644 index 00000000..e39321d5 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/lcpRegion.ts @@ -0,0 +1,11 @@ +export class LcpRegion { + id: string; + name: string; + isPermitted: boolean; + + constructor(serviceJson){ + this.id = serviceJson["cloudRegionID"]; + this.name = serviceJson["cloudRegionID"]; + this.isPermitted = serviceJson["is-permitted"]; + } +} diff --git a/vid-webpack-master/src/app/shared/models/lcpRegionTenants.ts b/vid-webpack-master/src/app/shared/models/lcpRegionTenants.ts new file mode 100644 index 00000000..d215546a --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/lcpRegionTenants.ts @@ -0,0 +1,13 @@ +export class LcpRegionTenants { + id: string; + tenantId: string; + tenantName: string; + isPermitted: boolean; + + constructor(serviceJson){ + this.id = serviceJson["cloudRegionID"]; + this.tenantId = serviceJson["tenantID"]; + this.tenantName = serviceJson["tenantName"]; + this.isPermitted = serviceJson["is-permitted"]; + } +} diff --git a/vid-webpack-master/src/app/shared/models/lcpRegionsAndTenants.ts b/vid-webpack-master/src/app/shared/models/lcpRegionsAndTenants.ts new file mode 100644 index 00000000..79f6e07d --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/lcpRegionsAndTenants.ts @@ -0,0 +1,12 @@ +import {LcpRegion} from "./lcpRegion"; +import {Tenant} from "./tenant"; + +export class LcpRegionsAndTenants { + lcpRegionList: LcpRegion[]; + lcpRegionsTenantsMap: { [lcpRegion: string] : Tenant[]; }; + + constructor(lcpRegionList: LcpRegion[] = [], lcpRegionsTenantsMap: any = {}) { + this.lcpRegionList = lcpRegionList; + this.lcpRegionsTenantsMap = lcpRegionsTenantsMap; + } +} diff --git a/vid-webpack-master/src/app/shared/models/modelInfo.ts b/vid-webpack-master/src/app/shared/models/modelInfo.ts new file mode 100644 index 00000000..091c02ef --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/modelInfo.ts @@ -0,0 +1,21 @@ + +export class ModelInfo { + modelInvariantId: string; + modelVersionId: string; + modelName: string; + modelVersion: string; + modelCustomizationId: string; + modelCustomizationName: string; + + + + constructor(instanceModel) { + this.modelInvariantId = instanceModel.invariantUuid; + this.modelVersionId = instanceModel.uuid; + this.modelName = instanceModel.name; + this.modelVersion = instanceModel.version; + this.modelCustomizationId = instanceModel.customizationUuid; + this.modelCustomizationName = instanceModel.modelCustomizationName; + } +} + diff --git a/vid-webpack-master/src/app/shared/models/nodeModel.ts b/vid-webpack-master/src/app/shared/models/nodeModel.ts new file mode 100644 index 00000000..4b22b8d9 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/nodeModel.ts @@ -0,0 +1,29 @@ +export interface NodeModelResponseInterface { + name: string; + version: string; + description: string; + category: string; + uuid: string; + invariantUuid: string; +} + +export class NodeModel { + name: string; + version: string; + description: string; + category: string; + uuid: string; + invariantUuid: string; + + constructor(serviceJson?: NodeModelResponseInterface) { + if (serviceJson) { + this.name = serviceJson.name; + this.version = serviceJson.version; + this.description = serviceJson.description; + this.category = serviceJson.category; + this.uuid = serviceJson.uuid; + this.invariantUuid = serviceJson.invariantUuid; + } + } + +} diff --git a/vid-webpack-master/src/app/shared/models/owningEntity.ts b/vid-webpack-master/src/app/shared/models/owningEntity.ts new file mode 100644 index 00000000..f06b24ff --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/owningEntity.ts @@ -0,0 +1,14 @@ +interface OwningEntityResponse { + id: string, + name: string +} + +export class OwningEntity { + id: string; + name: string; + + constructor(serviceJson: OwningEntityResponse){ + this.id = serviceJson.id; + this.name = serviceJson.name; + } +} diff --git a/vid-webpack-master/src/app/shared/models/productFamily.ts b/vid-webpack-master/src/app/shared/models/productFamily.ts new file mode 100644 index 00000000..3c55ac00 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/productFamily.ts @@ -0,0 +1,13 @@ +import {ServiceResponseInterface} from "../../services/aaiService/responseInterfaces/getServicesResponseInterface"; + +export class ProductFamily { + id: string; + name: string; + isPermitted: boolean; + + constructor(serviceResponse: ServiceResponseInterface){ + this.id = serviceResponse['service-id']; + this.name = serviceResponse["service-description"]; + this.isPermitted = serviceResponse["is-permitted"]; + } +} diff --git a/vid-webpack-master/src/app/shared/models/project.ts b/vid-webpack-master/src/app/shared/models/project.ts new file mode 100644 index 00000000..db8929e3 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/project.ts @@ -0,0 +1,14 @@ +interface ProjectResponseInterface { + id: string, + name: string +} + +export class Project { + id: string; + name: string; + + constructor(projectResponse: ProjectResponseInterface){ + this.id = projectResponse.id; + this.name = projectResponse.name; + } +} diff --git a/vid-webpack-master/src/app/shared/models/selectOption.ts b/vid-webpack-master/src/app/shared/models/selectOption.ts new file mode 100644 index 00000000..c12c1a82 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/selectOption.ts @@ -0,0 +1,17 @@ +export interface SelectOptionInterface { + id: string, + name: string + isPermitted?: boolean +} + +export class SelectOption { + id: string; + name: string; + isPermitted?: boolean; + + constructor(option: SelectOptionInterface){ + this.id = option.id; + this.name = option.name; + this.isPermitted = option.isPermitted; + } +} diff --git a/vid-webpack-master/src/app/shared/models/serviceInstance.ts b/vid-webpack-master/src/app/shared/models/serviceInstance.ts new file mode 100644 index 00000000..a952430b --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/serviceInstance.ts @@ -0,0 +1,28 @@ +import {VnfInstance} from "./vnfInstance"; + +export class ServiceInstance { + instanceName: string; + isUserProvidedNaming: boolean; + globalSubscriberId: string; + productFamilyId: string; + subscriptionServiceType: string; + lcpCloudRegionId: string; + tenantId: string; + tenantName: string; + aicZoneId: string; + aicZoneName: string; + projectName: string; + owningEntityId: string; + owningEntityName: string; + pause: boolean; + bulkSize: number; + vnfs: { [vnf_module_model_name: string] : VnfInstance; }; + instanceParams: { [key: string] : string; }; + rollbackOnFailure : boolean; + subscriberName : string; + + constructor() { + this.vnfs = {}; + this.instanceParams = {}; + } +} diff --git a/vid-webpack-master/src/app/shared/models/serviceModel.ts b/vid-webpack-master/src/app/shared/models/serviceModel.ts new file mode 100644 index 00000000..18d8582e --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/serviceModel.ts @@ -0,0 +1,44 @@ +import {NodeModel, NodeModelResponseInterface} from "./nodeModel"; +import * as _ from "lodash"; + + +export interface ServiceModelResponseInterface extends NodeModelResponseInterface{ + + serviceType: string; + serviceRole: string; + serviceEcompNaming: boolean; +} + +export class ServiceModel extends NodeModel{ + + serviceType: string; + serviceRole: string; + servicesQty: number; + isUserProvidedNaming: boolean; + isMultiStepDesign: boolean; + + constructor(serviceModelJson?: any){ + super(serviceModelJson.service); + if (serviceModelJson) { + const service: ServiceModelResponseInterface = serviceModelJson.service; + this.serviceType = service.serviceType; + this.serviceRole = service.serviceRole; + this.isUserProvidedNaming = this.getIsUserProvidedName(service); + this.isMultiStepDesign = this.getIsMultiStepDesign(serviceModelJson); + } + } + + private getIsUserProvidedName(serviceJson): boolean { + return serviceJson.serviceEcompNaming !== undefined && serviceJson.serviceEcompNaming === "false"; + }; + + private getIsMultiStepDesign(serviceModel): boolean { + for (let key in serviceModel.vnfs) { + const vnf = serviceModel.vnfs[key]; + if (vnf.properties.multi_stage_design === "true") { + return true + } + } + return false; + } +} diff --git a/vid-webpack-master/src/app/shared/models/serviceNodeTypeToModelKeyMapper.ts b/vid-webpack-master/src/app/shared/models/serviceNodeTypeToModelKeyMapper.ts new file mode 100644 index 00000000..1134bbb8 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/serviceNodeTypeToModelKeyMapper.ts @@ -0,0 +1,8 @@ +export enum ServiceNodeTypeToModelKeyMapper { + VF = "vnfs", + VFmodule="vfModules", + Network = "networks", + Configuration = "configurations" +} + + diff --git a/vid-webpack-master/src/app/shared/models/serviceType.ts b/vid-webpack-master/src/app/shared/models/serviceType.ts new file mode 100644 index 00000000..67aacdc0 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/serviceType.ts @@ -0,0 +1,17 @@ +export interface SubscriptionResponseInterface { + 'service-type': string + 'is-permitted': boolean +} + +export class ServiceType { + id: string; + name: string; + isPermitted: boolean; + + + constructor(id: string, subscription: SubscriptionResponseInterface){ + this.id = id; + this.name = subscription['service-type']; + this.isPermitted = subscription['is-permitted']; + } +} diff --git a/vid-webpack-master/src/app/shared/models/subscriber.ts b/vid-webpack-master/src/app/shared/models/subscriber.ts new file mode 100644 index 00000000..55fc8379 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/subscriber.ts @@ -0,0 +1,11 @@ +export class Subscriber { + id: string; + name: string; + isPermitted: boolean; + + constructor(subscriberResponse){ + this.id = subscriberResponse['global-customer-id']; + this.name = subscriberResponse['subscriber-name']; + this.isPermitted = subscriberResponse['is-permitted']; + } +} diff --git a/vid-webpack-master/src/app/shared/models/tenant.ts b/vid-webpack-master/src/app/shared/models/tenant.ts new file mode 100644 index 00000000..234f1dbf --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/tenant.ts @@ -0,0 +1,11 @@ +export class Tenant { + id: string; + name: string; + isPermitted: boolean; + + constructor(serviceJson){ + this.id = serviceJson["tenantID"]; + this.name = serviceJson["tenantName"]; + this.isPermitted = serviceJson["is-permitted"]; + } +} diff --git a/vid-webpack-master/src/app/shared/models/vfModule.ts b/vid-webpack-master/src/app/shared/models/vfModule.ts new file mode 100644 index 00000000..21f43ed1 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/vfModule.ts @@ -0,0 +1,39 @@ +import {NodeModel, NodeModelResponseInterface} from "./nodeModel"; + + +export interface properties{ + initialCount: number; + maxCountInstances: number; + minCountInstances: number; +} + +export interface VFModuleResponseInterface extends NodeModelResponseInterface { + customizationUuid: string; + modelCustomizationName: string; + properties: properties +} + +export class VfModule extends NodeModel { + + min:number; + max:number; + vgName:string; + rollbackOnFailure:boolean; + initial:number; + customizationUuid: string; + modelCustomizationName: string; + + constructor(vf?: VFModuleResponseInterface) { + super(vf); + if(vf){ + this.customizationUuid = vf.customizationUuid; + this.modelCustomizationName = vf.modelCustomizationName; + } + if (vf && vf.properties) { + this.min = vf.properties.minCountInstances; + this.max = vf.properties.maxCountInstances; + this.initial = vf.properties.initialCount; + this.rollbackOnFailure = true + } + } +} diff --git a/vid-webpack-master/src/app/shared/models/vfModuleInstance.ts b/vid-webpack-master/src/app/shared/models/vfModuleInstance.ts new file mode 100644 index 00000000..c6db0002 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/vfModuleInstance.ts @@ -0,0 +1,5 @@ +export class VfModuleInstance { + instanceName: string; + volumeGroupName: string; + instanceParams: { [key: string] : string; }; +} diff --git a/vid-webpack-master/src/app/shared/models/vfModuleTreeNode.ts b/vid-webpack-master/src/app/shared/models/vfModuleTreeNode.ts new file mode 100644 index 00000000..d4cc7e9c --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/vfModuleTreeNode.ts @@ -0,0 +1,17 @@ +import {VfModule} from "./vfModule"; +import {VfModuleInstance} from "./vfModuleInstance"; +import {ServiceNodeTypes} from "./ServiceNodeTypes"; + +export class VfModuleTreeNode { + modelId: string; + name: string; + modelName: string; + type: string; + + constructor(vfModuleInstance: VfModuleInstance, vfModuleModel: VfModule, vfModuleModelName: string){ + this.name = vfModuleInstance.instanceName || vfModuleInstance.volumeGroupName || '<Automatically Assigned>'; + this.modelId = vfModuleModel.uuid; + this.modelName = vfModuleModelName; + this.type = ServiceNodeTypes.VFmodule; + } +} diff --git a/vid-webpack-master/src/app/shared/models/vfModulesMap.ts b/vid-webpack-master/src/app/shared/models/vfModulesMap.ts new file mode 100644 index 00000000..95396fd5 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/vfModulesMap.ts @@ -0,0 +1,5 @@ +import {VfModuleInstance} from "./vfModuleInstance"; + +export class VfModuleMap { + [id: string] : VfModuleInstance; +} diff --git a/vid-webpack-master/src/app/shared/models/vfcInstanceGroup.ts b/vid-webpack-master/src/app/shared/models/vfcInstanceGroup.ts new file mode 100644 index 00000000..64354b01 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/vfcInstanceGroup.ts @@ -0,0 +1,14 @@ +import {VfcInstanceGroupProperties} from "./vfcInstanceGroupProperties"; + +export class VfcInstanceGroup { + name: string; + version: string; + uuid: string; + invariantUuid: string; + vfcInstanceGroupProperties: VfcInstanceGroupProperties; + + +} + + + diff --git a/vid-webpack-master/src/app/shared/models/vfcInstanceGroupMap.ts b/vid-webpack-master/src/app/shared/models/vfcInstanceGroupMap.ts new file mode 100644 index 00000000..7ee3888c --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/vfcInstanceGroupMap.ts @@ -0,0 +1,5 @@ +import {VfcInstanceGroup} from "./vfcInstanceGroup"; + +export class VfcInstanceGroupMap { + [id: string] : VfcInstanceGroup; +} diff --git a/vid-webpack-master/src/app/shared/models/vfcInstanceGroupProperties.ts b/vid-webpack-master/src/app/shared/models/vfcInstanceGroupProperties.ts new file mode 100644 index 00000000..1a7bf718 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/vfcInstanceGroupProperties.ts @@ -0,0 +1,7 @@ +export class VfcInstanceGroupProperties { + networkCollectionFunction: string; + subinterfaceRole: string; + vfcInstanceGroupFunction: string; + vfcParentPortRole: string; + +} diff --git a/vid-webpack-master/src/app/shared/models/vnfInstance.ts b/vid-webpack-master/src/app/shared/models/vnfInstance.ts new file mode 100644 index 00000000..7f41e483 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/vnfInstance.ts @@ -0,0 +1,19 @@ +import {VfModuleMap} from "./vfModulesMap"; + +export class VnfInstance { + instanceName: string; + isUserProvidedNaming: boolean; + productFamilyId: string; + lcpCloudRegionId: string; + legacyRegion: string; + tenantId: string; + platformName: string; + lineOfBusiness: string; + rollbackOnFailure: string; + vfModules: { [vf_module_model_name: string] : VfModuleMap; }; + + constructor() { + this.rollbackOnFailure = 'true'; + this.vfModules = {}; + } +} diff --git a/vid-webpack-master/src/app/shared/models/vnfModel.ts b/vid-webpack-master/src/app/shared/models/vnfModel.ts new file mode 100644 index 00000000..e1302f1d --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/vnfModel.ts @@ -0,0 +1,52 @@ +import {NodeModel, NodeModelResponseInterface} from "./nodeModel"; +import {VfcInstanceGroupMap} from "./vfcInstanceGroupMap"; + + +export interface VnfProperties { + ecomp_generated_naming: string +} + +export interface VNFModelResponseInterface extends NodeModelResponseInterface{ + + serviceType: string; + serviceRole: string; + subCategory: string; + customizationUuid: string; + serviceEcompNaming: boolean; + type: string; + modelCustomizationName: string; + properties: VnfProperties; + vfcInstanceGroups: VfcInstanceGroupMap; +} + +export class VNFModel extends NodeModel{ + + serviceType: string; + serviceRole: string; + subCategory: string; + customizationUuid: string; + isUserProvidedNaming: boolean; + type: string; + modelCustomizationName: string; + vfcInstanceGroups: VfcInstanceGroupMap; + + constructor(vnfJson?: VNFModelResponseInterface){ + super(vnfJson); + if (vnfJson) { + this.serviceType = vnfJson.serviceType; + this.serviceRole = vnfJson.serviceRole; + this.subCategory = vnfJson.subCategory; + this.customizationUuid = vnfJson.customizationUuid; + this.isUserProvidedNaming = this.getIsUserProvidedName(vnfJson); + this.type = vnfJson.type; + this.modelCustomizationName = vnfJson.modelCustomizationName; + this.vfcInstanceGroups = vnfJson.vfcInstanceGroups; + + } + } + + private getIsUserProvidedName(vnfJson) { + const ecompGeneratedNaming = vnfJson.properties.ecomp_generated_naming; + return ecompGeneratedNaming !== undefined && ecompGeneratedNaming === "false"; + }; +} diff --git a/vid-webpack-master/src/app/shared/models/vnfTreeNode.ts b/vid-webpack-master/src/app/shared/models/vnfTreeNode.ts new file mode 100644 index 00000000..316bf3e8 --- /dev/null +++ b/vid-webpack-master/src/app/shared/models/vnfTreeNode.ts @@ -0,0 +1,18 @@ +import {VNFModel} from "./vnfModel"; +import {VnfInstance} from "./vnfInstance"; +import {VfModuleTreeNode} from "./vfModuleTreeNode"; + +export class VnfTreeNode { + modelId: string; + name: string; + modelName: string; + type: string; + children: VfModuleTreeNode[]; + + constructor(instance: VnfInstance, vnfModel: VNFModel){ + this.name = instance.instanceName || vnfModel['properties'].ecomp_generated_naming == 'false' ? vnfModel.modelCustomizationName : '<Automatically Assigned>'; + this.modelId = vnfModel.uuid; + this.modelName = vnfModel.modelCustomizationName; + this.type = vnfModel.type; + } +} diff --git a/vid-webpack-master/src/app/shared/pipes/capitalize/capitalize-and-format.pipe.spec.ts b/vid-webpack-master/src/app/shared/pipes/capitalize/capitalize-and-format.pipe.spec.ts new file mode 100644 index 00000000..84d2ff4b --- /dev/null +++ b/vid-webpack-master/src/app/shared/pipes/capitalize/capitalize-and-format.pipe.spec.ts @@ -0,0 +1,25 @@ + +import {CapitalizeAndFormatPipe} from "./capitalize-and-format.pipe"; + +describe('Capitalize And Format Pipe', () => { + let capitalizeAndFormatPipe: CapitalizeAndFormatPipe; + + beforeEach(() => { + capitalizeAndFormatPipe = new CapitalizeAndFormatPipe(); + }); + + it('Capitalize And Format Pipe should be defined', () => { + expect(capitalizeAndFormatPipe).toBeDefined(); + }); + + it('Capitalize And Format Pipe : (UPPERCASE)', ()=> { + let result: string = capitalizeAndFormatPipe.transform('PENDING'); + expect(result).toEqual('Pending'); + }); + + it('Capitalize And Format Pipe (UPPERCASE) and Underscore should replace by -', ()=> { + let result: string = capitalizeAndFormatPipe.transform('IN_PROGRESS'); + expect(result).toEqual('In-progress'); + }); + +}); diff --git a/vid-webpack-master/src/app/shared/pipes/capitalize/capitalize-and-format.pipe.ts b/vid-webpack-master/src/app/shared/pipes/capitalize/capitalize-and-format.pipe.ts new file mode 100644 index 00000000..e3ec9ae9 --- /dev/null +++ b/vid-webpack-master/src/app/shared/pipes/capitalize/capitalize-and-format.pipe.ts @@ -0,0 +1,12 @@ +import {PipeTransform, Pipe} from '@angular/core'; + +@Pipe({ name: 'capitalizeAndFormat' }) +export class CapitalizeAndFormatPipe implements PipeTransform { + transform(text: string): string { + if (text) { + text = text.toLowerCase().replace('_', '-'); + return text.charAt(0).toUpperCase() + text.slice(1); + } + return text; + } +} diff --git a/vid-webpack-master/src/app/shared/pipes/data-filter.pipe.ts b/vid-webpack-master/src/app/shared/pipes/data-filter.pipe.ts new file mode 100644 index 00000000..1ff83676 --- /dev/null +++ b/vid-webpack-master/src/app/shared/pipes/data-filter.pipe.ts @@ -0,0 +1,29 @@ +/** + * Created by cp2122 on 1/4/2018. + */ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'dataFilter' +}) +export class DataFilterPipe implements PipeTransform { + keys = []; + transform(items: any, args: string): any { + if (items != null && items.length > 0) { + let ans = []; + + if (this.keys.length === 0) { + this.keys = Object.keys(items[0]); + } + for (let i of items) { + for (let k of this.keys) { + if (i[k] !== null && i[k].toString().match('^.*' + args + '.*$')) { + ans.push(i); + break; + } + } + } + return ans; + } + } +} diff --git a/vid-webpack-master/src/app/shared/pipes/dynamicInputLabel/dynamic-input-label.pipe.spec.ts b/vid-webpack-master/src/app/shared/pipes/dynamicInputLabel/dynamic-input-label.pipe.spec.ts new file mode 100644 index 00000000..22b61929 --- /dev/null +++ b/vid-webpack-master/src/app/shared/pipes/dynamicInputLabel/dynamic-input-label.pipe.spec.ts @@ -0,0 +1,43 @@ +import { DynamicInputLabelPipe } from './dynamic-input-label.pipe'; + +describe('Dynamic input label Pipe', () => { + let dynamicInputLabelPipe: DynamicInputLabelPipe; + + beforeEach(() => { + dynamicInputLabelPipe = new DynamicInputLabelPipe(); + }); + + it('Dynamic input label Pipe should be defined', () => { + expect(dynamicInputLabelPipe).toBeDefined(); + }); + + it('Dynamic input label Pipe : Empty string should return empty string', ()=> { + let result: string = dynamicInputLabelPipe.transform(''); + expect(result).toEqual(':*'); + }); + + it('Dynamic input label Pipe: vnf should be VNF (UPPERCASE)', ()=> { + let result: string = dynamicInputLabelPipe.transform('vnf'); + expect(result).toEqual('VNF:*'); + }); + + it('Dynamic input label Pipe : nf should be NF (UPPERCASE)\'', ()=> { + let result: string = dynamicInputLabelPipe.transform('nf'); + expect(result).toEqual('NF:*'); + }); + + it('Dynamic input label Pipe : Underscore should replace by empty character', ()=> { + let result: string = dynamicInputLabelPipe.transform('nf_Test'); + expect(result).toEqual('NF test:*'); + }); + + it('Dynamic input label Pipe : Complex string', ()=> { + let result: string = dynamicInputLabelPipe.transform('nf_Test_vnf_nf'); + expect(result).toEqual('NF test VNF NF:*'); + }); + + it('Dynamic input label Pipe : First letter should be uppercase', ()=> { + let result: string = dynamicInputLabelPipe.transform('nfr'); + expect(result).toEqual('Nfr:*'); + }); +}); diff --git a/vid-webpack-master/src/app/shared/pipes/dynamicInputLabel/dynamic-input-label.pipe.ts b/vid-webpack-master/src/app/shared/pipes/dynamicInputLabel/dynamic-input-label.pipe.ts new file mode 100644 index 00000000..bec87b46 --- /dev/null +++ b/vid-webpack-master/src/app/shared/pipes/dynamicInputLabel/dynamic-input-label.pipe.ts @@ -0,0 +1,12 @@ +import {PipeTransform, Pipe} from '@angular/core'; + +@Pipe({ name: 'dynamicInputLabel' }) +export class DynamicInputLabelPipe implements PipeTransform { + transform(text: string): string { + let split_label = text.toLowerCase().replace(/_/g,' '); + let uppercase_vnf = split_label.replace(/\bvnf\b/ig, 'VNF'); + let uppercase_nf = uppercase_vnf.replace(/\bnf\b/ig, 'NF'); + let capitalize_sentence = uppercase_nf.charAt(0).toUpperCase() + uppercase_nf.slice(1); + return capitalize_sentence + ':*'; + } +} diff --git a/vid-webpack-master/src/app/shared/pipes/highlight-filter.pipe.ts b/vid-webpack-master/src/app/shared/pipes/highlight-filter.pipe.ts new file mode 100644 index 00000000..93aecbf6 --- /dev/null +++ b/vid-webpack-master/src/app/shared/pipes/highlight-filter.pipe.ts @@ -0,0 +1,10 @@ +import {PipeTransform, Pipe} from '@angular/core'; + +@Pipe({ name: 'highlight' }) +export class HighlightPipe implements PipeTransform { + transform(text: string, search: string): string { + let pattern = search.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); + let regex = new RegExp(pattern, 'gi'); + return search ? text.replace(regex, (match) => `<span class="highlight">${match}</span>`) : text; + } +} diff --git a/vid-webpack-master/src/app/shared/pipes/serviceInfo/serviceInfo.pipe.spec.ts b/vid-webpack-master/src/app/shared/pipes/serviceInfo/serviceInfo.pipe.spec.ts new file mode 100644 index 00000000..984e3378 --- /dev/null +++ b/vid-webpack-master/src/app/shared/pipes/serviceInfo/serviceInfo.pipe.spec.ts @@ -0,0 +1,58 @@ +import {ServiceInfoPipe} from "./serviceInfo.pipe"; + + +describe('Service info Pipe', () => { + let pipe: ServiceInfoPipe; + + beforeEach(() => { + pipe = new ServiceInfoPipe(); + }); + + it('Service info Pipe should return model name', () => { + let store = { + getState : function() { + return { + service: { + serviceHierarchy : generateserviceHierarchy() + } + } + } + }; + let result : string = pipe.transform(null, store , '6e59c5de-f052-46fa-aa7e-2fca9d674c44', 'name'); + expect(result).toEqual('ComplexService') + }); + + + + it('Service info Pipe should return null if field name not exist', () => { + let store = { + getState : function() { + return { + service: { + serviceHierarchy : generateserviceHierarchy() + } + } + } + }; + let result : string = pipe.transform(null, store , '6e59c5de-f052-46fa-aa7e-2fca9d674c44', 'notExist'); + expect(result).toBeNull(); + }); + + it('Service info Pipe should return null if model not exist', () => { + let store = { + getState : function() { + return { + service: { + serviceHierarchy : generateserviceHierarchy() + } + } + } + }; + let result : string = pipe.transform(null, store , 'modelNotExist', 'name'); + expect(result).toBeNull(); + }); + + function generateserviceHierarchy(){ + return JSON.parse('{"6e59c5de-f052-46fa-aa7e-2fca9d674c44":{"service":{"uuid":"6e59c5de-f052-46fa-aa7e-2fca9d674c44","invariantUuid":"e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0","name":"ComplexService","version":"1.0","toscaModelURL":null,"category":"Mobility","serviceType":"","serviceRole":"","description":"ComplexService","serviceEcompNaming":"true","instantiationType":"Macro","inputs":{}},"vnfs":{"VF_vMee 0":{"uuid":"d6557200-ecf2-4641-8094-5393ae3aae60","invariantUuid":"4160458e-f648-4b30-a176-43881ffffe9e","description":"VSP_vMee","name":"VF_vMee","version":"2.0","customizationUuid":"91415b44-753d-494c-926a-456a9172bbb9","inputs":{},"commands":{},"properties":{"gpb2_Internal2_mac":"00:80:37:0E:02:22","sctp-b-ipv6-egress_src_start_port":"0","sctp-a-ipv6-egress_rule_application":"any","Internal2_allow_transit":"true","sctp-b-IPv6_ethertype":"IPv6","sctp-a-egress_rule_application":"any","sctp-b-ingress_action":"pass","sctp-b-ingress_rule_protocol":"icmp","ncb2_Internal1_mac":"00:80:37:0E:0F:12","sctp-b-ipv6-ingress-src_start_port":"0.0","ncb1_Internal2_mac":"00:80:37:0E:09:12","fsb_volume_size_0":"320.0","sctp-b-egress_src_addresses":"local","sctp-a-ipv6-ingress_ethertype":"IPv4","sctp-a-ipv6-ingress-dst_start_port":"0","sctp-b-ipv6-ingress_rule_application":"any","domain_name":"default-domain","sctp-a-ingress_rule_protocol":"icmp","sctp-b-egress-src_start_port":"0.0","sctp-a-egress_src_addresses":"local","sctp-b-display_name":"epc-sctp-b-ipv4v6-sec-group","sctp-a-egress-src_start_port":"0.0","sctp-a-ingress_ethertype":"IPv4","sctp-b-ipv6-ingress-dst_end_port":"65535","sctp-b-dst_subnet_prefix_v6":"::","nf_naming":"{ecomp_generated_naming=true}","sctp-a-ipv6-ingress_src_subnet_prefix":"0.0.0.0","sctp-b-egress-dst_start_port":"0.0","ncb_flavor_name":"nv.c20r64d1","gpb1_Internal1_mac":"00:80:37:0E:01:22","sctp-b-egress_dst_subnet_prefix_len":"0.0","Internal2_net_cidr":"169.255.0.0","sctp-a-ingress-dst_start_port":"0.0","sctp-a-egress-dst_start_port":"0.0","fsb1_Internal2_mac":"00:80:37:0E:0B:12","sctp-a-egress_ethertype":"IPv4","vlc_st_service_mode":"in-network-nat","sctp-a-ipv6-egress_ethertype":"IPv4","sctp-a-egress-src_end_port":"65535.0","sctp-b-ipv6-egress_rule_application":"any","sctp-b-egress_action":"pass","sctp-a-ingress-src_subnet_prefix_len":"0.0","sctp-b-ipv6-ingress-src_end_port":"65535.0","sctp-b-name":"epc-sctp-b-ipv4v6-sec-group","fsb2_Internal1_mac":"00:80:37:0E:0D:12","sctp-a-ipv6-ingress-src_start_port":"0.0","sctp-b-ipv6-egress_ethertype":"IPv4","Internal1_net_cidr":"169.253.0.0","sctp-a-egress_dst_subnet_prefix":"0.0.0.0","fsb_flavor_name":"nv.c20r64d1","sctp_rule_protocol":"132","sctp-b-ipv6-ingress_src_subnet_prefix_len":"0","sctp-a-ipv6-ingress_rule_application":"any","ecomp_generated_naming":"true","sctp-a-IPv6_ethertype":"IPv6","vlc2_Internal1_mac":"00:80:37:0E:02:12","vlc_st_virtualization_type":"virtual-machine","sctp-b-ingress-dst_start_port":"0.0","sctp-b-ingress-dst_end_port":"65535.0","sctp-a-ipv6-ingress-src_end_port":"65535.0","sctp-a-display_name":"epc-sctp-a-ipv4v6-sec-group","sctp-b-ingress_rule_application":"any","int2_sec_group_name":"int2-sec-group","vlc_flavor_name":"nd.c16r64d1","sctp-b-ipv6-egress_src_addresses":"local","vlc_st_interface_type_int1":"other1","sctp-b-egress-src_end_port":"65535.0","sctp-a-ipv6-egress-dst_start_port":"0","vlc_st_interface_type_int2":"other2","sctp-a-ipv6-egress_rule_protocol":"any","Internal2_shared":"false","sctp-a-ipv6-egress_dst_subnet_prefix_len":"0","Internal2_rpf":"disable","vlc1_Internal1_mac":"00:80:37:0E:01:12","sctp-b-ipv6-egress_src_end_port":"65535","sctp-a-ipv6-egress_src_addresses":"local","sctp-a-ingress-dst_end_port":"65535.0","sctp-a-ipv6-egress_src_end_port":"65535","Internal1_forwarding_mode":"l2","Internal2_dhcp":"false","sctp-a-dst_subnet_prefix_v6":"::","pxe_image_name":"MME_PXE-Boot_16ACP04_GA.qcow2","vlc_st_interface_type_gtp":"other0","ncb1_Internal1_mac":"00:80:37:0E:09:12","sctp-b-src_subnet_prefix_v6":"::","sctp-a-egress_dst_subnet_prefix_len":"0.0","int1_sec_group_name":"int1-sec-group","Internal1_dhcp":"false","sctp-a-ipv6-egress_dst_end_port":"65535","Internal2_forwarding_mode":"l2","fsb2_Internal2_mac":"00:80:37:0E:0D:12","sctp-b-egress_dst_subnet_prefix":"0.0.0.0","Internal1_net_cidr_len":"17","gpb2_Internal1_mac":"00:80:37:0E:02:22","sctp-b-ingress-src_subnet_prefix_len":"0.0","sctp-a-ingress_dst_addresses":"local","sctp-a-egress_action":"pass","fsb_volume_type_0":"SF-Default-SSD","ncb2_Internal2_mac":"00:80:37:0E:0F:12","vlc_st_interface_type_sctp_a":"left","vlc_st_interface_type_sctp_b":"right","sctp-a-src_subnet_prefix_v6":"::","vlc_st_version":"2","sctp-b-egress_ethertype":"IPv4","sctp-a-ingress_rule_application":"any","gpb1_Internal2_mac":"00:80:37:0E:01:22","instance_ip_family_v6":"v6","sctp-a-ipv6-egress_src_start_port":"0","sctp-b-ingress-src_start_port":"0.0","sctp-b-ingress_dst_addresses":"local","fsb1_Internal1_mac":"00:80:37:0E:0B:12","vlc_st_interface_type_oam":"management","multi_stage_design":"false","oam_sec_group_name":"oam-sec-group","Internal2_net_gateway":"169.255.0.3","sctp-a-ipv6-ingress-dst_end_port":"65535","sctp-b-ipv6-egress-dst_start_port":"0","Internal1_net_gateway":"169.253.0.3","sctp-b-ipv6-egress_rule_protocol":"any","gtp_sec_group_name":"gtp-sec-group","sctp-a-ipv6-egress_dst_subnet_prefix":"0.0.0.0","sctp-b-ipv6-egress_dst_subnet_prefix_len":"0","sctp-a-ipv6-ingress_dst_addresses":"local","sctp-a-egress_rule_protocol":"icmp","sctp-b-ipv6-egress_action":"pass","sctp-a-ipv6-egress_action":"pass","Internal1_shared":"false","sctp-b-ipv6-ingress_rule_protocol":"any","Internal2_net_cidr_len":"17","sctp-a-name":"epc-sctp-a-ipv4v6-sec-group","sctp-a-ingress-src_end_port":"65535.0","sctp-b-ipv6-ingress_src_subnet_prefix":"0.0.0.0","sctp-a-egress-dst_end_port":"65535.0","sctp-a-ingress_action":"pass","sctp-b-egress_rule_protocol":"icmp","sctp-b-ipv6-ingress_action":"pass","vlc_st_service_type":"firewall","sctp-b-ipv6-egress_dst_end_port":"65535","sctp-b-ipv6-ingress-dst_start_port":"0","vlc2_Internal2_mac":"00:80:37:0E:02:12","vlc_st_availability_zone":"true","fsb_volume_image_name_1":"MME_FSB2_16ACP04_GA.qcow2","sctp-b-ingress-src_subnet_prefix":"0.0.0.0","sctp-a-ipv6-ingress_src_subnet_prefix_len":"0","Internal1_allow_transit":"true","gpb_flavor_name":"nv.c20r64d1","availability_zone_max_count":"1","fsb_volume_image_name_0":"MME_FSB1_16ACP04_GA.qcow2","sctp-b-ipv6-ingress_dst_addresses":"local","sctp-b-ipv6-egress_dst_subnet_prefix":"0.0.0.0","sctp-b-ipv6-ingress_ethertype":"IPv4","vlc1_Internal2_mac":"00:80:37:0E:01:12","sctp-a-ingress-src_subnet_prefix":"0.0.0.0","sctp-a-ipv6-ingress_action":"pass","Internal1_rpf":"disable","sctp-b-ingress_ethertype":"IPv4","sctp-b-egress_rule_application":"any","sctp-b-ingress-src_end_port":"65535.0","sctp-a-ipv6-ingress_rule_protocol":"any","sctp-a-ingress-src_start_port":"0.0","sctp-b-egress-dst_end_port":"65535.0"},"type":"VF","modelCustomizationName":"VF_vMee 0","vfModules":{"vf_vmee0..VfVmee..vmme_vlc..module-1":{"uuid":"522159d5-d6e0-4c2a-aa44-5a542a12a830","invariantUuid":"98a7c88b-b577-476a-90e4-e25a5871e02b","customizationUuid":"55b1be94-671a-403e-a26c-667e9c47d091","description":null,"name":"VfVmee..vmme_vlc..module-1","version":"2","modelCustomizationName":"VfVmee..vmme_vlc..module-1","properties":{"minCountInstances":0,"maxCountInstances":null,"initialCount":0,"vfModuleLabel":"vmme_vlc"},"inputs":{},"volumeGroupAllowed":false},"vf_vmee0..VfVmee..vmme_gpb..module-2":{"uuid":"41708296-e443-4c71-953f-d9a010f059e1","invariantUuid":"1cca90b8-3490-495e-87da-3f3e4c57d5b9","customizationUuid":"6add59e0-7fe1-4bc4-af48-f8812422ae7c","description":null,"name":"VfVmee..vmme_gpb..module-2","version":"2","modelCustomizationName":"VfVmee..vmme_gpb..module-2","properties":{"minCountInstances":0,"maxCountInstances":null,"initialCount":0,"vfModuleLabel":"vmme_gpb"},"inputs":{},"volumeGroupAllowed":false},"vf_vmee0..VfVmee..base_vmme..module-0":{"uuid":"a27f5cfc-7f12-4f99-af08-0af9c3885c87","invariantUuid":"a6f9e51a-2b35-416a-ae15-15e58d61f36d","customizationUuid":"f8c040f1-7e51-4a11-aca8-acf256cfd861","description":null,"name":"VfVmee..base_vmme..module-0","version":"2","modelCustomizationName":"VfVmee..base_vmme..module-0","properties":{"minCountInstances":1,"maxCountInstances":1,"initialCount":1,"vfModuleLabel":"base_vmme"},"inputs":{},"volumeGroupAllowed":true}},"volumeGroups":{"vf_vmee0..VfVmee..base_vmme..module-0":{"uuid":"a27f5cfc-7f12-4f99-af08-0af9c3885c87","invariantUuid":"a6f9e51a-2b35-416a-ae15-15e58d61f36d","customizationUuid":"f8c040f1-7e51-4a11-aca8-acf256cfd861","description":null,"name":"VfVmee..base_vmme..module-0","version":"2","modelCustomizationName":"VfVmee..base_vmme..module-0","properties":{"minCountInstances":1,"maxCountInstances":1,"initialCount":1,"vfModuleLabel":"base_vmme"},"inputs":{}}},"vfcInstanceGroups":{}}},"networks":{"ExtVL 0":{"uuid":"ddc3f20c-08b5-40fd-af72-c6d14636b986","invariantUuid":"379f816b-a7aa-422f-be30-17114ff50b7c","description":"ECOMP generic virtual link (network) base type for all other service-level and global networks","name":"ExtVL","version":"37.0","customizationUuid":"94fdd893-4a36-4d70-b16a-ec29c54c184f","inputs":{},"commands":{},"properties":{"network_assignments":"{is_external_network=false, ipv4_subnet_default_assignment={min_subnets_count=1}, ecomp_generated_network_assignment=false, ipv6_subnet_default_assignment={min_subnets_count=1}}","exVL_naming":"{ecomp_generated_naming=true}","network_flows":"{is_network_policy=false, is_bound_to_vpn=false}","network_homing":"{ecomp_selected_instance_node_target=false}"},"type":"VL","modelCustomizationName":"ExtVL 0"}},"collectionResource":{},"configurations":{"Port Mirroring Configuration By Policy 0":{"uuid":"b4398538-e89d-4f13-b33d-ca323434ba50","invariantUuid":"6ef0ca40-f366-4897-951f-abd65d25f6f7","description":"A port mirroring configuration by policy object","name":"Port Mirroring Configuration By Policy","version":"27.0","customizationUuid":"3c3b7b8d-8669-4b3b-8664-61970041fad2","inputs":{},"commands":{},"properties":{},"type":"Configuration","modelCustomizationName":"Port Mirroring Configuration By Policy 0","sourceNodes":[],"collectorNodes":null,"configurationByPolicy":false}},"serviceProxies":{},"vfModules":{"vf_vmee0..VfVmee..vmme_vlc..module-1":{"uuid":"522159d5-d6e0-4c2a-aa44-5a542a12a830","invariantUuid":"98a7c88b-b577-476a-90e4-e25a5871e02b","customizationUuid":"55b1be94-671a-403e-a26c-667e9c47d091","description":null,"name":"VfVmee..vmme_vlc..module-1","version":"2","modelCustomizationName":"VfVmee..vmme_vlc..module-1","properties":{"minCountInstances":0,"maxCountInstances":null,"initialCount":0,"vfModuleLabel":"vmme_vlc"},"inputs":{},"volumeGroupAllowed":false},"vf_vmee0..VfVmee..vmme_gpb..module-2":{"uuid":"41708296-e443-4c71-953f-d9a010f059e1","invariantUuid":"1cca90b8-3490-495e-87da-3f3e4c57d5b9","customizationUuid":"6add59e0-7fe1-4bc4-af48-f8812422ae7c","description":null,"name":"VfVmee..vmme_gpb..module-2","version":"2","modelCustomizationName":"VfVmee..vmme_gpb..module-2","properties":{"minCountInstances":0,"maxCountInstances":null,"initialCount":0,"vfModuleLabel":"vmme_gpb"},"inputs":{},"volumeGroupAllowed":false},"vf_vmee0..VfVmee..base_vmme..module-0":{"uuid":"a27f5cfc-7f12-4f99-af08-0af9c3885c87","invariantUuid":"a6f9e51a-2b35-416a-ae15-15e58d61f36d","customizationUuid":"f8c040f1-7e51-4a11-aca8-acf256cfd861","description":null,"name":"VfVmee..base_vmme..module-0","version":"2","modelCustomizationName":"VfVmee..base_vmme..module-0","properties":{"minCountInstances":1,"maxCountInstances":1,"initialCount":1,"vfModuleLabel":"base_vmme"},"inputs":{},"volumeGroupAllowed":true}},"volumeGroups":{"vf_vmee0..VfVmee..base_vmme..module-0":{"uuid":"a27f5cfc-7f12-4f99-af08-0af9c3885c87","invariantUuid":"a6f9e51a-2b35-416a-ae15-15e58d61f36d","customizationUuid":"f8c040f1-7e51-4a11-aca8-acf256cfd861","description":null,"name":"VfVmee..base_vmme..module-0","version":"2","modelCustomizationName":"VfVmee..base_vmme..module-0","properties":{"minCountInstances":1,"maxCountInstances":1,"initialCount":1,"vfModuleLabel":"base_vmme"},"inputs":{}}},"pnfs":{}}}'); + } +}); diff --git a/vid-webpack-master/src/app/shared/pipes/serviceInfo/serviceInfo.pipe.ts b/vid-webpack-master/src/app/shared/pipes/serviceInfo/serviceInfo.pipe.ts new file mode 100644 index 00000000..8cb2e1dc --- /dev/null +++ b/vid-webpack-master/src/app/shared/pipes/serviceInfo/serviceInfo.pipe.ts @@ -0,0 +1,13 @@ +import {PipeTransform, Pipe} from '@angular/core'; +import {isNullOrUndefined} from "util"; + +@Pipe({ name: 'serviceInfo'}) +export class ServiceInfoPipe implements PipeTransform { + transform(service: string, store : any , modelId : string, fieldName : string): string { + const serviceHierarchy = store.getState().service.serviceHierarchy; + if(!isNullOrUndefined(serviceHierarchy) && !isNullOrUndefined(serviceHierarchy[modelId])){ + return serviceHierarchy[modelId].service[fieldName] || null; + } + return null; + } +} diff --git a/vid-webpack-master/src/app/shared/server/healthStatusService/health-status.service.spec.ts b/vid-webpack-master/src/app/shared/server/healthStatusService/health-status.service.spec.ts new file mode 100644 index 00000000..36f6349e --- /dev/null +++ b/vid-webpack-master/src/app/shared/server/healthStatusService/health-status.service.spec.ts @@ -0,0 +1,60 @@ +import {TestBed, inject, getTestBed} from '@angular/core/testing'; + +import { HealthStatusService } from './health-status.service'; +import {Constants} from "../../utils/constants"; +import {HttpClientTestingModule, HttpTestingController} from "@angular/common/http/testing"; +import {ExternalComponentStatus} from "../../models/externalComponentStatus"; + +describe('HealthStatusService', () => { + + let injector; + let service: HealthStatusService; + let httpMock: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [HealthStatusService] + }); + + injector = getTestBed(); + service = injector.get(HealthStatusService); + httpMock = injector.get(HttpTestingController); + }); + + describe('#getProbe', () =>{ + it('when call probe, there is http GET with right url', () => { + + service.getProbe().subscribe((result: Array<ExternalComponentStatus>)=>{ + expect(result[0].component).toEqual("AAI"); + expect(result[0].available).toBeTruthy(); + expect(result[0].metadata).toEqual({ myKey: 'myValue' }); + + expect(result[1].component).toEqual("MSO"); + expect(result[1].available).toBeFalsy(); + expect(result[1].metadata).toEqual({otherKey: 123}); + }); + + const req = httpMock.expectOne(Constants.Path.SERVICES_PROBE_PATH); + expect(req.request.method).toEqual('GET'); + req.flush([ + { + "component": "AAI", + "available": true, + "metadata": { + "myKey": "myValue" + } + }, + { + "component": "MSO", + "available": false, + "metadata": { + "otherKey": 123 + } + }, + ]); + }); + + }); + +}); diff --git a/vid-webpack-master/src/app/shared/server/healthStatusService/health-status.service.ts b/vid-webpack-master/src/app/shared/server/healthStatusService/health-status.service.ts new file mode 100644 index 00000000..4305ab97 --- /dev/null +++ b/vid-webpack-master/src/app/shared/server/healthStatusService/health-status.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; +import {HttpClient} from "@angular/common/http"; +import {Observable} from "rxjs/Observable"; +import {Constants} from "../../utils/constants"; +import {ExternalComponentStatus} from "../../models/externalComponentStatus"; + +@Injectable() +export class HealthStatusService { + + constructor(private _http: HttpClient) { + } + + getProbe(): Observable<Array<ExternalComponentStatus>> { + let pathQuery = Constants.Path.SERVICES_PROBE_PATH; + return this._http.get<Array<ExternalComponentStatus>>(pathQuery).map(res => res); + } +} diff --git a/vid-webpack-master/src/app/shared/server/serviceInfo/AuditStatus.model.ts b/vid-webpack-master/src/app/shared/server/serviceInfo/AuditStatus.model.ts new file mode 100644 index 00000000..0b4c70f9 --- /dev/null +++ b/vid-webpack-master/src/app/shared/server/serviceInfo/AuditStatus.model.ts @@ -0,0 +1,10 @@ +export class AuditStatus{ + id: number; + createdDate: number; + final: boolean; + jobId :string; + jobStatus :string; + source: string; + requestId: string; + additionalInfo :any +} diff --git a/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.model.ts b/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.model.ts new file mode 100644 index 00000000..0b469593 --- /dev/null +++ b/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.model.ts @@ -0,0 +1,38 @@ +import {ServiceStatus} from '../../../instantiationStatus/instantiationStatus.component.service'; + +export class ServiceInfoModel { + id: number; + created: Date; + modified: Date; + createdId: number; + modifiedId: number; + numRow: number; + uuid: string; + userId: string; + jobStatus: string; + pause: boolean; + owningEntityId: string; + owningEntityName: string; + project: string; + aicZoneId: string; + aicZoneName: string; + tenantId: string; + tenantName: string; + regionId: string; + regionName: string; + serviceType: string; + subscriberName: string; + serviceInstanceId: string; + serviceInstanceName: string; + serviceModelId: string; + serviceModelName: string; + serviceModelVersion: string; + templateId: string; + auditUserId: string; + jobId: string; +} + +export class ServiceInfoUiModel extends ServiceInfoModel{ + serviceStatus : ServiceStatus; + serviceIndex : number; +} diff --git a/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.service.spec.ts b/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.service.spec.ts new file mode 100644 index 00000000..78c1b5ab --- /dev/null +++ b/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.service.spec.ts @@ -0,0 +1,235 @@ +import {getTestBed, TestBed} from '@angular/core/testing'; +import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; +import {ServiceInfoService} from './serviceInfo.service'; +import {ServiceInfoModel} from './serviceInfo.model'; +import {Constants} from "../../utils/constants"; + +describe('Service Info Service', () => { + let injector; + let service: ServiceInfoService; + let httpMock: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ServiceInfoService] + }); + + injector = getTestBed(); + service = injector.get(ServiceInfoService); + httpMock = injector.get(HttpTestingController); + }); + + describe('#getServicesJobInfo', () => { + it('should return an Observable<ServiceInfoModel[]>', () => { + const dummyServiceInfo: ServiceInfoModel[] = generateServiceInfoData(); + + service.getServicesJobInfo(true).subscribe((serviceInfo:Array<ServiceInfoModel>) => { + expect(serviceInfo).toEqual(dummyServiceInfo); + }); + }); + + + }); + + describe('#deleteJob', () =>{ + it('delete job', () => { + const jobId : string = "1111"; + + service.deleteJob(jobId).subscribe(); + + const req = httpMock.expectOne(Constants.Path.SERVICES_JOB_INFO_PATH + '/job/' + jobId); + expect(req.request.method).toEqual('DELETE'); + + }); + }); + + describe('#hideJob', () =>{ + it('when call hide job, there is http POST with right url', () => { + const jobId : string = "3"; + + service.hideJob(jobId).subscribe(); + + const req = httpMock.expectOne(Constants.Path.SERVICES_JOB_INFO_PATH + '/hide/' + jobId); + expect(req.request.method).toEqual('POST'); + }); + }); + + describe('#getJobAuditStatus', ()=> { + it('should return Observable<Object[]>', ()=>{ + const jobId : string = '111'; + + service.getJobAuditStatus(jobId).subscribe(); + const req = httpMock.expectOne(Constants.Path.SERVICES_JOB_INFO_PATH + Constants.Path.SERVICES_JOB_AUDIT_PATH + '/' + jobId + '?source=VID'); + const req2 = httpMock.expectOne(Constants.Path.SERVICES_JOB_INFO_PATH + Constants.Path.SERVICES_JOB_AUDIT_PATH + '/' + jobId + '?source=MSO'); + + expect(req.request.method).toEqual('GET'); + expect(req2.request.method).toEqual('GET'); + }); + }); + + function generateServiceInfoData(){ + return <ServiceInfoModel[]>JSON.parse(JSON.stringify( + [{ + "created": 1519956533000, + "modified": 1521727738000, + "createdId": null, + "modifiedId": null, + "rowNum": null, + "auditUserId": null, + "auditTrail": null, + "jobId": "6748648484", + "userId": "2222", + "jobStatus": "FAILED", + "pause": false, + "owningEntityId": "1234", + "owningEntityName": null, + "project": null, + "aicZoneId": null, + "aicZoneName": null, + "tenantId": null, + "tenantName": null, + "regionId": null, + "regionName": null, + "serviceType": null, + "subscriberName": null, + "serviceInstanceId": "1", + "serviceInstanceName": null, + "serviceModelId": null, + "serviceModelName": null, + "serviceModelVersion": null, + "createdBulkDate": 1519956533000, + "statusModifiedDate": 1520042933000, + "hidden": false + }, + { + "created": 1519956533000, + "modified": 1521727738000, + "createdId": null, + "modifiedId": null, + "rowNum": null, + "auditUserId": null, + "auditTrail": null, + "jobId": "6748648484", + "userId": "2222", + "jobStatus": "FAILED", + "pause": false, + "owningEntityId": "1234", + "owningEntityName": null, + "project": null, + "aicZoneId": null, + "aicZoneName": null, + "tenantId": null, + "tenantName": null, + "regionId": null, + "regionName": null, + "serviceType": null, + "subscriberName": null, + "serviceInstanceId": "1", + "serviceInstanceName": null, + "serviceModelId": null, + "serviceModelName": null, + "serviceModelVersion": null, + "createdBulkDate": 1519956533000, + "statusModifiedDate": 1520042933000, + "hidden": false + }, + { + "created": 1519956533000, + "modified": 1521727738000, + "createdId": null, + "modifiedId": null, + "rowNum": null, + "auditUserId": null, + "auditTrail": null, + "jobId": "6748648484", + "userId": "2222", + "jobStatus": "FAILED", + "pause": false, + "owningEntityId": "1234", + "owningEntityName": null, + "project": null, + "aicZoneId": null, + "aicZoneName": null, + "tenantId": null, + "tenantName": null, + "regionId": null, + "regionName": null, + "serviceType": null, + "subscriberName": null, + "serviceInstanceId": "2", + "serviceInstanceName": null, + "serviceModelId": null, + "serviceModelName": null, + "serviceModelVersion": null, + "createdBulkDate": 1519956533000, + "statusModifiedDate": 1520042933000, + "hidden": false + }, + { + "created": 1519956533000, + "modified": 1521727738000, + "createdId": null, + "modifiedId": null, + "rowNum": null, + "auditUserId": null, + "auditTrail": null, + "jobId": "6748648484", + "userId": "2222", + "jobStatus": "FAILED", + "pause": false, + "owningEntityId": "1234", + "owningEntityName": null, + "project": null, + "aicZoneId": null, + "aicZoneName": null, + "tenantId": null, + "tenantName": null, + "regionId": null, + "regionName": null, + "serviceType": null, + "subscriberName": null, + "serviceInstanceId": "2", + "serviceInstanceName": null, + "serviceModelId": null, + "serviceModelName": null, + "serviceModelVersion": null, + "createdBulkDate": 1519956533000, + "statusModifiedDate": 1520042933000, + "hidden": false + }, + { + "created": 1519956533000, + "modified": 1521727738000, + "createdId": null, + "modifiedId": null, + "rowNum": null, + "auditUserId": null, + "auditTrail": null, + "jobId": "6748648484", + "userId": "2222", + "jobStatus": "FAILED", + "pause": false, + "owningEntityId": "1234", + "owningEntityName": null, + "project": null, + "aicZoneId": null, + "aicZoneName": null, + "tenantId": null, + "tenantName": null, + "regionId": null, + "regionName": null, + "serviceType": null, + "subscriberName": null, + "serviceInstanceId": "3", + "serviceInstanceName": null, + "serviceModelId": null, + "serviceModelName": null, + "serviceModelVersion": null, + "createdBulkDate": 1519956533000, + "statusModifiedDate": 1520042933000, + "hidden": false + }] + )); + } +}); diff --git a/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.service.ts b/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.service.ts new file mode 100644 index 00000000..e0057bb4 --- /dev/null +++ b/vid-webpack-master/src/app/shared/server/serviceInfo/serviceInfo.service.ts @@ -0,0 +1,38 @@ +import {Injectable} from '@angular/core'; +import {Observable} from 'rxjs/Observable'; +import {ServiceInfoModel} from './serviceInfo.model'; +import {HttpClient} from '@angular/common/http'; +import 'rxjs/add/operator/map' +import {Constants} from '../../utils/constants'; +import {forkJoin} from "rxjs/observable/forkJoin"; + +@Injectable() +export class ServiceInfoService { + constructor(private _http: HttpClient) { + } + + getServicesJobInfo(filterByUser : boolean): Observable<ServiceInfoModel[]> { + let pathQuery = Constants.Path.SERVICES_JOB_INFO_PATH; + return this._http.get<ServiceInfoModel[]>(pathQuery).map(res => res ); + } + + deleteJob(jobId: string): Observable<any> { + let pathQuery = Constants.Path.SERVICES_JOB_INFO_PATH + '/job/' + jobId; + return this._http.delete<any>(pathQuery).map(res => res); + } + + hideJob(jobId: string): Observable<any> { + let pathQuery = Constants.Path.SERVICES_JOB_INFO_PATH + '/hide/' + jobId; + return this._http.post<any>(pathQuery, null).map(res => res); + } + + getJobAuditStatus(jobId : string) : Observable<Object[]>{ + let pathQueryVID = Constants.Path.SERVICES_JOB_INFO_PATH + Constants.Path.SERVICES_JOB_AUDIT_PATH + '/' + jobId + '?source=VID'; + let pathQueryMSO = Constants.Path.SERVICES_JOB_INFO_PATH + Constants.Path.SERVICES_JOB_AUDIT_PATH + '/' + jobId + '?source=MSO'; + + let vidObs = this._http.get(pathQueryVID); + let msoObs = this._http.get(pathQueryMSO); + return forkJoin([vidObs, msoObs]); + } + +} diff --git a/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.spec.ts b/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.spec.ts new file mode 100644 index 00000000..617dbd3d --- /dev/null +++ b/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.spec.ts @@ -0,0 +1,73 @@ +import { getTestBed, TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { NgRedux } from '@angular-redux/store'; +import { DefaultDataGeneratorService } from './default.data.generator.service'; + +export class MockAppStore<T> {} + +describe('Default Data Generator Service', () => { + let injector; + let service: DefaultDataGeneratorService; + let httpMock: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [DefaultDataGeneratorService, + {provide: NgRedux, useClass: MockAppStore}] + }); + + injector = getTestBed(); + service = injector.get(DefaultDataGeneratorService); + httpMock = injector.get(HttpTestingController); + }); + + it('generateVFModule should create vf module object', () => { + const serviceHierarchy = generateServiceHierarchy(); + const vnfUUID: string = 'VF_vMee 0'; + const vnfModuleUUID: string = 'vf_vmee0..VfVmee..base_vmme..module-0'; + + let result = service.generateVFModule(serviceHierarchy, vnfUUID, vnfModuleUUID); + + expect(result.modelInfo.modelType).toEqual('VFmodule'); + expect(result.modelInfo.modelInvariantId).toEqual(serviceHierarchy.vnfs[vnfUUID].vfModules[vnfModuleUUID].invariantUuid); + expect(result.modelInfo.modelVersionId).toEqual(serviceHierarchy.vnfs[vnfUUID].vfModules[vnfModuleUUID].uuid); + expect(result.modelInfo.modelName).toEqual(serviceHierarchy.vnfs[vnfUUID].vfModules[vnfModuleUUID].name); + expect(result.modelInfo.modelVersion).toEqual(serviceHierarchy.vnfs[vnfUUID].vfModules[vnfModuleUUID].version); + expect(result.modelInfo.modelCustomizationId).toEqual(serviceHierarchy.vnfs[vnfUUID].vfModules[vnfModuleUUID].customizationUuid); + expect(result.modelInfo.modelCustomizationName).toEqual(serviceHierarchy.vnfs[vnfUUID].vfModules[vnfModuleUUID].modelCustomizationName); + expect(result.sdncPreReload).toBeNull(); + }); + + it('generateVNFData should create vnf object', () => { + const serviceHierarchy = generateServiceHierarchy(); + const vnfName: string = 'VF_vMee 0'; + const formValues = generateVNFFormValues(); + const vfUUID: string = 'vf_vmee0..VfVmee..base_vmme..module-0'; + + let result = service.generateVNFData(serviceHierarchy, vnfName, vfUUID, formValues); + + expect(result.productFamilyId).toEqual(formValues.productFamilyId); + expect(result.lcpCloudRegionId).toBeNull(); + expect(result.tenantId).toBeNull(); + expect(result.lineOfBusiness).toBeNull(); + expect(result.platformName).toBeNull(); + expect(result.modelInfo.modelType).toEqual('VF'); + expect(result.modelInfo.modelInvariantId).toEqual(serviceHierarchy.vnfs[vnfName].invariantUuid); + expect(result.modelInfo.modelVersionId).toEqual(formValues.modelInfo.modelVersionId); + expect(result.modelInfo.modelName).toEqual(serviceHierarchy.vnfs[vnfName].name); + expect(result.modelInfo.modelVersion).toEqual(serviceHierarchy.vnfs[vnfName].version); + expect(result.modelInfo.modelCustomizationId).toEqual(serviceHierarchy.vnfs[vnfName].modelCustomizationId); + expect(result.modelInfo.modelCustomizationName).toEqual(serviceHierarchy.vnfs[vnfName].modelCustomizationName); + }); +}); + + +function generateServiceHierarchy() { + return JSON.parse('{"service":{"uuid":"6e59c5de-f052-46fa-aa7e-2fca9d674c44","invariantUuid":"e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0","name":"ComplexService","version":"1.0","toscaModelURL":null,"category":"Mobility","serviceType":"","serviceRole":"","description":"ComplexService","serviceEcompNaming":"true","instantiationType":"Macro","inputs":{}},"vnfs":{"VF_vMee 0":{"uuid":"d6557200-ecf2-4641-8094-5393ae3aae60","invariantUuid":"4160458e-f648-4b30-a176-43881ffffe9e","description":"VSP_vMee","name":"VF_vMee","version":"2.0","customizationUuid":"91415b44-753d-494c-926a-456a9172bbb9","inputs":{},"commands":{},"properties":{"gpb2_Internal2_mac":"00:80:37:0E:02:22","sctp-b-ipv6-egress_src_start_port":"0","sctp-a-ipv6-egress_rule_application":"any","Internal2_allow_transit":"true","sctp-b-IPv6_ethertype":"IPv6","sctp-a-egress_rule_application":"any","sctp-b-ingress_action":"pass","sctp-b-ingress_rule_protocol":"icmp","ncb2_Internal1_mac":"00:80:37:0E:0F:12","sctp-b-ipv6-ingress-src_start_port":"0.0","ncb1_Internal2_mac":"00:80:37:0E:09:12","fsb_volume_size_0":"320.0","sctp-b-egress_src_addresses":"local","sctp-a-ipv6-ingress_ethertype":"IPv4","sctp-a-ipv6-ingress-dst_start_port":"0","sctp-b-ipv6-ingress_rule_application":"any","domain_name":"default-domain","sctp-a-ingress_rule_protocol":"icmp","sctp-b-egress-src_start_port":"0.0","sctp-a-egress_src_addresses":"local","sctp-b-display_name":"epc-sctp-b-ipv4v6-sec-group","sctp-a-egress-src_start_port":"0.0","sctp-a-ingress_ethertype":"IPv4","sctp-b-ipv6-ingress-dst_end_port":"65535","sctp-b-dst_subnet_prefix_v6":"::","nf_naming":"{ecomp_generated_naming=true}","sctp-a-ipv6-ingress_src_subnet_prefix":"0.0.0.0","sctp-b-egress-dst_start_port":"0.0","ncb_flavor_name":"nv.c20r64d1","gpb1_Internal1_mac":"00:80:37:0E:01:22","sctp-b-egress_dst_subnet_prefix_len":"0.0","Internal2_net_cidr":"169.255.0.0","sctp-a-ingress-dst_start_port":"0.0","sctp-a-egress-dst_start_port":"0.0","fsb1_Internal2_mac":"00:80:37:0E:0B:12","sctp-a-egress_ethertype":"IPv4","vlc_st_service_mode":"in-network-nat","sctp-a-ipv6-egress_ethertype":"IPv4","sctp-a-egress-src_end_port":"65535.0","sctp-b-ipv6-egress_rule_application":"any","sctp-b-egress_action":"pass","sctp-a-ingress-src_subnet_prefix_len":"0.0","sctp-b-ipv6-ingress-src_end_port":"65535.0","sctp-b-name":"epc-sctp-b-ipv4v6-sec-group","fsb2_Internal1_mac":"00:80:37:0E:0D:12","sctp-a-ipv6-ingress-src_start_port":"0.0","sctp-b-ipv6-egress_ethertype":"IPv4","Internal1_net_cidr":"169.253.0.0","sctp-a-egress_dst_subnet_prefix":"0.0.0.0","fsb_flavor_name":"nv.c20r64d1","sctp_rule_protocol":"132","sctp-b-ipv6-ingress_src_subnet_prefix_len":"0","sctp-a-ipv6-ingress_rule_application":"any","ecomp_generated_naming":"true","sctp-a-IPv6_ethertype":"IPv6","vlc2_Internal1_mac":"00:80:37:0E:02:12","vlc_st_virtualization_type":"virtual-machine","sctp-b-ingress-dst_start_port":"0.0","sctp-b-ingress-dst_end_port":"65535.0","sctp-a-ipv6-ingress-src_end_port":"65535.0","sctp-a-display_name":"epc-sctp-a-ipv4v6-sec-group","sctp-b-ingress_rule_application":"any","int2_sec_group_name":"int2-sec-group","vlc_flavor_name":"nd.c16r64d1","sctp-b-ipv6-egress_src_addresses":"local","vlc_st_interface_type_int1":"other1","sctp-b-egress-src_end_port":"65535.0","sctp-a-ipv6-egress-dst_start_port":"0","vlc_st_interface_type_int2":"other2","sctp-a-ipv6-egress_rule_protocol":"any","Internal2_shared":"false","sctp-a-ipv6-egress_dst_subnet_prefix_len":"0","Internal2_rpf":"disable","vlc1_Internal1_mac":"00:80:37:0E:01:12","sctp-b-ipv6-egress_src_end_port":"65535","sctp-a-ipv6-egress_src_addresses":"local","sctp-a-ingress-dst_end_port":"65535.0","sctp-a-ipv6-egress_src_end_port":"65535","Internal1_forwarding_mode":"l2","Internal2_dhcp":"false","sctp-a-dst_subnet_prefix_v6":"::","pxe_image_name":"MME_PXE-Boot_16ACP04_GA.qcow2","vlc_st_interface_type_gtp":"other0","ncb1_Internal1_mac":"00:80:37:0E:09:12","sctp-b-src_subnet_prefix_v6":"::","sctp-a-egress_dst_subnet_prefix_len":"0.0","int1_sec_group_name":"int1-sec-group","Internal1_dhcp":"false","sctp-a-ipv6-egress_dst_end_port":"65535","Internal2_forwarding_mode":"l2","fsb2_Internal2_mac":"00:80:37:0E:0D:12","sctp-b-egress_dst_subnet_prefix":"0.0.0.0","Internal1_net_cidr_len":"17","gpb2_Internal1_mac":"00:80:37:0E:02:22","sctp-b-ingress-src_subnet_prefix_len":"0.0","sctp-a-ingress_dst_addresses":"local","sctp-a-egress_action":"pass","fsb_volume_type_0":"SF-Default-SSD","ncb2_Internal2_mac":"00:80:37:0E:0F:12","vlc_st_interface_type_sctp_a":"left","vlc_st_interface_type_sctp_b":"right","sctp-a-src_subnet_prefix_v6":"::","vlc_st_version":"2","sctp-b-egress_ethertype":"IPv4","sctp-a-ingress_rule_application":"any","gpb1_Internal2_mac":"00:80:37:0E:01:22","instance_ip_family_v6":"v6","sctp-a-ipv6-egress_src_start_port":"0","sctp-b-ingress-src_start_port":"0.0","sctp-b-ingress_dst_addresses":"local","fsb1_Internal1_mac":"00:80:37:0E:0B:12","vlc_st_interface_type_oam":"management","multi_stage_design":"false","oam_sec_group_name":"oam-sec-group","Internal2_net_gateway":"169.255.0.3","sctp-a-ipv6-ingress-dst_end_port":"65535","sctp-b-ipv6-egress-dst_start_port":"0","Internal1_net_gateway":"169.253.0.3","sctp-b-ipv6-egress_rule_protocol":"any","gtp_sec_group_name":"gtp-sec-group","sctp-a-ipv6-egress_dst_subnet_prefix":"0.0.0.0","sctp-b-ipv6-egress_dst_subnet_prefix_len":"0","sctp-a-ipv6-ingress_dst_addresses":"local","sctp-a-egress_rule_protocol":"icmp","sctp-b-ipv6-egress_action":"pass","sctp-a-ipv6-egress_action":"pass","Internal1_shared":"false","sctp-b-ipv6-ingress_rule_protocol":"any","Internal2_net_cidr_len":"17","sctp-a-name":"epc-sctp-a-ipv4v6-sec-group","sctp-a-ingress-src_end_port":"65535.0","sctp-b-ipv6-ingress_src_subnet_prefix":"0.0.0.0","sctp-a-egress-dst_end_port":"65535.0","sctp-a-ingress_action":"pass","sctp-b-egress_rule_protocol":"icmp","sctp-b-ipv6-ingress_action":"pass","vlc_st_service_type":"firewall","sctp-b-ipv6-egress_dst_end_port":"65535","sctp-b-ipv6-ingress-dst_start_port":"0","vlc2_Internal2_mac":"00:80:37:0E:02:12","vlc_st_availability_zone":"true","fsb_volume_image_name_1":"MME_FSB2_16ACP04_GA.qcow2","sctp-b-ingress-src_subnet_prefix":"0.0.0.0","sctp-a-ipv6-ingress_src_subnet_prefix_len":"0","Internal1_allow_transit":"true","gpb_flavor_name":"nv.c20r64d1","availability_zone_max_count":"1","fsb_volume_image_name_0":"MME_FSB1_16ACP04_GA.qcow2","sctp-b-ipv6-ingress_dst_addresses":"local","sctp-b-ipv6-egress_dst_subnet_prefix":"0.0.0.0","sctp-b-ipv6-ingress_ethertype":"IPv4","vlc1_Internal2_mac":"00:80:37:0E:01:12","sctp-a-ingress-src_subnet_prefix":"0.0.0.0","sctp-a-ipv6-ingress_action":"pass","Internal1_rpf":"disable","sctp-b-ingress_ethertype":"IPv4","sctp-b-egress_rule_application":"any","sctp-b-ingress-src_end_port":"65535.0","sctp-a-ipv6-ingress_rule_protocol":"any","sctp-a-ingress-src_start_port":"0.0","sctp-b-egress-dst_end_port":"65535.0"},"type":"VF","modelCustomizationName":"VF_vMee 0","vfModules":{"vf_vmee0..VfVmee..vmme_vlc..module-1":{"uuid":"522159d5-d6e0-4c2a-aa44-5a542a12a830","invariantUuid":"98a7c88b-b577-476a-90e4-e25a5871e02b","customizationUuid":"55b1be94-671a-403e-a26c-667e9c47d091","description":null,"name":"VfVmee..vmme_vlc..module-1","version":"2","modelCustomizationName":"VfVmee..vmme_vlc..module-1","properties":{"minCountInstances":0,"maxCountInstances":null,"initialCount":0,"vfModuleLabel":"vmme_vlc"},"inputs":{},"volumeGroupAllowed":false},"vf_vmee0..VfVmee..vmme_gpb..module-2":{"uuid":"41708296-e443-4c71-953f-d9a010f059e1","invariantUuid":"1cca90b8-3490-495e-87da-3f3e4c57d5b9","customizationUuid":"6add59e0-7fe1-4bc4-af48-f8812422ae7c","description":null,"name":"VfVmee..vmme_gpb..module-2","version":"2","modelCustomizationName":"VfVmee..vmme_gpb..module-2","properties":{"minCountInstances":0,"maxCountInstances":null,"initialCount":0,"vfModuleLabel":"vmme_gpb"},"inputs":{},"volumeGroupAllowed":false},"vf_vmee0..VfVmee..base_vmme..module-0":{"uuid":"a27f5cfc-7f12-4f99-af08-0af9c3885c87","invariantUuid":"a6f9e51a-2b35-416a-ae15-15e58d61f36d","customizationUuid":"f8c040f1-7e51-4a11-aca8-acf256cfd861","description":null,"name":"VfVmee..base_vmme..module-0","version":"2","modelCustomizationName":"VfVmee..base_vmme..module-0","properties":{"minCountInstances":1,"maxCountInstances":1,"initialCount":1,"vfModuleLabel":"base_vmme"},"inputs":{},"volumeGroupAllowed":true}},"volumeGroups":{"vf_vmee0..VfVmee..base_vmme..module-0":{"uuid":"a27f5cfc-7f12-4f99-af08-0af9c3885c87","invariantUuid":"a6f9e51a-2b35-416a-ae15-15e58d61f36d","customizationUuid":"f8c040f1-7e51-4a11-aca8-acf256cfd861","description":null,"name":"VfVmee..base_vmme..module-0","version":"2","modelCustomizationName":"VfVmee..base_vmme..module-0","properties":{"minCountInstances":1,"maxCountInstances":1,"initialCount":1,"vfModuleLabel":"base_vmme"},"inputs":{}}},"vfcInstanceGroups":{}}},"networks":{"ExtVL 0":{"uuid":"ddc3f20c-08b5-40fd-af72-c6d14636b986","invariantUuid":"379f816b-a7aa-422f-be30-17114ff50b7c","description":"ECOMP generic virtual link (network) base type for all other service-level and global networks","name":"ExtVL","version":"37.0","customizationUuid":"94fdd893-4a36-4d70-b16a-ec29c54c184f","inputs":{},"commands":{},"properties":{"network_assignments":"{is_external_network=false, ipv4_subnet_default_assignment={min_subnets_count=1}, ecomp_generated_network_assignment=false, ipv6_subnet_default_assignment={min_subnets_count=1}}","exVL_naming":"{ecomp_generated_naming=true}","network_flows":"{is_network_policy=false, is_bound_to_vpn=false}","network_homing":"{ecomp_selected_instance_node_target=false}"},"type":"VL","modelCustomizationName":"ExtVL 0"}},"collectionResource":{},"configurations":{"Port Mirroring Configuration By Policy 0":{"uuid":"b4398538-e89d-4f13-b33d-ca323434ba50","invariantUuid":"6ef0ca40-f366-4897-951f-abd65d25f6f7","description":"A port mirroring configuration by policy object","name":"Port Mirroring Configuration By Policy","version":"27.0","customizationUuid":"3c3b7b8d-8669-4b3b-8664-61970041fad2","inputs":{},"commands":{},"properties":{},"type":"Configuration","modelCustomizationName":"Port Mirroring Configuration By Policy 0","sourceNodes":[],"collectorNodes":null,"configurationByPolicy":false}},"serviceProxies":{},"vfModules":{"vf_vmee0..VfVmee..vmme_vlc..module-1":{"uuid":"522159d5-d6e0-4c2a-aa44-5a542a12a830","invariantUuid":"98a7c88b-b577-476a-90e4-e25a5871e02b","customizationUuid":"55b1be94-671a-403e-a26c-667e9c47d091","description":null,"name":"VfVmee..vmme_vlc..module-1","version":"2","modelCustomizationName":"VfVmee..vmme_vlc..module-1","properties":{"minCountInstances":0,"maxCountInstances":null,"initialCount":0,"vfModuleLabel":"vmme_vlc"},"inputs":{},"volumeGroupAllowed":false},"vf_vmee0..VfVmee..vmme_gpb..module-2":{"uuid":"41708296-e443-4c71-953f-d9a010f059e1","invariantUuid":"1cca90b8-3490-495e-87da-3f3e4c57d5b9","customizationUuid":"6add59e0-7fe1-4bc4-af48-f8812422ae7c","description":null,"name":"VfVmee..vmme_gpb..module-2","version":"2","modelCustomizationName":"VfVmee..vmme_gpb..module-2","properties":{"minCountInstances":0,"maxCountInstances":null,"initialCount":0,"vfModuleLabel":"vmme_gpb"},"inputs":{},"volumeGroupAllowed":false},"vf_vmee0..VfVmee..base_vmme..module-0":{"uuid":"a27f5cfc-7f12-4f99-af08-0af9c3885c87","invariantUuid":"a6f9e51a-2b35-416a-ae15-15e58d61f36d","customizationUuid":"f8c040f1-7e51-4a11-aca8-acf256cfd861","description":null,"name":"VfVmee..base_vmme..module-0","version":"2","modelCustomizationName":"VfVmee..base_vmme..module-0","properties":{"minCountInstances":1,"maxCountInstances":1,"initialCount":1,"vfModuleLabel":"base_vmme"},"inputs":{},"volumeGroupAllowed":true}},"volumeGroups":{"vf_vmee0..VfVmee..base_vmme..module-0":{"uuid":"a27f5cfc-7f12-4f99-af08-0af9c3885c87","invariantUuid":"a6f9e51a-2b35-416a-ae15-15e58d61f36d","customizationUuid":"f8c040f1-7e51-4a11-aca8-acf256cfd861","description":null,"name":"VfVmee..base_vmme..module-0","version":"2","modelCustomizationName":"VfVmee..base_vmme..module-0","properties":{"minCountInstances":1,"maxCountInstances":1,"initialCount":1,"vfModuleLabel":"base_vmme"},"inputs":{}}},"pnfs":{}}'); +} + + +function generateVNFFormValues() { + return JSON.parse('{"globalSubscriberId":"e433710f-9217-458d-a79d-1c7aff376d89","productFamilyId":"vRRaaS","subscriptionServiceType":"VIRTUAL USP","lcpCloudRegionId":"mtn6","tenantId":"1178612d2b394be4834ad77f567c0af2","aicZoneId":"JAG1","projectName":"DFW","owningEntityId":"d61e6f2d-12fa-4cc2-91df-7c244011d6fc","rollbackOnFailure":"true","bulkSize":1,"instanceParams":[{}],"modelInfo":{"modelInvariantId":"e49fbd11-e60c-4a8e-b4bf-30fbe8f4fcc0","modelVersionId":"6e59c5de-f052-46fa-aa7e-2fca9d674c44","modelName":"ComplexService","modelVersion":"1.0"},"isUserProvidedNaming":false,"tenantName":"AIN Web Tool-15-D-SSPtestcustome","aicZoneName":"YUDFJULP-JAG1"}'); +} diff --git a/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.ts b/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.ts new file mode 100644 index 00000000..b1e676fc --- /dev/null +++ b/vid-webpack-master/src/app/shared/services/defaultDataServiceGenerator/default.data.generator.service.ts @@ -0,0 +1,82 @@ +import { Injectable } from '@angular/core'; +import * as _ from 'lodash'; +import { createVFModuleInstance, updateVNFInstance } from '../../../service.actions'; +import { NgRedux } from '@angular-redux/store'; +import { AppState } from '../../../store/reducers'; + +@Injectable() +export class DefaultDataGeneratorService { + static controlsFieldsStatus = {}; + + constructor(private store: NgRedux<AppState>) { } + + updateReduxOnFirstSet(serviceId: string, formServiceValues: any): void { + const serviceHierarchy = this.store.getState().service.serviceHierarchy[serviceId]; + if (serviceHierarchy && !_.isEmpty(serviceHierarchy.vnfs)) { + for (let vnfUUID in serviceHierarchy.vnfs) { + for (let vnfModuleUUID in serviceHierarchy.vnfs[vnfUUID].vfModules) { + if (serviceHierarchy.vnfs[vnfUUID].vfModules[vnfModuleUUID].properties.minCountInstances > 0) { + + let vfModule = this.generateVFModule(serviceHierarchy, vnfUUID, vnfModuleUUID); + this.updateVNFInstanceRedux( + serviceHierarchy.vnfs[vnfUUID].modelName, + serviceId, + serviceHierarchy.vnfs[vnfUUID].vfModules[vnfModuleUUID].properties.initialCount, + vfModule, + this.generateVNFData(serviceHierarchy, vnfUUID, vnfModuleUUID, formServiceValues), + vnfModuleUUID + ); + } + } + } + } + } + + updateVNFInstanceRedux(vnfModelName: string, serviceId: string, numberOfVfModules: number, vfModuleData: any, vnfData: any, vfModuleName : string): void { + if (numberOfVfModules > 0) { + this.store.dispatch(updateVNFInstance(vnfData, vnfData.modelInfo.modelCustomizationName, serviceId)); + for (let i = 0; i < numberOfVfModules; i++) { + this.store.dispatch(createVFModuleInstance(vfModuleData, vfModuleName, serviceId)); + } + } + } + + + generateVFModule(serviceHierarchy: any, vnfUUID: string, vnfModuleUUID: string) { + return { + 'sdncPreReload': null, + 'modelInfo': { + 'modelType': 'VFmodule', + 'modelInvariantId': serviceHierarchy.vnfs[vnfUUID].vfModules[vnfModuleUUID].invariantUuid, + 'modelVersionId': serviceHierarchy.vnfs[vnfUUID].vfModules[vnfModuleUUID].uuid, + 'modelName': serviceHierarchy.vnfs[vnfUUID].vfModules[vnfModuleUUID].name, + 'modelVersion': serviceHierarchy.vnfs[vnfUUID].vfModules[vnfModuleUUID].version, + 'modelCustomizationId': serviceHierarchy.vnfs[vnfUUID].vfModules[vnfModuleUUID].customizationUuid, + 'modelCustomizationName': serviceHierarchy.vnfs[vnfUUID].vfModules[vnfModuleUUID].modelCustomizationName + }, + 'instanceParams': [ + {} + ] + }; + } + + generateVNFData(serviceHierarchy: any, vnfName: string, vnfUUID: string, formValues: any) { + return { + 'productFamilyId': formValues.productFamilyId, + 'lcpCloudRegionId': null, + 'tenantId': null, + 'lineOfBusiness': null, + 'platformName': null, + 'modelInfo': { + 'modelType': 'VF', + 'modelInvariantId': serviceHierarchy.vnfs[vnfName].invariantUuid, + 'modelVersionId': formValues.modelInfo.modelVersionId, + 'modelName': serviceHierarchy.vnfs[vnfName].name, + 'modelVersion': serviceHierarchy.vnfs[vnfName].version, + 'modelCustomizationId': serviceHierarchy.vnfs[vnfName].modelCustomizationId, + 'modelCustomizationName': serviceHierarchy.vnfs[vnfName].modelCustomizationName + }, + 'isUserProvidedNaming': null + } + } +} diff --git a/vid-webpack-master/src/app/shared/shared.module.ts b/vid-webpack-master/src/app/shared/shared.module.ts new file mode 100644 index 00000000..ab6ecaa0 --- /dev/null +++ b/vid-webpack-master/src/app/shared/shared.module.ts @@ -0,0 +1,92 @@ +import {NgModule, ModuleWithProviders} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {RouterModule} from '@angular/router'; +import { BrowserModule } from '@angular/platform-browser'; +import { HttpClientModule } from '@angular/common/http'; +import { ServiceInfoService } from './server/serviceInfo/serviceInfo.service'; +import { PopoverModule } from 'ngx-bootstrap'; +import { AngularSvgIconModule } from 'angular-svg-icon'; +import { SvgDirective } from './directives/svg/svg.directive'; +import { PopoverComponent } from './components/popover/popover.component'; +import { EllipsisComponent } from './components/ellipsis/ellipsis.component'; +import { MessageBoxComponent } from './components/messageBox/messageBox.component'; +import { MessageBoxService } from './components/messageBox/messageBox.service'; +import { SdcUiComponentsModule , SdcUiComponents} from 'sdc-ui/lib/angular'; +import { HttpInterceptorService } from './utils/httpInterceptor/httpInterceptor.service'; +import { FormControlErrorComponent } from './components/formControlError/formControlError.component'; +import { InputPreventionPatternDirective } from './directives/inputPrevention/inputPreventionPattern.directive'; +import { FormGeneralErrorsComponent } from './components/formGeneralErrors/formGeneralErrors.component'; +import { NumbersLettersUnderscoreValidator } from './components/validators/numbersLettersUnderscore/numbersLettersUnderscore.validator'; +import { SpinnerComponent } from './components/spinner/spinner.component'; +import { NoContentMessageAndIconComponent } from './components/no-content-message-and-icon/no-content-message-and-icon.component'; +import { ModelInformationComponent } from './components/model-information/model-information.component'; +import { TooltipModule } from 'ngx-tooltip'; +import {IframeService} from "./utils/iframe.service"; +import {CapitalizeAndFormatPipe} from "./pipes/capitalize/capitalize-and-format.pipe"; +import { DefaultDataGeneratorService } from './services/defaultDataServiceGenerator/default.data.generator.service'; +import {ServiceInfoPipe} from "./pipes/serviceInfo/serviceInfo.pipe"; +import {HealthStatusService} from "./server/healthStatusService/health-status.service"; +import {ConfigurationService} from "../services/configuration.service"; +import {FlagsResolve} from "../services/flags.resolve"; + + +@NgModule({ + imports: [ + BrowserModule, + HttpClientModule, + CommonModule, + RouterModule, + PopoverModule.forRoot(), + AngularSvgIconModule, + TooltipModule, + SdcUiComponentsModule, + ], + declarations: [ + PopoverComponent, + SvgDirective, + EllipsisComponent, + MessageBoxComponent, + FormControlErrorComponent, + InputPreventionPatternDirective, + FormGeneralErrorsComponent, + SpinnerComponent, + NoContentMessageAndIconComponent, + ModelInformationComponent, + CapitalizeAndFormatPipe, + ServiceInfoPipe, + ], + exports: [ + PopoverComponent, + SvgDirective, + EllipsisComponent, + MessageBoxComponent, + FormControlErrorComponent, + InputPreventionPatternDirective, + FormGeneralErrorsComponent, + SpinnerComponent, + NoContentMessageAndIconComponent, + ModelInformationComponent, + CapitalizeAndFormatPipe, + ServiceInfoPipe, + ], + providers: [ + ServiceInfoService, + MessageBoxService, + SdcUiComponents.ModalService, + HttpInterceptorService, + IframeService, + NumbersLettersUnderscoreValidator, + DefaultDataGeneratorService, + HealthStatusService, + ConfigurationService, + FlagsResolve + ] +}) +export class SharedModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: SharedModule, + providers: [MessageBoxService] + }; + } +} diff --git a/vid-webpack-master/src/app/shared/utils/constants.ts b/vid-webpack-master/src/app/shared/utils/constants.ts new file mode 100644 index 00000000..c78e12e2 --- /dev/null +++ b/vid-webpack-master/src/app/shared/utils/constants.ts @@ -0,0 +1,287 @@ +export module Constants { + + export class Component { + public static NAME = 'name'; + public static SERVICE = 'service'; + public static A_LA_CARTE = 'a la carte'; + public static MACRO = 'Macro'; + public static SUBSCRIBER_NAME = 'subscriberName'; + public static OLDVERSION = 'oldversion'; + public static SERVICE_TYPE = 'serviceType'; + } + + export class Path { + public static FORWARD_SLASH = '/'; + + public static AAI_GET_FULL_SUBSCRIBERS = '../../aai_get_full_subscribers'; + public static AAI_REFRESH_FULL_SUBSCRIBERS = 'aai_refresh_full_subscribers'; + public static AAI_GET_SUBSCRIBERS = '../../aai_get_subscribers'; + + public static AAI_GET_TENTANTS = '../../aai_get_tenants'; + public static AAI_REFRESH_SUBSCRIBERS = 'aai_refresh_subscribers'; + public static AAI_SUB_DETAILS = 'aai_sub_details'; + public static AAI_SUB_VIEWEDIT = '../../aai_sub_viewedit'; + + public static GET_WORKFLOW = 'change-management/get_vnf_workflow_relation'; + public static GET_MSO_WORKFLOWS = 'change-management/mso'; + public static GET_SCHEDULER_CHANGE_MANAGEMENTS = 'change-management/scheduler'; + public static CANCEL_SCHEDULE_REQUEST = 'change-management/scheduler/schedules'; + public static ASSIGN = '?r='; + public static AAI_GET_SERVICE_INSTANCE_PATH = 'aai_get_service_instance/'; + public static AAI_GET_SERVICES = '../../aai_get_services'; + public static AAI_GET_AIC_ZONES = '../../aai_get_aic_zones'; + public static AAI_GET_AIC_ZONE_FOR_PNF = 'aai_get_aic_zone_for_pnf/@globalCustomerId/@serviceType/@serviceInstanceId'; + public static AAI_GET_SERVICES_BY_TYPE = 'aai_get_models_by_service_type'; + public static AAI_GET_TENANTS = '../../aai_get_tenants/'; + public static AAI_SUB_DETAILS_PATH = '../../aai_sub_details/'; + public static AAI_GET_VERSION_BY_INVARIANT_ID = 'aai_get_version_by_invariant_id/'; + public static SEARCH_SERVICE_INSTANCES = 'search_service_instances'; + public static AAI_GET_VNF_DATA_PATH = 'aai_get_vnf_data/'; + public static AAI_GET_VNF_BY_CUSTOMERID_AND_SERVICETYPE = 'get_vnf_data_by_globalid_and_service_type/'; + public static AAI_GET_SERVICES_BY_OWNING_ENTITY_ID = 'aai_get_services_by_owning_entity_id'; + public static AAI_GET_VNF_INFO = 'aai_get_vnf_information'; + public static AAI_GET_PNF_INSTANCE = 'aai_get_service_instance_pnfs'; + public static AAI_GET_VNF_INSTANCES_LIST = 'aai_get_vnf_instances'; + public static AAI_GET_PNF_INSTANCES_LIST = 'aai_get_pnf_instances'; + public static AAI_GET_BY_URI = 'aai_get_by_uri/'; + public static AAI_GET_CONFIGURATION = 'aai_get_configuration/'; + public static AAI_GET_TEST_ENVIRONMENTS = 'get_operational_environments?operationalEnvironmentType='; + public static GET_CATEGORY_PARAMETERS = '../../category_parameter'; + public static PARAMETER_STANDARDIZATION_FAMILY = 'PARAMETER_STANDARDIZATION'; + public static TENANT_ISOLATION_FAMILY = 'TENANT_ISOLATION'; + public static ASDC_GETMODEL_PATH = 'asdc/getModel/'; + public static CREATE_INSTANCE_PATH = '/models/services/createInstance'; + public static AAI_GET_PNF_BY_NAME = 'aai_get_pnfs/pnf/'; + + public static GET_SYSTEM_PROP_VNF_PROV_STATUS_PATH = 'get_system_prop_vnf_prov_status'; + public static GET_USER_ID = 'getuserID'; + public static INSTANTIATE_ROOT_PATH = '#/instantiate?subscriberId='; + public static INSTANTIATE_PATH = '/instantiate'; + public static INVALID_STRING = '/INVALID_STRING/'; + public static INVALID_STRING_MSO_CREATE_SVC_INSTANCE = 'INVALID_STRING_mso_create_svc_instance'; + public static MSO = 'mso'; + public static MSO_CREATE_NW_INSTANCE = 'mso_create_nw_instance'; + public static MSO_CREATE_NW_INSTANCE_PATH = 'mso_create_nw_instance/'; + public static MSO_CREATE_SVC_INSTANCE = 'mso_create_svc_instance'; + public static MSO_CREATE_VNF_INSTANCE = '../../mso/mso_create_vnf_instance/'; + public static MSO_DELETE_SVC_INSTANCE_PATH = 'mso_delete_svc_instance/'; + public static MSO_ACTIVATE_INSTANCE = 'mso/mso_activate_service_instance/@serviceInstanceId'; + public static MSO_DEACTIVATE_INSTANCE = 'mso/mso_deactivate_service_instance/@serviceInstanceId'; + public static MSO_CREATE_REALATIONSHIP = 'mso_add_relationship'; + public static MSO_REMOVE_RELATIONSHIP = 'mso_remove_relationship'; + public static SELECTED_SERVICE_SUB_PATH = '#/instances/subdetails?'; + public static SELECTED_SERVICE_INSTANCE_SUB_PATH = 'serviceInstanceIdentifier='; + public static SELECTED_SUBSCRIBER_SUB_PATH = 'subscriberId='; + public static OWNING_ENTITY_SUB_PATH = 'owningEntity='; + public static PROJECT_SUB_PATH = 'project='; + public static SERVICE_TYPE_LIST_PATH = '#/instances/serviceTypes?serviceTypeList='; + public static SERVICE_MODLES_INSTANCES_SUBSCRIBERS_PATH = 'serviceModels.htm#/instances/subscribers'; + public static SERVICES_DIST_STATUS_PATH = '../../rest/models/services?distributionStatus='; + public static SERVICES_PATH = '../../rest/models/services/'; + public static SERVICETYPE_SUB_PATH = '&serviceType='; + public static SERVICEINSTANCEID_SUB_PATH = '&serviceInstanceId='; + public static SERVICEMODELS_INSTANCES_SERVICES_PATH = 'serviceModels.htm#/instances/services'; + public static SERVICEMODELS_MODELS_SERVICES_PATH = 'serviceModels.htm#/models/services'; + public static SUBDETAILS_SELECTEDSUBSCRIBER = '#subdetails?selectedSubscriber='; + public static SUBSCRIBERNAME_SUB_PATH = '&subscriberName='; + public static WELCOME_PATH = 'welcome.htm'; + public static IS_PERMITTED_SUB_PATH = '&isPermitted='; + public static SERVICES_JOB_INFO_PATH = '../../asyncInstantiation'; + public static CONFIGURATION_PATH = '../../get_property/{name}/defaultvalue'; + public static SERVICES_JOB_AUDIT_PATH = '/auditStatus'; + public static SERVICES_PROBE_PATH = "../../probe"; + public static FEATURES_FLAG_PATH ="../../flags"; + + // Test Environment Urls = + public static OPERATIONAL_ENVIRONMENT_CREATE = 'operationalEnvironment/create'; + public static OPERATIONAL_ENVIRONMENT_DEACTIVATE = 'operationalEnvironment/deactivate?operationalEnvironment='; + public static OPERATIONAL_ENVIRONMENT_ACTIVATE = 'operationalEnvironment/activate?operationalEnvironment='; + public static OPERATIONAL_ENVIRONMENT_STATUS = 'operationalEnvironment/requestStatus?requestId='; + + } + + export class Key { + + public static DESCRIPTION = 'description'; + public static GENERIC_VNF = 'generic-vnf'; + public static GLOBAL_CUSTOMER_ID = 'global-customer-id'; + public static GLOBAL_CUST_ID = 'globalCustomerId'; + public static IN_MAINT = 'in-maint'; + public static INVENTORY_RESPONSE_ITEMS = 'inventory-response-items'; + public static INVENTORY_RESPONSE_ITEM = 'inventory-response-item'; + public static L3_NETWORK = 'l3-network'; + public static SUB_NET = 'subnet'; + public static SUBNET_NAME = 'subnet-name'; + public static SUBNET_ID = 'subnet-id'; + public static GATEWAY_ADDRESS = 'gateway-address'; + public static NETWORK_START_ADDRESS = 'network-start-address'; + public static CIDR_MASK = 'cidr-mask'; + public static MODEL_CUSTOMIZATION_ID = 'model-customization-id'; + public static MODEL_INVAR_ID = 'model-invariant-id'; + public static MODEL_VERSION_ID = 'model-version-id'; + public static NETWORK_NAME = 'network-name'; + public static NETWORK_ID = 'network-id'; + public static NETWORK_TYPE = 'network-type'; + public static NETWORKS = 'networks'; + public static OPERATIONAL_STATUS = 'operational-status'; + public static ORCHESTRATION_STATUS = 'orchestration-status'; + public static PERCENT_PROGRESS = 'percent-progress'; + public static PERSONA_MODEL_ID = 'persona-model-id'; + public static PERSONA_MODEL_VERSION = 'persona-model-version'; + public static PERSONA_MODEL_CUSTOMIZATION_ID = 'persona-model-customization-id'; + public static PROV_STATUS = 'prov-status'; + + public static RESOURCE_LINK = 'resource-link'; + public static RESULT_DATA = 'result-data'; + public static SERVICE_DESCRIPTION = 'service-description'; + public static SERVICE_ID = 'service-id'; + public static SERVICE_INSTANCE = 'service-instance'; + public static SERVICE_INSTANCES = 'service-instances'; + public static SERVICE_INSTANCE_ID = 'service-instance-id'; + public static SERVICE_INSTANCE_NAME = 'service-instance-name'; + public static SERVICE_SUBSCRIPTION = 'service-subscription'; + public static SERVICE_SUBSCRIPTIONS = 'service-subscriptions'; + public static SERVICETYPE = 'service-type'; + public static STATUS_MESSAGE = 'statusMessage'; + public static SUBNAME = 'subscriber-name'; + public static IS_PERMITTED = 'is-permitted'; + public static TIMESTAMP = 'timestamp'; + public static VF_MODULE = 'vf-module'; + public static VF_MODULES = 'vfModules'; + public static VF_MODULE_ID = 'vf-module-id'; + public static VF_MODULE_NAME = 'vf-module-name'; + public static VID = 'VID'; + public static VNF_ID = 'vnf-id'; + public static VNF_NAME = 'vnf-name'; + public static VNF_TYPE = 'vnf-type'; + public static VNFS = 'vnfs'; + public static AVAILABLEVOLUMEGROUPS = 'availableVolumeGroups'; + public static VOLUMEGROUPS = 'volumeGroups'; + public static VOLUME_GROUP = 'volume-group'; + public static VOLUME_GROUP_ID = 'volume-group-id'; + public static VOLUME_GROUP_NAME = 'volume-group-name'; + public static UPLOAD_SUPPLEMENTORY_DATA_FILE = 'uploadSupplementoryDataFile'; + public static SUPPLEMENTORY_DATA_FILE = 'supplementoryDataFile'; + public static ZONE_ID = 'zone-id'; + public static ZONE_NAME = 'zone-name'; + public static GENERIC_CONFIGURATION = 'configuration'; + public static CONFIGURATIONS = 'configurations'; + public static CONFIGURATION = 'configuration'; + public static CONFIGURATION_NAME = 'configuration-name'; + public static CONFIGURATION_TYPE = 'configuration-type'; + public static CONFIGURATION_ID = 'configuration-id'; + public static PORT = 'l-interface'; + public static PORT_ID = 'interface-id'; + public static PORT_NAME = 'interface-name'; + public static PORT_MIRRORED = 'is-port-mirrored'; + } + + export class Status { + public static ALL = 'ALL'; + public static COMPLETE = 'Complete'; + public static DONE = 'Done'; + public static ERROR = 'Error'; + public static FAILED = 'Failed'; + public static FAILED_SERVICE_MODELS_ASDC = 'Failed to get service models from SDC.'; + public static FETCHING_SERVICE_TYPES = 'Fetching service types list from A&AI'; + public static FETCHING_SERVICE_CATALOG = 'Fetching service catalog from AAI. Please wait.'; + public static FETCHING_SERVICE_CATALOG_ASDC = 'Fetching service catalog from SDC. Please wait.'; + public static FETCHING_SUB_DETAILS = 'Fetching subscriber details from A&AI for '; + public static FETCHING_SERVICE_INST_DATA = 'Fetching service instance data from A&AI for service-instance-id='; + public static FETCHING_SUBSCRIBER_LIST_AAI = 'Fetching subscriber list from A&AI...'; + public static IN_PROGRESS = 'In Progress'; + public static IS_SUCCESSFUL = ' isSuccessful = '; + public static MSO_FAILURE = 'msoFailure'; + public static NONE = 'None'; + public static NOT_FOUND = 'Not Found'; + public static NO_SERVICE_SUBSCRIPTION_FOUND = 'No Service Subscription Found'; + public static SUBMITTING_REQUEST = 'Submitting Request'; + public static SUCCESS_VNF_PROV_STATUS = 'Successfully set the VNF\'s Prov_Status to '; + public static UNLOCKED = 'Unlocked'; + public static AAI_ACTIVE = 'Active'; + public static AAI_INACTIVE = 'Inactive'; + public static AAI_CREATED = 'Created'; + public static AAI_ENABLED = 'Enabled'; + public static AAI_DISABLED = 'Disabled'; + } + + export class Error { + public static AAI = 'A&AI failure - see log below for details'; + public static AAI_ERROR = 'A&AI Error'; + public static AAI_FETCHING_CUST_DATA = 'Failed to fetch customer data from A&AI= Response Code= '; + public static FETCHING_SERVICE_TYPES = 'Failed to fetch service types from A&AI= Response Code= '; + public static FETCHING_SERVICES = 'Failed to fetch services from A&AI= Response Code= '; + public static FETCHING_SERVICE_INSTANCE_DATA = 'Failed to fetch service instance data from A&AI= Response Code= '; + public static INVALID_INSTANCE_NAME = 'Invalid instance name= '; + // tslint:disable-next-line:max-line-length + public static INSTANCE_NAME_VALIDATE = 'The instance name must contain only alphanumeric or \'_-.\' characters; and must start with an alphabetic character'; + public static INVALID_LIST = 'Invalid list parameter= '; + public static INVALID_MAP = 'Invalid map parameter= '; + public static LIST_VALIDATE = 'A list parameter value must have the following syntax= \'[<value1>;\.\.\.;<valueN>]\''; + // tslint:disable-next-line:max-line-length + public static MAP_VALIDATE = 'A map parameter value must have the following syntax= \'{ <entry_key_1>= <entry_value_1>; \.\.\.; <entry_key_n>= <entry_value_n> }\''; + public static MAX_POLLS_EXCEEDED = 'Maximum number of poll attempts exceeded'; + public static MISSING_DATA = 'Missing data'; + public static MODEL_VERSION_ID_MISSING = 'Error= model-version-id is not populated in A&AI'; + public static MSO = 'MSO failure - see log below for details'; + public static NO_MATCHING_MODEL = 'No matching model found matching the persona Model Id = '; + public static NO_MATCHING_MODEL_AAI = 'No matching model found matching the A&AI model version ID = '; + public static SELECT = 'Please select a subscriber or enter a service instance'; + public static SERVICE_INST_DNE = 'That service instance does not exist. Please try again.'; + public static SYSTEM_FAILURE = 'System failure'; + public static INVALID_DATA_FORMAT = 'Invalid data format.Please check your file content whether it is not in json or not.'; + public static MISSING_FILE = 'Please Select JSON File.'; + + public static MISSING_VNF_DETAILS = 'Missing required information.\n' + + 'Please open and fill in the details.\n'; + } + + export class EventType { + public static COMPONENT_STATUS = 'ComponentStatus'; + public static CREATE_COMPONENT = 'createComponent'; + public static DELETE_RESUME_COMPONENT = 'deleteResumeComponent'; + } + + export class ServicePopup { + public static TITLE = 'Create a new service instance'; + public static TOOLTIP_UUID = 'Unique identifier for this service in this version.'; + public static TOOLTIP_INVARIANT_UUID = 'Unique identifier for this service cross versions.'; + + } + + export class Parameter { + public static BOOLEAN = "boolean"; + public static SELECT = "select"; + public static MULTI_SELECT = "multi_select"; + public static STRING = "string"; + public static NUMBER = "number"; + public static VALID_VALUES = "valid_values"; + public static EQUAL = "equal"; + public static LENGTH = "length"; + public static MAX_LENGTH = "max_length"; + public static MIN_LENGTH = "min_length"; + public static IN_RANGE = "in_range"; + public static CONSTRAINTS = "constraints"; + public static OPERATOR = "operator"; + public static CONSTRAINT_VALUES = "constraintValues"; + public static DEFAULT = "default"; + public static DESCRIPTION = "description"; + public static TYPE = "type"; + public static INTEGER = "integer"; + public static RANGE = "range"; + public static LIST = "list"; + public static MAP = "map"; + public static REQUIRED = "required"; + public static GREATER_THAN = "greater_than"; + public static LESS_THAN = "less_than"; + public static GREATER_OR_EQUAL = "greater_or_equal"; + public static LESS_OR_EQUAL = "less_or_equal"; + public static DISPLAY_NAME = "displayName"; + public static CHECKBOX ='checkbox'; + public static FILE ='file'; + } + + export class AuditInfoModal{ + public static TITLE = 'Service Instantiation Information'; + } +} diff --git a/vid-webpack-master/src/app/shared/utils/httpInterceptor/httpInterceptor.service.ts b/vid-webpack-master/src/app/shared/utils/httpInterceptor/httpInterceptor.service.ts new file mode 100644 index 00000000..be9ade08 --- /dev/null +++ b/vid-webpack-master/src/app/shared/utils/httpInterceptor/httpInterceptor.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { + HttpInterceptor, + HttpRequest, + HttpHandler, + HttpEvent, HttpErrorResponse +} from '@angular/common/http'; + +import { Observable } from 'rxjs/Observable'; +import { ErrorMessage, ErrorService } from '../../components/error/error.component.service'; +import { SpinnerComponent } from '../../components/spinner/spinner.component'; + +@Injectable() +export class HttpInterceptorService implements HttpInterceptor { + intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { + SpinnerComponent.showSpinner.next(true); + return next.handle(request) + .catch((err: HttpErrorResponse) => { + if (err.status === 500) { + const errorMessage: ErrorMessage = new ErrorMessage('Server not available', + 'It appears that one of the backend servers is not responding.\n Please try later.', + 500); + ErrorService.showErrorWithMessage(errorMessage); + return Observable.of(null); + } + return Observable.throw(err); + }).finally(() => { + SpinnerComponent.showSpinner.next(false); + }); + } +} + diff --git a/vid-webpack-master/src/app/shared/utils/iframe.service.ts b/vid-webpack-master/src/app/shared/utils/iframe.service.ts new file mode 100644 index 00000000..9a6636f4 --- /dev/null +++ b/vid-webpack-master/src/app/shared/utils/iframe.service.ts @@ -0,0 +1,19 @@ +import {Injectable} from "@angular/core"; + +@Injectable() +export class IframeService { + + addClassOpenModal(elementClassName: string) { + var parentBodyElement = parent.document.getElementsByClassName(elementClassName)[0]; + if (parentBodyElement) { + parentBodyElement.classList.add("modal-open"); + } + } + + removeClassCloseModal(elementClassName: string) { + var parentBodyElement = parent.document.getElementsByClassName(elementClassName)[0]; + if (parentBodyElement) { + parentBodyElement.classList.remove("modal-open"); + } + } +} diff --git a/vid-webpack-master/src/app/shared/utils/log/log.service.spec.ts b/vid-webpack-master/src/app/shared/utils/log/log.service.spec.ts new file mode 100644 index 00000000..ea0eb049 --- /dev/null +++ b/vid-webpack-master/src/app/shared/utils/log/log.service.spec.ts @@ -0,0 +1,29 @@ + + +import {LogService} from "./log.service"; + +describe('log service service', () => { + let logService : LogService; + + beforeEach(() => { + logService = new LogService(); + }); + + + it('check all ILogger function are defined', ()=>{ + expect(logService.log).toBeDefined(); + expect(logService.assert).toBeDefined(); + expect(logService.error).toBeDefined(); + expect(logService.group).toBeDefined(); + expect(logService.groupEnd).toBeDefined(); + expect(logService.info).toBeDefined(); + expect(logService.warn).toBeDefined(); + }); + + it('test getPrefixLog function', ()=> { + let args = ['message', [1,2,3,4,5]]; + let result = LogService.getPrefixLog(args); + expect(result).toBeDefined(); + }); + +}); diff --git a/vid-webpack-master/src/app/shared/utils/log/log.service.ts b/vid-webpack-master/src/app/shared/utils/log/log.service.ts new file mode 100644 index 00000000..422fe013 --- /dev/null +++ b/vid-webpack-master/src/app/shared/utils/log/log.service.ts @@ -0,0 +1,58 @@ +import {isDevMode} from "@angular/core"; +declare var console: any; + +export interface ILogger { + assert(...args: any[]): void; + error(...args: any[]): void; + group(...args: any[]): void; + groupEnd(...args: any[]): void; + info(...args: any[]): void; + log(...args: any[]): void; + warn(...args: any[]): void; +} + +export class LogService implements ILogger { + + isProduction : boolean = !isDevMode(); + public assert(...args: any[]): void { + console.assert(LogService.getPrefixLog(...args)); + } + + public error(...args: any[]): void { + console.error(LogService.getPrefixLog(...args)); + } + + public group(...args: any[]): void { + console.group(LogService.getPrefixLog(...args)); + } + + public groupEnd(...args: any[]): void { + console.groupEnd(LogService.getPrefixLog(...args)); + } + + public info(...args: any[]): void { + console.info(LogService.getPrefixLog(...args)); + } + + public log(...args: any[]): void { + if(!this.isProduction){ + console.log(LogService.getPrefixLog(...args)); + } + } + + public warn(...args: any[]): void { + console.warn(LogService.getPrefixLog(...args)); + } + + static getPrefixLog(...args :any[]){ + return { + time : new Date(), + message : args[0], + data : args.length > 0 ? args[1] : '' + }; + } +} + + + + diff --git a/vid-webpack-master/src/app/store/epics.ts b/vid-webpack-master/src/app/store/epics.ts new file mode 100644 index 00000000..f424e735 --- /dev/null +++ b/vid-webpack-master/src/app/store/epics.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; + +import {AAIEpics} from "../services/aaiService/aai.epics"; + +@Injectable() +export class RootEpics { + constructor(private aaiEpics: AAIEpics) {} + + public createEpics() { + return this.aaiEpics.createEpic(); + + } +} diff --git a/vid-webpack-master/src/app/store/module.ts b/vid-webpack-master/src/app/store/module.ts new file mode 100644 index 00000000..c0909c12 --- /dev/null +++ b/vid-webpack-master/src/app/store/module.ts @@ -0,0 +1,29 @@ +import { NgModule } from '@angular/core'; +import { NgReduxModule, NgRedux, DevToolsExtension } from '@angular-redux/store'; +import { RootEpics } from './epics'; + +import rootReducer, {AppState} from "./reducers"; +import {AAIEpics} from "../services/aaiService/aai.epics"; + +@NgModule({ + imports: [NgReduxModule], + providers: [RootEpics, AAIEpics], +}) + +export class StoreModule { + constructor( + public store: NgRedux<AppState>, + devTools: DevToolsExtension, + rootEpics: RootEpics, + ) { + + const persistedState = sessionStorage.getItem('reduxState') ? + JSON.parse(sessionStorage.getItem('reduxState')) : {}; + + store.configureStore( + rootReducer, + persistedState, + rootEpics.createEpics(), + devTools.isEnabled() ? [ devTools.enhancer() ] : []); + } +} diff --git a/vid-webpack-master/src/app/store/reducers.ts b/vid-webpack-master/src/app/store/reducers.ts new file mode 100644 index 00000000..b3e4f471 --- /dev/null +++ b/vid-webpack-master/src/app/store/reducers.ts @@ -0,0 +1,17 @@ +import {Reducer, combineReducers} from 'redux'; +import {GlobalReducer, GlobalState} from "../global.reducer"; +import {ServiceReducer, ServiceState} from "../service.reducer"; + + +export interface AppState { + global: GlobalState; + service: ServiceState; + +} + +const rootReducer: Reducer<AppState> = combineReducers<AppState>({ + global: GlobalReducer, + service: ServiceReducer +}); + +export default rootReducer; diff --git a/vid-webpack-master/src/app/utils/utils.ts b/vid-webpack-master/src/app/utils/utils.ts new file mode 100644 index 00000000..dc4db3e4 --- /dev/null +++ b/vid-webpack-master/src/app/utils/utils.ts @@ -0,0 +1,271 @@ +import { Constants } from '../shared/utils/constants'; +import Parameter = Constants.Parameter; +import * as _ from 'lodash' + +export class Utils { + + public static clampNumber = (number, min, max) => { + return Math.max(min, Math.min(number, max)); + }; + + public static hasContents(object: Object): boolean { + if (object === undefined || object === null || object === "") { + return false; + } + return true; + }; + + public static convertModel(serviceModel) { + + let isNewFlow:boolean = false; + + for (let networkCustomizationName in serviceModel.networks) { + let networkModel = serviceModel.networks[networkCustomizationName]; + if ( networkModel.customizationUuid != null ) { + isNewFlow = true; + break; + } + } + if ( !isNewFlow ) { + for (let vnfCustomizationName in serviceModel.vnfs) { + let vnfModel = serviceModel.vnfs[vnfCustomizationName]; + if ( vnfModel.customizationUuid != null ) { + isNewFlow = true; + break; + } + } + } + if ( isNewFlow ) { + return (Utils.convertNewModel (serviceModel) ); + } + else { + return (Utils.convertOldModel (serviceModel) ); + } + }; + + private static convertNewModel (serviceModel ) { + let completeResources = new Array(); + let resource = {}; + let convertedAsdcModel = { + "service": serviceModel.service, + "networks": {}, + "vnfs": {}, + "pnfs": serviceModel.pnfs, + "serviceProxies": serviceModel.serviceProxies, + "completeDisplayInputs": {}, + "isNewFlow": true + }; + + for(let key in serviceModel.service.inputs) { + if(_.includes(["instance_node_target", "naming_policy", "vf_instance_name"], key)) { + delete convertedAsdcModel.service.inputs[key]; + } + } + + for (let networkCustomizationName in serviceModel.networks) { + let networkModel = serviceModel.networks[networkCustomizationName]; + + convertedAsdcModel.networks[networkModel.customizationUuid] = { + "uuid": networkModel.uuid, + "invariantUuid": networkModel.invariantUuid, + "version": networkModel.version, + "name": networkModel.name, + "modelCustomizationName": networkModel.modelCustomizationName, + "customizationUuid": networkModel.customizationUuid, + "inputs": "", + "description": networkModel.description, + "commands": {}, + "displayInputs": {} + }; + + resource = { + "name": networkModel.modelCustomizationName, + "description": networkModel.description + }; + + completeResources.push (resource); + + } + + _.forEach(serviceModel.configurations, function(element) { + element.isConfig = true; + }); + _.forEach(serviceModel.pnfs, function(element, key) { + element.isPnf= true; + element.modelCustomizationName= key; + }); + let mergedVnfs = Object.assign(serviceModel.vnfs, serviceModel.configurations, serviceModel.pnfs); + + for (let vnfCustomizationName in mergedVnfs) { + let vnfModel = mergedVnfs[vnfCustomizationName]; + let vnfCustomizationUuid = vnfModel.customizationUuid; + convertedAsdcModel.vnfs[vnfModel.customizationUuid] = { + "uuid": vnfModel.uuid, + "invariantUuid": vnfModel.invariantUuid, + "version": vnfModel.version, + "name": vnfModel.name, + "modelCustomizationName": vnfModel.modelCustomizationName, + "customizationUuid": vnfModel.customizationUuid, + "inputs": "", + "description": vnfModel.description, + "vfModules": {}, + "volumeGroups": {}, + "commands": {}, + "displayInputs": {}, + "properties": {}, + "nfRole": "", + "nfType": "", + "sourceNodes": vnfModel.sourceNodes, + "collectorNodes": vnfModel.collectorNodes, + "isConfigurationByPolicy": vnfModel.configurationByPolicy ? vnfModel.configurationByPolicy : false, + "isConfig": vnfModel.isConfig ? vnfModel.isConfig : false, + "isPnf": vnfModel.isPnf ? vnfModel.isPnf : false + }; + + resource = { + "name": vnfModel.modelCustomizationName, + "description": vnfModel.description + }; + completeResources.push (resource); + + if (vnfModel.commands != null) { + /* + * commands: { + * internal_net_param_ntu: { + * command: get_input, + * displaName: internal_net_param_ntu, + * inputName: vccfd1_internal_net_param_ntu // pointer to input key + * } + * If the input name (ptr) is one of instance_node_target, naming_policy or vf_instance_name + * then ignore it + * + */ + + convertedAsdcModel.vnfs[vnfCustomizationUuid].properties=vnfModel.properties; + // + let vnf_type = ""; + let vnf_role = ""; + let vnf_function = ""; + let vnf_code = ""; + if ( !( _.isEmpty(vnfModel.properties) ) ) { + if (this.hasContents (vnfModel.properties.nf_type) ) { + vnf_type = vnfModel.properties.nf_type; + } + if (this.hasContents (vnfModel.properties.nf_role) ) { + vnf_role = vnfModel.properties.nf_role; + } + if (this.hasContents (vnfModel.properties.nf_function) ) { + vnf_function = vnfModel.properties.nf_function; + } + if (this.hasContents (vnfModel.properties.nf_naming_code) ) { + vnf_code = vnfModel.properties.nf_naming_code; + } + } + convertedAsdcModel.vnfs[vnfCustomizationUuid]["nfType"] = vnf_type; + convertedAsdcModel.vnfs[vnfCustomizationUuid]["nfRole"] = vnf_role; + convertedAsdcModel.vnfs[vnfCustomizationUuid]["nfFunction"] = vnf_function; + convertedAsdcModel.vnfs[vnfCustomizationUuid]["nfCode"] = vnf_code; + // + for (let vfModuleCustomizationName in serviceModel.vnfs[vnfCustomizationName].vfModules) { + let vfModuleModel = serviceModel.vnfs[vnfCustomizationName].vfModules[vfModuleCustomizationName]; + convertedAsdcModel.vnfs[vnfCustomizationUuid].vfModules[vfModuleModel.customizationUuid] = vfModuleModel; + } + + for (let volumeGroupCustomizationName in serviceModel.vnfs[vnfCustomizationName].volumeGroups) { + let volumeGroupModel = serviceModel.vnfs[vnfCustomizationName].volumeGroups[volumeGroupCustomizationName]; + convertedAsdcModel.vnfs[vnfCustomizationUuid].volumeGroups[volumeGroupModel.customizationUuid] = volumeGroupModel; + } + } + } + + return (convertedAsdcModel); + }; + + private static convertOldModel(serviceModel ) { + let resource = {}; + let convertedAsdcModel = { + "service": serviceModel.service, + "networks": {}, + "vnfs": {}, + "pnfs": serviceModel.pnfs, + "serviceProxies": serviceModel.serviceProxies, + "completeDisplayInputs": {}, + "isNewFlow": false + }; + let completeResources = new Array(); + for (let networkCustomizationName in serviceModel.networks) { + let networkModel = serviceModel.networks[networkCustomizationName]; + convertedAsdcModel.networks[networkModel.invariantUuid] = {}; + //convertedAsdcModel.networks[networkModel.invariantUuid][networkModel.version] = networkModel; + // need a network model to test this + convertedAsdcModel.networks[networkModel.uuid] = { + "uuid": networkModel.uuid, + "invariantUuid": networkModel.invariantUuid, + "version": networkModel.version, + "name": networkModel.name, + "modelCustomizationName": networkModel.modelCustomizationName, + "customizationUuid": networkModel.customizationUuid, + "inputs": "", + "description": networkModel.description, + "commands": {}, + "displayInputs": {} + }; + resource = { + "name": networkModel.modelCustomizationName, + "description": networkModel.description + }; + completeResources.push (resource); + } + + _.forEach(serviceModel.configurations, function(element) { + element.isConfig = true; + }); + _.forEach(serviceModel.pnfs, function(element, key) { + element.isPnf= true; + element.modelCustomizationName= key; + }); + let mergedVnfs = Object.assign(serviceModel.vnfs, serviceModel.configurations, serviceModel.pnfs); + + for (let vnfCustomizationName in mergedVnfs) { + let vnfModel = mergedVnfs[vnfCustomizationName]; + convertedAsdcModel.vnfs[vnfModel.uuid] = { + "uuid": vnfModel.uuid, + "invariantUuid": vnfModel.invariantUuid, + "version": vnfModel.version, + "name": vnfModel.name, + "modelCustomizationName": vnfModel.modelCustomizationName, + "customizationUuid": vnfModel.customizationUuid, + "inputs": "", + "description": vnfModel.description, + "vfModules": {}, + "volumeGroups": {}, + "commands": {}, + "displayInputs": {}, + "sourceNodes": vnfModel.sourceNodes, + "collectorNodes": vnfModel.collectorNodes, + "isConfigurationByPolicy": vnfModel.configurationByPolicy ? vnfModel.configurationByPolicy : false, + "isConfig": vnfModel.isConfig ? vnfModel.isConfig : false, + "isPnf": vnfModel.isPnf ? vnfModel.isPnf : false + }; + resource = { + "name": vnfModel.modelCustomizationName, + "description": vnfModel.description + }; + completeResources.push (resource); + + for (let vfModuleCustomizationName in serviceModel.vnfs[vnfCustomizationName].vfModules) { + let vfModuleModel = serviceModel.vnfs[vnfCustomizationName].vfModules[vfModuleCustomizationName]; + convertedAsdcModel.vnfs[vnfModel.uuid].vfModules[vfModuleModel.uuid] = vfModuleModel; + } + + for (let volumeGroupCustomizationName in serviceModel.vnfs[vnfCustomizationName].volumeGroups) { + let volumeGroupModel = serviceModel.vnfs[vnfCustomizationName].volumeGroups[volumeGroupCustomizationName]; + convertedAsdcModel.vnfs[vnfModel.uuid].volumeGroups[volumeGroupModel.uuid] = volumeGroupModel; + } + } + + let completeDisplayInputs = {}; + + return (convertedAsdcModel); + }; +} diff --git a/vid-webpack-master/src/app/vlanTagging/network-selector/network-selector.component.html b/vid-webpack-master/src/app/vlanTagging/network-selector/network-selector.component.html new file mode 100644 index 00000000..fbb16949 --- /dev/null +++ b/vid-webpack-master/src/app/vlanTagging/network-selector/network-selector.component.html @@ -0,0 +1,14 @@ +<div class="form-wrapper"> + <form #form="ngForm"> + <div class="network" *ngFor="let group of groups"> + <label class="placeholder" [attr.data-tests-id]="'groupLabel'">{{getValueOfLabelInGroup(group,'Group Name')}}</label> + <select [(ngModel)]="groupSelection[getValueOfLabelInGroup(group,'Group Name')]" class="form-control input-text" + [attr.data-tests-id]="'groupTestId'" + id="{{getGroupName(group)}}" name="{{getGroupName(group)}}" required> + <option class="placeholder" [value]="undefined" disabled>Select Network</option> + <option *ngFor="let singleGroup of getNetworksByNetworkFunctionRole(group) | async" + [ngValue]="singleGroup">{{singleGroup["instance-group"]["instance-group-name"]}}</option> + </select> + </div> + </form> +</div> diff --git a/vid-webpack-master/src/app/vlanTagging/network-selector/network-selector.component.scss b/vid-webpack-master/src/app/vlanTagging/network-selector/network-selector.component.scss new file mode 100644 index 00000000..aa45b34f --- /dev/null +++ b/vid-webpack-master/src/app/vlanTagging/network-selector/network-selector.component.scss @@ -0,0 +1,17 @@ +.form-wrapper{ + width:640px; + padding-left: 50px; + padding-top: 50px; + .network{ + padding-bottom: 30px; + } + .network { + select { + font-family: OpenSans-Italic; + font-size: 14px; + color: #959595 !important; + } + } +} + + diff --git a/vid-webpack-master/src/app/vlanTagging/network-selector/network-selector.component.ts b/vid-webpack-master/src/app/vlanTagging/network-selector/network-selector.component.ts new file mode 100644 index 00000000..46a17698 --- /dev/null +++ b/vid-webpack-master/src/app/vlanTagging/network-selector/network-selector.component.ts @@ -0,0 +1,66 @@ +import {Component, Input, OnInit, ViewChild} from "@angular/core"; +import {NgRedux, select} from "@angular-redux/store"; +import {AppState} from "../../store/reducers"; +import {ModelInformationItem} from "../../shared/components/model-information/model-information.component"; +import {Observable} from "rxjs/Observable"; +import {NgForm} from "@angular/forms"; +import * as _ from 'lodash'; + +@Component({ + selector: "app-network-selector", + templateUrl: "./network-selector.component.html", + styleUrls: ["./network-selector.component.scss"] +}) +export class NetworkSelectorComponent implements OnInit { + private localStore: NgRedux<AppState>; + networkSelection: any; + groupSelection :any; + @Input() groups: Array<string>; + @Input() cloudOwner : string; + @Input() cloudRegionId : string; + @select(['service', 'networkFunctions']) + readonly networkFunctions: Observable<any>; + + @ViewChild('form') form: NgForm; + + constructor(store: NgRedux<AppState>) { + this.localStore = store; + this.groupSelection = {}; + console.log(store); + } + + getValueOfLabelInGroup(group: any, label: string){ + let item = _.find(group, {"label": label}); + if (item) { + return item["values"][0]; + } + } + + getNetworksByNetworkFunctionRole(group: any): Observable<string[]> { + let filteredItem = this.getValueOfLabelInGroup(group,"Network Collection Function"); + return this.networkFunctions.map(data => { + return (data && data[filteredItem]) || []; + }); + } + + getGroupName(group: ModelInformationItem[]) { + return this.getValueOfLabelInGroup(group,"Group Name"); + } + + /** + * [ncf=>aaiResonse] + * groups => groupsModel + */ + + ngOnInit() { + let store = this.localStore; + this.networkSelection = {}; + + } +} + + +// +/* +Info inside redux = {ncfNetworkMap } +*/ diff --git a/vid-webpack-master/src/app/vlanTagging/vlan-tagging.component.html b/vid-webpack-master/src/app/vlanTagging/vlan-tagging.component.html new file mode 100644 index 00000000..f474f4bd --- /dev/null +++ b/vid-webpack-master/src/app/vlanTagging/vlan-tagging.component.html @@ -0,0 +1,27 @@ +<div class="vlan-wrapper-config"> + <div class="head"> + <div class="title">Collection Resource Consumer VNF</div> + <div class="btn-wrapper"> + <button class="cancel-btn grey" data-tests-id="cancelButton" (click)="cancel()">{{cancelButtonText}}</button> + <button class="next-btn blue" data-tests-id="nextButton" [disabled]="isNextButtonDisabled()" (click)="nextStep()">{{nextButtonText}}</button> + </div> + </div> + <span (serviceInstamce)="onServiceInstanceChange($event)"></span> + <div class="content-wrapper"> + <div class="left-side"> + <formasync [hidden]="currentStep === wizardSteps.two" [model]="model" class="form"></formasync> + <app-network-selector [hidden]="currentStep === wizardSteps.one" class="form-wrapper" [groups]="groups"></app-network-selector> + </div> + + <div style="padding-left: 30px;padding-right: 30px" class="sidebar-right"> + <div class="headline">META DATA</div> + <div class="seperator"></div> + <model-information [modelInformationItems]="modelInfoItems"></model-information> + <div class="headline">Collection Resource Consumer VNF</div> + <div *ngFor="let group of groups"> + <div class="seperator"></div> + <model-information [modelInformationItems]="group"></model-information> + </div> + </div> + </div> +</div> diff --git a/vid-webpack-master/src/app/vlanTagging/vlan-tagging.component.scss b/vid-webpack-master/src/app/vlanTagging/vlan-tagging.component.scss new file mode 100644 index 00000000..af4a7e21 --- /dev/null +++ b/vid-webpack-master/src/app/vlanTagging/vlan-tagging.component.scss @@ -0,0 +1,168 @@ +$popupHeaderHeight: 59px; + +.vlan-wrapper-config * { + font-family: OpenSans-Regular, sans-serif; +} + +.body-content-jsp { + margin-top: 35px; + margin-left: 201px; + margin-right: 0px; + background-color: white; +} + +:host { + height: 100%; +} +.form-wrapper{ + min-width: 75%; + overflow: auto; +} +button { + border-radius: 2px; + font-size: 14px; + line-height: 28px; + height: 30px; + &.blue { + border: #0091c8 1px solid; + background-color: #009fdb; + color: white; + &:hover { + background-color: #1ec2ff; + border-color: #0091c8; + } + &:active { + background-color: #0091c7; + border-color: #006186; + } + &[disabled] { + background-color: rgba(5, 104, 174, 0.3); + border-color: rgba(4, 113, 168, 0.3); + } + } + &.grey { + color: #009FDB; + background-color: #ffffff; + border: 1px solid #009FDB; + &:hover { + background-color: #f8f8f8; + border-color: #009fdb; + } + &:active { + background-color: #d8d8d8; + border-color: #5a5a5a; + } + &[disabled] { + background-color: #f8f8f8; + border-color: #d8d8d8; + color: #cdcdcd; + } + } + &.white { + border: #009fdb 1px solid; + background-color: white; + color: #009fdb; + } +} + +.vlan-wrapper-config { + /*todo: remove. this a temporary fix for the sub-interface popup not showing as a page beside the sidebar. currently showing it in full screen.*/ + /*it is necessary inside the iframe (and not just in the angularjs - old app) angular app because in Firefox the iframe content isn't effected by the iframe size */ + width: 100vw; + height: 100vh; + position: fixed; + /********************/ + .head { + line-height: $popupHeaderHeight; + height: $popupHeaderHeight; + display: flex; + background-color: #F8F8F8; + border-bottom: 1px solid #D8D8D8; + .title { + font-size: 18px; + margin-left: 56px; + color: #5A5A5A; + } + .btn-wrapper { + flex: 1; + text-align: right; + margin-right: 15px; + button { + width: 120px; + &.delete-btn { + margin-left: 13px; + } + } + } + } + .content-wrapper { + display: flex; + .form { + overflow-y: auto; + } + height: calc(100% - #{$popupHeaderHeight}); + .form { + margin-top: 48px; + + } + .content { + .form-wrapper { + + margin: 0 auto; + } + } + + } + + span.error { + color: #cf2a2a; + } + .content-wrapper .content .form-wrapper .instance-field { + .ng-invalid-pattern { + border-color: #cf2a2a; + color: #cf2a2a; + } + margin-bottom: 25px; + label { + color: #191919; + font-size: 13px; + } + input, + select { + border-color: #D2D2D2; + } + &.lcpRegionText, + &.productFamily { + display: none; + } + } + +} + +.sidebar-right { + min-width: 25%; + background-color: #F8F8F8; + overflow-y: auto; + .headline { + font-family: OpenSans-Semibold; + font-size: 12px; + color: #009FDB; + padding-bottom: 6px; + padding-top: 20px + } + .seperator{ + background: #D2D2D2; + height: 1px; + margin-bottom: 10px; + } +} + +.left-side{ + min-width: 75%; + overflow:auto; + +} + +.sidebar-right service-metadata .metadata-content { + padding-bottom: 20px; +} diff --git a/vid-webpack-master/src/app/vlanTagging/vlan-tagging.component.ts b/vid-webpack-master/src/app/vlanTagging/vlan-tagging.component.ts new file mode 100644 index 00000000..f3f63e1e --- /dev/null +++ b/vid-webpack-master/src/app/vlanTagging/vlan-tagging.component.ts @@ -0,0 +1,203 @@ +import {formasync} from './../components/form-async/form-async.component'; +import {Component, OnInit, ViewChild} from "@angular/core"; +import {NetworkSelectorComponent} from "./network-selector/network-selector.component"; +import {NgRedux, select} from "@angular-redux/store"; +import {AppState} from "../store/reducers"; +import {ActivatedRoute} from "@angular/router"; +import { + loadServiceAccordingToUuid, loadAaiNetworkAccordingToNetworkCF, + loadUserId +} from "../services/aaiService/aai.actions"; +import {createRequest} from "../factories/mso.factory"; +import {Observable} from "rxjs/Observable"; +import {VNFModel} from "../shared/models/vnfModel"; +import {VfcInstanceGroupProperties} from "../shared/models/vfcInstanceGroupProperties"; +import * as _ from "lodash"; +import {ModelInformationItem} from "../shared/components/model-information/model-information.component"; + +enum WizardSteps { + one, + two +} + +const buttonTextNext = "Next"; +const buttonTextBack = "Back"; +const buttonTextConfirm = "Confirm"; +const buttonTextCancel = "Cancel"; + +@Component({ + selector: "vlan-tagging", + templateUrl: "./vlan-tagging.component.html", + styleUrls: ["./vlan-tagging.component.scss"] +}) + +export class VlanTaggingComponent implements OnInit { + constructor(private store: NgRedux<AppState>, + private route: ActivatedRoute) { + this.nextButtonText = buttonTextNext; + this.cancelButtonText = buttonTextCancel; + this.currentStep = WizardSteps.one; + } + + subscriberName: string; + serviceKey: string; + serviceType: string; + vnfKey: string; + serviceInstanceName: string; + serviceModelId: string; + modelInfoItems: Array<ModelInformationItem>; + groups: Array<Array<ModelInformationItem>>; + currentStep: WizardSteps; + nextButtonText: string; + cancelButtonText: string; + wizardSteps = WizardSteps; + cloudOwner: string; + cloudRegionId: string; + serviceInstanceId : string; + model: VNFModel; + userId: string; + modelCustomizationId : string; + onServiceInstanceChange(event) { + console.log(event); + } + + private serviceHirarchy: any; + + @select(['service', 'serviceHierarchy']) + readonly serviceHierarchyObserable: Observable<any>; + + @select(['service', 'userId']) + readonly userIdObs: Observable<any>; + + + @ViewChild(NetworkSelectorComponent) + public networkSelectorComponent: NetworkSelectorComponent; + @ViewChild(formasync) + public formAsync: formasync; + + + deploySubInterface() { + + const requestDetails = createRequest( + this.userId, + this.formAsync.serviceInstance, + this.serviceHirarchy[this.serviceModelId], + this.serviceKey, + this.networkSelectorComponent.groupSelection, + this.vnfKey, + this.modelCustomizationId + ); + + // this.msoService.createVnf(requestDetails, this.serviceKey).subscribe(); + + window.parent.postMessage({ + eventId: 'submitIframe', + data: requestDetails + }, "*"); + } + + ngOnInit() { + this.cloudRegionId = ""; + this.store.dispatch(loadUserId()); + this.userIdObs.subscribe(res => this.userId = res); + this.route.queryParams.subscribe(params => { + this.serviceModelId = params["serviceModelId"]; + this.subscriberName = params["subscriberName"]; + this.serviceType = params["serviceType"]; + this.serviceKey = params["serviceInstanceID"]; + this.vnfKey = params["modelCustomizationName"]; + this.serviceInstanceId = params["serviceInstanceID"]; + this.serviceInstanceName = params["serviceInstanceName"]; + this.modelCustomizationId = params["modelCustomizationId"]; + this.cloudOwner = params["globalCustomerId"]; + this.store.dispatch(loadServiceAccordingToUuid(this.serviceModelId)); + this.serviceHierarchyObserable.subscribe(data => { + this.serviceHirarchy = data; + if (data && data[this.serviceModelId]) { + this.model = new VNFModel(data[this.serviceModelId].vnfs[this.vnfKey]); + this.updateModelInfo(this.model); + } + }); + }); + } + + private updateModelInfo(vnfModel: VNFModel) { + this.modelInfoItems = [ + new ModelInformationItem("Service Instance Name", "serviceInstanceName", [this.serviceInstanceName], "", true), + new ModelInformationItem("Model Invariant UUID", "modelInvariantUUID", [vnfModel.invariantUuid], "", true), + new ModelInformationItem("Model Version", "modelVersion", [vnfModel.version], "", true), + new ModelInformationItem("Model UUID", "modelUuid", [vnfModel.uuid], "", true), + new ModelInformationItem("Model Customization UUID", "modelCustomizationUuid", [vnfModel.customizationUuid], "", true), + ]; + + this.groups = []; + _.forOwn(vnfModel.vfcInstanceGroups, (vfcInstanceGroup, key) => { + const properties: VfcInstanceGroupProperties = vfcInstanceGroup.vfcInstanceGroupProperties; + this.groups.push(this.createGroupsInformation(vfcInstanceGroup.name, properties)); + }); + } + + nextStep() { + switch (this.currentStep) { + case WizardSteps.one: + this.groups.map(group => { + let networkName = _.find(group, (groupElements: ModelInformationItem) => groupElements.testsId === "networkCollectionFunction"); + this.store.dispatch( + loadAaiNetworkAccordingToNetworkCF(networkName["values"][0], this.cloudOwner, this.formAsync.serviceInstance.lcpRegion) + ); + }); + this.currentStep = WizardSteps.two; + this.updateNavigationButtonText(this.currentStep); + break; + case WizardSteps.two: + this.deploySubInterface(); + break; + } + } + + cancel() { + switch (this.currentStep) { + case WizardSteps.one: + window.parent.postMessage({ + eventId: 'closeIframe' + }, "*"); + break; + case WizardSteps.two: + this.currentStep = WizardSteps.one; + this.updateNavigationButtonText(this.currentStep); + break; + } + } + + updateNavigationButtonText(step: WizardSteps) { + switch (step) { + case WizardSteps.one: + this.nextButtonText = buttonTextNext; + this.cancelButtonText = buttonTextCancel; + break; + case WizardSteps.two: + this.nextButtonText = buttonTextConfirm; + this.cancelButtonText = buttonTextBack; + break; + } + } + + isNextButtonDisabled() { + switch (this.currentStep) { + case WizardSteps.one: + return !this.formAsync.form.valid; + case WizardSteps.two: + return !this.networkSelectorComponent.form.valid; + } + } + + createGroupsInformation(name: string, properties: VfcInstanceGroupProperties): Array<ModelInformationItem> { + let modelInfoItems = []; + modelInfoItems.push(new ModelInformationItem("Group Name", "groupName", [name], "", true)); + modelInfoItems.push(new ModelInformationItem("Network Collection Function", "networkCollectionFunction", [properties.networkCollectionFunction], "", true)); + modelInfoItems.push(new ModelInformationItem("VFC Instance Group Function", "instanceGroupFunction", [properties.vfcInstanceGroupFunction], "", true)); + modelInfoItems.push(new ModelInformationItem("Parent Port Role", "parentPortRole", [properties.vfcParentPortRole], "", true)); + modelInfoItems.push(new ModelInformationItem("Sub Interface Role", "subInterfaceRole", [properties.subinterfaceRole], "", true)); + return modelInfoItems; + } +} diff --git a/vid-webpack-master/src/app/vlanTagging/vlan-tagging.module.ts b/vid-webpack-master/src/app/vlanTagging/vlan-tagging.module.ts new file mode 100644 index 00000000..3227542c --- /dev/null +++ b/vid-webpack-master/src/app/vlanTagging/vlan-tagging.module.ts @@ -0,0 +1,33 @@ + +import { formasync } from './../components/form-async/form-async.component'; +import {NgModule,} from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { VlanTaggingComponent } from './vlan-tagging.component'; +import {CommonModule} from '@angular/common'; +import { BrowserModule } from '@angular/platform-browser'; +import { NgReduxModule } from '@angular-redux/store'; +import { SharedModule } from '../shared/shared.module'; +import { NetworkSelectorComponent } from './network-selector/network-selector.component'; +import { TooltipModule } from 'ngx-tooltip'; + + + +@NgModule({ + +imports: [ + CommonModule, + NgReduxModule, + FormsModule, + BrowserModule, + TooltipModule, + SharedModule.forRoot() + + ], + providers: [ ], + declarations: [VlanTaggingComponent,formasync,NetworkSelectorComponent], + entryComponents: [], + exports: [formasync] + +}) + +export class VlanTaggingModule { } |