From f3153ee6f2ea63afda276c7c3abab9ee4004820b Mon Sep 17 00:00:00 2001 From: elinuxhenrik Date: Fri, 15 May 2020 17:15:04 +0200 Subject: [PATCH] Possibility to create and edit typeless policies Change-Id: I3ad0345c134705e7d60c0f09fee7f7874f963847 Issue-ID: NONRTRIC-212 Signed-off-by: elinuxhenrik --- .../config/PolicyControllerMockConfiguration.java | 9 +- webapp-frontend/src/app/controlpanel.module.ts | 3 + .../no-type-policy-instance-dialog.component.html | 75 ++++++++++++ .../no-type-policy-instance-dialog.component.scss | 47 ++++++++ .../no-type-policy-instance-dialog.component.ts | 128 +++++++++++++++++++++ .../policy-control/policy-control.component.html | 3 +- .../app/policy-control/policy-control.component.ts | 12 +- .../policy-instance-dialog.component.html | 2 +- .../policy-instance-dialog.component.ts | 23 +--- .../policy-control/policy-instance.component.ts | 18 ++- 10 files changed, 292 insertions(+), 28 deletions(-) create mode 100644 webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.html create mode 100644 webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.scss create mode 100644 webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.ts diff --git a/webapp-backend/src/test/java/org/oransc/portal/nonrtric/controlpanel/config/PolicyControllerMockConfiguration.java b/webapp-backend/src/test/java/org/oransc/portal/nonrtric/controlpanel/config/PolicyControllerMockConfiguration.java index ff17d06..89a3ede 100644 --- a/webapp-backend/src/test/java/org/oransc/portal/nonrtric/controlpanel/config/PolicyControllerMockConfiguration.java +++ b/webapp-backend/src/test/java/org/oransc/portal/nonrtric/controlpanel/config/PolicyControllerMockConfiguration.java @@ -21,7 +21,6 @@ package org.oransc.portal.nonrtric.controlpanel.config; import com.google.gson.GsonBuilder; - import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; @@ -34,7 +33,6 @@ import java.util.Map; import java.util.Optional; import java.util.Vector; import java.util.stream.Collectors; - import org.oransc.portal.nonrtric.controlpanel.model.ImmutablePolicyInfo; import org.oransc.portal.nonrtric.controlpanel.model.PolicyInfo; import org.oransc.portal.nonrtric.controlpanel.model.PolicyInstances; @@ -116,8 +114,11 @@ public class PolicyControllerMockConfiguration { class Database { Database() { + PolicyType policyType = new PolicyType("", "{}"); + types.put("", policyType); + String schema = getStringFromFile("demo-policy-schema-1.json"); - PolicyType policyType = new PolicyType("type2", schema); + policyType = new PolicyType("type2", schema); types.put("type2", policyType); schema = getStringFromFile("demo-policy-schema-2.json"); @@ -127,6 +128,8 @@ public class PolicyControllerMockConfiguration { schema = getStringFromFile("demo-policy-schema-3.json"); policyType = new PolicyType("type4", schema); types.put("type4", policyType); + + putInstance("", "123", "{\"data\":\"data\"}", "ric_1"); } private String getStringFromFile(String path) { diff --git a/webapp-frontend/src/app/controlpanel.module.ts b/webapp-frontend/src/app/controlpanel.module.ts index 2540edd..4abd69f 100644 --- a/webapp-frontend/src/app/controlpanel.module.ts +++ b/webapp-frontend/src/app/controlpanel.module.ts @@ -46,6 +46,7 @@ import { FlexLayoutModule } from '@angular/flex-layout'; import { FooterComponent } from './footer/footer.component'; import { MainComponent } from './main/main.component'; import { MaterialDesignFrameworkModule } from 'angular6-json-schema-form'; +import { NoTypePolicyInstanceDialogComponent } from './policy-control/no-type-policy-instance-dialog.component'; import { PolicyCardComponent } from './ui/policy-card/policy-card.component'; import { PolicyControlComponent } from './policy-control/policy-control.component'; import { PolicyInstanceComponent } from './policy-control/policy-instance.component'; @@ -62,6 +63,7 @@ import { CookieModule } from 'ngx-cookie'; ErrorDialogComponent, FooterComponent, MainComponent, + NoTypePolicyInstanceDialogComponent, PolicyCardComponent, PolicyControlComponent, PolicyInstanceComponent, @@ -129,6 +131,7 @@ import { CookieModule } from 'ngx-cookie'; entryComponents: [ ConfirmDialogComponent, ErrorDialogComponent, + NoTypePolicyInstanceDialogComponent, PolicyInstanceDialogComponent ], providers: [ diff --git a/webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.html b/webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.html new file mode 100644 index 0000000..393d40b --- /dev/null +++ b/webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.html @@ -0,0 +1,75 @@ + + +
+ +
+ +
Since this is a policy type without a schema, the user will have to make sure that the content + of the policy has the necessary content. This dialog will only validate that it is a valid JSON string.
+ +
+
+ + Select RIC + + + {{ric}} + + +
+
+ A Ric must be selected. +
+
+
+ + + Policy content + +
+
+ The policy body is required. +
+
+ The policy body must be a valid JSON. +
+
+
+
+ +
\ No newline at end of file diff --git a/webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.scss b/webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.scss new file mode 100644 index 0000000..39787cd --- /dev/null +++ b/webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.scss @@ -0,0 +1,47 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 Nordix Foundation + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ + + +.logo__text { + fill: #2B244D; +} + +.logo__text-dark { + fill: #ffffff; +} + +.header-dark { + background: #2B244D; +} + +.logo { + margin-left: 10px; +} + +.logo__icon { + height: 2rem; + margin-left: 1rem; +} + +.submitBtn { + background-color: #4CAF50; + /* Green */ + margin-right: 10px; +} diff --git a/webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.ts b/webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.ts new file mode 100644 index 0000000..da05ebb --- /dev/null +++ b/webapp-frontend/src/app/policy-control/no-type-policy-instance-dialog.component.ts @@ -0,0 +1,128 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2020 Nordix Foundation + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ +import { Component, Inject, OnInit } from '@angular/core'; +import { FormControl, FormGroup, Validators, ValidatorFn, AbstractControl } from '@angular/forms'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { PolicyService } from '../services/policy/policy.service'; +import { NotificationService } from '../services/ui/notification.service'; +import { UiService } from '../services/ui/ui.service'; +import { HttpErrorResponse } from '@angular/common/http'; +import { ErrorDialogService } from '../services/ui/error-dialog.service'; +import * as uuid from 'uuid'; + +@Component({ + selector: 'rd-no-type-policy-instance-dialog', + templateUrl: './no-type-policy-instance-dialog.component.html', + styleUrls: ['./no-type-policy-instance-dialog.component.scss'] +}) +export class NoTypePolicyInstanceDialogComponent implements OnInit { + + // Declare following variables as Public variable. Private variables should not be used in template HTML + instanceForm: FormGroup; + + policyInstanceId: string; // null if not yet created + policyJson: string; + darkMode: boolean; + ric: string; + allRics: string[]; + + constructor( + private policySvc: PolicyService, + private errorService: ErrorDialogService, + private notificationService: NotificationService, + @Inject(MAT_DIALOG_DATA) private data, + private ui: UiService) { + this.policyInstanceId = data.instanceId; + this.policyJson = data.instanceJson ? JSON.stringify(JSON.parse(data.instanceJson), null, 2) : ''; + this.ric = data.ric; + } + + ngOnInit() { + this.ui.darkModeState.subscribe((isDark) => { + this.darkMode = isDark; + }); + this.instanceForm = new FormGroup({ + 'ricSelector': new FormControl(this.ric, [ + Validators.required + ]), + 'policyJsonTextArea': new FormControl(this.policyJson, [ + Validators.required, + jsonValidator() + ]) + }); + if (!this.policyInstanceId) { + this.fetchRics(); + } + } + + get policyJsonTextArea() { return this.instanceForm.get('policyJsonTextArea'); } + + get ricSelector() { return this.instanceForm.get('ricSelector'); } + + onSubmit() { + if (this.policyInstanceId == null) { + this.policyInstanceId = uuid.v4(); + } + const self: NoTypePolicyInstanceDialogComponent = this; + this.policySvc.putPolicy('', this.policyInstanceId, this.policyJsonTextArea.value, this.ric).subscribe( + { + next(_) { + self.notificationService.success('Policy without type:' + self.policyInstanceId + ' submitted'); + }, + error(error: HttpErrorResponse) { + self.errorService.displayError('Submit failed: ' + error.error); + }, + complete() { } + }); + } + + private fetchRics() { + const self: NoTypePolicyInstanceDialogComponent = this; + this.policySvc.getRics('').subscribe( + { + next(value) { + self.allRics = value; + console.log(value); + }, + error(error: HttpErrorResponse) { + self.errorService.displayError('Fetching of rics failed: ' + error.message); + }, + complete() { } + }); + } +} + +export function jsonValidator(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } | null => { + const notValid = !isJsonValid(control.value); + return notValid ? { 'invalidJson': { value: control.value } } : null; + }; +} + +export function isJsonValid(json: string) { + try { + if (json != null) { + JSON.parse(json); + return true; + } + } catch (jsonError) { + return false; + } +} diff --git a/webapp-frontend/src/app/policy-control/policy-control.component.html b/webapp-frontend/src/app/policy-control/policy-control.component.html index 997defd..fc74800 100644 --- a/webapp-frontend/src/app/policy-control/policy-control.component.html +++ b/webapp-frontend/src/app/policy-control/policy-control.component.html @@ -41,8 +41,7 @@ Action - diff --git a/webapp-frontend/src/app/policy-control/policy-control.component.ts b/webapp-frontend/src/app/policy-control/policy-control.component.ts index 4b9d85a..9cf2438 100644 --- a/webapp-frontend/src/app/policy-control/policy-control.component.ts +++ b/webapp-frontend/src/app/policy-control/policy-control.component.ts @@ -30,6 +30,7 @@ import { PolicyInstanceDialogComponent } from './policy-instance-dialog.componen import { NotificationService } from '../services/ui/notification.service'; import { BehaviorSubject, Observable } from 'rxjs'; import { UiService } from '../services/ui/ui.service'; +import { NoTypePolicyInstanceDialogComponent } from './no-type-policy-instance-dialog.component'; class PolicyTypeInfo { constructor(public type: PolicyType) { } @@ -74,10 +75,17 @@ export class PolicyControlComponent implements OnInit { } createPolicyInstance(policyType: PolicyType): void { - const dialogRef = this.dialog.open(PolicyInstanceDialogComponent, getPolicyDialogProperties(policyType, null, this.darkMode)); + let dialogRef; + if (this.isSchemaEmpty(policyType)) { + dialogRef = this.dialog.open(NoTypePolicyInstanceDialogComponent, + getPolicyDialogProperties(policyType, null, this.darkMode)); + } else { + dialogRef = this.dialog.open(PolicyInstanceDialogComponent, + getPolicyDialogProperties(policyType, null, this.darkMode)); + } const info: PolicyTypeInfo = this.getPolicyTypeInfo(policyType); dialogRef.afterClosed().subscribe( - (result: any) => { + (_) => { info.isExpanded.next(info.isExpanded.getValue()); } ); diff --git a/webapp-frontend/src/app/policy-control/policy-instance-dialog.component.html b/webapp-frontend/src/app/policy-control/policy-instance-dialog.component.html index f052cca..d447c70 100644 --- a/webapp-frontend/src/app/policy-control/policy-instance-dialog.component.html +++ b/webapp-frontend/src/app/policy-control/policy-instance-dialog.component.html @@ -78,7 +78,7 @@ [class.text-danger]="!formIsValid"> {{formIsValid ? 'Json' : 'Not valid'}} - Not submittable + Invalid form — errors:
diff --git a/webapp-frontend/src/app/policy-control/policy-instance-dialog.component.ts b/webapp-frontend/src/app/policy-control/policy-instance-dialog.component.ts index 60ffbd3..8ab798e 100644 --- a/webapp-frontend/src/app/policy-control/policy-instance-dialog.component.ts +++ b/webapp-frontend/src/app/policy-control/policy-instance-dialog.component.ts @@ -123,20 +123,11 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit { if (!this.policyInstanceId) { this.fetchRics(); } - if (this.isSchemaEmpty()) { - // Empty schema, hide form, show json instead - this.isVisible.form = false; - this.isVisible.json = true; - } } ngAfterViewInit() { } - private isSchemaEmpty(): boolean { - return Object.keys(this.jsonSchemaObject).length === 0; - } - onSubmit() { if (this.policyInstanceId == null) { this.policyInstanceId = uuid.v4(); @@ -145,8 +136,9 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit { const self: PolicyInstanceDialogComponent = this; this.dataService.putPolicy(this.policyTypeName, this.policyInstanceId, policyJson, this.ric).subscribe( { - next(value) { - self.notificationService.success('Policy ' + self.policyTypeName + ':' + self.policyInstanceId + ' submitted'); + next(_) { + self.notificationService.success('Policy ' + self.policyTypeName + ':' + self.policyInstanceId + + ' submitted'); }, error(error: HttpErrorResponse) { self.errorService.displayError('Submit failed: ' + error.error); @@ -163,18 +155,15 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit { this.liveFormData = formData; } - get prettyLiveFormData(): string { - if (this.isSchemaEmpty()) { - return this.jsonAsString; - } + get prettyLiveFormData() { return JSON.stringify(this.liveFormData, null, 2); } - get schemaAsString(): string { + get schemaAsString() { return JSON.stringify(this.jsonSchemaObject, null, 2); } - get jsonAsString(): string { + get jsonAsString() { return JSON.stringify(this.jsonObject, null, 2); } diff --git a/webapp-frontend/src/app/policy-control/policy-instance.component.ts b/webapp-frontend/src/app/policy-control/policy-instance.component.ts index 55f50f7..55d9dcb 100644 --- a/webapp-frontend/src/app/policy-control/policy-instance.component.ts +++ b/webapp-frontend/src/app/policy-control/policy-instance.component.ts @@ -28,6 +28,7 @@ import { NotificationService } from '../services/ui/notification.service'; import { PolicyService } from '../services/policy/policy.service'; import { ConfirmDialogService } from './../services/ui/confirm-dialog.service'; import { PolicyInstance } from '../interfaces/policy.types'; +import { NoTypePolicyInstanceDialogComponent } from './no-type-policy-instance-dialog.component'; import { PolicyInstanceDialogComponent } from './policy-instance-dialog.component'; import { getPolicyDialogProperties } from './policy-instance-dialog.component'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; @@ -75,15 +76,26 @@ export class PolicyInstanceComponent implements OnInit, AfterViewInit { } } + private isSchemaEmpty(): boolean { + return Object.keys(this.policyType.schemaObject).length === 0; + } + modifyInstance(instance: PolicyInstance): void { this.policySvc.getPolicy(this.policyType.name, instance.id).subscribe( (refreshedJson: any) => { instance.json = JSON.stringify(refreshedJson); - this.dialog.open(PolicyInstanceDialogComponent, getPolicyDialogProperties(this.policyType, instance, this.darkMode)); + if (this.isSchemaEmpty()) { + this.dialog.open( + NoTypePolicyInstanceDialogComponent, + getPolicyDialogProperties(this.policyType, instance, this.darkMode)); + } else { + this.dialog.open( + PolicyInstanceDialogComponent, + getPolicyDialogProperties(this.policyType, instance, this.darkMode)); + } }, (httpError: HttpErrorResponse) => { - this.notificationService.error('Could not refresh instance ' + httpError.message); - this.dialog.open(PolicyInstanceDialogComponent, getPolicyDialogProperties(this.policyType, instance, this.darkMode)); + this.notificationService.error('Could not refresh instance. Please try again.' + httpError.message); } ); } -- 2.16.6