Extend Xapp deploy workflow 24/4624/3
authorNicolas Hu <jh245g@att.com>
Wed, 26 Aug 2020 19:18:50 +0000 (15:18 -0400)
committerNicolas Hu <jh245g@att.com>
Thu, 27 Aug 2020 14:42:37 +0000 (10:42 -0400)
Signed-off-by: Jun (Nicolas) Hu <jh245g@att.com>
Issue-ID: OAM-109
Change-Id: Ieeef49538102d6596b337942025208d0a92f94c3

dashboard/webapp-frontend/src/app/catalog/catalog.component.ts
dashboard/webapp-frontend/src/app/onboard/onboard.component.ts
dashboard/webapp-frontend/src/app/rd.module.ts
dashboard/webapp-frontend/src/app/services/app-mgr/app-mgr.service.ts
dashboard/webapp-frontend/src/app/ui/deploy-dialog/deploy-dialog.component.html [new file with mode: 0644]
dashboard/webapp-frontend/src/app/ui/deploy-dialog/deploy-dialog.component.scss [new file with mode: 0644]
dashboard/webapp-frontend/src/app/ui/deploy-dialog/deploy-dialog.component.spec.ts [new file with mode: 0644]
dashboard/webapp-frontend/src/app/ui/deploy-dialog/deploy-dialog.component.ts [new file with mode: 0644]
dashboard/webapp-frontend/src/assets/mockdata/config.json
docs/release-notes.rst

index ac8e6ec..df43995 100644 (file)
  * limitations under the License.
  * ========================LICENSE_END===================================
  */
-import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
 import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
 import { MatDialog } from '@angular/material/dialog';
 import { MatSort } from '@angular/material/sort';
 import { Subscription } from 'rxjs';
-import { finalize } from 'rxjs/operators';
-import { RicInstance } from '../interfaces/dashboard.types';
 import { XMXapp } from '../interfaces/app-mgr.types';
+import { RicInstance } from '../interfaces/dashboard.types';
 import { AppMgrService } from '../services/app-mgr/app-mgr.service';
 import { InstanceSelectorService } from '../services/instance-selector/instance-selector.service';
+import { ConfirmDialogService } from '../services/ui/confirm-dialog.service';
 import { LoadingDialogService } from '../services/ui/loading-dialog.service';
+import { NotificationService } from '../services/ui/notification.service';
 import { UiService } from '../services/ui/ui.service';
+import { DeployDialogComponent } from '../ui/deploy-dialog/deploy-dialog.component';
 import { AppConfigurationComponent } from './../app-configuration/app-configuration.component';
-import { ConfirmDialogService } from '../services/ui/confirm-dialog.service';
 import { OnboardComponent } from './../onboard/onboard.component';
-import { NotificationService } from '../services/ui/notification.service';
 import { CatalogDataSource } from './catalog.datasource';
 
 @Component({
@@ -100,30 +99,24 @@ export class CatalogComponent implements OnInit, OnDestroy {
   }
 
   onDeployApp(app: XMXapp): void {
-    this.confirmDialogService.openConfirmDialog('Deploy application ' + app.name + '?')
-      .afterClosed().subscribe((res: boolean) => {
-        if (res) {
-          this.loadingDialogService.startLoading('Deploying ' + app.name);
-          this.appMgrService.deployXapp(this.instanceKey, app.name)
-            .pipe(
-              finalize(() => this.loadingDialogService.stopLoading())
-            )
-            .subscribe(
-              (response: HttpResponse<Object>) => {
-                this.notificationService.success('App deploy succeeded!');
-              },
-              ((her: HttpErrorResponse) => {
-                // the error field should have an ErrorTransport object
-                let msg = her.message;
-                if (her.error && her.error.message) {
-                  msg = her.error.message;
-                }
-                this.notificationService.warn('App deploy failed: ' + msg);
-              })
-            );
-        }
+    if (this.darkMode) {
+      this.panelClass = 'dark-theme';
+    } else {
+      this.panelClass = '';
+    }
+    const dialogRef = this.dialog.open(DeployDialogComponent, {
+      panelClass: this.panelClass,
+      width: '400px',
+      maxHeight: '1000px',
+      position: {
+        top: '10%'
+      },
+      data: {
+        xappName: app.name,
+        instanceKey: this.instanceKey
       }
-      );
+
+    });
   }
 
   onboard(): void {
index 7f609a6..4f843db 100644 (file)
@@ -117,21 +117,24 @@ export class OnboardComponent implements OnInit {
 
 
   selectConfigFile(event) {
-    this.configFile = event.target.files[0];
-    let fileReader = new FileReader();
-    fileReader.onload = (e) => {
-      this.descriptor["config-file.json"] = JSON.parse(fileReader.result as string);
+    if (event.target.files.length) {
+      this.configFile = event.target.files[0];
+      let fileReader = new FileReader();
+      fileReader.onload = (e) => {
+        this.descriptor["config-file.json"] = JSON.parse(fileReader.result as string);
+      }
+      fileReader.readAsText(this.configFile);
     }
-    fileReader.readAsText(this.configFile);
   }
 
   selectControlsSchema(event) {
-    this.controlsSchema = event.target.files[0];
-    let fileReader = new FileReader();
-    fileReader.onload = (e) => {
-      this.descriptor["controls-schema.json"] = JSON.parse(fileReader.result as string);
+    if (event.target.files.length) {
+      this.controlsSchema = event.target.files[0];
+      let fileReader = new FileReader();
+      fileReader.onload = (e) => {
+        this.descriptor["controls-schema.json"] = JSON.parse(fileReader.result as string);
+      }
+      fileReader.readAsText(this.controlsSchema);
     }
-    fileReader.readAsText(this.controlsSchema);
-
   }
 }
index 9de3bad..9aa1cb5 100644 (file)
@@ -89,6 +89,7 @@ import { InstanceSelectorService } from './services/instance-selector/instance-s
 import { InstanceSelectorDialogService } from './services/ui/instance-selector-dialog.service';
 import { UiService } from './services/ui/ui.service';
 import { XappOnboarderService } from './services/xapp-onboarder/xapp-onboarder.service';
+import { DeployDialogComponent } from './ui/deploy-dialog/deploy-dialog.component';
 
 
 @NgModule({
@@ -116,7 +117,8 @@ import { XappOnboarderService } from './services/xapp-onboarder/xapp-onboarder.s
     StatsDialogComponent,
     UserComponent,
     InstanceSelectorDialogComponent,
-    OnboardComponent
+    OnboardComponent,
+    DeployDialogComponent
   ],
   imports: [
     BrowserModule,
index 5d89ce4..bddf690 100644 (file)
@@ -45,8 +45,7 @@ export class AppMgrService {
     return this.httpClient.get<XMAllDeployedXapps>(path);
   }
 
-  deployXapp(instanceKey: string, xappName: string): Observable<HttpResponse<Object>> {
-    const xappDescriptor: XMXappDescriptor = { xappName: xappName };
+  deployXapp(instanceKey: string, xappDescriptor: XMXappDescriptor): Observable<HttpResponse<Object>> {
     const path = this.dashboardSvc.buildPath(this.component, instanceKey, this.xappsPath);
     return this.httpClient.post(path, xappDescriptor, { observe: 'response' });
   }
diff --git a/dashboard/webapp-frontend/src/app/ui/deploy-dialog/deploy-dialog.component.html b/dashboard/webapp-frontend/src/app/ui/deploy-dialog/deploy-dialog.component.html
new file mode 100644 (file)
index 0000000..17099b5
--- /dev/null
@@ -0,0 +1,50 @@
+<!--
+  ========================LICENSE_START=================================
+  O-RAN-SC
+  %%
+  Copyright (C) 2020 AT&T Intellectual Property
+  %%
+  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>
+  Deploy Xapp
+</div>
+<form [formGroup]="deployForm" novalidate autocomplete="off" (ngSubmit)="deploy(deployForm.value)">
+  <mat-form-field>
+    <mat-label>xappName</mat-label>
+    <input matInput formControlName="xappName">
+  </mat-form-field>
+  <mat-form-field>
+    <mat-label>helmVersion (Optional)</mat-label>
+    <input matInput formControlName="helmVersion">
+  </mat-form-field>
+  <mat-form-field>
+    <mat-label>releaseName (Optional)</mat-label>
+    <input matInput formControlName="releaseName">
+  </mat-form-field>
+  <mat-form-field>
+    <mat-label>namespace (Optional)</mat-label>
+    <input matInput formControlName="namespace">
+  </mat-form-field>
+  <input #overrideFile type="file" (change)="selectoverrideFile($event)" style="display:none;" />
+  <mat-form-field class="example-full-width" (click)="overrideFile.click()">
+    <input matInput placeholder="Choose override File (Optional)"  value="{{overrideFile.value.substr(overrideFile.value.lastIndexOf('\\')+1)}}" >
+    <mat-icon matSuffix>folder_open</mat-icon>
+  </mat-form-field>
+  <div mat-dialog-actions class="modal-footer justify-content-center">
+    <button mat-button class="mat-raised-button" [mat-dialog-close]="false">Cancel</button>
+    <button mat-button class="mat-raised-button mat-primary" [disabled]="!deployForm.valid">OK</button>
+  </div>
+</form>
diff --git a/dashboard/webapp-frontend/src/app/ui/deploy-dialog/deploy-dialog.component.scss b/dashboard/webapp-frontend/src/app/ui/deploy-dialog/deploy-dialog.component.scss
new file mode 100644 (file)
index 0000000..b4c6863
--- /dev/null
@@ -0,0 +1,22 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 AT&T Intellectual Property
+ * %%
+ * 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===================================
+ */
+mat-form-field{
+  width:90%
+}
\ No newline at end of file
diff --git a/dashboard/webapp-frontend/src/app/ui/deploy-dialog/deploy-dialog.component.spec.ts b/dashboard/webapp-frontend/src/app/ui/deploy-dialog/deploy-dialog.component.spec.ts
new file mode 100644 (file)
index 0000000..08e121d
--- /dev/null
@@ -0,0 +1,45 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 AT&T Intellectual Property
+ * %%
+ * 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 { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DeployDialogComponent } from './deploy-dialog.component';
+
+describe('DeployDialogComponent', () => {
+  let component: DeployDialogComponent;
+  let fixture: ComponentFixture<DeployDialogComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ DeployDialogComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(DeployDialogComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/dashboard/webapp-frontend/src/app/ui/deploy-dialog/deploy-dialog.component.ts b/dashboard/webapp-frontend/src/app/ui/deploy-dialog/deploy-dialog.component.ts
new file mode 100644 (file)
index 0000000..1d173ec
--- /dev/null
@@ -0,0 +1,101 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 AT&T Intellectual Property
+ * %%
+ * 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, Inject, OnInit } from '@angular/core';
+import { FormControl, FormGroup, Validators } from '@angular/forms';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { BehaviorSubject } from 'rxjs/BehaviorSubject';
+import { finalize } from 'rxjs/operators';
+import { XMXappDescriptor} from '../../interfaces/app-mgr.types';
+import { AppMgrService } from '../../services/app-mgr/app-mgr.service';
+import { NotificationService } from '../../services/ui/notification.service';
+import { LoadingDialogService } from '../../services/ui/loading-dialog.service';
+import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
+
+@Component({
+  selector: 'rd-deploy-dialog',
+  templateUrl: './deploy-dialog.component.html',
+  styleUrls: ['./deploy-dialog.component.scss']
+})
+export class DeployDialogComponent implements OnInit {
+
+  private loadingSubject = new BehaviorSubject<boolean>(false);
+  public loading$ = this.loadingSubject.asObservable();
+  public deployForm: FormGroup;
+  xappDescriptor: XMXappDescriptor ;
+  overrideFile: File;
+
+  constructor(
+    private dialogRef: MatDialogRef<DeployDialogComponent>,
+    private appMgrService: AppMgrService,
+    private loadingDialogService: LoadingDialogService,
+    private notificationService: NotificationService,
+    @Inject(MAT_DIALOG_DATA) private data
+  ) { }
+
+  ngOnInit(): void {
+    this.deployForm = new FormGroup({
+      xappName: new FormControl(this.data.xappName, [Validators.required]),
+      helmVersion: new FormControl(''),
+      releaseName: new FormControl(''),
+      namespace: new FormControl(''),
+      overrideFile: new FormControl({}),
+    })
+  }
+
+  selectoverrideFile(event) {
+    if (event.target.files.length) {
+      this.overrideFile = event.target.files[0];
+      let fileReader = new FileReader();
+      fileReader.onload = (e) => {
+        this.deployForm.value.overrideFile = JSON.parse(fileReader.result as string);
+      }
+      fileReader.readAsText(this.overrideFile);
+    }
+    else {
+      this.deployForm.value.overrideFile =null
+    }
+  }
+
+  deploy(xapp: XMXappDescriptor) {   
+    this.xappDescriptor = xapp
+    this.loadingDialogService.startLoading('Deploying ' + this.xappDescriptor.xappName);
+    this.appMgrService.deployXapp(this.data.instanceKey, this.xappDescriptor)
+      .pipe(
+        finalize(() => {
+          this.loadingDialogService.stopLoading();
+          this.dialogRef.close();
+        })
+      )
+      .subscribe(
+        (response: HttpResponse<Object>) => {
+          this.notificationService.success('App deploy succeeded!');
+        },
+        ((her: HttpErrorResponse) => {
+          // the error field should have an ErrorTransport object
+          let msg = her.message;
+          if (her.error && her.error.message) {
+            msg = her.error.message;
+          }
+          this.notificationService.warn('App deploy failed: ' + msg);
+        })
+    );
+  }
+}
index eb39742..f69aef7 100644 (file)
@@ -1,7 +1,7 @@
 [
   {
     "metadata": {
-      "name": "UE Event Collector",
+      "name": "UE Event Collector i1",
       "configName": "UEEC-appconfig",
       "namespace": "ricxapp"
     },
index cfbae03..762fd15 100644 (file)
@@ -5,8 +5,9 @@
 RIC Dashboard Release Notes
 ===========================
 
-Version 2.1.0, 17 Aug 2020
+Version 2.1.0, 26 Aug 2020
 --------------------------
+* Extend the Dashboard Xapp deploy workflow to accept configuration (`OAM-109 <https://jira.o-ran-sc.org/browse/OAM-109>`_)
 * Add Xapp Onboarder client to backend (`OAM-108 <https://jira.o-ran-sc.org/browse/OAM-108>`_)
 * Add Xapp Onboarder frontend UI (`OAM-108 <https://jira.o-ran-sc.org/browse/OAM-108>`_)