Decouple policy instance components 78/5778/1
authorelinuxhenrik <henrik.b.andersson@est.tech>
Thu, 18 Mar 2021 11:27:14 +0000 (12:27 +0100)
committerelinuxhenrik <henrik.b.andersson@est.tech>
Thu, 18 Mar 2021 11:27:19 +0000 (12:27 +0100)
Also make the tooltips work again.

Change-Id: I34147ba88524a5ce90c6a32bb1678d2e861908f5
Signed-off-by: elinuxhenrik <henrik.b.andersson@est.tech>
Issue-ID: NONRTRIC-463

13 files changed:
webapp-frontend/src/app/app.module.ts
webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.html
webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.spec.ts
webapp-frontend/src/app/policy/no-type-policy-editor/no-type-policy-editor.component.ts
webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.html
webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.spec.ts
webapp-frontend/src/app/policy/policy-instance-dialog/policy-instance-dialog.component.ts
webapp-frontend/src/app/policy/policy.module.ts
webapp-frontend/src/app/policy/ric-selector/ric-selector.component.html
webapp-frontend/src/app/policy/ric-selector/ric-selector.component.spec.ts
webapp-frontend/src/app/policy/ric-selector/ric-selector.component.ts
webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.html
webapp-frontend/src/app/policy/typed-policy-editor/typed-policy-editor.component.ts

index e84cf6b..4c2779e 100644 (file)
@@ -42,6 +42,7 @@ import { MatSortModule } from '@angular/material/sort';
 import { MatTableModule } from '@angular/material/table';
 import { MatTabsModule } from '@angular/material/tabs';
 import { MatToolbarModule } from '@angular/material/toolbar';
+import { MatTooltipModule } from '@angular/material/tooltip';
 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
 import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
 import { NgModule } from '@angular/core';
@@ -108,6 +109,7 @@ export const isMock = environment.mock;
     MatTableModule,
     MatTabsModule,
     MatToolbarModule,
+    MatTooltipModule,
     MaterialDesignFrameworkModule,
     MDBBootstrapModule.forRoot(),
     PolicyModule,
index 9a2112e..fdf174e 100644 (file)
   /
 -->
 
-<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">
+<h4>
+    Properties *
+</h4>
+<form [formGroup]="instanceForm">
+    <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 *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>
-        <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>
+        <button id="formatButton" (click)="formatJsonInput();" mat-raised-button [disabled]="!policyJsonTextArea.valid">
+            Format JSON
+        </button>
+    </mat-form-field>
+</form>
\ No newline at end of file
index fb3a6a6..7fb64c4 100644 (file)
@@ -69,15 +69,6 @@ describe("NoTypePolicyEditorComponent", () => {
     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" })
@@ -91,7 +82,7 @@ describe("NoTypePolicyEditorComponent", () => {
   });
 
   it("Format button should be disabled when json not valid", async () => {
-    const ele = formGroup.get("policyJsonTextArea");
+    const ele = component.noTypePolicyEditorComponent.instanceForm.get("policyJsonTextArea");
     ele.setValue("{");
 
     let formatButton: MatButtonHarness = await loader.getHarness(
@@ -101,7 +92,7 @@ describe("NoTypePolicyEditorComponent", () => {
   });
 
   it("should format unformatted json", async () => {
-    const textArea = formGroup.get("policyJsonTextArea");
+    const textArea = component.noTypePolicyEditorComponent.instanceForm.get("policyJsonTextArea");
     textArea.setValue('{"A":"A"}');
     component.noTypePolicyEditorComponent.formatJsonInput();
     expect(component.noTypePolicyEditorComponent.policyJson).toEqual(
@@ -113,13 +104,11 @@ describe("NoTypePolicyEditorComponent", () => {
     selector: `no-type-policy-editor-host-component`,
     template: `<nrcp-no-type-policy-editor
       [policyJson]="this.policyJson"
-      [instanceForm]="instanceForm"
     ></nrcp-no-type-policy-editor>`,
   })
   class TestNoTypePolicyEditorComponentHostComponent {
     @ViewChild(NoTypePolicyEditorComponent)
     noTypePolicyEditorComponent: NoTypePolicyEditorComponent;
-    instanceForm: FormGroup = formGroup;
     policyJson: string = '{"A":"A"}';
   }
 });
index 8d2294c..5fa92d9 100644 (file)
@@ -18,8 +18,9 @@
 //   ========================LICENSE_END===================================
 //  /
 
-import { Component, Input, OnInit } from '@angular/core';
+import { Component, Input, OnInit, Output } from '@angular/core';
 import { AbstractControl, ControlContainer, FormBuilder, FormControl, FormGroup, FormGroupDirective, ValidatorFn, Validators } from '@angular/forms';
+import { EventEmitter } from '@angular/core';
 
 @Component({
   selector: 'nrcp-no-type-policy-editor',
@@ -28,51 +29,61 @@ import { AbstractControl, ControlContainer, FormBuilder, FormControl, FormGroup,
   viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }]
 })
 export class NoTypePolicyEditorComponent implements OnInit {
+  @Input() policyJson: string = null;
+  @Output() validJson: EventEmitter<string> = new EventEmitter<string>();
 
-  @Input() instanceForm: FormGroup;
-  @Input() policyJson: string;
+  instanceForm: FormGroup = new FormGroup({});
 
   constructor(
     private formBuilder: FormBuilder) { }
 
-  ngOnInit(): void {
-    this.instanceForm.addControl(
-      'policyJsonTextArea', new FormControl(this.policyJson, [
-        Validators.required,
-        jsonValidator()
-      ])
-    )
-  }
+    ngOnInit(): void {
+      this.instanceForm.addControl(
+        'policyJsonTextArea', new FormControl(this.policyJson, [
+          Validators.required,
+          this.jsonValidator()
+        ])
+      )
+    }
 
-  get policyJsonTextArea(): AbstractControl {
+    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);
-}
+  jsonValidator(): ValidatorFn {
+    return (control: AbstractControl): { [key: string]: any } | null => {
+      const notValid = !this.isJsonValid(control.value);
+      this.handleJsonChangeEvent(notValid, control.value);
+      return notValid ? { 'invalidJson': { value: control.value } } : null;
+    };
+  }
 
-export function jsonValidator(): ValidatorFn {
-  return (control: AbstractControl): { [key: string]: any } | null => {
-    const notValid = !isJsonValid(control.value);
-    return notValid ? { 'invalidJson': { value: control.value } } : null;
-  };
-}
+  handleJsonChangeEvent(notValid: boolean, newValue: string): void {
+    let json = newValue;
+    if (notValid) {
+      json = null;
+    }
+    this.validJson.emit(json);
+  }
 
-export function isJsonValid(json: string): boolean {
-  try {
-    if (json != null) {
-      JSON.parse(json);
-      return true;
-    } else {
+  isJsonValid(json: string): boolean {
+    try {
+      if (json != null) {
+        JSON.parse(json);
+        return true;
+      } else {
+        return false;
+      }
+    } catch (jsonError) {
       return false;
     }
-  } catch (jsonError) {
-    return false;
   }
 }
+
+export function formatJsonString(jsonToFormat: any): string {
+  return JSON.stringify(jsonToFormat, null, 2);
+}
index 6e79e78..a3654b4 100644 (file)
@@ -19,7 +19,7 @@
   -->
 
 <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 id="instanceInfo" *ngIf="policyInstance.policy_id">[{{policyInstance.ric_id}}] Instance ID: {{policyInstance.policy_id}}</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="!this.policyInstanceId">Create new policy instance of type </tspan>
-                <tspan *ngIf="policyTypeName">{{policyTypeName}}</tspan>
-                <tspan *ngIf="!policyTypeName">&lt; No Type &gt;</tspan>
+                <tspan *ngIf="!this.policyInstance.policy_id">Create new policy instance of type </tspan>
+                <tspan *ngIf="policyInstance.policytype_id">{{policyInstance.policytype_id}}</tspan>
+                <tspan *ngIf="!policyInstance.policytype_id">&lt; No Type &gt;</tspan>
             </text>
         </svg>
     </div>
 
 <div class="text-muted" *ngIf="jsonSchemaObject.description">{{jsonSchemaObject.description}}</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="!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>
-</form>
\ No newline at end of file
+<mat-card class="card" [ngClass]="{'card-dark': darkMode}">
+    <nrcp-ric-selector *ngIf="!policyInstance.policy_id" [policyTypeName]="policyInstance.policytype_id"
+        (selectedRic)="onSelectedRicChanged($event)"></nrcp-ric-selector>
+
+    <nrcp-no-type-policy-editor *ngIf="!typeHasSchema()" (validJson)="onJsonChanged($event)" [policyJson]="policyJson"></nrcp-no-type-policy-editor>
+
+    <nrcp-typed-policy-editor *ngIf="typeHasSchema()" (validJson)="onJsonChanged($event)" [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>
\ No newline at end of file
index b4c2732..ff11013 100644 (file)
@@ -126,29 +126,20 @@ describe("PolicyInstanceDialogComponent", () => {
       expect(ele).toBeFalsy();
     });
 
-    it("should contain ric select with instance form and no policy type", async () => {
+    it("should contain ric select with 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 () => {
+    it("should contain json editor with 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 () => {
+    it("should contain enabled Close button and 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" })
@@ -159,7 +150,6 @@ describe("PolicyInstanceDialogComponent", () => {
       let submitButton: MatButtonHarness = await loader.getHarness(
         MatButtonHarness.with({ selector: "#submitButton" })
       );
-      expect(await submitButton.isDisabled()).toBeTruthy();
       expect(await submitButton.getText()).toEqual("Submit");
     });
   });
@@ -191,10 +181,9 @@ describe("PolicyInstanceDialogComponent", () => {
       expect(ele).toBeFalsy();
     });
 
-    it("should contain ric select with instance form and provided policy type", async () => {
+    it("should contain ric select with 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");
     });
 
@@ -206,15 +195,8 @@ describe("PolicyInstanceDialogComponent", () => {
       expect(typedPolicyEditor.darkMode).toBeTruthy();
     });
 
-    it("should contain enabled Close button and disabled Submit button", async () => {
+    it("should contain enabled Close button and 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" })
@@ -225,7 +207,6 @@ describe("PolicyInstanceDialogComponent", () => {
       let submitButton: MatButtonHarness = await loader.getHarness(
         MatButtonHarness.with({ selector: "#submitButton" })
       );
-      expect(await submitButton.isDisabled()).toBeTruthy();
       expect(await submitButton.getText()).toEqual("Submit");
     });
   });
@@ -265,16 +246,13 @@ describe("PolicyInstanceDialogComponent", () => {
       expect(ricSelector).toBeFalsy();
     });
 
-    it("should contain json editor with form and json data", async () => {
+    it("should contain json editor with 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" })
       );
@@ -333,8 +311,6 @@ describe("PolicyInstanceDialogComponent", () => {
     });
 
     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" })
       );
@@ -379,7 +355,6 @@ function unescapeQuotes(string: string): string {
   ],
 })
 class RicSelectorStubComponent {
-  @Input() instanceForm: FormGroup;
   @Input() policyTypeName: string = "";
 
   get selectedRic(): string {
@@ -398,7 +373,6 @@ class RicSelectorStubComponent {
   ],
 })
 class NoTypePolicyEditorStubComponent {
-  @Input() instanceForm: FormGroup;
   @Input() policyJson: string;
 
   get policyJsonTextArea(): AbstractControl {
index 096346e..f649911 100644 (file)
@@ -23,9 +23,7 @@ import {
   Component,
   Inject,
   OnInit,
-  ViewChild,
 } from "@angular/core";
-import { FormGroup } from "@angular/forms";
 import {
   MatDialogConfig,
   MatDialogRef,
@@ -42,12 +40,7 @@ import {
   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";
+import { formatJsonString } from "../no-type-policy-editor/no-type-policy-editor.component";
 
 @Component({
   selector: "nrcp-policy-instance-dialog",
@@ -55,19 +48,10 @@ import { TypedPolicyEditorComponent } from "../typed-policy-editor/typed-policy-
   styleUrls: ["./policy-instance-dialog.component.scss"],
 })
 export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit {
-  instanceForm: FormGroup;
-  @ViewChild(RicSelectorComponent)
-  ricSelector: RicSelectorComponent;
-  @ViewChild(NoTypePolicyEditorComponent)
-  noTypePolicyEditor: NoTypePolicyEditorComponent;
-  @ViewChild(TypedPolicyEditorComponent)
-  typedPolicyEditor: TypedPolicyEditorComponent;
-  policyInstanceId: string; // null if not yet created
+  policyInstance = {} as CreatePolicyInstance;
   policyJson: string;
-  policyTypeName: string;
   jsonSchemaObject: any;
   darkMode: boolean;
-  ric: string;
   allRicIds: string[] = [];
 
   constructor(
@@ -79,18 +63,18 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit {
     @Inject(MAT_DIALOG_DATA) private data,
     private ui: UiService
   ) {
-    this.policyInstanceId = data.instanceId;
-    this.policyTypeName = data.name;
+    this.policyInstance.policy_id = data.instanceId;
+    this.policyInstance.policytype_id = data.name;
+    this.policyInstance.policy_data = data.instanceJson;
     this.policyJson = data.instanceJson;
     this.jsonSchemaObject = data.createSchema;
-    this.ric = data.ric;
+    this.policyInstance.ric_id = data.ric;
   }
 
   ngOnInit() {
     this.ui.darkModeState.subscribe((isDark) => {
       this.darkMode = isDark;
     });
-    this.instanceForm = new FormGroup({});
     this.formatNoTypePolicyJson();
   }
 
@@ -101,32 +85,31 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit {
 
   private formatNoTypePolicyJson() {
     if (!this.typeHasSchema()) {
-      if (this.policyJson) {
-        this.policyJson = formatJsonString(this.policyJson);
+      if (this.policyInstance.policy_data) {
+        this.policyJson = formatJsonString(this.policyInstance.policy_data);
       } else {
         this.policyJson = "{}";
       }
     }
   }
 
+  onSelectedRicChanged(newRic: string): void {
+    this.policyInstance.ric_id = newRic;
+  }
+
+  onJsonChanged(newJson: string): void {
+    this.policyInstance.policy_data = newJson;
+  }
+
   onSubmit() {
-    if (this.policyInstanceId == null) {
-      this.policyInstanceId = uuid.v4();
+    if (this.policyInstance.policy_id == null) {
+      this.policyInstance.policy_id = uuid.v4();
     }
     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({
+    this.policySvc.putPolicy(this.policyInstance).subscribe({
       next(_) {
         self.notificationService.success(
-          "Policy without type:" + self.policyInstanceId + " submitted"
+          "Policy " + self.policyInstance.policy_id + " submitted"
         );
         self.dialogRef.close();
       },
@@ -142,26 +125,10 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit {
   }
 
   isFormValid(): boolean {
-    let isValid: boolean = this.instanceForm.valid;
-    if (this.typeHasSchema()) {
-      isValid =
-        isValid && this.typedPolicyEditor
-          ? this.typedPolicyEditor.formIsValid
-          : false;
-    }
-    return isValid;
-  }
-
-  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;
+    return (
+      this.policyInstance.ric_id !== null &&
+      this.policyInstance.policy_data !== null
+    );
   }
 }
 
index d4a4b1e..c273c2b 100644 (file)
@@ -18,6 +18,7 @@
 //   ========================LICENSE_END===================================
 //
 
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
 import { CommonModule } from '@angular/common';
 import { NgModule } from '@angular/core';
 import { MatTableModule } from '@angular/material/table';
@@ -47,6 +48,7 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
 import { MatSortModule } from '@angular/material/sort';
 import { MatTabsModule } from '@angular/material/tabs';
 import { MatToolbarModule } from '@angular/material/toolbar';
+import { MatTooltipModule } from '@angular/material/tooltip';
 import { MaterialDesignFrameworkModule } from 'angular6-json-schema-form';
 import { FlexLayoutModule } from '@angular/flex-layout';
 import { Routes, RouterModule } from '@angular/router';
@@ -69,6 +71,7 @@ const routes:Routes = [
     TypedPolicyEditorComponent,
   ],
   imports: [
+    BrowserAnimationsModule,
     CommonModule,
     FlexLayoutModule,
     FormsModule,
@@ -94,6 +97,7 @@ const routes:Routes = [
     MatTableModule,
     MatTabsModule,
     MatToolbarModule,
+    MatTooltipModule,
     MaterialDesignFrameworkModule,
     ReactiveFormsModule,
     RouterModule.forChild(routes)
index 13d1ec0..30bfcf8 100644 (file)
    ========================LICENSE_END===================================
   /
 -->
-
-<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
+<form [formGroup]="instanceForm">
+    <mat-form-field appearance="fill">
+        <mat-select id="ricSelector" formControlName="ricSelector" (selectionChange)="onRicChanged($event)" 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" 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>
+</form>
\ No newline at end of file
index f452fb5..378279a 100644 (file)
@@ -82,13 +82,6 @@ describe('RicSelectorComponent', () => {
     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' }));
 
index 996d9a0..15e1a61 100644 (file)
@@ -18,7 +18,7 @@
 //   ========================LICENSE_END===================================
 //  /
 
-import { Component, Input, OnInit } from "@angular/core";
+import { Component, Input, OnInit, Output } from "@angular/core";
 import {
   AbstractControl,
   ControlContainer,
@@ -28,8 +28,10 @@ import {
   FormGroupDirective,
   Validators,
 } from "@angular/forms";
+import { EventEmitter } from "@angular/core";
 import { Rics } from "src/app/interfaces/ric";
 import { PolicyService } from "src/app/services/policy/policy.service";
+import { MatSelectChange } from "@angular/material/select";
 
 @Component({
   selector: "nrcp-ric-selector",
@@ -40,9 +42,13 @@ import { PolicyService } from "src/app/services/policy/policy.service";
   ],
 })
 export class RicSelectorComponent implements OnInit {
-  @Input() instanceForm: FormGroup;
   @Input() policyTypeName: string = "";
-  ric: string;
+  @Output() selectedRic: EventEmitter<string> = new EventEmitter<string>();
+
+  ric: string = null;
+  instanceForm: FormGroup = new FormGroup({
+    ricSelector: new FormControl(this.ric, [Validators.required]),
+  });
   allRics: string[] = [];
 
   constructor(
@@ -51,17 +57,12 @@ export class RicSelectorComponent implements OnInit {
   ) {}
 
   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;
+  onRicChanged(newvalue: MatSelectChange): void {
+    this.selectedRic.emit(newvalue.value);
   }
 
   get ricSelector(): AbstractControl {
index 6b8c233..cd2c0c4 100644 (file)
@@ -22,7 +22,7 @@
 
 <h4 id="propertiesHeading" class="default-cursor" (click)="toggleVisible('form')">
     <mat-icon id="propertiesIcon" matTooltip="Properties">{{isVisible.form ? 'expand_less' : 'expand_more'}}</mat-icon>
-    Properties
+    Properties *
 </h4>
 <div id="propertiesDiv" *ngIf="isVisible.form" class="json-schema-form" [@expandSection]="true">
     <json-schema-form id="json-schema-form" [form]="jsonSchemaObject"
index f9651a4..990b882 100644 (file)
@@ -19,7 +19,7 @@
 //
 
 import { animate, state, style, transition, trigger } from '@angular/animations';
-import { Component, Input, OnInit } from '@angular/core';
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
 import { JsonPointer } from 'angular6-json-schema-form';
 
 @Component({
@@ -50,6 +50,7 @@ export class TypedPolicyEditorComponent implements OnInit {
   @Input() jsonSchemaObject: any = {};
   @Input() jsonObject: any = {};
   @Input() darkMode: boolean;
+  @Output() validJson: EventEmitter<string> = new EventEmitter<string>();
 
   isVisible = {
     form: true,
@@ -82,6 +83,11 @@ export class TypedPolicyEditorComponent implements OnInit {
 
   isValid(isValid: boolean): void {
     this.formIsValid = isValid;
+    let json = this.prettyLiveFormData;
+    if (!this.formIsValid) {
+      json = null;
+    }
+    this.validJson.emit(json);
   }
 
   validationErrors(validationErrors: any): void {