Add test coverage of PolicyInstanceCompnent
[portal/nonrtric-controlpanel.git] / webapp-frontend / src / app / policy / policy-instance / policy-instance.component.ts
1 /*-
2  * ========================LICENSE_START=================================
3  * O-RAN-SC
4  * %%
5  * Copyright (C) 2019 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
21 import { Sort } from "@angular/material/sort";
22 import { Component, OnInit, Input } from "@angular/core";
23 import { MatDialog } from "@angular/material/dialog";
24 import { PolicyTypeSchema } from "@interfaces/policy.types";
25 import { ErrorDialogService } from "@services/ui/error-dialog.service";
26 import { NotificationService } from "@services/ui/notification.service";
27 import { PolicyService } from "@services/policy/policy.service";
28 import { ConfirmDialogService } from "@services/ui/confirm-dialog.service";
29 import { PolicyInstance } from "@interfaces/policy.types";
30 import { PolicyInstanceDialogComponent } from "../policy-instance-dialog/policy-instance-dialog.component";
31 import { getPolicyDialogProperties } from "../policy-instance-dialog/policy-instance-dialog.component";
32 import { HttpErrorResponse, HttpResponse } from "@angular/common/http";
33 import { BehaviorSubject } from "rxjs";
34 import { UiService } from "@services/ui/ui.service";
35 import { FormControl, FormGroup } from "@angular/forms";
36 import { MatTableDataSource } from "@angular/material/table";
37
38 class PolicyTypeInfo {
39   constructor(public type: PolicyTypeSchema) {}
40
41   isExpanded: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
42 }
43
44 @Component({
45   selector: "nrcp-policy-instance",
46   templateUrl: "./policy-instance.component.html",
47   styleUrls: ["./policy-instance.component.scss"],
48 })
49 export class PolicyInstanceComponent implements OnInit {
50   @Input() policyTypeSchema: PolicyTypeSchema;
51   policyInstances: PolicyInstance[] = [];
52   private policyInstanceSubject = new BehaviorSubject<PolicyInstance[]>([]);
53   policyTypeInfo = new Map<string, PolicyTypeInfo>();
54   instanceDataSource: MatTableDataSource<PolicyInstance> = new MatTableDataSource<PolicyInstance>();
55   policyInstanceForm: FormGroup;
56   darkMode: boolean;
57
58   constructor(
59     private policySvc: PolicyService,
60     private dialog: MatDialog,
61     private errorDialogService: ErrorDialogService,
62     private notificationService: NotificationService,
63     private confirmDialogService: ConfirmDialogService,
64     private ui: UiService
65   ) {
66     this.policyInstanceForm = new FormGroup({
67       id: new FormControl(""),
68       target: new FormControl(""),
69       owner: new FormControl(""),
70       lastModified: new FormControl(""),
71     });
72   }
73
74   ngOnInit() {
75     this.getPolicyInstances();
76     this.policyInstanceSubject.subscribe((data) => {
77       this.instanceDataSource.data = data;
78     });
79
80     this.policyInstanceForm.valueChanges.subscribe((value) => {
81       const filter = { ...value, id: value.id.trim().toLowerCase() } as string;
82       this.instanceDataSource.filter = filter;
83     });
84
85     this.instanceDataSource.filterPredicate = ((
86       data: PolicyInstance,
87       filter
88     ) => {
89       return (
90         this.isDataIncluding(data.policy_id, filter.id) &&
91         this.isDataIncluding(data.ric_id, filter.target) &&
92         this.isDataIncluding(data.service_id, filter.owner) &&
93         this.isDataIncluding(data.lastModified, filter.lastModified)
94       );
95     }) as (data: PolicyInstance, filter: any) => boolean;
96
97     this.ui.darkModeState.subscribe((isDark) => {
98       this.darkMode = isDark;
99     });
100   }
101
102   getPolicyInstances() {
103     this.policyInstances = [] as PolicyInstance[];
104     this.policySvc
105     .getPolicyInstancesByType(this.policyTypeSchema.id)
106     .subscribe((policies) => {
107       if (policies.policy_ids.length != 0) {
108         policies.policy_ids.forEach((policyId) => {
109           this.policySvc
110           .getPolicyInstance(policyId)
111           .subscribe((policyInstance) => {
112             this.policySvc
113                   .getPolicyStatus(policyId)
114                   .subscribe((policyStatus) => {
115                     policyInstance.lastModified = policyStatus.last_modified;
116                   });
117                 this.policyInstances.push(policyInstance);
118               });
119             this.policyInstanceSubject.next(this.policyInstances);
120           });
121         }
122       });
123   }
124
125   getSortedData(sort: Sort) {
126     const data = this.instanceDataSource.data;
127     data.sort((a, b) => {
128       const isAsc = sort.direction === "asc";
129       switch (sort.active) {
130         case "instanceId":
131           return compare(a.policy_id, b.policy_id, isAsc);
132         case "ric":
133           return compare(a.ric_id, b.ric_id, isAsc);
134         case "service":
135           return compare(a.service_id, b.service_id, isAsc);
136         case "lastModified":
137           return compare(a.lastModified, b.lastModified, isAsc);
138         default:
139           return 0;
140       }
141     });
142     this.instanceDataSource.data = data;
143   }
144
145   stopSort(event: any) {
146     event.stopPropagation();
147   }
148
149   isDataIncluding(data: string, filter: string): boolean {
150     return !filter || data.toLowerCase().includes(filter);
151   }
152
153   private onExpand(isExpanded: boolean) {
154     if (isExpanded) {
155       this.getPolicyInstances();
156     }
157   }
158
159   private isSchemaEmpty(): boolean {
160     return this.policyTypeSchema.schemaObject === "{}";
161   }
162
163   modifyInstance(instance: PolicyInstance): void {
164     this.policySvc.getPolicyInstance(instance.policy_id).subscribe(
165       (refreshedJson: any) => {
166         instance = refreshedJson;
167         this.dialog
168           .open(
169             PolicyInstanceDialogComponent,
170             getPolicyDialogProperties(
171               this.policyTypeSchema,
172               instance,
173               this.darkMode
174             )
175           )
176           .afterClosed()
177           .subscribe((_: any) => {
178             this.getPolicyInstances();
179           });
180       },
181       (httpError: HttpErrorResponse) => {
182         this.notificationService.error(
183           "Could not refresh instance. Please try again." + httpError.message
184         );
185       }
186     );
187   }
188
189   nbInstances(): number {
190     return this.policyInstances.length;
191   }
192
193   toLocalTime(utcTime: string): string {
194     const date = new Date(utcTime);
195     const toutc = date.toUTCString();
196     return new Date(toutc + " UTC").toLocaleString();
197   }
198
199   createPolicyInstance(policyTypeSchema: PolicyTypeSchema): void {
200     let dialogRef = this.dialog.open(
201       PolicyInstanceDialogComponent,
202       getPolicyDialogProperties(policyTypeSchema, null, this.darkMode)
203     );
204     const info: PolicyTypeInfo = this.getPolicyTypeInfo(policyTypeSchema);
205     dialogRef.afterClosed().subscribe((_) => {
206       info.isExpanded.next(info.isExpanded.getValue());
207     });
208   }
209
210   deleteInstance(instance: PolicyInstance): void {
211     this.confirmDialogService
212       .openConfirmDialog(
213         "Are you sure you want to delete this policy instance?"
214       )
215       .afterClosed()
216       .subscribe((res: any) => {
217         if (res) {
218           this.policySvc.deletePolicy(instance.policy_id).subscribe(
219             (response: HttpResponse<Object>) => {
220               switch (response.status) {
221                 case 204:
222                   this.notificationService.success("Delete succeeded!");
223                   this.getPolicyInstances();
224                   break;
225                 default:
226                   this.notificationService.warn(
227                     "Delete failed " + response.status + " " + response.body
228                   );
229               }
230             },
231             (error: HttpErrorResponse) => {
232               this.errorDialogService.displayError(
233                 error.statusText + ", " + error.error
234               );
235             }
236           );
237         }
238       });
239   }
240
241   getPolicyTypeInfo(policyTypeSchema: PolicyTypeSchema): PolicyTypeInfo {
242     let info: PolicyTypeInfo = this.policyTypeInfo.get(policyTypeSchema.name);
243     if (!info) {
244       info = new PolicyTypeInfo(policyTypeSchema);
245       this.policyTypeInfo.set(policyTypeSchema.name, info);
246     }
247     return info;
248   }
249
250   refreshTable() {
251     this.getPolicyInstances();
252   }
253 }
254
255 function compare(a: string, b: string, isAsc: boolean) {
256   return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
257 }