diff options
author | Ittay Stern <ittay.stern@att.com> | 2018-08-29 17:01:32 +0300 |
---|---|---|
committer | Ittay Stern <ittay.stern@att.com> | 2019-02-18 18:35:30 +0200 |
commit | 6f900cc45d7dd7f97430812b86b5c1d1693c8ae3 (patch) | |
tree | 936005c364dc5a7264d6304d4777c3d83494db22 /vid-webpack-master/src/app/instantiationStatus | |
parent | 67d99f816cc583643c35193197594cf78d8ce60a (diff) |
merge from ecomp a88f0072 - Modern UI
Issue-ID: VID-378
Change-Id: Ibcb23dd27f550cf32ce2fe0239f0f496ae014ff6
Signed-off-by: Ittay Stern <ittay.stern@att.com>
Diffstat (limited to 'vid-webpack-master/src/app/instantiationStatus')
11 files changed, 585 insertions, 504 deletions
diff --git a/vid-webpack-master/src/app/instantiationStatus/InstantiationStatus.module.ts b/vid-webpack-master/src/app/instantiationStatus/InstantiationStatus.module.ts index 01db0f187..6d14b149d 100644 --- a/vid-webpack-master/src/app/instantiationStatus/InstantiationStatus.module.ts +++ b/vid-webpack-master/src/app/instantiationStatus/InstantiationStatus.module.ts @@ -1,33 +1,30 @@ 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"; +import {SdcUiComponentsModule} from "onap-ui-angular"; @NgModule({ imports: [ CommonModule, FormsModule, ReactiveFormsModule, + SdcUiComponentsModule, BootstrapModalModule, DataTableModule, TooltipModule, ModalModule, - InputsModule, - AngularSvgIconModule, ContextMenuModule, SharedModule.forRoot(), PopoverModule.forRoot()], - declarations: [InstantiationStatusComponent, AuditInfoModalComponent], + declarations: [InstantiationStatusComponent, ], providers: [InstantiationStatusComponentService, ContextMenuService] }) export class InstantiationStatusModule { } diff --git a/vid-webpack-master/src/app/instantiationStatus/InstantiationStatus.routing.ts b/vid-webpack-master/src/app/instantiationStatus/InstantiationStatus.routing.ts new file mode 100644 index 000000000..6991eefef --- /dev/null +++ b/vid-webpack-master/src/app/instantiationStatus/InstantiationStatus.routing.ts @@ -0,0 +1,19 @@ +import {Route} from "@angular/router"; +import {FlagsResolve} from "../shared/resolvers/flag/flag.resolver"; +import {InstantiationStatusComponent} from "./instantiationStatus.component"; + +export const InstantiationStatusRoutes: Route[] = [ + { + path: 'instantiationStatus', + children: [ + { + path: '', + component: InstantiationStatusComponent, + resolve: { + flags: FlagsResolve + }, + } + ] + } +]; + diff --git a/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.html b/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.html deleted file mode 100644 index 9386af347..000000000 --- a/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.html +++ /dev/null @@ -1,84 +0,0 @@ -<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 deleted file mode 100644 index 27b271496..000000000 --- a/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.scss +++ /dev/null @@ -1,159 +0,0 @@ -.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 deleted file mode 100644 index e69de29bb..000000000 --- a/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.service.ts +++ /dev/null diff --git a/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.ts b/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.ts deleted file mode 100644 index 1cff97f5b..000000000 --- a/vid-webpack-master/src/app/instantiationStatus/auditInfoModal/auditInfoModal.component.ts +++ /dev/null @@ -1,83 +0,0 @@ -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 index e0641d03b..212981aaf 100644 --- a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.html +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.html @@ -3,7 +3,7 @@ <div> <div class="row" style="margin-left: 0;"> <div> - <span class="title">Instantiation Status</span> + <span class="title" [attr.data-tests-id]="'instantiation-status-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." @@ -24,6 +24,7 @@ <thead class="thead-dark"> <tr> <th scope="col" class="smallTd">User ID</th> + <th scope="col" class="smallTd">Action</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> @@ -40,8 +41,9 @@ </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"> + <tr *ngFor="let data of serviceInfoData; trackBy: trackByFn; let i = index" [ngClass]="{'odd' : data.serviceIndex%2 == 1}" [id]="data.jobId"> <td class="smallTd" id="userId"><custom-ellipsis [id]="data.userId" [value]="data.userId"></custom-ellipsis></td> + <td class="smallTd" id="action"><custom-ellipsis [id]="data.action" [value]="data.action | capitalizeAndFormat"></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> @@ -55,37 +57,32 @@ <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 [value]="data.serviceStatus.tooltip" [popoverType]="data?.serviceStatus?.color" style="float: left;"> + <svg-icon + id="jobStatusIcon-{{i}}" + (click)="auditInfo(data)" + [mode]="data.serviceStatus.color" + [size]="'large'" + [name]="data.serviceStatus.iconClassName"> + </svg-icon> + </custom-popover> - <div class="menu-div" (click)="onContextMenu($event, data); currentJobId = data.jobId"> + <div class="menu-div" (click)="onContextMenu($event, data)"> <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> + <ng-template *ngFor="let action of contextMenuActions" contextMenuItem let-item + [visible]="action.visible" + [enabled]="action.enabled" + (execute)="action.click($event.item)"> + <div [attr.data-tests-id]="action.dataTestId" + [tooltip]="action?.tooltip" + [tooltipDisabled]="!action.tooltip"> + <span class="context-menu-icon"> + <i class="fa {{action.className}}" aria-hidden="true"></i> + </span> + {{action.name}} + </div> + </ng-template> </context-menu> </div> </td> @@ -93,7 +90,6 @@ </tbody> </table> </div> - <audit-info-modal></audit-info-modal> </div> 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 index c9f434e99..4848d8e99 100644 --- a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.spec.ts +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.spec.ts @@ -1,5 +1,6 @@ import {getTestBed, TestBed} from '@angular/core/testing'; import { + COMPLETED_WITH_ERRORS, INPROGRESS, InstantiationStatusComponentService, PAUSE, @@ -10,24 +11,64 @@ import { X_O } from './instantiationStatus.component.service'; import {ServiceInfoModel} from '../shared/server/serviceInfo/serviceInfo.model'; -import { Observable } from 'rxjs/Rx'; +import {AaiService} from "../shared/services/aaiService/aai.service"; +import {MsoService} from "../shared/services/msoService/mso.service"; +import {NgRedux} from "@angular-redux/store"; +import {HttpClientTestingModule} from "@angular/common/http/testing"; +import {FeatureFlagsService} from "../shared/services/featureFlag/feature-flags.service"; +import {DrawingBoardModes} from "../drawingBoard/service-planning/drawing-board.modes"; +import {RouterTestingModule} from "@angular/router/testing"; +import {of} from "rxjs"; +import {UrlTree} from "@angular/router"; +class MockAppStore<T> { + + getState() { + return { + global: { + flags: { + 'FLAG_1902_NEW_VIEW_EDIT': true, + + } + } + } + } + + dispatch() { + + } +} describe('Instantiation Status Service', () => { let injector; + let aaiService: AaiService; + let msoService: MsoService; let service: InstantiationStatusComponentService; - beforeEach(() => { + + beforeAll(done => (async () => { TestBed.configureTestingModule({ - imports: [], - providers: [InstantiationStatusComponentService] + imports: [ + HttpClientTestingModule, + RouterTestingModule, + ], + providers: [ + InstantiationStatusComponentService, + AaiService, + MsoService, + FeatureFlagsService, + {provide: NgRedux, useClass: MockAppStore}] }); + await TestBed.compileComponents(); injector = getTestBed(); + aaiService = injector.get(AaiService); + msoService = injector.get(MsoService); service = injector.get(InstantiationStatusComponentService); - }); - it('generateServiceInfoDataMapping should return mapping of arrays', (done: DoneFn) => { - let data : Array<ServiceInfoModel> = generateServiceInfoData(); + })().then(done).catch(done.fail)); + + test('generateServiceInfoDataMapping should return mapping of arrays', () => { + let data : ServiceInfoModel[] = generateServiceInfoData(); let result = service.generateServiceInfoDataMapping(data); expect(result['1']).toBeDefined(); @@ -37,53 +78,81 @@ describe('Instantiation Status Service', () => { 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) => { + test('generateServiceInfoDataMapping if array is empty should return empty object', () => { let result = service.generateServiceInfoDataMapping([]); expect(result['1']).not.toBeDefined(); expect(result['2']).not.toBeDefined(); expect(result['3']).not.toBeDefined(); - done(); }); - it('convertObjectToArray', (done: DoneFn) => { + test('convertObjectToArray', () => { - spyOn(service, 'convertObjectToArray').and.returnValue( - Observable.of([]) + jest.spyOn(service, 'convertObjectToArray').mockReturnValue( + of([]) ); - let data : Array<ServiceInfoModel> = generateServiceInfoData(); + let data : ServiceInfoModel[] = generateServiceInfoData(); service.convertObjectToArray(data).subscribe((result) => { expect(result).toBeDefined(); - done(); }); }); - it('getStatusTooltip should return status popover', (done: DoneFn) => { + test('click on "Open" button should open new view edit' , ()=>{ + const item = { + serviceModelId : 'serviceModelId', + serviceInstanceId : 'serviceInstanceId', + serviceType : 'serviceType', + subscriberId : 'subscriberId' + }; + let params:UrlTree = service.getNewViewEditUrlTree(item, DrawingBoardModes.VIEW); + expect(params.toString().startsWith('/servicePlanning/VIEW')).toBeTruthy(); + expect(params.queryParams).toEqual( + { + serviceModelId: item.serviceModelId, + serviceInstanceId: item.serviceInstanceId, + serviceType : item.serviceType, + subscriberId : item.subscriberId + }); + }); + + test('build the View Edit url' , ()=>{ + const item = { + serviceModelId : '28aeb8f6-5620-4148-8bfb-a5fb406f0309', + }; + let serviceModelUrl: string = '/servicePlanning/EDIT?serviceModelId=28aeb8f6-5620-4148-8bfb-a5fb406f0309'; + let suffix:string = '../../serviceModels.htm#'; + let tree:UrlTree = service.getNewViewEditUrlTree(item, DrawingBoardModes.EDIT); + let result = service.getViewEditUrl(tree); + expect (suffix + serviceModelUrl).toEqual(result); + }); + + test('getStatusTooltip should return status popover', () => { let result : ServiceStatus = service.getStatus('pending'); - expect(result.tooltip).toEqual('Pending: The service will automatically be sent for instantiation as soon as possible.'); + expect(result.tooltip).toEqual('Pending: The action required will be sent as soon as possible.'); result = service.getStatus('IN_PROGRESS'); - expect(result.tooltip).toEqual('In-progress: the service is in process of instantiation.'); + expect(result.tooltip).toEqual('In-progress: the service is in process of the action required.'); 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.'); + expect(result.tooltip).toEqual('Failed: All planned actions have failed.'); result = service.getStatus('COMPLETED'); - expect(result.tooltip).toEqual('Completed successfully: Service is successfully instantiated.'); + expect(result.tooltip).toEqual('Completed successfully: Service is successfully instantiated, updated or deleted.'); result = service.getStatus('STOPPED'); expect(result.tooltip).toEqual('Stopped: Due to previous failure, will not be instantiated.'); - done(); + + result = service.getStatus('COMPLETED_WITH_ERRORS'); + expect(result.tooltip).toEqual('Completed with errors: some of the planned actions where successfully committed while other have not.\n Open the service to check it out.'); }); - it('getStatusTooltip should return correct icon per job status', (done: DoneFn) => { + test('getStatusTooltip should return correct icon per job status', () => { let result : ServiceStatus = service.getStatus('pending'); expect(result.iconClassName).toEqual(PENDING); @@ -101,9 +170,10 @@ describe('Instantiation Status Service', () => { result = service.getStatus('STOPPED'); expect(result.iconClassName).toEqual(STOPED); - done(); - }); + result = service.getStatus('COMPLETED_WITH_ERRORS'); + expect(result.iconClassName).toEqual(COMPLETED_WITH_ERRORS); + }); function generateServiceInfoData(){ return JSON.parse(JSON.stringify([ diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.ts b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.ts index 293397cc9..0e4451ca8 100644 --- a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.ts +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.service.ts @@ -1,23 +1,38 @@ 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'; - +import * as _ from 'lodash'; +import {Observable} from 'rxjs/Observable'; +import {NgRedux} from "@angular-redux/store"; +import {AppState} from "../shared/store/reducers"; +import {AaiService} from "../shared/services/aaiService/aai.service"; +import {ServiceModel} from "../shared/models/serviceModel"; +import {FeatureFlagsService, Features} from "../shared/services/featureFlag/feature-flags.service"; +import {DrawingBoardModes} from "../drawingBoard/service-planning/drawing-board.modes"; +import {updateDrawingBoardStatus} from "../shared/storeUtil/utils/global/global.actions"; +import {Router, UrlTree} from "@angular/router"; +import {of} from "rxjs"; +import {MsoService} from "../shared/services/msoService/mso.service"; export let PENDING : string = "pending"; -export let INPROGRESS : string = "inprogress"; +export let INPROGRESS : string = "in_progress"; export let PAUSE : string = "pause"; -export let X_O : string = "X_o"; -export let SUCCESS_CIRCLE : string = "success+Circle"; -export let STOPED : string = "stoped"; +export let X_O : string = "x-circle-o"; +export let SUCCESS_CIRCLE : string = "success-circle-o"; +export let STOPED : string = "stop"; +export let COMPLETED_WITH_ERRORS : string = "success_with_warning"; @Injectable() export class InstantiationStatusComponentService { - generateServiceInfoDataMapping(arr: Array<ServiceInfoModel>) : { [serviceInstanceId: string]: Array<ServiceInfoModel>}{ - let serviceInfoData: { [serviceInstanceId: string]: Array<ServiceInfoModel>; } = {}; + constructor( private _aaiService: AaiService, + private _msoService: MsoService, + private _router : Router, + private _store: NgRedux<AppState>) { + } + + generateServiceInfoDataMapping(arr: ServiceInfoModel[]) : { [serviceInstanceId: string]: ServiceInfoModel[]}{ + let serviceInfoData: { [serviceInstanceId: string]: ServiceInfoModel[]; } = {}; for(let item of arr){ - if(isNullOrUndefined(serviceInfoData[item.templateId])){ + if(_.isNil(serviceInfoData[item.templateId])){ serviceInfoData[item.templateId] = [item]; }else { serviceInfoData[item.templateId].push(item); @@ -26,7 +41,7 @@ export class InstantiationStatusComponentService { return serviceInfoData; } - convertObjectToArray(arr: Array<ServiceInfoModel>) : Observable<Array<ServiceInfoUiModel>>{ + convertObjectToArray(arr: ServiceInfoModel[]) : Observable<ServiceInfoUiModel[]>{ const obj = this.generateServiceInfoDataMapping(arr); let index:number = 0; let result = []; @@ -40,25 +55,108 @@ export class InstantiationStatusComponentService { } console.log(result); - return Observable.of(result); + return of(result); + } + + isDrawingBoardViewEdit(serviceModel: ServiceModel): boolean { + if (!_.isNil(serviceModel.vidNotions) && !_.isNil(serviceModel.vidNotions.viewEditUI) + && serviceModel.vidNotions.viewEditUI !== 'legacy'){ + return true; + } + return false; + } + + open(item: ServiceInfoModel): void { + if (FeatureFlagsService.getFlagState(Features.FLAG_1902_VNF_GROUPING, this._store)) { + this._aaiService.getServiceModelById(item['serviceModelId']).subscribe((result)=>{ + const serviceModel = new ServiceModel(result); + + if (this.isDrawingBoardViewEdit(serviceModel)) { + this.navigateToNewViewEdit(item, DrawingBoardModes.EDIT); + return; + } + + this.navigateToNewViewOnlyOrOldEditView(item); + + }); + } + + /*this else is here only to save time in case we don't need to retrieve service model + it can be removed once it service model is always needed, and it doesn't save time*/ + else { + this.navigateToNewViewOnlyOrOldEditView(item); + } + } + + navigateToNewViewOnlyOrOldEditView(item: ServiceInfoModel) { + if (FeatureFlagsService.getFlagState(Features.FLAG_1902_NEW_VIEW_EDIT, this._store)) { + this.navigateToNewViewEdit(item, DrawingBoardModes.VIEW); + } + else { + this.navigateToOldViewEdit(item); + } + } + + navigateToOldViewEdit(item: ServiceInfoModel) { + let query = + `subscriberId=${item.subscriberId}&` + + `subscriberName=${item.subscriberName}&` + + `serviceType=${item.serviceType}&` + + `serviceInstanceId=${item.serviceInstanceId}`; + + this._store.dispatch(updateDrawingBoardStatus(DrawingBoardModes.OLD_VIEW_EDIT)); + window.parent.location.assign('../../serviceModels.htm#/instantiate?' + query); + } + + navigateToNewViewEdit(item: ServiceInfoModel, mode: DrawingBoardModes): void{ + this._store.dispatch(updateDrawingBoardStatus(mode)); + const viewEditUrlTree:UrlTree = this.getNewViewEditUrlTree(item, mode); + this._router.navigateByUrl(viewEditUrlTree); + window.parent.location.assign(this.getViewEditUrl(viewEditUrlTree)); + } + + getNewViewEditUrlTree(item: ServiceInfoModel, mode: DrawingBoardModes): UrlTree { + return this._router.createUrlTree( + ['/servicePlanning/' + mode], + { + queryParams: + { + serviceModelId: item.serviceModelId, + serviceInstanceId: item.serviceInstanceId, + serviceType : item.serviceType, + subscriberId : item.subscriberId, + jobId: item.jobId + } + }); + } + + getViewEditUrl(viewEditUrlTree:UrlTree): string { + return '../../serviceModels.htm#' + viewEditUrlTree.toString(); + } 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.'); + return new ServiceStatus(PENDING, 'primary', 'Pending: The action required will be sent as soon as possible.'); case 'IN_PROGRESS' : - return new ServiceStatus(INPROGRESS, '#009FDB', 'In-progress: the service is in process of instantiation.'); + return new ServiceStatus(INPROGRESS, 'primary', 'In-progress: the service is in process of the action required.'); 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.'); + return new ServiceStatus(PAUSE, 'primary', '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.'); + return new ServiceStatus(X_O, 'error', 'Failed: All planned actions have failed.'); case 'COMPLETED' : - return new ServiceStatus(SUCCESS_CIRCLE, '#53AD15', 'Completed successfully: Service is successfully instantiated.'); + return new ServiceStatus(SUCCESS_CIRCLE, 'success', 'Completed successfully: Service is successfully instantiated, updated or deleted.'); case 'STOPPED' : - return new ServiceStatus(STOPED, '#D02B2B', 'Stopped: Due to previous failure, will not be instantiated.'); + return new ServiceStatus(STOPED, 'error', 'Stopped: Due to previous failure, will not be instantiated.'); + case 'COMPLETED_WITH_ERRORS' : + return new ServiceStatus(COMPLETED_WITH_ERRORS, 'success', 'Completed with errors: some of the planned actions where successfully committed while other have not.\n Open the service to check it out.'); } } + + retry(item: ServiceInfoModel): void { + this.navigateToNewViewEdit(item, DrawingBoardModes.RETRY_EDIT); + } } diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.spec.ts b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.spec.ts index 00b6a9945..53dfcc1f2 100644 --- a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.spec.ts +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.spec.ts @@ -1,88 +1,227 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import {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 {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'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {ScrollToModule} from '@nicky-lenaers/ngx-scroll-to'; +import {ConfigurationService} from '../shared/services/configuration.service'; +import {LogService} from '../shared/utils/log/log.service'; +import {NgRedux} from '@angular-redux/store'; +import {RouterTestingModule} from '@angular/router/testing'; +import {CapitalizeAndFormatPipe} from "../shared/pipes/capitalize/capitalize-and-format.pipe"; +import {AaiService} from "../shared/services/aaiService/aai.service"; +import {MsoService} from "../shared/services/msoService/mso.service"; +import {FeatureFlagsService} from "../shared/services/featureFlag/feature-flags.service"; +import {JobStatus, ServiceAction} from "../shared/models/serviceInstanceActions"; +import each from 'jest-each'; +import {ServiceInfoModel} from "../shared/server/serviceInfo/serviceInfo.model"; +import { TooltipModule } from 'ngx-tooltip'; + +class MockAppStore<T> { + + getState() { + return { + global: { + flags: { + 'FLAG_1902_NEW_VIEW_EDIT': true + } + } + } + } + + dispatch() { + + } +} 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"}]; - +let item = new ServiceInfoModel(); + beforeAll(done => (async () => { - beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, ContextMenuModule, ScrollToModule.forRoot()], - providers: [ServiceInfoService, InstantiationStatusComponentService, ContextMenuService, ConfigurationService, LogService], - declarations: [InstantiationStatusComponent], + imports: [ + HttpClientTestingModule, + ContextMenuModule, + ScrollToModule.forRoot(), + RouterTestingModule, + TooltipModule + ], + providers: [ + ServiceInfoService, + InstantiationStatusComponentService, + AaiService, + MsoService, + ContextMenuService, + FeatureFlagsService, + ConfigurationService, + LogService, + {provide: NgRedux, useClass: MockAppStore} + ], + declarations: [InstantiationStatusComponent, CapitalizeAndFormatPipe], schemas: [ CUSTOM_ELEMENTS_SCHEMA ] - }).compileComponents(); - })); + }); + await TestBed.compileComponents(); - beforeEach(() => { fixture = TestBed.createComponent(InstantiationStatusComponent); component = fixture.componentInstance; fixture.detectChanges(); - }); + })().then(done).catch(done.fail)); + - it('component should initialize basic parameters', (done: DoneFn) => { + test('component should initialize basic parameters', () => { 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) => { + test('component constructor should call activateInterval and ngOnInit', () => { 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(); - }); + const enableDeleteItemsDataProvider = [ + ['INSTANTIATE action PENDING job status',JobStatus.PENDING , ServiceAction.INSTANTIATE], + ['DELETE action PENDING job status',JobStatus.PENDING , ServiceAction.DELETE], + ['UPDATE action PENDING job status',JobStatus.PENDING , ServiceAction.UPDATE], + ['INSTANTIATE action STOPPED job status',JobStatus.STOPPED , ServiceAction.INSTANTIATE]]; + each(enableDeleteItemsDataProvider).test('delete item should enable for %s', (desc, jobStatus, action ) => { + item.action=action; + item.jobStatus=jobStatus; + let isDeleteEnabled: boolean = component.isDeleteEnabled(item); + expect(isDeleteEnabled).toBeTruthy(); + }); - disableDeleteItems.forEach((item) => { - let isDeleteEnabled: boolean = component.isDeleteEnabled(item); - expect(isDeleteEnabled).toBeFalsy(); - }); - done(); + const disableDeleteItemsDataProvider = [ + [ 'INSTANTIATE action COMPLETED job status', JobStatus.COMPLETED , ServiceAction.INSTANTIATE], + [ 'INSTANTIATE action FAILED job status', JobStatus.FAILED , ServiceAction.INSTANTIATE], + [ 'INSTANTIATE action IN_PROGRESS job status', JobStatus.IN_PROGRESS, ServiceAction.INSTANTIATE], + [ 'INSTANTIATE action COMPLETED_WITH_ERRORS job status', JobStatus.COMPLETED_WITH_ERRORS, ServiceAction.INSTANTIATE], + [ 'DELETE action IN_PROGRESS job status', JobStatus.IN_PROGRESS, ServiceAction.DELETE], + [ 'DELETE action COMPLETED_WITH_ERRORS job status',JobStatus.COMPLETED_WITH_ERRORS, ServiceAction.DELETE], + [ 'UPDATE action IN_PROGRESS job status', JobStatus.IN_PROGRESS, ServiceAction.UPDATE], + [ 'UPDATE action COMPLETED_WITH_ERRORS job status', JobStatus.COMPLETED_WITH_ERRORS, ServiceAction.UPDATE], + [ 'INSTANTIATE action UNKNOWN job status', "UNKNOWN", ServiceAction.INSTANTIATE]]; + each(disableDeleteItemsDataProvider).test('delete item should disable for %s', (desc,jobStatus, action ) => { + item.action=action; + item.jobStatus=jobStatus; + let isDeleteEnabled: boolean = component.isDeleteEnabled(item); + expect(isDeleteEnabled).toBeFalsy(); }); - 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 enableHideItemsDataProvider = [ + ['instantiate action job status COMPLETED',JobStatus.COMPLETED, ServiceAction.INSTANTIATE ], + ['instantiate action job status FAILED',JobStatus.FAILED, ServiceAction.INSTANTIATE ], + ['instantiate action job status STOPPED', JobStatus.STOPPED, ServiceAction.INSTANTIATE ], + ['instantiate action job status COMPLETED_WITH_ERRORS', JobStatus.COMPLETED_WITH_ERRORS, ServiceAction.INSTANTIATE ], + ['delete action job status COMPLETED', JobStatus.COMPLETED, ServiceAction.DELETE ], + ['delete action job status FAILED', JobStatus.FAILED, ServiceAction.DELETE ], + ['delete action job status STOPPED',JobStatus.STOPPED, ServiceAction.DELETE ], + ['delete action job status COMPLETED_WITH_ERRORS', JobStatus.COMPLETED_WITH_ERRORS, ServiceAction.DELETE ], + ['update action job status COMPLETED', JobStatus.COMPLETED, ServiceAction.UPDATE ], + ['update action job status FAILED',JobStatus.FAILED, ServiceAction.UPDATE ], + ['update action job status STOPPED', JobStatus.STOPPED, ServiceAction.UPDATE ], + ['update action job status COMPLETED_WITH_ERRORS', JobStatus.COMPLETED_WITH_ERRORS, ServiceAction.UPDATE ]]; + each(enableHideItemsDataProvider).test('hide item should be enabled for %s', (desc, jobStatus, action ) => { + item.action=action; + item.jobStatus=jobStatus; + let isHideEnabled: boolean = component.isHideEnabled(item); + expect(isHideEnabled).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(); + const disableHideItemsDataProvider = [ + ['action instantiate job status IN_PROGRESS',JobStatus.IN_PROGRESS , ServiceAction.INSTANTIATE], + ['action instantiate job status PAUSE',JobStatus.PAUSE, ServiceAction.INSTANTIATE], + ['action instantiate job status PENDING', JobStatus.PENDING, ServiceAction.INSTANTIATE ], + ['action instantiate job status UNKNOWN', "UNKNOWN", ServiceAction.INSTANTIATE], + ['update instantiate job status IN_PROGRESS', JobStatus.IN_PROGRESS , ServiceAction.UPDATE], + ['update instantiate job status PAUSE', JobStatus.PAUSE, ServiceAction.UPDATE], + ['update instantiate job status PENDING', JobStatus.PENDING, ServiceAction.UPDATE ], + ['update instantiate job status UNKNOWN',"UNKNOWN", ServiceAction.UPDATE], + ['delete instantiate job status IN_PROGRESS',JobStatus.IN_PROGRESS , ServiceAction.DELETE], + ['delete instantiate job status PAUSE', JobStatus.PAUSE, ServiceAction.DELETE], + ['delete instantiate job status PENDING', JobStatus.PENDING, ServiceAction.DELETE ], + ['delete instantiate job status UNKNOWN', "UNKNOWN", ServiceAction.DELETE]]; + each(disableHideItemsDataProvider).test('hide item should disable for %s', (desc, jobStatus, action ) => { + item.action=action; + item.jobStatus=jobStatus; + let isHideEnabled: boolean = component.isHideEnabled(item); + expect(isHideEnabled).toBeFalsy(); + }); + + const enableAuditItemsDataProvider = [ + ['instantiate action UNKNOWN job status', "UNKNOWN", ServiceAction.INSTANTIATE ], + ['delete action JobStatus IN_PROGRESS', JobStatus.IN_PROGRESS, ServiceAction.DELETE ], + ['delete action JobStatus PAUSE', JobStatus.PAUSE, ServiceAction.DELETE ], + ['delete action JobStatus FAILED', JobStatus.FAILED, ServiceAction.DELETE ], + ['delete action JobStatus COMPLETED', JobStatus.COMPLETED, ServiceAction.DELETE ], + ['delete action JobStatus COMPLETED_WITH_ERRORS', JobStatus.COMPLETED_WITH_ERRORS, ServiceAction.DELETE ], + ['update action JobStatus IN_PROGRESS', JobStatus.IN_PROGRESS, ServiceAction.UPDATE ], + ['update action JobStatus PAUSE', JobStatus.PAUSE, ServiceAction.UPDATE ], + ['update action JobStatus COMPLETED', JobStatus.COMPLETED, ServiceAction.UPDATE ], + ['update action JobStatus FAILED', JobStatus.FAILED, ServiceAction.UPDATE ], + ['update action JobStatus COMPLETED_WITH_ERRORS', JobStatus.COMPLETED_WITH_ERRORS, ServiceAction.UPDATE ]]; + + each(enableAuditItemsDataProvider).test('audit item should be enabled for %s', (desc, jobStatus, action ) => { + item.action=action; + item.jobStatus=jobStatus; + const isAuditEnabled: boolean = component.isAuditInfoEnabled(item); + expect(isAuditEnabled).toBeTruthy(); + }); + + const disableAuditItemsDataProvider = [ + ['Job status STOPPED action update', JobStatus.STOPPED, ServiceAction.UPDATE ], + ['Job status PENDING action update', JobStatus.PENDING, ServiceAction.UPDATE ], + ['Job status UNKNOWN action update', "UNKNOWN", ServiceAction.UPDATE], + ['Job status STOPPED action delete',JobStatus.STOPPED, ServiceAction.DELETE], + ['Job status PENDING action delete', JobStatus.PENDING, ServiceAction.DELETE ], + ['Job status UNKNOWN action delete', "UNKNOWN", ServiceAction.DELETE]]; + each(disableAuditItemsDataProvider).test('audit item should be disabled for %s', (desc, jobStatus, action ) => { + item.action=action; + item.jobStatus=jobStatus; + let isAuditEnabled: boolean = component.isAuditInfoEnabled(item); + expect(isAuditEnabled).toBeFalsy(); + }); + + const enableOpenItemsDataProvider = [ + ['action instantiate job status PAUSE', JobStatus.PAUSE, ServiceAction.INSTANTIATE ], + ['action instantiate job status COMPLETED', JobStatus.COMPLETED, ServiceAction.INSTANTIATE ], + ['action instantiate job status COMPLETED_WITH_ERRORS', JobStatus.COMPLETED_WITH_ERRORS, ServiceAction.INSTANTIATE ], + ['action delete job status PENDING', JobStatus.PENDING, ServiceAction.DELETE ], + ['action delete job status FAILED', JobStatus.FAILED, ServiceAction.DELETE ], + ['action delete job status COMPLETED_WITH_ERRORS', JobStatus.COMPLETED_WITH_ERRORS, ServiceAction.DELETE ], + ['action update job status PENDING', JobStatus.PENDING, ServiceAction.UPDATE ], + ['action update job status PAUSE', JobStatus.PAUSE, ServiceAction.UPDATE ], + ['action update job status COMPLETED', JobStatus.COMPLETED, ServiceAction.UPDATE ], + ['action update job status FAILED', JobStatus.FAILED, ServiceAction.UPDATE ], + ['action update job status COMPLETED_WITH_ERRORS', JobStatus.COMPLETED_WITH_ERRORS, ServiceAction.UPDATE ]]; + each(enableOpenItemsDataProvider).test('open item should be enabled for %s', (desc, jobStatus, action ) => { + item.action=action; + item.jobStatus=jobStatus; + let isOpenEnabled: boolean = component.isOpenEnabled(item); + expect(isOpenEnabled).toBeTruthy(); }); + + const disableOpenItemsDataProvider = [ + ['action instantiate job status STOPPED', JobStatus.STOPPED, ServiceAction.INSTANTIATE], + ['action instantiate job status FAILED', JobStatus.FAILED, ServiceAction.INSTANTIATE], + ['action instantiate job status UNKNOWN', "UNKNOWN", ServiceAction.INSTANTIATE], + ['action update job status STOPPED', JobStatus.STOPPED, ServiceAction.UPDATE ], + ['action update job status IN_PROGRESS', JobStatus.IN_PROGRESS, ServiceAction.UPDATE ], + ['action update job status UNKNOWN', "UNKNOWN", ServiceAction.UPDATE], + ['action delete job status COMPLETED', JobStatus.COMPLETED, ServiceAction.DELETE], + ['action delete job status PAUSE', JobStatus.PAUSE, ServiceAction.DELETE], + ['action delete job status IN_PROGRESS', JobStatus.IN_PROGRESS, ServiceAction.DELETE ], + ['action delete job status UNKNOWN',"UNKNOWN", ServiceAction.DELETE]]; + each(disableOpenItemsDataProvider).test('open item should be disabled for %s', (desc, jobStatus, action ) => { + item.action=action; + item.jobStatus=jobStatus; + let isOpenEnabled: boolean = component.isOpenEnabled(item); + expect(isOpenEnabled).toBeFalsy(); + }); + }); diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.ts b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.ts index ed45ce43c..f3f651960 100644 --- a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.ts +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.ts @@ -1,40 +1,97 @@ -import {AfterViewChecked, Component, ViewChild} from '@angular/core'; +import {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 {AuditInfoModalComponent} from "../shared/components/auditInfoModal/auditInfoModal.component"; import * as _ from 'lodash'; import {ScrollToConfigOptions, ScrollToService} from '@nicky-lenaers/ngx-scroll-to'; -import {ConfigurationService} from "../services/configuration.service"; +import {ConfigurationService} from "../shared/services/configuration.service"; import {LogService} from '../shared/utils/log/log.service'; - +import {AppState} from "../shared/store/reducers"; +import {NgRedux} from '@angular-redux/store'; +import {JobStatus, ServiceAction} from "../shared/models/serviceInstanceActions"; +import {DrawingBoardModes} from "../drawingBoard/service-planning/drawing-board.modes"; + +export interface MenuAction{ + name: string; + dataTestId: string; + className: string; + tooltip?: string; + click(item: ServiceInfoModel): void; + enabled (item?: ServiceInfoModel): boolean; + visible (item?: ServiceInfoModel): boolean; +} @Component({ selector : 'instantiation-status', templateUrl : './instantiationStatus.component.html', styleUrls : ['./instantiationStatus.component.scss'] }) -export class InstantiationStatusComponent implements AfterViewChecked{ - +export class InstantiationStatusComponent { 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; + serviceInfoData: ServiceInfoModel[] = null; @ViewChild(ContextMenuComponent) public contextMenu: ContextMenuComponent; + public contextMenuActions: Array<MenuAction> = [ + { + name: "Redeploy", + dataTestId: "context-menu-retry", + className: "fa-repeat", + click: (item: ServiceInfoModel) => this.retryItem(item), + enabled: () => true, + visible: (item: ServiceInfoModel) => item.isRetryEnabled, + }, + { + name: "Open", + dataTestId: "context-menu-open", + className: "fa-external-link", + click: (item: ServiceInfoModel) => this.instantiationStatusComponentService.open(item), + enabled: (item: ServiceInfoModel) => this.isOpenEnabled(item), + visible: () => true, + }, + { + name: "Audit info", + dataTestId: "context-menu-audit-info", + className: "fa-info-circle", + click: (item: ServiceInfoModel) => this.auditInfo(item), + enabled: (item: ServiceInfoModel) => this.isAuditInfoEnabled(item), + visible: () => true, + }, + { + name: "Delete", + dataTestId: "context-menu-remove", + className: "fa-trash-o", + click: (item: ServiceInfoModel) => this.deleteItem(item), + enabled: (item: ServiceInfoModel) => this.isDeleteEnabled(item), + visible: () => true, + }, + { + name: "Hide request", + dataTestId: "context-menu-hide", + className: "fa-eye-slash", + tooltip: "Hide this service from this table", + click: (item: ServiceInfoModel) => this.hideItem(item), + enabled: (item: ServiceInfoModel) => this.isHideEnabled(item), + visible: () => true, + } + ]; + + flags: any; constructor(private _serviceInfoService: ServiceInfoService, private _instantiationStatusComponentService : InstantiationStatusComponentService, private _contextMenuService: ContextMenuService, private _configurationService : ConfigurationService, private _scrollToService: ScrollToService, - private _logService : LogService) { + private _logService : LogService, + private _store: NgRedux<AppState>) { this.instantiationStatusComponentService = _instantiationStatusComponentService; this.configurationService = this._configurationService; this.configurationService.getConfiguration("refreshTimeInstantiationDashboard").subscribe(response => { @@ -58,61 +115,73 @@ export class InstantiationStatusComponent implements AfterViewChecked{ refreshData(): void { this.dataIsReady = false; - this._serviceInfoService.getServicesJobInfo(true) - .subscribe((res: Array<ServiceInfoModel>) => { + this._serviceInfoService.getServicesJobInfo(true, this.lastUpdatedDate === null) + .subscribe((res: 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; + this.scrollToElement(this.findFirstVisibleJob()); } }); }) } - ngAfterViewChecked(){ - if (this.scroll) { - this.scrollToElement(); - this.scroll = false; - } + trackByFn(index: number, item: ServiceInfoModel){ + return _.isNil(item) ? null : item.jobId; } - - - isDeleteEnabled(item):boolean { - return _.includes(['PENDING', 'STOPPED'], item.jobStatus); - } - - deleteItem(item): void { + deleteItem(item: ServiceInfoModel): void { this._serviceInfoService.deleteJob(item.jobId).subscribe(() => { this.refreshData(); }); } - hideItem(item): void { + hideItem(item: ServiceInfoModel): void { this._serviceInfoService.hideJob(item.jobId).subscribe(() => { this.refreshData(); }); } + + retryItem(item: ServiceInfoModel) : void { + if (item.isRetryEnabled) { + this._instantiationStatusComponentService.retry(item); + } + } auditInfo(jobData : ServiceInfoModel): void { AuditInfoModalComponent.openModal.next(jobData); + } + isOpenEnabled(item: ServiceInfoModel):boolean { + switch(item.action) { + case ServiceAction.DELETE: + return _.includes([ JobStatus.PENDING, JobStatus.COMPLETED_WITH_ERRORS, JobStatus.FAILED], item.jobStatus); + case ServiceAction.UPDATE: + return _.includes([JobStatus.PENDING, JobStatus.PAUSE, JobStatus.COMPLETED_WITH_ERRORS, JobStatus.COMPLETED, JobStatus.FAILED], item.jobStatus); + default: + return _.includes([JobStatus.COMPLETED, JobStatus.PAUSE, JobStatus.COMPLETED_WITH_ERRORS], item.jobStatus); + } } - isOpenVisible(item):boolean { - return _.includes(['COMPLETED', 'PAUSE'], item.jobStatus); + isAuditInfoEnabled(item: ServiceInfoModel): boolean { + if(item.action === ServiceAction.DELETE || item.action=== ServiceAction.UPDATE) { + return _.includes([JobStatus.FAILED, JobStatus.IN_PROGRESS, JobStatus.COMPLETED_WITH_ERRORS, JobStatus.PAUSE, JobStatus.COMPLETED], item.jobStatus); + } + return true;// ServiceAction.INSTANTIATE } - open(item): void { - let query = - `subscriberId=${item['subscriberName']}&` + - `serviceType=${item['serviceType']}&` + - `serviceInstanceId=${item['serviceInstanceId']}`; + isDeleteEnabled(item: ServiceInfoModel):boolean { + if( item.action === ServiceAction.DELETE || item.action === ServiceAction.UPDATE){ + return _.includes([JobStatus.PENDING], item.jobStatus); + } + return _.includes([JobStatus.PENDING, JobStatus.STOPPED], item.jobStatus); + } - window.parent.location.assign('../../serviceModels.htm#/instantiate?' + query); + isHideEnabled(item: ServiceInfoModel):boolean { + return _.includes([JobStatus.COMPLETED, JobStatus.FAILED, JobStatus.STOPPED, JobStatus.COMPLETED_WITH_ERRORS], item.jobStatus); } public onContextMenu($event: MouseEvent, item: any): void { @@ -129,17 +198,36 @@ export class InstantiationStatusComponent implements AfterViewChecked{ return './' + imageName + '.svg'; } - isHideEnabled(item: any):boolean { - return _.includes(['COMPLETED', 'FAILED', 'STOPPED'], item.jobStatus); + private getHeaderHeaderClientRect(): ClientRect { + const element = document.querySelector("#instantiation-status thead") as HTMLElement; + return element.getBoundingClientRect(); } - scrollToElement() { - if(this.currentJobId){ + + findFirstVisibleJob(): HTMLElement { + const elements : any = document.querySelectorAll('#instantiation-status tr'); + const headerRect = this.getHeaderHeaderClientRect(); + if (headerRect) { + const topEdge = headerRect.bottom; + for (let i = 0; i < elements.length; i++) { + if (elements[i].getBoundingClientRect().top >= topEdge) + return elements[i]; + } + } + return null; + } + + scrollToElement(currentJob: HTMLElement) { + if (currentJob) { const config: ScrollToConfigOptions = { - target: this.currentJobId, - duration: 50, - offset: -35 //header height + target: currentJob, + duration: 0, + offset: -1 * (this.getHeaderHeaderClientRect().height + 2), }; - this._scrollToService.scrollTo(config); + + // wait after render + setTimeout(() => { + this._scrollToService.scrollTo(config); + }, 0) } } } |