Possibility to create and edit typeless policies
[portal/nonrtric-controlpanel.git] / webapp-frontend / src / app / policy-control / policy-instance-dialog.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 import { animate, state, style, transition, trigger } from '@angular/animations';
21 import { AfterViewInit, Component, Inject, OnInit, ViewChild } from '@angular/core';
22 import { MatDialogConfig, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
23 import { MatMenuTrigger } from '@angular/material/menu';
24 import { JsonPointer } from 'angular6-json-schema-form';
25 import * as uuid from 'uuid';
26 import { PolicyInstance, PolicyType } from '../interfaces/policy.types';
27 import { PolicyService } from '../services/policy/policy.service';
28 import { ErrorDialogService } from '../services/ui/error-dialog.service';
29 import { NotificationService } from './../services/ui/notification.service';
30 import { UiService } from '../services/ui/ui.service';
31 import { HttpErrorResponse } from '@angular/common/http';
32
33
34 @Component({
35     selector: 'rd-policy-instance-dialog',
36     templateUrl: './policy-instance-dialog.component.html',
37     styleUrls: ['./policy-instance-dialog.component.scss'],
38     animations: [
39         trigger('expandSection', [
40             state('in', style({ height: '*' })),
41             transition(':enter', [
42                 style({ height: 0 }), animate(100),
43             ]),
44             transition(':leave', [
45                 style({ height: '*' }),
46                 animate(100, style({ height: 0 })),
47             ]),
48         ]),
49     ],
50 })
51 export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit {
52
53     formActive = false;
54     isVisible = {
55         form: true,
56         json: false,
57         schema: false
58     };
59
60     jsonFormStatusMessage = 'Loading form...';
61     jsonSchemaObject: any = {};
62     jsonObject: any = {};
63
64
65     jsonFormOptions: any = {
66         addSubmit: false, // Add a submit button if layout does not have one
67         debug: false, // Don't show inline debugging information
68         loadExternalAssets: true, // Load external css and JavaScript for frameworks
69         returnEmptyFields: false, // Don't return values for empty input fields
70         setSchemaDefaults: true, // Always use schema defaults for empty fields
71         defautWidgetOptions: { feedback: true }, // Show inline feedback icons
72     };
73
74     liveFormData: any = {};
75     formValidationErrors: any;
76     formIsValid = false;
77
78     @ViewChild(MatMenuTrigger, { static: true }) menuTrigger: MatMenuTrigger;
79
80     policyInstanceId: string; // null if not yet created
81     policyTypeName: string;
82     darkMode: boolean;
83     ric: string;
84     allRics: string[];
85
86     private fetchRics() {
87         console.log('fetchRics ' + this.policyTypeName);
88         const self: PolicyInstanceDialogComponent = this;
89         this.dataService.getRics(this.policyTypeName).subscribe(
90             {
91                 next(value) {
92                     self.allRics = value;
93                     console.log(value);
94                 },
95                 error(error: HttpErrorResponse) {
96                     self.errorService.displayError('Fetching of rics failed: ' + error.message);
97                 },
98                 complete() { }
99             });
100     }
101
102     constructor(
103         private dataService: PolicyService,
104         private errorService: ErrorDialogService,
105         private notificationService: NotificationService,
106         @Inject(MAT_DIALOG_DATA) private data,
107         private dialogRef: MatDialogRef<PolicyInstanceDialogComponent>,
108         private ui: UiService) {
109         this.formActive = false;
110         this.policyInstanceId = data.instanceId;
111         this.policyTypeName = data.name;
112         this.jsonSchemaObject = data.createSchema;
113         this.jsonObject = this.parseJson(data.instanceJson);
114         this.ric = data.ric;
115     }
116
117     ngOnInit() {
118         this.jsonFormStatusMessage = 'Init';
119         this.formActive = true;
120         this.ui.darkModeState.subscribe((isDark) => {
121             this.darkMode = isDark;
122         });
123         if (!this.policyInstanceId) {
124             this.fetchRics();
125         }
126     }
127
128     ngAfterViewInit() {
129     }
130
131     onSubmit() {
132         if (this.policyInstanceId == null) {
133             this.policyInstanceId = uuid.v4();
134         }
135         const policyJson: string = this.prettyLiveFormData;
136         const self: PolicyInstanceDialogComponent = this;
137         this.dataService.putPolicy(this.policyTypeName, this.policyInstanceId, policyJson, this.ric).subscribe(
138             {
139                 next(_) {
140                     self.notificationService.success('Policy ' + self.policyTypeName + ':' + self.policyInstanceId +
141                     ' submitted');
142                 },
143                 error(error: HttpErrorResponse) {
144                     self.errorService.displayError('Submit failed: ' + error.error);
145                 },
146                 complete() { }
147             });
148     }
149
150     onClose() {
151         this.dialogRef.close();
152     }
153
154     public onChanges(formData: any) {
155         this.liveFormData = formData;
156     }
157
158     get prettyLiveFormData() {
159         return JSON.stringify(this.liveFormData, null, 2);
160     }
161
162     get schemaAsString() {
163         return JSON.stringify(this.jsonSchemaObject, null, 2);
164     }
165
166     get jsonAsString() {
167         return JSON.stringify(this.jsonObject, null, 2);
168     }
169
170     isValid(isValid: boolean): void {
171         this.formIsValid = isValid;
172     }
173
174     validationErrors(validationErrors: any): void {
175         this.formValidationErrors = validationErrors;
176     }
177
178     get prettyValidationErrors() {
179         if (!this.formValidationErrors) { return null; }
180         const errorArray = [];
181         for (const error of this.formValidationErrors) {
182             const message = error.message;
183             const dataPathArray = JsonPointer.parse(error.dataPath);
184             if (dataPathArray.length) {
185                 let field = dataPathArray[0];
186                 for (let i = 1; i < dataPathArray.length; i++) {
187                     const key = dataPathArray[i];
188                     field += /^\d+$/.test(key) ? `[${key}]` : `.${key}`;
189                 }
190                 errorArray.push(`${field}: ${message}`);
191             } else {
192                 errorArray.push(message);
193             }
194         }
195         return errorArray.join('<br>');
196     }
197
198     private parseJson(str: string): string {
199         try {
200             if (str != null) {
201                 return JSON.parse(str);
202             }
203         } catch (jsonError) {
204             this.jsonFormStatusMessage =
205                 'Invalid JSON\n' +
206                 'parser returned:\n\n' + jsonError;
207         }
208         return null;
209     }
210
211     public toggleVisible(item: string) {
212         this.isVisible[item] = !this.isVisible[item];
213     }
214 }
215
216 export function getPolicyDialogProperties(policyType: PolicyType, instance: PolicyInstance, darkMode: boolean): MatDialogConfig {
217     const createSchema = policyType.schemaObject;
218     const instanceId = instance ? instance.id : null;
219     const instanceJson = instance ? instance.json : null;
220     const name = policyType.name;
221     const ric = instance ? instance.ric : null;
222     return {
223         maxWidth: '1200px',
224         maxHeight: '900px',
225         width: '900px',
226         role: 'dialog',
227         disableClose: false,
228         panelClass: darkMode ? 'dark-theme' : '',
229         data: {
230             createSchema,
231             instanceId,
232             instanceJson,
233             name,
234             ric
235         }
236     };
237 }
238