From b3f060ed6175d6dab20e16f51cb96a8ed02a6fc2 Mon Sep 17 00:00:00 2001 From: elinuxhenrik Date: Tue, 9 Mar 2021 09:23:01 +0100 Subject: [PATCH] Create typed policy editor component Change-Id: I544d025b57fdbea823424033ee5a48ebb0655916 Signed-off-by: elinuxhenrik Issue-ID: NONRTRIC-462 --- .../policy-instance-dialog.component.html | 60 ++-------- .../policy-instance-dialog.component.ts | 118 ++----------------- webapp-frontend/src/app/policy/policy.module.ts | 22 ++++ .../typed-policy-editor.component.html | 63 ++++++++++ .../typed-policy-editor.component.scss | 23 ++++ .../typed-policy-editor.component.spec.ts | 41 +++++++ .../typed-policy-editor.component.ts | 128 +++++++++++++++++++++ 7 files changed, 298 insertions(+), 157 deletions(-) create mode 100644 webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.html create mode 100644 webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.scss create mode 100644 webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.spec.ts create mode 100644 webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.ts diff --git a/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.html b/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.html index 2d0526e..695dc6d 100644 --- a/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.html +++ b/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.html @@ -19,7 +19,7 @@ -->
@@ -41,11 +41,11 @@ fxLayoutAlign.lt-sm="flex-start center"> - - + - + {{ric.ric_id}} @@ -56,51 +56,11 @@
-

- {{isVisible.form ? 'expand_less' : 'expand_more'}} - Properties -

-
-
{{jsonFormStatusMessage}}
- - - -
+
- - +
-

- {{isVisible.json ? 'expand_less' : 'expand_more'}} - Json -

-
-
- - {{formIsValid ? 'Json' : 'Not valid'}} - - Invalid form - — errors: -
-
-
-
{{prettyLiveFormData}}
-
-
- -

- {{isVisible.schema ? 'expand_less' : 'expand_more'}} - Json Schema -

-
- Schema -
{{schemaAsString}}
-
\ No newline at end of file diff --git a/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.ts b/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.ts index a2bd7a5..3c2ea2b 100644 --- a/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.ts +++ b/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.ts @@ -17,10 +17,8 @@ * limitations under the License. * ========================LICENSE_END=================================== */ -import { animate, state, style, transition, trigger } from '@angular/animations'; import { AfterViewInit, Component, Inject, OnInit, ViewChild } from '@angular/core'; import { MatDialogConfig, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { JsonPointer } from 'angular6-json-schema-form'; import * as uuid from 'uuid'; import { CreatePolicyInstance, PolicyInstance, PolicyTypeSchema } from '../../interfaces/policy.types'; import { PolicyService } from '../../services/policy/policy.service'; @@ -31,59 +29,26 @@ import { HttpErrorResponse } from '@angular/common/http'; import { FormGroup, FormControl, Validators } from '@angular/forms'; import { ChangeDetectorRef } from '@angular/core'; import { Ric, Rics } from '../../interfaces/ric'; +import { TypedPolicyEditorComponent } from '../typed-policy-editor/typed-policy-editor.component'; @Component({ selector: 'nrcp-policy-instance-dialog', templateUrl: './policy-instance-dialog.component.html', - styleUrls: ['./policy-instance-dialog.component.scss'], - animations: [ - trigger('expandSection', [ - state('in', style({ height: '*' })), - transition(':enter', [ - style({ height: 0 }), animate(100), - ]), - transition(':leave', [ - style({ height: '*' }), - animate(100, style({ height: 0 })), - ]), - ]), - ], + styleUrls: ['./policy-instance-dialog.component.scss'] }) export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit { + @ViewChild(TypedPolicyEditorComponent) + policyEditor: TypedPolicyEditorComponent; instanceForm: FormGroup; - formActive = false; - isVisible = { - form: true, - json: false, - schema: false - }; - - jsonFormStatusMessage = 'Loading form...'; - jsonSchemaObject: any = {}; - jsonObject: any = {}; - - - jsonFormOptions: any = { - addSubmit: false, // Add a submit button if layout does not have one - debug: false, // Don't show inline debugging information - loadExternalAssets: true, // Load external css and JavaScript for frameworks - returnEmptyFields: false, // Don't return values for empty input fields - setSchemaDefaults: true, // Always use schema defaults for empty fields - defautWidgetOptions: { feedback: true }, // Show inline feedback icons - }; - - liveFormData: any = {}; - formValidationErrors: any; - formIsValid = false; - + ric: string; + allRics: Ric[]; policyInstanceId: string; // null if not yet created policyTypeName: string; + jsonSchemaObject: any = {}; darkMode: boolean; - ric: string; - allRics: Ric[]; private fetchRics() { console.log('fetchRics ' + this.policyTypeName); @@ -102,20 +67,16 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit { private dataService: PolicyService, private errorService: ErrorDialogService, private notificationService: NotificationService, - @Inject(MAT_DIALOG_DATA) private data, + @Inject(MAT_DIALOG_DATA) public data, private dialogRef: MatDialogRef, private ui: UiService) { - this.formActive = false; this.policyInstanceId = data.instanceId; this.policyTypeName = data.name; this.jsonSchemaObject = data.createSchema; - this.jsonObject = data.instanceJson; this.ric = data.ric; } ngOnInit() { - this.jsonFormStatusMessage = 'Init'; - this.formActive = true; this.ui.darkModeState.subscribe((isDark) => { this.darkMode = isDark; }); @@ -139,7 +100,7 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit { if (this.policyInstanceId == null) { this.policyInstanceId = uuid.v4(); } - const policyJson: string = this.prettyLiveFormData; + const policyJson: string = this.policyEditor.prettyLiveFormData; const self: PolicyInstanceDialogComponent = this; let createPolicyInstance: CreatePolicyInstance = this.createPolicyInstance(policyJson); this.dataService.putPolicy(createPolicyInstance).subscribe( @@ -170,65 +131,8 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit { this.dialogRef.close(); } - public onChanges(formData: any) { - this.liveFormData = formData; - } - - get prettyLiveFormData(): string { - return JSON.stringify(this.liveFormData, null, 2); - } - - get schemaAsString(): string { - return JSON.stringify(this.jsonSchemaObject, null, 2); - } - - get jsonAsString(): string { - return JSON.stringify(this.jsonObject, null, 2); - } - - isValid(isValid: boolean): void { - this.formIsValid = isValid; - } - - validationErrors(validationErrors: any): void { - this.formValidationErrors = validationErrors; - } - - get prettyValidationErrors() { - if (!this.formValidationErrors) { return null; } - const errorArray = []; - for (const error of this.formValidationErrors) { - const message = error.message; - const dataPathArray = JsonPointer.parse(error.dataPath); - if (dataPathArray.length) { - let field = dataPathArray[0]; - for (let i = 1; i < dataPathArray.length; i++) { - const key = dataPathArray[i]; - field += /^\d+$/.test(key) ? `[${key}]` : `.${key}`; - } - errorArray.push(`${field}: ${message}`); - } else { - errorArray.push(message); - } - } - return errorArray.join('
'); - } - - private parseJson(str: string): string { - try { - if (str != null) { - return JSON.parse(str); - } - } catch (jsonError) { - this.jsonFormStatusMessage = - 'Invalid JSON\n' + - 'parser returned:\n\n' + jsonError; - } - return null; - } - - public toggleVisible(item: string) { - this.isVisible[item] = !this.isVisible[item]; + get isJsonFormValid(): boolean { + return this.policyEditor ? this.policyEditor.formIsValid : false; } } diff --git a/webapp-frontend/src/app/policy/policy.module.ts b/webapp-frontend/src/app/policy/policy.module.ts index 9bf2959..5f7e4fc 100644 --- a/webapp-frontend/src/app/policy/policy.module.ts +++ b/webapp-frontend/src/app/policy/policy.module.ts @@ -1,3 +1,23 @@ +// - +// ========================LICENSE_START================================= +// O-RAN-SC +// %% +// Copyright (C) 2021: 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 { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { MatTableModule } from '@angular/material/table'; @@ -33,6 +53,7 @@ import { FlexLayoutModule } from '@angular/flex-layout'; import { Routes, RouterModule } from '@angular/router'; import { RicSelectorComponent } from './ric-selector/ric-selector.component'; import { NoTypePolicyEditorComponent } from './no-type-policy-editor/no-type-policy-editor.component'; +import { TypedPolicyEditorComponent } from './typed-policy-editor/typed-policy-editor.component'; const routes:Routes = [ {path: 'policy', component: PolicyControlComponent} @@ -47,6 +68,7 @@ const routes:Routes = [ PolicyInstanceDialogComponent, RicSelectorComponent, NoTypePolicyEditorComponent, + TypedPolicyEditorComponent, ], imports: [ CommonModule, diff --git a/webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.html b/webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.html new file mode 100644 index 0000000..9e6e1e4 --- /dev/null +++ b/webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.html @@ -0,0 +1,63 @@ + + +

+ {{isVisible.form ? 'expand_less' : 'expand_more'}} + Properties +

+
+
{{jsonFormStatusMessage}}
+ + + +
+

+{{isVisible.json ? 'expand_less' : 'expand_more'}} +Json +

+
+
+ + {{formIsValid ? 'Json' : 'Not valid'}} + + Invalid form + — errors: +
+
+
+
{{prettyLiveFormData}}
+
+
+ +

+{{isVisible.schema ? 'expand_less' : 'expand_more'}} +Json Schema +

+
+Schema +
{{schemaAsString}}
+
diff --git a/webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.scss b/webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.scss new file mode 100644 index 0000000..3666abc --- /dev/null +++ b/webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.scss @@ -0,0 +1,23 @@ +// - +// ========================LICENSE_START================================= +// O-RAN-SC +// %% +// Copyright (C) 2021: 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=================================== +// + +.text__dark { + color: white; +} diff --git a/webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.spec.ts b/webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.spec.ts new file mode 100644 index 0000000..9f58af2 --- /dev/null +++ b/webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.spec.ts @@ -0,0 +1,41 @@ +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/compiler'; +import { ChangeDetectorRef } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatIconModule } from '@angular/material/icon'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; + +import { TypedPolicyEditorComponent } from './typed-policy-editor.component'; + +describe('TypedPolicyEditorComponent', () => { + let component: TypedPolicyEditorComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + BrowserAnimationsModule, + MatIconModule + ], + declarations: [ + TypedPolicyEditorComponent + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ], + providers: [ + ChangeDetectorRef + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TypedPolicyEditorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.ts b/webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.ts new file mode 100644 index 0000000..d05c78f --- /dev/null +++ b/webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.ts @@ -0,0 +1,128 @@ +// - +// ========================LICENSE_START================================= +// O-RAN-SC +// %% +// Copyright (C) 2021: 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 { animate, state, style, transition, trigger } from '@angular/animations'; +import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { JsonPointer } from 'angular6-json-schema-form'; + +@Component({ + selector: 'nrcp-typed-policy-editor', + templateUrl: './typed-policy-editor.component.html', + styleUrls: ['./typed-policy-editor.component.scss'], + animations: [ + trigger('expandSection', [ + state('in', style({ height: '*' })), + transition(':enter', [ + style({ height: 0 }), animate(100), + ]), + transition(':leave', [ + style({ height: '*' }), + animate(100, style({ height: 0 })), + ]), + ]), + ], + +}) +export class TypedPolicyEditorComponent implements OnInit { + jsonFormOptions: any = { + addSubmit: false, // Add a submit button if layout does not have one + debug: false, // Don't show inline debugging information + loadExternalAssets: false, // Load external css and JavaScript for frameworks + returnEmptyFields: false, // Don't return values for empty input fields + setSchemaDefaults: true, // Always use schema defaults for empty fields + defautWidgetOptions: { feedback: true }, // Show inline feedback icons + }; + + @Input() jsonSchemaObject: any = {}; + @Input() jsonObject: any = {}; + @Input() darkMode: boolean; + + isVisible = { + form: true, + json: false, + schema: false + }; + formActive: boolean = false; + jsonFormStatusMessage: string = 'Loading form...'; + liveFormData: any = {}; + formIsValid: boolean = false; + formValidationErrors: any; + + constructor( + private cdr: ChangeDetectorRef) { + this.formActive = false; + } + + ngOnInit(): void { + this.formActive = true; + } + + ngAfterViewInit() { + this.cdr.detectChanges(); + } + + public onChanges(formData: any) { + this.liveFormData = formData; + } + + get prettyLiveFormData(): string { + return JSON.stringify(this.liveFormData, null, 2); + } + + get schemaAsString(): string { + return JSON.stringify(this.jsonSchemaObject, null, 2); + } + + get jsonAsString(): string { + return JSON.stringify(this.jsonObject, null, 2); + } + + isValid(isValid: boolean): void { + this.formIsValid = isValid; + } + + validationErrors(validationErrors: any): void { + this.formValidationErrors = validationErrors; + } + + get prettyValidationErrors() { + if (!this.formValidationErrors) { return null; } + const errorArray = []; + for (const error of this.formValidationErrors) { + const message = error.message; + const dataPathArray = JsonPointer.parse(error.dataPath); + if (dataPathArray.length) { + let field = dataPathArray[0]; + for (let i = 1; i < dataPathArray.length; i++) { + const key = dataPathArray[i]; + field += /^\d+$/.test(key) ? `[${key}]` : `.${key}`; + } + errorArray.push(`${field}: ${message}`); + } else { + errorArray.push(message); + } + } + return errorArray.join('
'); + } + + public toggleVisible(item: string) { + this.isVisible[item] = !this.isVisible[item]; + } +} -- 2.16.6