This enables the user to add/delete/update RIC config from dashboard.
Unit test for this component will be covered in next commit.
Issue-ID: NONRTRIC-498
Change-Id: Ia055127522f1784595c1667f1a8e4f0f22e14bd3
Signed-off-by: Lathish <lathishbabu.ganesan@est.tech>
import { MainComponent } from './main/main.component';
import { PolicyControlComponent } from '@policy/policy-control.component';
import { EICoordinatorComponent } from '@ei-coordinator/ei-coordinator.component';
+import { RicConfigComponent } from './ric-config/ric-config.component';
const routes: Routes = [
{ path: '', component: MainComponent },
{ path: 'policy', component: PolicyControlComponent },
+ { path: 'ric-config', component: RicConfigComponent},
{ path: 'ei-coordinator', component: EICoordinatorComponent }
];
import { environment } from 'environments/environment';
import { HttpRequestInterceptor } from './interceptor';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
+import { RicConfigModule } from './ric-config/ricconfig.module';
export const isMock = environment.mock;
MDBBootstrapModule.forRoot(),
PolicyModule,
EiCoordinatorModule,
+ RicConfigModule,
ToastrModule.forRoot(),
],
entryComponents: [
import * as policyinstanceedit from "./mock/policy-instance-edit.json";
import * as ric1 from "./mock/ric1.json";
import * as ric2 from "./mock/ric2.json";
+import * as ricconfig from "./mock/ric-configuration.json";
import { delay } from "rxjs/operators";
const urls = [
url: "/ei-producer/v1/eiproducers/producer2/eijobs",
json: eijobsProd2,
},
+ {
+ url: '/a1-policy/v2/configuration',
+ json: ricconfig
+ }
];
@Injectable()
--- /dev/null
+/*-
+ * ========================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===================================
+ */
+
+
+export interface RicConfig {
+}
--- /dev/null
+{
+ "config": {
+ "//description": "Application configuration",
+ "ric": [
+ {
+ "name": "ric1",
+ "baseUrl": "http://localhost:30001/",
+ "managedElementIds": [
+ "kista_1",
+ "kista_2"
+ ]
+ }
+ ]
+ }
+}
\ No newline at end of file
<a mat-list-item routerLink="/" (click)="onSidenavClose()">
<mat-icon>home</mat-icon> <span class="nav-caption">Home</span>
</a>
- <a mat-list-item routerLink="/policy" (click)="onSidenavClose()">
- <mat-icon>assignment</mat-icon> <span class="nav-caption">Policy</span>
+ <a mat-list-item (click)="toggle()">
+ <mat-icon>dashboard</mat-icon> <span class="nav-caption">Policy</span>
+ <mat-icon *ngIf="showSubMenu">expand_more</mat-icon>
+ <mat-icon *ngIf="!showSubMenu">chevron_right</mat-icon>
</a>
+ <mat-nav-list [ngClass]="{'dark': darkMode}" *ngIf="showSubMenu">
+ <a mat-list-item routerLink="/policy" (click)="onSidenavClose()">
+ <mat-icon>dashboard</mat-icon> <span class="nav-caption">Policy Types</span>
+ </a>
+ <a mat-list-item routerLink="/ric-config" (click)="onSidenavClose()">
+ <mat-icon>build</mat-icon> <span class="nav-caption">RIC Config</span>
+ </a>
+ </mat-nav-list>
<a mat-list-item routerLink="/ei-coordinator" (click)="onSidenavClose()">
- <mat-icon>assignment</mat-icon> <span class="nav-caption">Enrichment Information Coordinator</span>
+ <mat-icon>dashboard</mat-icon> <span class="nav-caption">Enrichment Information Coordinator</span>
</a>
</mat-nav-list>
export class SidenavListComponent implements OnInit {
darkMode: boolean;
@Output() sidenavClose = new EventEmitter();
+ showSubMenu = false;
constructor(private ui: UiService) { }
public onSidenavClose = () => {
this.sidenavClose.emit();
+ this.showSubMenu = !this.showSubMenu;
+ }
+
+ public toggle(){
+ this.showSubMenu = !this.showSubMenu;
}
}
--- /dev/null
+<!--
+ ========================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===================================
+ -->
+ <div fxLayout="row">
+ <div class="nrcp-global-page-title">RIC Configuration</div>
+ <button #refreshButton mat-icon-button color="primary" (click)="getConfig()">
+ <mat-icon>refresh</mat-icon>
+ </button>
+</div>
+<form [formGroup]="ricConfigForm">
+ <mat-form-field style="width: 800px;" appearance="fill">
+ <textarea style= "resize:none;" id="ricConfigInfo" formControlName="ricConfigInfo" matInput cdkTextareaAutosize
+ cdkAutosizeMinRows="10" cdkAutosizeMaxRows="20" placeholder="Ric Configuration"
+ matTooltip="Ric Configuration" matTooltipPosition="before">
+ </textarea>
+ <div *ngIf="ricConfigInfo.invalid && (ricConfigInfo.dirty || ricConfigInfo.touched)">
+ <div *ngIf="ricConfigInfo.errors.required">
+ <mat-error role="alert">
+ This field is required.
+ </mat-error>
+ </div>
+ <div *ngIf="ricConfigInfo.errors.invalidJson">
+ <mat-error role="alert">
+ Should be a valid JSON.
+ </mat-error>
+ </div>
+ </div>
+ <button id="formatButton" (click)="formatJsonInput();" mat-raised-button>
+ Format JSON
+ </button>
+ </mat-form-field>
+</form>
+<div>
+<button style="margin-right:20px" id="update" (click)="updateRicConfig();" mat-raised-button [disabled]="!ricConfigInfo.valid">
+ Update
+</button>
+</div>
\ No newline at end of file
--- /dev/null
+/*-
+ * ========================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===================================
+ */
+
+ textarea[cdktextareaautosize].mat-input-element {
+ overflow: auto;
+}
\ No newline at end of file
--- /dev/null
+/*-
+ * ========================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 { Component, OnInit, Output } from "@angular/core";
+import { RicConfig } from "@app/interfaces/ric.config";
+import {
+ AbstractControl,
+ ControlContainer,
+ FormControl,
+ FormGroup,
+ FormGroupDirective,
+ ValidatorFn,
+ Validators,
+} from "@angular/forms";
+import { PolicyService } from "@services/policy/policy.service";
+import { EventEmitter } from "@angular/core";
+import { NotificationService } from "@app/services/ui/notification.service";
+import { HttpResponse } from "@angular/common/http";
+
+@Component({
+ selector: "nrcp-ric-config",
+ templateUrl: "./ric-config.component.html",
+ styleUrls: ["./ric-config.component.scss"],
+ viewProviders: [
+ { provide: ControlContainer, useExisting: FormGroupDirective },
+ ],
+})
+
+export class RicConfigComponent implements OnInit {
+ @Output() validJson: EventEmitter<string> = new EventEmitter<string>();
+ ricConfig: string;
+ ricConfigForm: FormGroup = new FormGroup({});
+
+ constructor(private policyService: PolicyService,
+ private notificationService: NotificationService) {
+ }
+
+ ngOnInit(): void {
+ let initialJson: RicConfig = "{}";
+ this.getConfig();
+ this.ricConfigForm.addControl(
+ "ricConfigInfo",
+ new FormControl(initialJson, [
+ Validators.required,
+ this.jsonValidator(),
+ ])
+ );
+ }
+
+ 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;
+ };
+ }
+
+ isJsonValid(json: string): boolean {
+ let valid = false as boolean;
+ try {
+ if (json != null) {
+ JSON.parse(json);
+ valid = true;
+ }
+ } catch (jsonError) {}
+ return valid;
+ }
+
+ handleJsonChangeEvent(notValid: boolean, newValue: string): void {
+ let json = newValue;
+ if (notValid) {
+ json = null;
+ }
+ this.validJson.emit(json);
+ }
+ get ricConfigInfo(): AbstractControl {
+ return this.ricConfigForm
+ ? this.ricConfigForm.get("ricConfigInfo")
+ : null;
+ }
+
+ formatJsonInput(): void {
+ let jsonBefore: string = this.ricConfigInfo.value;
+ let jsonAfter = formatJsonString(JSON.parse(jsonBefore));
+ this.ricConfigInfo.setValue(jsonAfter);
+ }
+
+ getConfig() {
+ this.policyService.getConfiguration().subscribe((ricConfig: RicConfig) => {
+ this.ricConfig = formatJsonString(ricConfig);
+ let initialJson: RicConfig;
+ if (this.ricConfig) {
+ initialJson = this.ricConfig;
+ } else {
+ initialJson = "{}";
+ }
+ this.ricConfigInfo.setValue(initialJson);
+ });
+ }
+
+ updateRicConfig() {
+ let updateRic = this.ricConfigInfo.value.replace(/\n/g, '');
+ this.policyService.updateConfiguration(updateRic).subscribe((response: HttpResponse<Object>) => {
+ if (response.status === 200) {
+ this.notificationService.success("RIC Configuration Updated!");
+ }
+ });
+ }
+}
+
+export function formatJsonString(jsonToFormat: any): string {
+ return JSON.stringify(jsonToFormat, null, 2);
+}
--- /dev/null
+// -
+// ========================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 { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { MatTableModule } from '@angular/material/table';
+import { ReactiveFormsModule } from '@angular/forms';
+import { MatIconModule } from '@angular/material/icon';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatDialogModule } from '@angular/material/dialog';
+import { MatSelectModule } from '@angular/material/select';
+import { MaterialDesignFrameworkModule } from 'angular6-json-schema-form';
+import { Routes, RouterModule } from '@angular/router';
+import { MatSortModule } from '@angular/material/sort';
+import { MatInputModule } from '@angular/material/input';
+import { MatButtonModule } from '@angular/material/button';
+import { FlexLayoutModule } from '@angular/flex-layout';
+import { MatCardModule } from '@angular/material/card';
+import { MatTooltipModule } from '@angular/material/tooltip';
+import { RicConfigComponent } from './ric-config.component';
+
+const routes:Routes = [
+ {path: 'ric-config', component: RicConfigComponent}
+];
+
+@NgModule({
+ declarations: [
+ RicConfigComponent
+ ],
+ imports: [
+ BrowserAnimationsModule,
+ CommonModule,
+ FlexLayoutModule,
+ MatButtonModule,
+ MatCardModule,
+ MatDialogModule,
+ MatFormFieldModule,
+ MatIconModule,
+ MatInputModule,
+ MatSelectModule,
+ MatSortModule,
+ MaterialDesignFrameworkModule,
+ MatTableModule,
+ MatTooltipModule,
+ ReactiveFormsModule,
+ RouterModule.forChild(routes)
+ ],
+ exports: [
+ RicConfigComponent
+ ],
+})
+export class RicConfigModule { }
PolicyTypes,
} from "@interfaces/policy.types";
import { Rics } from "@interfaces/ric";
+import { RicConfig } from "@app/interfaces/ric.config";
+import { HttpHeaders } from "@angular/common/http";
/**
* Services for calling the policy endpoints.
const url = this.buildPath("rics") + "?policytype_id=" + policyTypeId;
return this.httpClient.get<any>(url);
}
+
+ getConfiguration(): Observable<RicConfig> {
+ const url = this.buildPath("configuration");
+ return this.httpClient.get<RicConfig>(url);
+ }
+
+ updateConfiguration(ricConfig: RicConfig): Observable<RicConfig> {
+ const httpOptions = {
+ headers: new HttpHeaders({'Content-Type': 'application/json'}),
+ observe: 'response' as 'body'
+ }
+ const url = this.buildPath("configuration");
+ return this.httpClient.put<RicConfig>(url, ricConfig, httpOptions);
+ }
}