From: Patrik Buhr Date: Wed, 17 Mar 2021 12:33:31 +0000 (+0000) Subject: Merge "Include datasource of jobs and producers into component" X-Git-Tag: 2.2.0~59 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=042a087cf3eea5e6f941ee9add6e1c50cb106e91;hp=d5e2acb3624c6705f47bed520d1330c0e75f78e4;p=portal%2Fnonrtric-controlpanel.git Merge "Include datasource of jobs and producers into component" --- diff --git a/webapp-frontend/package.json b/webapp-frontend/package.json index 96af51f..8ed13a4 100644 --- a/webapp-frontend/package.json +++ b/webapp-frontend/package.json @@ -28,7 +28,6 @@ "angular6-json-schema-form": "^8.0.0", "bootstrap": "^4.4.1", "chart.js": "^2.9.3", - "core-js": "^2.6.11", "hammerjs": "^2.0.8", "jquery": "^3.5.1", "lodash-es": "^4.17.15", @@ -36,6 +35,7 @@ "ng2-completer": "^2.0.8", "ngx-cookie-service": "^3.1.3", "ngx-toastr": "^11.0.0", + "popper.js": "^1.16.1", "rxjs": "6.5.3", "rxjs-compat": "6.3.3", "tslib": "^1.10.0", diff --git a/webapp-frontend/src/app/interceptor.mock.ts b/webapp-frontend/src/app/interceptor.mock.ts index 29bcf6a..749d762 100644 --- a/webapp-frontend/src/app/interceptor.mock.ts +++ b/webapp-frontend/src/app/interceptor.mock.ts @@ -21,11 +21,13 @@ import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http'; import { Injectable, Injector } from '@angular/core'; import { Observable, of } from 'rxjs'; -import * as policyinstances1 from './mock/policy-instance-1.json'; -import * as policies from './mock/policies.json'; -import * as policyinstances2 from './mock/policy-instance-2.json'; -import * as policyinstances1Status from './mock/policy-instance-1-status.json'; -import * as policyinstances2Status from './mock/policy-instance-2-status.json'; +import * as policyinstance1 from './mock/policy-instance-1.json'; +import * as noTypePolicies from './mock/no-type-policies.json'; +import * as typedPolicies from './mock/policies.json'; +import * as policyinstance2 from './mock/policy-instance-2.json'; +import * as noTypePolicyinstance from './mock/policy-instance-notype.json'; +import * as policyinstance1Status from './mock/policy-instance-1-status.json'; +import * as policyinstance2Status from './mock/policy-instance-2-status.json'; import * as eijobsProd1 from './mock/ei-jobs-producer1.json'; import * as eijobsProd2 from './mock/ei-jobs-producer2.json'; import * as eiProducerIds from './mock/ei-producerids.json'; @@ -54,27 +56,35 @@ const urls = [ }, { url: '/a1-policy/v2/policies?policytype_id=', - json: policies + json: noTypePolicies }, { url: '/a1-policy/v2/policies?policytype_id=1', - json: policies + json: typedPolicies + }, + { + url: '/a1-policy/v2/policies/2001', + json: noTypePolicyinstance }, { url: '/a1-policy/v2/policies/2000', - json: policyinstances1 + json: policyinstance1 }, { url: '/a1-policy/v2/policies/2100', - json: policyinstances2 + json: policyinstance2 + }, + { + url: '/a1-policy/v2/policies/2001/status', + json: policyinstance1Status }, { url: '/a1-policy/v2/policies/2000/status', - json: policyinstances1Status + json: policyinstance1Status }, { url: '/a1-policy/v2/policies/2100/status', - json: policyinstances2Status + json: policyinstance2Status }, { url: '/a1-policy/v2/policies/2000?type=', diff --git a/webapp-frontend/src/app/mock/no-type-policies.json b/webapp-frontend/src/app/mock/no-type-policies.json new file mode 100644 index 0000000..6f00441 --- /dev/null +++ b/webapp-frontend/src/app/mock/no-type-policies.json @@ -0,0 +1,5 @@ +{ + "policy_ids": [ + "2001" + ] +} \ No newline at end of file diff --git a/webapp-frontend/src/app/mock/policy-instance-notype.json b/webapp-frontend/src/app/mock/policy-instance-notype.json index 7c91060..a3d95e7 100644 --- a/webapp-frontend/src/app/mock/policy-instance-notype.json +++ b/webapp-frontend/src/app/mock/policy-instance-notype.json @@ -1,18 +1,17 @@ -[ - { - "id": "2001", - "type": "", - "ric": "ric1", - "json": { - "scope": { - "ueId": "ue3100", - "qosId": "qos3100" - }, - "qosObjectives": { - "priorityLevel": 3100 - } - }, - "service": "service1", - "lastModified": "2020-12-08T21:12:43.719084Z" +{ + "policy_id": "2001", + "policytype_id": "", + "ric_id": "ric1", + "policy_data": { + "scope": { + "ueId": "ue3200", + "qosId": "qos3200" + }, + "qosObjectives": { + "priorityLevel": 3100 } - ] \ No newline at end of file + }, + "service_id": "service1", + "transient": false, + "status_notification_uri": "" +} \ No newline at end of file diff --git a/webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.spec.ts b/webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.spec.ts index 529caee..fb3a6a6 100644 --- a/webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.spec.ts +++ b/webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.spec.ts @@ -18,22 +18,22 @@ // ========================LICENSE_END=================================== // -import { HarnessLoader } from '@angular/cdk/testing'; -import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; -import { Component, ViewChild, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { FormBuilder, FormGroup } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatButtonHarness } from '@angular/material/button/testing'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import { MatInputHarness } from '@angular/material/input/testing'; -import { BrowserModule } from '@angular/platform-browser'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { HarnessLoader } from "@angular/cdk/testing"; +import { TestbedHarnessEnvironment } from "@angular/cdk/testing/testbed"; +import { Component, ViewChild, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { FormBuilder, FormGroup } from "@angular/forms"; +import { MatButtonModule } from "@angular/material/button"; +import { MatButtonHarness } from "@angular/material/button/testing"; +import { MatFormFieldModule } from "@angular/material/form-field"; +import { MatInputModule } from "@angular/material/input"; +import { MatInputHarness } from "@angular/material/input/testing"; +import { BrowserModule } from "@angular/platform-browser"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -import { NoTypePolicyEditorComponent } from './no-type-policy-editor.component'; +import { NoTypePolicyEditorComponent } from "./no-type-policy-editor.component"; -describe('NoTypePolicyEditorComponent', () => { +describe("NoTypePolicyEditorComponent", () => { let formGroup: FormGroup = new FormGroup({}); let component: TestNoTypePolicyEditorComponentHostComponent; @@ -47,65 +47,74 @@ describe('NoTypePolicyEditorComponent', () => { BrowserAnimationsModule, MatButtonModule, MatFormFieldModule, - MatInputModule - ], - schemas: [ - CUSTOM_ELEMENTS_SCHEMA + MatInputModule, ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], declarations: [ NoTypePolicyEditorComponent, - TestNoTypePolicyEditorComponentHostComponent + TestNoTypePolicyEditorComponentHostComponent, ], - providers: [ - FormBuilder - ] - }) - .compileComponents(); + providers: [FormBuilder], + }).compileComponents(); - fixture = TestBed.createComponent(TestNoTypePolicyEditorComponentHostComponent); + fixture = TestBed.createComponent( + TestNoTypePolicyEditorComponentHostComponent + ); component = fixture.componentInstance; fixture.detectChanges(); loader = TestbedHarnessEnvironment.loader(fixture); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); - it('should be added to form group with required validator', async () => { - let textArea: MatInputHarness = await loader.getHarness(MatInputHarness.with({ selector: '#policyJsonTextArea' })); + it("should be added to form group with required validator", async () => { + let textArea: MatInputHarness = await loader.getHarness( + MatInputHarness.with({ selector: "#policyJsonTextArea" }) + ); - expect(formGroup.get('policyJsonTextArea')).toBeTruthy(); + expect(formGroup.get("policyJsonTextArea")).toBeTruthy(); expect(await textArea.isRequired()).toBeTruthy(); }); - it('should contain provided policy json and enabled Format button', async () => { - let textArea: MatInputHarness = await loader.getHarness(MatInputHarness.with({ selector: '#policyJsonTextArea' })); + it("should contain provided policy json and enabled Format button", async () => { + let textArea: MatInputHarness = await loader.getHarness( + MatInputHarness.with({ selector: "#policyJsonTextArea" }) + ); expect(await textArea.getValue()).toEqual('{"A":"A"}'); - console.log('Validity:',formGroup.valid); - let formatButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({ selector: '#formatButton' })); + let formatButton: MatButtonHarness = await loader.getHarness( + MatButtonHarness.with({ selector: "#formatButton" }) + ); expect(await formatButton.isDisabled()).toBeFalsy(); }); - it('Format button should be disabled when json not valid', async () => { - const ele = formGroup.get('policyJsonTextArea'); - ele.setValue('{'); + it("Format button should be disabled when json not valid", async () => { + const ele = formGroup.get("policyJsonTextArea"); + ele.setValue("{"); - let formatButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({ selector: '#formatButton' })); + let formatButton: MatButtonHarness = await loader.getHarness( + MatButtonHarness.with({ selector: "#formatButton" }) + ); expect(await formatButton.isDisabled()).toBeTruthy(); }); - it('should format unformatted json', async () => { - const textArea = formGroup.get('policyJsonTextArea'); + it("should format unformatted json", async () => { + const textArea = formGroup.get("policyJsonTextArea"); textArea.setValue('{"A":"A"}'); component.noTypePolicyEditorComponent.formatJsonInput(); - expect(component.noTypePolicyEditorComponent.policyJson).toEqual('{\n "A": "A"\n}'); + expect(component.noTypePolicyEditorComponent.policyJson).toEqual( + '{\n "A": "A"\n}' + ); }); @Component({ selector: `no-type-policy-editor-host-component`, - template: `` + template: ``, }) class TestNoTypePolicyEditorComponentHostComponent { @ViewChild(NoTypePolicyEditorComponent) diff --git a/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.html b/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.html deleted file mode 100644 index afba575..0000000 --- a/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.html +++ /dev/null @@ -1,52 +0,0 @@ - - - -
- -
- -
- - - -

- Properties -

- -
- - -
-
-
\ No newline at end of file diff --git a/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.scss b/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.scss deleted file mode 100644 index accb527..0000000 --- a/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.scss +++ /dev/null @@ -1,73 +0,0 @@ -/*- - * ========================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; -} - -.header { - background: linear-gradient(to bottom, white 0%, grayscale($color: #eeeaea) 100%); - font-size: 40px; - margin-top: 10px; - margin-bottom: 10px; - margin-left: 2px; - margin-right: 2px; -} - -.header-dark { - background: #2B244D; -} - - -.card { - height: 100%; - width: 100%; - margin-left: 2px; - margin-right: 2px; - background-color: grayscale($color: #eeeaea); -} - -.card-dark { - background-color: #1c1c24; -} diff --git a/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.spec.ts b/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.spec.ts deleted file mode 100644 index fc07a5b..0000000 --- a/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.spec.ts +++ /dev/null @@ -1,171 +0,0 @@ -/*- - * ========================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=================================== - */ - -import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { HarnessLoader } from "@angular/cdk/testing"; -import { MatButtonModule } from '@angular/material/button'; -import { MatButtonHarness } from '@angular/material/button/testing'; -import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { MatSelectModule } from '@angular/material/select'; -import { MatInputModule } from '@angular/material/input'; -import { ReactiveFormsModule } from "@angular/forms"; -import { TestbedHarnessEnvironment } from "@angular/cdk/testing/testbed"; -import { ToastrModule } from "ngx-toastr"; - -import { PolicyService } from "../../services/policy/policy.service"; -import { ErrorDialogService } from "../../services/ui/error-dialog.service"; -import { UiService } from "../../services/ui/ui.service"; -import { NoTypePolicyInstanceDialogComponent } from "./no-type-policy-instance-dialog.component"; -import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; - -describe('NoTypePolicyInstanceDialogComponent', () => { - let component: NoTypePolicyInstanceDialogComponent; - let fixture: ComponentFixture; - let loader: HarnessLoader; - let policyServiceSpy: jasmine.SpyObj; - let errDialogServiceSpy: jasmine.SpyObj; - - beforeEach(async () => { - policyServiceSpy = jasmine.createSpyObj('PolicyService', [ 'putPolicy' ]); - errDialogServiceSpy = jasmine.createSpyObj('ErrorDialogService', [ 'displayError' ]); - - TestBed.configureTestingModule({ - imports: [ - BrowserAnimationsModule, - MatButtonModule, - MatDialogModule, - MatInputModule, - MatSelectModule, - ReactiveFormsModule, - ToastrModule.forRoot() - ], - schemas: [ - CUSTOM_ELEMENTS_SCHEMA - ], - declarations: [ - NoTypePolicyInstanceDialogComponent - ], - providers: [ - { provide: MatDialogRef, useValue: component }, - { provide: PolicyService, useValue: policyServiceSpy }, - { provide: ErrorDialogService, useValue: errDialogServiceSpy }, - { provide: MAT_DIALOG_DATA, useValue: true }, - UiService - ] - }); - }); - - describe('content when creating policy', () => { - beforeEach(async () => { - ({ fixture, component, loader } = compileAndGetComponents(fixture, component, loader)); - }); - - it('should contain oran logo and create title and no instance info', async () => { - let ele = fixture.debugElement.nativeElement.querySelector('img'); - expect(ele.src).toContain('assets/oran-logo.png'); - - ele = fixture.debugElement.nativeElement.querySelector('text'); - expect(ele.childNodes[0].childNodes[0].textContent).toEqual('Create new policy instance of < No type >'); - - ele = fixture.debugElement.nativeElement.querySelector('#instanceInfo'); - expect(ele).toBeFalsy(); - }); - - it('should contain ric select', async () => { - const ele = fixture.debugElement.nativeElement.querySelector('nrcp-ric-selector'); - expect(ele).toBeTruthy(); - }); - - it('should contain json editor', async () => { - const ele = fixture.debugElement.nativeElement.querySelector('nrcp-no-type-policy-editor'); - expect(ele).toBeTruthy(); - }); - - it('should contain enabled Close button and disabled Submit button', async () => { - component.ngOnInit(); - - let closeButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({ selector: '#closeButton' })); - expect(await closeButton.isDisabled()).toBeFalsy(); - expect(await closeButton.getText()).toEqual('Close'); - - let submitButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({selector: '#submitButton'})); - // expect(await submitButton.isDisabled()).toBeTruthy(); - expect(await submitButton.getText()).toEqual('Submit'); - }); - }); - - describe('content when editing policy', () => { - beforeEach(async () => { - const policyData = { - createSchema: "{}", - instanceId: "instanceId", - instanceJson: '{"qosObjectives": {"priorityLevel": 3100}}', - name: "name", - ric: "ric1" - }; - TestBed.overrideProvider(MAT_DIALOG_DATA, {useValue: policyData }); // Should be provided with a policy - ({ fixture, component, loader } = compileAndGetComponents(fixture, component, loader)); - }); - - it('should contain oran logo and instance info', async () => { - let ele = fixture.debugElement.nativeElement.querySelector('img'); - expect(ele.src).toContain('assets/oran-logo.png'); - - ele = fixture.debugElement.nativeElement.querySelector('text'); - expect(ele.childNodes[0].childNodes[0]).toBeFalsy(); // No create title - - ele = fixture.debugElement.nativeElement.querySelector('#instanceInfo'); - expect(ele).toBeTruthy(); - expect(ele.innerText).toEqual('[ric1] Instance ID: instanceId'); - }); - - it('should not contain ric select', async () => { - const ele = fixture.debugElement.nativeElement.querySelector('nrcp-ric-selector'); - expect(ele).toBeFalsy(); - }); - - it('should contain json editor', async () => { - const ele = fixture.debugElement.nativeElement.querySelector('nrcp-no-type-policy-editor'); - expect(ele).toBeTruthy(); - }); - - it('should contain enabled Close and Submit buttons', async () => { - let closeButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({selector: '#closeButton'})); - expect(await closeButton.isDisabled()).toBeFalsy(); - expect(await closeButton.getText()).toEqual('Close'); - - let submitButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({selector: '#submitButton'})); - expect(await submitButton.isDisabled()).toBeFalsy(); - expect(await submitButton.getText()).toEqual('Submit'); - }); - - }); -}); - -function compileAndGetComponents(fixture: ComponentFixture, component: NoTypePolicyInstanceDialogComponent, loader: HarnessLoader) { - TestBed.compileComponents(); - - fixture = TestBed.createComponent(NoTypePolicyInstanceDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - loader = TestbedHarnessEnvironment.loader(fixture); - return { fixture, component, loader }; -} diff --git a/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.ts b/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.ts deleted file mode 100644 index 855d718..0000000 --- a/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.ts +++ /dev/null @@ -1,97 +0,0 @@ -/*- - * ========================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, ViewChild } from '@angular/core'; -import { FormGroup } from '@angular/forms'; -import { MatDialogRef, 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'; -import { CreatePolicyInstance } from '../../interfaces/policy.types'; -import { RicSelectorComponent } from '../ric-selector/ric-selector.component'; -import { formatJsonString, NoTypePolicyEditorComponent } from '../no-type-policy-editor/no-type-policy-editor.component'; - -@Component({ - selector: 'nrcp-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 { - instanceForm: FormGroup; - @ViewChild(RicSelectorComponent) - private ricSelectorComponent: RicSelectorComponent; - @ViewChild(NoTypePolicyEditorComponent) - private policyEditorComponent: NoTypePolicyEditorComponent; - policyInstanceId: string; // null if not yet created - policyJson: string; - darkMode: boolean; - ric: string; - allRicIds: string[] = []; - - constructor( - public dialogRef: MatDialogRef, - 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 ? formatJsonString(data.instanceJson) : '{}'; - this.ric = data.ric; - } - - ngOnInit() { - this.ui.darkModeState.subscribe((isDark) => { - this.darkMode = isDark; - }); - this.instanceForm = new FormGroup({}); - } - - onSubmit() { - if (this.policyInstanceId == null) { - this.policyInstanceId = uuid.v4(); - } - const self: NoTypePolicyInstanceDialogComponent = this; - let createPolicyInstance: CreatePolicyInstance = this.createPolicyInstance(this.policyEditorComponent.policyJsonTextArea.value); - this.policySvc.putPolicy(createPolicyInstance).subscribe( - { - next(_) { - self.notificationService.success('Policy without type:' + self.policyInstanceId + ' submitted'); - self.dialogRef.close(); - }, - error(error: HttpErrorResponse) { - self.errorService.displayError('Submit failed: ' + error.error); - }, - complete() { } - }); - } - - private createPolicyInstance(policyJson: string): CreatePolicyInstance { - let createPolicyInstance = {} as CreatePolicyInstance; - createPolicyInstance.policy_data = JSON.parse(policyJson); - createPolicyInstance.policy_id = this.policyInstanceId; - createPolicyInstance.policytype_id = ''; - createPolicyInstance.ric_id = this.ricSelectorComponent ? this.ricSelectorComponent.selectedRic : this.ric; - createPolicyInstance.service_id = 'controlpanel'; - return createPolicyInstance; - } -} diff --git a/webapp-frontend/src/app/policy/policy-control.component.ts b/webapp-frontend/src/app/policy/policy-control.component.ts index 01c38b6..28253c8 100644 --- a/webapp-frontend/src/app/policy/policy-control.component.ts +++ b/webapp-frontend/src/app/policy/policy-control.component.ts @@ -23,7 +23,6 @@ import { MatDialog } from '@angular/material/dialog'; import { BehaviorSubject, Observable } from 'rxjs'; -import { NoTypePolicyInstanceDialogComponent } from './no-type-policy-instance-dialog/no-type-policy-instance-dialog.component'; import { PolicyTypeSchema } from '../interfaces/policy.types'; import { PolicyTypeDataSource } from './policy-type/policy-type.datasource'; import { getPolicyDialogProperties } from './policy-instance-dialog/policy-instance-dialog.component'; @@ -67,14 +66,8 @@ export class PolicyControlComponent implements OnInit { } createPolicyInstance(policyTypeSchema: PolicyTypeSchema): void { - let dialogRef; - if (this.isSchemaEmpty(policyTypeSchema)) { - dialogRef = this.dialog.open(NoTypePolicyInstanceDialogComponent, - getPolicyDialogProperties(policyTypeSchema, null, this.darkMode)); - } else { - dialogRef = this.dialog.open(PolicyInstanceDialogComponent, - getPolicyDialogProperties(policyTypeSchema, null, this.darkMode)); - } + let dialogRef = this.dialog.open(PolicyInstanceDialogComponent, + getPolicyDialogProperties(policyTypeSchema, null, this.darkMode)); const info: PolicyTypeInfo = this.getPolicyTypeInfo(policyTypeSchema); dialogRef.afterClosed().subscribe( (_) => { @@ -88,10 +81,6 @@ export class PolicyControlComponent implements OnInit { info.isExpanded.next(!info.isExpanded.getValue()); } - private isSchemaEmpty(policyTypeSchema: PolicyTypeSchema): boolean { - return policyTypeSchema.schemaObject === '{}'; - } - getPolicyTypeInfo(policyTypeSchema: PolicyTypeSchema): PolicyTypeInfo { let info: PolicyTypeInfo = this.policyTypeInfo.get(policyTypeSchema.name); if (!info) { 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 695dc6d..6e79e78 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 @@ -2,7 +2,7 @@ ========================LICENSE_START================================= O-RAN-SC %% - Copyright (C) 2019 Nordix Foundation + 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. @@ -19,7 +19,7 @@ -->
@@ -37,30 +37,21 @@
{{jsonSchemaObject.description}}
-
- - - - {{ric.ric_id}} - - -
-
- This field is required. -
-
-
- - -
- - -
+ +

+ Properties +

+ + +
+ + +
-
\ No newline at end of file + \ No newline at end of file diff --git a/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.scss b/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.scss index 7ecfff1..accb527 100644 --- a/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.scss +++ b/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.scss @@ -27,19 +27,6 @@ fill: #ffffff; } -.text__dark { - color: white; -} - -.header { - background: linear-gradient(to bottom, white 0%, grayscale($color: #eeeaea) 100%); - font-size: 40px; - margin-top: 10px; - margin-bottom: 10px; - margin-left: 2px; - margin-right: 2px; -} - .header-dark { background: #2B244D; } @@ -59,6 +46,20 @@ margin-right: 10px; } +.header { + background: linear-gradient(to bottom, white 0%, grayscale($color: #eeeaea) 100%); + font-size: 40px; + margin-top: 10px; + margin-bottom: 10px; + margin-left: 2px; + margin-right: 2px; +} + +.header-dark { + background: #2B244D; +} + + .card { height: 100%; width: 100%; @@ -69,4 +70,4 @@ .card-dark { background-color: #1c1c24; -} \ No newline at end of file +} diff --git a/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.spec.ts b/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.spec.ts new file mode 100644 index 0000000..b4c2732 --- /dev/null +++ b/webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.spec.ts @@ -0,0 +1,429 @@ +/*- + * ========================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=================================== + */ + +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { HarnessLoader } from "@angular/cdk/testing"; +import { MatButtonModule } from "@angular/material/button"; +import { MatButtonHarness } from "@angular/material/button/testing"; +import { + MatDialogModule, + MatDialogRef, + MAT_DIALOG_DATA, +} from "@angular/material/dialog"; +import { MatSelectModule } from "@angular/material/select"; +import { MatInputModule } from "@angular/material/input"; +import { + AbstractControl, + FormControl, + FormGroup, + ReactiveFormsModule, + Validators, +} from "@angular/forms"; +import { TestbedHarnessEnvironment } from "@angular/cdk/testing/testbed"; +import { ToastrModule } from "ngx-toastr"; + +import { PolicyService } from "../../services/policy/policy.service"; +import { ErrorDialogService } from "../../services/ui/error-dialog.service"; +import { UiService } from "../../services/ui/ui.service"; +import { PolicyInstanceDialogComponent } from "./policy-instance-dialog.component"; +import { + ChangeDetectorRef, + Component, + CUSTOM_ELEMENTS_SCHEMA, + Input, +} from "@angular/core"; +import { TypedPolicyEditorComponent } from "../typed-policy-editor/typed-policy-editor.component"; +import { RicSelectorComponent } from "../ric-selector/ric-selector.component"; +import { NoTypePolicyEditorComponent } from "../no-type-policy-editor/no-type-policy-editor.component"; +import { By } from "@angular/platform-browser"; + +describe("PolicyInstanceDialogComponent", () => { + const untypedSchema = "{}"; + const typedSchema = + '{ "description": "Type 1 policy type", "title": "1", "type": "object", "properties": { "priorityLevel": "number" }}'; + + let component: PolicyInstanceDialogComponent; + let fixture: ComponentFixture; + let loader: HarnessLoader; + let policyServiceSpy: jasmine.SpyObj; + let errDialogServiceSpy: jasmine.SpyObj; + + beforeEach(async () => { + policyServiceSpy = jasmine.createSpyObj("PolicyService", ["putPolicy"]); + errDialogServiceSpy = jasmine.createSpyObj("ErrorDialogService", [ + "displayError", + ]); + + TestBed.configureTestingModule({ + imports: [ + BrowserAnimationsModule, + MatButtonModule, + MatDialogModule, + MatInputModule, + MatSelectModule, + ReactiveFormsModule, + ToastrModule.forRoot(), + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + declarations: [ + PolicyInstanceDialogComponent, + RicSelectorStubComponent, + NoTypePolicyEditorStubComponent, + TypedPolicyEditorStubComponent, + ], + providers: [ + ChangeDetectorRef, + { provide: MatDialogRef, useValue: component }, + { provide: PolicyService, useValue: policyServiceSpy }, + { provide: ErrorDialogService, useValue: errDialogServiceSpy }, + { provide: MAT_DIALOG_DATA, useValue: true }, + UiService, + ], + }); + }); + + describe("content when creating policy without type", () => { + beforeEach(async () => { + const policyData = { + createSchema: untypedSchema, + }; + TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy + ({ fixture, component, loader } = compileAndGetComponents( + fixture, + component, + loader + )); + }); + + it("should contain oran logo and create title and no instance info", async () => { + let ele = fixture.debugElement.nativeElement.querySelector("img"); + expect(ele.src).toContain("assets/oran-logo.png"); + + ele = fixture.debugElement.nativeElement.querySelector("text"); + expect(ele.textContent).toEqual( + "Create new policy instance of type < No Type >" + ); + + ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo"); + expect(ele).toBeFalsy(); + }); + + it("should contain ric select with instance form and no policy type", async () => { + const ricSelector: RicSelectorComponent = fixture.debugElement.query(By.directive(RicSelectorComponent)).componentInstance; + expect(ricSelector).toBeTruthy(); + expect(ricSelector.instanceForm).toBeTruthy(); + expect(ricSelector.policyTypeName).toBeFalsy(); + }); + + it("should contain json editor with instance form and empty JSON", async () => { + const noTypePolicyEditor: NoTypePolicyEditorComponent = fixture.debugElement.query(By.directive(NoTypePolicyEditorComponent)).componentInstance; + expect(noTypePolicyEditor).toBeTruthy(); + expect(noTypePolicyEditor.instanceForm).toBeTruthy(); + expect(noTypePolicyEditor.policyJson).toEqual("{}"); + }); + + it("should contain enabled Close button and disabled Submit button", async () => { + component.ngOnInit(); + // Add an empty value with required validator to set the dialog's instance form to be invalid. + const value: any = null; + component.instanceForm.addControl( + "dummy", + new FormControl(value, [Validators.required]) + ); + expect(component.instanceForm.valid).toBeFalsy(); + + let closeButton: MatButtonHarness = await loader.getHarness( + MatButtonHarness.with({ selector: "#closeButton" }) + ); + expect(await closeButton.isDisabled()).toBeFalsy(); + expect(await closeButton.getText()).toEqual("Close"); + + let submitButton: MatButtonHarness = await loader.getHarness( + MatButtonHarness.with({ selector: "#submitButton" }) + ); + expect(await submitButton.isDisabled()).toBeTruthy(); + expect(await submitButton.getText()).toEqual("Submit"); + }); + }); + + describe("content when creating policy with type", () => { + beforeEach(async () => { + const policyData = { + name: "Type 1", + createSchema: typedSchema, + }; + TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy + ({ fixture, component, loader } = compileAndGetComponents( + fixture, + component, + loader + )); + }); + + it("should contain oran logo and create title and no instance info", async () => { + let ele = fixture.debugElement.nativeElement.querySelector("img"); + expect(ele.src).toContain("assets/oran-logo.png"); + + ele = fixture.debugElement.nativeElement.querySelector("text"); + expect(ele.textContent).toEqual( + "Create new policy instance of type Type 1" + ); + + ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo"); + expect(ele).toBeFalsy(); + }); + + it("should contain ric select with instance form and provided policy type", async () => { + const ricSelector: RicSelectorComponent = fixture.debugElement.query(By.directive(RicSelectorComponent)).componentInstance; + expect(ricSelector).toBeTruthy(); + expect(ricSelector.instanceForm).toBeTruthy(); + expect(ricSelector.policyTypeName).toEqual("Type 1"); + }); + + it("should contain typed json editor with empty JSON, schema and dark mode true", async () => { + const typedPolicyEditor: TypedPolicyEditorComponent = fixture.debugElement.query(By.directive(TypedPolicyEditorComponent)).componentInstance; + expect(typedPolicyEditor).toBeTruthy(); + expect(typedPolicyEditor.jsonObject).toBeFalsy(); + expect(typedPolicyEditor.jsonSchemaObject).toEqual(typedSchema); + expect(typedPolicyEditor.darkMode).toBeTruthy(); + }); + + it("should contain enabled Close button and disabled Submit button", async () => { + component.ngOnInit(); + // Add an empty value with required validator to set the dialog's instance form to be invalid. + const value: any = null; + component.instanceForm.addControl( + "dummy", + new FormControl(value, [Validators.required]) + ); + expect(component.instanceForm.valid).toBeFalsy(); + + let closeButton: MatButtonHarness = await loader.getHarness( + MatButtonHarness.with({ selector: "#closeButton" }) + ); + expect(await closeButton.isDisabled()).toBeFalsy(); + expect(await closeButton.getText()).toEqual("Close"); + + let submitButton: MatButtonHarness = await loader.getHarness( + MatButtonHarness.with({ selector: "#submitButton" }) + ); + expect(await submitButton.isDisabled()).toBeTruthy(); + expect(await submitButton.getText()).toEqual("Submit"); + }); + }); + + describe("content when editing policy without type", () => { + const instanceJson = '{"qosObjectives": {"priorityLevel": 3100}}'; + beforeEach(async () => { + const policyData = { + createSchema: untypedSchema, + instanceId: "instanceId", + instanceJson: instanceJson, + name: "Type 1", + ric: "ric1", + }; + TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy + ({ fixture, component, loader } = compileAndGetComponents( + fixture, + component, + loader + )); + }); + + it("should contain oran logo and instance info", async () => { + let ele = fixture.debugElement.nativeElement.querySelector("img"); + expect(ele.src).toContain("assets/oran-logo.png"); + + ele = fixture.debugElement.nativeElement.querySelector("text"); + expect(ele.childNodes[0].childNodes[0]).toBeFalsy(); // No create title + + ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo"); + expect(ele).toBeTruthy(); + expect(ele.innerText).toEqual("[ric1] Instance ID: instanceId"); + }); + + it("should not contain ric select", async () => { + const ricSelector = fixture.debugElement.query(By.directive(RicSelectorComponent)); + expect(ricSelector).toBeFalsy(); + }); + + it("should contain json editor with form and json data", async () => { + const noTypePolicyEditor: NoTypePolicyEditorComponent = fixture.debugElement.query(By.directive(NoTypePolicyEditorComponent)).componentInstance; + expect(noTypePolicyEditor).toBeTruthy(); + expect(noTypePolicyEditor.instanceForm).toBeTruthy(); + expect(unescapeQuotes(noTypePolicyEditor.policyJson)).toEqual('"' + instanceJson + '"'); + }); + + it("should contain enabled Close and Submit buttons when all inputs are valid", async () => { + expect(component.instanceForm.valid).toBeTruthy(); + + let closeButton: MatButtonHarness = await loader.getHarness( + MatButtonHarness.with({ selector: "#closeButton" }) + ); + expect(await closeButton.isDisabled()).toBeFalsy(); + expect(await closeButton.getText()).toEqual("Close"); + + let submitButton: MatButtonHarness = await loader.getHarness( + MatButtonHarness.with({ selector: "#submitButton" }) + ); + expect(await submitButton.isDisabled()).toBeFalsy(); + expect(await submitButton.getText()).toEqual("Submit"); + }); + }); + + describe("content when editing policy with type", () => { + const instanceJson = '{"qosObjectives": {"priorityLevel": 3100}}'; + beforeEach(async () => { + const policyData = { + createSchema: typedSchema, + instanceId: "instanceId", + instanceJson: instanceJson, + name: "name", + ric: "ric1", + }; + TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: policyData }); // Should be provided with a policy + ({ fixture, component, loader } = compileAndGetComponents( + fixture, + component, + loader + )); + }); + + it("should contain oran logo and instance info", async () => { + let ele = fixture.debugElement.nativeElement.querySelector("img"); + expect(ele.src).toContain("assets/oran-logo.png"); + + ele = fixture.debugElement.nativeElement.querySelector("text"); + expect(ele.childNodes[0].childNodes[0]).toBeFalsy(); // No create title + + ele = fixture.debugElement.nativeElement.querySelector("#instanceInfo"); + expect(ele).toBeTruthy(); + expect(ele.innerText).toEqual("[ric1] Instance ID: instanceId"); + }); + + it("should not contain ric select", async () => { + const ricSelector = fixture.debugElement.query(By.directive(RicSelectorComponent)); + expect(ricSelector).toBeFalsy(); + }); + + it("should contain typed json editor with instance JSON, schema and dark mode true", async () => { + const typedPolicyEditor: TypedPolicyEditorComponent = fixture.debugElement.query(By.directive(TypedPolicyEditorComponent)).componentInstance; + expect(typedPolicyEditor).toBeTruthy(); + expect(unescapeQuotes(typedPolicyEditor.jsonObject)).toEqual(instanceJson); + expect(typedPolicyEditor.jsonSchemaObject).toEqual(typedSchema); + expect(typedPolicyEditor.darkMode).toBeTruthy(); + }); + + it("should contain enabled Close and Submit buttons when all inputs are valid", async () => { + expect(component.instanceForm.valid).toBeTruthy(); + + let closeButton: MatButtonHarness = await loader.getHarness( + MatButtonHarness.with({ selector: "#closeButton" }) + ); + expect(await closeButton.isDisabled()).toBeFalsy(); + expect(await closeButton.getText()).toEqual("Close"); + + let submitButton: MatButtonHarness = await loader.getHarness( + MatButtonHarness.with({ selector: "#submitButton" }) + ); + expect(await submitButton.isDisabled()).toBeFalsy(); + expect(await submitButton.getText()).toEqual("Submit"); + }); + }); +}); + +function compileAndGetComponents( + fixture: ComponentFixture, + component: PolicyInstanceDialogComponent, + loader: HarnessLoader +) { + TestBed.compileComponents(); + + fixture = TestBed.createComponent(PolicyInstanceDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + loader = TestbedHarnessEnvironment.loader(fixture); + return { fixture, component, loader }; +} + +function unescapeQuotes(string: string): string { + return string.replace(/\\"/g, '"'); +} + +@Component({ + selector: "nrcp-ric-selector", + template: "", + providers: [ + { + provide: RicSelectorComponent, + useClass: RicSelectorStubComponent, + }, + ], +}) +class RicSelectorStubComponent { + @Input() instanceForm: FormGroup; + @Input() policyTypeName: string = ""; + + get selectedRic(): string { + return "ric1"; + } +} + +@Component({ + selector: "nrcp-no-type-policy-editor", + template: "", + providers: [ + { + provide: NoTypePolicyEditorComponent, + useClass: NoTypePolicyEditorStubComponent, + }, + ], +}) +class NoTypePolicyEditorStubComponent { + @Input() instanceForm: FormGroup; + @Input() policyJson: string; + + get policyJsonTextArea(): AbstractControl { + const textArea = { value: "{}" } as AbstractControl; + return textArea; + } +} + +@Component({ + selector: "nrcp-typed-policy-editor", + template: "", + providers: [ + { + provide: TypedPolicyEditorComponent, + useClass: TypedPolicyEditorStubComponent, + }, + ], +}) +class TypedPolicyEditorStubComponent { + @Input() jsonSchemaObject: any = {}; + @Input() jsonObject: any = {}; + @Input() darkMode: boolean; + + prettyLiveFormData = '"A": "string"'; + get formIsValid(): boolean { + return true; + } +} 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 3c2ea2b..096346e 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 @@ -2,7 +2,7 @@ * ========================LICENSE_START================================= * O-RAN-SC * %% - * Copyright (C) 2019 Nordix Foundation + * 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. @@ -17,145 +17,177 @@ * limitations under the License. * ========================LICENSE_END=================================== */ -import { AfterViewInit, Component, Inject, OnInit, ViewChild } from '@angular/core'; -import { MatDialogConfig, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; -import * as uuid from 'uuid'; -import { CreatePolicyInstance, PolicyInstance, PolicyTypeSchema } from '../../interfaces/policy.types'; -import { PolicyService } from '../../services/policy/policy.service'; -import { ErrorDialogService } from '../../services/ui/error-dialog.service'; -import { NotificationService } from './../../services/ui/notification.service'; -import { UiService } from '../../services/ui/ui.service'; -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'; - +import { + AfterViewInit, + ChangeDetectorRef, + Component, + Inject, + OnInit, + ViewChild, +} from "@angular/core"; +import { FormGroup } from "@angular/forms"; +import { + MatDialogConfig, + MatDialogRef, + 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"; +import { + CreatePolicyInstance, + PolicyInstance, + PolicyTypeSchema, +} from "../../interfaces/policy.types"; +import { RicSelectorComponent } from "../ric-selector/ric-selector.component"; +import { + formatJsonString, + NoTypePolicyEditorComponent, +} from "../no-type-policy-editor/no-type-policy-editor.component"; +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'] + selector: "nrcp-policy-instance-dialog", + templateUrl: "./policy-instance-dialog.component.html", + styleUrls: ["./policy-instance-dialog.component.scss"], }) export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit { - @ViewChild(TypedPolicyEditorComponent) - policyEditor: TypedPolicyEditorComponent; - instanceForm: FormGroup; + instanceForm: FormGroup; + @ViewChild(RicSelectorComponent) + ricSelector: RicSelectorComponent; + @ViewChild(NoTypePolicyEditorComponent) + noTypePolicyEditor: NoTypePolicyEditorComponent; + @ViewChild(TypedPolicyEditorComponent) + typedPolicyEditor: TypedPolicyEditorComponent; + policyInstanceId: string; // null if not yet created + policyJson: string; + policyTypeName: string; + jsonSchemaObject: any; + darkMode: boolean; + ric: string; + allRicIds: string[] = []; + constructor( + private cdr: ChangeDetectorRef, + public dialogRef: MatDialogRef, + private policySvc: PolicyService, + private errorService: ErrorDialogService, + private notificationService: NotificationService, + @Inject(MAT_DIALOG_DATA) private data, + private ui: UiService + ) { + this.policyInstanceId = data.instanceId; + this.policyTypeName = data.name; + this.policyJson = data.instanceJson; + this.jsonSchemaObject = data.createSchema; + this.ric = data.ric; + } - ric: string; - allRics: Ric[]; - policyInstanceId: string; // null if not yet created - policyTypeName: string; - jsonSchemaObject: any = {}; - darkMode: boolean; + ngOnInit() { + this.ui.darkModeState.subscribe((isDark) => { + this.darkMode = isDark; + }); + this.instanceForm = new FormGroup({}); + this.formatNoTypePolicyJson(); + } - private fetchRics() { - console.log('fetchRics ' + this.policyTypeName); - const self: PolicyInstanceDialogComponent = this; - this.dataService.getRics(this.policyTypeName).subscribe( - { - next(value: Rics) { - self.allRics = value.rics; - console.log(value); - } - }); - } + // Do not remove! Needed to avoid "Expression has changed after it was checked" warning + ngAfterViewInit() { + this.cdr.detectChanges(); + } - constructor( - private cdr: ChangeDetectorRef, - private dataService: PolicyService, - private errorService: ErrorDialogService, - private notificationService: NotificationService, - @Inject(MAT_DIALOG_DATA) public data, - private dialogRef: MatDialogRef, - private ui: UiService) { - this.policyInstanceId = data.instanceId; - this.policyTypeName = data.name; - this.jsonSchemaObject = data.createSchema; - this.ric = data.ric; + private formatNoTypePolicyJson() { + if (!this.typeHasSchema()) { + if (this.policyJson) { + this.policyJson = formatJsonString(this.policyJson); + } else { + this.policyJson = "{}"; + } } + } - ngOnInit() { - this.ui.darkModeState.subscribe((isDark) => { - this.darkMode = isDark; - }); - this.instanceForm = new FormGroup({ - 'ricSelector': new FormControl(this.ric, [ - Validators.required - ]) - }); - if (!this.policyInstanceId) { - this.fetchRics(); - } + onSubmit() { + if (this.policyInstanceId == null) { + this.policyInstanceId = uuid.v4(); } - - ngAfterViewInit() { - this.cdr.detectChanges(); + const self: PolicyInstanceDialogComponent = this; + let policyData: string; + if (this.typeHasSchema()) { + policyData = this.typedPolicyEditor.prettyLiveFormData; + } else { + policyData = this.noTypePolicyEditor.policyJsonTextArea.value; } + let createPolicyInstance: CreatePolicyInstance = this.createPolicyInstance( + policyData + ); + this.policySvc.putPolicy(createPolicyInstance).subscribe({ + next(_) { + self.notificationService.success( + "Policy without type:" + self.policyInstanceId + " submitted" + ); + self.dialogRef.close(); + }, + error(error: HttpErrorResponse) { + self.errorService.displayError("Submit failed: " + error.error); + }, + complete() {}, + }); + } - get ricSelector() { return this.instanceForm.get('ricSelector'); } - - onSubmit() { - if (this.policyInstanceId == null) { - this.policyInstanceId = uuid.v4(); - } - const policyJson: string = this.policyEditor.prettyLiveFormData; - const self: PolicyInstanceDialogComponent = this; - let createPolicyInstance: CreatePolicyInstance = this.createPolicyInstance(policyJson); - this.dataService.putPolicy(createPolicyInstance).subscribe( - { - next(_) { - self.notificationService.success('Policy ' + self.policyTypeName + ':' + self.policyInstanceId + - ' submitted'); - self.dialogRef.close(); - }, - error(error: HttpErrorResponse) { - self.errorService.displayError('Submit failed: ' + error.error); - }, - complete() { } - }); - } + typeHasSchema(): boolean { + return this.jsonSchemaObject !== "{}"; + } - private createPolicyInstance(policyJson: string) { - let createPolicyInstance = {} as CreatePolicyInstance; - createPolicyInstance.policy_data = JSON.parse(policyJson); - createPolicyInstance.policy_id = this.policyInstanceId; - createPolicyInstance.policytype_id = this.policyTypeName; - createPolicyInstance.ric_id = (!this.ricSelector.value.ric_id) ? this.ric : this.ricSelector.value.ric_id; - createPolicyInstance.service_id = 'controlpanel'; - return createPolicyInstance; + isFormValid(): boolean { + let isValid: boolean = this.instanceForm.valid; + if (this.typeHasSchema()) { + isValid = + isValid && this.typedPolicyEditor + ? this.typedPolicyEditor.formIsValid + : false; } + return isValid; + } - onClose() { - this.dialogRef.close(); - } - - get isJsonFormValid(): boolean { - return this.policyEditor ? this.policyEditor.formIsValid : false; - } + private createPolicyInstance(policyJson: string): CreatePolicyInstance { + let createPolicyInstance = {} as CreatePolicyInstance; + createPolicyInstance.policy_data = JSON.parse(policyJson); + createPolicyInstance.policy_id = this.policyInstanceId; + createPolicyInstance.policytype_id = ""; + createPolicyInstance.ric_id = this.ricSelector + ? this.ricSelector.selectedRic + : this.ric; + createPolicyInstance.service_id = "controlpanel"; + return createPolicyInstance; + } } -export function getPolicyDialogProperties(policyTypeSchema: PolicyTypeSchema, instance: PolicyInstance, darkMode: boolean): MatDialogConfig { - const createSchema = policyTypeSchema.schemaObject; - const instanceId = instance ? instance.policy_id : null; - const instanceJson = instance ? instance.policy_data : null; - const name = policyTypeSchema.name; - const ric = instance ? instance.ric_id : null; - return { - maxWidth: '1200px', - maxHeight: '900px', - width: '900px', - role: 'dialog', - disableClose: false, - panelClass: darkMode ? 'dark-theme' : '', - data: { - createSchema, - instanceId, - instanceJson, - name, - ric - } - }; +export function getPolicyDialogProperties( + policyTypeSchema: PolicyTypeSchema, + instance: PolicyInstance, + darkMode: boolean +): MatDialogConfig { + const createSchema = policyTypeSchema.schemaObject; + const instanceId = instance ? instance.policy_id : null; + const instanceJson = instance ? instance.policy_data : null; + const name = policyTypeSchema.name; + const ric = instance ? instance.ric_id : null; + return { + maxWidth: "1200px", + maxHeight: "900px", + width: "900px", + role: "dialog", + disableClose: false, + panelClass: darkMode ? "dark-theme" : "", + data: { + createSchema, + instanceId, + instanceJson, + name, + ric, + }, + }; } - diff --git a/webapp-frontend/src/app/policy/policy-instance/policy-instance.component.ts b/webapp-frontend/src/app/policy/policy-instance/policy-instance.component.ts index 95313d0..0f34c43 100644 --- a/webapp-frontend/src/app/policy/policy-instance/policy-instance.component.ts +++ b/webapp-frontend/src/app/policy/policy-instance/policy-instance.component.ts @@ -28,7 +28,6 @@ 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/no-type-policy-instance-dialog.component'; import { PolicyInstanceDialogComponent } from '../policy-instance-dialog/policy-instance-dialog.component'; import { getPolicyDialogProperties } from '../policy-instance-dialog/policy-instance-dialog.component'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; @@ -84,24 +83,13 @@ export class PolicyInstanceComponent implements OnInit, AfterViewInit { this.policySvc.getPolicyInstance(instance.policy_id).subscribe( (refreshedJson: any) => { instance = refreshedJson; - if (this.isSchemaEmpty()) { - this.dialog.open( - NoTypePolicyInstanceDialogComponent, - getPolicyDialogProperties(this.policyTypeSchema, instance, this.darkMode)).afterClosed().subscribe( - (_: any) => { - this.instanceDataSource.getPolicyInstances(); - } - ); - } else { - this.dialog.open( - PolicyInstanceDialogComponent, - getPolicyDialogProperties(this.policyTypeSchema, instance, this.darkMode)).afterClosed().subscribe( - (_: any) => { - this.instanceDataSource.getPolicyInstances(); - } - ); - - } + this.dialog.open( + PolicyInstanceDialogComponent, + getPolicyDialogProperties(this.policyTypeSchema, instance, this.darkMode)).afterClosed().subscribe( + (_: any) => { + this.instanceDataSource.getPolicyInstances(); + } + ); }, (httpError: HttpErrorResponse) => { this.notificationService.error('Could not refresh instance. Please try again.' + httpError.message); diff --git a/webapp-frontend/src/app/policy/policy.module.ts b/webapp-frontend/src/app/policy/policy.module.ts index 5f7e4fc..d4a4b1e 100644 --- a/webapp-frontend/src/app/policy/policy.module.ts +++ b/webapp-frontend/src/app/policy/policy.module.ts @@ -23,7 +23,6 @@ import { NgModule } from '@angular/core'; import { MatTableModule } from '@angular/material/table'; import { PolicyCardComponent } from './policy-card/policy-card.component'; import { PolicyControlComponent } from './policy-control.component'; -import { NoTypePolicyInstanceDialogComponent } from './no-type-policy-instance-dialog/no-type-policy-instance-dialog.component'; import { PolicyInstanceDialogComponent } from './policy-instance-dialog/policy-instance-dialog.component'; import { PolicyInstanceComponent } from './policy-instance/policy-instance.component'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @@ -52,8 +51,8 @@ import { MaterialDesignFrameworkModule } from 'angular6-json-schema-form'; 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'; +import { NoTypePolicyEditorComponent } from './no-type-policy-editor/no-type-policy-editor.component'; const routes:Routes = [ {path: 'policy', component: PolicyControlComponent} @@ -61,7 +60,6 @@ const routes:Routes = [ @NgModule({ declarations: [ - NoTypePolicyInstanceDialogComponent, PolicyCardComponent, PolicyControlComponent, PolicyInstanceComponent, diff --git a/webapp-frontend/src/app/policy/ric-selector/ric-selector.component.ts b/webapp-frontend/src/app/policy/ric-selector/ric-selector.component.ts index e0ba87e..996d9a0 100644 --- a/webapp-frontend/src/app/policy/ric-selector/ric-selector.component.ts +++ b/webapp-frontend/src/app/policy/ric-selector/ric-selector.component.ts @@ -18,57 +18,66 @@ // ========================LICENSE_END=================================== // / -import { Component, Input, OnInit } from '@angular/core'; -import { AbstractControl, ControlContainer, FormBuilder, FormControl, FormGroup, FormGroupDirective, Validators } from '@angular/forms'; -import { Rics } from 'src/app/interfaces/ric'; -import { PolicyService } from 'src/app/services/policy/policy.service'; +import { Component, Input, OnInit } from "@angular/core"; +import { + AbstractControl, + ControlContainer, + FormBuilder, + FormControl, + FormGroup, + FormGroupDirective, + Validators, +} from "@angular/forms"; +import { Rics } from "src/app/interfaces/ric"; +import { PolicyService } from "src/app/services/policy/policy.service"; @Component({ - selector: 'nrcp-ric-selector', - templateUrl: './ric-selector.component.html', - styleUrls: ['./ric-selector.component.scss'], - viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }] - + selector: "nrcp-ric-selector", + templateUrl: "./ric-selector.component.html", + styleUrls: ["./ric-selector.component.scss"], + viewProviders: [ + { provide: ControlContainer, useExisting: FormGroupDirective }, + ], }) export class RicSelectorComponent implements OnInit { - @Input() instanceForm: FormGroup; - @Input() policyTypeName: string = ''; + @Input() policyTypeName: string = ""; ric: string; allRics: string[] = []; constructor( private dataService: PolicyService, - private formBuilder: FormBuilder) { - } + private formBuilder: FormBuilder + ) {} ngOnInit(): void { this.instanceForm.addControl( - 'ricSelector', new FormControl(this.ric, [ - Validators.required - ])); + "ricSelector", + new FormControl(this.ric, [Validators.required]) + ); - console.log('Ric:', this.ric); + console.log("Ric:", this.ric); this.fetchRics(); } - get selectedRic(): string { return this.ric; } + get selectedRic(): string { + return this.ric; + } get ricSelector(): AbstractControl { - return this.instanceForm.get('ricSelector'); + return this.instanceForm.get("ricSelector"); } private fetchRics() { - console.log('fetchRics ', this.policyTypeName); + console.log("fetchRics ", this.policyTypeName); const self: RicSelectorComponent = this; - this.dataService.getRics(this.policyTypeName).subscribe( - { - next(value: Rics) { - value.rics.forEach(ric => { - self.allRics.push(ric.ric_id) - }); - console.log(value); - } - }); + this.dataService.getRics(this.policyTypeName).subscribe({ + next(value: Rics) { + value.rics.forEach((ric) => { + self.allRics.push(ric.ric_id); + }); + console.log(value); + }, + }); } } 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 index 9e6e1e4..6b8c233 100644 --- 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 @@ -20,25 +20,23 @@ --> -

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

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

-
-
{{jsonFormStatusMessage}}
- - +
-

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

-
+
@@ -53,11 +51,11 @@ Json
-

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

+{{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.spec.ts b/webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.spec.ts index 9f58af2..fc4a166 100644 --- 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 @@ -1,41 +1,174 @@ -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'; +// - +// ========================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 { TypedPolicyEditorComponent } from './typed-policy-editor.component'; +import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/compiler"; +import { Component } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { MatIconModule } from "@angular/material/icon"; +import { BrowserModule } from "@angular/platform-browser"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -describe('TypedPolicyEditorComponent', () => { - let component: TypedPolicyEditorComponent; - let fixture: ComponentFixture; +import { TypedPolicyEditorComponent } from "./typed-policy-editor.component"; - beforeEach(async(() => { +describe("TypedPolicyEditorComponent", () => { + let component: TestTypedPolicyEditorComponentHostComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { TestBed.configureTestingModule({ - imports: [ - BrowserAnimationsModule, - MatIconModule - ], + imports: [BrowserModule, BrowserAnimationsModule, MatIconModule], declarations: [ - TypedPolicyEditorComponent - ], - schemas: [ - CUSTOM_ELEMENTS_SCHEMA + TypedPolicyEditorComponent, + TestTypedPolicyEditorComponentHostComponent, ], - providers: [ - ChangeDetectorRef - ] - }) - .compileComponents(); - })); + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }).compileComponents(); + }); beforeEach(() => { - fixture = TestBed.createComponent(TypedPolicyEditorComponent); + fixture = TestBed.createComponent( + TestTypedPolicyEditorComponentHostComponent + ); component = fixture.componentInstance; fixture.detectChanges(); }); - it('should create', () => { + it("should create", () => { expect(component).toBeTruthy(); }); + + it("should have JSON form visible and JSON and JSON Schema not visible", () => { + let propertiesHeading = fixture.debugElement.nativeElement.querySelector( + "#propertiesHeading" + ); + expect(propertiesHeading).toBeTruthy(); + expect(propertiesHeading.innerText).toContain("Properties"); + let propertiesIcon = fixture.debugElement.nativeElement.querySelector( + "#propertiesIcon" + ); + expect(propertiesIcon).toBeTruthy(); + expect(propertiesIcon.innerText).toEqual("expand_less"); + let jsonForm = fixture.debugElement.nativeElement.querySelector( + "json-schema-form" + ); + expect(jsonForm).toBeTruthy(); + + let jsonHeading = fixture.debugElement.nativeElement.querySelector( + "#jsonHeading" + ); + expect(jsonHeading).toBeTruthy(); + expect(jsonHeading.innerText).toContain("JSON"); + let jsonIcon = fixture.debugElement.nativeElement.querySelector( + "#jsonIcon" + ); + expect(jsonIcon).toBeTruthy(); + expect(jsonIcon.innerText).toEqual("expand_more"); + let jsonDiv = fixture.debugElement.nativeElement.querySelector("#jsonDiv"); + expect(jsonDiv).toBeFalsy(); + + let schemaHeading = fixture.debugElement.nativeElement.querySelector( + "#schemaHeading" + ); + expect(schemaHeading).toBeTruthy(); + expect(schemaHeading.innerText).toContain("JSON Schema"); + let schemaIcon = fixture.debugElement.nativeElement.querySelector( + "#schemaIcon" + ); + expect(schemaIcon).toBeTruthy(); + expect(schemaIcon.innerText).toEqual("expand_more"); + let schemaDiv = fixture.debugElement.nativeElement.querySelector( + "#schemaDiv" + ); + expect(schemaDiv).toBeFalsy(); + }); + + it("should hide JSON form", () => { + let propertiesHeading = fixture.debugElement.nativeElement.querySelector( + "#propertiesHeading" + ); + expect(propertiesHeading).toBeTruthy(); + propertiesHeading.click(); + fixture.detectChanges(); + + let propertiesIcon = fixture.debugElement.nativeElement.querySelector( + "#propertiesIcon" + ); + expect(propertiesIcon).toBeTruthy(); + expect(propertiesIcon.innerText).toEqual("expand_more"); + let propertiesDiv = fixture.debugElement.nativeElement.querySelector( + "propertiesDiv" + ); + expect(propertiesDiv).toBeFalsy(); + }); + + it("should show JSON with text for dark mode", () => { + let jsonHeading = fixture.debugElement.nativeElement.querySelector( + "#jsonHeading" + ); + expect(jsonHeading).toBeTruthy(); + jsonHeading.click(); + fixture.detectChanges(); + + let jsonIcon = fixture.debugElement.nativeElement.querySelector( + "#jsonIcon" + ); + expect(jsonIcon).toBeTruthy(); + expect(jsonIcon.innerText).toEqual("expand_less"); + let jsonDiv = fixture.debugElement.nativeElement.querySelector("#jsonDiv"); + expect(jsonDiv).toBeTruthy(); + let jsonText = jsonDiv.querySelector("pre"); + expect(jsonText.classList).toContain("text__dark"); + }); + + it("should show JSON Schema with text for dark mode", () => { + let schemaHeading = fixture.debugElement.nativeElement.querySelector( + "#schemaHeading" + ); + expect(schemaHeading).toBeTruthy(); + schemaHeading.click(); + fixture.detectChanges(); + + let schemaIcon = fixture.debugElement.nativeElement.querySelector( + "#schemaIcon" + ); + expect(schemaIcon).toBeTruthy(); + expect(schemaIcon.innerText).toEqual("expand_less"); + let schemaDiv = fixture.debugElement.nativeElement.querySelector( + "#schemaDiv" + ); + expect(schemaDiv).toBeTruthy(); + let jsonSchemaText = schemaDiv.querySelector("pre"); + expect(jsonSchemaText.classList).toContain("text__dark"); + }); + + @Component({ + selector: `typed-policy-editor-host-component`, + template: ``, + }) + class TestTypedPolicyEditorComponentHostComponent { + policyJson: string = '{"A":"A"}'; + jsonSchemaObject: string = + 'policy_schema": { "description": "Type 1 policy type", "title": "1", "type": "object", "properties": { "A": "string" }, "required": [ "A" ]}'; + } }); 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 index d05c78f..f9651a4 100644 --- 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 @@ -19,110 +19,98 @@ // import { animate, state, style, transition, trigger } from '@angular/animations'; -import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { 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 })), - ]), - ]), - ], - + 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); + 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, + }; + liveFormData: any = {}; + formIsValid: boolean = false; + formValidationErrors: any; + + constructor() {} + + ngOnInit(): void {} + + 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; } - - 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); - } + 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}`; } - return errorArray.join('
'); + errorArray.push(`${field}: ${message}`); + } else { + errorArray.push(message); + } } + return errorArray.join("
"); + } - public toggleVisible(item: string) { - this.isVisible[item] = !this.isVisible[item]; - } + public toggleVisible(item: string) { + this.isVisible[item] = !this.isVisible[item]; + } }