<div class="mode-toggle__container">
<span class="mode-toggle__text">Light</span>
<label class="toggle-button__container">
- <input (click)="modeToggleSwitch()" type="checkbox" class="mode-toggle__input" />
+ <input id="darkModeSwitch" (click)="modeToggleSwitch()" type="checkbox" class="mode-toggle__input" />
<span [ngClass]="{'mode-toggle__bg-checked': darkMode}" class="mode-toggle__bg"></span>
<span [ngClass]="{'mode-toggle__circle-checked': darkMode}" class="mode-toggle__circle"></span>
</label>
describe('AppComponent', () => {
let fixture: ComponentFixture<AppComponent>;
+ let app: AppComponent;
+ let cookieServiceSpy: any;
+ let uiService: UiService;
beforeEach(async(() => {
- const cookieSpy = jasmine.createSpyObj('CookieService', [ 'get' ]);
+ cookieServiceSpy = jasmine.createSpyObj('CookieService', [ 'get', 'put' ]);
TestBed.configureTestingModule({
imports: [
RouterTestingModule
AppComponent
],
providers: [
- { provide: CookieService, useValue: cookieSpy },
+ { provide: CookieService, useValue: cookieServiceSpy },
UiService
]
}).compileComponents();
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
+ app = fixture.componentInstance;
+ uiService = TestBed.get(UiService);
fixture.detectChanges();
});
it('should create the app', () => {
- const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
expect(ele.src).toContain('assets/oran-logo.png');
}));
+ it('should contain navigation menu', async(() => {
+ const ele = fixture.debugElement.nativeElement.querySelector('nrcp-sidenav-list');
+ expect(ele).toBeTruthy();
+ }));
+
+ it('should contain dark mode selector', async(() => {
+ const ele = fixture.debugElement.nativeElement.querySelector('#darkModeSwitch');
+ expect(ele).toBeTruthy();
+ }));
+
it('should contain heading', async(() => {
const ele = fixture.debugElement.nativeElement.querySelector('tspan');
expect(ele.textContent.trim()).toBe('Non-RT RIC Control Panel');
expect(ele).toBeTruthy();
}));
});
+
+ describe('#darkMode', () => {
+ it('when dark mode value yes from cookie at start should set mode to dark', () => {
+ uiService.darkModeState.next(false); // Set internal state to light mode.
+ cookieServiceSpy.get.and.returnValue('yes'); // Cookie shall return dark mode used.
+
+ app.ngOnInit();
+
+ expect(uiService.getDarkMode()).toBeTruthy();
+ expect(app.darkMode).toBeTruthy();
+ });
+
+ it('should toggle dark mode when selector is clicked', () => {
+ const darkModeSelector = fixture.debugElement.nativeElement.querySelector('#darkModeSwitch');
+
+ // Toggle to light mode
+ darkModeSelector.click();
+ expect(uiService.getDarkMode()).toBeFalsy();
+ expect(cookieServiceSpy.put).toHaveBeenCalledWith('darkMode', 'no');
+
+
+ // Toggle to dark mode
+ darkModeSelector.click();
+ expect(uiService.getDarkMode()).toBeTruthy();
+ expect(cookieServiceSpy.put).toHaveBeenCalledWith('darkMode', 'yes');
+ });
+ });
});
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
- private showMenu = false;
darkMode: boolean;
private 'DARK_MODE_COOKIE' = 'darkMode';
});
}
- toggleMenu() {
- this.showMenu = !this.showMenu;
- }
-
modeToggleSwitch() {
this.ui.darkModeState.next(!this.darkMode);
this.cookieService.put(this.DARK_MODE_COOKIE, this.darkMode ? 'yes' : 'no');
}
-
}
<br>
<h4>Producers</h4>
-<form [formGroup]="formGroup" class="filter-form">
- <input
- type="text"
- class="form-control"
- name="searchString"
- placeholder="Search Producer"
- formControlName="filter"
- />
-</form>
+
<div class="table-container">
- <table [ngClass]="{'table-dark': darkMode}" matSort
- class="ei-coordinator-table mat-elevation-z8">
- <tr>
- <th>Producer ID</th>
- <th>Producer type</th>
- <th>Producer status</th>
- </tr>
- <tr *ngFor="let eiProducer of filteredProducers$ | async">
- <td class="text-left">
- {{eiProducer.ei_producer_id}}
- </td>
- <td class="text-left">
- {{this.getEIProducerTypes(eiProducer)}}
- </td>
- <td class="text-left">
- {{this.getEIProducerStatus(eiProducer)}}
- </td>
- </tr>
- </table>
+ <mat-table #producersTable [dataSource]="producersDataSource" matSort class="mat-elevation-z8">
+
+ <ng-container matColumnDef="id">
+ <mat-header-cell *matHeaderCellDef>
+ <form style="display: flex" [formGroup]="producersFormControl">
+ <mat-form-field>
+ <input matInput formControlName="ei_producer_id">
+ <mat-placeholder>Producer ID</mat-placeholder>
+ </mat-form-field>
+ </form>
+ </mat-header-cell>
+ <mat-cell *matCellDef="let eiProducer"> {{eiProducer.ei_producer_id}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="types">
+ <mat-header-cell *matHeaderCellDef>
+ <br>
+ <form style="display: flex" [formGroup]="producersFormControl">
+ <mat-form-field>
+ <input matInput formControlName="ei_producer_types">
+ <mat-placeholder>Producer types</mat-placeholder>
+ </mat-form-field>
+ </form>
+ </mat-header-cell>
+ <mat-cell *matCellDef="let eiProducer"> {{this.getEIProducerTypes(eiProducer)}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="status">
+ <mat-header-cell *matHeaderCellDef>
+ <br>
+ <form style="display: flex" [formGroup]="producersFormControl">
+ <mat-form-field>
+ <input matInput formControlName="status">
+ <mat-placeholder>Producer status</mat-placeholder>
+ </mat-form-field>
+ </form>
+ </mat-header-cell>
+ <mat-cell *matCellDef="let eiProducer"> {{this.getEIProducerStatus(eiProducer)}} </mat-cell>
+ </ng-container>
+
+ <mat-header-row *matHeaderRowDef="['id', 'types', 'status']"></mat-header-row>
+ <mat-row *matRowDef="let row; columns: ['id', 'types', 'status'];"></mat-row>
+
+ <!-- Row shown when there is no matching data. -->
+ <mat-row *matNoDataRow>
+ <mat-cell colspan="3">No data matching the filter "{{input.value}}"</mat-cell>
+ </mat-row>
+ </mat-table>
</div>
<br>
<h4>Jobs</h4>
+
<div class="table-container">
- <table mat-table EIJobTable [dataSource]="eiJobsDataSource" [ngClass]="{'table-dark': darkMode}" matSort
- multiTemplateDataRows class="ei-coordinator-table mat-elevation-z8">
+ <mat-table [dataSource]="jobsDataSource"
+ matSort class="ei-coordinator-table mat-elevation-z8">
<ng-container matColumnDef="id">
- <th mat-header-cell *matHeaderCellDef> Job ID </th>
- <td mat-cell *matCellDef="let eiJob"> {{this.getDisplayName(eiJob)}} </td>
+ <mat-header-cell *matHeaderCellDef>
+ <br>
+ <form style="display: flex" [formGroup]="jobsFormControl">
+ <mat-form-field>
+ <input matInput formControlName="id">
+ <mat-placeholder>Job ID</mat-placeholder>
+ </mat-form-field>
+ </form>
+ </mat-header-cell>
+ <mat-cell *matCellDef="let eiJob"> {{this.getDisplayName(eiJob)}} </mat-cell>
</ng-container>
<ng-container matColumnDef="typeId">
- <th mat-header-cell *matHeaderCellDef> Type ID </th>
- <td mat-cell *matCellDef="let eiJob"> {{this.getEITypeId(eiJob)}} </td>
- </ng-container>
+ <mat-header-cell *matHeaderCellDef>
+ <br>
+ <form style="display: flex" [formGroup]="jobsFormControl">
+ <mat-form-field>
+ <input matInput formControlName="typeId">
+ <mat-placeholder>Type ID</mat-placeholder>
+ </mat-form-field>
+ </form>
+ </mat-header-cell>
+ <mat-cell *matCellDef="let eiJob">{{this.getEITypeId(eiJob)}} </mat-cell>
+ </ng-container>
<ng-container matColumnDef="owner">
- <th mat-header-cell *matHeaderCellDef> Owner </th>
- <td mat-cell *matCellDef="let eiJob"> {{eiJob.owner}} </td>
+ <mat-header-cell *matHeaderCellDef>
+ <br>
+ <form style="display: flex" [formGroup]="jobsFormControl">
+ <mat-form-field>
+ <input matInput formControlName="owner">
+ <mat-placeholder>Owner</mat-placeholder>
+ </mat-form-field>
+ </form>
+ </mat-header-cell>
+ <mat-cell *matCellDef="let eiJob">{{eiJob.owner}} </mat-cell>
</ng-container>
<ng-container matColumnDef="targetUri">
- <th mat-header-cell *matHeaderCellDef> Target URI </th>
- <td mat-cell *matCellDef="let eiJob"> {{this.getTargetUri(eiJob)}} </td>
+ <mat-header-cell *matHeaderCellDef>
+ <br>
+ <form style="display: flex" [formGroup]="jobsFormControl">
+ <mat-form-field>
+ <input matInput formControlName="targetUri">
+ <mat-placeholder>Target URI</mat-placeholder>
+ </mat-form-field>
+ </form>
+ </mat-header-cell>
+ <mat-cell *matCellDef="let eiJob"> {{this.getTargetUri(eiJob)}} </mat-cell>
</ng-container>
- <tr mat-header-row *matHeaderRowDef="['id', 'typeId', 'owner', 'targetUri']"></tr>
- <tr mat-row *matRowDef="let row; columns: ['id', 'typeId', 'owner', 'targetUri'];"></tr>
- </table>
+ <mat-header-row *matHeaderRowDef="['id', 'typeId', 'owner', 'targetUri']"></mat-header-row>
+ <mat-row *matRowDef="let row; columns: ['id', 'typeId', 'owner', 'targetUri'];"></mat-row>
+ </mat-table>
</div>
\ No newline at end of file
background-color: #2d2d3d;
}
-.action-cell {
- display: flex;
- justify-content: flex-end;
-}
-
.display-none {
display: none;
}
.filter-form {
width: 200px;
-}
\ No newline at end of file
+}
+
+.mat-form-field {
+ font-size: 14px;
+ width: 100%;
+}
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { MatIconModule, MatTableModule } from '@angular/material';
-import { of } from 'rxjs';
-
import { EICoordinatorComponent } from './ei-coordinator.component';
import { EIJobDataSource } from './ei-job.datasource';
import { EIProducerDataSource } from './ei-producer.datasource';
let fixture: ComponentFixture<EICoordinatorComponent>;
beforeEach(async(() => {
- const jobDataSourceSpy = jasmine.createSpyObj('EIJobDataSource', [ 'connect', 'getJobs', 'disconnect' ]);
- const producerDataSourceSpy = jasmine.createSpyObj('EIProducerDataSource', [ 'connect', 'loadTable', 'loadProducers', 'disconnect' ]);
+ const jobDataSourceSpy = jasmine.createSpyObj('EIJobDataSource', [ 'loadJobs', 'eiJobs' ]);
+ const producerDataSourceSpy = jasmine.createSpyObj('EIProducerDataSource', [ 'loadProducers', 'eiProducers' ]);
+
+ jobDataSourceSpy.eiJobs.and.returnValue([]);
- jobDataSourceSpy.connect.and.returnValue(of([]));
- jobDataSourceSpy.disconnect();
- producerDataSourceSpy.connect.and.returnValue(of([]));
- producerDataSourceSpy.loadProducers.and.returnValue(of([]));
- producerDataSourceSpy.disconnect();
+ producerDataSourceSpy.eiProducers.and.returnValue([]);
TestBed.configureTestingModule({
imports: [
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatSort } from '@angular/material/sort';
import { animate, state, style, transition, trigger } from '@angular/animations';
-import { FormBuilder, FormGroup } from '@angular/forms';
-import { MatTableDataSource } from '@angular/material';
+import { FormBuilder, FormGroup, AbstractControl } from '@angular/forms';
+import { MatTableDataSource, MatTable } from '@angular/material';
-import { defer, BehaviorSubject, Observable } from 'rxjs';
-import { map, withLatestFrom, startWith } from 'rxjs/operators';
+import { BehaviorSubject, Observable } from 'rxjs';
import { EIJob, EIProducer } from '../interfaces/ei.types';
import { EIJobDataSource } from './ei-job.datasource';
})
export class EICoordinatorComponent implements OnInit {
- producers$: Observable<EIProducer[]>;
- filteredProducers$: Observable<EIProducer[]>;
@ViewChild(MatSort, { static: true }) sort: MatSort;
+ @ViewChild('producersTable', { static: true }) table: MatTable<Element>;
eiJobInfo = new Map<string, EIJobInfo>();
darkMode: boolean;
searchString: string;
formGroup: FormGroup;
- eiProducersData: MatTableDataSource<EIProducerDataSource>;
+ jobsDataSource: MatTableDataSource<EIJob>;
+ producersDataSource: MatTableDataSource<EIProducer>;
+
+ readonly jobsFormControl: AbstractControl;
+ readonly producersFormControl: AbstractControl;
constructor(
private eiJobsDataSource: EIJobDataSource,
private ui: UiService,
private formBuilder: FormBuilder) {
this.formGroup = formBuilder.group({ filter: [""] });
- }
+
+ this.jobsFormControl = formBuilder.group({
+ id: '',
+ typeId: '',
+ owner: '',
+ targetUri:''
+ });
+ this.producersFormControl = formBuilder.group({
+ ei_producer_id: '',
+ ei_producer_types: '',
+ status: ''
+ });
+ }
ngOnInit() {
- this.eiJobsDataSource.getJobs();
-
- this.producers$= this.eiProducersDataSource.loadProducers();
- this.filteredProducers$ = defer(() => this.formGroup.get("filter")
- .valueChanges.pipe(
- startWith(""),
- withLatestFrom(this.producers$),
- map(([val, producers]) =>
- !val ? producers : producers.filter((x) =>
- x.ei_producer_id.toLowerCase().includes(val))))
- );
+ this.eiJobsDataSource.loadJobs();
+ this.eiProducersDataSource.loadProducers();
+ this.jobsDataSource = new MatTableDataSource(this.eiJobsDataSource.eiJobs());
+ this.producersDataSource = new MatTableDataSource(this.eiProducersDataSource.eiProducers());
+
+ this.jobsFormControl.valueChanges.subscribe(value => {
+ const filter = {...value, id: value.id.trim().toLowerCase()} as string;
+ this.jobsDataSource.filter = filter;
+ });
+ this.producersFormControl.valueChanges.subscribe(value => {
+ const filter = {...value, ei_producer_id: value.ei_producer_id.trim().toLowerCase()} as string;
+ this.producersDataSource.filter = filter;
+ });
+
+ this.jobsDataSource.filterPredicate = ((data, filter) => {
+ return this.isDataIncluding(data.ei_job_identity, filter.id)
+ && this.isDataIncluding(data.target_uri, filter.target_uri)
+ && this.isDataIncluding(data.owner, filter.owner)
+ && this.isDataIncluding(data.ei_type_identity, filter.typeId);
+ }) as (EIJob, string) => boolean;
+
+ this.producersDataSource.filterPredicate = ((data, filter) => {
+ return this.isDataIncluding(data.ei_producer_id, filter.ei_producer_id)
+ && this.isDataIncluding(data.ei_producer_types.join(','), filter.ei_producer_types)
+ && this.isDataIncluding(data.status, filter.status);
+ }) as (EIProducer, string) => boolean;
this.ui.darkModeState.subscribe((isDark) => {
this.darkMode = isDark;
});
}
+ isDataIncluding(data: string, filter: string) : boolean {
+ return !filter || data.toLowerCase().includes(filter);
+ }
+
getEIJobInfo(eiJob: EIJob): EIJobInfo {
let info: EIJobInfo = this.eiJobInfo.get(eiJob.ei_job_data);
if (!info) {
}
refreshTables() {
- this.eiJobsDataSource.getJobs();
+ this.eiJobsDataSource.loadJobs();
this.eiProducersDataSource.loadProducers();
}
}
* ========================LICENSE_END===================================
*/
import { TestBed } from '@angular/core/testing';
-import { BehaviorSubject, of } from 'rxjs';
+import { of } from 'rxjs';
import { EIJobDataSource } from './ei-job.datasource';
import { EIService } from '../services/ei/ei.service';
});
it('#getJobs', () => {
- dataSource.getJobs();
- const jobsSubject: BehaviorSubject<EIJob[]> = dataSource.eiJobsSubject;
- const value = jobsSubject.getValue();
- expect(value).toEqual([ job, job ]);
+ dataSource.loadJobs();
+ const actualJobs: EIJob[] = dataSource.eiJobs();
+ expect(actualJobs).toEqual([ job, job ]);
expect(dataSource.rowCount).toEqual(2);
});
});
*/
import { Injectable } from '@angular/core';
-import { MatTableDataSource } from '@angular/material';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
providedIn: 'root'
})
-export class EIJobDataSource extends MatTableDataSource<EIJob> {
+export class EIJobDataSource {
- eiJobsSubject = new BehaviorSubject<EIJob[]>([]);
+ private jobs: Array<EIJob> = [];
+
+ public eiJobs(): EIJob[] {
+ return this.jobs;
+ }
private loadingSubject = new BehaviorSubject<boolean>(false);
constructor(
private eiSvc: EIService) {
- super();
}
- getJobs() {
+ loadJobs() {
this.loadingSubject.next(true);
+ this.jobs = [];
this.eiSvc.getProducerIds()
.subscribe((producerIds: string[]) => {
producerIds.forEach(id => {
this.getJobsForProducer(id);
});
});
+ this.rowCount = this.jobs.length;
}
private getJobsForProducer(id: string) {
console.log('Getting jobs for producer ID: ', id);
- this.eiSvc.getJobsForProducer(id).subscribe(jobs => {
- this.addJobsToSubject(jobs);
- this.rowCount = this.eiJobsSubject.getValue().length;
+ this.eiSvc.getJobsForProducer(id).subscribe(producerJobs => {
+ this.jobs = this.jobs.concat(producerJobs);
});
}
-
- private addJobsToSubject(jobs: EIJob[]) {
- const currentValue = this.eiJobsSubject.value;
- const updatedValue = [...currentValue, ...jobs];
- this.eiJobsSubject.next(updatedValue);
- }
-
- connect(): BehaviorSubject<EIJob[]> {
- return this.eiJobsSubject;
- }
-
- disconnect(): void {
- this.eiJobsSubject.complete();
- this.loadingSubject.complete();
- }
}
* ========================LICENSE_END===================================
*/
import { TestBed } from '@angular/core/testing';
-import { BehaviorSubject, of } from 'rxjs';
+import { of } from 'rxjs';
import { EIService } from '../services/ei/ei.service';
import { ToastrModule } from 'ngx-toastr';
it('#loadProducers', () => {
dataSource.loadProducers();
- const jobsSubject: BehaviorSubject<EIProducer[]> = dataSource.producerSubject;
- const value = jobsSubject.getValue();
- expect(value).toEqual([ expectedProducer1, expectedProducer2 ]);
+ const actualProducers: EIProducer[] = dataSource.eiProducers();
+ expect(actualProducers).toEqual([ expectedProducer1, expectedProducer2 ]);
expect(dataSource.rowCount).toEqual(2);
});
});
*/
import { Injectable } from '@angular/core';
-import { MatTableDataSource } from '@angular/material';
-import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
-import { of } from 'rxjs/observable/of';
import { EIProducer } from '../interfaces/ei.types';
import { EIService } from '../services/ei/ei.service';
providedIn: 'root'
})
-export class EIProducerDataSource extends MatTableDataSource<EIProducer> {
+export class EIProducerDataSource {
- producerSubject = new BehaviorSubject<EIProducer[]>([]);
+ private producers: Array<EIProducer> = [];
+
+ public eiProducers(): EIProducer[] {
+ return this.producers;
+ }
private loadingSubject = new BehaviorSubject<boolean>(false);
constructor(
private eiSvc: EIService) {
- super();
}
- loadProducers(): Observable<EIProducer[]> {
+ loadProducers() {
this.loadingSubject.next(true);
- let producers: Array<EIProducer> = [];
+ this.producers = [];
this.eiSvc.getProducerIds()
.subscribe((prodIds: string[]) => {
console.log("ProducerIds: " + prodIds);
this.eiSvc.getProducerStatus(id).subscribe(prodStatus => {
eiProducer.status = prodStatus.opState.toString();
});
- this.addProducerToSubject(eiProducer);
- producers.push(eiProducer);
+ this.producers.push(eiProducer);
});
- this.rowCount = this.producerSubject.value.length;
+ this.rowCount = this.producers.length;
});
- return of(producers);
- }
-
- private addProducerToSubject(producer: EIProducer) {
- const currentValue = this.producerSubject.value;
- const updatedValue = [...currentValue, producer];
- this.producerSubject.next(updatedValue);
- }
-
- connect(): BehaviorSubject<EIProducer[]> {
- return this.producerSubject;
- }
-
- disconnect(): void {
- this.producerSubject.complete();
- this.loadingSubject.complete();
}
-}
+}
\ No newline at end of file
{
"ei_job_identity": "job2",
"ei_type_identity": "type1",
- "owner": "owner1",
+ "owner": "owner2",
+ "ei_job_data": {
+ "jobparam2": "value2_job2",
+ "jobparam3": "value3_job2",
+ "jobparam1": "value1_job2"
+ },
+ "target_uri": "https://ricsim_g3_1:8185/datadelivery"
+ },
+ {
+ "ei_job_identity": "job4",
+ "ei_type_identity": "type1",
+ "owner": "owner3",
"ei_job_data": {
"jobparam2": "value2_job2",
"jobparam3": "value3_job2",
"jobparam1": "value1_job3"
},
"target_uri": "https://ricsim_g3_1:8185/datadelivery"
+ },
+ {
+ "ei_job_identity": "job5",
+ "ei_type_identity": "type2",
+ "owner": "owner5",
+ "ei_job_data": {
+ "jobparam2": "value2_job2",
+ "jobparam3": "value3_job2",
+ "jobparam1": "value1_job2"
+ },
+ "target_uri": "https://ricsim_g3_1:8185/datadelivery"
}
]
\ No newline at end of file
this.darkModeState = new BehaviorSubject<boolean>(true);
this.expanded = new Observable<boolean>();
}
+
+ getDarkMode(): boolean {
+ return this.darkModeState.getValue();
+ }
}