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 @@
},
{
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 @@
BrowserAnimationsModule,
MatButtonModule,
MatFormFieldModule,
- MatInputModule
+ MatInputModule,
],
- schemas: [
- CUSTOM_ELEMENTS_SCHEMA
- ],
+ 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: `<nrcp-no-type-policy-editor [policyJson]="this.policyJson" [instanceForm]="instanceForm"></nrcp-no-type-policy-editor>`
+ template: `<nrcp-no-type-policy-editor
+ [policyJson]="this.policyJson"
+ [instanceForm]="instanceForm"
+ ></nrcp-no-type-policy-editor>`,
})
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 @@
-<!--
- ========================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===================================
- -->
-
-<div class="text-muted logo" fxLayout="row" fxLayoutGap="50px" fxLayoutAlign="space-around center">
- <div id="instanceInfo" *ngIf="policyInstanceId">[{{this.ric}}] Instance ID: {{policyInstanceId}}</div>
-</div>
-<div class="mat-elevation-z8 header row" [ngClass]="{'header-dark': darkMode}">
- <div class="logo">
- <img src="assets/oran-logo.png" width="30px" height="30px" style="position: relative; z-index: 50" />
- <svg class="logo__icon" viewBox="150.3 22.2 1000 50">
- <text class="logo__text" [ngClass]="{'logo__text-dark': darkMode}" font-size="30" font-weight="600"
- letter-spacing=".1em" transform="translate(149 56)">
- <tspan *ngIf="!this.policyInstanceId">Create new policy instance of < No type ></tspan>
- </text>
- </svg>
- </div>
-</div>
-
-<form [formGroup]="instanceForm" fxLayout="row" fxLayoutAlign="space-around start" fxLayout.lt-sm="column"
- fxLayoutAlign.lt-sm="flex-start center">
-
- <mat-card class="card" [ngClass]="{'card-dark': darkMode}">
- <nrcp-ric-selector *ngIf="!this.policyInstanceId" [instanceForm]="instanceForm"></nrcp-ric-selector>
- <h4>
- Properties
- </h4>
- <nrcp-no-type-policy-editor [policyJson]="this.policyJson" [instanceForm]="instanceForm"></nrcp-no-type-policy-editor>
- <div mat-dialog-actions>
- <button id="closeButton" mat-raised-button [mat-dialog-close]="false">Close</button>
- <button id="submitButton" mat-raised-button (click)="this.onSubmit()" class="submitBtn" [disabled]="!instanceForm.valid">
- Submit
- </button>
- </div>
- </mat-card>
-</form>
\ 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<NoTypePolicyInstanceDialogComponent>;
- let loader: HarnessLoader;
- let policyServiceSpy: jasmine.SpyObj<PolicyService>;
- let errDialogServiceSpy: jasmine.SpyObj<ErrorDialogService>;
-
- 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<NoTypePolicyInstanceDialogComponent>, 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<NoTypePolicyInstanceDialogComponent>,
- 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 { 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 @@
}
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 @@
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 @@
-->
<div class="text-muted logo" fxLayout="row" fxLayoutGap="50px" fxLayoutAlign="space-around center">
- <div *ngIf="policyInstanceId">[{{ric}}] Instance ID: {{policyInstanceId}}</div>
+ <div id="instanceInfo" *ngIf="policyInstanceId">[{{this.ric}}] Instance ID: {{policyInstanceId}}</div>
</div>
<div class="mat-elevation-z8 header row" [ngClass]="{'header-dark': darkMode}">
<div class="logo">
@@ -27,9 +27,9 @@
<svg class="logo__icon" viewBox="150.3 22.2 1000 50">
<text class="logo__text" [ngClass]="{'logo__text-dark': darkMode}" font-size="30" font-weight="600"
letter-spacing=".1em" transform="translate(149 56)">
- <tspan *ngIf="!policyInstanceId">Create new policy instance of type </tspan>
- <tspan *ngIf="jsonSchemaObject.title"> {{jsonSchemaObject.title}}</tspan>
- <tspan *ngIf="!jsonSchemaObject.title"> {{policyTypeName}}</tspan>
+ <tspan *ngIf="!this.policyInstanceId">Create new policy instance of type </tspan>
+ <tspan *ngIf="policyTypeName">{{policyTypeName}}</tspan>
+ <tspan *ngIf="!policyTypeName">< No Type ></tspan>
</text>
</svg>
</div>
@@ -37,30 +37,21 @@
<div class="text-muted" *ngIf="jsonSchemaObject.description">{{jsonSchemaObject.description}}</div>
-<div [formGroup]="instanceForm" fxLayout="row" fxLayoutAlign="space-around start" fxLayout.lt-sm="column"
+<form [formGroup]="instanceForm" fxLayout="row" fxLayoutAlign="space-around start" fxLayout.lt-sm="column"
fxLayoutAlign.lt-sm="flex-start center">
<mat-card class="card" [ngClass]="{'card-dark': darkMode}">
- <mat-form-field *ngIf="!policyInstanceId" appearance="fill">
- <mat-select id="ricSelector" formControlName="ricSelector" matInput required [(value)]="ric"
- placeholder="Target"
- matTooltip="Element where the policy instance resides, e.g. a gNodeB or Near-RT RIC">
- <mat-option *ngFor="let ric of allRics" [value]="ric">
- {{ric.ric_id}}
- </mat-option>
- </mat-select>
- <div *ngIf="ricSelector.invalid && (ricSelector.dirty || ricSelector.touched)">
- <div *ngIf="ricSelector.errors.required">
- <mat-error role="alert">This field is required.</mat-error>
- </div>
- </div>
- </mat-form-field>
-
- <nrcp-typed-policy-editor [jsonSchemaObject]="jsonSchemaObject" [jsonObject]="data.instanceJson" [darkMode]="darkMode"></nrcp-typed-policy-editor>
- <hr />
- <button mat-raised-button (click)="onClose()">Close</button>
- <button mat-raised-button (click)="onSubmit()" [disabled]="!isJsonFormValid || !ric"
- class="submitBtn">Submit</button>
- <hr />
+ <nrcp-ric-selector *ngIf="!policyInstanceId" [instanceForm]="instanceForm" [policyTypeName]="policyTypeName"></nrcp-ric-selector>
+ <h4 *ngIf="!typeHasSchema()">
+ Properties
+ </h4>
+ <nrcp-no-type-policy-editor *ngIf="!typeHasSchema()" [instanceForm]="instanceForm" [policyJson]="policyJson"></nrcp-no-type-policy-editor>
+ <nrcp-typed-policy-editor *ngIf="typeHasSchema()" [jsonSchemaObject]="jsonSchemaObject" [jsonObject]="policyJson" [darkMode]="darkMode"></nrcp-typed-policy-editor>
+ <div mat-dialog-actions>
+ <button id="closeButton" mat-raised-button [mat-dialog-close]="false">Close</button>
+ <button id="submitButton" mat-raised-button (click)="onSubmit()" class="submitBtn" [disabled]="!isFormValid()">
+ Submit
+ </button>
+ </div>
</mat-card>
-</div>
\ No newline at end of file
+</form>
\ 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<PolicyInstanceDialogComponent>;
+ let loader: HarnessLoader;
+ let policyServiceSpy: jasmine.SpyObj<PolicyService>;
+ let errDialogServiceSpy: jasmine.SpyObj<ErrorDialogService>;
+
+ 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<PolicyInstanceDialogComponent>,
+ 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<PolicyInstanceDialogComponent>,
+ 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();
+ }
+
+ private formatNoTypePolicyJson() {
+ if (!this.typeHasSchema()) {
+ if (this.policyJson) {
+ this.policyJson = formatJsonString(this.policyJson);
+ } else {
+ this.policyJson = "{}";
+ }
}
+ }
- constructor(
- private cdr: ChangeDetectorRef,
- private dataService: PolicyService,
- private errorService: ErrorDialogService,
- private notificationService: NotificationService,
- @Inject(MAT_DIALOG_DATA) public data,
- private dialogRef: MatDialogRef<PolicyInstanceDialogComponent>,
- private ui: UiService) {
- this.policyInstanceId = data.instanceId;
- this.policyTypeName = data.name;
- this.jsonSchemaObject = data.createSchema;
- this.ric = data.ric;
+ onSubmit() {
+ if (this.policyInstanceId == null) {
+ this.policyInstanceId = uuid.v4();
}
-
- 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();
- }
+ 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() {},
+ });
+ }
- ngAfterViewInit() {
- this.cdr.detectChanges();
+ typeHasSchema(): boolean {
+ return this.jsonSchemaObject !== "{}";
+ }
+
+ isFormValid(): boolean {
+ let isValid: boolean = this.instanceForm.valid;
+ if (this.typeHasSchema()) {
+ isValid =
+ isValid && this.typedPolicyEditor
+ ? this.typedPolicyEditor.formIsValid
+ : false;
}
+ return isValid;
+ }
- 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() { }
- });
- }
-
- 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;
- }
-
- 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 { 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 @@
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 { 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 { 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 @@
@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 @@
-->
-<h4 class="default-cursor" (click)="toggleVisible('form')">
- <mat-icon matTooltip="Properties">{{isVisible.form ? 'expand_less' : 'expand_more'}}</mat-icon>
+<h4 id="propertiesHeading" class="default-cursor" (click)="toggleVisible('form')">
+ <mat-icon id="propertiesIcon" matTooltip="Properties">{{isVisible.form ? 'expand_less' : 'expand_more'}}</mat-icon>
Properties
</h4>
-<div *ngIf="isVisible.form" class="json-schema-form" [@expandSection]="true">
- <div *ngIf="!formActive">{{jsonFormStatusMessage}}</div>
-
- <json-schema-form id="json-schema-form" *ngIf="formActive" [form]="jsonSchemaObject"
+<div id="propertiesDiv" *ngIf="isVisible.form" class="json-schema-form" [@expandSection]="true">
+ <json-schema-form id="json-schema-form" [form]="jsonSchemaObject"
[(data)]="jsonObject" [options]="jsonFormOptions" [framework]="'material-design'" [language]="'en'"
(onChanges)="onChanges($event)" (isValid)="isValid($event)"
(validationErrors)="validationErrors($event)">
</json-schema-form>
</div>
-<h4 [class.text-danger]="!formIsValid && !isVisible.json" [class.default-cursor]="formIsValid || isVisible.json"
+<h4 id="jsonHeading" [class.text-danger]="!formIsValid && !isVisible.json" [class.default-cursor]="formIsValid || isVisible.json"
(click)="toggleVisible('json')">
-<mat-icon matTooltip="Json">{{isVisible.json ? 'expand_less' : 'expand_more'}}</mat-icon>
-Json
+<mat-icon id="jsonIcon" matTooltip="Json">{{isVisible.json ? 'expand_less' : 'expand_more'}}</mat-icon>
+JSON
</h4>
-<div *ngIf="isVisible.json" fxLayout="column" [@expandSection]="true">
+<div id="jsonDiv" *ngIf="isVisible.json" fxLayout="column" [@expandSection]="true">
<div>
<strong *ngIf="formIsValid || prettyValidationErrors" [class.text-muted]="formIsValid"
[class.text-danger]="!formIsValid">
@@ -53,11 +51,11 @@
</div>
</div>
-<h4 class="default-cursor" (click)="toggleVisible('schema')">
-<mat-icon matTooltip="Json Schema">{{isVisible.schema ? 'expand_less' : 'expand_more'}}</mat-icon>
-Json Schema
+<h4 id="schemaHeading" class="default-cursor" (click)="toggleVisible('schema')">
+<mat-icon id="schemaIcon" matTooltip="Json Schema">{{isVisible.schema ? 'expand_less' : 'expand_more'}}</mat-icon>
+JSON Schema
</h4>
-<div *ngIf="isVisible.schema" fxLayout="column" [@expandSection]="true">
+<div id="schemaDiv" *ngIf="isVisible.schema" fxLayout="column" [@expandSection]="true">
<strong class="text-muted">Schema</strong>
<pre [class.text__dark]="darkMode">{{schemaAsString}}</pre>
</div>
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<TypedPolicyEditorComponent>;
+import { TypedPolicyEditorComponent } from "./typed-policy-editor.component";
- beforeEach(async(() => {
+describe("TypedPolicyEditorComponent", () => {
+ let component: TestTypedPolicyEditorComponentHostComponent;
+ let fixture: ComponentFixture<TestTypedPolicyEditorComponentHostComponent>;
+
+ beforeEach(async () => {
TestBed.configureTestingModule({
- imports: [
- BrowserAnimationsModule,
- MatIconModule
- ],
+ imports: [BrowserModule, BrowserAnimationsModule, MatIconModule],
declarations: [
- TypedPolicyEditorComponent
+ TypedPolicyEditorComponent,
+ TestTypedPolicyEditorComponentHostComponent,
],
- schemas: [
- CUSTOM_ELEMENTS_SCHEMA
- ],
- 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: `<nrcp-typed-policy-editor
+ [jsonObject]="policyJson"
+ [jsonSchemaObject]="jsonSchemaObject"
+ [darkMode]="true"
+ ></nrcp-typed-policy-editor>`,
+ })
+ 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
- };
+ 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;
+ @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;
+ isVisible = {
+ form: true,
+ json: false,
+ schema: false,
+ };
+ liveFormData: any = {};
+ formIsValid: boolean = false;
+ formValidationErrors: any;
- constructor(
- private cdr: ChangeDetectorRef) {
- this.formActive = false;
+ 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;
}
-
- 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);
- }
+ 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('<br>');
+ errorArray.push(`${field}: ${message}`);
+ } else {
+ errorArray.push(message);
+ }
}
+ return errorArray.join("<br>");
+ }
- public toggleVisible(item: string) {
- this.isVisible[item] = !this.isVisible[item];
- }
+ public toggleVisible(item: string) {
+ this.isVisible[item] = !this.isVisible[item];
+ }
}