You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@submarine.apache.org by li...@apache.org on 2020/03/08 09:48:41 UTC
[submarine] branch master updated: SUBMARINE-397. [WEB]Implement
the job page in workbench with Angular
This is an automated email from the ASF dual-hosted git repository.
liuxun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/submarine.git
The following commit(s) were added to refs/heads/master by this push:
new 6c68fa6 SUBMARINE-397. [WEB]Implement the job page in workbench with Angular
6c68fa6 is described below
commit 6c68fa6dbe9356e9bd6e51b6faa2b36f177137c1
Author: jasoonn <b0...@ntu.edu.tw>
AuthorDate: Sun Mar 8 09:32:57 2020 +0800
SUBMARINE-397. [WEB]Implement the job page in workbench with Angular
### What is this PR for?
Implement the job page in workbench with Angular.
### What type of PR is it?
[Feature]
### Todos
* [ ] - Task
### What is the Jira issue?
https://issues.apache.org/jira/browse/SUBMARINE-397
### How should this be tested?
https://travis-ci.org/apache/submarine/builds/659683029?utm_source=github_status&utm_medium=notification
### Screenshots
![](https://i.imgur.com/faii8qm.gif)
### Questions:
* Does the licenses files need update? No
* Is there breaking changes for older versions? No
* Does this needs documentation? No
Author: jasoonn <b0...@ntu.edu.tw>
Closes #206 from jasoonn/SUBMARINE-397 and squashes the following commits:
c1a192c [jasoonn] update jobIT
cf4dda4 [jasoonn] update jobIT.java position
37d830f [jasoonn] update
94f1675 [jasoonn] update all
---
.../org/apache/submarine/integration/jobIT.java | 71 ++++++++++++
.../src/app/pages/workbench/job/job.component.html | 128 ++++++++++++++++++++-
.../src/app/pages/workbench/job/job.component.scss | 50 ++++++++
.../src/app/pages/workbench/job/job.component.ts | 69 +++++++++++
.../job/{job.component.scss => job.module.ts} | 10 +-
.../src/app/pages/workbench/workbench.module.ts | 5 +-
6 files changed, 330 insertions(+), 3 deletions(-)
diff --git a/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/jobIT.java b/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/jobIT.java
new file mode 100644
index 0000000..aebe245
--- /dev/null
+++ b/submarine-test/test-e2e/src/test/java/org/apache/submarine/integration/jobIT.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package org.apache.submarine.integration;
+
+import org.apache.submarine.AbstractSubmarineIT;
+import org.apache.submarine.WebDriverManager;
+import org.apache.submarine.SubmarineITUtils;
+import org.openqa.selenium.By;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.testng.Assert;
+
+public class jobIT extends AbstractSubmarineIT {
+
+ public final static Logger LOG = LoggerFactory.getLogger(jobIT.class);
+
+ @BeforeClass
+ public static void startUp(){
+ LOG.info("[Testcase]: jobIT");
+ driver = WebDriverManager.getWebDriver();
+ }
+
+ @AfterClass
+ public static void tearDown(){
+ driver.quit();
+ }
+
+ @Test
+ public void workspaceNavigation() throws Exception {
+ // Login
+ LOG.info("Login");
+ pollingWait(By.cssSelector("input[ng-reflect-name='userName']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("admin");
+ pollingWait(By.cssSelector("input[ng-reflect-name='password']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("admin");
+ clickAndWait(By.cssSelector("button[class='login-form-button ant-btn ant-btn-primary']"));
+ pollingWait(By.cssSelector("a[routerlink='/workbench/dashboard']"), MAX_BROWSER_TIMEOUT_SEC);
+
+ // Routing to workspace
+ pollingWait(By.xpath("//span[contains(text(), \"Job\")]"), MAX_BROWSER_TIMEOUT_SEC).click();
+ Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8080/workbench/job");
+
+ // Test create new job
+ pollingWait(By.xpath("//button[@id='openJob']"), MAX_BROWSER_TIMEOUT_SEC).click();
+ Assert.assertEquals(pollingWait(By.xpath("//form"), MAX_BROWSER_TIMEOUT_SEC).isDisplayed(), true);
+ pollingWait(By.xpath("//input[@id='jobname']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("e2e test Job");
+ pollingWait(By.xpath("//textarea"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("e2e test Project description");
+ pollingWait(By.xpath("//button[@id='go']"), MAX_BROWSER_TIMEOUT_SEC).click();
+ //Next step
+ Assert.assertEquals(pollingWait(By.xpath("//div[@id='page2']"), MAX_BROWSER_TIMEOUT_SEC).isDisplayed(), true);
+ pollingWait(By.xpath("//button[@id='go']"), MAX_BROWSER_TIMEOUT_SEC).click();
+ Assert.assertEquals(pollingWait(By.xpath("//label[@class='pg3-form-label']"), MAX_BROWSER_TIMEOUT_SEC).isDisplayed(), true);
+ pollingWait(By.xpath("//button[@id='go']"), MAX_BROWSER_TIMEOUT_SEC).click();
+ }
+}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.html b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.html
index fcc2676..f46c164 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.html
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.html
@@ -17,4 +17,130 @@
~ under the License.
-->
-<p>job works!</p>
+<nz-layout style="margin: -24px -24px 16px;">
+ <nz-layout class="inner-layout">
+ <div id="jobOuter">
+ <nz-breadcrumb>
+ <nz-breadcrumb-item>
+ <a [routerLink]="['/', 'workbench', 'home']">Home</a>
+ </nz-breadcrumb-item>
+ <nz-breadcrumb-item>Job</nz-breadcrumb-item>
+ </nz-breadcrumb>
+ <br/>
+ <h2>Job</h2>
+ <nz-content>A job is a way of running a notebook or on a scheduled basis.</nz-content>
+ <br/>
+ </div>
+ </nz-layout>
+ <div id="jobData">
+ <div align="right">
+ <nz-radio-group [(ngModel)]="showJob" (ngModelChange)="showChange()">
+ <label nz-radio-button nzValue="All">All</label>
+ <label nz-radio-button nzValue="Own">Owned By Me</label>
+ <label nz-radio-button nzValue="Access">Accessable By Me</label>
+ </nz-radio-group>
+ <nz-input-group style="width:300px;margin-top: 15px;margin-left:10px;margin-right:5px;" [nzSuffix]="suffixIconSearch">
+ <input type="text" nz-input [(ngModel)]="searchText" (keydown)="filter($event)" placeholder="input search text" />
+ </nz-input-group>
+ <ng-template #suffixIconSearch>
+ <i nz-icon nzType="search"></i>
+ </ng-template>
+ <button nz-button id="openJob" nzType="primary" style="margin-right: 30px;margin-bottom: 15px;" (click)="isVisible=true;"><i nz-icon nzType="plus"></i>New Job</button>
+ </div>
+ <nz-table id="releaseTable" nzBordered #basicTable [nzData]="joblist" [nzNoResult]="'No data'">
+ <thead>
+ <tr>
+ <th nzWidth="60px">Job Name</th>
+ <th nzWidth="20px">Job ID</th>
+ <th nzWidth="40px">Owner</th>
+ <th nzWidth="60px">Actuator</th>
+ <th nzWidth="60px">Status</th>
+ <th nzWidth="60px">Progress</th>
+ <th nzWidth="60px">Last Run</th>
+ <th nzWidth="60px">Action</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr *ngFor="let data of basicTable.data">
+ <td>{{ data.name }}</td>
+ <td>{{ data.id }}</td>
+ <td>{{ data.owner }}</td>
+ <td>{{ data.actuator }}</td>
+ <td>{{ data.status }}</td>
+ <td><nz-progress nzStrokeLinecap="round" [nzPercent]="data.progress"></nz-progress></td>
+ <td>{{ data.lastRun }}</td>
+ <td><button nz-button nzType="link" (click)="startJob(data)" style="padding-left: 2px;padding-right: 5px;">Start</button>|<button nz-button nzType="link" (click)="editJob(data)" style="padding-left: 2px;padding-right: 5px;">Edit</button></td>
+ </tr>
+ </tbody>
+ </nz-table>
+ </div>
+ <nz-modal
+ [(nzVisible)]="isVisible"
+ nzTitle="Create Job"
+ [(nzOkText)]="okText"
+ [nzOkLoading]="isOkLoading"
+ (nzOnCancel)="isVisible=false;"
+ [nzWidth]="740"
+ >
+ <div >
+ <nz-steps [nzCurrent]="current">
+ <nz-step nzTitle="Basic Information"></nz-step>
+ <nz-step nzTitle="Configuration"></nz-step>
+ <nz-step nzTitle="Scheduling Cycle"></nz-step>
+ </nz-steps>
+ </div>
+ <div>
+ <form [formGroup]="createJob">
+ <div *nzModalFooter>
+ <button nz-button nzType="default" (click)="isVisible=false;"> Cancel </button>
+ <button id="go" nz-button nzType="primary" [disabled]="!(createJob.get('jobName').valid && createJob.get('description').valid)" (click)="handleOk()">{{okText}}</button>
+ <button *ngIf="current>0" nz-button nzType="default" style="float: left;" (click)="current=current-1;okText = 'Next Step';">Prev Step</button>
+ </div>
+ <div [ngSwitch]="current" style="margin-top: 30px;">
+ <div *ngSwitchCase="0">
+ <div>
+ <label class="form-label"><span class="red-star">* </span> Job Name:</label>
+ <input type="text" id="jobname" style="margin-top: 32px;width: 350px;" class="form-control" formControlName="jobName">
+ </div>
+ <div>
+ <label class="form-label"><span class="red-star">* </span>Description:</label>
+ <textarea rows="6" class="form-control" style="margin-top: 32px;width: 350px;" formControlName="description"></textarea>
+ </div>
+ </div>
+ <div *ngSwitchCase="1" id="page2">
+ <div>
+ <label class="form-label"><span class="red-star">* </span> Monitor Object:</label>
+ <nz-select formControlName="monitorObject" nzPlaceHolder="Choose" style="width: 350px;margin-top: 30px;">
+ <nz-option *ngFor="let monitorObject of monitorObjects" [nzValue]="monitorObject" [nzLabel]="monitorObject"></nz-option>
+ </nz-select>
+ </div>
+ <div>
+ <label class="form-label"><span class="red-star">* </span> Rule Template:</label>
+ <nz-select formControlName="ruleTemplate" nzPlaceHolder="Choose" style="width: 350px;margin-top: 30px;">
+ <nz-option *ngFor="let ruleTemplate of ruleTemplates" [nzValue]="ruleTemplate" [nzLabel]="ruleTemplate"></nz-option>
+ </nz-select>
+ </div>
+ <div>
+ <label class="form-label"><span class="red-star">* </span> Rule Type:</label>
+ <nz-radio-group formControlName="ruleType" style="width: 350px;margin-top: 30px;margin-bottom: 30px;">
+ <label *ngFor="let ruletype of ruleTypes" nz-radio [nzValue]="ruletype">{{ ruletype }}</label>
+ </nz-radio-group>
+ </div>
+ </div>
+ <div *ngSwitchCase="2">
+ <div>
+ <label class="pg3-form-label"><span class="red-star">* </span> Start Date:</label>
+ <nz-date-picker [nzFormat]="'yyyy/MM/dd'" formControlName="startDate" style="width: 350px;margin-top: 30px;"></nz-date-picker>
+ </div>
+ <div>
+ <label class="pg3-form-label"><span class="red-star">* </span> Schedule Cycle:</label>
+ <nz-select formControlName="scheduleCycle" nzPlaceHolder="Choose" style="width: 195px;margin-top: 30px;margin-bottom: 30px;">
+ <nz-option *ngFor="let scheduleCycle of scheduleCycles" [nzValue]="scheduleCycle" [nzLabel]="scheduleCycle"></nz-option>
+ </nz-select>
+ </div>
+ </div>
+ </div>
+ </form>
+ </div>
+ </nz-modal>
+</nz-layout>
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.scss b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.scss
index 3d56d22..88ee4fc 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.scss
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.scss
@@ -16,3 +16,53 @@
* specific language governing permissions and limitations
* under the License.
*/
+
+ #jobOuter{
+ background-color: white;
+ padding-left: 30px;
+ padding-top: 20px;
+ }
+
+ #jobData{
+ margin-top: 25px;
+ margin-left: 20px;
+ margin-right: 20px;
+ background-color:white;
+ padding-left: 10px;
+ padding-right: 10px;
+ }
+
+ input.ng-invalid.ng-touched {
+ border: 1px solid red;
+ }
+
+ textarea.ng-invalid.ng-touched {
+ border: 1px solid red;
+}
+
+.red-star {
+ margin-top: 20px;
+ color: red;
+}
+
+.form-label {
+ float:left;
+ width:200px;
+ text-align: right;
+ padding-right: 12px;
+ margin-top: 32px;
+ margin-left: 10px;
+ clear: left;
+ color: black;
+}
+
+.pg3-form-label {
+ float:left;
+ width:200px;
+ text-align: right;
+ padding-right: 12px;
+ margin-top: 32px;
+ margin-left: 100px;
+ clear: left;
+ color: black;
+}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.ts
index 985fece..0c2e1b4 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.ts
@@ -18,6 +18,7 @@
*/
import { Component, OnInit } from '@angular/core';
+import { FormGroup, FormControl, Validators, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'submarine-job',
@@ -26,9 +27,77 @@ import { Component, OnInit } from '@angular/core';
})
export class JobComponent implements OnInit {
+ //About show existing jobs
+ showJob = 'All';
+ searchText = '';
+ joblist=[
+ {
+ name: 'Spark actuator',
+ id: 1,
+ owner: 'Frank',
+ actuator: 'Spark Actuator',
+ status: 'Running',
+ progress: 85,
+ lastRun: '2009-09-24 20:38:24'
+ }
+ ]
+ //About new Job
+ createJob: FormGroup;
+ current = 0;
+ okText = 'Next Step';
+ isVisible = false;
+
+ monitorObjects = ['Table1', 'Table2'];
+ ruleTemplates = ['Template1', 'Template2'];
+ ruleTypes = ['Strong', 'Weak'];
+
+ scheduleCycles = ['Month', 'Week'];
+
constructor() { }
ngOnInit() {
+ this.createJob = new FormGroup({
+ 'jobName': new FormControl(null, Validators.required),
+ 'description': new FormControl(null, [Validators.required]),
+ 'monitorObject': new FormControl('Table1'),
+ 'ruleTemplate': new FormControl('Template1'),
+ 'ruleType': new FormControl('Strong'),
+ 'startDate': new FormControl(new Date()),
+ 'scheduleCycle': new FormControl('Month')
+ });
+ }
+
+ handleOk(){
+ if (this.current === 1){
+ this.okText = 'Complete';
+ this.current++;
+ }
+ else if (this.current === 2){
+ this.okText = 'Next Step';
+ this.current = 0;
+ this.isVisible = false;
+ //TODO(jasoonn): Create Real Job
+ console.log(this.createJob);
+ }
+ else {
+ this.current++;
+ }
}
+ //TODO(jasoonn): Filter Job list
+ filter(event){
+ console.log(this.searchText+event.key);
+ }
+ //TODO(jasoonn): Perfrom part of list
+ showChange(){
+ console.log("Change to " + this.showJob);
+ }
+ //TODO(jasoonn): Start Job
+ startJob(job){
+ console.log(job);
+ }
+ //TODO(jasoonn): Edit job
+ editJob(job){
+ console.log(job);
+ }
}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.scss b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.module.ts
similarity index 81%
copy from submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.scss
copy to submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.module.ts
index 3d56d22..01a9df3 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.component.scss
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/job/job.module.ts
@@ -1,4 +1,7 @@
-/*!
+import { NgModule } from "@angular/core";
+import { ReactiveFormsModule } from '@angular/forms';
+
+/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -16,3 +19,8 @@
* specific language governing permissions and limitations
* under the License.
*/
+
+ @NgModule({
+ exports: [ReactiveFormsModule]
+ })
+ export class jobModule {}
diff --git a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.module.ts b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.module.ts
index 25609cd..6346b80 100644
--- a/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.module.ts
+++ b/submarine-workbench/workbench-web-ng/src/app/pages/workbench/workbench.module.ts
@@ -31,6 +31,8 @@ import { WorkbenchComponent } from './workbench.component';
import { WorkspaceComponent } from './workspace/workspace.component';
import { FormsModule } from '@angular/forms';
import { WorkspaceModule } from './workspace/workspace.module';
+import { jobModule } from './job/job.module';
+
@NgModule({
@@ -49,7 +51,8 @@ import { WorkspaceModule } from './workspace/workspace.module';
NgZorroAntdModule,
RouterModule,
FormsModule,
- WorkspaceModule
+ WorkspaceModule,
+ jobModule
]
})
export class WorkbenchModule {
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@submarine.apache.org
For additional commands, e-mail: dev-help@submarine.apache.org