http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/app.component.html ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/app.component.html b/ui/angular/src/app/app.component.html index da8f7e2..d5b2951 100644 --- a/ui/angular/src/app/app.component.html +++ b/ui/angular/src/app/app.component.html @@ -31,7 +31,7 @@ under the License. </div> <div class="collapse navbar-collapse" id="navbar-collapse"> <ul class="nav navbar-nav"> - <li><a routerLink="/health" routerLinkActive="active" class="changecolor">Health</a></li> + <li><a routerLink="/health" class="changecolor">Health</a></li> <li><a routerLink="/measures" class="highlight">Measures</a></li> <li><a routerLink="/jobs">Jobs</a></li> <li><a routerLink="/mydashboard">My Dashboard</a></li>
http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/app.module.ts ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/app.module.ts b/ui/angular/src/app/app.module.ts index 4815e55..44ea4a1 100644 --- a/ui/angular/src/app/app.module.ts +++ b/ui/angular/src/app/app.module.ts @@ -39,7 +39,7 @@ import { MeasureDetailComponent } from './measure/measure-detail/measure-detail. import { MetricComponent } from './metric/metric.component'; import { DetailMetricComponent } from './metric/detail-metric/detail-metric.component'; import { DataassetComponent } from './dataasset/dataasset.component'; -import { CreateJobComponent } from './job/create-job/create-job.component'; +import { BatchComponent } from './job/create-job/batch/batch.component'; import { AcComponent} from './measure/create-measure/ac/ac.component'; import { PrComponent } from './measure/create-measure/pr/pr.component'; import { PubComponent } from './measure/create-measure/pub/pub.component'; @@ -50,8 +50,10 @@ import { TruncatePipe} from './sidebar/truncate.pipe'; import { ConfigurationComponent } from './measure/create-measure/configuration/configuration.component'; import { NouisliderModule } from 'ng2-nouislider'; import { HttpService } from './service/http.service'; -import {LoaderService} from './loader/loader.service'; +import { LoaderService } from './loader/loader.service'; import { LoaderComponent } from './loader/loader.component'; +import { JobDetailComponent } from './job/job-detail/job-detail.component'; +import { StreamingComponent } from './job/create-job/streaming/streaming.component'; const appRoutes: Routes = [ @@ -73,11 +75,20 @@ const appRoutes: Routes = [ }, { path: 'jobs', - component: JobComponent, + component: JobComponent }, { - path: 'createjob', - component: CreateJobComponent, + path: 'job/:id', + component: JobDetailComponent + }, + { + path: 'createjob/batch', + component: BatchComponent + + }, + { + path: 'createjob/streaming', + component: StreamingComponent }, { @@ -137,7 +148,7 @@ const appRoutes: Routes = [ MetricComponent, DetailMetricComponent, DataassetComponent, - CreateJobComponent, + BatchComponent, AcComponent, PrComponent, PubComponent, @@ -145,7 +156,9 @@ const appRoutes: Routes = [ RuleComponent, TruncatePipe, ConfigurationComponent, - LoaderComponent + LoaderComponent, + JobDetailComponent, + StreamingComponent ], imports: [ BrowserModule, http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/health/health.component.ts ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/health/health.component.ts b/ui/angular/src/app/health/health.component.ts index 8ea6c55..c0fe83b 100644 --- a/ui/angular/src/app/health/health.component.ts +++ b/ui/angular/src/app/health/health.component.ts @@ -167,7 +167,7 @@ export class HealthComponent implements OnInit { this.resizeTreeMap(); this.chartOption = option; } - + checkvalue(job){ return job.metricValues.length === 0; } @@ -184,7 +184,7 @@ export class HealthComponent implements OnInit { for (let mesName in this.mesWithJob) { var jobs = this.mesWithJob[mesName]; if ( - jobs.length > 0 && jobs[0].type == "accuracy" + jobs.length > 0 && jobs[0].type == "ACCURACY" ) { var jobs = this.mesWithJob[mesName]; var node = null; @@ -192,7 +192,7 @@ export class HealthComponent implements OnInit { node.name = mesName; node.dq = 0; node.metrics = []; - node.type = "accuracy"; + node.type = "ACCURACY"; for (let i = 0; i < jobs.length; i++) { if (jobs[i].metricValues.length != 0) { var metricNode = { @@ -235,4 +235,4 @@ export class HealthComponent implements OnInit { // }) } -} \ No newline at end of file +} http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/create-job/batch/batch.component.css ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/create-job/batch/batch.component.css b/ui/angular/src/app/job/create-job/batch/batch.component.css new file mode 100644 index 0000000..6288f12 --- /dev/null +++ b/ui/angular/src/app/job/create-job/batch/batch.component.css @@ -0,0 +1,90 @@ +/* +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. +*/ +.job { + font-size: 20px; +} + +.info { + color: #b2c831; +} + +.btn-o { + background: 0 0 !important; +} + +legend { + background-color: #000; + color: #007aff; + left: 10px; + padding: 0 10px; + position: absolute; + top: -12px; + font-weight: 400; + color: #fff; + margin-bottom: 20px; + font-size: 21px; + width: auto !important; + border: none !important; +} +fieldset { + border: 1px solid #e6e8e8; + border-radius: 5px; + margin: 20px 0; + padding: 25px; + position: relative; + min-width: 0; + display: block; + height: 320px; +} + +.formStep { + background-color: #000; +} +.setcolor { + color: #b2c831; +} +.setgrey { + color: #888888; +} +.mat-calendar-table { + height: 400px; +} +.mat-datepicker-content { + overflow-y: auto; +} + +#md-datepicker-0 { + height: 250px; +} + +.center { + margin-left: 5%; +} + +.range { + display: block; + width: 20%; + height: 10%; + margin-bottom: 5px; +} +.setborder { + border: 2px solid; + border-radius: 5px; + width: 8%; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/create-job/batch/batch.component.html ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/create-job/batch/batch.component.html b/ui/angular/src/app/job/create-job/batch/batch.component.html new file mode 100644 index 0000000..86b82e3 --- /dev/null +++ b/ui/angular/src/app/job/create-job/batch/batch.component.html @@ -0,0 +1,179 @@ +<!-- +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. +--> +<div class="mask" *ngIf='maskOpen' (click)="close()"></div> +<div class="container-fluid"> + <div class="row"> + <h5 class="over-title margin-bottom-15 job">Create Job</h5> + </div> + <!--//row--> + <div class="row"> + <!-- <form name="Form" id="form" novalidate> --> + <form name="jobForm" id="form" (ngSubmit)="submit(jobForm)" #jobForm="ngForm" novalidate> + <div class="formStep"> + <label class="stepDesc info">Please setup the job required information</label> + <div class="container-fluid"> + <!-- schema definition list --> + <div class="col-md-12 col-lg-12 col-sm-12"> + <fieldset (window:resize)="onResize($event)"> + <legend> + Required Information + </legend> + <div class="y-scrollable"> + <div class="col-md-12 col-lg-12 col-sm-12"> + <div class="form-group" [ngClass]="{'has-error':jobName.dirty&&jobName.invalid, 'has-success':jobName.valid}"> + <label class="col-md-2 col-lg-2 col-sm-2 control-label"> + Job Name<span class="symbol required"></span>: + </label> + <div class="col-md-10 col-lg-10 col-sm-10 "> + <input type="text" class="form-control" [(ngModel)]="jobname" name="jobName" #jobName="ngModel" placeholder="Please input the job name" required pattern="^[a-zA-Z0-9_-]*$" id="jobName"> + <span class="error text-small block " *ngIf="jobName.dirty && (jobName.errors?.required)"> + Job Name is required</span> + <span class="error text-small block " *ngIf="jobName.dirty && (jobName.errors?.pattern)">Only letter, number, "-" and "_" are allowed</span> + </div> + </div> + </div> + <div class="col-md-12 col-lg-12 col-sm-12"> + <div class="form-group"> + <label for="measureSelector" class="col-md-2 col-lg-2 col-sm-2 control-label"> + Measure Name<span class="symbol required"></span>: + </label> + <div class="col-md-10 col-lg-10 col-sm-10 "> + <select id="measureSelector" class="form-control" ngControl="name" required name="measure.name" [(ngModel)]="measure" (ngModelChange)="onChange($event)"> + <option *ngFor="let row of Measures" value="{{row.name}}">{{row.name}}</option> + </select> + </div> + </div> + </div> + <div class="col-md-12 col-lg-12 col-sm-12"> + <div class="form-group"> + <label for="measureSelector" class="col-md-2 col-lg-2 col-sm-2 control-label"> + Cron Expression<span class="symbol required"></span>: + </label> + <div class="col-md-10 col-lg-10 col-sm-10 "> + <input class="form-control" ngControl="name" required [(ngModel)]="cronExp" name="cronExp"> + </div> + </div> + </div> + <div *ngFor="let connector of this.dropdownList; let i=index"> + <div> + <label style="padding-left: 30px;padding-top: 10px;"> + please select data range for {{connector.name}} + </label> + </div> + <p style="margin-left:60px" class="setcolor"> + <i class="fa fa-info-circle"></i> One step means a partition size,and {{connector.name}} partition size = {{connector.size}} + </p> + <div class="col-md-11 col-lg-11 col-sm-11 center"> + begin : + <input type="number" class="setborder" value="{{ someKeyboard[i][0] }}" [(ngModel)]="someKeyboard[i][0]" max="0" name="begin{{i}}" (ngModelChange)="changeRange(index,someKeyboard[i][0],i)"> end : + <input type="number" class="setborder" max="0" value="{{ someKeyboard[i][1] }}" [(ngModel)]="someKeyboard[i][1]" name="end{{i}}" (ngModelChange)="changeRange(1,$event,i)"> + </div> + <nouislider class="col-md-11 col-lg-11 col-sm-11 center" id="slider{{i}}" #sliderRef name="slider{{i}}" [config]="someKeyboardConfig[i]" [(ngModel)]="someKeyboard[i]" (ngModelChange)="rangeChange($event,i)" (keyup)="blinkKeyupLabel()" (keydown)="blinkKeydownLabel()" style="margin-bottom: 5rem"></nouislider> + </div> + </div> + <div class="setcolor"> + <p> + <i class="fa fa-info-circle"></i> After submitted, please go to " + <a class="bark-link" href="/jobs">Jobs</a>" to check the job status + </p> + </div> + </fieldset> + </div> + <div class="form-group btn-container"> + <button class="btn btn-primary btn-o back-step btn-wide pull-left" (click)="prev()"> + <i class="fa fa-arrow-circle-left"></i> Back + </button> + <toaster-container></toaster-container> + <button type="submit" class="btn btn-primary btn-o next-step btn-wide pull-right" (click)="submit(jobForm)"> + Submit + </button> + </div> + </div> + </div> + <div class="modal fade" id="confirm-job" role="dialog" [ngClass]="{'in': visibleAnimate}" [ngStyle]="{'display': visible ? 'block' : 'none', 'opacity': visibleAnimate ? 1 : 0}" (click)="onContainerClicked($event)"> + <div class="modal-dialog modal-xg modal-lg"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true" (click)="hide()">×</button> + <h4 class="modal-title">Save the job with the below information?</h4> + </div> + <div class="modal-body"> + <div class="container-fluid" id="viewJobContent" style="overflow:auto;"> + <div class="row"> + <h5 class="over-title margin-bottom-15">Basic information</h5> + </div> + <div class="row"> + <div class="col-lg-12 col-md-12 col-sm-12"> + <div id="viewrule-definition" class="viewrule-content"> + <div class="row"> + <label class="col-md-4 col-lg-4 col-sm-4"> + Measure Name: + </label> + <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff"> + {{measure}} + <!-- {{Measures}} --> + </div> + </div> + <div class="row"> + <label class="col-md-4 col-lg-4 col-sm-4"> + Cron Expression: + </label> + <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff"> + {{cronExp}} + </div> + </div> + <div *ngFor="let connector of this.dropdownList; let i=index"> + <div class="row"> + <label class="col-md-12 col-lg-12 col-sm-12"> + {{connector.name}}: + </label> + </div> + <div class="row"> + <label class="col-md-4 col-lg-4 col-sm-4"> + Begin: + </label> + <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff"> + {{originBegin[i]}} + </div> + </div> + <div class="row"> + <label class="col-md-4 col-lg-4 col-sm-4"> + Length: + </label> + <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff"> + {{originLength[i]}} + </div> + </div> + </div> + </div> + </div> + </div> + <br/> + </div> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-default" data-dismiss="modal" (click)="hide()">Cancel</button> + <button type="button" id="save" class="btn btn-primary" (click)="save()">Save</button> + </div> + </div> + </div> + </div> + </form> + </div> +</div> http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/create-job/batch/batch.component.spec.ts ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/create-job/batch/batch.component.spec.ts b/ui/angular/src/app/job/create-job/batch/batch.component.spec.ts new file mode 100644 index 0000000..25b1479 --- /dev/null +++ b/ui/angular/src/app/job/create-job/batch/batch.component.spec.ts @@ -0,0 +1,43 @@ +/* +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. +*/ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BatchComponent } from './batch.component'; + +describe('BatchComponent', () => { + let component: BatchComponent; + let fixture: ComponentFixture<BatchComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ BatchComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(BatchComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/create-job/batch/batch.component.ts ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/create-job/batch/batch.component.ts b/ui/angular/src/app/job/create-job/batch/batch.component.ts new file mode 100644 index 0000000..d94748f --- /dev/null +++ b/ui/angular/src/app/job/create-job/batch/batch.component.ts @@ -0,0 +1,395 @@ +/* +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. +*/ +import { Component, OnInit, AfterViewChecked, ViewChildren } from "@angular/core"; +import { FormControl } from "@angular/forms"; +import { FormsModule } from "@angular/forms"; +import { MaxLengthValidator } from "@angular/forms"; +import { NgControlStatus, Validators } from "@angular/forms"; +import { PatternValidator } from "@angular/forms"; +import { MatDatepickerModule } from "@angular/material"; +import { ServiceService } from "../../../service/service.service"; +import { AngularMultiSelectModule } from "angular2-multiselect-dropdown/angular2-multiselect-dropdown"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { ToasterModule, ToasterService, ToasterConfig } from "angular2-toaster"; +import * as $ from "jquery"; +import { HttpParams } from "@angular/common/http"; +import { Router } from "@angular/router"; +import { NouisliderModule } from "ng2-nouislider"; +import { HttpClient } from "@angular/common/http"; + +@Component({ + selector: "app-batch", + templateUrl: "./batch.component.html", + providers: [ServiceService], + styleUrls: ["./batch.component.css"] +}) +export class BatchComponent implements OnInit, AfterViewChecked { + constructor( + toasterService: ToasterService, + private http: HttpClient, + private router: Router, + public serviceService: ServiceService + ) { + this.toasterService = toasterService; + } + + @ViewChildren("sliderRef") sliderRefs; + + someKeyboard = []; + someKeyboardConfig = []; + config: any; + baseline: string; + cronExp: string; + dropdownList = []; + currentStep = 1; + maskOpen = false; + keyupLabelOn = false; + keydownLabelOn = false; + createResult = ""; + jobname: string; + Measures = []; + measure: string; + measureid: any; + + newJob = { + "cron.expression": "", + "measure.id": "", + "job.name": "", + "cron.time.zone": "", + // "cron.time.zone": "GMT+8:00", + // "predicate.config": { + // "interval": "1m", + // "repeat": 2 + // }, + "data.segments": [ + // { + // "data.connector.index": "source[0]", + // "segment.range": { + // "begin": "", + // "length": "" + // } + // }, + // { + // "data.connector.index": "target[0]", + // "segment.range": { + // "begin": "", + // "length": "" + // } + // } + ] + }; + + beginTime = []; + timeLength = []; + originBegin = []; + originLength = []; + + private toasterService: ToasterService; + + public visible = false; + public visibleAnimate = false; + + public hide(): void { + this.visibleAnimate = false; + setTimeout(() => (this.visible = false), 300); + this.originBegin = []; + this.originLength = []; + $("#save").removeAttr("disabled"); + } + + public onContainerClicked(event: MouseEvent): void { + if ((<HTMLElement>event.target).classList.contains("modal")) { + this.hide(); + } + } + + close() { + this.maskOpen = false; + } + + prev() { + history.back(); + } + + submit(form) { + if (!form.valid) { + this.toasterService.pop("error", "Error!", "Please complete the form!"); + return false; + } + this.measureid = this.getMeasureId(); + let time = new Date().getTimezoneOffset() / 60; + let timezone = "GMT" + time + ":00"; + this.newJob = { + "job.name": this.jobname, + "measure.id": this.measureid, + "cron.expression": this.cronExp, + "cron.time.zone": timezone, + // "cron.time.zone": "GMT+8:00", + // "predicate.config": { + // "interval": "1m", + // "repeat": 2 + // }, + "data.segments": [ + // { + // "data.connector.index": "source[0]", + // "segment.range": { + // "begin": "", + // "length": "" + // } + // }, + // { + // "data.connector.index": "target[0]", + // "segment.range": { + // "begin": "", + // "length": "" + // } + // } + ] + }; + for (let i = 0; i < this.dropdownList.length; i++) { + var connector = this.dropdownList[i]; + var begin = this.someKeyboard[i][0]; + var length = this.someKeyboard[i][1] - this.someKeyboard[i][0]; + var beginStr = this.getTimeByUnit(begin, connector.size); + var lengthStr = this.getTimeByUnit(length, connector.size); + this.newJob["data.segments"].push({ + "data.connector.name": connector.connectorname, + "as.baseline": true, + "segment.range": { + begin: beginStr, + length: lengthStr + } + }); + this.originBegin.push(beginStr); + this.originLength.push(lengthStr); + } + if (this.dropdownList.length == 2) { + delete this.newJob["data.segments"][1]["as.baseline"]; + } + this.visible = true; + setTimeout(() => (this.visibleAnimate = true), 100); + } + + save() { + var addJobs = this.serviceService.config.uri.addJobs; + $("#save").attr("disabled", "true"); + this.http.post(addJobs, this.newJob).subscribe( + data => { + this.createResult = data["results"]; + this.hide(); + this.router.navigate(["/jobs"]); + }, + err => { + let response = JSON.parse(err.error); + if(response.code === '40004'){ + this.toasterService.pop("error", "Error!", "Job name already exists!"); + } else { + this.toasterService.pop("error", "Error!", "Error when creating job"); + } + console.log("Error when creating job"); + } + ); + } + + onResize(event) { + this.resizeWindow(); + } + + resizeWindow() { + var stepSelection = ".formStep"; + $(stepSelection).css({ + height: + window.innerHeight - + $(stepSelection).offset().top - + $("#footerwrap").outerHeight() + }); + $("fieldset").height( + $(stepSelection).height() - + $(stepSelection + ">.stepDesc").height() - + $(".btn-container").height() - + 200 + ); + $(".y-scrollable").css({ + height: $("fieldset").height() + }); + $("#data-asset-pie").css({ + height: $("#data-asset-pie") + .parent() + .width(), + width: $("#data-asset-pie") + .parent() + .width() + }); + } + + getTimeByUnit(multiplier, unit) { + var regex = /^(\d+)([a-zA-Z]+)$/g; + var arr = regex.exec(unit); + if (arr.length > 2) { + var n = parseInt(arr[1]); + var unitStr = arr[2]; + return ((n * multiplier).toString() + arr[2]); + } else { + return multiplier.toString(); + } + } + + getMeasureId() { + for (let index in this.Measures) { + if (this.measure == this.Measures[index].name) { + return this.Measures[index].id; + } + } + } + + onChange(measure) { + this.dropdownList = []; + for (let index in this.Measures) { + var map = this.Measures[index]; + if (measure == map.name) { + var source = map["data.sources"]; + for (let i = 0; i < source.length; i++) { + var details = source[i].connectors; + for (let j = 0; j < details.length; j++) { + if (details[j]["data.unit"] != undefined) { + var table = + details[j].config.database + + "." + + details[j].config["table.name"]; + var size = details[j]["data.unit"]; + var connectorname = details[j]["name"]; + var detail = { + id: i + 1, + name: table, + size: size, + connectorname: connectorname + }; + this.dropdownList.push(detail); + } + } + } + } + } + for (let i = 0; i < this.dropdownList.length; i++) { + this.someKeyboard[i] = [-1, 0]; + this.someKeyboardConfig[i] = JSON.parse(JSON.stringify(this.config)); + if (this.sliderRefs._results[i]) { + this.sliderRefs._results[i].slider.updateOptions({ + range: { + min: -10, + max: 0 + } + }); + } + } + } + + changeRange(index, value, i) { + let newRange = []; + newRange[i] = [this.someKeyboard[i][0], this.someKeyboard[i][1]]; + newRange[i][index] = value; + this.updateSliderRange(value, i); + this.someKeyboard[i] = newRange[i]; + } + + rangeChange(evt, i) { + var oldmin = this.sliderRefs._results[i].config.range.min; + if (evt[0] - oldmin <= 2) { + this.sliderRefs._results[i].slider.updateOptions({ + range: { + min: oldmin - 10, + max: 0 + } + }); + } + if (evt[0] - oldmin >= 13) { + this.sliderRefs._results[i].slider.updateOptions({ + range: { + min: oldmin + 10, + max: 0 + } + }); + } + this.someKeyboard[i] = evt; + } + + updateSliderRange(value, i) { + // setTimeout(() => { + var oldmin = this.sliderRefs._results[i].config.range.min; + var oldmax = this.sliderRefs._results[i].config.range.max; + var newmin = Math.floor(value / 10); + if (value - oldmin <= 3) { + this.sliderRefs._results[i].slider.updateOptions({ + range: { + min: newmin * 10, + max: 0 + } + }); + } + // }, 100) + } + + blinkKeyupLabel() { + this.keyupLabelOn = true; + setTimeout(() => { + this.keyupLabelOn = false; + }, 450); + } + + blinkKeydownLabel() { + this.keydownLabelOn = true; + setTimeout(() => { + this.keydownLabelOn = false; + }, 450); + } + + ngOnInit() { + var allModels = this.serviceService.config.uri.allModels + '?type=griffin'; + this.http.get(allModels).subscribe(data => { + let originData = data; + for(let i in originData){ + if(originData[i]["process.type"] === "BATCH"){ + this.Measures.push(originData[i]); + } + } + }); + this.config = { + behaviour: "drag", + connect: true, + start: [-10, 0], + keyboard: true, // same as [keyboard]="true" + step: 1, + pageSteps: 0, // number of page steps, defaults to 10 + range: { + min: -10, + max: 0 + }, + pips: { + mode: "steps", + density: 10, + // values: 1, + stepped: true + } + }; + } + + ngAfterViewChecked() { + this.resizeWindow(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/create-job/create-job.component.css ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/create-job/create-job.component.css b/ui/angular/src/app/job/create-job/create-job.component.css deleted file mode 100644 index 6288f12..0000000 --- a/ui/angular/src/app/job/create-job/create-job.component.css +++ /dev/null @@ -1,90 +0,0 @@ -/* -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. -*/ -.job { - font-size: 20px; -} - -.info { - color: #b2c831; -} - -.btn-o { - background: 0 0 !important; -} - -legend { - background-color: #000; - color: #007aff; - left: 10px; - padding: 0 10px; - position: absolute; - top: -12px; - font-weight: 400; - color: #fff; - margin-bottom: 20px; - font-size: 21px; - width: auto !important; - border: none !important; -} -fieldset { - border: 1px solid #e6e8e8; - border-radius: 5px; - margin: 20px 0; - padding: 25px; - position: relative; - min-width: 0; - display: block; - height: 320px; -} - -.formStep { - background-color: #000; -} -.setcolor { - color: #b2c831; -} -.setgrey { - color: #888888; -} -.mat-calendar-table { - height: 400px; -} -.mat-datepicker-content { - overflow-y: auto; -} - -#md-datepicker-0 { - height: 250px; -} - -.center { - margin-left: 5%; -} - -.range { - display: block; - width: 20%; - height: 10%; - margin-bottom: 5px; -} -.setborder { - border: 2px solid; - border-radius: 5px; - width: 8%; -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/create-job/create-job.component.html ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/create-job/create-job.component.html b/ui/angular/src/app/job/create-job/create-job.component.html deleted file mode 100644 index 86b82e3..0000000 --- a/ui/angular/src/app/job/create-job/create-job.component.html +++ /dev/null @@ -1,179 +0,0 @@ -<!-- -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. ---> -<div class="mask" *ngIf='maskOpen' (click)="close()"></div> -<div class="container-fluid"> - <div class="row"> - <h5 class="over-title margin-bottom-15 job">Create Job</h5> - </div> - <!--//row--> - <div class="row"> - <!-- <form name="Form" id="form" novalidate> --> - <form name="jobForm" id="form" (ngSubmit)="submit(jobForm)" #jobForm="ngForm" novalidate> - <div class="formStep"> - <label class="stepDesc info">Please setup the job required information</label> - <div class="container-fluid"> - <!-- schema definition list --> - <div class="col-md-12 col-lg-12 col-sm-12"> - <fieldset (window:resize)="onResize($event)"> - <legend> - Required Information - </legend> - <div class="y-scrollable"> - <div class="col-md-12 col-lg-12 col-sm-12"> - <div class="form-group" [ngClass]="{'has-error':jobName.dirty&&jobName.invalid, 'has-success':jobName.valid}"> - <label class="col-md-2 col-lg-2 col-sm-2 control-label"> - Job Name<span class="symbol required"></span>: - </label> - <div class="col-md-10 col-lg-10 col-sm-10 "> - <input type="text" class="form-control" [(ngModel)]="jobname" name="jobName" #jobName="ngModel" placeholder="Please input the job name" required pattern="^[a-zA-Z0-9_-]*$" id="jobName"> - <span class="error text-small block " *ngIf="jobName.dirty && (jobName.errors?.required)"> - Job Name is required</span> - <span class="error text-small block " *ngIf="jobName.dirty && (jobName.errors?.pattern)">Only letter, number, "-" and "_" are allowed</span> - </div> - </div> - </div> - <div class="col-md-12 col-lg-12 col-sm-12"> - <div class="form-group"> - <label for="measureSelector" class="col-md-2 col-lg-2 col-sm-2 control-label"> - Measure Name<span class="symbol required"></span>: - </label> - <div class="col-md-10 col-lg-10 col-sm-10 "> - <select id="measureSelector" class="form-control" ngControl="name" required name="measure.name" [(ngModel)]="measure" (ngModelChange)="onChange($event)"> - <option *ngFor="let row of Measures" value="{{row.name}}">{{row.name}}</option> - </select> - </div> - </div> - </div> - <div class="col-md-12 col-lg-12 col-sm-12"> - <div class="form-group"> - <label for="measureSelector" class="col-md-2 col-lg-2 col-sm-2 control-label"> - Cron Expression<span class="symbol required"></span>: - </label> - <div class="col-md-10 col-lg-10 col-sm-10 "> - <input class="form-control" ngControl="name" required [(ngModel)]="cronExp" name="cronExp"> - </div> - </div> - </div> - <div *ngFor="let connector of this.dropdownList; let i=index"> - <div> - <label style="padding-left: 30px;padding-top: 10px;"> - please select data range for {{connector.name}} - </label> - </div> - <p style="margin-left:60px" class="setcolor"> - <i class="fa fa-info-circle"></i> One step means a partition size,and {{connector.name}} partition size = {{connector.size}} - </p> - <div class="col-md-11 col-lg-11 col-sm-11 center"> - begin : - <input type="number" class="setborder" value="{{ someKeyboard[i][0] }}" [(ngModel)]="someKeyboard[i][0]" max="0" name="begin{{i}}" (ngModelChange)="changeRange(index,someKeyboard[i][0],i)"> end : - <input type="number" class="setborder" max="0" value="{{ someKeyboard[i][1] }}" [(ngModel)]="someKeyboard[i][1]" name="end{{i}}" (ngModelChange)="changeRange(1,$event,i)"> - </div> - <nouislider class="col-md-11 col-lg-11 col-sm-11 center" id="slider{{i}}" #sliderRef name="slider{{i}}" [config]="someKeyboardConfig[i]" [(ngModel)]="someKeyboard[i]" (ngModelChange)="rangeChange($event,i)" (keyup)="blinkKeyupLabel()" (keydown)="blinkKeydownLabel()" style="margin-bottom: 5rem"></nouislider> - </div> - </div> - <div class="setcolor"> - <p> - <i class="fa fa-info-circle"></i> After submitted, please go to " - <a class="bark-link" href="/jobs">Jobs</a>" to check the job status - </p> - </div> - </fieldset> - </div> - <div class="form-group btn-container"> - <button class="btn btn-primary btn-o back-step btn-wide pull-left" (click)="prev()"> - <i class="fa fa-arrow-circle-left"></i> Back - </button> - <toaster-container></toaster-container> - <button type="submit" class="btn btn-primary btn-o next-step btn-wide pull-right" (click)="submit(jobForm)"> - Submit - </button> - </div> - </div> - </div> - <div class="modal fade" id="confirm-job" role="dialog" [ngClass]="{'in': visibleAnimate}" [ngStyle]="{'display': visible ? 'block' : 'none', 'opacity': visibleAnimate ? 1 : 0}" (click)="onContainerClicked($event)"> - <div class="modal-dialog modal-xg modal-lg"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal" aria-hidden="true" (click)="hide()">×</button> - <h4 class="modal-title">Save the job with the below information?</h4> - </div> - <div class="modal-body"> - <div class="container-fluid" id="viewJobContent" style="overflow:auto;"> - <div class="row"> - <h5 class="over-title margin-bottom-15">Basic information</h5> - </div> - <div class="row"> - <div class="col-lg-12 col-md-12 col-sm-12"> - <div id="viewrule-definition" class="viewrule-content"> - <div class="row"> - <label class="col-md-4 col-lg-4 col-sm-4"> - Measure Name: - </label> - <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff"> - {{measure}} - <!-- {{Measures}} --> - </div> - </div> - <div class="row"> - <label class="col-md-4 col-lg-4 col-sm-4"> - Cron Expression: - </label> - <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff"> - {{cronExp}} - </div> - </div> - <div *ngFor="let connector of this.dropdownList; let i=index"> - <div class="row"> - <label class="col-md-12 col-lg-12 col-sm-12"> - {{connector.name}}: - </label> - </div> - <div class="row"> - <label class="col-md-4 col-lg-4 col-sm-4"> - Begin: - </label> - <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff"> - {{originBegin[i]}} - </div> - </div> - <div class="row"> - <label class="col-md-4 col-lg-4 col-sm-4"> - Length: - </label> - <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff"> - {{originLength[i]}} - </div> - </div> - </div> - </div> - </div> - </div> - <br/> - </div> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-default" data-dismiss="modal" (click)="hide()">Cancel</button> - <button type="button" id="save" class="btn btn-primary" (click)="save()">Save</button> - </div> - </div> - </div> - </div> - </form> - </div> -</div> http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/create-job/create-job.component.spec.ts ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/create-job/create-job.component.spec.ts b/ui/angular/src/app/job/create-job/create-job.component.spec.ts deleted file mode 100644 index 44292d5..0000000 --- a/ui/angular/src/app/job/create-job/create-job.component.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* -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. -*/ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { CreateJobComponent } from './create-job.component'; - -describe('CreateJobComponent', () => { - let component: CreateJobComponent; - let fixture: ComponentFixture<CreateJobComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ CreateJobComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(CreateJobComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); -}); http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/create-job/create-job.component.ts ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/create-job/create-job.component.ts b/ui/angular/src/app/job/create-job/create-job.component.ts deleted file mode 100644 index 2b97728..0000000 --- a/ui/angular/src/app/job/create-job/create-job.component.ts +++ /dev/null @@ -1,394 +0,0 @@ -/* -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. -*/ -import { Component, OnInit, AfterViewChecked, ViewChildren } from "@angular/core"; -import { FormControl } from "@angular/forms"; -import { FormsModule } from "@angular/forms"; -import { MaxLengthValidator } from "@angular/forms"; -import { NgControlStatus, Validators } from "@angular/forms"; -import { PatternValidator } from "@angular/forms"; -import { MatDatepickerModule } from "@angular/material"; -import { ServiceService } from "../../service/service.service"; -import { AngularMultiSelectModule } from "angular2-multiselect-dropdown/angular2-multiselect-dropdown"; -import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -import { ToasterModule, ToasterService, ToasterConfig } from "angular2-toaster"; -import * as $ from "jquery"; -import { HttpParams } from "@angular/common/http"; -import { Router } from "@angular/router"; -import { NouisliderModule } from "ng2-nouislider"; -import { HttpClient } from "@angular/common/http"; - -@Component({ - selector: "app-create-job", - templateUrl: "./create-job.component.html", - providers: [ServiceService], - styleUrls: ["./create-job.component.css"] -}) -export class CreateJobComponent implements OnInit, AfterViewChecked { - constructor( - toasterService: ToasterService, - private http: HttpClient, - private router: Router, - public serviceService: ServiceService - ) { - this.toasterService = toasterService; - } - - @ViewChildren("sliderRef") sliderRefs; - - someKeyboard = []; - someKeyboardConfig = []; - config: any; - baseline: string; - cronExp: string; - dropdownList = []; - currentStep = 1; - maskOpen = false; - keyupLabelOn = false; - keydownLabelOn = false; - createResult = ""; - jobname: string; - Measures: object; - measure: string; - measureid: any; - - newJob = { - "cron.expression": "", - "measure.id": "", - "job.name": "", - "cron.time.zone": "", - // "cron.time.zone": "GMT+8:00", - // "predicate.config": { - // "interval": "1m", - // "repeat": 2 - // }, - "data.segments": [ - // { - // "data.connector.index": "source[0]", - // "segment.range": { - // "begin": "", - // "length": "" - // } - // }, - // { - // "data.connector.index": "target[0]", - // "segment.range": { - // "begin": "", - // "length": "" - // } - // } - ] - }; - - beginTime = []; - timeLength = []; - originBegin = []; - originLength = []; - - private toasterService: ToasterService; - - public visible = false; - public visibleAnimate = false; - - public hide(): void { - this.visibleAnimate = false; - setTimeout(() => (this.visible = false), 300); - this.originBegin = []; - this.originLength = []; - $("#save").removeAttr("disabled"); - } - - public onContainerClicked(event: MouseEvent): void { - if ((<HTMLElement>event.target).classList.contains("modal")) { - this.hide(); - } - } - - close() { - this.maskOpen = false; - } - - prev() { - history.back(); - } - - submit(form) { - if (!form.valid) { - this.toasterService.pop("error", "Error!", "Please complete the form!"); - return false; - } - this.measureid = this.getMeasureId(); - let time = new Date().getTimezoneOffset() / 60; - let timezone = "GMT" + time + ":00"; - this.newJob = { - "job.name": this.jobname, - "measure.id": this.measureid, - "cron.expression": this.cronExp, - "cron.time.zone": timezone, - // "cron.time.zone": "GMT+8:00", - // "predicate.config": { - // "interval": "1m", - // "repeat": 2 - // }, - "data.segments": [ - // { - // "data.connector.index": "source[0]", - // "segment.range": { - // "begin": "", - // "length": "" - // } - // }, - // { - // "data.connector.index": "target[0]", - // "segment.range": { - // "begin": "", - // "length": "" - // } - // } - ] - }; - for (let i = 0; i < this.dropdownList.length; i++) { - var connector = this.dropdownList[i]; - var begin = this.someKeyboard[i][0]; - var length = this.someKeyboard[i][1] - this.someKeyboard[i][0]; - var beginStr = this.getTimeByUnit(begin, connector.size); - var lengthStr = this.getTimeByUnit(length, connector.size); - this.newJob["data.segments"].push({ - "data.connector.name": connector.connectorname, - "as.baseline": true, - "segment.range": { - begin: beginStr, - length: lengthStr - } - }); - this.originBegin.push(beginStr); - this.originLength.push(lengthStr); - } - if (this.dropdownList.length == 2) { - delete this.newJob["data.segments"][1]["as.baseline"]; - } - this.visible = true; - setTimeout(() => (this.visibleAnimate = true), 100); - } - - save() { - var addJobs = this.serviceService.config.uri.addJobs; - $("#save").attr("disabled", "true"); - this.http.post(addJobs, this.newJob).subscribe( - data => { - this.createResult = data["results"]; - this.hide(); - this.router.navigate(["/jobs"]); - }, - err => { - let response = JSON.parse(err.error); - if(response.code === '40004'){ - this.toasterService.pop("error", "Error!", "Job name already exists!"); - } else { - this.toasterService.pop("error", "Error!", "Error when creating job"); - } - console.log("Error when creating job"); - } - ); - } - - onResize(event) { - this.resizeWindow(); - } - - resizeWindow() { - var stepSelection = ".formStep"; - $(stepSelection).css({ - height: - window.innerHeight - - $(stepSelection).offset().top - - $("#footerwrap").outerHeight() - }); - $("fieldset").height( - $(stepSelection).height() - - $(stepSelection + ">.stepDesc").height() - - $(".btn-container").height() - - 200 - ); - $(".y-scrollable").css({ - height: $("fieldset").height() - }); - $("#data-asset-pie").css({ - height: $("#data-asset-pie") - .parent() - .width(), - width: $("#data-asset-pie") - .parent() - .width() - }); - } - - setHeight() { - $("#md-datepicker-0").height(250); - } - - getTimeByUnit(multiplier, unit) { - var regex = /^(\d+)([a-zA-Z]+)$/g; - var arr = regex.exec(unit); - if (arr.length > 2) { - var n = parseInt(arr[1]); - var unitStr = arr[2]; - return ((n * multiplier).toString() + arr[2]); - } else { - return multiplier.toString(); - } - } - - getMeasureId() { - for (let index in this.Measures) { - if (this.measure == this.Measures[index].name) { - return this.Measures[index].id; - } - } - } - - onChange(measure) { - this.dropdownList = []; - for (let index in this.Measures) { - var map = this.Measures[index]; - if (measure == map.name) { - var source = map["data.sources"]; - for (let i = 0; i < source.length; i++) { - var details = source[i].connectors; - for (let j = 0; j < details.length; j++) { - if (details[j]["data.unit"] != undefined) { - var table = - details[j].config.database + - "." + - details[j].config["table.name"]; - var size = details[j]["data.unit"]; - var connectorname = details[j]["name"]; - var detail = { - id: i + 1, - name: table, - size: size, - connectorname: connectorname - }; - this.dropdownList.push(detail); - } - } - } - } - } - for (let i = 0; i < this.dropdownList.length; i++) { - this.someKeyboard[i] = [-1, 0]; - this.someKeyboardConfig[i] = JSON.parse(JSON.stringify(this.config)); - if (this.sliderRefs._results[i]) { - this.sliderRefs._results[i].slider.updateOptions({ - range: { - min: -10, - max: 0 - } - }); - } - } - } - - changeRange(index, value, i) { - let newRange = []; - newRange[i] = [this.someKeyboard[i][0], this.someKeyboard[i][1]]; - newRange[i][index] = value; - this.updateSliderRange(value, i); - this.someKeyboard[i] = newRange[i]; - } - - rangeChange(evt, i) { - var oldmin = this.sliderRefs._results[i].config.range.min; - if (evt[0] - oldmin <= 2) { - this.sliderRefs._results[i].slider.updateOptions({ - range: { - min: oldmin - 10, - max: 0 - } - }); - } - if (evt[0] - oldmin >= 13) { - this.sliderRefs._results[i].slider.updateOptions({ - range: { - min: oldmin + 10, - max: 0 - } - }); - } - this.someKeyboard[i] = evt; - } - - updateSliderRange(value, i) { - // setTimeout(() => { - var oldmin = this.sliderRefs._results[i].config.range.min; - var oldmax = this.sliderRefs._results[i].config.range.max; - var newmin = Math.floor(value / 10); - if (value - oldmin <= 3) { - this.sliderRefs._results[i].slider.updateOptions({ - range: { - min: newmin * 10, - max: 0 - } - }); - } - // }, 100) - } - - blinkKeyupLabel() { - this.keyupLabelOn = true; - setTimeout(() => { - this.keyupLabelOn = false; - }, 450); - } - - blinkKeydownLabel() { - this.keydownLabelOn = true; - setTimeout(() => { - this.keydownLabelOn = false; - }, 450); - } - - ngOnInit() { - var allModels = this.serviceService.config.uri.allModels + '?type=griffin'; - this.http.get(allModels).subscribe(data => { - this.Measures = data; - }); - this.config = { - behaviour: "drag", - connect: true, - start: [-10, 0], - keyboard: true, // same as [keyboard]="true" - step: 1, - pageSteps: 0, // number of page steps, defaults to 10 - range: { - min: -10, - max: 0 - }, - pips: { - mode: "steps", - density: 10, - // values: 1, - stepped: true - } - }; - } - - ngAfterViewChecked() { - this.resizeWindow(); - } -} http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/create-job/streaming/streaming.component.css ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/create-job/streaming/streaming.component.css b/ui/angular/src/app/job/create-job/streaming/streaming.component.css new file mode 100644 index 0000000..6288f12 --- /dev/null +++ b/ui/angular/src/app/job/create-job/streaming/streaming.component.css @@ -0,0 +1,90 @@ +/* +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. +*/ +.job { + font-size: 20px; +} + +.info { + color: #b2c831; +} + +.btn-o { + background: 0 0 !important; +} + +legend { + background-color: #000; + color: #007aff; + left: 10px; + padding: 0 10px; + position: absolute; + top: -12px; + font-weight: 400; + color: #fff; + margin-bottom: 20px; + font-size: 21px; + width: auto !important; + border: none !important; +} +fieldset { + border: 1px solid #e6e8e8; + border-radius: 5px; + margin: 20px 0; + padding: 25px; + position: relative; + min-width: 0; + display: block; + height: 320px; +} + +.formStep { + background-color: #000; +} +.setcolor { + color: #b2c831; +} +.setgrey { + color: #888888; +} +.mat-calendar-table { + height: 400px; +} +.mat-datepicker-content { + overflow-y: auto; +} + +#md-datepicker-0 { + height: 250px; +} + +.center { + margin-left: 5%; +} + +.range { + display: block; + width: 20%; + height: 10%; + margin-bottom: 5px; +} +.setborder { + border: 2px solid; + border-radius: 5px; + width: 8%; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/create-job/streaming/streaming.component.html ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/create-job/streaming/streaming.component.html b/ui/angular/src/app/job/create-job/streaming/streaming.component.html new file mode 100644 index 0000000..db85aa5 --- /dev/null +++ b/ui/angular/src/app/job/create-job/streaming/streaming.component.html @@ -0,0 +1,122 @@ +<!-- +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. +--> +<div class="mask" *ngIf='maskOpen' (click)="close()"></div> +<div class="container-fluid"> + <div class="row"> + <h5 class="over-title margin-bottom-15 job">Create Streaming Job</h5> + </div> + <!--//row--> + <div class="row"> + <!-- <form name="Form" id="form" novalidate> --> + <form name="jobForm" id="form" (ngSubmit)="submit(jobForm)" #jobForm="ngForm" novalidate> + <div class="formStep"> + <label class="stepDesc info">Please setup the job required information</label> + <div class="container-fluid"> + <!-- schema definition list --> + <div class="col-md-12 col-lg-12 col-sm-12"> + <fieldset (window:resize)="onResize($event)"> + <legend> + Required Information + </legend> + <div class="y-scrollable"> + <div class="col-md-12 col-lg-12 col-sm-12"> + <div class="form-group" [ngClass]="{'has-error':jobName.dirty&&jobName.invalid, 'has-success':jobName.valid}"> + <label class="col-md-2 col-lg-2 col-sm-2 control-label"> + Job Name<span class="symbol required"></span>: + </label> + <div class="col-md-10 col-lg-10 col-sm-10 "> + <input type="text" class="form-control" [(ngModel)]="jobname" name="jobName" #jobName="ngModel" placeholder="Please input the job name" required pattern="^[a-zA-Z0-9_-]*$" id="jobName"> + <span class="error text-small block " *ngIf="jobName.dirty && (jobName.errors?.required)"> + Job Name is required</span> + <span class="error text-small block " *ngIf="jobName.dirty && (jobName.errors?.pattern)">Only letter, number, "-" and "_" are allowed</span> + </div> + </div> + </div> + <div class="col-md-12 col-lg-12 col-sm-12"> + <div class="form-group"> + <label for="measureSelector" class="col-md-2 col-lg-2 col-sm-2 control-label"> + Measure Name<span class="symbol required"></span>: + </label> + <div class="col-md-10 col-lg-10 col-sm-10 "> + <select id="measureSelector" class="form-control" ngControl="name" required name="measure.name" [(ngModel)]="measure" (ngModelChange)="onChange($event)"> + <option *ngFor="let row of Measures" value="{{row.name}}">{{row.name}}</option> + </select> + </div> + </div> + </div> + </div> + <div class="setcolor"> + <p> + <i class="fa fa-info-circle"></i> After submitted, please go to " + <a class="bark-link" href="/jobs">Jobs</a>" to check the job status + </p> + </div> + </fieldset> + </div> + <div class="form-group btn-container"> + <button class="btn btn-primary btn-o back-step btn-wide pull-left" (click)="prev()"> + <i class="fa fa-arrow-circle-left"></i> Back + </button> + <toaster-container></toaster-container> + <button type="submit" class="btn btn-primary btn-o next-step btn-wide pull-right" (click)="submit(jobForm)"> + Submit + </button> + </div> + </div> + </div> + <div class="modal fade" id="confirm-job" role="dialog" [ngClass]="{'in': visibleAnimate}" [ngStyle]="{'display': visible ? 'block' : 'none', 'opacity': visibleAnimate ? 1 : 0}" (click)="onContainerClicked($event)"> + <div class="modal-dialog modal-xg modal-lg"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true" (click)="hide()">×</button> + <h4 class="modal-title">Save the job with the below information?</h4> + </div> + <div class="modal-body"> + <div class="container-fluid" id="viewJobContent" style="overflow:auto;"> + <div class="row"> + <h5 class="over-title margin-bottom-15">Basic information</h5> + </div> + <div class="row"> + <div class="col-lg-12 col-md-12 col-sm-12"> + <div id="viewrule-definition" class="viewrule-content"> + <div class="row"> + <label class="col-md-4 col-lg-4 col-sm-4"> + Measure Name: + </label> + <div class="col-md-8 col-lg-8 col-sm-8 " style="color: #fff"> + {{measure}} + <!-- {{Measures}} --> + </div> + </div> + </div> + </div> + </div> + <br/> + </div> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-default" data-dismiss="modal" (click)="hide()">Cancel</button> + <button type="button" id="save" class="btn btn-primary" (click)="save()">Save</button> + </div> + </div> + </div> + </div> + </form> + </div> +</div> http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/create-job/streaming/streaming.component.spec.ts ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/create-job/streaming/streaming.component.spec.ts b/ui/angular/src/app/job/create-job/streaming/streaming.component.spec.ts new file mode 100644 index 0000000..58bc51f --- /dev/null +++ b/ui/angular/src/app/job/create-job/streaming/streaming.component.spec.ts @@ -0,0 +1,43 @@ +/* +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. +*/ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { StreamingComponent } from './streaming.component'; + +describe('StreamingComponent', () => { + let component: StreamingComponent; + let fixture: ComponentFixture<StreamingComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ StreamingComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(StreamingComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/create-job/streaming/streaming.component.ts ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/create-job/streaming/streaming.component.ts b/ui/angular/src/app/job/create-job/streaming/streaming.component.ts new file mode 100644 index 0000000..4162d69 --- /dev/null +++ b/ui/angular/src/app/job/create-job/streaming/streaming.component.ts @@ -0,0 +1,273 @@ +/* +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. +*/ +import { Component, OnInit, AfterViewChecked, ViewChildren } from "@angular/core"; +import { FormControl } from "@angular/forms"; +import { FormsModule } from "@angular/forms"; +import { MaxLengthValidator } from "@angular/forms"; +import { NgControlStatus, Validators } from "@angular/forms"; +import { PatternValidator } from "@angular/forms"; +import { MatDatepickerModule } from "@angular/material"; +import { ServiceService } from "../../../service/service.service"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { ToasterModule, ToasterService, ToasterConfig } from "angular2-toaster"; +import * as $ from "jquery"; +import { HttpParams } from "@angular/common/http"; +import { Router } from "@angular/router"; +import { HttpClient } from "@angular/common/http"; + +@Component({ + selector: 'app-streaming', + templateUrl: './streaming.component.html', + styleUrls: ['./streaming.component.css'] +}) +export class StreamingComponent implements OnInit { + + constructor( + toasterService: ToasterService, + private http: HttpClient, + private router: Router, + public serviceService: ServiceService + ) { + this.toasterService = toasterService; + } + + @ViewChildren("sliderRef") sliderRefs; + + someKeyboard = []; + someKeyboardConfig = []; + config: any; + baseline: string; + cronExp: string; + dropdownList = []; + currentStep = 1; + maskOpen = false; + keyupLabelOn = false; + keydownLabelOn = false; + createResult = ""; + jobname: string; + Measures = []; + measure: string; + measureid: any; + + newJob = { + "cron.expression": "", + "measure.id": "", + "job.name": "", + "cron.time.zone": "", + // "cron.time.zone": "GMT+8:00", + // "predicate.config": { + // "interval": "1m", + // "repeat": 2 + // }, + "data.segments": [ + // { + // "data.connector.index": "source[0]", + // "segment.range": { + // "begin": "", + // "length": "" + // } + // }, + // { + // "data.connector.index": "target[0]", + // "segment.range": { + // "begin": "", + // "length": "" + // } + // } + ] + }; + + beginTime = []; + timeLength = []; + originBegin = []; + originLength = []; + + private toasterService: ToasterService; + + public visible = false; + public visibleAnimate = false; + + public hide(): void { + this.visibleAnimate = false; + setTimeout(() => (this.visible = false), 300); + this.originBegin = []; + this.originLength = []; + $("#save").removeAttr("disabled"); + } + + public onContainerClicked(event: MouseEvent): void { + if ((<HTMLElement>event.target).classList.contains("modal")) { + this.hide(); + } + } + + close() { + this.maskOpen = false; + } + + prev() { + history.back(); + } + + submit(form) { + if (!form.valid) { + this.toasterService.pop("error", "Error!", "Please complete the form!"); + return false; + } + this.measureid = this.getMeasureId(); + let time = new Date().getTimezoneOffset() / 60; + let timezone = "GMT" + time + ":00"; + this.newJob = { + "job.name": this.jobname, + "measure.id": this.measureid, + "cron.expression": this.cronExp, + "cron.time.zone": timezone, + // "cron.time.zone": "GMT+8:00", + // "predicate.config": { + // "interval": "1m", + // "repeat": 2 + // }, + "data.segments": [ + // { + // "data.connector.index": "source[0]", + // "segment.range": { + // "begin": "", + // "length": "" + // } + // }, + // { + // "data.connector.index": "target[0]", + // "segment.range": { + // "begin": "", + // "length": "" + // } + // } + ] + }; + this.visible = true; + setTimeout(() => (this.visibleAnimate = true), 100); + } + + save() { + var addJobs = this.serviceService.config.uri.addJobs; + $("#save").attr("disabled", "true"); + this.http.post(addJobs, this.newJob).subscribe( + data => { + this.createResult = data["results"]; + this.hide(); + this.router.navigate(["/jobs"]); + }, + err => { + let response = JSON.parse(err.error); + if(response.code === '40004'){ + this.toasterService.pop("error", "Error!", "Job name already exists!"); + } else { + this.toasterService.pop("error", "Error!", "Error when creating job"); + } + console.log("Error when creating job"); + } + ); + } + + onResize(event) { + this.resizeWindow(); + } + + resizeWindow() { + var stepSelection = ".formStep"; + $(stepSelection).css({ + height: + window.innerHeight - + $(stepSelection).offset().top - + $("#footerwrap").outerHeight() + }); + $("fieldset").height( + $(stepSelection).height() - + $(stepSelection + ">.stepDesc").height() - + $(".btn-container").height() - + 200 + ); + $(".y-scrollable").css({ + height: $("fieldset").height() + }); + $("#data-asset-pie").css({ + height: $("#data-asset-pie") + .parent() + .width(), + width: $("#data-asset-pie") + .parent() + .width() + }); + } + + getMeasureId() { + for (let index in this.Measures) { + if (this.measure == this.Measures[index].name) { + return this.Measures[index].id; + } + } + } + + onChange(measure) { + this.dropdownList = []; + for (let index in this.Measures) { + var map = this.Measures[index]; + if (measure == map.name) { + var source = map["data.sources"]; + for (let i = 0; i < source.length; i++) { + var details = source[i].connectors; + for (let j = 0; j < details.length; j++) { + if (details[j]["data.unit"] != undefined) { + var table = + details[j].config.database + + "." + + details[j].config["table.name"]; + var size = details[j]["data.unit"]; + var connectorname = details[j]["name"]; + var detail = { + id: i + 1, + name: table, + size: size, + connectorname: connectorname + }; + this.dropdownList.push(detail); + } + } + } + } + } + } + + ngOnInit() { + var allModels = this.serviceService.config.uri.allModels + '?type=griffin'; + this.http.get(allModels).subscribe(data => { + let originData = data; + for(let i in originData){ + if(originData[i]["process.type"] === "STREAING"){ + this.Measures.push(originData[i]); + } + } + }); + } + + ngAfterViewChecked() { + this.resizeWindow(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/job-detail/job-detail.component.css ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/job-detail/job-detail.component.css b/ui/angular/src/app/job/job-detail/job-detail.component.css new file mode 100644 index 0000000..214f8c6 --- /dev/null +++ b/ui/angular/src/app/job/job-detail/job-detail.component.css @@ -0,0 +1,18 @@ +/* +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. +*/ \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/job-detail/job-detail.component.html ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/job-detail/job-detail.component.html b/ui/angular/src/app/job/job-detail/job-detail.component.html new file mode 100644 index 0000000..befa9ff --- /dev/null +++ b/ui/angular/src/app/job/job-detail/job-detail.component.html @@ -0,0 +1,101 @@ +<!-- +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. +--> +<div class="container-fluid" id="viewruleContent"> + <div class="row"> + <h5 class="over-title margin-bottom-15">View Job</h5> + </div> + <div class="row y-scrollable" style="max-height: 350px;"> + <div class="col-sm-6 col-xs-12"> + <div id="viewruleDefinition" class="viewrule-content"> + <div class="row"> + <label class="col-xs-4"> + Job Name: + </label> + <div class="col-xs-8 " style="color: #fff"> + {{jobName}} + </div> + </div> + <div class="row"> + <label class="col-xs-4"> + Measure Name: + </label> + <div class="col-xs-8 " style="color: #fff"> + {{measureName}} + </div> + </div> + <div class="row"> + <label class="col-xs-4"> + Measure Type: + </label> + <div class="col-xs-8 " style="color: #fff"> + {{measureType}} + </div> + </div> + <div class="row"> + <label class="col-xs-4"> + Process Type: + </label> + <div class="col-xs-8 " style="color: #fff"> + {{processType}} + </div> + </div> + <div class="row"> + <label class="col-xs-4"> + Cron Expression: + </label> + <div class="col-xs-8 " style="color: #fff"> + {{cronExp}} + </div> + </div> + <div class="row"> + <label class="col-xs-4"> + Cron Time Zone: + </label> + <div class="col-xs-8 " style="color: #fff"> + {{cronTimeZone}} + </div> + </div> + <div *ngIf="this.rangeConfig.length != 0"> + <div class="row" style="margin-left: 0px" *ngFor="let item of this.tableInfo;let i = index"> + <div class="row"> + <label class="col-xs-12"> + {{item}}: + </label> + </div> + <div class="row"> + <label class="col-xs-4"> + Begin: + </label> + <div class="col-xs-8 " style="color: #fff"> + {{this.rangeConfig[i].begin}} + </div> + </div> + <div class="row"> + <label class="col-xs-4"> + Length: + </label> + <div class="col-xs-8 " style="color: #fff"> + {{this.rangeConfig[i].length}} + </div> + </div> + </div> + </div> + </div> + </div> + </div> http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/job-detail/job-detail.component.spec.ts ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/job-detail/job-detail.component.spec.ts b/ui/angular/src/app/job/job-detail/job-detail.component.spec.ts new file mode 100644 index 0000000..5f2e168 --- /dev/null +++ b/ui/angular/src/app/job/job-detail/job-detail.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { JobDetailComponent } from './job-detail.component'; + +describe('JobDetailComponent', () => { + let component: JobDetailComponent; + let fixture: ComponentFixture<JobDetailComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ JobDetailComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(JobDetailComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/job-detail/job-detail.component.ts ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/job-detail/job-detail.component.ts b/ui/angular/src/app/job/job-detail/job-detail.component.ts new file mode 100644 index 0000000..e5c8a8d --- /dev/null +++ b/ui/angular/src/app/job/job-detail/job-detail.component.ts @@ -0,0 +1,91 @@ +/* +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. +*/ +import { Component, OnInit } from '@angular/core'; +import { ServiceService } from "../../service/service.service"; +import { HttpClient } from "@angular/common/http"; +import { Router, ActivatedRoute, ParamMap } from "@angular/router"; + +@Component({ + selector: 'app-job-detail', + templateUrl: './job-detail.component.html', + providers: [ServiceService], + styleUrls: ['./job-detail.component.css'] +}) +export class JobDetailComponent implements OnInit { + + currentId: string; + jobData: any; + measureData: any; + jobName: string; + cronExp: string; + cronTimeZone: string; + measureName: string; + measureType: string; + processType: string; + rangeConfig = []; + tableInfo = []; + constructor( + private route: ActivatedRoute, + private router: Router, + private http: HttpClient, + public serviceService: ServiceService) { } + + getMeasureById(measureId) { + let url = this.serviceService.config.uri.getModel + "/" + measureId; + this.http.get(url).subscribe( + data => { + this.measureData = data; + this.measureName = this.measureData.name; + this.measureType = this.measureData["dq.type"].toLowerCase(); + this.processType = this.measureData["process.type"].toLowerCase(); + for(let item of this.measureData["data.sources"]){ + let config = item.connectors[0].config; + let tableName = config.database + "." + config["table.name"]; + this.tableInfo.push(tableName); + } + }, + err => { + console.log("error"); + } + ); + } + + ngOnInit() { + this.currentId = this.route.snapshot.paramMap.get("id"); + var getJobById = this.serviceService.config.uri.getJobById + "?jobId=" + this.currentId; + this.http.get(getJobById).subscribe( + data => { + this.jobData = data; + this.jobName = this.jobData["job.name"]; + this.cronExp = this.jobData["cron.expression"]; + this.cronTimeZone = this.jobData["cron.time.zone"]; + let mesureId = this.jobData["measure.id"]; + this.getMeasureById(mesureId); + for(let item of this.jobData["data.segments"]){ + this.rangeConfig.push(item["segment.range"]); + } + }, + err => { + console.log("error"); + // toaster.pop('error', 'Error when geting record', response.message); + } + ); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/c1b90c1a/ui/angular/src/app/job/job.component.css ---------------------------------------------------------------------- diff --git a/ui/angular/src/app/job/job.component.css b/ui/angular/src/app/job/job.component.css index d8e7034..03977d3 100644 --- a/ui/angular/src/app/job/job.component.css +++ b/ui/angular/src/app/job/job.component.css @@ -37,6 +37,10 @@ under the License. border-top: 1px solid transparent; } +.table > tbody > tr > td { + vertical-align: middle; +} + a { color: white; } @@ -55,3 +59,12 @@ a { #pagination .pagination { margin: 20px 0 0 0; } + +.dropdown-menu { + top: 0; + left: 100%; +} + +.btn-group { + margin: 0 0 10px; +}
