Add table with automatic neighbor relation data 88/188/6
authorLott, Christopher (cl778h) <cl778h@att.com>
Mon, 20 May 2019 15:38:32 +0000 (11:38 -0400)
committerLott, Christopher (cl778h) <cl778h@att.com>
Tue, 28 May 2019 19:20:16 +0000 (15:20 -0400)
Includes edit and delete features.
Drops the xapp screen which was for pendulum control.

Bump version to 1.0.4

Change-Id: I64c5379164dd3a52f33c782d206f11d2b792b18e
Signed-off-by: Lott, Christopher (cl778h) <cl778h@att.com>
34 files changed:
a1-med-client/pom.xml
anr-xapp-client/pom.xml
docs/release-notes.rst
e2-mgr-client/pom.xml
pom.xml
webapp-backend/pom.xml
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AnrXappMockConfiguration.java
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AcXappController.java
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AnrXappController.java
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/E2ManagerController.java
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/HealthcheckController.java
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/XappManagerController.java
webapp-frontend/pom.xml
webapp-frontend/src/app/anr-xapp/anr-edit-ncr-dialog.component.html [new file with mode: 0644]
webapp-frontend/src/app/anr-xapp/anr-edit-ncr-dialog.component.scss [moved from webapp-frontend/src/app/xapp/xapp.component.spec.ts with 55% similarity]
webapp-frontend/src/app/anr-xapp/anr-edit-ncr-dialog.component.ts [new file with mode: 0644]
webapp-frontend/src/app/anr-xapp/anr-xapp.component.html
webapp-frontend/src/app/anr-xapp/anr-xapp.component.scss
webapp-frontend/src/app/anr-xapp/anr-xapp.component.spec.ts
webapp-frontend/src/app/anr-xapp/anr-xapp.component.ts
webapp-frontend/src/app/anr-xapp/anr-xapp.datasource.ts [new file with mode: 0644]
webapp-frontend/src/app/app-routing.module.ts
webapp-frontend/src/app/app.module.ts
webapp-frontend/src/app/control/control.component.html
webapp-frontend/src/app/control/control.component.ts
webapp-frontend/src/app/interfaces/anr-xapp.types.ts
webapp-frontend/src/app/interfaces/xapp-mgr.types.ts
webapp-frontend/src/app/navigation/sidenav-list/sidenav-list.component.html
webapp-frontend/src/app/services/anr-xapp/anr-xapp.service.ts
webapp-frontend/src/app/services/xapp-mgr/xapp-mgr.service.ts
webapp-frontend/src/app/xapp/xapp.component.html [deleted file]
webapp-frontend/src/app/xapp/xapp.component.scss [deleted file]
webapp-frontend/src/app/xapp/xapp.component.ts [deleted file]
xapp-mgr-client/pom.xml

index 8c106b0..974abcc 100644 (file)
@@ -25,7 +25,7 @@ limitations under the License.
        <parent>
                <groupId>org.o-ran-sc.portal.ric-dashboard</groupId>
                <artifactId>ric-dash-parent</artifactId>
-               <version>1.0.3-SNAPSHOT</version>
+               <version>1.0.4-SNAPSHOT</version>
        </parent>
        <!-- This groupId will NOT allow deployment in LF -->
        <groupId>org.o-ran-sc.ric.a1med.client</groupId>
index 83f79e2..2a73208 100644 (file)
@@ -25,7 +25,7 @@ limitations under the License.
        <parent>
                <groupId>org.o-ran-sc.portal.ric-dashboard</groupId>
                <artifactId>ric-dash-parent</artifactId>
-               <version>1.0.3-SNAPSHOT</version>
+               <version>1.0.4-SNAPSHOT</version>
        </parent>
        <!-- This groupId will NOT allow deployment in LF -->
        <groupId>org.o-ran-sc.ric.anrxapp.client</groupId>
index 915df84..7265c47 100644 (file)
 RIC Dashboard Release Notes
 ===========================
 
+Version 1.0.4, 29 May 2019
+--------------------------
+* Add ANR xApp neighbor cell relation table
+* Drop the pendulum xApp control screen
+
 Version 1.0.3, 28 May 2019
 --------------------------
 * Add AC xApp controller to backend
index b180f89..997c90b 100644 (file)
@@ -25,7 +25,7 @@ limitations under the License.
        <parent>
                <groupId>org.o-ran-sc.portal.ric-dashboard</groupId>
                <artifactId>ric-dash-parent</artifactId>
-               <version>1.0.3-SNAPSHOT</version>
+               <version>1.0.4-SNAPSHOT</version>
        </parent>
        <!-- This groupId will NOT allow deployment in LF -->
        <groupId>org.o-ran-sc.ric.e2mgr.client</groupId>
diff --git a/pom.xml b/pom.xml
index 1946823..4e6feaf 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@ limitations under the License.
        <artifactId>ric-dash-parent</artifactId>
        <name>RIC Dashboard project</name>
        <packaging>pom</packaging>
-       <version>1.0.3-SNAPSHOT</version>
+       <version>1.0.4-SNAPSHOT</version>
        <!-- Properties for the license-maven-plugin in child POMs -->
        <properties>
                <lmp.organization.name>AT&amp;T Intellectual Property and Nokia</lmp.organization.name>
index 77d3921..0f19b87 100644 (file)
@@ -25,7 +25,7 @@ limitations under the License.
        <parent>
                <groupId>org.o-ran-sc.portal.ric-dashboard</groupId>
                <artifactId>ric-dash-parent</artifactId>
-               <version>1.0.3-SNAPSHOT</version>
+               <version>1.0.4-SNAPSHOT</version>
        </parent>
        <artifactId>ric-dash-be</artifactId>
        <name>RIC Dashboard Webapp backend</name>
index 578107f..550963b 100644 (file)
@@ -32,6 +32,7 @@ import java.lang.invoke.MethodHandles;
 import org.oransc.ric.anrxapp.client.api.HealthApi;
 import org.oransc.ric.anrxapp.client.api.NcrtApi;
 import org.oransc.ric.anrxapp.client.invoker.ApiClient;
+import org.oransc.ric.anrxapp.client.model.GgNodeBTable;
 import org.oransc.ric.anrxapp.client.model.NeighborCellRelation;
 import org.oransc.ric.anrxapp.client.model.NeighborCellRelationMod;
 import org.oransc.ric.anrxapp.client.model.NeighborCellRelationTable;
@@ -52,9 +53,12 @@ public class AnrXappMockConfiguration {
        private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
        private final NeighborCellRelationTable ncrt, ncrtNodeB1, ncrtNodeB2;
+       private final GgNodeBTable gNodebTable;
 
        public AnrXappMockConfiguration() {
                logger.info("Configuring mock ANR xApp client");
+               gNodebTable = new GgNodeBTable();
+               gNodebTable.addGNodeBIdsItem("A").addGNodeBIdsItem("B");
                ncrtNodeB1 = new NeighborCellRelationTable();
                ncrtNodeB2 = new NeighborCellRelationTable();
                ncrt = new NeighborCellRelationTable();
@@ -81,11 +85,10 @@ public class AnrXappMockConfiguration {
        }
 
        @Bean
-       public HealthApi anrHealthMockApi() {
-               ApiClient mockClient = mock(ApiClient.class);
-               when(mockClient.getStatusCode()).thenReturn(HttpStatus.OK);
+       public HealthApi anrHealthApi() {
+               ApiClient apiClient = apiClient();
                HealthApi mockApi = mock(HealthApi.class);
-               when(mockApi.getApiClient()).thenReturn(mockClient);
+               when(mockApi.getApiClient()).thenReturn(apiClient);
                doAnswer(i -> {
                        return null;
                }).when(mockApi).getHealthAlive();
@@ -100,6 +103,7 @@ public class AnrXappMockConfiguration {
                ApiClient apiClient = apiClient();
                NcrtApi mockApi = mock(NcrtApi.class);
                when(mockApi.getApiClient()).thenReturn(apiClient);
+               when(mockApi.getgNodeB()).thenReturn(gNodebTable);
                // Swagger sends nulls; front end sends empty strings
                when(mockApi.getNcrt((String) isNull(), (String) isNull(), (String) isNull())).thenReturn(ncrt);
                when(mockApi.getNcrt(eq(""), any(String.class), any(String.class))).thenReturn(ncrt);
index c1aac8f..69864d9 100644 (file)
@@ -66,8 +66,7 @@ public class AcXappController {
 
        @ApiOperation(value = "Gets the A1 client library MANIFEST.MF property Implementation-Version.", response = SuccessTransport.class)
        @RequestMapping(value = DashboardConstants.VERSION_PATH, method = RequestMethod.GET)
-       public SuccessTransport getVersion() {
-               logger.debug("getVersion enter");
+       public SuccessTransport getA1MediatorClientVersion() {
                return new SuccessTransport(200, DashboardApplication.getImplementationVersion(A1MediatorApi.class));
        }
 
index 7f14cde..5b1a6e2 100644 (file)
@@ -79,23 +79,20 @@ public class AnrXappController {
 
        @ApiOperation(value = "Gets the ANR client library MANIFEST.MF property Implementation-Version.", response = SuccessTransport.class)
        @RequestMapping(value = DashboardConstants.VERSION_PATH, method = RequestMethod.GET)
-       public SuccessTransport getVersion() {
-               logger.debug("getVersion enter");
+       public SuccessTransport getAnrXappClientVersion() {
                return new SuccessTransport(200, DashboardApplication.getImplementationVersion(HealthApi.class));
        }
 
        @ApiOperation(value = "Performs a liveness probe on the ANR xApp, result expressed as the response code.")
        @RequestMapping(value = "/health/alive", method = RequestMethod.GET)
-       public void getHealthAlive(HttpServletResponse response) {
-               logger.debug("getHealthAlive");
+       public void getAnrXappHealthAlive(HttpServletResponse response) {
                healthApi.getHealthAlive();
                response.setStatus(healthApi.getApiClient().getStatusCode().value());
        }
 
        @ApiOperation(value = "Performs a readiness probe on the ANR xApp, result expressed as the response code.")
        @RequestMapping(value = "/health/ready", method = RequestMethod.GET)
-       public void getHealthReady(HttpServletResponse response) {
-               logger.debug("getHealthReady");
+       public void getAnrXappHealthReady(HttpServletResponse response) {
                healthApi.getHealthReady();
                response.setStatus(healthApi.getApiClient().getStatusCode().value());
        }
@@ -112,7 +109,7 @@ public class AnrXappController {
                        @RequestParam(name = QP_NODEB, required = false) String ggnbId, //
                        @RequestParam(name = QP_SERVING, required = false) String servingCellNrcgi, //
                        @RequestParam(name = QP_NEIGHBOR, required = false) String neighborCellNrpci) {
-               logger.debug("getNcrtInfo: ggnbid {}, servingCellNrpci {} neighborCellNrcgi {}", ggnbId, servingCellNrcgi,
+               logger.debug("getNcrtInfo: ggnbid {}, servingCellNrpci {}, neighborCellNrcgi {}", ggnbId, servingCellNrcgi,
                                neighborCellNrpci);
                return ncrtApi.getNcrt(ggnbId, servingCellNrcgi, neighborCellNrpci);
        }
index 97ea334..b9213ea 100644 (file)
@@ -90,15 +90,13 @@ public class E2ManagerController {
 
        @ApiOperation(value = "Gets the E2 manager client library MANIFEST.MF property Implementation-Version.", response = SuccessTransport.class)
        @RequestMapping(value = DashboardConstants.VERSION_PATH, method = RequestMethod.GET)
-       public SuccessTransport getVersion() {
-               logger.debug("getVersion enter");
+       public SuccessTransport getE2ManagerClientVersion() {
                return new SuccessTransport(200, DashboardApplication.getImplementationVersion(HealthCheckApi.class));
        }
 
        @ApiOperation(value = "Gets the health from the E2 manager, expressed as the response code.")
        @RequestMapping(value = "/health", method = RequestMethod.GET)
-       public void getHealth(HttpServletResponse response) {
-               logger.debug("getHealth");
+       public void getE2ManagerHealth(HttpServletResponse response) {
                e2HealthCheckApi.healthGet();
                response.setStatus(e2HealthCheckApi.getApiClient().getStatusCode().value());
        }
index f12c59a..235bcab 100644 (file)
@@ -24,8 +24,6 @@ import java.lang.invoke.MethodHandles;
 import org.oransc.ric.portal.dashboard.DashboardApplication;
 import org.oransc.ric.portal.dashboard.DashboardConstants;
 import org.oransc.ric.portal.dashboard.model.SuccessTransport;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
@@ -40,20 +38,16 @@ import io.swagger.annotations.ApiOperation;
 @RequestMapping(value = DashboardConstants.ENDPOINT_PREFIX + "/dashboard", produces = MediaType.APPLICATION_JSON_VALUE)
 public class HealthcheckController {
 
-       private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
        @ApiOperation(value = "Checks the health of the application by (TBD).", response = SuccessTransport.class)
        @RequestMapping(value = DashboardConstants.HEALTHCHECK_PATH, method = RequestMethod.GET)
-       public SuccessTransport getHealth() {
-               logger.debug("getHealth enter");
+       public SuccessTransport getDashblardHealth() {
                long count = 0;
                return new SuccessTransport(200, "(TBD) reports count is " + count);
        }
 
        @ApiOperation(value = "Gets the Dashboard MANIFEST.MF property Implementation-Version.", response = SuccessTransport.class)
        @RequestMapping(value = DashboardConstants.VERSION_PATH, method = RequestMethod.GET)
-       public SuccessTransport getVersion() {
-               logger.debug("getVersion enter");
+       public SuccessTransport getDashboardVersion() {
                return new SuccessTransport(200,
                                DashboardApplication.getImplementationVersion(MethodHandles.lookup().lookupClass()));
        }
index ad62bb0..efb296d 100644 (file)
@@ -75,8 +75,7 @@ public class XappManagerController {
 
        @ApiOperation(value = "Gets the XApp manager client library MANIFEST.MF property Implementation-Version.", response = SuccessTransport.class)
        @RequestMapping(value = DashboardConstants.VERSION_PATH, method = RequestMethod.GET)
-       public SuccessTransport getVersion() {
-               logger.debug("getVersion enter");
+       public SuccessTransport getXappManagerClientVersion() {
                return new SuccessTransport(200, DashboardApplication.getImplementationVersion(HealthApi.class));
        }
 
index 9a8e84e..29ddc86 100644 (file)
@@ -25,7 +25,7 @@ limitations under the License.
        <parent>
                <groupId>org.o-ran-sc.portal.ric-dashboard</groupId>
                <artifactId>ric-dash-parent</artifactId>
-               <version>1.0.3-SNAPSHOT</version>
+               <version>1.0.4-SNAPSHOT</version>
        </parent>
        <artifactId>ric-dash-fe</artifactId>
        <name>RIC Dashboard Webapp frontend</name>
diff --git a/webapp-frontend/src/app/anr-xapp/anr-edit-ncr-dialog.component.html b/webapp-frontend/src/app/anr-xapp/anr-edit-ncr-dialog.component.html
new file mode 100644 (file)
index 0000000..7f22819
--- /dev/null
@@ -0,0 +1,53 @@
+<!--
+  ========================LICENSE_START=================================
+  O-RAN-SC
+  %%
+  Copyright (C) 2019 AT&T Intellectual Property and Nokia
+  %%
+  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 mat-dialog-title>
+    Edit Neighbor Cell Relation
+</div>
+
+<form [formGroup]="ncrDialogForm" novalidate autocomplete="off" (ngSubmit)="modifyNcr(ncrDialogForm.value)">
+  <div mat-dialog-content>
+    <mat-form-field class="input-display-block">
+      <input matInput readonly type="text" placeholder="Serving cell NRCGI" formControlName="servingCellNrcgi">
+    </mat-form-field>
+    <mat-form-field class="input-display-block">
+      <input matInput readonly type="text" placeholder="Neighbor cell NRPCI" formControlName="neighborCellNrpci">
+    </mat-form-field>
+    <mat-form-field class="input-display-block">
+      <input matInput type="text" placeholder="Neighbor cell NRCGI" formControlName="neighborCellNrcgi">
+      <mat-hint align="end">Example: A12345</mat-hint>
+      <mat-error *ngIf="validateControl('neighborCellNrcgi') && hasError('neighborCellNrcgi', 'required')">Neighbor cell identifier is required</mat-error>
+      <mat-error *ngIf="hasError('neighborCellNrcgi', 'pattern')">Valid NRCGI is required</mat-error>
+    </mat-form-field>
+    <div name="flagNoHo">
+        <mat-checkbox formControlName="flagNoHo">Flag No Handover</mat-checkbox>
+    </div>
+    <div name="flagNoXn">
+        <mat-checkbox formControlName="flagNoXn">Flag No Transaction</mat-checkbox>
+    </div>
+    <div name="flagNoRemove">
+      <mat-checkbox formControlName="flagNoRemove">Flag No Remove</mat-checkbox>
+    </div>
+</div>
+  <div mat-dialog-actions class="modal-footer justify-content-center">
+    <button class="mat-raised-button" (click)="onCancel()">Cancel</button>
+    <button class="mat-raised-button mat-primary" [disabled]="!ncrDialogForm.valid">Save</button>
+  </div>
+</form>
@@ -7,9 +7,9 @@
  * 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.
  * limitations under the License.
  * ========================LICENSE_END===================================
  */
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 
-import { XappComponent } from './xapp.component';
-
-describe('XappComponent', () => {
-  let component: XappComponent;
-  let fixture: ComponentFixture<XappComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ XappComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(XappComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
+ /* used to place form fields on separate lines/rows in dialog */
+.input-display-block {
+    display: block;
+  }
\ No newline at end of file
diff --git a/webapp-frontend/src/app/anr-xapp/anr-edit-ncr-dialog.component.ts b/webapp-frontend/src/app/anr-xapp/anr-edit-ncr-dialog.component.ts
new file mode 100644 (file)
index 0000000..2857f55
--- /dev/null
@@ -0,0 +1,93 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property and Nokia
+ * %%
+ * 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, Inject } from '@angular/core';
+import { FormGroup, FormControl, Validators } from '@angular/forms';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { ANRXappService } from '../services/anr-xapp/anr-xapp.service';
+import { ErrorDialogService } from '../services/ui/error-dialog.service';
+import { ANRNeighborCellRelation, ANRNeighborCellRelationMod } from '../interfaces/anr-xapp.types';
+import { modelGroupProvider } from '@angular/forms/src/directives/ng_model_group';
+
+@Component({
+    selector: 'app-ncr-edit-dialog',
+    templateUrl: './anr-edit-ncr-dialog.component.html',
+    styleUrls: ['./anr-edit-ncr-dialog.component.scss']
+})
+
+export class ANREditNCRDialogComponent implements OnInit {
+
+    private ncrDialogForm: FormGroup;
+
+    constructor(
+        private dialogRef: MatDialogRef<ANREditNCRDialogComponent>,
+        private dataService: ANRXappService, private errorService: ErrorDialogService,
+        @Inject(MAT_DIALOG_DATA) private data: ANRNeighborCellRelation) {
+        console.log('constructed with data ' + data);
+    }
+
+    ngOnInit() {
+        const namePattern = /^([A-Z])+([0-9])+$/;
+        this.ncrDialogForm = new FormGroup({
+            servingCellNrcgi: new FormControl(this.data.servingCellNrcgi), // readonly
+            neighborCellNrpci: new FormControl(this.data.neighborCellNrpci), // readonly
+            neighborCellNrcgi: new FormControl(this.data.neighborCellNrcgi, [Validators.required, Validators.pattern(namePattern)]),
+            flagNoHo: new FormControl(this.data.flagNoHo),
+            flagNoXn: new FormControl(this.data.flagNoXn),
+            flagNoRemove: new FormControl(this.data.flagNoRemove)
+        });
+    }
+
+    onCancel() {
+        this.dialogRef.close();
+    }
+
+    modifyNcr = (ncrFormValue: ANRNeighborCellRelation) => {
+      if (this.ncrDialogForm.valid) {
+        const ncrm = {} as ANRNeighborCellRelationMod;
+        // there must be a btter way
+        ncrm.neighborCellNrcgi = ncrFormValue.neighborCellNrcgi;
+        ncrm.neighborCellNrpci = ncrFormValue.neighborCellNrpci;
+        ncrm.flagNoHo = ncrFormValue.flagNoHo;
+        ncrm.flagNoXn = ncrFormValue.flagNoXn;
+        ncrm.flagNoRemove = ncrFormValue.flagNoRemove;
+        this.dataService.modifyNcr(ncrFormValue.servingCellNrcgi, ncrFormValue.neighborCellNrpci, ncrm).subscribe((val: any[]) => {},
+            (error => {
+                this.errorService.displayError('NCR update failed: ' + error.message);
+            })
+        );
+        this.dialogRef.close();
+      }
+    }
+
+    public hasError(controlName: string, errorName: string) {
+        if (this.ncrDialogForm.controls[controlName].hasError(errorName)) {
+          return true;
+        }
+        return false;
+    }
+
+    public validateControl(controlName: string) {
+        if (this.ncrDialogForm.controls[controlName].invalid && this.ncrDialogForm.controls[controlName].touched) {
+            return true;
+        }
+        return false;
+    }
+
+}
index 0684ac7..a245b2d 100644 (file)
@@ -7,9 +7,9 @@
   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.
   ========================LICENSE_END===================================
 -->
 <div class="anr__section">
-  <h3 class="anr__header">ANR xApp</h3>
-  <ng2-smart-table [settings]="settings" [source]="source" (custom)="onAnrControlAction($event)">
-  </ng2-smart-table>
-  <app-modal-event hidden></app-modal-event>
-</div>
+  <h3 class="anr__header">ANR xApp Neighbor Cell Relation Table</h3>
+
+  <mat-form-field>
+    <mat-label>GgNodeB</mat-label>
+    <!-- use a native selection widget -->
+    <select matNativeControl #ggNodeB (selectionChange)="loadNcrtPage()">
+      <option value="" selected>Select..</option>
+      <option *ngFor="let g of gNodeBIds" [value]="g">{{g}}</option>
+    </select>
+  </mat-form-field>
+  <mat-form-field class="input-pad-left">
+    <input matInput placeholder="Serving Cell NRCGI" #servingCellNrcgi>
+  </mat-form-field>
+  <mat-form-field class="input-pad-left">
+      <input matInput placeholder="Neighbor cell NRPCI" #neighborCellNrpci>
+  </mat-form-field>
+
+  <div class="spinner-container" *ngIf="dataSource.loading$ | async">
+    <mat-spinner></mat-spinner>
+  </div>
+
+  <table mat-table class="ncr-table mat-elevation-z8" [dataSource]="dataSource">
+
+    <ng-container matColumnDef="cellIdentifierNrcgi">
+        <mat-header-cell *matHeaderCellDef>Serving Cell NRCGI</mat-header-cell>
+        <mat-cell *matCellDef="let ncr">{{ncr.servingCellNrcgi}}</mat-cell>
+    </ng-container>
+
+    <ng-container matColumnDef="neighborCellNrpci">
+        <mat-header-cell *matHeaderCellDef>Neighbor Cell NRPCI</mat-header-cell>
+        <mat-cell *matCellDef="let ncr">{{ncr.neighborCellNrpci}}</mat-cell>
+    </ng-container>
+    <ng-container matColumnDef="neighborCellNrcgi">
+        <mat-header-cell *matHeaderCellDef>Neighbor Cell NRCGI</mat-header-cell>
+        <mat-cell *matCellDef="let ncr">{{ncr.neighborCellNrcgi}}</mat-cell>
+    </ng-container>
+
+    <ng-container matColumnDef="flagNoHo">
+        <mat-header-cell *matHeaderCellDef>Flag No Handover</mat-header-cell>
+        <mat-cell *matCellDef="let ncr">{{ncr.flagNoHo}}</mat-cell>
+    </ng-container>
 
+    <ng-container matColumnDef="flagNoXn">
+        <mat-header-cell *matHeaderCellDef>Flag No Xn</mat-header-cell>
+        <mat-cell *matCellDef="let ncr">{{ncr.flagNoXn}}</mat-cell>
+    </ng-container>
+
+    <ng-container matColumnDef="flagNoRemove">
+        <mat-header-cell *matHeaderCellDef>Flag No Remove</mat-header-cell>
+        <mat-cell *matCellDef="let ncr">{{ncr.flagNoRemove}}</mat-cell>
+    </ng-container>
+
+    <ng-container matColumnDef="action">
+      <mat-header-cell *matHeaderCellDef>Action</mat-header-cell>
+      <mat-cell class="action-cell" *matCellDef="let ncr">
+        <button mat-icon-button (click)="modifyNcr(ncr)">
+          <mat-icon>edit</mat-icon>
+        </button>
+        <button mat-icon-button color="warn" (click)="deleteNcr(ncr)">
+          <mat-icon>delete</mat-icon>
+             </button>
+           </mat-cell>
+         </ng-container>
+
+    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
+
+    <mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
+
+  </table>
+
+  <div class="version__text">
+      ANR client version {{anrClientVersion}}
+  </div>
+</div>
index 183d90d..bcac3a0 100644 (file)
@@ -7,9 +7,9 @@
  * 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.
@@ -19,7 +19,7 @@
  */
  .anr__section {
     position: relative;
-    top: -150px;
+    top: -50px;
 }
 
 .anr__header {
     transform: translate(149 56);
 }
 
-:host /deep/ ng2-smart-table tbody > tr > td{
-  text-align: left;
+.spinner-container {
+    height: 360px;
+    width: 390px;
+    position: fixed;
 }
 
-:host /deep/ ng2-smart-table thead th{
-  text-align: left;
+.spinner-container mat-spinner {
+    margin: 130px auto 0 auto;
+}
+
+.ncr-table {
+    width: 99%; /* 100 looks wrong */
+    min-height: 150px;
+    margin-top: 10px;
+    background-color:transparent;
+}
+
+.ncr-table__bg-dark {
+    color: white;
+}
+
+.input-pad-left {
+    padding-left: 10px;
+}
+
+.input-pad-right {
+    padding-right: 10px;
+}
+
+.version__text {
+    color: gray;
+    letter-spacing: 0.1rem;
+    font-size: 10px;
 }
index 6a156fd..173ecd2 100644 (file)
@@ -7,9 +7,9 @@
  * 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.
index efe0992..d173505 100644 (file)
@@ -7,9 +7,9 @@
  * 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.
  * limitations under the License.
  * ========================LICENSE_END===================================
  */
-import { Component, OnInit } from '@angular/core';
+
+import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
+import { MatDialog } from '@angular/material/dialog';
+import { fromEvent } from 'rxjs/observable/fromEvent';
+import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators';
+import { ANRNeighborCellRelation } from '../interfaces/anr-xapp.types';
+import { ANRXappDataSource } from './anr-xapp.datasource';
+import { ANRXappService } from '../services/anr-xapp/anr-xapp.service';
+import { ANREditNCRDialogComponent } from './anr-edit-ncr-dialog.component';
+import { ConfirmDialogService } from './../services/ui/confirm-dialog.service';
+import { ErrorDialogService } from '../services/ui/error-dialog.service';
+import { NotificationService } from './../services/ui/notification.service';
 
 @Component({
-  selector: 'app-anr-xapp',
+  selector: 'app-anr',
   templateUrl: './anr-xapp.component.html',
   styleUrls: ['./anr-xapp.component.scss']
 })
-export class AnrXappComponent implements OnInit {
+export class AnrXappComponent implements AfterViewInit, OnInit {
+
+  dataSource: ANRXappDataSource;
+  anrClientVersion: string;
+  gNodeBIds: string[];
+  @ViewChild('ggNodeB') ggNodeB: ElementRef;
+  @ViewChild('servingCellNrcgi') servingCellNrcgi: ElementRef;
+  @ViewChild('neighborCellNrpci') neighborCellNrpci: ElementRef;
+
+  displayedColumns = ['cellIdentifierNrcgi', 'neighborCellNrpci', 'neighborCellNrcgi',
+    'flagNoHo', 'flagNoXn', 'flagNoRemove', 'action'];
 
-  constructor() { }
+  constructor(
+    private anrXappService: ANRXappService,
+    private dialog: MatDialog,
+    private confirmDialogService: ConfirmDialogService,
+    private errorDialogService: ErrorDialogService,
+    private notificationService: NotificationService) { }
 
   ngOnInit() {
+    this.dataSource = new ANRXappDataSource(this.anrXappService);
+    this.dataSource.loadTable();
+    // Empty string occurs first in the array of gNodeBIds
+    this.anrXappService.getgNodeBs().subscribe((res: string[]) => this.gNodeBIds = res);
+    this.anrXappService.getVersion().subscribe((res: string) => this.anrClientVersion = res);
+  }
+
+  ngAfterViewInit() {
+    // the selector event calls loadNcrtPage() directly.
+    fromEvent(this.servingCellNrcgi.nativeElement, 'keyup')
+      .pipe(
+        debounceTime(150),
+        distinctUntilChanged(),
+        tap(() => {
+          this.loadNcrtPage();
+        })
+      )
+      .subscribe();
+    fromEvent(this.neighborCellNrpci.nativeElement, 'keyup')
+      .pipe(
+        debounceTime(150),
+        distinctUntilChanged(),
+        tap(() => {
+          this.loadNcrtPage();
+        })
+      )
+      .subscribe();
+  }
+
+  loadNcrtPage() {
+    this.dataSource.loadTable(
+      this.ggNodeB.nativeElement.value,
+      this.servingCellNrcgi.nativeElement.value,
+      this.neighborCellNrpci.nativeElement.value);
+  }
+
+  modifyNcr(ncr: ANRNeighborCellRelation): void {
+    const dialogRef = this.dialog.open(ANREditNCRDialogComponent, {
+      width: '300px',
+      data: ncr
+    });
+    dialogRef.afterClosed().subscribe(result => {
+      this.loadNcrtPage();
+    });
+  }
+
+  deleteNcr(ncr: ANRNeighborCellRelation): void {
+    this.confirmDialogService
+      .openConfirmDialog('Are you sure you want to delete this relation?')
+      .afterClosed().subscribe(res => {
+        if (res) {
+          this.anrXappService.deleteNcr(ncr.servingCellNrcgi, ncr.neighborCellNrpci)
+            .subscribe(
+              response => {
+                switch (response.status) {
+                  case 200:
+                    this.notificationService.success('Delete succeeded!');
+                    break;
+                  default:
+                    this.notificationService.warn('Delete failed.');
+                }
+              },
+              error => {
+                this.errorDialogService.displayError(error.message);
+              });
+        }
+      });
   }
 
 }
diff --git a/webapp-frontend/src/app/anr-xapp/anr-xapp.datasource.ts b/webapp-frontend/src/app/anr-xapp/anr-xapp.datasource.ts
new file mode 100644 (file)
index 0000000..bca766c
--- /dev/null
@@ -0,0 +1,61 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property and Nokia
+ * %%
+ * 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 { CollectionViewer, DataSource} from '@angular/cdk/collections';
+import { Observable } from 'rxjs/Observable';
+import { catchError, finalize } from 'rxjs/operators';
+import { of } from 'rxjs/observable/of';
+import { BehaviorSubject } from 'rxjs/BehaviorSubject';
+import { ANRNeighborCellRelation } from '../interfaces/anr-xapp.types';
+import { ANRXappService } from '../services/anr-xapp/anr-xapp.service';
+
+// https://blog.angular-university.io/angular-material-data-table/
+export class ANRXappDataSource extends DataSource<ANRNeighborCellRelation> {
+
+    private relationsSubject = new BehaviorSubject<ANRNeighborCellRelation[]>([]);
+
+    private loadingSubject = new BehaviorSubject<boolean>(false);
+
+    public loading$ = this.loadingSubject.asObservable();
+
+    constructor(private anrXappService: ANRXappService) {
+        super();
+    }
+
+    loadTable(ggnodeb = '', servingCellNrcgi = '', neighborCellNrpci = '') {
+        this.loadingSubject.next(true);
+        this.anrXappService.getNcrtInfo(ggnodeb, servingCellNrcgi, neighborCellNrpci)
+            .pipe(
+                catchError(() => of([])),
+                finalize(() => this.loadingSubject.next(false))
+            )
+            .subscribe(ncrt => this.relationsSubject.next(ncrt));
+    }
+
+    connect(collectionViewer: CollectionViewer): Observable<ANRNeighborCellRelation[]> {
+        return this.relationsSubject.asObservable();
+    }
+
+    disconnect(collectionViewer: CollectionViewer): void {
+        this.relationsSubject.complete();
+        this.loadingSubject.complete();
+    }
+
+}
index 3fa11e6..130703c 100644 (file)
@@ -26,7 +26,7 @@ import { ControlComponent } from './control/control.component';
 import { RANConnectionComponent } from './ran-connection/ran-connection.component';
 import { StatsComponent } from './stats/stats.component';
 import { AdminComponent } from './admin/admin.component';
-import { XappComponent } from './xapp/xapp.component';
+import { AnrXappComponent } from './anr-xapp/anr-xapp.component';
 
 const routes: Routes = [
     {path: '', component: LoginComponent},
@@ -36,7 +36,7 @@ const routes: Routes = [
     {path: 'ran-connection', component: RANConnectionComponent},
     {path: 'stats', component: StatsComponent},
     {path: 'admin', component: AdminComponent},
-    {path: 'xapp', component: XappComponent},
+    {path: 'anr', component: AnrXappComponent},
 ];
 
 @NgModule({
index 8bd8b4a..ab3afd0 100644 (file)
  */
 import { BrowserModule } from '@angular/platform-browser';
 // tslint:disable-next-line:max-line-length
-import {MatButtonModule, MatButtonToggleModule, MatCardModule, MatDialogModule,
-    MatExpansionModule, MatFormFieldModule, MatGridListModule, MatIconModule,
-    MatInputModule, MatListModule, MatPaginatorModule, MatProgressSpinnerModule,
-    MatSidenavModule, MatSliderModule, MatSlideToggleModule, MatSnackBarModule,
-    MatSortModule,MatTableModule, MatTabsModule} from '@angular/material';
+import {MatButtonModule, MatButtonToggleModule, MatCardModule, MatCheckboxModule,
+    MatDialogModule, MatExpansionModule, MatFormFieldModule, MatGridListModule,
+    MatIconModule, MatInputModule, MatListModule, MatPaginatorModule,
+    MatProgressSpinnerModule, MatSelectModule, MatSidenavModule, MatSliderModule,
+    MatSlideToggleModule, MatSnackBarModule, MatSortModule, MatTableModule,
+    MatTabsModule} from '@angular/material';
 import { BrowserAnimationsModule} from '@angular/platform-browser/animations';
 import { NgModule } from '@angular/core';
-import { Ng2SmartTableModule } from 'ng2-smart-table';
 import { MatRadioModule } from '@angular/material/radio';
 import { ChartsModule } from 'ng2-charts';
 import { MDBBootstrapModule } from 'angular-bootstrap-md';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 
+// RETIRE THIS
+import { Ng2SmartTableModule } from 'ng2-smart-table';
+
 import { AppRoutingModule } from './app-routing.module';
 import { AppComponent } from './app.component';
 import { LoginComponent } from './login/login.component';
@@ -45,13 +48,13 @@ import { SidenavListComponent } from './navigation/sidenav-list/sidenav-list.com
 import { ControlComponent } from './control/control.component';
 import { RANConnectionDialogComponent } from './ran-connection/ran-connection-dialog.component';
 import { RANConnectionComponent } from './ran-connection/ran-connection.component';
+import { ANREditNCRDialogComponent } from './anr-xapp/anr-edit-ncr-dialog.component';
 import { StatsComponent } from './stats/stats.component';
 import { AdminComponent } from './admin/admin.component';
 import { CatalogCardComponent } from './ui/catalog-card/catalog-card.component';
 import { ControlCardComponent } from './ui/control-card/control-card.component';
 import { StatCardComponent } from './ui/stat-card/stat-card.component';
 import { ModalEventComponent } from './ui/modal-event/modal-event.component';
-import { XappComponent } from './xapp/xapp.component';
 import { ConfigEventComponent } from './ui/config-event/config-event.component';
 import { ConfirmDialogComponent } from './ui/confirm-dialog/confirm-dialog.component';
 import { FooterComponent } from './footer/footer.component';
@@ -73,41 +76,44 @@ import { ErrorDialogService } from './services/ui/error-dialog.service';
     StatsComponent,
     AdminComponent,
     ModalEventComponent,
-    XappComponent,
     ConfigEventComponent,
     AnrXappComponent,
     RANConnectionDialogComponent,
+    ANREditNCRDialogComponent,
     ConfirmDialogComponent,
     FooterComponent,
     ErrorDialogComponent
   ],
     imports: [
+    AppRoutingModule,
     BrowserModule,
     BrowserAnimationsModule,
     ChartsModule,
-    AppRoutingModule,
     FormsModule,
-    MatDialogModule,
-    ReactiveFormsModule,
+    MatButtonModule,
     MatButtonToggleModule,
-    MatExpansionModule,
-    MatRadioModule,
-    MatSliderModule,
     MatCardModule,
-    MatIconModule,
+    MatCheckboxModule,
+    MatDialogModule,
+    MatExpansionModule,
+    MatFormFieldModule,
     MatGridListModule,
+    MatIconModule,
+    MatInputModule,
     MatListModule,
+    MatPaginatorModule,
+    MatProgressSpinnerModule,
+    MatRadioModule,
+    MatSelectModule,
+    MatSliderModule,
     MatSidenavModule,
     MatSlideToggleModule,
+    MatSnackBarModule,
+    MatSortModule,
     MatTableModule,
     MatTabsModule,
-    MatSortModule,
-    MatFormFieldModule,
-    MatButtonModule,
-    MatInputModule,
-    MatProgressSpinnerModule,
     Ng2SmartTableModule,
-    MatSnackBarModule,
+    ReactiveFormsModule,
     MDBBootstrapModule.forRoot(),
   ],
     exports: [
@@ -131,6 +137,7 @@ import { ErrorDialogService } from './services/ui/error-dialog.service';
     ],
     entryComponents: [
     RANConnectionDialogComponent,
+    ANREditNCRDialogComponent,
     ConfirmDialogComponent,
     ErrorDialogComponent
     ],
index d29191c..1e7bca2 100644 (file)
 
     <ng-container matColumnDef="action">
       <mat-header-cell *matHeaderCellDef> Action </mat-header-cell>
-      <mat-cell *matCellDef="let element">
-        <button mat-icon-button
-                (click)="view()">
+      <!-- click on button should not expand/collapse the row -->
+      <mat-cell *matCellDef="let element" (click)="$event.stopPropagation()">
+        <button mat-icon-button (click)="controlApp(element)">
           <mat-icon>settings</mat-icon>
         </button>
-        <button mat-icon-button
-                color="warn"
-                (click)="undeploy(element.xapp)">
+        <button mat-icon-button color="warn" (click)="undeployApp(element)">
           <mat-icon>delete</mat-icon>
         </button>
       </mat-cell>
@@ -85,8 +83,8 @@
 
     <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
     <mat-row *matRowDef="let element; columns: displayedColumns;"
-             [class.example-expanded-row]="expandedElement === element"
-             (click)="expandedElement = expandedElement === element ? null : element"></mat-row>
+      [class.example-expanded-row]="expandedElement === element"
+      (click)="expandedElement = expandedElement === element ? null : element"></mat-row>
     <tr mat-row *matRowDef="let row; columns: ['expandedDetail']" class="message-row"></tr>
   </table>
-</div>
+</div>
\ No newline at end of file
index 54ce96d..29a88cc 100644 (file)
@@ -7,9 +7,9 @@
  * 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.
 import { Component, OnInit } from '@angular/core';
 import { XappMgrService } from '../services/xapp-mgr/xapp-mgr.service';
 import { Router } from '@angular/router';
-import { ConfirmDialogService } from './../services/ui/confirm-dialog.service'
-import { NotificationService } from './../services/ui/notification.service'
-import { XMXapp } from '../interfaces/xapp-mgr.types';
+import { ErrorDialogService } from './../services/ui/error-dialog.service';
+import { ConfirmDialogService } from './../services/ui/confirm-dialog.service';
+import { NotificationService } from './../services/ui/notification.service';
+import { XappControlRow } from '../interfaces/xapp-mgr.types';
 import { ControlAnimations } from './control.animations';
 import { ControlDataSource } from './control.datasource';
-
+import { routerNgProbeToken } from '@angular/router/src/router_module';
 
 @Component({
   selector: 'app-control',
@@ -42,6 +43,7 @@ export class ControlComponent implements OnInit {
     private xappMgrSvc: XappMgrService,
     private router: Router,
     private confirmDialogService: ConfirmDialogService,
+    private errorDialogService: ErrorDialogService,
     private notification: NotificationService) { }
 
   ngOnInit() {
@@ -49,16 +51,20 @@ export class ControlComponent implements OnInit {
     this.dataSource.loadTable();
   }
 
-  view(): void {
-    const url = '/xapp';
-    this.router.navigate([url]);
+  controlApp(app: XappControlRow): void {
+    const anrXappPattern = /[Aa][Nn][Rr]/;
+    if (anrXappPattern.test(app.xapp)) {
+      this.router.navigate(['/anr']);
+    } else {
+      this.errorDialogService.displayError('No control available for ' + app.xapp + ' (yet)');
+    }
   }
 
-  undeploy(name: string): void {
-    this.confirmDialogService.openConfirmDialog('Are you sure you want to undeploy this xApp ?')
+  undeployApp(app: XappControlRow): void {
+    this.confirmDialogService.openConfirmDialog('Are you sure you want to undeploy xApp ' + app.xapp + '?')
       .afterClosed().subscribe(res => {
         if (res) {
-          this.xappMgrSvc.undeployXapp(name).subscribe(
+          this.xappMgrSvc.undeployXapp(app.xapp).subscribe(
             response => {
               this.dataSource.loadTable();
               switch (response.status) {
@@ -74,6 +80,4 @@ export class ControlComponent implements OnInit {
       });
   }
 
-
-
 }
index f0a5a43..078753f 100644 (file)
 
 // Models of data used by the ANR xApp
 
+export interface ANRGgNodeBTable {
+  gNodeBIds: Array<string>;
+}
+
+export interface ANRNeighborCellRelationTable {
+  ncrtRelations: Array<ANRNeighborCellRelation>;
+}
+
 export interface ANRNeighborCellRelation {
-  cellIdentifierNrcgi: string;
+  servingCellNrcgi: string;
   neighborCellNrpci: string;
   neighborCellNrcgi: string;
   flagNoHo: boolean;
@@ -29,15 +37,8 @@ export interface ANRNeighborCellRelation {
   flagNoRemove: boolean;
 }
 
-export interface ANRNeighborCellRelationDel {
-  idType: string;
-  neighborCellNrpci: string;
-  neighborCellNrcgi: string;
-}
-
 export interface ANRNeighborCellRelationMod {
-  neighbourCellIdentifierType: string;
-  action: string;
+  servingCellNrcgi: string;
   neighborCellNrpci: string;
   neighborCellNrcgi: string;
   flagNoHo: boolean;
index 7654377..1c3cc38 100644 (file)
@@ -51,4 +51,4 @@ export interface XMXapp {
 export interface XappControlRow {
   xapp: string;
   instance: XMXappInstance;
-}
\ No newline at end of file
+}
index 7df8c47..8e8a75e 100644 (file)
@@ -17,6 +17,8 @@
   limitations under the License.
   ========================LICENSE_END===================================
   -->
+
+<!-- browse icons at https://material.io/tools/icons/?style=baseline -->
 <mat-nav-list>
   <a mat-list-item routerLink="/login" (click)="onSidenavClose()">
       <mat-icon>home</mat-icon> <span class="nav-caption">Home</span>
index f9403f1..a2dfb75 100644 (file)
 import { Injectable } from '@angular/core';
 import { HttpClient, HttpParams } from '@angular/common/http';
 import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
 import { DashboardSuccessTransport } from '../../interfaces/dashboard.types';
 import { ANRNeighborCellRelation, ANRNeighborCellRelationMod } from '../../interfaces/anr-xapp.types';
 
 @Injectable({
   providedIn: 'root'
 })
-export class AnrXappService {
+export class ANRXappService {
 
-  private basePath = 'api/xapp/anr/';
-  private cellPath = 'cell/cellIdentifier/';
+  // Trailing slashes are confusing so omit them here
+  private basePath = 'api/xapp/anr';
+  private ncrtPath = 'ncrt';
+  private servingPath = 'servingcells';
+  private neighborPath = 'neighborcells';
 
   constructor(private httpClient: HttpClient) {
     // injects to variable httpClient
   }
 
+  private buildPath(...args: any[]) {
+    let result = this.basePath;
+    args.forEach(part => {
+      result = result + '/' + part;
+    });
+    return result;
+  }
+
   /**
    * Gets ANR xApp client version details
    * @returns Observable that should yield a DashboardSuccessTransport
    */
-  getVersion(): Observable<DashboardSuccessTransport> {
-    return this.httpClient.get<DashboardSuccessTransport>(this.basePath + 'version');
+  getVersion(): Observable<string> {
+    const url = this.buildPath('version');
+    return this.httpClient.get<DashboardSuccessTransport>(url).pipe(
+      // Extract the string here
+      map(res => res['data'])
+    );
   }
 
   /**
@@ -49,7 +65,8 @@ export class AnrXappService {
    * @returns Observable that should yield a response code (no data)
    */
   getHealthAlive(): Observable<any> {
-    return this.httpClient.get(this.basePath + 'health/alive');
+    const url = this.buildPath('health/alive');
+    return this.httpClient.get(url);
   }
 
   /**
@@ -57,62 +74,63 @@ export class AnrXappService {
    * @returns Observable that should yield a response code (no data)
    */
   getHealthReady(): Observable<any> {
-    return this.httpClient.get(this.basePath + 'health/ready');
+    const url = this.buildPath('health/ready');
+    return this.httpClient.get(url);
+  }
+
+    /**
+   * Gets ANR xApp client version details
+   * @returns Observable that should yield a DashboardSuccessTransport
+   */
+  getgNodeBs(): Observable<string[]> {
+    const url = this.buildPath('gnodebs');
+    return this.httpClient.get<string[]>(url).pipe(
+      // Extract the array of IDs here
+      map(res => res['gNodeBIds'])
+    );
   }
 
   /**
-   * Query NCRT of all cells, all or one gNB(s)
+   * Gets the neighbor cell relation table for all gNodeBs or based on query parameters
    * @param ggnbId Optional parameter for the gNB ID
-   * @param startIndex Optional parameter for the start index
-   * @param limit Optional parameter for the limit (page size)
-   * @returns Observable of ANRNeighborCellRelation
+   * @param servingCellNrcgi Serving cell NRCGI
+   * @param neighborCellNrpci Neighbor cell NRPCI
+   * @returns Neighbor cell relation table, which wraps an array
    */
-  getNcrtInfo(ggnbId?: string, startIndex?: string, limit?: number): Observable<ANRNeighborCellRelation[]> {
-    const queryParams = new HttpParams();
-    if (ggnbId) {
-      queryParams.set('ggnbid', ggnbId);
-    }
-    if (startIndex) {
-      queryParams.set('startIndex', startIndex);
-    }
-    if (limit) {
-      queryParams.set('limit', limit.toString());
-    }
-    return this.httpClient.get<ANRNeighborCellRelation[]>(this.basePath + 'cell', { params: queryParams } );
+  getNcrtInfo(ggnodeb: string = '', servingCellNrcgi: string = '', neighborCellNrpci: string = ''): Observable<ANRNeighborCellRelation[]> {
+    const url = this.buildPath(this.ncrtPath);
+    return this.httpClient.get<ANRNeighborCellRelation[]>(url, {
+      params: new HttpParams()
+        .set('ggnodeb', ggnodeb)
+        .set('servingCellNrcgi', servingCellNrcgi)
+        .set('neighborCellNrpci', neighborCellNrpci)
+    }).pipe(
+      // Extract the array of relations here
+      map(res => res['ncrtRelations'])
+    );
   }
 
   /**
-   * Query NCRT of a single serving cell, all or one gNB(s)
-   * @param cellId cell ID
-   * @param ggnbid Optional parameter for the gNB ID
-   * @param startIndex Optional parameter for the start index
-   * @param limit Optional parameter for the limit (page size)
-   * @returns Observable of ANRNeighborCellRelation
+   * Modify neighbor cell relation based on Serving Cell NRCGI and Neighbor Cell NRPCI
+   * @param servingCellNrcgi Serving cell NRCGI
+   * @param neighborCellNrpci Neighbor cell NRPCI
+   * @param mod Values to store in the specified relation
+   * @returns Response code only, no data
    */
-  getCellNcrtInfo(cellId: string, ggnbId?: string, startIndex?: string, limit?: number): Observable<ANRNeighborCellRelation[]> {
-    const queryParams = new HttpParams();
-    if (ggnbId) {
-      queryParams.set('ggnbid', ggnbId);
-    }
-    if (startIndex) {
-      queryParams.set('startIndex', startIndex);
-    }
-    if (limit) {
-      queryParams.set('limit', limit.toString());
-    }
-    return this.httpClient.get<ANRNeighborCellRelation[]>(this.basePath + this.cellPath + cellId, { params: queryParams } );
+  modifyNcr(servingCellNrcgi: string, neighborCellNrpci: string, mod: ANRNeighborCellRelationMod): Observable<any> {
+    const url = this.buildPath(this.ncrtPath, this.servingPath, servingCellNrcgi, this.neighborPath, neighborCellNrpci);
+    return this.httpClient.put(url, mod, { observe: 'response' });
   }
 
   /**
-   * Modify neighbor cell relations based on Source Cell NR CGI and Target Cell NR PCI / NR CGI
-   * @param cellId cell ID
-   * @param table Array of ANRNeighborCellRelationMod
-   * @returns Observable that should yield a response code (no data)
+   * Deletes neighbor cell relation based on Serving Cell NRCGI and Neighbor Cell NRPCI
+   * @param servingCellNrcgi Serving cell NRCGI
+   * @param neighborCellNrpci Neighbor cell NRPCI
+   * @returns Response code only, no data
    */
-  modifyNcrt(cellId: string, table: ANRNeighborCellRelationMod []): Observable<any> {
-    return this.httpClient.put(this.basePath + this.cellPath + cellId, table);
+  deleteNcr(servingCellNrcgi: string, neighborCellNrpci: string): Observable<any> {
+    const url = this.buildPath(this.ncrtPath, this.servingPath, servingCellNrcgi, this.neighborPath, neighborCellNrpci);
+    return this.httpClient.delete(url, { observe: 'response' });
   }
 
-  /** TODO: deleteNcrt */
-
 }
index 13ae13d..48132d2 100644 (file)
@@ -34,7 +34,6 @@ export class XappMgrService {
 
   getAll(): Observable<XMXapp[]>{
     return this.httpClient.get<XMXapp[]>(this.basePath)
-
   }
 
   deployXapp(name: string) {
@@ -46,4 +45,4 @@ export class XappMgrService {
     return this.httpClient.delete((this.basePath + '/' + name), { observe: 'response' });
   }
 
-}
\ No newline at end of file
+}
diff --git a/webapp-frontend/src/app/xapp/xapp.component.html b/webapp-frontend/src/app/xapp/xapp.component.html
deleted file mode 100644 (file)
index 5058c9d..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-<!--
-  ========================LICENSE_START=================================
-  O-RAN-SC
-  %%
-  Copyright (C) 2019 AT&T Intellectual Property and Nokia
-  %%
-  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>
-    <p class="heading lead">Pendulum Control xApp</p>
-    <p>Pod ID: dc-ric-app-b8c6668d8-56bjb</p>
-    <p>Status: running</p>
-</div>
-
-<button type="button" mdbBtn color="default" rounded="true" data-toggle="modal" data-target="#basicExample"
-  (click)="frame.show()" mdbWavesEffect>Edit Configuration</button>
-
-<div mdbModal #frame="mdbModal" class="modal fade left" id="frameModalTop" tabindex="-1" role="dialog"
-  aria-labelledby="myModalLabel" aria-hidden="true" (opened)="onOpened($event)">
-  <div class="modal-dialog modal-notify modal-info modal-side modal-top-left" role="document">
-    <!--Content-->
-    <div class="modal-content">
-      <!--Header-->
-      <div class="modal-header">
-        <p class="heading lead">xApp Info</p>
-
-        <button type="button" class="close" data-dismiss="modal" aria-label="Close" (click)="frame.hide()">
-          <span aria-hidden="true" class="white-text">&times;</span>
-        </button>
-      </div>
-
-      <!--Body-->
-      <div class="modal-body">
-
-        <img src="../../../assets/intelligence.png" alt=""
-          class="img-fluid">
-
-        <div class="text-center">
-          <p>xApp Configuration</p>
-            <div class="md-form">
-                <textarea type="text" id="form8" class="md-textarea form-control" rows="1" mdbInput
-                    [formControl]="contactFormModalHelm"></textarea>
-                <label data-error="wrong" data-success="right" for="form8">Delay</label>
-            </div>
-            <div class="md-form">
-                <textarea type="text" id="form8" class="md-textarea form-control" rows="1" mdbInput
-                    [formControl]="contactFormModalHelm"></textarea>
-                <label data-error="wrong" data-success="right" for="form8">Load</label>
-            </div>
-        </div>
-      </div>
-
-      <!--Footer-->
-      <div class="modal-footer justify-content-center">
-        <a type="button" mdbBtn color="primary" class="waves-effect" mdbWavesEffect>
-            <mat-icon style="vertical-align: -21%;">launch</mat-icon> Update
-        </a>
-        <a type="button" mdbBtn color="primary" outline="true" class="waves-effect" mdbWavesEffect (click)="frame.hide()"
-          data-dismiss="modal">
-          <mat-icon style="vertical-align: -21%; size: 1em">close</mat-icon> Cancel</a>
-      </div>
-    </div>
-    <!--/.Content-->
-  </div>
-</div>
diff --git a/webapp-frontend/src/app/xapp/xapp.component.scss b/webapp-frontend/src/app/xapp/xapp.component.scss
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/webapp-frontend/src/app/xapp/xapp.component.ts b/webapp-frontend/src/app/xapp/xapp.component.ts
deleted file mode 100644 (file)
index f337b73..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*-
- * ========================LICENSE_START=================================
- * O-RAN-SC
- * %%
- * Copyright (C) 2019 AT&T Intellectual Property and Nokia
- * %%
- * 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, Input, OnInit , Output, EventEmitter  } from '@angular/core';
-import { FormControl, Validators } from '@angular/forms';
-import { ViewCell } from 'ng2-smart-table';
-
-@Component({
-  selector: 'app-xapp',
-  templateUrl: './xapp.component.html',
-  styleUrls: ['./xapp.component.scss']
-})
-export class XappComponent implements ViewCell, OnInit {
-
-    public renderValue;
-
-    @Input() value;
-    @Input() rowData: any;
-    @Output() save: EventEmitter<any> = new EventEmitter();
-    contactFormModalHelm = new FormControl('', Validators.required);
-    onOpened(event: any) {
-    console.log(event);
-            this.rowData = event.data;
-    }
-
-
-    constructor() {  }
-
-    ngOnInit() {
-        this.renderValue = this.value;
-
-    }
-
-    example() {
-        alert(this.renderValue);
-    }
-
-    onDeployxApp() {
-        this.save.emit(this.rowData);
-    }
-
-
-}
index 1b37bf5..86639f4 100644 (file)
@@ -25,7 +25,7 @@ limitations under the License.
        <parent>
                <groupId>org.o-ran-sc.portal.ric-dashboard</groupId>
                <artifactId>ric-dash-parent</artifactId>
-               <version>1.0.3-SNAPSHOT</version>
+               <version>1.0.4-SNAPSHOT</version>
        </parent>
        <!-- This groupId will NOT allow deployment in LF -->
        <groupId>org.o-ran-sc.ric.xappmgr.client</groupId>