f06202ccabf00ee2232e775dceb516f9fae7ecdd
[portal/nonrtric-controlpanel.git] / webapp-frontend / src / app / ei-coordinator / jobs-list / jobs-list.component.ts
1 /*-
2  * ========================LICENSE_START=================================
3  * O-RAN-SC
4  * %%
5  * Copyright (C) 2021 Nordix Foundation
6  * %%
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ========================LICENSE_END===================================
19  */
20 import { Component, OnInit, ViewChild } from "@angular/core";
21 import { FormControl, FormGroup } from "@angular/forms";
22 import { MatPaginator } from "@angular/material/paginator";
23 import { Sort } from "@angular/material/sort";
24 import { MatTableDataSource } from "@angular/material/table";
25 import { EMPTY, forkJoin, Subscription, timer } from "rxjs";
26 import { BehaviorSubject } from "rxjs/BehaviorSubject";
27 import { mergeMap, finalize, map, tap, switchMap } from "rxjs/operators";
28 import { JobInfo } from "@interfaces/producer.types";
29 import { ProducerService } from "@services/ei/producer.service";
30 import { UiService } from "@services/ui/ui.service";
31
32 export interface Job {
33   jobId: string;
34   jobData: any;
35   typeId: string;
36   targetUri: string;
37   owner: string;
38   prodId: string;
39 }
40
41 @Component({
42   selector: "nrcp-jobs-list",
43   templateUrl: "./jobs-list.component.html",
44   styleUrls: ["./jobs-list.component.scss"],
45 })
46 export class JobsListComponent implements OnInit {
47   @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
48   jobsDataSource: MatTableDataSource<Job>;
49   jobForm: FormGroup;
50   darkMode: boolean;
51
52   private jobsSubject$ = new BehaviorSubject<Job[]>([]);
53   private refresh$ = new BehaviorSubject("");
54   private loadingSubject$ = new BehaviorSubject<boolean>(false);
55   private polling$ = new BehaviorSubject(0);
56   public loading$ = this.loadingSubject$.asObservable();
57   subscription: Subscription;
58   checked: boolean = false;
59   firstTime: boolean = true;
60
61   constructor(private producerService: ProducerService, private ui: UiService) {
62     this.jobForm = new FormGroup({
63       jobId: new FormControl(""),
64       typeId: new FormControl(""),
65       owner: new FormControl(""),
66       targetUri: new FormControl(""),
67       prodId: new FormControl(""),
68     });
69   }
70
71   ngOnInit(): void {
72     this.subscription = this.dataSubscription();
73
74     this.jobsSubject$.subscribe((data) => {
75       this.jobsDataSource = new MatTableDataSource<Job>(data);
76       this.jobsDataSource.paginator = this.paginator;
77
78       this.jobsDataSource.filterPredicate = ((data: Job, filter) => {
79         let searchTerms = JSON.parse(filter);
80         return (
81           this.isDataIncluding(data.targetUri, searchTerms.targetUri) &&
82           this.isDataIncluding(data.jobId, searchTerms.jobId) &&
83           this.isDataIncluding(data.owner, searchTerms.owner) &&
84           this.isDataIncluding(data.typeId, searchTerms.typeId) &&
85           this.isDataIncluding(data.prodId, searchTerms.prodId)
86         );
87       }) as (data: Job, filter: any) => boolean;
88     });
89
90     this.jobForm.valueChanges.subscribe((value) => {
91       this.jobsDataSource.filter = JSON.stringify(value);
92     });
93
94     this.ui.darkModeState.subscribe((isDark) => {
95       this.darkMode = isDark;
96     });
97   }
98
99   dataSubscription(): Subscription {
100     let prodId = [];
101     const jobs$ = this.producerService.getProducerIds().pipe(
102       tap((data) => (prodId = data)),
103       mergeMap((prodIds) =>
104         forkJoin(prodIds.map((id) => this.producerService.getJobsForProducer(id)))
105       ),
106       finalize(() => this.loadingSubject$.next(false))
107     );
108
109     const refreshedJobs$ = this.refresh$.pipe(
110       switchMap((_) =>
111         timer(0, 10000).pipe(
112           tap((_) => {
113             this.loadingSubject$.next(true);
114           }),
115           switchMap((_) => jobs$),
116           map((response) => this.extractJobs(prodId, response))
117         )
118       )
119     );
120
121     return this.polling$
122       .pipe(
123         switchMap((value) => {
124           let pollCondition = value == 0 || this.checked;
125           return pollCondition ? refreshedJobs$ : EMPTY;
126         })
127       )
128       .subscribe();
129   }
130
131   ngOnDestroy() {
132     this.subscription.unsubscribe();
133   }
134
135   clearFilter() {
136     this.jobForm.get("jobId").setValue("");
137     this.jobForm.get("typeId").setValue("");
138     this.jobForm.get("owner").setValue("");
139     this.jobForm.get("targetUri").setValue("");
140     this.jobForm.get("prodId").setValue("");
141   }
142
143   sortJobs(sort: Sort) {
144     const data = this.jobsDataSource.data;
145     data.sort((a: Job, b: Job) => {
146       const isAsc = sort.direction === "asc";
147       switch (sort.active) {
148         case "jobId":
149           return this.compare(a.jobId, b.jobId, isAsc);
150         case "typeId":
151           return this.compare(a.typeId, b.typeId, isAsc);
152         case "owner":
153           return this.compare(a.owner, b.owner, isAsc);
154         case "targetUri":
155           return this.compare(a.targetUri, b.targetUri, isAsc);
156         case "prodId":
157           return this.compare(a.prodId, b.prodId, isAsc);
158         default:
159           return 0;
160       }
161     });
162     this.jobsDataSource.data = data;
163   }
164
165   stopPolling(checked) {
166     this.checked = checked;
167     this.polling$.next(this.jobs().length);
168   }
169
170   compare(a: any, b: any, isAsc: boolean) {
171     return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
172   }
173
174   stopSort(event: any) {
175     event.stopPropagation();
176   }
177
178   isDataIncluding(data: string, filter: string): boolean {
179     const transformedFilter = filter.trim().toLowerCase();
180     return data.toLowerCase().includes(transformedFilter);
181   }
182
183   getJobTypeId(job: Job): string {
184     if (job.typeId) {
185       return job.typeId;
186     }
187     return "< No type >";
188   }
189
190   getJobOwner(job: Job): string {
191     if (job.owner) {
192       return job.owner;
193     }
194     return "< No owner >";
195   }
196
197   public jobs(): Job[] {
198     return this.jobsSubject$.value;
199   }
200
201   private extractJobs(prodId: number[], res: JobInfo[][]) {
202     this.clearFilter();
203     let jobList = [];
204     prodId.forEach((element, index) => {
205       let jobs = res[index];
206       jobList = jobList.concat(jobs.map((job) => this.createJob(element, job)));
207     });
208     this.jobsSubject$.next(jobList);
209     if (this.firstTime && jobList.length > 0) {
210       this.polling$.next(jobList.length);
211       this.firstTime = false;
212     }
213     return jobList;
214   }
215
216   createJobList(prodId: any[], result: JobInfo[][]) {
217     let jobList = [];
218     prodId.forEach((element, index) => {
219       let jobs = result[index];
220       jobList = jobList.concat(jobs.map((job) => this.createJob(element, job)));
221     });
222     return jobList;
223   }
224
225   createJob(element: any, job: JobInfo): any {
226     let infoJob = <Job>{};
227     infoJob.jobId = job.info_job_identity;
228     infoJob.typeId = job.info_type_identity;
229     infoJob.owner = job.owner;
230     infoJob.targetUri = job.target_uri;
231     infoJob.prodId = element;
232     return infoJob;
233   }
234
235   refreshDataClick() {
236     this.refresh$.next("");
237   }
238 }