diff options
Diffstat (limited to 'vid-webpack-master/src/app/instantiationStatus')
11 files changed, 1322 insertions, 0 deletions
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); + } + } +} |