You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by cw...@apache.org on 2019/05/25 05:52:35 UTC
[incubator-druid] branch master updated: Web-Console: add go to
editor button to tasks and supervisors view (#7705)
This is an automated email from the ASF dual-hosted git repository.
cwylie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-druid.git
The following commit(s) were added to refs/heads/master by this push:
new 5f50f35 Web-Console: add go to editor button to tasks and supervisors view (#7705)
5f50f35 is described below
commit 5f50f357a47f4564481f4321105f14f971952174
Author: mcbrewster <37...@users.noreply.github.com>
AuthorDate: Fri May 24 22:52:26 2019 -0700
Web-Console: add go to editor button to tasks and supervisors view (#7705)
* add go to editor button to tasks and supervisors
* fix package.json
* remove intent
* quuick fixes
* fixes
* add getsupervisorjson and gettaskjson
* remove space
* remove gotoloaddata
* fixes
* add error handling
* save
* add loader
* remove initspec
* fixup! add loader
* update snapshots
* remove gotoloaddataview form headerbar.spec
---
.../src/components/header-bar/header-bar.spec.tsx | 1 -
.../src/components/header-bar/header-bar.tsx | 1 -
web-console/src/console-application.tsx | 14 +-
.../__snapshots__/load-data-view.spec.tsx.snap | 222 +++------------------
.../views/load-data-view/load-data-view.spec.tsx | 1 -
.../src/views/load-data-view/load-data-view.tsx | 60 ++++--
web-console/src/views/task-view/tasks-view.tsx | 53 +++--
7 files changed, 116 insertions(+), 236 deletions(-)
diff --git a/web-console/src/components/header-bar/header-bar.spec.tsx b/web-console/src/components/header-bar/header-bar.spec.tsx
index 7299103..54b842b 100644
--- a/web-console/src/components/header-bar/header-bar.spec.tsx
+++ b/web-console/src/components/header-bar/header-bar.spec.tsx
@@ -27,7 +27,6 @@ describe('describe header bar', () => {
<HeaderBar
active={'load-data'}
hideLegacy={false}
- goToLoadDataView={() => {}}
/>);
expect(headerBar).toMatchSnapshot();
});
diff --git a/web-console/src/components/header-bar/header-bar.tsx b/web-console/src/components/header-bar/header-bar.tsx
index acc722c..be8bbfe 100644
--- a/web-console/src/components/header-bar/header-bar.tsx
+++ b/web-console/src/components/header-bar/header-bar.tsx
@@ -52,7 +52,6 @@ export type HeaderActiveTab = null | 'load-data' | 'query' | 'datasources' | 'se
export interface HeaderBarProps extends React.Props<any> {
active: HeaderActiveTab;
hideLegacy: boolean;
- goToLoadDataView: () => void;
}
export interface HeaderBarState {
diff --git a/web-console/src/console-application.tsx b/web-console/src/console-application.tsx
index 0ddb5cc..582dc8e 100644
--- a/web-console/src/console-application.tsx
+++ b/web-console/src/console-application.tsx
@@ -100,7 +100,7 @@ export class ConsoleApplication extends React.Component<ConsoleApplicationProps,
});
}
- private initSpec: any | null;
+ private supervisorId: string | null;
private taskId: string | null;
private openDialog: string | null;
private datasource: string | null;
@@ -151,8 +151,8 @@ export class ConsoleApplication extends React.Component<ConsoleApplicationProps,
private resetInitialsWithDelay() {
setTimeout(() => {
- this.initSpec = null;
this.taskId = null;
+ this.supervisorId = null;
this.openDialog = null;
this.datasource = null;
this.onlyUnavailable = null;
@@ -161,8 +161,9 @@ export class ConsoleApplication extends React.Component<ConsoleApplicationProps,
}, 50);
}
- private goToLoadDataView = (initSpec?: any) => {
- if (initSpec) this.initSpec = initSpec;
+ private goToLoadDataView = (supervisorId?: string, taskId?: string ) => {
+ if (taskId) this.taskId = taskId;
+ if (supervisorId) this.supervisorId = supervisorId;
window.location.hash = 'load-data';
this.resetInitialsWithDelay();
}
@@ -197,7 +198,7 @@ export class ConsoleApplication extends React.Component<ConsoleApplicationProps,
const { hideLegacy } = this.props;
return <>
- <HeaderBar active={active} hideLegacy={hideLegacy} goToLoadDataView={this.goToLoadDataView}/>
+ <HeaderBar active={active} hideLegacy={hideLegacy}/>
<div className={classNames('view-container', classType)}>{el}</div>
</>;
}
@@ -208,7 +209,8 @@ export class ConsoleApplication extends React.Component<ConsoleApplicationProps,
}
private wrappedLoadDataView = () => {
- return this.wrapInViewContainer('load-data', <LoadDataView initSpec={this.initSpec} goToTask={this.goToTask}/>, 'narrow-pad');
+
+ return this.wrapInViewContainer('load-data', <LoadDataView initSupervisorId={this.supervisorId} initTaskId={this.taskId} goToTask={this.goToTask}/>, 'narrow-pad');
}
private wrappedSqlView = () => {
diff --git a/web-console/src/views/load-data-view/__snapshots__/load-data-view.spec.tsx.snap b/web-console/src/views/load-data-view/__snapshots__/load-data-view.spec.tsx.snap
index 78b7fb7..dc90d04 100644
--- a/web-console/src/views/load-data-view/__snapshots__/load-data-view.spec.tsx.snap
+++ b/web-console/src/views/load-data-view/__snapshots__/load-data-view.spec.tsx.snap
@@ -2,211 +2,41 @@
exports[`describe load data view load data view snapshot 1`] = `
<div
- className="load-data-view app-view connect"
+ className="load-data-view app-view init"
>
<div
- className="bp3-tabs stage-nav"
+ className="intro"
>
- <div
- className="stage-section"
- key="Connect and parse raw data"
- >
- <div
- className="stage-nav-l1"
- >
- Connect and parse raw data
- </div>
- <Blueprint3.ButtonGroup
- className="stage-nav-l2"
- >
- <Blueprint3.Button
- active={true}
- className="connect"
- icon={false}
- key="connect"
- onClick={[Function]}
- text="Connect"
- />
- <Blueprint3.Button
- active={false}
- className="parser"
- icon={false}
- key="parser"
- onClick={[Function]}
- text="Parse data"
- />
- <Blueprint3.Button
- active={false}
- className="timestamp"
- icon={false}
- key="timestamp"
- onClick={[Function]}
- text="Parse time"
- />
- </Blueprint3.ButtonGroup>
- </div>
- <div
- className="stage-section"
- key="Transform and configure schema"
- >
- <div
- className="stage-nav-l1"
- >
- Transform and configure schema
- </div>
- <Blueprint3.ButtonGroup
- className="stage-nav-l2"
- >
- <Blueprint3.Button
- active={false}
- className="transform"
- icon={false}
- key="transform"
- onClick={[Function]}
- text="Transform"
- />
- <Blueprint3.Button
- active={false}
- className="filter"
- icon={false}
- key="filter"
- onClick={[Function]}
- text="Filter"
- />
- <Blueprint3.Button
- active={false}
- className="schema"
- icon={false}
- key="schema"
- onClick={[Function]}
- text="Configure schema"
- />
- </Blueprint3.ButtonGroup>
- </div>
- <div
- className="stage-section"
- key="Tune parameters"
- >
- <div
- className="stage-nav-l1"
- >
- Tune parameters
- </div>
- <Blueprint3.ButtonGroup
- className="stage-nav-l2"
- >
- <Blueprint3.Button
- active={false}
- className="partition"
- icon={false}
- key="partition"
- onClick={[Function]}
- text="Partition"
- />
- <Blueprint3.Button
- active={false}
- className="tuning"
- icon={false}
- key="tuning"
- onClick={[Function]}
- text="Tune"
- />
- <Blueprint3.Button
- active={false}
- className="publish"
- icon={false}
- key="publish"
- onClick={[Function]}
- text="Publish"
- />
- </Blueprint3.ButtonGroup>
- </div>
- <div
- className="stage-section"
- key="Verify and submit"
- >
- <div
- className="stage-nav-l1"
- >
- Verify and submit
- </div>
- <Blueprint3.ButtonGroup
- className="stage-nav-l2"
- >
- <Blueprint3.Button
- active={false}
- className="json-spec"
- icon="eye-open"
- key="json-spec"
- onClick={[Function]}
- text="Edit JSON spec"
- />
- </Blueprint3.ButtonGroup>
- </div>
- </div>
- <div
- className="main"
- >
- <Loader
- loading={true}
- />
+ Please specify where your raw data is located
</div>
<div
- className="control"
+ className="cards"
>
- <Blueprint3.Callout
- className="intro"
- >
- <p>
- Druid ingests raw data and converts it into a custom,
- <ExternalLink
- href="http://druid.io/docs/latest/design/segments.html"
- >
- indexed
- </ExternalLink>
- format that is optimized for analytic queries.
- </p>
- <p>
- To get started, please specify where your raw data is stored and what data you want to ingest.
- </p>
- <p>
- Click "Preview" to look at the sampled raw data.
- </p>
- </Blueprint3.Callout>
- <Blueprint3.FormGroup
- label="IO Config"
+ <Blueprint3.Card
+ elevation={0}
+ interactive={true}
+ onClick={[Function]}
>
- <JSONInput
- height="300px"
- onChange={[Function]}
- value={
- Object {
- "type": "test",
- }
- }
- />
- </Blueprint3.FormGroup>
- <Blueprint3.Button
- disabled={false}
+ Other (streaming)
+ </Blueprint3.Card>
+ <Blueprint3.Card
+ elevation={0}
+ interactive={true}
onClick={[Function]}
- text="Preview"
- />
+ >
+ Other (batch)
+ </Blueprint3.Card>
</div>
- <div
- className="next-bar"
+ <Blueprint3.Alert
+ canEscapeKeyCancel={false}
+ canOutsideClickCancel={false}
+ confirmButtonText="Close"
+ icon="warning-sign"
+ intent="warning"
+ isOpen={false}
+ onConfirm={[Function]}
>
- <Blueprint3.Button
- className="prev"
- icon="arrow-left"
- onClick={[Function]}
- text="Restart"
- />
- <Blueprint3.Button
- disabled={true}
- intent="primary"
- onClick={[Function]}
- text="Next: Parse data"
- />
- </div>
+ <p />
+ </Blueprint3.Alert>
</div>
`;
diff --git a/web-console/src/views/load-data-view/load-data-view.spec.tsx b/web-console/src/views/load-data-view/load-data-view.spec.tsx
index 28e5382..3a33d6a 100644
--- a/web-console/src/views/load-data-view/load-data-view.spec.tsx
+++ b/web-console/src/views/load-data-view/load-data-view.spec.tsx
@@ -29,7 +29,6 @@ describe('describe load data view', () => {
it('load data view snapshot', () => {
const loadDataView = shallow(
<LoadDataView
- initSpec={{dataSchema: {dataSource: 'test', parser:{parseSpec: {format: 'test', dimensionsSpec: {}, timestampSpec: {column: 'test', format: 'test', missingValue: 'test'}, }}}, ioConfig: { type: 'test'}}}
goToTask={(taskId: string | null) => {}}
/>);
expect(loadDataView).toMatchSnapshot();
diff --git a/web-console/src/views/load-data-view/load-data-view.tsx b/web-console/src/views/load-data-view/load-data-view.tsx
index 773b6fd..fdba39c 100644
--- a/web-console/src/views/load-data-view/load-data-view.tsx
+++ b/web-console/src/views/load-data-view/load-data-view.tsx
@@ -143,8 +143,8 @@ function getTimestampSpec(headerAndRows: HeaderAndRows | null): TimestampSpec {
return timestampSpecs[0] || getEmptyTimestampSpec();
}
-type Stage = 'connect' | 'parser' | 'timestamp' | 'transform' | 'filter' | 'schema' | 'partition' | 'tuning' | 'publish' | 'json-spec';
-const STAGES: Stage[] = ['connect', 'parser', 'timestamp', 'transform', 'filter', 'schema', 'partition', 'tuning', 'publish', 'json-spec'];
+type Stage = 'connect' | 'parser' | 'timestamp' | 'transform' | 'filter' | 'schema' | 'partition' | 'tuning' | 'publish' | 'json-spec' | 'loading';
+const STAGES: Stage[] = ['connect', 'parser', 'timestamp', 'transform', 'filter', 'schema', 'partition', 'tuning', 'publish', 'json-spec', 'loading'];
const SECTIONS: { name: string, stages: Stage[] }[] = [
{ name: 'Connect and parse raw data', stages: ['connect', 'parser', 'timestamp'] },
@@ -163,19 +163,20 @@ const VIEW_TITLE: Record<Stage, string> = {
'partition': 'Partition',
'tuning': 'Tune',
'publish': 'Publish',
- 'json-spec': 'Edit JSON spec'
+ 'json-spec': 'Edit JSON spec',
+ 'loading': 'Loading'
};
export interface LoadDataViewProps extends React.Props<any> {
- initSpec: IngestionSpec | null;
- goToTask: (taskId: string | null, openDialog?: string) => void;
+ initSupervisorId?: string | null;
+ initTaskId?: string | null;
+ goToTask: (taskId: string | null, supervisor?: string) => void;
}
export interface LoadDataViewState {
stage: Stage;
spec: IngestionSpec;
cacheKey: string | undefined;
-
// dialogs / modals
showResetConfirm: boolean;
newRollup: boolean | null;
@@ -225,9 +226,8 @@ export class LoadDataView extends React.Component<LoadDataViewProps, LoadDataVie
constructor(props: LoadDataViewProps) {
super(props);
- let spec = props.initSpec || parseJson(String(localStorageGet(LocalStorageKeys.INGESTION_SPEC)));
+ let spec = parseJson(String(localStorageGet(LocalStorageKeys.INGESTION_SPEC)));
if (!spec || typeof spec !== 'object') spec = {};
-
this.state = {
stage: 'connect',
spec,
@@ -281,9 +281,15 @@ export class LoadDataView extends React.Component<LoadDataViewProps, LoadDataVie
componentDidMount(): void {
this.getOverlordModules();
- this.updateStage('connect');
+ if (this.props.initTaskId) {
+ this.updateStage('loading');
+ this.getTaskJson();
+ } else if (this.props.initSupervisorId) {
+ this.updateStage('loading');
+ this.getSupervisorJson(); } else this.updateStage('connect');
}
+
async getOverlordModules() {
let overlordModules: string[];
try {
@@ -327,8 +333,7 @@ export class LoadDataView extends React.Component<LoadDataViewProps, LoadDataVie
render() {
const { stage, spec } = this.state;
-
- if (!Object.keys(spec).length) {
+ if (!Object.keys(spec).length && !this.props.initSupervisorId && !this.props.initTaskId) {
return <div className={classNames('load-data-view', 'app-view', 'init')}>
{this.renderInitStage()}
</div>;
@@ -350,11 +355,11 @@ export class LoadDataView extends React.Component<LoadDataViewProps, LoadDataVie
{stage === 'publish' && this.renderPublishStage()}
{stage === 'json-spec' && this.renderJsonSpecStage()}
+ {stage === 'loading' && this.renderLoading()}
{this.renderResetConfirm()}
</div>;
}
-
renderStepNav() {
const { stage } = this.state;
@@ -1052,7 +1057,7 @@ export class LoadDataView extends React.Component<LoadDataViewProps, LoadDataVie
},
minWidth: timestamp ? 200 : 100,
resizable: !timestamp
- };
+ };
})}
defaultPageSize={50}
showPagination={false}
@@ -2348,6 +2353,35 @@ export class LoadDataView extends React.Component<LoadDataViewProps, LoadDataVie
}
// ==================================================================
+ private getSupervisorJson = async (): Promise<void> => {
+ try {
+ const resp = await axios.get(`/druid/indexer/v1/supervisor/${this.props.initSupervisorId}`);
+ this.updateSpec(resp.data);
+ this.updateStage('json-spec');
+ } catch (e) {
+ AppToaster.show({
+ message: `Failed to get supervisor spec: ${getDruidErrorMessage(e)}`,
+ intent: Intent.DANGER
+ });
+ }
+ }
+
+ private getTaskJson = async (): Promise<void> => {
+ try {
+ const resp = await axios.get(`/druid/indexer/v1/task/${this.props.initTaskId}`);
+ this.updateSpec(resp.data.payload.spec);
+ this.updateStage('json-spec');
+ } catch (e) {
+ AppToaster.show({
+ message: `Failed to get task spec: ${getDruidErrorMessage(e)}`,
+ intent: Intent.DANGER
+ });
+ }
+ }
+
+ renderLoading() {
+ return <Loader loading/>;
+ }
renderJsonSpecStage() {
const { goToTask } = this.props;
diff --git a/web-console/src/views/task-view/tasks-view.tsx b/web-console/src/views/task-view/tasks-view.tsx
index 4d65dd8..da4f3ae 100644
--- a/web-console/src/views/task-view/tasks-view.tsx
+++ b/web-console/src/views/task-view/tasks-view.tsx
@@ -48,7 +48,7 @@ export interface TasksViewProps extends React.Props<any> {
openDialog: string | null;
goToSql: (initSql: string) => void;
goToMiddleManager: (middleManager: string) => void;
- goToLoadDataView: () => void;
+ goToLoadDataView: (supervisorId?: string, taskId?: string) => void;
noSqlMode: boolean;
}
@@ -286,9 +286,17 @@ ORDER BY "rank" DESC, "created_time" DESC`);
this.taskQueryManager.rerunLastQuery();
}
- private getSupervisorActions(id: string, supervisorSuspended: boolean): BasicAction[] {
- return [
- {
+ private getSupervisorActions(id: string, supervisorSuspended: boolean, type: string): BasicAction[] {
+ const actions: BasicAction[] = [];
+ if (type === 'kafka' || type === 'kinesis') {
+ actions.push(
+ {
+ icon: IconNames.CLOUD_UPLOAD,
+ title: 'Open in data loader',
+ onAction: () => this.props.goToLoadDataView(id)
+ });
+ }
+ actions.push({
icon: IconNames.STEP_BACKWARD,
title: 'Reset',
onAction: () => this.setState({ resetSupervisorId: id })
@@ -304,7 +312,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
intent: Intent.DANGER,
onAction: () => this.setState({ terminateSupervisorId: id })
}
- ];
+ );
+ // @ts-ignore
+ return actions;
}
renderResumeSupervisorAction() {
@@ -413,7 +423,6 @@ ORDER BY "rank" DESC, "created_time" DESC`);
renderSupervisorTable() {
const { supervisors, supervisorsLoading, supervisorsError } = this.state;
const { supervisorTableColumnSelectionHandler } = this;
-
return <>
<ReactTable
data={supervisors || []}
@@ -477,8 +486,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
filterable: false,
Cell: row => {
const id = row.value;
+ const type = row.row.type;
const supervisorSuspended = row.original.spec.suspended;
- const supervisorActions = this.getSupervisorActions(id, supervisorSuspended);
+ const supervisorActions = this.getSupervisorActions(id, supervisorSuspended, type);
const supervisorMenu = basicActionsToMenu(supervisorActions);
return <ActionCell>
@@ -510,18 +520,24 @@ ORDER BY "rank" DESC, "created_time" DESC`);
</>;
}
- // --------------------------------------
-
- private getTaskActions(id: string, status: string): BasicAction[] {
- if (status !== 'RUNNING' && status !== 'WAITING' && status !== 'PENDING') return [];
- return [
- {
+ private getTaskActions(id: string, status: string, type: string): BasicAction[] {
+ const actions: BasicAction[] = [];
+ if (type === 'index' || type === 'index_parallel') {
+ actions.push({
+ icon: IconNames.CLOUD_UPLOAD,
+ title: 'Open in data loader',
+ onAction: () => this.props.goToLoadDataView(undefined, id)
+ });
+ }
+ if (status === 'RUNNING' || status === 'WAITING' || status === 'PENDING') {
+ actions.push({
icon: IconNames.CROSS,
title: 'Kill',
intent: Intent.DANGER,
- onAction: () => this.setState({ killTaskId: id })
- }
- ];
+ onAction: () => this.setState({killTaskId: id})
+ });
+ }
+ return actions;
}
renderKillTaskAction() {
@@ -657,8 +673,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
Cell: row => {
if (row.aggregated) return '';
const id = row.value;
+ const type = row.row.type;
const { status } = row.original;
- const taskActions = this.getTaskActions(id, status);
+ const taskActions = this.getTaskActions(id, status, type);
const taskMenu = basicActionsToMenu(taskActions);
return <ActionCell>
@@ -688,11 +705,11 @@ ORDER BY "rank" DESC, "created_time" DESC`);
</>;
}
+
render() {
const { goToSql, goToLoadDataView, noSqlMode } = this.props;
const { groupTasksBy, supervisorSpecDialogOpen, taskSpecDialogOpen, alertErrorMsg, taskTableActionDialogId, taskTableActionDialogActions, supervisorTableActionDialogId, supervisorTableActionDialogActions } = this.state;
const { supervisorTableColumnSelectionHandler, taskTableColumnSelectionHandler } = this;
-
const submitTaskMenu = <Menu>
<MenuItem
text="Raw JSON task"
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org