From f7c54883fabc2ffd90a32197d06b04369357219b Mon Sep 17 00:00:00 2001 From: maximesson Date: Mon, 9 Nov 2020 10:25:45 +0100 Subject: [PATCH] Frontend EI Coordinator Change-Id: Ic13740d159f9a783aa80c40b3fad65f9a0718e22 Issue-ID: NONRTRIC-294 Signed-off-by: maximesson --- webapp-frontend/package.json | 2 +- .../src/app/controlpanel-routing.module.ts | 4 +- webapp-frontend/src/app/controlpanel.module.ts | 9 +- .../ei-coordinator/ei-coordinator.component.html | 65 ++++++++++ .../ei-coordinator/ei-coordinator.component.scss | 45 +++++++ .../ei-coordinator.component.spec.ts | 44 +++++++ .../app/ei-coordinator/ei-coordinator.component.ts | 141 +++++++++++++++++++++ .../src/app/ei-coordinator/ei-job.datasource.ts | 104 +++++++++++++++ .../app/ei-coordinator/ei-producer.datasource.ts | 99 +++++++++++++++ .../src/app/ei-coordinator/ei-type.datasource.ts | 98 ++++++++++++++ webapp-frontend/src/app/interfaces/ei.jobs.ts | 47 +++++++ webapp-frontend/src/app/main/main.component.html | 1 + .../sidenav-list/sidenav-list.component.html | 3 + .../app/node-modules/node-modules.component.html | 1 + .../app/node-modules/node-modules.component.scss | 0 .../node-modules/node-modules.component.spec.ts | 25 ++++ .../src/app/node-modules/node-modules.component.ts | 15 +++ .../src/app/services/ei/ei.service.spec.ts | 31 +++++ webapp-frontend/src/app/services/ei/ei.service.ts | 80 ++++++++++++ .../src/app/ui/ei-card/ei-card.component.html | 31 +++++ .../src/app/ui/ei-card/ei-card.component.scss | 58 +++++++++ .../src/app/ui/ei-card/ei-card.component.spec.ts | 44 +++++++ .../src/app/ui/ei-card/ei-card.component.ts | 46 +++++++ 23 files changed, 990 insertions(+), 3 deletions(-) create mode 100644 webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.html create mode 100644 webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.scss create mode 100644 webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts create mode 100644 webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts create mode 100644 webapp-frontend/src/app/ei-coordinator/ei-job.datasource.ts create mode 100644 webapp-frontend/src/app/ei-coordinator/ei-producer.datasource.ts create mode 100644 webapp-frontend/src/app/ei-coordinator/ei-type.datasource.ts create mode 100644 webapp-frontend/src/app/interfaces/ei.jobs.ts create mode 100644 webapp-frontend/src/app/node-modules/node-modules.component.html create mode 100644 webapp-frontend/src/app/node-modules/node-modules.component.scss create mode 100644 webapp-frontend/src/app/node-modules/node-modules.component.spec.ts create mode 100644 webapp-frontend/src/app/node-modules/node-modules.component.ts create mode 100644 webapp-frontend/src/app/services/ei/ei.service.spec.ts create mode 100644 webapp-frontend/src/app/services/ei/ei.service.ts create mode 100644 webapp-frontend/src/app/ui/ei-card/ei-card.component.html create mode 100644 webapp-frontend/src/app/ui/ei-card/ei-card.component.scss create mode 100644 webapp-frontend/src/app/ui/ei-card/ei-card.component.spec.ts create mode 100644 webapp-frontend/src/app/ui/ei-card/ei-card.component.ts diff --git a/webapp-frontend/package.json b/webapp-frontend/package.json index dd68902..b7823a1 100644 --- a/webapp-frontend/package.json +++ b/webapp-frontend/package.json @@ -23,7 +23,7 @@ "@angular/platform-browser-dynamic": "^8.2.14", "@angular/router": "^8.2.14", "@fortawesome/fontawesome-free": "^5.12.0", - "@kubernetes/client-node": "^0.10.3", + "@kubernetes/client-node": "^0.12.3", "@material/radio": "^2.3.0", "@types/chart.js": "^2.9.11", "@types/uuid": "^7.0.3", diff --git a/webapp-frontend/src/app/controlpanel-routing.module.ts b/webapp-frontend/src/app/controlpanel-routing.module.ts index 23665bc..366ef9f 100644 --- a/webapp-frontend/src/app/controlpanel-routing.module.ts +++ b/webapp-frontend/src/app/controlpanel-routing.module.ts @@ -23,11 +23,13 @@ import { CommonModule } from '@angular/common'; import { Routes, RouterModule } from '@angular/router'; import { MainComponent } from './main/main.component'; import { PolicyControlComponent } from './policy-control/policy-control.component'; +import { EICoordinatorComponent } from './ei-coordinator/ei-coordinator.component'; const routes: Routes = [ { path: '', component: MainComponent }, - { path: 'policy', component: PolicyControlComponent } + { path: 'policy', component: PolicyControlComponent }, + { path: 'ei-coordinator', component: EICoordinatorComponent } ]; @NgModule({ diff --git a/webapp-frontend/src/app/controlpanel.module.ts b/webapp-frontend/src/app/controlpanel.module.ts index 4abd69f..b20e871 100644 --- a/webapp-frontend/src/app/controlpanel.module.ts +++ b/webapp-frontend/src/app/controlpanel.module.ts @@ -56,10 +56,16 @@ import { ControlpanelRoutingModule } from './controlpanel-routing.module'; import { SidenavListComponent } from './navigation/sidenav-list/sidenav-list.component'; import { UiService } from './services/ui/ui.service'; import { CookieModule } from 'ngx-cookie'; +import { NodeModulesComponent } from './node-modules/node-modules.component'; +import { EICardComponent } from './ui/ei-card/ei-card.component'; +import { EICoordinatorComponent } from './ei-coordinator/ei-coordinator.component'; + @NgModule({ declarations: [ ConfirmDialogComponent, + EICardComponent, + EICoordinatorComponent, ErrorDialogComponent, FooterComponent, MainComponent, @@ -69,7 +75,8 @@ import { CookieModule } from 'ngx-cookie'; PolicyInstanceComponent, PolicyInstanceDialogComponent, ControlpanelComponent, - SidenavListComponent + SidenavListComponent, + NodeModulesComponent ], imports: [ BrowserModule, diff --git a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.html b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.html new file mode 100644 index 0000000..aee65c8 --- /dev/null +++ b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.html @@ -0,0 +1,65 @@ + + +

Enrichment Information Coordinator

+ +
+

Jobs

+ + + + + + + + + + + + + + + + +
Job ID {{this.getDisplayName(eiJob)}} Type ID {{this.getEITypeId(eiJob)}} Target URI {{this.getTargetUri(eiJob)}}
+ +
+

Producers

+ + + + + + + + + + + + + + + +
Producer ID {{this.getEIProducerId(eiProducer)}} Producer type {{this.getEIProducerTypes(eiProducer)}} Producer status {{this.getEIProducerStatus(eiProducer)}}
\ No newline at end of file diff --git a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.scss b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.scss new file mode 100644 index 0000000..8e873ec --- /dev/null +++ b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.scss @@ -0,0 +1,45 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 Nordix Foundation + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ + +.spinner-container { + height: 100px; + width: 100px; +} + +.spinner-container mat-spinner { + margin: 0 auto 0 auto; +} + +.ei-coordinator-table { + width: 100%; + min-height: 150px; + margin-top: 10px; + margin-bottom: 10px; + background-color: transparent; +} + +.action-cell { + display: flex; + justify-content: flex-end; +} + +.display-none { + display: none; +} \ No newline at end of file diff --git a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts new file mode 100644 index 0000000..36b2cc8 --- /dev/null +++ b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.spec.ts @@ -0,0 +1,44 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 Nordix Foundation + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EICoordinatorComponent } from './ei-coordinator.component'; + +describe('EICoordinatorComponent', () => { + let component: EICoordinatorComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ EICoordinatorComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EICoordinatorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts new file mode 100644 index 0000000..bcff218 --- /dev/null +++ b/webapp-frontend/src/app/ei-coordinator/ei-coordinator.component.ts @@ -0,0 +1,141 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 Nordix Foundation + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { MatSort } from '@angular/material/sort'; +import { animate, state, style, transition, trigger } from '@angular/animations'; + +import { EIService } from '../services/ei/ei.service'; +import { EIJob, EIProducer } from '../interfaces/ei.jobs'; +import { EIProducerDataSource } from './ei-producer.datasource'; +import { EIJobDataSource } from './ei-job.datasource'; +import { NotificationService } from '../services/ui/notification.service'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { UiService } from '../services/ui/ui.service'; + +class EIJobInfo { + constructor(public eiJob: EIJob) { } + + isExpanded: BehaviorSubject = new BehaviorSubject(false); +} + +@Component({ + selector: 'rd-ei-coordinator', + templateUrl: './ei-coordinator.component.html', + styleUrls: ['./ei-coordinator.component.scss'], + animations: [ + trigger('detailExpand', [ + state('collapsed, void', style({ height: '0px', minHeight: '0', display: 'none' })), + state('expanded', style({ height: '*' })), + transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')), + transition('expanded <=> void', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')) + ]), + ], +}) +export class EICoordinatorComponent implements OnInit { + + + eiJobsDataSource: EIJobDataSource; + eiProducersDataSource: EIProducerDataSource; + @ViewChild(MatSort, { static: true }) sort: MatSort; + + eiJobInfo = new Map(); + darkMode: boolean; + + constructor( + private eiSvc: EIService, + private dialog: MatDialog, + private notificationService: NotificationService, + private ui: UiService) { } + + ngOnInit() { + this.eiJobsDataSource = new EIJobDataSource(this.eiSvc, this.sort, this.notificationService); + this.eiProducersDataSource = new EIProducerDataSource(this.eiSvc, this.sort, this.notificationService); + this.eiJobsDataSource.loadTable(); + this.eiProducersDataSource.loadTable(); + this.ui.darkModeState.subscribe((isDark) => { + this.darkMode = isDark; + }); + } + + toggleListInstances(eiJob: EIJob): void { + const info = this.getEIJobInfo(eiJob); + info.isExpanded.next(!info.isExpanded.getValue()); + } + + getEIJobInfo(eiJob: EIJob): EIJobInfo { + let info: EIJobInfo = this.eiJobInfo.get(eiJob.ei_job_data); + if (!info) { + info = new EIJobInfo(eiJob); + this.eiJobInfo.set(eiJob.ei_job_data, info); + } + return info; + } + + getDisplayName(eiJob: EIJob): string { + if (eiJob.ei_job_identity) { + return eiJob.ei_job_identity; + } + return '< No id >'; + } + + getEITypeId(eiJob: EIJob): string { + if (eiJob.ei_type_identity){ + return eiJob.ei_type_identity; + } + return '< No type >'; + } + + getTargetUri(eiJob: EIJob): string { + if (eiJob.target_uri){ + return eiJob.target_uri; + } + return '< No target URI >'; + } + + isInstancesShown(eiJob: EIJob): boolean { + return this.getEIJobInfo(eiJob).isExpanded.getValue(); + } + + getExpandedObserver(eiJob: EIJob): Observable { + return this.getEIJobInfo(eiJob).isExpanded.asObservable(); + } + + getEIProducerId(eiProducer: EIProducer): string { + if (eiProducer.ei_producer_id){ + return eiProducer.ei_producer_id; + } + return '< No id>'; + } + + getEIProducerTypes(eiProducer: EIProducer): string[] { + if (eiProducer.ei_producer_types){ + return eiProducer.ei_producer_types; + } + return ['< No types >']; + } + + getEIProducerStatus(eiProducer: EIProducer): string { + if (eiProducer.status){ + return eiProducer.status; + } + return '< No status >'; + } +} diff --git a/webapp-frontend/src/app/ei-coordinator/ei-job.datasource.ts b/webapp-frontend/src/app/ei-coordinator/ei-job.datasource.ts new file mode 100644 index 0000000..a8a786f --- /dev/null +++ b/webapp-frontend/src/app/ei-coordinator/ei-job.datasource.ts @@ -0,0 +1,104 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 Nordix Foundation + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ + +import { DataSource } from '@angular/cdk/collections'; +import { HttpErrorResponse } from '@angular/common/http'; +import { MatSort } from '@angular/material'; +import { Observable } from 'rxjs/Observable'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { merge } from 'rxjs'; +import { of } from 'rxjs/observable/of'; +import { catchError, finalize, map } from 'rxjs/operators'; +import { EIJob } from '../interfaces/ei.jobs'; +import { EIService } from '../services/ei/ei.service'; +import { NotificationService } from '../services/ui/notification.service'; + +export class EIJobDataSource extends DataSource { + + private eiJobSubject = new BehaviorSubject([]); + + private loadingSubject = new BehaviorSubject(false); + + public loading$ = this.loadingSubject.asObservable(); + + public rowCount = 1; // hide footer during intial load + + constructor( + private eiSvc: EIService, + public sort: MatSort, + private notificationService: NotificationService) { + super(); + } + + loadTable() { + this.loadingSubject.next(true); + this.eiSvc.getEIJobs() + .pipe( + catchError((her: HttpErrorResponse) => { + this.notificationService.error('Failed to get EI jobs: ' + her.error); + return of([]); + }), + finalize(() => this.loadingSubject.next(false)) + ) + .subscribe((instances: EIJob[]) => { + console.log("Jobs: " + instances); + this.rowCount = instances.length; + this.eiJobSubject.next(instances); + }); + } + + connect(): Observable { + const dataMutations = [ + this.eiJobSubject.asObservable(), + this.sort.sortChange + ]; + return merge(...dataMutations).pipe(map(() => { + return this.getSortedData([...this.eiJobSubject.getValue()]); + })); + } + + disconnect(): void { + this.eiJobSubject.complete(); + this.loadingSubject.complete(); + } + + private getSortedData(data: EIJob[]) { + if (!this.sort || !this.sort.active || this.sort.direction === '') { + return data; + } + + return data.sort((a, b) => { + const isAsc = this.sort.direction === 'asc'; + switch (this.sort.active) { + case 'id': return compare(a.ei_job_identity, b.ei_job_identity, isAsc); + //case 'eiTypeId': return compare(a.eiTypeId, b.eiTypeId, isAsc); + //case 'jobResultUri': return compare(a.jobResultUri, b.jobResultUri, isAsc); + //case 'jobOwner': return compare(a.jobOwner, b.jobOwner, isAsc); + //case 'jobStatusNotificationUri': return compare(a.jobStatusNotificationUri, b.jobStatusNotificationUri, isAsc); + //case 'jobDefinition': return compare(a.jobDefinition, b.jobDefinition, isAsc); + default: return 0; + } + }); + } +} + +function compare(a: string, b: string, isAsc: boolean) { + return (a < b ? -1 : 1) * (isAsc ? 1 : -1); +} diff --git a/webapp-frontend/src/app/ei-coordinator/ei-producer.datasource.ts b/webapp-frontend/src/app/ei-coordinator/ei-producer.datasource.ts new file mode 100644 index 0000000..866d785 --- /dev/null +++ b/webapp-frontend/src/app/ei-coordinator/ei-producer.datasource.ts @@ -0,0 +1,99 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 Nordix Foundation + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ + +import { DataSource } from '@angular/cdk/collections'; +import { HttpErrorResponse } from '@angular/common/http'; +import { MatSort } from '@angular/material'; +import { Observable } from 'rxjs/Observable'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { merge } from 'rxjs'; +import { of } from 'rxjs/observable/of'; +import { catchError, finalize, map } from 'rxjs/operators'; +import { EIProducer } from '../interfaces/ei.jobs'; +import { EIService } from '../services/ei/ei.service'; +import { NotificationService } from '../services/ui/notification.service'; + +export class EIProducerDataSource extends DataSource { + + private producerSubject = new BehaviorSubject([]); + + private loadingSubject = new BehaviorSubject(false); + + public loading$ = this.loadingSubject.asObservable(); + + public rowCount = 1; // hide footer during intial load + + constructor( + private eiSvc: EIService, + public sort: MatSort, + private notificationService: NotificationService) { + super(); + } + + loadTable() { + this.loadingSubject.next(true); + this.eiSvc.getEIProducers() + .pipe( + catchError((her: HttpErrorResponse) => { + this.notificationService.error('Failed to get producers: ' + her.error); + return of([]); + }), + finalize(() => this.loadingSubject.next(false)) + ) + .subscribe((prods: EIProducer[]) => { + console.log("Producers: " + prods); + this.rowCount = prods.length; + this.producerSubject.next(prods); + }); + } + + connect(): Observable { + const dataMutations = [ + this.producerSubject.asObservable(), + this.sort.sortChange + ]; + return merge(...dataMutations).pipe(map(() => { + return this.getSortedData([...this.producerSubject.getValue()]); + })); + } + + disconnect(): void { + this.producerSubject.complete(); + this.loadingSubject.complete(); + } + + private getSortedData(data: EIProducer[]) { + if (!this.sort || !this.sort.active || this.sort.direction === '') { + return data; + } + + return data.sort((a, b) => { + const isAsc = this.sort.direction === 'asc'; + switch (this.sort.active) { + case 'ei_producer_id': return compare(a.ei_producer_id, b.ei_producer_id, isAsc); + default: return 0; + } + }); + } +} + +function compare(a: string, b: string, isAsc: boolean) { + return (a < b ? -1 : 1) * (isAsc ? 1 : -1); +} diff --git a/webapp-frontend/src/app/ei-coordinator/ei-type.datasource.ts b/webapp-frontend/src/app/ei-coordinator/ei-type.datasource.ts new file mode 100644 index 0000000..8d48196 --- /dev/null +++ b/webapp-frontend/src/app/ei-coordinator/ei-type.datasource.ts @@ -0,0 +1,98 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 Nordix Foundation + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ + +import { CollectionViewer, DataSource } from '@angular/cdk/collections'; +import { HttpErrorResponse } from '@angular/common/http'; +import { MatSort } from '@angular/material'; +import { Observable } from 'rxjs/Observable'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { merge } from 'rxjs'; +import { of } from 'rxjs/observable/of'; +import { catchError, finalize, map } from 'rxjs/operators'; +import { EIService } from '../services/ei/ei.service'; +import { EIType } from '../interfaces/ei.jobs'; +import { NotificationService } from '../services/ui/notification.service'; + +export class EITypeDataSource extends DataSource { + + private eiTypeSubject = new BehaviorSubject([]); + + private loadingSubject = new BehaviorSubject(false); + + public loading$ = this.loadingSubject.asObservable(); + + public rowCount = 1; // hide footer during intial load + + constructor(private eiSvc: EIService, + private sort: MatSort, + private notificationService: NotificationService) { + super(); + } + + loadTable() { + this.loadingSubject.next(true); + this.eiSvc.getEITypes() + .pipe( + catchError((her: HttpErrorResponse) => { + this.notificationService.error('Failed to get EI types: ' + her.statusText + ', ' + her.error); + return of([]); + }), + finalize(() => this.loadingSubject.next(false)) + ) + .subscribe((types: EIType[]) => { + console.log("Types: " + types); + this.rowCount = types.length; + this.eiTypeSubject.next(types); + }); + } + + connect(collectionViewer: CollectionViewer): Observable { + const dataMutations = [ + this.eiTypeSubject.asObservable(), + this.sort.sortChange + ]; + return merge(...dataMutations).pipe(map(() => { + return this.getSortedData([...this.eiTypeSubject.getValue()]); + })); + } + + disconnect(collectionViewer: CollectionViewer): void { + this.eiTypeSubject.complete(); + this.loadingSubject.complete(); + } + + private getSortedData(data: EIType[]) { + if (!this.sort.active || this.sort.direction === '') { + return data; + } + + return data.sort((a, b) => { + const isAsc = this.sort.direction === 'asc'; + switch (this.sort.active) { + case 'eiTypeId': return compare(a.description, b.description, isAsc); + default: return 0; + } + }); + } +} + +function compare(a: any, b: any, isAsc: boolean) { + return (a < b ? -1 : 1) * (isAsc ? 1 : -1); +} diff --git a/webapp-frontend/src/app/interfaces/ei.jobs.ts b/webapp-frontend/src/app/interfaces/ei.jobs.ts new file mode 100644 index 0000000..e7d034a --- /dev/null +++ b/webapp-frontend/src/app/interfaces/ei.jobs.ts @@ -0,0 +1,47 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2020 Nordix Foundation + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ + +// Models of data used by the EI Coordinator + +export interface EIJobBis { + eiTypeId: string; + jobResultUri: string; + jobOwner: string; + jobStatusNotificationUri: string; + jobDefinition: any; + } + + export interface EIJob { + ei_job_identity: string; + ei_job_data: any; + ei_type_identity: string; + target_uri: string; + } + + export interface EIType { + id: string; + description: string; + } + + export interface EIProducer { + ei_producer_id: string; + ei_producer_types: string[]; + status: string; + } \ No newline at end of file diff --git a/webapp-frontend/src/app/main/main.component.html b/webapp-frontend/src/app/main/main.component.html index f05fd50..4eedb08 100644 --- a/webapp-frontend/src/app/main/main.component.html +++ b/webapp-frontend/src/app/main/main.component.html @@ -20,4 +20,5 @@ -->
+
diff --git a/webapp-frontend/src/app/navigation/sidenav-list/sidenav-list.component.html b/webapp-frontend/src/app/navigation/sidenav-list/sidenav-list.component.html index 9dea90a..d110619 100644 --- a/webapp-frontend/src/app/navigation/sidenav-list/sidenav-list.component.html +++ b/webapp-frontend/src/app/navigation/sidenav-list/sidenav-list.component.html @@ -27,4 +27,7 @@ assignment Policy + + assignment Enrichment Information Coordinator + diff --git a/webapp-frontend/src/app/node-modules/node-modules.component.html b/webapp-frontend/src/app/node-modules/node-modules.component.html new file mode 100644 index 0000000..a1950bc --- /dev/null +++ b/webapp-frontend/src/app/node-modules/node-modules.component.html @@ -0,0 +1 @@ +

node-modules works!

diff --git a/webapp-frontend/src/app/node-modules/node-modules.component.scss b/webapp-frontend/src/app/node-modules/node-modules.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/webapp-frontend/src/app/node-modules/node-modules.component.spec.ts b/webapp-frontend/src/app/node-modules/node-modules.component.spec.ts new file mode 100644 index 0000000..ea620d9 --- /dev/null +++ b/webapp-frontend/src/app/node-modules/node-modules.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NodeModulesComponent } from './node-modules.component'; + +describe('NodeModulesComponent', () => { + let component: NodeModulesComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ NodeModulesComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NodeModulesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/webapp-frontend/src/app/node-modules/node-modules.component.ts b/webapp-frontend/src/app/node-modules/node-modules.component.ts new file mode 100644 index 0000000..6d650a1 --- /dev/null +++ b/webapp-frontend/src/app/node-modules/node-modules.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'rd-node-modules', + templateUrl: './node-modules.component.html', + styleUrls: ['./node-modules.component.scss'] +}) +export class NodeModulesComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/webapp-frontend/src/app/services/ei/ei.service.spec.ts b/webapp-frontend/src/app/services/ei/ei.service.spec.ts new file mode 100644 index 0000000..6814d8a --- /dev/null +++ b/webapp-frontend/src/app/services/ei/ei.service.spec.ts @@ -0,0 +1,31 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 Nordix Foundation + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ +import { TestBed } from '@angular/core/testing'; + +import { EIService } from './ei.service'; + +describe('PolicyService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: EIService = TestBed.get(EIService); + expect(service).toBeTruthy(); + }); +}); diff --git a/webapp-frontend/src/app/services/ei/ei.service.ts b/webapp-frontend/src/app/services/ei/ei.service.ts new file mode 100644 index 0000000..cbc0a55 --- /dev/null +++ b/webapp-frontend/src/app/services/ei/ei.service.ts @@ -0,0 +1,80 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 Nordix Foundation + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ + +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { EIJob, EIType, EIProducer } from '../../interfaces/ei.jobs'; +import { ControlpanelSuccessTransport } from '../../interfaces/controlpanel.types'; + +/** + * Services for calling the EI endpoints. + */ +@Injectable({ + providedIn: 'root' +}) +export class EIService { + + private basePath = 'api/enrichment'; + private eiTypePath = 'eitypes'; + private eiJobPath = 'eijobs'; + private eiProducerPath = 'eiproducers'; + + private buildPath(...args: any[]) { + let result = this.basePath; + args.forEach(part => { + result = result + '/' + part; + }); + console.log("URL: "+result); + return result; + } + + constructor(private httpClient: HttpClient) { + // injects to variable httpClient + } + + /** + * Gets version details + * @returns Observable that should yield a String + */ + getVersion(): Observable { + const url = this.buildPath('version'); + return this.httpClient.get(url).pipe( + // Extract the string here + map(res => res['data']) + ); + } + + getEITypes(): Observable { + const url = this.buildPath(this.eiTypePath); + return this.httpClient.get(url); + } + + getEIJobs(): Observable { + const url = this.buildPath(this.eiJobPath); + return this.httpClient.get(url); + } + + getEIProducers(): Observable { + const url = this.buildPath(this.eiProducerPath); + return this.httpClient.get(url); + } +} diff --git a/webapp-frontend/src/app/ui/ei-card/ei-card.component.html b/webapp-frontend/src/app/ui/ei-card/ei-card.component.html new file mode 100644 index 0000000..fff0670 --- /dev/null +++ b/webapp-frontend/src/app/ui/ei-card/ei-card.component.html @@ -0,0 +1,31 @@ + +
+
+ Enrichment Information Coordinator


+
+ buildConfig
+ + +
+
+ +
+
diff --git a/webapp-frontend/src/app/ui/ei-card/ei-card.component.scss b/webapp-frontend/src/app/ui/ei-card/ei-card.component.scss new file mode 100644 index 0000000..8f91696 --- /dev/null +++ b/webapp-frontend/src/app/ui/ei-card/ei-card.component.scss @@ -0,0 +1,58 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 Nordix Foundation + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ +.ei__card { + background-color: #ffffff; + box-shadow: 0 0 2rem rgba(0, 0, 255, 0.1); + display: grid; + grid-template-columns: 1fr; + grid-template-rows: 1fr 1fr; + padding: 2rem; + margin: 2rem; + width: 19rem; + height: 30rem; + justify-items: center; + cursor: pointer; + border-radius: 1.75rem; + animation: fadein 1.25s ease-in-out 0ms 1; + color: #443282; +} + +.add__card-dark { + background: linear-gradient(to bottom, rgb(78, 78, 129), rgb(45, 44, 61)); + color: white; +} + +.card__title { + text-transform: uppercase; + letter-spacing: 0.1rem; +} + +.body__container { + align-self: end; + display: flex; + justify-content: space-between; + align-items: center; + flex-flow: column; +} + +.add__icon { + width: 10rem; + margin-bottom: 1.15rem; +} \ No newline at end of file diff --git a/webapp-frontend/src/app/ui/ei-card/ei-card.component.spec.ts b/webapp-frontend/src/app/ui/ei-card/ei-card.component.spec.ts new file mode 100644 index 0000000..c438461 --- /dev/null +++ b/webapp-frontend/src/app/ui/ei-card/ei-card.component.spec.ts @@ -0,0 +1,44 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 Nordix Foundation + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {EICardComponent} from './ei-card.component'; + +describe('EICardComponent', () => { + let component: EICardComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [EICardComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EICardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/webapp-frontend/src/app/ui/ei-card/ei-card.component.ts b/webapp-frontend/src/app/ui/ei-card/ei-card.component.ts new file mode 100644 index 0000000..6a7f83f --- /dev/null +++ b/webapp-frontend/src/app/ui/ei-card/ei-card.component.ts @@ -0,0 +1,46 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 Nordix Foundation + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { UiService } from '../../services/ui/ui.service'; + +@Component({ + selector: 'rd-ei-card', + templateUrl: './ei-card.component.html', + styleUrls: ['./ei-card.component.scss'] +}) +export class EICardComponent implements OnInit, OnDestroy { + darkMode: boolean; + + constructor(public router: Router, private ui: UiService) { } + + ngOnInit() { + this.ui.darkModeState.subscribe((isDark) => { + this.darkMode = isDark; + }); + } + + ngOnDestroy() { } + + openDetails() { + this.router.navigateByUrl('../../ei-coordinator'); + } + +} -- 2.16.6