Merge "First version of policy editor components"
diff --git a/webapp-frontend/src/app/interceptor.mock.ts b/webapp-frontend/src/app/interceptor.mock.ts
index 72423ca..29bcf6a 100644
--- a/webapp-frontend/src/app/interceptor.mock.ts
+++ b/webapp-frontend/src/app/interceptor.mock.ts
@@ -140,7 +140,7 @@
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (request.method === "PUT" && request.url.includes("policies")) {
- console.log('Answered PUT policy ' + request.url);
+ console.log('Answered PUT policy ', request.url, request.body);
return of(new HttpResponse({ status: 200 }));
}
for (const element of urls) {
diff --git a/webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.html b/webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.html
new file mode 100644
index 0000000..6fe3ca9
--- /dev/null
+++ b/webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.html
@@ -0,0 +1,21 @@
+<mat-form-field style="width: 800px;" appearance="fill">
+ <textarea id="policyJsonTextArea" formControlName="policyJsonTextArea" matInput cdkTextareaAutosize
+ cdkAutosizeMinRows="10" required [(value)]="this.policyJson" placeholder="Policy properties"
+ matTooltip="The properties of the policy instance, in JSON format" matTooltipPosition="before">
+ </textarea>
+ <div *ngIf="policyJsonTextArea.invalid && (policyJsonTextArea.dirty || policyJsonTextArea.touched)">
+ <div *ngIf="policyJsonTextArea.errors.required">
+ <mat-error role="alert">
+ This field is required.
+ </mat-error>
+ </div>
+ <div *ngIf="policyJsonTextArea.errors.invalidJson">
+ <mat-error role="alert">
+ The policy properties must be a valid JSON.
+ </mat-error>
+ </div>
+ </div>
+ <button id="formatButton" (click)="formatJsonInput();" mat-raised-button [disabled]="!policyJsonTextArea.valid">
+ Format JSON
+ </button>
+</mat-form-field>
diff --git a/webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.scss b/webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.scss
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
new file mode 100644
index 0000000..b0700fd
--- /dev/null
+++ b/webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.spec.ts
@@ -0,0 +1,82 @@
+import { HarnessLoader } from '@angular/cdk/testing';
+import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
+import { Component, ViewChild } from '@angular/core';
+import { async, 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 { MatInput, MatInputModule } from '@angular/material/input';
+import { MatInputHarness } from '@angular/material/input/testing';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+
+import { NoTypePolicyEditorComponent } from './no-type-policy-editor.component';
+
+let formGroup: FormGroup = new FormGroup({});
+
+describe('NoTypePolicyEditorComponent', () => {
+ let component: TestNoTypePolicyEditorComponentHostComponent;
+ let fixture: ComponentFixture<TestNoTypePolicyEditorComponentHostComponent>;
+ let loader: HarnessLoader;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ BrowserAnimationsModule,
+ MatButtonModule,
+ MatInputModule
+ ],
+ declarations: [
+ NoTypePolicyEditorComponent,
+ TestNoTypePolicyEditorComponentHostComponent
+ ],
+ providers: [
+ FormBuilder
+ ]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(TestNoTypePolicyEditorComponentHostComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ loader = TestbedHarnessEnvironment.loader(fixture);
+ }));
+
+ 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' }));
+
+ 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' }));
+ expect(await textArea.getValue()).toEqual('{"A":"A"}');
+
+ console.log('Validity:',formGroup.valid);
+ let formatButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({ selector: '#formatButton' }));
+ expect(await formatButton.isDisabled()).toBeFalsy();
+ });
+
+ it('Format button should be disabled wen json not valid', async () => {
+ let textArea: MatInputHarness = await loader.getHarness(MatInputHarness.with({ selector: '#policyJsonTextArea' }));
+ await textArea.setValue('{');
+
+ let formatButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({ selector: '#formatButton' }));
+ // expect(await formatButton.isDisabled()).toBeTruthy();
+ });
+});
+
+@Component({
+ selector: `no-type-policy-editor-host-component`,
+ template: `<nrcp-no-type-policy-editor [policyJson]="this.policyJson" [instanceForm]="instanceForm"></nrcp-no-type-policy-editor>`
+})
+export class TestNoTypePolicyEditorComponentHostComponent {
+ @ViewChild(NoTypePolicyEditorComponent)
+ private noTypePolicyEditorComponentHostComponent: NoTypePolicyEditorComponent;
+ instanceForm: FormGroup = formGroup;
+ policyJson: string = '{"A":"A"}';
+}
diff --git a/webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.ts b/webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.ts
new file mode 100644
index 0000000..a4ce36f
--- /dev/null
+++ b/webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.ts
@@ -0,0 +1,58 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { AbstractControl, ControlContainer, FormBuilder, FormControl, FormGroup, FormGroupDirective, ValidatorFn, Validators } from '@angular/forms';
+
+@Component({
+ selector: 'nrcp-no-type-policy-editor',
+ templateUrl: './no-type-policy-editor.component.html',
+ styleUrls: ['./no-type-policy-editor.component.scss'],
+ viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }]
+})
+export class NoTypePolicyEditorComponent implements OnInit {
+
+ @Input() instanceForm: FormGroup;
+ @Input() policyJson: string;
+
+ constructor(
+ private formBuilder: FormBuilder) { }
+
+ ngOnInit(): void {
+ this.instanceForm.addControl(
+ 'policyJsonTextArea', new FormControl(this.policyJson, [
+ Validators.required,
+ jsonValidator()
+ ])
+ )
+ }
+
+ get policyJsonTextArea(): AbstractControl {
+ return this.instanceForm ? this.instanceForm.get('policyJsonTextArea') : null;
+ }
+
+ formatJsonInput(): void {
+ this.policyJson = formatJsonString(JSON.parse(this.policyJsonTextArea.value));
+ }
+}
+
+export function formatJsonString(jsonToFormat: any): string {
+ return JSON.stringify(jsonToFormat, null, 2);
+}
+
+export function jsonValidator(): ValidatorFn {
+ return (control: AbstractControl): { [key: string]: any } | null => {
+ const notValid = !isJsonValid(control.value);
+ return notValid ? { 'invalidJson': { value: control.value } } : null;
+ };
+}
+
+export function isJsonValid(json: string): boolean {
+ try {
+ if (json != null) {
+ JSON.parse(json);
+ return true;
+ } else {
+ return false;
+ }
+ } catch (jsonError) {
+ return false;
+ }
+}
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
index a24264f..df45cc2 100644
--- 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
@@ -33,56 +33,20 @@
</div>
</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="!this.policyInstanceId" appearance="fill">
- <mat-select id="ricSelector" formControlName="ricSelector" matInput required [value]="this.ric"
- placeholder="Target"
- matTooltip="Element where the policy instance resides, e.g. a gNodeB or Near-RT RIC">
- <mat-option *ngFor="let ric of this.allRicIds" [value]="ric">
- {{ric}}
- </mat-option>
- </mat-select>
- <div *ngIf="ricSelector.invalid && (ricSelector.dirty || ricSelector.touched)" class="alert mat-error"
- style="width: 300px;">
- <div *ngIf="ricSelector.errors.required">
- <mat-error role="alert">
- This field is required.
- </mat-error>
- </div>
- </div>
- </mat-form-field>
+ <nrcp-ric-selector *ngIf="!this.policyInstanceId" [policyInstanceId]="this.policyInstanceId" [instanceForm]="instanceForm"></nrcp-ric-selector>
<h4>
Properties
</h4>
- <mat-form-field style="width: 800px;" appearance="fill">
- <textarea id="policyJsonTextArea" formControlName="policyJsonTextArea" matInput cdkTextareaAutosize
- cdkAutosizeMinRows="10" required [(value)]="this.policyJson" placeholder="Policy properties"
- matTooltip="The properties of the policy instance, in JSON format" matTooltipPosition="before">
- </textarea>
- <div *ngIf="policyJsonTextArea.invalid && (policyJsonTextArea.dirty || policyJsonTextArea.touched)">
- <div *ngIf="policyJsonTextArea.errors.required">
- <mat-error role="alert">
- This field is required.
- </mat-error>
- </div>
- <div *ngIf="policyJsonTextArea.errors.invalidJson">
- <mat-error role="alert">
- The policy properties must be a valid JSON.
- </mat-error>
- </div>
- </div>
- </mat-form-field>
+ <nrcp-no-type-policy-editor [policyJson]="this.policyJson" [instanceForm]="instanceForm"></nrcp-no-type-policy-editor>
<div mat-dialog-actions>
- <button id="formatButton" (click)="formatJsonInput();" mat-raised-button [disabled]="!policyJsonTextArea.valid">
- Format JSON
- </button>
<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>
-</div>
\ No newline at end of file
+</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.spec.ts b/webapp-frontend/src/app/policy/no-type-policy-instance-dialog/no-type-policy-instance-dialog.component.spec.ts
index 80f5e78..2e62f57 100644
--- 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
@@ -29,7 +29,7 @@
import { MatInputModule } from '@angular/material/input';
import { MatInputHarness } from '@angular/material/input/testing';
import { of } from "rxjs/observable/of";
-import { ReactiveFormsModule } from "@angular/forms";
+import { FormControl, ReactiveFormsModule } from "@angular/forms";
import { TestbedHarnessEnvironment } from "@angular/cdk/testing/testbed";
import { ToastrModule } from "ngx-toastr";
@@ -37,7 +37,8 @@
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 { Ric } from "../../interfaces/ric";
+import { RicSelectorComponent } from "../ric-selector/ric-selector.component";
+import { NoTypePolicyEditorComponent } from "../no-type-policy-editor/no-type-policy-editor.component";
describe('NoTypePolicyInstanceDialogComponent', () => {
let component: NoTypePolicyInstanceDialogComponent;
@@ -45,15 +46,11 @@
let loader: HarnessLoader;
let policyServiceSpy: jasmine.SpyObj<PolicyService>;
let errDialogServiceSpy: jasmine.SpyObj<ErrorDialogService>;
- const ric1: Ric = { ric_id: 'ric1', managed_element_ids: [ 'me1' ], policytype_ids: [ 'type1' ], state: ''};
- const ric2: Ric = { ric_id: 'ric2', managed_element_ids: [ 'me1' ], policytype_ids: [ 'type1' ], state: ''};
beforeEach(async () => {
- policyServiceSpy = jasmine.createSpyObj('PolicyService', [ 'putPolicy', 'getRics' ]);
+ policyServiceSpy = jasmine.createSpyObj('PolicyService', [ 'putPolicy' ]);
errDialogServiceSpy = jasmine.createSpyObj('ErrorDialogService', [ 'displayError' ]);
- policyServiceSpy.getRics.and.returnValue(of({ rics: [ ric1, ric2 ] }));
-
TestBed.configureTestingModule({
imports: [
BrowserAnimationsModule,
@@ -93,34 +90,25 @@
expect(ele).toBeFalsy();
});
- it('should contain enabled Target selection with no ric selected and json should be empty', async () => {
- let ricSelector: MatSelectHarness = await loader.getHarness(MatSelectHarness.with({selector: '#ricSelector'}));
-
- expect(await ricSelector.isEmpty()).toBeTruthy();
- expect(await ricSelector.isDisabled()).toBeFalsy();
- await ricSelector.open();
- const count = (await ricSelector.getOptions()).length;
- expect(count).toEqual(2);
-
- let jsonTextArea: MatInputHarness = await loader.getHarness(MatInputHarness.with({selector: '#policyJsonTextArea'}));
- expect(await jsonTextArea.isDisabled()).toBeFalsy();
- const actualJson: string = await jsonTextArea.getValue();
- expect(actualJson).toEqual('');
+ it('should contain ric select', async () => {
+ const ele = fixture.debugElement.nativeElement.querySelector('nrcp-ric-selector');
+ expect(ele).toBeTruthy();
});
- it('should contain disabled Format and Submit buttons and enabled Close button', async () => {
+ 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 formatButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({selector: '#formatButton'}));
- expect(await formatButton.isDisabled()).toBeTruthy();
- expect(await formatButton.getText()).toEqual('Format JSON');
-
- let closeButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({selector: '#closeButton'}));
+ 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.isDisabled()).toBeTruthy();
expect(await submitButton.getText()).toEqual('Submit');
});
});
@@ -150,21 +138,17 @@
expect(ele.innerText).toEqual('[ric1] Instance ID: instanceId');
});
- it('should contain json and no Target selection', async () => {
- let ele = fixture.debugElement.nativeElement.querySelector('#ricSelector');
- expect(ele).toBeFalsy();
-
- let jsonTextArea: MatInputHarness = await loader.getHarness(MatInputHarness.with({selector: '#policyJsonTextArea'}));
- expect(await jsonTextArea.isDisabled()).toBeFalsy();
- const actualJson: string = await jsonTextArea.getValue();
- expect(actualJson).toContain('qosObjectives');
+ it('should not contain ric select', async () => {
+ const ele = fixture.debugElement.nativeElement.querySelector('nrcp-ric-selector');
+ expect(ele).toBeFalsy();
});
- it('should contain enabled Format, Submit and Close buttons', async () => {
- let formatButton: MatButtonHarness = await loader.getHarness(MatButtonHarness.with({selector: '#formatButton'}));
- expect(await formatButton.isDisabled()).toBeFalsy();
- expect(await formatButton.getText()).toEqual('Format JSON');
+ 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');
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
index 9dd0d53..ef6bfd0 100644
--- 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
@@ -17,8 +17,8 @@
* limitations under the License.
* ========================LICENSE_END===================================
*/
-import { Component, Inject, OnInit } from '@angular/core';
-import { FormControl, FormGroup, Validators, ValidatorFn, AbstractControl } from '@angular/forms';
+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';
@@ -26,8 +26,9 @@
import { HttpErrorResponse } from '@angular/common/http';
import { ErrorDialogService } from '../../services/ui/error-dialog.service';
import * as uuid from 'uuid';
-import { Rics } from '../../interfaces/ric';
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',
@@ -36,7 +37,10 @@
})
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;
@@ -51,7 +55,7 @@
@Inject(MAT_DIALOG_DATA) private data,
private ui: UiService) {
this.policyInstanceId = data.instanceId;
- this.policyJson = data.instanceJson ? this.formatJsonString(data.instanceJson) : '';
+ this.policyJson = data.instanceJson ? formatJsonString(data.instanceJson) : '{}';
this.ric = data.ric;
}
@@ -59,30 +63,15 @@
this.ui.darkModeState.subscribe((isDark) => {
this.darkMode = isDark;
});
- this.instanceForm = new FormGroup({
- 'ricSelector': new FormControl(this.ric, [
- Validators.required
- ]),
- 'policyJsonTextArea': new FormControl(this.policyJson, [
- Validators.required,
- jsonValidator()
- ])
- });
- if (!this.policyInstanceId) {
- this.getRicIds();
- }
+ this.instanceForm = new FormGroup({});
}
- get policyJsonTextArea() { return this.instanceForm.get('policyJsonTextArea'); }
-
- get ricSelector() { return this.instanceForm.get('ricSelector'); }
-
onSubmit() {
if (this.policyInstanceId == null) {
this.policyInstanceId = uuid.v4();
}
const self: NoTypePolicyInstanceDialogComponent = this;
- let createPolicyInstance: CreatePolicyInstance = this.createPolicyInstance(this.policyJsonTextArea.value);
+ let createPolicyInstance: CreatePolicyInstance = this.createPolicyInstance(this.policyEditorComponent.policyJsonTextArea.value);
this.policySvc.putPolicy(createPolicyInstance).subscribe(
{
next(_) {
@@ -101,48 +90,8 @@
createPolicyInstance.policy_data = JSON.parse(policyJson);
createPolicyInstance.policy_id = this.policyInstanceId;
createPolicyInstance.policytype_id = '';
- createPolicyInstance.ric_id = (!this.ricSelector.value.ric_id) ? this.ric : this.ricSelector.value.ric_id;
+ createPolicyInstance.ric_id = this.ricSelectorComponent.selectedRic;
createPolicyInstance.service_id = 'controlpanel';
return createPolicyInstance;
}
-
- getRicIds() {
- const self: NoTypePolicyInstanceDialogComponent = this;
- this.policySvc.getRics('').subscribe(
- {
- next(value: Rics) {
- value.rics.forEach(ric => {
- self.allRicIds.push(ric.ric_id);
- });
- }
- });
- }
-
- private formatJsonString(jsonToFormat: any): string {
- return JSON.stringify(jsonToFormat, null, 2);
- }
-
- formatJsonInput() {
- this.policyJson = this.formatJsonString(JSON.parse(this.policyJsonTextArea.value));
- }
-}
-
-export function jsonValidator(): ValidatorFn {
- return (control: AbstractControl): { [key: string]: any } | null => {
- const notValid = !isJsonValid(control.value);
- return notValid ? { 'invalidJson': { value: control.value } } : null;
- };
-}
-
-export function isJsonValid(json: string): boolean {
- try {
- if (json != null) {
- JSON.parse(json);
- return true;
- } else {
- return false;
- }
- } catch (jsonError) {
- return false;
- }
}
diff --git a/webapp-frontend/src/app/policy/policy.module.ts b/webapp-frontend/src/app/policy/policy.module.ts
index f7a41dc..9bf2959 100644
--- a/webapp-frontend/src/app/policy/policy.module.ts
+++ b/webapp-frontend/src/app/policy/policy.module.ts
@@ -31,6 +31,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';
const routes:Routes = [
{path: 'policy', component: PolicyControlComponent}
@@ -43,6 +45,8 @@
PolicyControlComponent,
PolicyInstanceComponent,
PolicyInstanceDialogComponent,
+ RicSelectorComponent,
+ NoTypePolicyEditorComponent,
],
imports: [
CommonModule,
@@ -77,6 +81,6 @@
exports: [
PolicyCardComponent,
PolicyControlComponent
- ],
+ ]
})
export class PolicyModule { }
diff --git a/webapp-frontend/src/app/policy/ric-selector/ric-selector.component.html b/webapp-frontend/src/app/policy/ric-selector/ric-selector.component.html
new file mode 100644
index 0000000..1299058
--- /dev/null
+++ b/webapp-frontend/src/app/policy/ric-selector/ric-selector.component.html
@@ -0,0 +1,16 @@
+<mat-form-field appearance="fill">
+ <mat-select id="ricSelector" formControlName="ricSelector" matInput required
+ placeholder="Target" matTooltip="Element where the policy instance resides, e.g. a gNodeB or Near-RT RIC">
+ <mat-option *ngFor="let ric of this.allRics" [value]="ric">
+ {{ric}}
+ </mat-option>
+ </mat-select>
+ <div *ngIf="ricSelector.invalid && (ricSelector.dirty || ricSelector.touched)" class="alert mat-error"
+ style="width: 300px;">
+ <div id="ricSelectorError" *ngIf="ricSelector.errors.required">
+ <mat-error role="alert">
+ This field is required.
+ </mat-error>
+ </div>
+</div>
+</mat-form-field>
\ No newline at end of file
diff --git a/webapp-frontend/src/app/policy/ric-selector/ric-selector.component.scss b/webapp-frontend/src/app/policy/ric-selector/ric-selector.component.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/webapp-frontend/src/app/policy/ric-selector/ric-selector.component.scss
diff --git a/webapp-frontend/src/app/policy/ric-selector/ric-selector.component.spec.ts b/webapp-frontend/src/app/policy/ric-selector/ric-selector.component.spec.ts
new file mode 100644
index 0000000..0306ec4
--- /dev/null
+++ b/webapp-frontend/src/app/policy/ric-selector/ric-selector.component.spec.ts
@@ -0,0 +1,94 @@
+import { HarnessLoader } from '@angular/cdk/testing';
+import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
+import { Component, ViewChild } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormBuilder, FormGroup } from '@angular/forms';
+import { MatSelectModule } from '@angular/material/select';
+import { MatSelectHarness } from '@angular/material/select/testing';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { of } from "rxjs/observable/of";
+import { Ric } from 'src/app/interfaces/ric';
+import { PolicyService } from 'src/app/services/policy/policy.service';
+
+import { RicSelectorComponent } from './ric-selector.component';
+
+let formGroup: FormGroup = new FormGroup({});
+
+describe('RicSelectorComponent', () => {
+ let component: TestRicSelectorHostComponent;
+ let fixture: ComponentFixture<TestRicSelectorHostComponent>;
+ let loader: HarnessLoader;
+ let policyServiceSpy: jasmine.SpyObj<PolicyService>;
+ const ric1: Ric = { ric_id: 'ric1', managed_element_ids: ['me1'], policytype_ids: ['type1'], state: '' };
+ const ric2: Ric = { ric_id: 'ric2', managed_element_ids: ['me1'], policytype_ids: ['type1'], state: '' };
+
+ beforeEach(async(() => {
+ policyServiceSpy = jasmine.createSpyObj('PolicyService', ['getRics']);
+ const policyData = {
+ createSchema: "{}",
+ instanceId: null,
+ instanceJson: '{"qosObjectives": {"priorityLevel": 3100}}',
+ name: "name",
+ ric: null
+ };
+
+ policyServiceSpy.getRics.and.returnValue(of({ rics: [ric1, ric2] }));
+ TestBed.configureTestingModule({
+ imports: [
+ BrowserAnimationsModule,
+ MatSelectModule,
+ ],
+ declarations: [
+ RicSelectorComponent,
+ TestRicSelectorHostComponent
+ ],
+ providers: [
+ { provide: PolicyService, useValue: policyServiceSpy },
+ FormBuilder
+ ]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(TestRicSelectorHostComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ loader = TestbedHarnessEnvironment.loader(fixture);
+ }));
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should be added to form group with required validator', async () => {
+ let ricSelector: MatSelectHarness = await loader.getHarness(MatSelectHarness.with({ selector: '#ricSelector' }));
+
+ expect(formGroup.get('ricSelector')).toBeTruthy();
+ expect(await ricSelector.isRequired()).toBeTruthy();
+ });
+
+ it('no ric selected', async () => {
+ let ricSelector: MatSelectHarness = await loader.getHarness(MatSelectHarness.with({ selector: '#ricSelector' }));
+
+ expect(await ricSelector.isEmpty()).toBeTruthy();
+ });
+
+ it('options should contain rics for policy type', async () => {
+ let ricSelector: MatSelectHarness = await loader.getHarness(MatSelectHarness.with({ selector: '#ricSelector' }));
+
+ expect(policyServiceSpy.getRics).toHaveBeenCalledWith('policyTypeName');
+ await ricSelector.open();
+ const count = (await ricSelector.getOptions()).length;
+ expect(count).toEqual(2);
+ });
+});
+
+@Component({
+ selector: `ric-selector-host-component`,
+ template: `<nrcp-ric-selector [instanceForm]="instanceForm" [policyTypeName]="policyTypeName"></nrcp-ric-selector>`
+})
+export class TestRicSelectorHostComponent {
+ @ViewChild(RicSelectorComponent)
+ private ricSelectorComponent: RicSelectorComponent;
+ instanceForm: FormGroup = formGroup;
+ policyTypeName: string = 'policyTypeName';
+}
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
new file mode 100644
index 0000000..583f02a
--- /dev/null
+++ b/webapp-frontend/src/app/policy/ric-selector/ric-selector.component.ts
@@ -0,0 +1,54 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { AbstractControl, ControlContainer, FormBuilder, FormControl, FormGroup, FormGroupDirective, Validators } from '@angular/forms';
+import { Ric, 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 }]
+
+})
+export class RicSelectorComponent implements OnInit {
+
+ @Input() instanceForm: FormGroup;
+ @Input() policyTypeName: string = '';
+ ric: string;
+ allRics: string[] = [];
+
+ constructor(
+ private dataService: PolicyService,
+ private formBuilder: FormBuilder) {
+ }
+
+ ngOnInit(): void {
+ this.instanceForm.addControl(
+ 'ricSelector', new FormControl(this.ric, [
+ Validators.required
+ ]));
+
+ console.log('Ric:', this.ric);
+ this.fetchRics();
+ }
+
+ get selectedRic(): string { return this.ric; }
+
+ get ricSelector(): AbstractControl {
+ return this.instanceForm.get('ricSelector');
+ }
+
+ private fetchRics() {
+ 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);
+ }
+ });
+ }
+}