Add configurable delay to mock endpoints 08/508/6
authorLott, Christopher (cl778h) <cl778h@att.com>
Fri, 12 Jul 2019 18:22:30 +0000 (14:22 -0400)
committerLott, Christopher (cl778h) <cl778h@att.com>
Mon, 15 Jul 2019 18:14:57 +0000 (14:14 -0400)
Extend all mocked methods with a configurable call
to Thread.sleep() to simulate the delays seen when
testing real implementations.
Clean up some minor front-end type definitions.

Change-Id: Ibd224696649641df27e82888181d89149f47c385
Signed-off-by: Lott, Christopher (cl778h) <cl778h@att.com>
21 files changed:
docs/release-notes.rst
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/A1MediatorMockConfiguration.java
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AnrXappMockConfiguration.java
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AppManagerMockConfiguration.java
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/E2ManagerMockConfiguration.java
webapp-frontend/src/app/anr-xapp/anr-xapp.component.ts
webapp-frontend/src/app/app-control/app-control.component.html
webapp-frontend/src/app/app-control/app-control.component.ts
webapp-frontend/src/app/catalog/catalog.component.html
webapp-frontend/src/app/catalog/catalog.component.scss
webapp-frontend/src/app/catalog/catalog.component.ts
webapp-frontend/src/app/interfaces/e2-mgr.types.ts
webapp-frontend/src/app/ran-control/ran-connection-dialog.component.html
webapp-frontend/src/app/ran-control/ran-connection-dialog.component.ts
webapp-frontend/src/app/ran-control/ran-control.component.html
webapp-frontend/src/app/ran-control/ran-control.component.ts
webapp-frontend/src/app/services/anr-xapp/anr-xapp.service.ts
webapp-frontend/src/app/services/app-mgr/app-mgr.service.ts
webapp-frontend/src/app/services/ui/confirm-dialog.service.ts
webapp-frontend/src/app/user/user.component.html
webapp-frontend/src/app/user/user.component.scss

index bb84273..5a3198d 100644 (file)
 RIC Dashboard Release Notes
 ===========================
 
-Version 1.2.0, 11 July 2019
+Version 1.2.0, 12 July 2019
 ---------------------------
 * Split URL properties into prefix/suffix parts
 * Add jacoco plugin to back-end for code coverage
 * Compile with Java version 11, run with image openjdk:11
 * Clean code of issues reported by Sonar
 * Drop mock RAN names feature that supported R1 testing
+* Extend mock endpoints to simulate delay seen in tests
 
 Version 1.0.5, 5 July 2019
 --------------------------
index 4b394e5..351f114 100644 (file)
@@ -43,6 +43,8 @@ import org.springframework.http.HttpStatus;
 public class A1MediatorMockConfiguration {
 
        private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+       // Simulate remote method delay for UI testing
+       private final int delayMs = 500;
 
        public A1MediatorMockConfiguration() {
                logger.info("Configuring mock A1 Mediator");
@@ -60,8 +62,16 @@ public class A1MediatorMockConfiguration {
                ApiClient apiClient = apiClient();
                A1MediatorApi mockApi = mock(A1MediatorApi.class);
                when(mockApi.getApiClient()).thenReturn(apiClient);
-               doAnswer(i -> null).when(mockApi).a1ControllerGetHandler(any(String.class));
-               doAnswer(i -> null).when(mockApi).a1ControllerPutHandler(any(String.class), any(Object.class));
+               doAnswer(inv -> {
+                       logger.debug("a1ControllerGetHandler sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return null;
+               }).when(mockApi).a1ControllerGetHandler(any(String.class));
+               doAnswer(inv -> {
+                       logger.debug("a1ControllerPutHandler sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return null;
+               }).when(mockApi).a1ControllerPutHandler(any(String.class), any(Object.class));
                return mockApi;
        }
 
index a1e276d..65960b9 100644 (file)
@@ -51,20 +51,19 @@ public class AnrXappMockConfiguration {
 
        private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
+       private static final String GNODEB1 = "001EF5:0045FE50";
+       private static final String GNODEB2 = "001EF6:0045FE51";
+       private static final String GNODEB3 = "001EF7:0045FE52";
        // Sonar wants separate declarations
        private final NeighborCellRelationTable ncrt;
        private final NeighborCellRelationTable ncrtNodeB1;
        private final NeighborCellRelationTable ncrtNodeB2;
        private final NeighborCellRelationTable ncrtNodeB3;
-
        private final GgNodeBTable gNodebTable;
-
-       private static final String GNODEB1 = "001EF5:0045FE50";
-       private static final String GNODEB2 = "001EF6:0045FE51";
-       private static final String GNODEB3 = "001EF7:0045FE52";
+       // Simulate remote method delay for UI testing
+       private final int delayMs = 500;
 
        public AnrXappMockConfiguration() {
-
                logger.info("Configuring mock ANR xApp client");
                gNodebTable = new GgNodeBTable();
                gNodebTable.addGNodeBIdsItem(GNODEB1).addGNodeBIdsItem(GNODEB2).addGNodeBIdsItem(GNODEB3);
@@ -118,16 +117,47 @@ public class AnrXappMockConfiguration {
                ApiClient apiClient = apiClient();
                NcrtApi mockApi = mock(NcrtApi.class);
                when(mockApi.getApiClient()).thenReturn(apiClient);
-               when(mockApi.getgNodeB()).thenReturn(gNodebTable);
+               doAnswer(inv -> {
+                       logger.debug("getgNodeB sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return gNodebTable;
+               }).when(mockApi).getgNodeB();
                // 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);
-               when(mockApi.getNcrt(eq(GNODEB1), any(String.class), any(String.class))).thenReturn(ncrtNodeB1);
-               when(mockApi.getNcrt(eq(GNODEB2), any(String.class), any(String.class))).thenReturn(ncrtNodeB2);
-               when(mockApi.getNcrt(eq(GNODEB3), any(String.class), any(String.class))).thenReturn(ncrtNodeB3);
-               doAnswer(i -> null).when(mockApi).deleteNcrt(any(String.class), any(String.class));
-               doAnswer(i -> null).when(mockApi).modifyNcrt(any(String.class), any(String.class),
-                               any(NeighborCellRelationMod.class));
+               doAnswer(inv -> {
+                       logger.debug("getNcrt (1) sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return ncrt;
+               }).when(mockApi).getNcrt((String) isNull(), (String) isNull(), (String) isNull());
+               doAnswer(inv -> {
+                       logger.debug("getNcrt (2) sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return ncrt;
+               }).when(mockApi).getNcrt(eq(""), any(String.class), any(String.class));
+               doAnswer(inv -> {
+                       logger.debug("getNcrt (3) sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return ncrtNodeB1;
+               }).when(mockApi).getNcrt(eq(GNODEB1), any(String.class), any(String.class));
+               doAnswer(inv -> {
+                       logger.debug("getNcrt (4) sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return ncrtNodeB2;
+               }).when(mockApi).getNcrt(eq(GNODEB2), any(String.class), any(String.class));
+               doAnswer(inv -> {
+                       logger.debug("getNcrt (5) sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return ncrtNodeB3;
+               }).when(mockApi).getNcrt(eq(GNODEB3), any(String.class), any(String.class));
+               doAnswer(inv -> {
+                       logger.debug("deleteNcrt sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return null;
+               }).when(mockApi).deleteNcrt(any(String.class), any(String.class));
+               doAnswer(inv -> {
+                       logger.debug("modifyNcrt sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return null;
+               }).when(mockApi).modifyNcrt(any(String.class), any(String.class), any(NeighborCellRelationMod.class));
                return mockApi;
        }
 
index d97884d..ef1e9d5 100644 (file)
@@ -60,6 +60,9 @@ public class AppManagerMockConfiguration {
        private final AllDeployableXapps availXapps;
        private final AllDeployedXapps deployedXapps;
        private final AllXappConfig allXappConfigs;
+       private final SubscriptionResponse subRes;
+       // Simulate remote method delay for UI testing
+       private final int delayMs = 500;
 
        public AppManagerMockConfiguration() {
                logger.info("Configuring mock xApp Manager");
@@ -79,6 +82,7 @@ public class AppManagerMockConfiguration {
                                        .status(XappInstance.StatusEnum.RUNNING));
                        deployedXapps.add(xapp);
                }
+               subRes = new SubscriptionResponse().eventType(SubscriptionResponse.EventTypeEnum.ALL).id("subid").version(1);
        }
 
        @Bean
@@ -100,19 +104,61 @@ public class AppManagerMockConfiguration {
                when(mockClient.getStatusCode()).thenReturn(HttpStatus.OK);
                XappApi mockApi = mock(XappApi.class);
                when(mockApi.getApiClient()).thenReturn(mockClient);
-               when(mockApi.getAllXappConfig()).thenReturn(allXappConfigs);
-               when(mockApi.createXappConfig(any(XAppConfig.class))).thenReturn(allXappConfigs.get(0));
-               when(mockApi.modifyXappConfig(any(XAppConfig.class))).thenReturn(allXappConfigs.get(0));
-               doAnswer(i -> null).when(mockApi).deleteXappConfig(any(ConfigMetadata.class));
-               when(mockApi.deployXapp(any(XAppInfo.class))).thenReturn(deployedXapps.get(0));
-               when(mockApi.listAllXapps()).thenReturn(availXapps);
-               when(mockApi.getAllXapps()).thenReturn(deployedXapps);
-               when(mockApi.getXappByName(any(String.class))).thenReturn(deployedXapps.get(0));
-               doAnswer(i -> null).when(mockApi).undeployXapp(any(String.class));
-               SubscriptionResponse subRes = new SubscriptionResponse().eventType(SubscriptionResponse.EventTypeEnum.ALL)
-                               .id("subid").version(1);
-               when(mockApi.addSubscription(any(SubscriptionRequest.class))).thenReturn(subRes);
-               doAnswer(i -> null).when(mockApi).deleteSubscription(any(String.class));
+               doAnswer(inv -> {
+                       logger.debug("getAllXappConfig sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return allXappConfigs;
+               }).when(mockApi).getAllXappConfig();
+               doAnswer(inv -> {
+                       logger.debug("createXappConfig sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return allXappConfigs.get(0);
+               }).when(mockApi).createXappConfig(any(XAppConfig.class));
+               doAnswer(inv -> {
+                       logger.debug("modifyXappConfig sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return allXappConfigs.get(0);
+               }).when(mockApi).modifyXappConfig(any(XAppConfig.class));
+               doAnswer(inv -> {
+                       logger.debug("deleteXappConfig sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return null;
+               }).when(mockApi).deleteXappConfig(any(ConfigMetadata.class));
+               doAnswer(inv -> {
+                       logger.debug("deployXapp of {} sleeping {}", inv.getArgument(0), delayMs);
+                       Thread.sleep(delayMs);
+                       return deployedXapps.get(0);
+               }).when(mockApi).deployXapp(any(XAppInfo.class));
+               doAnswer(inv -> {
+                       logger.debug("listAllXapps sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return availXapps;
+               }).when(mockApi).listAllXapps();
+               doAnswer(inv -> {
+                       logger.debug("getAllXapps sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return deployedXapps;
+               }).when(mockApi).getAllXapps();
+               doAnswer(inv -> {
+                       logger.debug("getXappByName of {} sleeping {}", inv.getArgument(0), delayMs);
+                       Thread.sleep(delayMs);
+                       return deployedXapps.get(0);
+               }).when(mockApi).getXappByName(any(String.class));
+               doAnswer(inv -> {
+                       logger.debug("undeployXapp of {} sleeping {}", inv.getArgument(0), delayMs);
+                       Thread.sleep(delayMs);
+                       return null;
+               }).when(mockApi).undeployXapp(any(String.class));
+               doAnswer(inv -> {
+                       logger.debug("addSubscription sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return subRes;
+               }).when(mockApi).addSubscription(any(SubscriptionRequest.class));
+               doAnswer(inv -> {
+                       logger.debug("deleteSubscription sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return null;
+               }).when(mockApi).deleteSubscription(any(String.class));
                return mockApi;
        }
 
index 875d01b..4561dd7 100644 (file)
@@ -53,6 +53,8 @@ public class E2ManagerMockConfiguration {
 
        private final List<NodebIdentity> nodebIdList;
        private final GetNodebResponse nodebResponse;
+       // Simulate remote method delay for UI testing
+       private final int delayMs = 500;
 
        public E2ManagerMockConfiguration() {
                logger.info("Configuring mock E2 Manager");
@@ -86,11 +88,31 @@ public class E2ManagerMockConfiguration {
                ApiClient apiClient = apiClient();
                NodebApi mockApi = mock(NodebApi.class);
                when(mockApi.getApiClient()).thenReturn(apiClient);
-               doAnswer(i -> null).when(mockApi).nodebDelete();
-               doAnswer(i -> nodebResponse).when(mockApi).getNb(any(String.class));
-               doAnswer(i -> nodebIdList).when(mockApi).getNodebIdList();
-               doAnswer(i -> null).when(mockApi).endcSetup(any(SetupRequest.class));
-               doAnswer(i -> null).when(mockApi).x2Setup(any(SetupRequest.class));
+               doAnswer(inv -> {
+                       logger.debug("nodebDelete sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return null;
+               }).when(mockApi).nodebDelete();
+               doAnswer(inv -> {
+                       logger.debug("getNb sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return nodebResponse;
+               }).when(mockApi).getNb(any(String.class));
+               doAnswer(inv -> {
+                       logger.debug("getNodebIdList sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return nodebIdList;
+               }).when(mockApi).getNodebIdList();
+               doAnswer(inv -> {
+                       logger.debug("endcSetup sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return null;
+               }).when(mockApi).endcSetup(any(SetupRequest.class));
+               doAnswer(inv -> {
+                       logger.debug("x2Setup sleeping {}", delayMs);
+                       Thread.sleep(delayMs);
+                       return null;
+               }).when(mockApi).x2Setup(any(SetupRequest.class));
                return mockApi;
        }
 
index ba24071..652c955 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
+import { HttpResponse } from '@angular/common/http';
 import { MatSort } from '@angular/material';
 import { MatDialog } from '@angular/material/dialog';
 import { fromEvent } from 'rxjs/observable/fromEvent';
@@ -108,7 +109,7 @@ export class AnrXappComponent implements AfterViewInit, OnInit {
         if (res) {
           this.anrXappService.deleteNcr(ncr.servingCellNrcgi, ncr.neighborCellNrpci)
             .subscribe(
-              response => {
+              (response: HttpResponse<Object>) => {
                 switch (response.status) {
                   case 200:
                     this.notificationService.success('Delete succeeded!');
index 6c8a453..5c69c2f 100644 (file)
@@ -55,7 +55,7 @@
       <button mat-icon-button (click)="controlApp(element)">
           <mat-icon matTooltip="Adjust settings">settings</mat-icon>
         </button>
-        <button mat-icon-button (click)="undeployApp(element)">
+        <button mat-icon-button (click)="onUndeployApp(element)">
           <mat-icon matTooltip="Undeploy app">cloud_download</mat-icon>
         </button>
       </mat-cell>
@@ -94,7 +94,7 @@
   </table>
 
   <div class="spinner-container" *ngIf="dataSource.loading$ | async">
-    <mat-spinner></mat-spinner>
+    <mat-spinner diameter=50></mat-spinner>
   </div>
 
 </div>
index b172152..341754b 100644 (file)
@@ -18,6 +18,7 @@
  * ========================LICENSE_END===================================
  */
 import { Component, OnInit, ViewChild } from '@angular/core';
+import { HttpResponse, HttpErrorResponse } from '@angular/common/http';
 import { MatSort } from '@angular/material/sort';
 import { Router } from '@angular/router';
 import { XappControlRow } from '../interfaces/app-mgr.types';
@@ -67,21 +68,24 @@ export class AppControlComponent implements OnInit {
     }
   }
 
-  undeployApp(app: XappControlRow): void {
+  onUndeployApp(app: XappControlRow): void {
     this.confirmDialogService.openConfirmDialog('Are you sure you want to undeploy xApp ' + app.xapp + '?')
-      .afterClosed().subscribe(res => {
+      .afterClosed().subscribe( (res: boolean) => {
         if (res) {
           this.appMgrSvc.undeployXapp(app.xapp).subscribe(
-            response => {
+            ( httpResponse: HttpResponse<Object>) => {
               this.dataSource.loadTable();
-              switch (response.status) {
+              switch (httpResponse.status) {
                 case 200:
                   this.notificationService.success('xApp undeployed successfully!');
                   break;
                 default:
                   this.notificationService.warn('xApp undeploy failed.');
               }
-            }
+            },
+            ( (error: HttpErrorResponse) => {
+              this.notificationService.warn(error.message);
+            })
           );
         }
       });
index 2a9b72d..3089655 100644 (file)
       <mat-header-cell *matHeaderCellDef> Action </mat-header-cell>
       <mat-cell *matCellDef="let element">
         <div class="catalog-button-row">
-          <button mat-icon-button (click)="onConfigureApp(element.name)">
+          <button mat-icon-button (click)="onConfigureApp(element)">
             <mat-icon matTooltip="Adjust settings">settings</mat-icon>
           </button>
-          <button mat-icon-button (click)="onDeployApp(element.name)">
+          <button mat-icon-button (click)="onDeployApp(element)">
             <mat-icon matTooltip="Deploy app">cloud_upload</mat-icon>
           </button>
         </div>
@@ -57,7 +57,7 @@
   </table>
 
   <div class="spinner-container" *ngIf="dataSource.loading$ | async">
-    <mat-spinner></mat-spinner>
+    <mat-spinner diameter=50></mat-spinner>
   </div>
 
 </div>
index 0f9d888..23184da 100644 (file)
   background-color: transparent;
 }
 
+.mat-column-action {
+  max-width: 200px;
+}
+
 .catalog-button-row button{
   margin-right: 5px;
 }
index 97dfd7c..4eab408 100644 (file)
@@ -25,6 +25,7 @@ import { AppMgrService } from '../services/app-mgr/app-mgr.service';
 import { ConfirmDialogService } from './../services/ui/confirm-dialog.service';
 import { NotificationService } from './../services/ui/notification.service';
 import { CatalogDataSource } from './catalog.datasource';
+import { XMDeployableApp } from '../interfaces/app-mgr.types';
 
 @Component({
   selector: 'rd-app-catalog',
@@ -53,12 +54,12 @@ export class CatalogComponent implements OnInit {
     this.errorDiaglogService.displayError(aboutError);
   }
 
-  onDeployApp(name: string): void {
-    this.confirmDialogService.openConfirmDialog('Deploy application ' + name + '?')
-      .afterClosed().subscribe( (res: any) => {
+  onDeployApp(app: XMDeployableApp): void {
+    this.confirmDialogService.openConfirmDialog('Deploy application ' + app.name + '?')
+      .afterClosed().subscribe( (res: boolean) => {
         if (res) {
-          this.appMgrService.deployXapp(name).subscribe(
-            (response: HttpResponse<object>) => {
+          this.appMgrService.deployXapp(app.name).subscribe(
+            (response: HttpResponse<Object>) => {
               this.notificationService.success('Deploy succeeded!');
             },
             (error: HttpErrorResponse) => {
index bc29cfe..4f2cb4a 100644 (file)
@@ -23,7 +23,7 @@
 export interface E2SetupRequest {
   ranName: string;
   ranIp: string;
-  ranPort: number;
+  ranPort: string;
 }
 
 export interface E2ErrorResponse {
@@ -57,3 +57,10 @@ export interface E2RanDetails {
   nodebIdentity: E2NodebIdentity;
   nodebStatus: E2GetNodebResponse;
 }
+
+export interface RanDialogFormData {
+  ranIp: string;
+  ranName: string;
+  ranPort: string;
+  ranType: string;
+}
index 1dac647..2347a82 100644 (file)
@@ -21,7 +21,6 @@
 <div mat-dialog-title>
     Setup RAN Connection
 </div>
-
 <form [formGroup]="ranDialogForm" novalidate autocomplete="off" (ngSubmit)="setupConnection(ranDialogForm.value)">
   <div mat-dialog-content>
     <div name="rantype">
@@ -50,6 +49,6 @@
   </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]="!ranDialogForm.valid">Connect</button>
+    <button class="mat-raised-button mat-primary" [disabled]="!ranDialogForm.valid || processing">Connect</button>
   </div>
 </form>
index 7f0c368..25442f7 100644 (file)
  * ========================LICENSE_END===================================
  */
 import { Component, OnInit, Inject } from '@angular/core';
+import { HttpResponse, HttpErrorResponse } from '@angular/common/http';
 import { FormGroup, FormControl, Validators } from '@angular/forms';
 import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { Observable } from 'rxjs';
 import { E2ManagerService } from '../services/e2-mgr/e2-mgr.service';
 import { NotificationService } from '../services/ui/notification.service';
 import { ErrorDialogService } from '../services/ui/error-dialog.service';
-import { E2SetupRequest } from '../interfaces/e2-mgr.types';
-import { HttpErrorResponse } from '@angular/common/http';
+import { E2SetupRequest, RanDialogFormData } from '../interfaces/e2-mgr.types';
 
 @Component({
     selector: 'rd-ran-control-connect-dialog',
@@ -35,13 +36,14 @@ import { HttpErrorResponse } from '@angular/common/http';
 export class RanControlConnectDialogComponent implements OnInit {
 
     public ranDialogForm: FormGroup;
+    public processing = false;
 
     constructor(
         private dialogRef: MatDialogRef<RanControlConnectDialogComponent>,
         private service: E2ManagerService,
         private errorService: ErrorDialogService,
-        private notifService: NotificationService,
-        @Inject(MAT_DIALOG_DATA) public data: E2SetupRequest) {
+        private notifService: NotificationService) {
+        // opens with empty fields; accepts no data to display
     }
 
     ngOnInit() {
@@ -55,64 +57,55 @@ export class RanControlConnectDialogComponent implements OnInit {
             ranIp: new FormControl('', [Validators.required, Validators.pattern(ipPattern)]),
             ranPort: new FormControl('', [Validators.required, Validators.pattern(portPattern)])
         });
-
     }
 
     onCancel() {
-        this.dialogRef.close();
+        this.dialogRef.close(false);
     }
 
-    public setupConnection = (ranFormValue) => {
-        if (this.ranDialogForm.valid) {
-            this.executeSetupConnection(ranFormValue);
+    setupConnection = (ranFormValue: RanDialogFormData) => {
+        if (!this.ranDialogForm.valid) {
+            // should never happen
+            return;
         }
-    }
-
-    private executeSetupConnection = (ranFormValue) => {
-        let httpErrRes: HttpErrorResponse;
-        const aboutError = 'RAN Connection Failed: ';
+        this.processing = true;
         const setupRequest: E2SetupRequest = {
             ranName: ranFormValue.ranName,
             ranIp: ranFormValue.ranIp,
             ranPort: ranFormValue.ranPort
         };
+        let observable: Observable<HttpResponse<Object>>;
         if (ranFormValue.ranType === 'endc') {
-            this.service.endcSetup(setupRequest).subscribe(
-                (response: any) => {
-                    this.notifService.success('Endc connect succeeded!');
-                    this.dialogRef.close();
-                },
-                (error => {
-                    httpErrRes = error;
-                    this.errorService.displayError(aboutError + httpErrRes.message);
-                })
-            );
+            observable = this.service.endcSetup(setupRequest);
         } else {
-            this.service.x2Setup(setupRequest).subscribe(
-                (response: any) => {
-                    this.notifService.success('X2 connect succeeded!');
-                    this.dialogRef.close();
-                },
-                (error => {
-                    httpErrRes = error;
-                    this.errorService.displayError(aboutError + httpErrRes.message);
-                })
-            );
+            observable = this.service.x2Setup(setupRequest);
         }
+        observable.subscribe(
+            (response: any) => {
+                this.processing = false;
+                this.notifService.success('Connect succeeded!');
+                this.dialogRef.close(true);
+            },
+            ( (error: HttpErrorResponse) => {
+                this.processing = false;
+                this.errorService.displayError('RAN Connection Failed: ' + error.message);
+                // keep the dialog open
+            })
+        );
     }
 
-    public hasError(controlName: string, errorName: string) {
+    hasError(controlName: string, errorName: string) {
         if (this.ranDialogForm.controls[controlName].hasError(errorName)) {
           return true;
         }
         return false;
     }
 
-    public validateControl(controlName: string) {
+    validateControl(controlName: string) {
         if (this.ranDialogForm.controls[controlName].invalid && this.ranDialogForm.controls[controlName].touched) {
             return true;
         }
         return false;
     }
 
-} // class AppRANConnectDialog
+}
index 474542a..d249d44 100644 (file)
@@ -68,7 +68,7 @@
   </table>
 
   <div class="spinner-container" *ngIf="dataSource.loading$ | async">
-    <mat-spinner></mat-spinner>
+    <mat-spinner diameter=50></mat-spinner>
   </div>
 
 </div>
index e911d18..26665ca 100644 (file)
@@ -18,6 +18,7 @@
  * ========================LICENSE_END===================================
  */
 import { Component, OnInit } from '@angular/core';
+import { HttpResponse, HttpErrorResponse } from '@angular/common/http';
 import { MatDialog } from '@angular/material/dialog';
 import { RanControlConnectDialogComponent } from './ran-connection-dialog.component';
 import { E2ManagerService } from '../services/e2-mgr/e2-mgr.service';
@@ -25,7 +26,6 @@ import { ErrorDialogService } from '../services/ui/error-dialog.service';
 import { ConfirmDialogService } from '../services/ui/confirm-dialog.service';
 import { NotificationService } from '../services/ui/notification.service';
 import { RANControlDataSource } from './ran-control.datasource';
-import { HttpErrorResponse } from '@angular/common/http';
 
 @Component({
   selector: 'rd-ran-control',
@@ -49,33 +49,33 @@ export class RanControlComponent implements OnInit {
 
   setupRANConnection() {
     const dialogRef = this.dialog.open(RanControlConnectDialogComponent, {
-      width: '450px',
-      data: {}
+      width: '450px'
     });
-    dialogRef.afterClosed().subscribe(result => {
-      this.dataSource.loadTable();
+    dialogRef.afterClosed().subscribe( (result: boolean) => {
+      if (result) {
+        this.dataSource.loadTable();
+      }
     });
   }
 
   disconnectAllRANConnections() {
-    let httpErrRes: HttpErrorResponse;
     const aboutError = 'Disconnect all RAN Connections Failed: ';
     this.confirmDialogService.openConfirmDialog('Are you sure you want to disconnect all RAN connections?')
-      .afterClosed().subscribe(res => {
+      .afterClosed().subscribe( (res: boolean) => {
         if (res) {
           this.e2MgrSvc.nodebDelete().subscribe(
-            response => {
+            ( response: HttpResponse<Object>) => {
               if (response.status === 200) {
                 this.notificationService.success('Disconnect all RAN Connections Succeeded!');
                 this.dataSource.loadTable();
               }
             },
-            (error => {
-              httpErrRes = error;
-              this.errorDialogService.displayError(aboutError + httpErrRes.message);
+            ( (error: HttpErrorResponse) => {
+              this.errorDialogService.displayError(aboutError + error.message);
             })
           );
         }
       });
   }
+
 }
index 759ad7e..c789261 100644 (file)
@@ -95,7 +95,7 @@ export class ANRXappService {
    * @param ggnbId Optional parameter for the gNB ID
    * @param servingCellNrcgi Serving cell NRCGI
    * @param neighborCellNrpci Neighbor cell NRPCI
-   * @returns Neighbor cell relation table, which wraps an array
+   * @returns Observable of ANR neighbor cell relation array
    */
   getNcrtInfo(ggnodeb: string = '', servingCellNrcgi: string = '', neighborCellNrpci: string = ''): Observable<ANRNeighborCellRelation[]> {
     const url = this.buildPath(this.ncrtPath);
@@ -115,9 +115,9 @@ export class ANRXappService {
    * @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
+   * @returns Observable that yields a response code only, no data
    */
-  modifyNcr(servingCellNrcgi: string, neighborCellNrpci: string, mod: ANRNeighborCellRelationMod): Observable<any> {
+  modifyNcr(servingCellNrcgi: string, neighborCellNrpci: string, mod: ANRNeighborCellRelationMod): Observable<Object> {
     const url = this.buildPath(this.ncrtPath, this.servingPath, servingCellNrcgi, this.neighborPath, neighborCellNrpci);
     return this.httpClient.put(url, mod, { observe: 'response' });
   }
@@ -126,9 +126,9 @@ export class ANRXappService {
    * 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
+   * @returns Observable that yields a response code only, no data
    */
-  deleteNcr(servingCellNrcgi: string, neighborCellNrpci: string): Observable<any> {
+  deleteNcr(servingCellNrcgi: string, neighborCellNrpci: string): Observable<Object> {
     const url = this.buildPath(this.ncrtPath, this.servingPath, servingCellNrcgi, this.neighborPath, neighborCellNrpci);
     return this.httpClient.delete(url, { observe: 'response' });
   }
index 10683ae..13df94c 100644 (file)
@@ -18,7 +18,7 @@
  * ========================LICENSE_END===================================
  */
 import { Injectable } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
+import { HttpClient, HttpResponse } from '@angular/common/http';
 import { Observable } from 'rxjs';
 import { XMXappInfo, XMDeployableApp, XMDeployedApp } from '../../interfaces/app-mgr.types';
 
@@ -39,12 +39,12 @@ export class AppMgrService {
     return this.httpClient.get<XMDeployedApp[]>(this.basePath);
   }
 
-  deployXapp(name: string) {
+  deployXapp(name: string): Observable<HttpResponse<Object>> {
     const xappInfo: XMXappInfo = { name: name };
     return this.httpClient.post(this.basePath, xappInfo, { observe: 'response' });
   }
 
-  undeployXapp(name: string) {
+  undeployXapp(name: string): Observable<HttpResponse<Object>> {
     return this.httpClient.delete((this.basePath + '/' + name), { observe: 'response' });
   }
 
index 2c38c31..1036504 100644 (file)
@@ -29,7 +29,7 @@ export class ConfirmDialogService {
 
   constructor(private dialog: MatDialog) { }
 
-  openConfirmDialog(msg) {
+  openConfirmDialog(msg: string) {
     return this.dialog.open(ConfirmDialogComponent, {
       width: '480px',
       position: { top: '100px' },
index d32a3f2..b8cd9cb 100644 (file)
   limitations under the License.
   ========================LICENSE_END===================================
 -->
-  <div class="user__section">
-    <h3 class="user__header">Users</h3>
-    <button mat-raised-button (click)="addUser()">Add User</button>
-    <div class="spinner-container" *ngIf="dataSource.loading$ | async">
-      <mat-spinner></mat-spinner>
-    </div>
-    <table mat-table [dataSource]="dataSource" matSort class="user-table mat-elevation-z8">
+<div class="user__section">
+  <h3 class="user__header">Users</h3>
+  <button mat-raised-button (click)="addUser()">Add User</button>
+  <div class="spinner-container" *ngIf="dataSource.loading$ | async">
+    <mat-spinner></mat-spinner>
+  </div>
+  <table mat-table [dataSource]="dataSource" matSort class="user-table mat-elevation-z8">
+
+    <ng-container matColumnDef="id">
+      <mat-header-cell *matHeaderCellDef mat-sort-header> ID </mat-header-cell>
+      <mat-cell *matCellDef="let element"> {{element.id}} </mat-cell>
+    </ng-container>
+
+    <ng-container matColumnDef="firstName">
+      <mat-header-cell *matHeaderCellDef mat-sort-header> First Name </mat-header-cell>
+      <mat-cell *matCellDef="let element"> {{element.firstName}} </mat-cell>
+    </ng-container>
 
-      <ng-container matColumnDef="id">
-        <mat-header-cell *matHeaderCellDef mat-sort-header> ID </mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.id}} </mat-cell>
-      </ng-container>
+    <ng-container matColumnDef="lastName">
+      <mat-header-cell *matHeaderCellDef mat-sort-header> Last Name </mat-header-cell>
+      <mat-cell *matCellDef="let element"> {{element.lastName}} </mat-cell>
+    </ng-container>
 
-      <ng-container matColumnDef="firstName">
-        <mat-header-cell *matHeaderCellDef mat-sort-header> First Name </mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.firstName}} </mat-cell>
-      </ng-container>
+    <ng-container matColumnDef="status">
+      <mat-header-cell *matHeaderCellDef mat-sort-header> Status </mat-header-cell>
+      <mat-cell *matCellDef="let element"> {{element.status}} </mat-cell>
+    </ng-container>
 
-      <ng-container matColumnDef="lastName">
-        <mat-header-cell *matHeaderCellDef mat-sort-header> Last Name </mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.lastName}} </mat-cell>
-      </ng-container>
+    <ng-container matColumnDef="action">
+      <mat-header-cell *matHeaderCellDef> Action </mat-header-cell>
+      <mat-cell *matCellDef="let element">
+        <div class="user-button-row">
+          <button mat-icon-button (click)="editUser(element)">
+            <mat-icon matTooltip="Edit properties">edit</mat-icon>
+          </button>
+          <button mat-icon-button color="warn" (click)="deleteUser(element)">
+            <mat-icon matTooltip="Delete user">delete</mat-icon>
+          </button>
+        </div>
+      </mat-cell>
+    </ng-container>
 
-      <ng-container matColumnDef="status">
-        <mat-header-cell *matHeaderCellDef mat-sort-header> Status </mat-header-cell>
-        <mat-cell *matCellDef="let element"> {{element.status}} </mat-cell>
-      </ng-container>
+    <ng-container matColumnDef="noRecordsFound">
+      <mat-footer-cell *matFooterCellDef>No records found.</mat-footer-cell>
+    </ng-container>
 
-      <ng-container matColumnDef="action">
-        <mat-header-cell *matHeaderCellDef> Action </mat-header-cell>
-        <mat-cell *matCellDef="let element">
-          <div class="user-button-row">
-            <button mat-icon-button (click)="editUser(element)">
-              <mat-icon matTooltip="Edit properties">edit</mat-icon>
-            </button>
-            <button mat-icon-button color="warn" (click)="deleteUser(element)">
-              <mat-icon matTooltip="Delete user">delete</mat-icon>
-            </button>
-          </div>
-        </mat-cell>
-      </ng-container>
+    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
+    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
+    <mat-footer-row *matFooterRowDef="['noRecordsFound']" [ngClass]="{'display-none': dataSource.rowCount > 0}"></mat-footer-row>
 
-      <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
-      <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
-    </table>
+  </table>
+
+  <div class="spinner-container" *ngIf="dataSource.loading$ | async">
+    <mat-spinner diameter=50></mat-spinner>
   </div>
+
+</div>
\ No newline at end of file
index 4d06c6d..80ddf63 100644 (file)
@@ -49,3 +49,7 @@
 .user-button-row button {
     margin-right: 5px;
 }
+
+.display-none {
+    display: none;
+}