Adding checkbox to enable/disable auto-refresh
[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 {
26   EMPTY,
27   forkJoin,
28   Subscription,
29   timer,
30 } from "rxjs";
31 import { BehaviorSubject } from "rxjs/BehaviorSubject";
32 import {
33   mergeMap,
34   finalize,
35   map,
36   tap,
37   switchMap
38 } from "rxjs/operators";
39 import { EIJob } from "@interfaces/ei.types";
40 import { EIService } from "@services/ei/ei.service";
41 import { UiService } from "@services/ui/ui.service";
42
43 export interface Job {
44   jobId: string;
45   jobData: any;
46   typeId: string;
47   targetUri: string;
48   owner: string;
49   prodId: string;
50 }
51
52 @Component({
53   selector: "nrcp-jobs-list",
54   templateUrl: "./jobs-list.component.html",
55   styleUrls: ["./jobs-list.component.scss"],
56 })
57 export class JobsListComponent implements OnInit {
58   @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
59   jobsDataSource: MatTableDataSource<Job>;
60   jobForm: FormGroup;
61   darkMode: boolean;
62
63   private jobsSubject$ = new BehaviorSubject<Job[]>([]);
64   private refresh$ = new BehaviorSubject("");
65   private loadingSubject$ = new BehaviorSubject<boolean>(false);
66   private polling$ = new BehaviorSubject<boolean>(true);
67   public loading$ = this.loadingSubject$.asObservable();
68   subscription: Subscription;
69   checked: boolean = false;
70
71   constructor(private eiSvc: EIService, private ui: UiService) {
72     this.jobForm = new FormGroup({
73       jobId: new FormControl(""),
74       typeId: new FormControl(""),
75       owner: new FormControl(""),
76       targetUri: new FormControl(""),
77       prodId: new FormControl(""),
78     });
79   }
80
81   ngOnInit(): void {
82     this.subscription = this.dataSubscription();
83
84     this.jobsSubject$.subscribe((data) => {
85       this.jobsDataSource = new MatTableDataSource<Job>(data);
86       this.jobsDataSource.paginator = this.paginator;
87
88       this.jobsDataSource.filterPredicate = ((data: Job, filter) => {
89         let searchTerms = JSON.parse(filter);
90         return (
91           this.isDataIncluding(data.targetUri, searchTerms.targetUri) &&
92           this.isDataIncluding(data.jobId, searchTerms.jobId) &&
93           this.isDataIncluding(data.owner, searchTerms.owner) &&
94           this.isDataIncluding(data.typeId, searchTerms.typeId) &&
95           this.isDataIncluding(data.prodId, searchTerms.prodId)
96         );
97       }) as (data: Job, filter: any) => boolean;
98     });
99
100     this.jobForm.valueChanges.subscribe((value) => {
101       this.jobsDataSource.filter = JSON.stringify(value);
102     });
103
104     this.ui.darkModeState.subscribe((isDark) => {
105       this.darkMode = isDark;
106     });
107   }
108
109   dataSubscription(): Subscription {
110     let prodId = [];
111     const jobs$ = this.eiSvc.getProducerIds().pipe(
112       tap((data) => (prodId = data)),
113       mergeMap((prodIds) =>
114         forkJoin(prodIds.map((id) => this.eiSvc.getJobsForProducer(id)))
115       ),
116       finalize(() => this.loadingSubject$.next(false))
117     );
118
119     const refreshedJobs$ = this.refresh$
120       .pipe(
121         switchMap((_) => timer(0, 10000).pipe(
122           tap((_) => {
123             this.loadingSubject$.next(true);
124           }),
125           switchMap((_) => jobs$),
126           map((response) => this.extractJobs(prodId, response))
127         )
128         )
129       );
130     
131     return this.polling$.pipe(
132       switchMap(p => {
133         return p ? refreshedJobs$ : EMPTY;
134       })
135     ).subscribe();
136   }
137
138   ngOnDestroy() {
139     this.subscription.unsubscribe();
140   }
141
142   clearFilter() {
143     this.jobForm.get("jobId").setValue("");
144     this.jobForm.get("typeId").setValue("");
145     this.jobForm.get("owner").setValue("");
146     this.jobForm.get("targetUri").setValue("");
147     this.jobForm.get("prodId").setValue("");
148   }
149
150   sortJobs(sort: Sort) {
151     const data = this.jobsDataSource.data;
152     data.sort((a: Job, b: Job) => {
153       const isAsc = sort.direction === "asc";
154       switch (sort.active) {
155         case "jobId":
156           return this.compare(a.jobId, b.jobId, isAsc);
157         case "typeId":
158           return this.compare(a.typeId, b.typeId, isAsc);
159         case "owner":
160           return this.compare(a.owner, b.owner, isAsc);
161         case "targetUri":
162           return this.compare(a.targetUri, b.targetUri, isAsc);
163         case "prodId":
164           return this.compare(a.prodId, b.prodId, isAsc);
165         default:
166           return 0;
167       }
168     });
169     this.jobsDataSource.data = data;
170   }
171
172   stopPolling(checked){
173     this.polling$.next(checked);
174   }
175
176   compare(a: any, b: any, isAsc: boolean) {
177     return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
178   }
179
180   stopSort(event: any) {
181     event.stopPropagation();
182   }
183
184   isDataIncluding(data: string, filter: string): boolean {
185     const transformedFilter = filter.trim().toLowerCase();
186     return data.toLowerCase().includes(transformedFilter);
187   }
188
189   getJobTypeId(eiJob: Job): string {
190     if (eiJob.typeId) {
191       return eiJob.typeId;
192     }
193     return "< No type >";
194   }
195
196   getJobOwner(eiJob: Job): string {
197     if (eiJob.owner) {
198       return eiJob.owner;
199     }
200     return "< No owner >";
201   }
202
203   public jobs(): Job[] {
204     return this.jobsSubject$.value;
205   }
206
207   private extractJobs(prodId: number[], res: EIJob[][]) {
208     this.clearFilter();
209     let jobList = [];
210     prodId.forEach((element, index) => {
211       let jobs = res[index];
212       jobList = jobList.concat(jobs.map((job) => this.createJob(element, job)));
213     });
214     this.jobsSubject$.next(jobList);
215     return jobList;
216   }
217
218   createJobList(prodId: any[], result: EIJob[][]) {
219     let jobList = [];
220     prodId.forEach((element, index) => {
221       let jobs = result[index];
222       jobList = jobList.concat(jobs.map((job) => this.createJob(element, job)));
223     });
224     return jobList;
225   }
226
227   createJob(element: any, job: EIJob): any {
228     let eiJob = <Job>{};
229     eiJob.jobId = job.ei_job_identity;
230     eiJob.typeId = job.ei_type_identity;
231     eiJob.owner = job.owner;
232     eiJob.targetUri = job.target_uri;
233     eiJob.prodId = element;
234     return eiJob;
235   }
236
237   refreshDataClick() {
238     this.refresh$.next("");
239   }
240 }