X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;f=webapp-frontend%2Fsrc%2Fapp%2Fei-coordinator%2Fjobs-list%2Fjobs-list.component.ts;h=ca393307ae83ac892cfc8baa3229ae61a7d0c13f;hb=HEAD;hp=52081120a47818810aaf817f93e169784b4e4b8f;hpb=869354bad80ea5db92893ad3017eaf9c421c0731;p=portal%2Fnonrtric-controlpanel.git diff --git a/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.ts b/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.ts index 5208112..ca39330 100644 --- a/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.ts +++ b/webapp-frontend/src/app/ei-coordinator/jobs-list/jobs-list.component.ts @@ -17,73 +17,190 @@ * limitations under the License. * ========================LICENSE_END=================================== */ -import { Component, OnInit } from '@angular/core'; -import { FormControl, FormGroup } from '@angular/forms'; -import { Sort } from '@angular/material/sort'; -import { MatTableDataSource } from '@angular/material/table'; -import { EIJob } from 'src/app/interfaces/ei.types'; -import { UiService } from 'src/app/services/ui/ui.service'; -import { EIJobDataSource } from '../ei-job.datasource'; +import { Component, OnInit, ViewChild } from "@angular/core"; +import { FormControl, FormGroup } from "@angular/forms"; +import { MatPaginator } from "@angular/material/paginator"; +import { Sort } from "@angular/material/sort"; +import { MatTableDataSource } from "@angular/material/table"; +import { EMPTY, forkJoin, of, Subscription, concat } from "rxjs"; +import { BehaviorSubject } from "rxjs/BehaviorSubject"; +import { mergeMap, finalize, map, tap, concatMap, delay, skip, catchError } from "rxjs/operators"; +import { ConsumerService } from "@services/ei/consumer.service"; +import { UiService } from "@services/ui/ui.service"; +import { OperationalState } from '@app/interfaces/consumer.types'; + +export interface Job { + jobId: string; + typeId: string; + targetUri: string; + owner: string; + prodIds: string[]; + status: OperationalState; +} @Component({ - selector: 'nrcp-jobs-list', - templateUrl: './jobs-list.component.html', - styleUrls: ['./jobs-list.component.scss'] + selector: "nrcp-jobs-list", + templateUrl: "./jobs-list.component.html", + styleUrls: ["./jobs-list.component.scss"], }) export class JobsListComponent implements OnInit { + @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; + jobsDataSource: MatTableDataSource; + jobForm: FormGroup; darkMode: boolean; - jobsDataSource: MatTableDataSource = new MatTableDataSource(); - jobForm: FormGroup; + private jobsSubject$ = new BehaviorSubject([]); + private refresh$ = new BehaviorSubject(""); + private loadingSubject$ = new BehaviorSubject(false); + private polling$ = new BehaviorSubject(0); + public loading$ = this.loadingSubject$.asObservable(); + subscription: Subscription; + checked: boolean = false; + firstTime: boolean = true; + jobList: Job[] = []; - constructor( - private eiJobsDataSource: EIJobDataSource, - private ui: UiService - ) { + constructor(private consumerService: ConsumerService, private ui: UiService) { this.jobForm = new FormGroup({ - id: new FormControl(''), - typeId: new FormControl(''), - owner: new FormControl(''), - targetUri: new FormControl('') + jobId: new FormControl(""), + typeId: new FormControl(""), + owner: new FormControl(""), + targetUri: new FormControl(""), + prodIds: new FormControl(""), + status: new FormControl("") }); - } ngOnInit(): void { - this.refresh(); + this.subscription = this.dataSubscription(); - this.jobForm.valueChanges.subscribe(value => { - const filter = { ...value, id: value.id.trim().toLowerCase() } as string; - this.jobsDataSource.filter = filter; + this.jobsSubject$.subscribe((data) => { + this.jobsDataSource = new MatTableDataSource(data); + this.jobsDataSource.paginator = this.paginator; + + this.jobsDataSource.filterPredicate = ((data: Job, filter) => { + let searchTerms = JSON.parse(filter); + return ( + this.isDataIncluding(data.targetUri, searchTerms.targetUri) && + this.isDataIncluding(data.jobId, searchTerms.jobId) && + this.isDataIncluding(data.owner, searchTerms.owner) && + this.isDataIncluding(data.typeId, searchTerms.typeId) && + this.isArrayIncluding(data.prodIds, searchTerms.prodIds) && + this.isDataIncluding(data.status, searchTerms.status) + ); + }) as (data: Job, filter: any) => boolean; }); - this.jobsDataSource.filterPredicate = ((data: EIJob, filter) => { - return this.isDataIncluding(data.ei_job_identity, filter.id) - && this.isDataIncluding(data.target_uri, filter.targetUri) - && this.isDataIncluding(data.owner, filter.owner) - && this.isDataIncluding(data.ei_type_identity, filter.typeId); - }) as (data: EIJob, filter: any) => boolean; + this.jobForm.valueChanges.subscribe((value) => { + this.jobsDataSource.filter = JSON.stringify(value); + }); this.ui.darkModeState.subscribe((isDark) => { this.darkMode = isDark; }); } + dataSubscription(): Subscription { + const jobsInfo$ = this.consumerService.getJobIds().pipe( + catchError(_ => { return EMPTY }), + tap((_) => { + this.jobList = [] as Job[]; + }), + mergeMap((jobIds) => + forkJoin(jobIds.map((jobId) => { + return forkJoin([ + of(jobId).pipe( + catchError(err => { + return of([-1]); + }) + ), + this.consumerService.getJobInfo(jobId).pipe( + catchError(err => { + return of([-1]); + })), + this.consumerService.getConsumerStatus(jobId).pipe( + catchError(err => { + return of([-1]); + })), + ]) + })) + ), + finalize(() => { + this.loadingSubject$.next(false); + this.jobsSubject$.next(this.jobList); + }) + ); + + const whenToRefresh$ = of('').pipe( + delay(10000), + tap((_) => this.refresh$.next('')), + skip(1), + ); + + const poll$ = concat(jobsInfo$, whenToRefresh$); + + const refreshedJobs$ = this.refresh$.pipe( + tap((_) => { + this.loadingSubject$.next(true); + }), + concatMap((_) => this.checked ? poll$ : jobsInfo$), + map((response) => this.extractJobs(response)) + ); + + return this.polling$ + .pipe( + concatMap((value) => { + let pollCondition = value == 0 || this.checked; + return pollCondition ? refreshedJobs$ : EMPTY; + }) + ) + .subscribe(); + } + + ngOnDestroy() { + this.subscription.unsubscribe(); + } + + clearFilter() { + this.jobForm.get("jobId").setValue(""); + this.jobForm.get("typeId").setValue(""); + this.jobForm.get("owner").setValue(""); + this.jobForm.get("targetUri").setValue(""); + this.jobForm.get("prodIds").setValue(""); + this.jobForm.get("status").setValue(""); + } + sortJobs(sort: Sort) { - const data = this.jobsDataSource.data - data.sort((a: EIJob, b: EIJob) => { - const isAsc = sort.direction === 'asc'; + const data = this.jobsDataSource.data; + data.sort((a: Job, b: Job) => { + const isAsc = sort.direction === "asc"; switch (sort.active) { - case 'id': return this.compare(a.ei_job_identity, b.ei_job_identity, isAsc); - case 'typeId': return this.compare(a.ei_type_identity, b.ei_type_identity, isAsc); - case 'owner': return this.compare(a.owner, b.owner, isAsc); - case 'targetUri': return this.compare(a.target_uri, b.owner, isAsc); - default: return 0; + case "jobId": + return this.compare(a.jobId, b.jobId, isAsc); + case "typeId": + return this.compare(a.typeId, b.typeId, isAsc); + case "owner": + return this.compare(a.owner, b.owner, isAsc); + case "targetUri": + return this.compare(a.targetUri, b.targetUri, isAsc); + case "prodIds": + return this.compare(a.prodIds, b.prodIds, isAsc); + case "status": + return this.compare(a.status, b.status, isAsc); + default: + return 0; } }); this.jobsDataSource.data = data; } + stopPolling(checked) { + this.checked = checked; + this.polling$.next(this.jobs().length); + if (this.checked) { + this.refreshDataClick(); + } + } + compare(a: any, b: any, isAsc: boolean) { return (a < b ? -1 : 1) * (isAsc ? 1 : -1); } @@ -93,29 +210,83 @@ export class JobsListComponent implements OnInit { } isDataIncluding(data: string, filter: string): boolean { - return !filter || data.toLowerCase().includes(filter); + const transformedFilter = filter.trim().toLowerCase(); + return data.toLowerCase().includes(transformedFilter); + } + + isArrayIncluding(data: string[], filter: string): boolean { + if (!data) + return true; + for (let i = 0; i < data.length; i++) { + return this.isDataIncluding(data[i], filter); + } } - getJobTypeId(eiJob: EIJob): string { - if (eiJob.ei_type_identity) { - return eiJob.ei_type_identity; + getJobTypeId(job: Job): string { + if (job.typeId) { + return job.typeId; } - return '< No type >'; + return "< No type >"; } - getJobOwner(eiJob: EIJob): string { - if (eiJob.owner) { - return eiJob.owner; + getJobOwner(job: Job): string { + if (job.owner) { + return job.owner; } - return '< No owner >'; + return "< No owner >"; } - refresh() { - this.eiJobsDataSource.loadJobs(); + public jobs(): Job[] { + return this.jobsSubject$.value; + } - this.eiJobsDataSource.eiJobsSubject().subscribe((data) => { - this.jobsDataSource.data = data; + private extractJobs(res: any) { + this.clearFilter(); + res.forEach(element => { + if (element[0] != -1) { + if (element[1] != -1 && element[2] != -1) { + let jobObj = {}; + jobObj.jobId = element[0]; + jobObj.owner = element[1].job_owner; + jobObj.targetUri = element[1].job_result_uri; + jobObj.typeId = element[1].info_type_id; + jobObj.prodIds = (element[2].producers) ? element[2].producers : ["No Producers"]; + jobObj.status = element[2].info_job_status; + this.jobList = this.jobList.concat(jobObj); + } else { + let jobObj = {}; + jobObj.jobId = element[0]; + if (element[1] == -1) { + jobObj.owner = "--Missing information--"; + jobObj.targetUri = "--Missing information--"; + jobObj.typeId = "--Missing information--"; + } + if (element[2] == -1) { + jobObj.prodIds = "--Missing information--" as unknown as []; + jobObj.status = "--Missing information--" as OperationalState; + } + this.jobList = this.jobList.concat(jobObj); + } + } }); + + if (this.firstTime && this.jobList.length > 0) { + this.polling$.next(this.jobList.length); + this.firstTime = false; + } + return this.jobList; + } + + refreshDataClick() { + this.refresh$.next(""); + } + + jobsNumber() : number { + return this.jobsDataSource.data.length; + } + + hasJobs(): boolean { + return this.jobsNumber() > 0; } }