You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by fj...@apache.org on 2019/07/01 02:33:23 UTC
[incubator-druid] branch master updated: Web console: Improve data
loader styling, enforce stricter TS types (#8001)
This is an automated email from the ASF dual-hosted git repository.
fjy 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 f16f13c Web console: Improve data loader styling, enforce stricter TS types (#8001)
f16f13c is described below
commit f16f13cf619d52aee97d2f8229fa60ffd9c818f6
Author: Vadim Ogievetsky <va...@gmail.com>
AuthorDate: Sun Jun 30 19:33:16 2019 -0700
Web console: Improve data loader styling, enforce stricter TS types (#8001)
* add assets to auth exclude path
* add frame to tile page
* better empty filter state
* strict TS
* fix segments go to sql
* add unavailable segments
* factor out sugestable input
* fix tests
* update datasources sql
* no depricated extend
* add index spec to tuning configs
* fix scss lint
---
.../druid/cli/RouterJettyServerInitializer.java | 1 +
web-console/package.json | 4 +-
web-console/script/mkcomp | 4 +-
.../bootstrap/react-table-custom-pagination.tsx | 2 +-
web-console/src/bootstrap/react-table-defaults.tsx | 2 +-
.../src/components/action-cell/action-cell.tsx | 2 +-
.../src/components/action-icon/action-icon.tsx | 2 +-
.../__snapshots__/auto-form.spec.tsx.snap | 2 +-
.../src/components/auto-form/auto-form.spec.tsx | 2 +-
web-console/src/components/auto-form/auto-form.tsx | 68 +---
.../components/center-message/center-message.tsx | 2 +-
.../__snapshots__/clearable-input.spec.tsx.snap | 2 +-
.../clearable-input/clearable-input.spec.tsx | 6 +-
.../components/clearable-input/clearable-input.tsx | 2 +-
.../src/components/external-link/external-link.tsx | 2 +-
.../src/components/header-bar/header-bar.tsx | 7 +-
.../src/components/json-collapse/json-collapse.tsx | 3 +-
.../src/components/json-input/json-input.spec.tsx | 2 +-
.../src/components/json-input/json-input.tsx | 2 +-
web-console/src/components/loader/loader.tsx | 2 +-
.../components/refresh-button/refresh-button.tsx | 6 +-
.../components/rule-editor/rule-editor.spec.tsx | 4 +-
.../src/components/rule-editor/rule-editor.tsx | 5 +-
web-console/src/components/show-json/show-json.tsx | 4 +-
web-console/src/components/show-log/show-log.tsx | 4 +-
.../__snapshots__/suggestible-input.spec.tsx.snap | 51 +++
.../suggestible-input/suggestible-input.spec.tsx} | 11 +-
.../suggestible-input/suggestible-input.tsx | 106 ++++++
.../src/components/table-cell/table-cell.tsx | 5 +-
.../table-column-selector.spec.tsx | 2 +-
.../table-column-selector.tsx | 4 +-
.../src/components/timed-button/timed-button.scss | 5 +-
web-console/src/console-application.tsx | 46 +--
.../src/dialogs/about-dialog/about-dialog.spec.tsx | 4 +-
.../src/dialogs/about-dialog/about-dialog.tsx | 2 +-
.../async-action-dialog.spec.tsx | 6 +-
.../async-action-dialog/async-action-dialog.tsx | 15 +-
.../__snapshots__/compaction-dialog.spec.tsx.snap | 2 +-
.../compaction-dialog/compaction-dialog.spec.tsx | 6 +-
.../compaction-dialog/compaction-dialog.tsx | 2 +-
...oordinator-dynamic-config-dialog.spec.tsx.snap} | 12 +-
.../coordinator-dynamic-config-dialog.scss} | 2 +-
.../coordinator-dynamic-config-dialog.spec.tsx} | 6 +-
.../coordinator-dynamic-config-dialog.tsx} | 22 +-
.../dialogs/history-dialog/history-dialog.spec.tsx | 4 +-
.../src/dialogs/history-dialog/history-dialog.tsx | 6 +-
web-console/src/dialogs/index.ts | 4 +-
.../lookup-edit-dialog/lookup-edit-dialog.spec.tsx | 4 +-
.../lookup-edit-dialog/lookup-edit-dialog.tsx | 5 +-
.../overload-dynamic-config-dialog.spec.tsx.snap} | 20 +-
.../overload-dynamic-config-dialog.spec.tsx} | 11 +-
.../overlord-dynamic-config-dialog.scss} | 2 +-
.../overlord-dynamic-config-dialog.tsx} | 18 +-
.../overload-dynamic-config.spec.tsx.snap | 356 ---------------------
.../overload-dynamic-config.spec.tsx | 43 ---
.../query-plan-dialog/query-plan-dialog.spec.tsx | 4 +-
.../query-plan-dialog/query-plan-dialog.tsx | 2 +-
.../__snapshots__/retention-dialog.spec.tsx.snap | 10 -
.../retention-dialog.array.spec.ts | 56 ----
.../retention-dialog/retention-dialog.spec.tsx | 41 ++-
.../dialogs/retention-dialog/retention-dialog.tsx | 18 +-
.../segment-table-action-dialog.spec.tsx.snap | 13 +-
.../segment-table-action-dialog.spec.tsx | 7 +-
.../segment-table-action-dialog.tsx | 2 +-
.../__snapshots__/show-value-dialog.spec.tsx.snap | 2 +-
.../show-value-dialog/show-value-dialog.scss | 9 +-
.../show-value-dialog/show-value-dialog.spec.tsx | 4 +-
.../show-value-dialog/show-value-dialog.tsx | 31 +-
.../dialogs/snitch-dialog/snitch-dialog.spec.tsx | 4 +-
.../src/dialogs/snitch-dialog/snitch-dialog.tsx | 26 +-
.../src/dialogs/spec-dialog/spec-dialog.spec.tsx | 8 +-
.../src/dialogs/spec-dialog/spec-dialog.tsx | 5 +-
.../supervisor-table-action-dialog.spec.tsx | 4 +-
.../table-action-dialog/table-action-dialog.scss | 2 +-
.../table-action-dialog.spec.tsx | 4 +-
.../table-action-dialog/table-action-dialog.tsx | 4 +-
.../task-table-action-dialog.spec.tsx | 4 +-
web-console/src/utils/general.tsx | 11 +-
web-console/src/utils/ingestion-spec.tsx | 49 ++-
.../src/utils/local-storage-backed-array.tsx | 15 +-
web-console/src/utils/local-storage-keys.tsx | 1 +
web-console/src/utils/query-manager.tsx | 7 +-
.../__snapshots__/datasource-view.spec.tsx.snap | 9 +
.../views/datasource-view/datasource-view.spec.tsx | 6 +-
.../src/views/datasource-view/datasource-view.tsx | 106 +++---
web-console/src/views/home-view/home-view.tsx | 122 +++----
.../__snapshots__/load-data-view.spec.tsx.snap | 2 +-
.../load-data-view/filter-table/filter-table.tsx | 8 +-
.../src/views/load-data-view/load-data-view.scss | 9 +-
.../views/load-data-view/load-data-view.spec.tsx | 2 +-
.../src/views/load-data-view/load-data-view.tsx | 59 +++-
.../parse-data-table/parse-data-table.tsx | 2 +-
.../parse-time-table/parse-time-table.scss | 1 +
.../parse-time-table/parse-time-table.tsx | 2 +-
.../load-data-view/schema-table/schema-table.tsx | 12 +-
.../transform-table/transform-table.tsx | 6 +-
.../__snapshots__/lookups-view.spec.tsx.snap | 15 +-
.../src/views/lookups-view/lookups-view.tsx | 46 ++-
.../views/query-view/column-tree/column-tree.tsx | 8 +-
.../query-extra-info/query-extra-info.tsx | 2 +-
.../views/query-view/query-input/query-input.tsx | 8 +-
.../views/query-view/query-output/query-output.tsx | 2 +-
web-console/src/views/query-view/query-view.tsx | 18 +-
.../src/views/query-view/run-button/run-button.tsx | 2 +-
.../__snapshots__/segments-view.spec.tsx.snap | 2 +-
.../src/views/segments-view/segments-view.spec.tsx | 2 +-
.../src/views/segments-view/segments-view.tsx | 45 +--
.../src/views/servers-view/servers-view.spec.tsx | 4 +-
.../src/views/servers-view/servers-view.tsx | 108 +++----
.../src/views/task-view/tasks-view.spec.tsx | 4 +-
web-console/src/views/task-view/tasks-view.tsx | 115 ++++---
web-console/tsconfig.json | 7 +-
web-console/tslint.json | 1 -
web-console/unified-console.html | 2 +-
web-console/webpack.config.js | 20 +-
115 files changed, 876 insertions(+), 1148 deletions(-)
diff --git a/services/src/main/java/org/apache/druid/cli/RouterJettyServerInitializer.java b/services/src/main/java/org/apache/druid/cli/RouterJettyServerInitializer.java
index b206e4b..a336ae6 100644
--- a/services/src/main/java/org/apache/druid/cli/RouterJettyServerInitializer.java
+++ b/services/src/main/java/org/apache/druid/cli/RouterJettyServerInitializer.java
@@ -65,6 +65,7 @@ public class RouterJettyServerInitializer implements JettyServerInitializer
protected static final List<String> UNSECURED_PATHS_FOR_UI = ImmutableList.of(
"/",
"/coordinator-console/*",
+ "/assets/*",
"/public/*",
"/old-console/*",
"/pages/*",
diff --git a/web-console/package.json b/web-console/package.json
index a4369d6..edb85f2 100644
--- a/web-console/package.json
+++ b/web-console/package.json
@@ -1,6 +1,6 @@
{
"name": "web-console",
- "version": "0.15.0",
+ "version": "0.16.0",
"description": "A web console for Apache Druid",
"author": "Imply Data Inc.",
"license": "Apache-2.0",
@@ -37,7 +37,7 @@
"compile": "./script/build",
"pretest": "./script/build",
"run": "./script/run",
- "test": "npm run tslint && jest --silent 2>&1",
+ "test": "npm run tslint && npm run stylelint && jest --silent 2>&1",
"coverage": "jest --coverage",
"update-snapshots": "jest -u",
"tslint": "./node_modules/.bin/tslint -c tslint.json --project tsconfig.json --formatters-dir ./node_modules/awesome-code-style/formatter 'src/**/*.ts?(x)'",
diff --git a/web-console/script/mkcomp b/web-console/script/mkcomp
index 5c60a80..5634959 100755
--- a/web-console/script/mkcomp
+++ b/web-console/script/mkcomp
@@ -85,7 +85,7 @@ import React from 'react';
import './${name}.scss';
-export interface ${camelName}Props extends React.Props<any> {
+export interface ${camelName}Props {
}
export interface ${camelName}State {
@@ -100,7 +100,7 @@ export class ${camelName} extends React.PureComponent<${camelName}Props, ${camel
render() {
return <div className="${name}">
-
+ Stuff...
</div>;
}
}
diff --git a/web-console/src/bootstrap/react-table-custom-pagination.tsx b/web-console/src/bootstrap/react-table-custom-pagination.tsx
index 5c53b8e..5be343df 100644
--- a/web-console/src/bootstrap/react-table-custom-pagination.tsx
+++ b/web-console/src/bootstrap/react-table-custom-pagination.tsx
@@ -21,7 +21,7 @@ import React from 'react';
import './react-table-custom-pagination.scss';
-interface ReactTableCustomPaginationProps extends React.Props<any> {
+interface ReactTableCustomPaginationProps {
pages: number;
page: number;
showPageSizeOptions: boolean;
diff --git a/web-console/src/bootstrap/react-table-defaults.tsx b/web-console/src/bootstrap/react-table-defaults.tsx
index 15c09cd..f0d2b2f 100644
--- a/web-console/src/bootstrap/react-table-defaults.tsx
+++ b/web-console/src/bootstrap/react-table-defaults.tsx
@@ -38,7 +38,7 @@ class NoData extends React.PureComponent {
Object.assign(ReactTableDefaults, {
className: '-striped -highlight',
- defaultFilterMethod: (filter: Filter, row: any, column: any) => {
+ defaultFilterMethod: (filter: Filter, row: any) => {
const id = filter.pivotId || filter.id;
return booleanCustomTableFilter(filter, row[id]);
},
diff --git a/web-console/src/components/action-cell/action-cell.tsx b/web-console/src/components/action-cell/action-cell.tsx
index 0bd7e79..32c2fa2 100644
--- a/web-console/src/components/action-cell/action-cell.tsx
+++ b/web-console/src/components/action-cell/action-cell.tsx
@@ -25,7 +25,7 @@ import { ActionIcon } from '../action-icon/action-icon';
import './action-cell.scss';
-export interface ActionCellProps extends React.Props<any> {
+export interface ActionCellProps {
onDetail?: () => void;
actions?: BasicAction[];
}
diff --git a/web-console/src/components/action-icon/action-icon.tsx b/web-console/src/components/action-icon/action-icon.tsx
index 36b4b4e..c3c7da2 100644
--- a/web-console/src/components/action-icon/action-icon.tsx
+++ b/web-console/src/components/action-icon/action-icon.tsx
@@ -22,7 +22,7 @@ import React from 'react';
import './action-icon.scss';
-export interface ActionIconProps extends React.Props<any> {
+export interface ActionIconProps {
className?: string;
icon: IconName;
onClick?: () => void;
diff --git a/web-console/src/components/auto-form/__snapshots__/auto-form.spec.tsx.snap b/web-console/src/components/auto-form/__snapshots__/auto-form.spec.tsx.snap
index df8004a..b95c63c 100644
--- a/web-console/src/components/auto-form/__snapshots__/auto-form.spec.tsx.snap
+++ b/web-console/src/components/auto-form/__snapshots__/auto-form.spec.tsx.snap
@@ -190,7 +190,7 @@ exports[`auto-form snapshot matches snapshot 1`] = `
class="bp3-form-content"
>
<div
- class="bp3-input-group"
+ class="bp3-input-group suggestible-input"
>
<input
class="bp3-input"
diff --git a/web-console/src/components/auto-form/auto-form.spec.tsx b/web-console/src/components/auto-form/auto-form.spec.tsx
index 87ad211..c0b4a96 100644
--- a/web-console/src/components/auto-form/auto-form.spec.tsx
+++ b/web-console/src/components/auto-form/auto-form.spec.tsx
@@ -35,7 +35,7 @@ describe('auto-form snapshot', () => {
{ name: 'testSeven', type: 'json' },
]}
model={String}
- onChange={(newModel: Record<string, any>) => {}}
+ onChange={() => {}}
/>
);
const { container } = render(autoForm);
diff --git a/web-console/src/components/auto-form/auto-form.tsx b/web-console/src/components/auto-form/auto-form.tsx
index 85ee1de..e7db357 100644
--- a/web-console/src/components/auto-form/auto-form.tsx
+++ b/web-console/src/components/auto-form/auto-form.tsx
@@ -16,32 +16,17 @@
* limitations under the License.
*/
-import {
- Button,
- FormGroup,
- HTMLSelect,
- Icon,
- InputGroup,
- Menu,
- MenuItem,
- NumericInput,
- Popover,
- Position,
-} from '@blueprintjs/core';
+import { FormGroup, HTMLSelect, Icon, NumericInput, Popover } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import React from 'react';
import { deepDelete, deepGet, deepSet } from '../../utils/object-change';
import { ArrayInput } from '../array-input/array-input';
import { JSONInput } from '../json-input/json-input';
+import { SuggestibleInput, SuggestionGroup } from '../suggestible-input/suggestible-input';
import './auto-form.scss';
-export interface SuggestionGroup {
- group: string;
- suggestions: string[];
-}
-
export interface Field<T> {
name: string;
label?: string;
@@ -55,7 +40,7 @@ export interface Field<T> {
min?: number;
}
-export interface AutoFormProps<T> extends React.Props<any> {
+export interface AutoFormProps<T> {
fields: Field<T>[];
model: T | null;
onChange: (newModel: T) => void;
@@ -64,13 +49,13 @@ export interface AutoFormProps<T> extends React.Props<any> {
large?: boolean;
}
-export interface AutoFormState<T> {
+export interface AutoFormState {
jsonInputsValidity: any;
}
export class AutoForm<T extends Record<string, any>> extends React.PureComponent<
AutoFormProps<T>,
- AutoFormState<T>
+ AutoFormState
> {
static makeLabelName(label: string): string {
let newLabel = label
@@ -159,42 +144,11 @@ export class AutoForm<T extends Record<string, any>> extends React.PureComponent
private renderStringInput(field: Field<T>, sanitize?: (str: string) => string): JSX.Element {
const { model, large } = this.props;
- const suggestionsMenu = field.suggestions ? (
- <Menu>
- {field.suggestions.map(suggestion => {
- if (typeof suggestion === 'string') {
- return (
- <MenuItem
- key={suggestion}
- text={suggestion}
- onClick={() => this.fieldChange(field, suggestion)}
- />
- );
- } else {
- return (
- <MenuItem key={suggestion.group} text={suggestion.group}>
- {suggestion.suggestions.map(suggestion => (
- <MenuItem
- key={suggestion}
- text={suggestion}
- onClick={() => this.fieldChange(field, suggestion)}
- />
- ))}
- </MenuItem>
- );
- }
- })}
- </Menu>
- ) : (
- undefined
- );
-
const modalValue = deepGet(model as any, field.name);
return (
- <InputGroup
+ <SuggestibleInput
value={modalValue != null ? modalValue : field.defaultValue || ''}
- onChange={(e: any) => {
- let v = e.target.value;
+ onValueChange={v => {
if (sanitize) v = sanitize(v);
this.fieldChange(field, v);
}}
@@ -202,13 +156,7 @@ export class AutoForm<T extends Record<string, any>> extends React.PureComponent
if (modalValue === '') this.fieldChange(field, undefined);
}}
placeholder={field.placeholder}
- rightElement={
- suggestionsMenu && (
- <Popover content={suggestionsMenu} position={Position.BOTTOM_RIGHT} autoFocus={false}>
- <Button icon={IconNames.CARET_DOWN} minimal />
- </Popover>
- )
- }
+ suggestions={field.suggestions}
large={large}
disabled={field.disabled}
/>
diff --git a/web-console/src/components/center-message/center-message.tsx b/web-console/src/components/center-message/center-message.tsx
index c961cf2..b84e0cf 100644
--- a/web-console/src/components/center-message/center-message.tsx
+++ b/web-console/src/components/center-message/center-message.tsx
@@ -20,7 +20,7 @@ import React from 'react';
import './center-message.scss';
-export interface CenterMessageProps extends React.Props<any> {}
+export interface CenterMessageProps {}
export class CenterMessage extends React.PureComponent<CenterMessageProps> {
render() {
diff --git a/web-console/src/components/clearable-input/__snapshots__/clearable-input.spec.tsx.snap b/web-console/src/components/clearable-input/__snapshots__/clearable-input.spec.tsx.snap
index 8f6e315..8e59dc6 100644
--- a/web-console/src/components/clearable-input/__snapshots__/clearable-input.spec.tsx.snap
+++ b/web-console/src/components/clearable-input/__snapshots__/clearable-input.spec.tsx.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`decribe clearable-input matches snapshot 1`] = `
+exports[`clearable-input matches snapshot 1`] = `
<div
class="bp3-input-group clearable-input testClassName"
>
diff --git a/web-console/src/components/clearable-input/clearable-input.spec.tsx b/web-console/src/components/clearable-input/clearable-input.spec.tsx
index 289d2ee..ef44701 100644
--- a/web-console/src/components/clearable-input/clearable-input.spec.tsx
+++ b/web-console/src/components/clearable-input/clearable-input.spec.tsx
@@ -21,16 +21,16 @@ import { render } from 'react-testing-library';
import { ClearableInput } from './clearable-input';
-describe('decribe clearable-input', () => {
+describe('clearable-input', () => {
it('matches snapshot', () => {
const centerMessage = (
<ClearableInput
className={'testClassName'}
value={'testValue'}
placeholder={'testPlaceholder'}
- onChange={(value: string) => null}
+ onChange={() => null}
>
- ;<div>Hello World</div>
+ <div>Hello World</div>
</ClearableInput>
);
diff --git a/web-console/src/components/clearable-input/clearable-input.tsx b/web-console/src/components/clearable-input/clearable-input.tsx
index 0cbe36c..1376759 100644
--- a/web-console/src/components/clearable-input/clearable-input.tsx
+++ b/web-console/src/components/clearable-input/clearable-input.tsx
@@ -21,7 +21,7 @@ import { IconNames } from '@blueprintjs/icons';
import classNames from 'classnames';
import React from 'react';
-export interface ClearableInputProps extends React.Props<any> {
+export interface ClearableInputProps {
className?: string;
value: string;
onChange: (value: string) => void;
diff --git a/web-console/src/components/external-link/external-link.tsx b/web-console/src/components/external-link/external-link.tsx
index 3b84e4e..f4ca4b0 100644
--- a/web-console/src/components/external-link/external-link.tsx
+++ b/web-console/src/components/external-link/external-link.tsx
@@ -18,7 +18,7 @@
import React from 'react';
-export interface ExternalLinkProps extends React.Props<any> {
+export interface ExternalLinkProps {
href: string;
}
diff --git a/web-console/src/components/header-bar/header-bar.tsx b/web-console/src/components/header-bar/header-bar.tsx
index 054fc4b..5d4bc15 100644
--- a/web-console/src/components/header-bar/header-bar.tsx
+++ b/web-console/src/components/header-bar/header-bar.tsx
@@ -22,7 +22,6 @@ import {
Button,
Intent,
Menu,
- MenuDivider,
MenuItem,
Navbar,
NavbarDivider,
@@ -34,8 +33,8 @@ import { IconNames } from '@blueprintjs/icons';
import React from 'react';
import { AboutDialog } from '../../dialogs/about-dialog/about-dialog';
-import { CoordinatorDynamicConfigDialog } from '../../dialogs/coordinator-dynamic-config/coordinator-dynamic-config';
-import { OverlordDynamicConfigDialog } from '../../dialogs/overlord-dynamic-config/overlord-dynamic-config';
+import { CoordinatorDynamicConfigDialog } from '../../dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog';
+import { OverlordDynamicConfigDialog } from '../../dialogs/overlord-dynamic-config-dialog/overlord-dynamic-config-dialog';
import {
DRUID_DOCS,
DRUID_GITHUB,
@@ -56,7 +55,7 @@ export type HeaderActiveTab =
| 'servers'
| 'lookups';
-export interface HeaderBarProps extends React.Props<any> {
+export interface HeaderBarProps {
active: HeaderActiveTab;
hideLegacy: boolean;
}
diff --git a/web-console/src/components/json-collapse/json-collapse.tsx b/web-console/src/components/json-collapse/json-collapse.tsx
index afc7a09..0441d78 100644
--- a/web-console/src/components/json-collapse/json-collapse.tsx
+++ b/web-console/src/components/json-collapse/json-collapse.tsx
@@ -17,10 +17,9 @@
*/
import { Button, Collapse, TextArea } from '@blueprintjs/core';
-import classNames from 'classnames';
import React from 'react';
-interface JSONCollapseProps extends React.Props<any> {
+interface JSONCollapseProps {
stringValue: string;
buttonText: string;
}
diff --git a/web-console/src/components/json-input/json-input.spec.tsx b/web-console/src/components/json-input/json-input.spec.tsx
index 4fd1266..c723186 100644
--- a/web-console/src/components/json-input/json-input.spec.tsx
+++ b/web-console/src/components/json-input/json-input.spec.tsx
@@ -23,7 +23,7 @@ import { JSONInput } from './json-input';
describe('json input', () => {
it('matches snapshot', () => {
- const jsonCollapse = <JSONInput onChange={(newJSONValue: any) => {}} value={'test'} />;
+ const jsonCollapse = <JSONInput onChange={() => {}} value={'test'} />;
const { container } = render(jsonCollapse);
expect(container.firstChild).toMatchSnapshot();
});
diff --git a/web-console/src/components/json-input/json-input.tsx b/web-console/src/components/json-input/json-input.tsx
index 975b52f..0aff544 100644
--- a/web-console/src/components/json-input/json-input.tsx
+++ b/web-console/src/components/json-input/json-input.tsx
@@ -21,7 +21,7 @@ import AceEditor from 'react-ace';
import { parseStringToJSON, stringifyJSON, validJson } from '../../utils';
-interface JSONInputProps extends React.Props<any> {
+interface JSONInputProps {
onChange: (newJSONValue: any) => void;
value: any;
updateInputValidity?: (valueValid: boolean) => void;
diff --git a/web-console/src/components/loader/loader.tsx b/web-console/src/components/loader/loader.tsx
index c6dc529..69d74be 100644
--- a/web-console/src/components/loader/loader.tsx
+++ b/web-console/src/components/loader/loader.tsx
@@ -20,7 +20,7 @@ import React from 'react';
import './loader.scss';
-export interface LoaderProps extends React.Props<any> {
+export interface LoaderProps {
loadingText?: string;
loading?: boolean; // This is needed so that this component can be used as a LoadingComponent in react table
}
diff --git a/web-console/src/components/refresh-button/refresh-button.tsx b/web-console/src/components/refresh-button/refresh-button.tsx
index 71905b6..ab945e3 100644
--- a/web-console/src/components/refresh-button/refresh-button.tsx
+++ b/web-console/src/components/refresh-button/refresh-button.tsx
@@ -21,7 +21,7 @@ import React from 'react';
import { LocalStorageKeys } from '../../utils';
import { TimedButton } from '../timed-button/timed-button';
-export interface RefreshButtonProps extends React.Props<any> {
+export interface RefreshButtonProps {
onRefresh: (auto: boolean) => void;
localStorageKey?: LocalStorageKeys;
}
@@ -45,10 +45,10 @@ export class RefreshButton extends React.PureComponent<RefreshButtonProps> {
return (
<TimedButton
defaultValue={30000}
- label={'Auto refresh every:'}
+ label="Auto refresh every:"
intervals={intervals}
icon={IconNames.REFRESH}
- text={'Refresh'}
+ text="Refresh"
onRefresh={onRefresh}
localStorageKey={localStorageKey}
/>
diff --git a/web-console/src/components/rule-editor/rule-editor.spec.tsx b/web-console/src/components/rule-editor/rule-editor.spec.tsx
index 5f17209..d1eeefd 100644
--- a/web-console/src/components/rule-editor/rule-editor.spec.tsx
+++ b/web-console/src/components/rule-editor/rule-editor.spec.tsx
@@ -19,7 +19,7 @@
import React from 'react';
import { render } from 'react-testing-library';
-import { Rule, RuleEditor } from './rule-editor';
+import { RuleEditor } from './rule-editor';
describe('rule editor', () => {
it('matches snapshot', () => {
@@ -27,7 +27,7 @@ describe('rule editor', () => {
<RuleEditor
rule={{ type: 'loadForever' }}
tiers={['test', 'test', 'test']}
- onChange={(newRule: Rule) => null}
+ onChange={() => null}
onDelete={() => null}
moveUp={null}
moveDown={null}
diff --git a/web-console/src/components/rule-editor/rule-editor.tsx b/web-console/src/components/rule-editor/rule-editor.tsx
index 9cc79ca..f46a42f 100644
--- a/web-console/src/components/rule-editor/rule-editor.tsx
+++ b/web-console/src/components/rule-editor/rule-editor.tsx
@@ -28,7 +28,6 @@ import {
TagInput,
} from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
-import axios from 'axios';
import React from 'react';
import './rule-editor.scss';
@@ -53,7 +52,7 @@ export interface Rule {
export type LoadType = 'load' | 'drop' | 'broadcast';
export type TimeType = 'Forever' | 'ByInterval' | 'ByPeriod';
-export interface RuleEditorProps extends React.Props<any> {
+export interface RuleEditorProps {
rule: Rule;
tiers: any[];
onChange: (newRule: Rule) => void;
@@ -241,7 +240,7 @@ export class RuleEditor extends React.PureComponent<RuleEditorProps, RuleEditorS
}
render() {
- const { tiers, onChange, rule, onDelete, moveUp, moveDown } = this.props;
+ const { onChange, rule, onDelete, moveUp, moveDown } = this.props;
const { isOpen } = this.state;
if (!rule) return null;
diff --git a/web-console/src/components/show-json/show-json.tsx b/web-console/src/components/show-json/show-json.tsx
index 27b8fc3..7b8e2cc 100644
--- a/web-console/src/components/show-json/show-json.tsx
+++ b/web-console/src/components/show-json/show-json.tsx
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-import { Button, ButtonGroup, InputGroup, Intent, TextArea } from '@blueprintjs/core';
+import { Button, ButtonGroup, Intent, TextArea } from '@blueprintjs/core';
import axios from 'axios';
import copy from 'copy-to-clipboard';
import React from 'react';
@@ -27,7 +27,7 @@ import { downloadFile } from '../../utils';
import './show-json.scss';
-export interface ShowJsonProps extends React.Props<any> {
+export interface ShowJsonProps {
endpoint: string;
transform?: (x: any) => any;
downloadFilename?: string;
diff --git a/web-console/src/components/show-log/show-log.tsx b/web-console/src/components/show-log/show-log.tsx
index 5d6492b..8efd2b0 100644
--- a/web-console/src/components/show-log/show-log.tsx
+++ b/web-console/src/components/show-log/show-log.tsx
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-import { Button, ButtonGroup, Checkbox, InputGroup, Intent, TextArea } from '@blueprintjs/core';
+import { Button, ButtonGroup, Checkbox, Intent } from '@blueprintjs/core';
import axios from 'axios';
import copy from 'copy-to-clipboard';
import React from 'react';
@@ -35,7 +35,7 @@ function removeFirstPartialLine(log: string): string {
return lines.join('\n');
}
-export interface ShowLogProps extends React.Props<any> {
+export interface ShowLogProps {
endpoint: string;
downloadFilename?: string;
tailOffset?: number;
diff --git a/web-console/src/components/suggestible-input/__snapshots__/suggestible-input.spec.tsx.snap b/web-console/src/components/suggestible-input/__snapshots__/suggestible-input.spec.tsx.snap
new file mode 100644
index 0000000..9399672
--- /dev/null
+++ b/web-console/src/components/suggestible-input/__snapshots__/suggestible-input.spec.tsx.snap
@@ -0,0 +1,51 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`suggestible input matches snapshot 1`] = `
+<div
+ class="bp3-input-group suggestible-input"
+>
+ <input
+ class="bp3-input"
+ style="padding-right: 0px;"
+ suggestions="a,b,c"
+ type="text"
+ value=""
+ />
+ <span
+ class="bp3-input-action"
+ >
+ <span
+ class="bp3-popover-wrapper"
+ >
+ <span
+ class="bp3-popover-target"
+ >
+ <button
+ class="bp3-button bp3-minimal"
+ type="button"
+ >
+ <span
+ class="bp3-icon bp3-icon-caret-down"
+ icon="caret-down"
+ >
+ <svg
+ data-icon="caret-down"
+ height="16"
+ viewBox="0 0 16 16"
+ width="16"
+ >
+ <desc>
+ caret-down
+ </desc>
+ <path
+ d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 0 0-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
+ fill-rule="evenodd"
+ />
+ </svg>
+ </span>
+ </button>
+ </span>
+ </span>
+ </span>
+</div>
+`;
diff --git a/web-console/src/dialogs/spec-dialog/spec-dialog.spec.tsx b/web-console/src/components/suggestible-input/suggestible-input.spec.tsx
similarity index 78%
copy from web-console/src/dialogs/spec-dialog/spec-dialog.spec.tsx
copy to web-console/src/components/suggestible-input/suggestible-input.spec.tsx
index 35d7214..ddc48c1 100644
--- a/web-console/src/dialogs/spec-dialog/spec-dialog.spec.tsx
+++ b/web-console/src/components/suggestible-input/suggestible-input.spec.tsx
@@ -19,14 +19,15 @@
import React from 'react';
import { render } from 'react-testing-library';
-import { SpecDialog } from './spec-dialog';
+import { SuggestibleInput } from './suggestible-input';
-describe('spec dialog', () => {
+describe('suggestible input', () => {
it('matches snapshot', () => {
- const specDialog = (
- <SpecDialog onSubmit={(spec: JSON) => null} onClose={() => null} title={'test'} />
+ const suggestibleInput = (
+ <SuggestibleInput onValueChange={() => {}} suggestions={['a', 'b', 'c']} />
);
- const { container } = render(specDialog, { container: document.body });
+
+ const { container } = render(suggestibleInput);
expect(container.firstChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/components/suggestible-input/suggestible-input.tsx b/web-console/src/components/suggestible-input/suggestible-input.tsx
new file mode 100644
index 0000000..47eb174
--- /dev/null
+++ b/web-console/src/components/suggestible-input/suggestible-input.tsx
@@ -0,0 +1,106 @@
+/*
+ * 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 {
+ Button,
+ HTMLInputProps,
+ InputGroup,
+ Menu,
+ MenuItem,
+ Popover,
+ Position,
+} from '@blueprintjs/core';
+import { IconNames } from '@blueprintjs/icons';
+import classNames from 'classnames';
+import React from 'react';
+
+export interface SuggestionGroup {
+ group: string;
+ suggestions: string[];
+}
+
+export interface SuggestibleInputProps extends HTMLInputProps {
+ onValueChange: (newValue: string) => void;
+ suggestions?: (string | SuggestionGroup)[];
+ large?: boolean;
+}
+
+export class SuggestibleInput extends React.PureComponent<SuggestibleInputProps> {
+ constructor(props: SuggestibleInputProps, context: any) {
+ super(props, context);
+ // this.state = {};
+ }
+
+ renderSuggestionsMenu() {
+ const { suggestions, onValueChange } = this.props;
+ if (!suggestions) return undefined;
+
+ return (
+ <Menu>
+ {suggestions.map(suggestion => {
+ if (typeof suggestion === 'string') {
+ return (
+ <MenuItem
+ key={suggestion}
+ text={suggestion}
+ onClick={() => onValueChange(suggestion)}
+ />
+ );
+ } else {
+ return (
+ <MenuItem key={suggestion.group} text={suggestion.group}>
+ {suggestion.suggestions.map(suggestion => (
+ <MenuItem
+ key={suggestion}
+ text={suggestion}
+ onClick={() => onValueChange(suggestion)}
+ />
+ ))}
+ </MenuItem>
+ );
+ }
+ })}
+ </Menu>
+ );
+ }
+
+ render() {
+ const { className, value, defaultValue, onValueChange, large, ...rest } = this.props;
+ const suggestionsMenu = this.renderSuggestionsMenu();
+
+ return (
+ <InputGroup
+ className={classNames('suggestible-input', className)}
+ value={value as string}
+ defaultValue={defaultValue as string}
+ onChange={(e: any) => {
+ onValueChange(e.target.value);
+ }}
+ rightElement={
+ suggestionsMenu && (
+ <Popover content={suggestionsMenu} position={Position.BOTTOM_RIGHT} autoFocus={false}>
+ <Button icon={IconNames.CARET_DOWN} minimal />
+ </Popover>
+ )
+ }
+ large={large}
+ {...rest}
+ />
+ );
+ }
+}
diff --git a/web-console/src/components/table-cell/table-cell.tsx b/web-console/src/components/table-cell/table-cell.tsx
index 92cfa39..e4164fd 100644
--- a/web-console/src/components/table-cell/table-cell.tsx
+++ b/web-console/src/components/table-cell/table-cell.tsx
@@ -16,17 +16,14 @@
* limitations under the License.
*/
-import { Intent } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
-import copy from 'copy-to-clipboard';
import React from 'react';
-import { AppToaster } from '../../singletons/toaster';
import { ActionIcon } from '../action-icon/action-icon';
import './table-cell.scss';
-export interface NullTableCellProps extends React.Props<any> {
+export interface NullTableCellProps {
value?: any;
timestamp?: boolean;
unparseable?: boolean;
diff --git a/web-console/src/components/table-column-selector/table-column-selector.spec.tsx b/web-console/src/components/table-column-selector/table-column-selector.spec.tsx
index 1f1d7dd..2cc3c74 100644
--- a/web-console/src/components/table-column-selector/table-column-selector.spec.tsx
+++ b/web-console/src/components/table-column-selector/table-column-selector.spec.tsx
@@ -26,7 +26,7 @@ describe('table column', () => {
const tableColumn = (
<TableColumnSelector
columns={['a', 'b', 'c']}
- onChange={(column: string) => {}}
+ onChange={() => {}}
tableColumnsHidden={['a', 'b', 'c']}
/>
);
diff --git a/web-console/src/components/table-column-selector/table-column-selector.tsx b/web-console/src/components/table-column-selector/table-column-selector.tsx
index 46660c2..2876404 100644
--- a/web-console/src/components/table-column-selector/table-column-selector.tsx
+++ b/web-console/src/components/table-column-selector/table-column-selector.tsx
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-import { Button, Checkbox, FormGroup, Menu, Popover, Position } from '@blueprintjs/core';
+import { Button, Menu, Popover, Position } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import React from 'react';
@@ -24,7 +24,7 @@ import { MenuCheckbox } from '../menu-checkbox/menu-checkbox';
import './table-column-selector.scss';
-interface TableColumnSelectorProps extends React.Props<any> {
+interface TableColumnSelectorProps {
columns: string[];
onChange: (column: string) => void;
tableColumnsHidden: string[];
diff --git a/web-console/src/components/timed-button/timed-button.scss b/web-console/src/components/timed-button/timed-button.scss
index 2879880..f4d7700 100644
--- a/web-console/src/components/timed-button/timed-button.scss
+++ b/web-console/src/components/timed-button/timed-button.scss
@@ -17,6 +17,5 @@
*/
.timed-button {
- padding: 10px 10px 5px 10px;
- }
-
+ padding: 10px 10px 5px 10px;
+}
diff --git a/web-console/src/console-application.tsx b/web-console/src/console-application.tsx
index 85e0f44..8485a07 100644
--- a/web-console/src/console-application.tsx
+++ b/web-console/src/console-application.tsx
@@ -41,7 +41,9 @@ import {
import './console-application.scss';
-export interface ConsoleApplicationProps extends React.Props<any> {
+type Capabilities = 'working-with-sql' | 'working-without-sql' | 'broken';
+
+export interface ConsoleApplicationProps {
hideLegacy: boolean;
baseURL?: string;
customHeaderName?: string;
@@ -60,11 +62,9 @@ export class ConsoleApplication extends React.PureComponent<
> {
static MESSAGE_KEY = 'druid-console-message';
static MESSAGE_DISMISSED = 'dismissed';
- private capabilitiesQueryManager: QueryManager<string, string>;
+ private capabilitiesQueryManager: QueryManager<string, Capabilities>;
- static async discoverCapabilities(): Promise<
- 'working-with-sql' | 'working-without-sql' | 'broken'
- > {
+ static async discoverCapabilities(): Promise<Capabilities> {
try {
await axios.post('/druid/v2/sql', { query: 'SELECT 1337' });
} catch (e) {
@@ -109,13 +109,13 @@ export class ConsoleApplication extends React.PureComponent<
});
}
- private supervisorId: string | null;
- private taskId: string | null;
- private openDialog: string | null;
- private datasource: string | null;
- private onlyUnavailable: boolean | null;
- private initQuery: string | null;
- private middleManager: string | null;
+ private supervisorId: string | undefined;
+ private taskId: string | undefined;
+ private openDialog: string | undefined;
+ private datasource: string | undefined;
+ private onlyUnavailable: boolean | undefined;
+ private initQuery: string | undefined;
+ private middleManager: string | undefined;
constructor(props: ConsoleApplicationProps, context: any) {
super(props, context);
@@ -134,16 +134,16 @@ export class ConsoleApplication extends React.PureComponent<
}
this.capabilitiesQueryManager = new QueryManager({
- processQuery: async (query: string) => {
+ processQuery: async () => {
const capabilities = await ConsoleApplication.discoverCapabilities();
if (capabilities !== 'working-with-sql') {
ConsoleApplication.shownNotifications(capabilities);
}
return capabilities;
},
- onStateChange: ({ result, loading, error }) => {
+ onStateChange: ({ result, loading }) => {
this.setState({
- noSqlMode: result === 'working-with-sql' ? false : true,
+ noSqlMode: result !== 'working-with-sql',
capabilitiesLoading: loading,
});
},
@@ -160,13 +160,13 @@ export class ConsoleApplication extends React.PureComponent<
private resetInitialsWithDelay() {
setTimeout(() => {
- this.taskId = null;
- this.supervisorId = null;
- this.openDialog = null;
- this.datasource = null;
- this.onlyUnavailable = null;
- this.initQuery = null;
- this.middleManager = null;
+ this.taskId = undefined;
+ this.supervisorId = undefined;
+ this.openDialog = undefined;
+ this.datasource = undefined;
+ this.onlyUnavailable = undefined;
+ this.initQuery = undefined;
+ this.middleManager = undefined;
}, 50);
}
@@ -177,7 +177,7 @@ export class ConsoleApplication extends React.PureComponent<
this.resetInitialsWithDelay();
};
- private goToTask = (taskId: string | null, openDialog?: string) => {
+ private goToTask = (taskId: string | undefined, openDialog?: string) => {
this.taskId = taskId;
if (openDialog) this.openDialog = openDialog;
window.location.hash = 'tasks';
diff --git a/web-console/src/dialogs/about-dialog/about-dialog.spec.tsx b/web-console/src/dialogs/about-dialog/about-dialog.spec.tsx
index fcc3d85..e6ac855 100644
--- a/web-console/src/dialogs/about-dialog/about-dialog.spec.tsx
+++ b/web-console/src/dialogs/about-dialog/about-dialog.spec.tsx
@@ -24,7 +24,7 @@ import { AboutDialog } from './about-dialog';
describe('about dialog', () => {
it('matches snapshot', () => {
const aboutDialog = <AboutDialog onClose={() => null} />;
- const { container } = render(aboutDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ render(aboutDialog);
+ expect(document.body.lastChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/dialogs/about-dialog/about-dialog.tsx b/web-console/src/dialogs/about-dialog/about-dialog.tsx
index be0a89f..fe2ea9d 100644
--- a/web-console/src/dialogs/about-dialog/about-dialog.tsx
+++ b/web-console/src/dialogs/about-dialog/about-dialog.tsx
@@ -28,7 +28,7 @@ import {
DRUID_WEBSITE,
} from '../../variables';
-export interface AboutDialogProps extends React.Props<any> {
+export interface AboutDialogProps {
onClose: () => void;
}
diff --git a/web-console/src/dialogs/async-action-dialog/async-action-dialog.spec.tsx b/web-console/src/dialogs/async-action-dialog/async-action-dialog.spec.tsx
index d54a75a..27b468f 100644
--- a/web-console/src/dialogs/async-action-dialog/async-action-dialog.spec.tsx
+++ b/web-console/src/dialogs/async-action-dialog/async-action-dialog.spec.tsx
@@ -28,13 +28,13 @@ describe('async action dialog', () => {
action={() => {
return Promise.resolve();
}}
- onClose={(success: boolean) => null}
+ onClose={() => null}
confirmButtonText={'test'}
successText={'test'}
failText={'test'}
/>
);
- const { container } = render(asyncActionDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ render(asyncActionDialog);
+ expect(document.body.lastChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/dialogs/async-action-dialog/async-action-dialog.tsx b/web-console/src/dialogs/async-action-dialog/async-action-dialog.tsx
index c5b11cd..e5a26c3 100644
--- a/web-console/src/dialogs/async-action-dialog/async-action-dialog.tsx
+++ b/web-console/src/dialogs/async-action-dialog/async-action-dialog.tsx
@@ -16,25 +16,14 @@
* limitations under the License.
*/
-import {
- Button,
- ButtonGroup,
- Classes,
- Dialog,
- FormGroup,
- Icon,
- Intent,
- NumericInput,
- ProgressBar,
- TagInput,
-} from '@blueprintjs/core';
+import { Button, Classes, Dialog, Icon, Intent, ProgressBar } from '@blueprintjs/core';
import { IconName } from '@blueprintjs/icons';
import classNames from 'classnames';
import React from 'react';
import { AppToaster } from '../../singletons/toaster';
-export interface AsyncAlertDialogProps extends React.Props<any> {
+export interface AsyncAlertDialogProps {
action: null | (() => Promise<void>);
onClose: (success: boolean) => void;
confirmButtonText: string;
diff --git a/web-console/src/dialogs/compaction-dialog/__snapshots__/compaction-dialog.spec.tsx.snap b/web-console/src/dialogs/compaction-dialog/__snapshots__/compaction-dialog.spec.tsx.snap
index 5e8951c..749de70 100644
--- a/web-console/src/dialogs/compaction-dialog/__snapshots__/compaction-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/compaction-dialog/__snapshots__/compaction-dialog.spec.tsx.snap
@@ -240,7 +240,7 @@ exports[`compaction dialog matches snapshot 1`] = `
class="bp3-form-content"
>
<div
- class="bp3-input-group"
+ class="bp3-input-group suggestible-input"
>
<input
class="bp3-input"
diff --git a/web-console/src/dialogs/compaction-dialog/compaction-dialog.spec.tsx b/web-console/src/dialogs/compaction-dialog/compaction-dialog.spec.tsx
index 7cd5a4c..38be490 100644
--- a/web-console/src/dialogs/compaction-dialog/compaction-dialog.spec.tsx
+++ b/web-console/src/dialogs/compaction-dialog/compaction-dialog.spec.tsx
@@ -26,13 +26,13 @@ describe('compaction dialog', () => {
const compactionDialog = (
<CompactionDialog
onClose={() => null}
- onSave={(config: any) => null}
+ onSave={() => null}
onDelete={() => null}
datasource={'test'}
configData={'test'}
/>
);
- const { container } = render(compactionDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ render(compactionDialog);
+ expect(document.body.lastChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/dialogs/compaction-dialog/compaction-dialog.tsx b/web-console/src/dialogs/compaction-dialog/compaction-dialog.tsx
index 72484b2..f5d9508 100644
--- a/web-console/src/dialogs/compaction-dialog/compaction-dialog.tsx
+++ b/web-console/src/dialogs/compaction-dialog/compaction-dialog.tsx
@@ -23,7 +23,7 @@ import { AutoForm } from '../../components';
import './compaction-dialog.scss';
-export interface CompactionDialogProps extends React.Props<any> {
+export interface CompactionDialogProps {
onClose: () => void;
onSave: (config: any) => void;
onDelete: () => void;
diff --git a/web-console/src/dialogs/coordinator-dynamic-config/__snapshots__/coordinator-dynamic-config.spec.tsx.snap b/web-console/src/dialogs/coordinator-dynamic-config-dialog/__snapshots__/coordinator-dynamic-config-dialog.spec.tsx.snap
similarity index 92%
copy from web-console/src/dialogs/coordinator-dynamic-config/__snapshots__/coordinator-dynamic-config.spec.tsx.snap
copy to web-console/src/dialogs/coordinator-dynamic-config-dialog/__snapshots__/coordinator-dynamic-config-dialog.spec.tsx.snap
index bccc553..b8cb588 100644
--- a/web-console/src/dialogs/coordinator-dynamic-config/__snapshots__/coordinator-dynamic-config.spec.tsx.snap
+++ b/web-console/src/dialogs/coordinator-dynamic-config-dialog/__snapshots__/coordinator-dynamic-config-dialog.spec.tsx.snap
@@ -16,7 +16,7 @@ exports[`coordinator dynamic config matches snapshot 1`] = `
tabindex="0"
>
<div
- class="bp3-dialog snitch-dialog coordinator-dynamic-config"
+ class="bp3-dialog snitch-dialog coordinator-dynamic-config-dialog"
>
<div
class="bp3-dialog-header"
@@ -77,16 +77,6 @@ exports[`coordinator dynamic config matches snapshot 1`] = `
class="bp3-dialog-footer-actions"
>
<button
- class="bp3-button bp3-minimal left-align-button"
- type="button"
- >
- <span
- class="bp3-button-text"
- >
- History
- </span>
- </button>
- <button
class="bp3-button bp3-intent-primary"
type="button"
>
diff --git a/web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.scss b/web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.scss
similarity index 96%
rename from web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.scss
rename to web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.scss
index 8032103..0213e2c 100644
--- a/web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.scss
+++ b/web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.scss
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-.coordinator-dynamic-config {
+.coordinator-dynamic-config-dialog {
&.bp3-dialog {
margin-top: 5vh;
top: 5%;
diff --git a/web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.spec.tsx b/web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.spec.tsx
similarity index 87%
rename from web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.spec.tsx
rename to web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.spec.tsx
index fe99155..660b027 100644
--- a/web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.spec.tsx
+++ b/web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.spec.tsx
@@ -19,12 +19,12 @@
import React from 'react';
import { render } from 'react-testing-library';
-import { CoordinatorDynamicConfigDialog } from './coordinator-dynamic-config';
+import { CoordinatorDynamicConfigDialog } from './coordinator-dynamic-config-dialog';
describe('coordinator dynamic config', () => {
it('matches snapshot', () => {
const coordinatorDynamicConfig = <CoordinatorDynamicConfigDialog onClose={() => null} />;
- const { container } = render(coordinatorDynamicConfig, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ render(coordinatorDynamicConfig);
+ expect(document.body.lastChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.tsx b/web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.tsx
similarity index 93%
rename from web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.tsx
rename to web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.tsx
index ca8bd8f..233d532 100644
--- a/web-console/src/dialogs/coordinator-dynamic-config/coordinator-dynamic-config.tsx
+++ b/web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.tsx
@@ -26,9 +26,9 @@ import { AppToaster } from '../../singletons/toaster';
import { getDruidErrorMessage, QueryManager } from '../../utils';
import { SnitchDialog } from '../snitch-dialog/snitch-dialog';
-import './coordinator-dynamic-config.scss';
+import './coordinator-dynamic-config-dialog.scss';
-export interface CoordinatorDynamicConfigDialogProps extends React.Props<any> {
+export interface CoordinatorDynamicConfigDialogProps {
onClose: () => void;
}
@@ -41,7 +41,7 @@ export class CoordinatorDynamicConfigDialog extends React.PureComponent<
CoordinatorDynamicConfigDialogProps,
CoordinatorDynamicConfigDialogState
> {
- private historyQueryManager: QueryManager<string, any>;
+ private historyQueryManager: QueryManager<null, any>;
constructor(props: CoordinatorDynamicConfigDialogProps) {
super(props);
@@ -49,24 +49,24 @@ export class CoordinatorDynamicConfigDialog extends React.PureComponent<
dynamicConfig: null,
historyRecords: [],
};
- }
-
- componentDidMount() {
- this.getClusterConfig();
this.historyQueryManager = new QueryManager({
- processQuery: async query => {
+ processQuery: async () => {
const historyResp = await axios(`/druid/coordinator/v1/config/history?count=100`);
return historyResp.data;
},
- onStateChange: ({ result, loading, error }) => {
+ onStateChange: ({ result }) => {
this.setState({
historyRecords: result,
});
},
});
+ }
+
+ componentDidMount() {
+ this.getClusterConfig();
- this.historyQueryManager.runQuery(`dummy`);
+ this.historyQueryManager.runQuery(null);
}
async getClusterConfig() {
@@ -118,7 +118,7 @@ export class CoordinatorDynamicConfigDialog extends React.PureComponent<
return (
<SnitchDialog
- className="coordinator-dynamic-config"
+ className="coordinator-dynamic-config-dialog"
isOpen
onSave={this.saveClusterConfig}
onClose={onClose}
diff --git a/web-console/src/dialogs/history-dialog/history-dialog.spec.tsx b/web-console/src/dialogs/history-dialog/history-dialog.spec.tsx
index 5a26884..7281186 100644
--- a/web-console/src/dialogs/history-dialog/history-dialog.spec.tsx
+++ b/web-console/src/dialogs/history-dialog/history-dialog.spec.tsx
@@ -32,7 +32,7 @@ describe('history dialog', () => {
isOpen
/>
);
- const { container } = render(historyDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ render(historyDialog);
+ expect(document.body.lastChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/dialogs/history-dialog/history-dialog.tsx b/web-console/src/dialogs/history-dialog/history-dialog.tsx
index 45ebfc1..c07c620 100644
--- a/web-console/src/dialogs/history-dialog/history-dialog.tsx
+++ b/web-console/src/dialogs/history-dialog/history-dialog.tsx
@@ -24,7 +24,7 @@ import { JSONCollapse } from '../../components';
import './history-dialog.scss';
interface HistoryDialogProps extends IDialogProps {
- historyRecords: any;
+ historyRecords: any[];
}
interface HistoryDialogState {}
@@ -45,13 +45,13 @@ export class HistoryDialog extends React.PureComponent<HistoryDialogProps, Histo
<>
<span className="history-dialog-title">History</span>
<div className="history-record-entries">
- {historyRecords.map((record: any) => {
+ {historyRecords.map((record, i) => {
const auditInfo = record.auditInfo;
const auditTime = record.auditTime;
const formattedTime = auditTime.replace('T', ' ').substring(0, auditTime.length - 5);
return (
- <div key={record.auditTime} className="history-record-entry">
+ <div key={i} className="history-record-entry">
<Card>
<div className="history-record-title">
<span className="history-record-title-change">Change</span>
diff --git a/web-console/src/dialogs/index.ts b/web-console/src/dialogs/index.ts
index 8a4ebe9..27bd358 100644
--- a/web-console/src/dialogs/index.ts
+++ b/web-console/src/dialogs/index.ts
@@ -18,10 +18,10 @@
export * from './about-dialog/about-dialog';
export * from './async-action-dialog/async-action-dialog';
export * from './compaction-dialog/compaction-dialog';
-export * from './coordinator-dynamic-config/coordinator-dynamic-config';
+export * from './coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog';
export * from './history-dialog/history-dialog';
export * from './lookup-edit-dialog/lookup-edit-dialog';
-export * from './overlord-dynamic-config/overlord-dynamic-config';
+export * from './overlord-dynamic-config-dialog/overlord-dynamic-config-dialog';
export * from './query-plan-dialog/query-plan-dialog';
export * from './retention-dialog/retention-dialog';
export * from './snitch-dialog/snitch-dialog';
diff --git a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.spec.tsx b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.spec.tsx
index 214cf49..8411612 100644
--- a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.spec.tsx
+++ b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.spec.tsx
@@ -38,7 +38,7 @@ describe('lookup edit dialog', () => {
/>
);
- const { container } = render(lookupEditDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ render(lookupEditDialog);
+ expect(document.body.lastChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx
index 9415e60..180ec2b 100644
--- a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx
+++ b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx
@@ -32,7 +32,7 @@ import { validJson } from '../../utils';
import './lookup-edit-dialog.scss';
-export interface LookupEditDialogProps extends React.Props<any> {
+export interface LookupEditDialogProps {
isOpen: boolean;
onClose: () => void;
onSubmit: () => void;
@@ -105,7 +105,6 @@ export class LookupEditDialog extends React.PureComponent<
lookupVersion,
onChange,
isEdit,
- allLookupTiers,
} = this.props;
const disableSubmit =
@@ -155,8 +154,6 @@ export class LookupEditDialog extends React.PureComponent<
value={lookupSpec}
editorProps={{ $blockScrolling: Infinity }}
setOptions={{
- enableBasicAutocompletion: false,
- enableLiveAutocompletion: false,
tabSize: 2,
}}
style={{}}
diff --git a/web-console/src/dialogs/coordinator-dynamic-config/__snapshots__/coordinator-dynamic-config.spec.tsx.snap b/web-console/src/dialogs/overlord-dynamic-config-dialog/__snapshots__/overload-dynamic-config-dialog.spec.tsx.snap
similarity index 83%
rename from web-console/src/dialogs/coordinator-dynamic-config/__snapshots__/coordinator-dynamic-config.spec.tsx.snap
rename to web-console/src/dialogs/overlord-dynamic-config-dialog/__snapshots__/overload-dynamic-config-dialog.spec.tsx.snap
index bccc553..fcde8c0 100644
--- a/web-console/src/dialogs/coordinator-dynamic-config/__snapshots__/coordinator-dynamic-config.spec.tsx.snap
+++ b/web-console/src/dialogs/overlord-dynamic-config-dialog/__snapshots__/overload-dynamic-config-dialog.spec.tsx.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`coordinator dynamic config matches snapshot 1`] = `
+exports[`overload dynamic config matches snapshot 1`] = `
<div
class="bp3-portal"
>
@@ -16,7 +16,7 @@ exports[`coordinator dynamic config matches snapshot 1`] = `
tabindex="0"
>
<div
- class="bp3-dialog snitch-dialog coordinator-dynamic-config"
+ class="bp3-dialog snitch-dialog overlord-dynamic-config-dialog"
>
<div
class="bp3-dialog-header"
@@ -24,7 +24,7 @@ exports[`coordinator dynamic config matches snapshot 1`] = `
<h4
class="bp3-heading"
>
- Coordinator dynamic config
+ Overlord dynamic config
</h4>
<button
aria-label="Close"
@@ -56,10 +56,10 @@ exports[`coordinator dynamic config matches snapshot 1`] = `
class="bp3-dialog-body"
>
<p>
- Edit the coordinator dynamic configuration on the fly. For more information please refer to the
+ Edit the overlord dynamic configuration on the fly. For more information please refer to the
<a
- href="https://druid.apache.org/docs/latest/configuration/index.html#dynamic-configuration"
+ href="https://druid.apache.org/docs/latest/configuration/index.html#overlord-dynamic-configuration"
target="_blank"
>
documentation
@@ -77,16 +77,6 @@ exports[`coordinator dynamic config matches snapshot 1`] = `
class="bp3-dialog-footer-actions"
>
<button
- class="bp3-button bp3-minimal left-align-button"
- type="button"
- >
- <span
- class="bp3-button-text"
- >
- History
- </span>
- </button>
- <button
class="bp3-button bp3-intent-primary"
type="button"
>
diff --git a/web-console/src/dialogs/about-dialog/about-dialog.spec.tsx b/web-console/src/dialogs/overlord-dynamic-config-dialog/overload-dynamic-config-dialog.spec.tsx
similarity index 75%
copy from web-console/src/dialogs/about-dialog/about-dialog.spec.tsx
copy to web-console/src/dialogs/overlord-dynamic-config-dialog/overload-dynamic-config-dialog.spec.tsx
index fcc3d85..8a2011d 100644
--- a/web-console/src/dialogs/about-dialog/about-dialog.spec.tsx
+++ b/web-console/src/dialogs/overlord-dynamic-config-dialog/overload-dynamic-config-dialog.spec.tsx
@@ -19,12 +19,13 @@
import React from 'react';
import { render } from 'react-testing-library';
-import { AboutDialog } from './about-dialog';
+import { OverlordDynamicConfigDialog } from './overlord-dynamic-config-dialog';
-describe('about dialog', () => {
+describe('overload dynamic config', () => {
it('matches snapshot', () => {
- const aboutDialog = <AboutDialog onClose={() => null} />;
- const { container } = render(aboutDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ const lookupEditDialog = <OverlordDynamicConfigDialog onClose={() => null} />;
+
+ render(lookupEditDialog);
+ expect(document.body.lastChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/dialogs/overlord-dynamic-config/overlord-dynamic-config.scss b/web-console/src/dialogs/overlord-dynamic-config-dialog/overlord-dynamic-config-dialog.scss
similarity index 97%
rename from web-console/src/dialogs/overlord-dynamic-config/overlord-dynamic-config.scss
rename to web-console/src/dialogs/overlord-dynamic-config-dialog/overlord-dynamic-config-dialog.scss
index 6e676ad..f655cc9 100644
--- a/web-console/src/dialogs/overlord-dynamic-config/overlord-dynamic-config.scss
+++ b/web-console/src/dialogs/overlord-dynamic-config-dialog/overlord-dynamic-config-dialog.scss
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-.overlord-dynamic-config {
+.overlord-dynamic-config-dialog {
&.bp3-dialog {
margin-top: 5vh;
top: 5%;
diff --git a/web-console/src/dialogs/overlord-dynamic-config/overlord-dynamic-config.tsx b/web-console/src/dialogs/overlord-dynamic-config-dialog/overlord-dynamic-config-dialog.tsx
similarity index 94%
rename from web-console/src/dialogs/overlord-dynamic-config/overlord-dynamic-config.tsx
rename to web-console/src/dialogs/overlord-dynamic-config-dialog/overlord-dynamic-config-dialog.tsx
index 81ee2f6..dcf9423 100644
--- a/web-console/src/dialogs/overlord-dynamic-config/overlord-dynamic-config.tsx
+++ b/web-console/src/dialogs/overlord-dynamic-config-dialog/overlord-dynamic-config-dialog.tsx
@@ -26,9 +26,9 @@ import { AppToaster } from '../../singletons/toaster';
import { getDruidErrorMessage, QueryManager } from '../../utils';
import { SnitchDialog } from '../snitch-dialog/snitch-dialog';
-import './overlord-dynamic-config.scss';
+import './overlord-dynamic-config-dialog.scss';
-export interface OverlordDynamicConfigDialogProps extends React.Props<any> {
+export interface OverlordDynamicConfigDialogProps {
onClose: () => void;
}
@@ -51,22 +51,22 @@ export class OverlordDynamicConfigDialog extends React.PureComponent<
allJSONValid: true,
historyRecords: [],
};
- }
-
- componentDidMount() {
- this.getConfig();
this.historyQueryManager = new QueryManager({
- processQuery: async query => {
+ processQuery: async () => {
const historyResp = await axios(`/druid/indexer/v1/worker/history?count=100`);
return historyResp.data;
},
- onStateChange: ({ result, loading, error }) => {
+ onStateChange: ({ result }) => {
this.setState({
historyRecords: result,
});
},
});
+ }
+
+ componentDidMount() {
+ this.getConfig();
this.historyQueryManager.runQuery(`dummy`);
}
@@ -120,7 +120,7 @@ export class OverlordDynamicConfigDialog extends React.PureComponent<
return (
<SnitchDialog
- className="overlord-dynamic-config"
+ className="overlord-dynamic-config-dialog"
isOpen
onSave={this.saveConfig}
onClose={onClose}
diff --git a/web-console/src/dialogs/overlord-dynamic-config/__snapshots__/overload-dynamic-config.spec.tsx.snap b/web-console/src/dialogs/overlord-dynamic-config/__snapshots__/overload-dynamic-config.spec.tsx.snap
deleted file mode 100644
index 1111228..0000000
--- a/web-console/src/dialogs/overlord-dynamic-config/__snapshots__/overload-dynamic-config.spec.tsx.snap
+++ /dev/null
@@ -1,356 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`overload dynamic config matches snapshot 1`] = `
-<div
- class="bp3-portal"
->
- <div
- class="bp3-overlay bp3-overlay-open bp3-overlay-scroll-container"
- >
- <div
- class="bp3-overlay-backdrop bp3-overlay-appear bp3-overlay-appear-active"
- tabindex="0"
- />
- <div
- class="bp3-dialog-container bp3-overlay-content bp3-overlay-appear bp3-overlay-appear-active"
- tabindex="0"
- >
- <div
- class="bp3-dialog lookup-edit-dialog"
- >
- <div
- class="bp3-dialog-header"
- >
- <h4
- class="bp3-heading"
- >
- Add lookup
- </h4>
- <button
- aria-label="Close"
- class="bp3-button bp3-minimal bp3-dialog-close-button"
- type="button"
- >
- <span
- class="bp3-icon bp3-icon-small-cross"
- icon="small-cross"
- >
- <svg
- data-icon="small-cross"
- height="20"
- viewBox="0 0 20 20"
- width="20"
- >
- <desc>
- small-cross
- </desc>
- <path
- d="M11.41 10l3.29-3.29c.19-.18.3-.43.3-.71a1.003 1.003 0 0 0-1.71-.71L10 8.59l-3.29-3.3a1.003 1.003 0 0 0-1.42 1.42L8.59 10 5.3 13.29c-.19.18-.3.43-.3.71a1.003 1.003 0 0 0 1.71.71l3.29-3.3 3.29 3.29c.18.19.43.3.71.3a1.003 1.003 0 0 0 .71-1.71L11.41 10z"
- fill-rule="evenodd"
- />
- </svg>
- </span>
- </button>
- </div>
- <div
- class="bp3-form-group lookup-label"
- >
- <label
- class="bp3-label"
- >
- Name:
-
- <span
- class="bp3-text-muted"
- />
- </label>
- <div
- class="bp3-form-content"
- >
- <div
- class="bp3-input-group"
- >
- <input
- class="bp3-input"
- placeholder="Enter the lookup name"
- style="padding-right: 10px;"
- type="text"
- value="test"
- />
- </div>
- </div>
- </div>
- <div
- class="bp3-form-group lookup-label"
- >
- <label
- class="bp3-label"
- >
- Tier:
-
- <span
- class="bp3-text-muted"
- />
- </label>
- <div
- class="bp3-form-content"
- >
- <div
- class="bp3-html-select"
- >
- <select>
- <option
- value="a"
- >
- a
- </option>
- <option
- value="b"
- >
- b
- </option>
- <option
- value="c"
- >
- c
- </option>
- <option
- value="d"
- >
- d
- </option>
- <option
- value="e"
- >
- e
- </option>
- <option
- value="f"
- >
- f
- </option>
- <option
- value="g"
- >
- g
- </option>
- <option
- value="h"
- >
- h
- </option>
- <option
- value="i"
- >
- i
- </option>
- <option
- value="j"
- >
- j
- </option>
- </select>
- <span
- class="bp3-icon bp3-icon-double-caret-vertical"
- icon="double-caret-vertical"
- >
- <svg
- data-icon="double-caret-vertical"
- height="16"
- viewBox="0 0 16 16"
- width="16"
- >
- <desc>
- double-caret-vertical
- </desc>
- <path
- d="M5 7h6a1.003 1.003 0 0 0 .71-1.71l-3-3C8.53 2.11 8.28 2 8 2s-.53.11-.71.29l-3 3A1.003 1.003 0 0 0 5 7zm6 2H5a1.003 1.003 0 0 0-.71 1.71l3 3c.18.18.43.29.71.29s.53-.11.71-.29l3-3A1.003 1.003 0 0 0 11 9z"
- fill-rule="evenodd"
- />
- </svg>
- </span>
- </div>
- </div>
- </div>
- <div
- class="bp3-form-group lookup-label"
- >
- <label
- class="bp3-label"
- >
- Version:
-
- <span
- class="bp3-text-muted"
- />
- </label>
- <div
- class="bp3-form-content"
- >
- <div
- class="bp3-input-group"
- >
- <input
- class="bp3-input"
- placeholder="Enter the lookup version"
- style="padding-right: 0px;"
- type="text"
- value="test"
- />
- <span
- class="bp3-input-action"
- >
- <button
- class="bp3-button bp3-minimal"
- type="button"
- >
- <span
- class="bp3-button-text"
- >
- Use ISO as version
- </span>
- </button>
- </span>
- </div>
- </div>
- </div>
- <div
- class="bp3-form-group lookup-label"
- >
- <label
- class="bp3-label"
- >
- Spec:
-
- <span
- class="bp3-text-muted"
- />
- </label>
- <div
- class="bp3-form-content"
- />
- </div>
- <div
- class=" ace_editor ace-tm lookup-edit-dialog-textarea"
- id="brace-editor"
- style="width: auto; height: 40vh;"
- >
- <textarea
- autocapitalize="off"
- autocorrect="off"
- class="ace_text-input"
- spellcheck="false"
- style="opacity: 0;"
- wrap="off"
- />
- <div
- aria-hidden="true"
- class="ace_gutter"
- style="display: none;"
- >
- <div
- class="ace_layer ace_gutter-layer ace_folding-enabled"
- />
- <div
- class="ace_gutter-active-line"
- />
- </div>
- <div
- class="ace_scroller"
- >
- <div
- class="ace_content"
- >
- <div
- class="ace_layer ace_print-margin-layer"
- >
- <div
- class="ace_print-margin"
- style="left: 4px; visibility: hidden;"
- />
- </div>
- <div
- class="ace_layer ace_marker-layer"
- />
- <div
- class="ace_layer ace_text-layer"
- style="padding: 0px 4px;"
- />
- <div
- class="ace_layer ace_marker-layer"
- />
- <div
- class="ace_layer ace_cursor-layer ace_hidden-cursors"
- >
- <div
- class="ace_cursor"
- />
- </div>
- </div>
- </div>
- <div
- class="ace_scrollbar ace_scrollbar-v"
- style="display: none; width: 20px;"
- >
- <div
- class="ace_scrollbar-inner"
- style="width: 20px;"
- />
- </div>
- <div
- class="ace_scrollbar ace_scrollbar-h"
- style="display: none; height: 20px;"
- >
- <div
- class="ace_scrollbar-inner"
- style="height: 20px;"
- />
- </div>
- <div
- style="height: auto; width: auto; top: 0px; left: 0px; visibility: hidden; position: absolute; white-space: pre; overflow: hidden;"
- >
- <div
- style="height: auto; width: auto; top: 0px; left: 0px; visibility: hidden; position: absolute; white-space: pre; overflow: visible;"
- />
- <div
- style="height: auto; width: auto; top: 0px; left: 0px; visibility: hidden; position: absolute; white-space: pre; overflow: visible;"
- >
- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- </div>
- </div>
- </div>
- <div
- class="bp3-dialog-footer"
- >
- <div
- class="bp3-dialog-footer-actions"
- >
- <button
- class="bp3-button"
- type="button"
- >
- <span
- class="bp3-button-text"
- >
- Close
- </span>
- </button>
- <button
- class="bp3-button bp3-disabled bp3-intent-primary"
- disabled=""
- tabindex="-1"
- type="button"
- >
- <span
- class="bp3-button-text"
- >
- Submit
- </span>
- </button>
- </div>
- </div>
- </div>
- </div>
- </div>
-</div>
-`;
diff --git a/web-console/src/dialogs/overlord-dynamic-config/overload-dynamic-config.spec.tsx b/web-console/src/dialogs/overlord-dynamic-config/overload-dynamic-config.spec.tsx
deleted file mode 100644
index f9d635b..0000000
--- a/web-console/src/dialogs/overlord-dynamic-config/overload-dynamic-config.spec.tsx
+++ /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 React from 'react';
-import { render } from 'react-testing-library';
-
-import { LookupEditDialog } from '../lookup-edit-dialog/lookup-edit-dialog';
-
-describe('overload dynamic config', () => {
- it('matches snapshot', () => {
- const lookupEditDialog = (
- <LookupEditDialog
- isOpen
- onClose={() => null}
- onSubmit={() => null}
- onChange={() => null}
- lookupName={'test'}
- lookupTier={'test'}
- lookupVersion={'test'}
- lookupSpec={'test'}
- isEdit={false}
- allLookupTiers={['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']}
- />
- );
- const { container } = render(lookupEditDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
- });
-});
diff --git a/web-console/src/dialogs/query-plan-dialog/query-plan-dialog.spec.tsx b/web-console/src/dialogs/query-plan-dialog/query-plan-dialog.spec.tsx
index 19cdaf1..045b5d1 100644
--- a/web-console/src/dialogs/query-plan-dialog/query-plan-dialog.spec.tsx
+++ b/web-console/src/dialogs/query-plan-dialog/query-plan-dialog.spec.tsx
@@ -30,7 +30,7 @@ describe('query plan dialog', () => {
onClose={() => null}
/>
);
- const { container } = render(queryPlanDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ render(queryPlanDialog);
+ expect(document.body.lastChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/dialogs/query-plan-dialog/query-plan-dialog.tsx b/web-console/src/dialogs/query-plan-dialog/query-plan-dialog.tsx
index c398d5b..8fa438b 100644
--- a/web-console/src/dialogs/query-plan-dialog/query-plan-dialog.tsx
+++ b/web-console/src/dialogs/query-plan-dialog/query-plan-dialog.tsx
@@ -23,7 +23,7 @@ import { BasicQueryExplanation, SemiJoinQueryExplanation } from '../../utils';
import './query-plan-dialog.scss';
-export interface QueryPlanDialogProps extends React.Props<any> {
+export interface QueryPlanDialogProps {
explainResult: BasicQueryExplanation | SemiJoinQueryExplanation | string | null;
explainError: Error | null;
onClose: () => void;
diff --git a/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap b/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap
index 769a0b2..554dcda 100644
--- a/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap
@@ -117,16 +117,6 @@ exports[`retention dialog matches snapshot 1`] = `
class="bp3-dialog-footer-actions"
>
<button
- class="bp3-button bp3-minimal left-align-button"
- type="button"
- >
- <span
- class="bp3-button-text"
- >
- History
- </span>
- </button>
- <button
class="bp3-button"
type="button"
>
diff --git a/web-console/src/dialogs/retention-dialog/retention-dialog.array.spec.ts b/web-console/src/dialogs/retention-dialog/retention-dialog.array.spec.ts
deleted file mode 100644
index a496d40..0000000
--- a/web-console/src/dialogs/retention-dialog/retention-dialog.array.spec.ts
+++ /dev/null
@@ -1,56 +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 React from 'react';
-
-import { reorderArray } from './retention-dialog';
-
-describe('reorderArray', () => {
- it('works when nothing changes', () => {
- const array = ['a', 'b', 'c', 'd', 'e'];
-
- const newArray = reorderArray(array, 0, 0);
-
- expect(newArray).toEqual(['a', 'b', 'c', 'd', 'e']);
- expect(array).toEqual(['a', 'b', 'c', 'd', 'e']);
- });
-
- it('works upward', () => {
- const array = ['a', 'b', 'c', 'd', 'e'];
-
- let newArray = reorderArray(array, 2, 1);
- expect(newArray).toEqual(['a', 'c', 'b', 'd', 'e']);
- expect(array).toEqual(['a', 'b', 'c', 'd', 'e']);
-
- newArray = reorderArray(array, 2, 0);
- expect(newArray).toEqual(['c', 'a', 'b', 'd', 'e']);
- expect(array).toEqual(['a', 'b', 'c', 'd', 'e']);
- });
-
- it('works downward', () => {
- const array = ['a', 'b', 'c', 'd', 'e'];
-
- let newArray = reorderArray(array, 2, 3);
- expect(newArray).toEqual(['a', 'b', 'c', 'd', 'e']);
- expect(array).toEqual(['a', 'b', 'c', 'd', 'e']);
-
- newArray = reorderArray(array, 2, 4);
- expect(newArray).toEqual(['a', 'b', 'd', 'c', 'e']);
- expect(array).toEqual(['a', 'b', 'c', 'd', 'e']);
- });
-});
diff --git a/web-console/src/dialogs/retention-dialog/retention-dialog.spec.tsx b/web-console/src/dialogs/retention-dialog/retention-dialog.spec.tsx
index bcf1e77..80ccb71 100644
--- a/web-console/src/dialogs/retention-dialog/retention-dialog.spec.tsx
+++ b/web-console/src/dialogs/retention-dialog/retention-dialog.spec.tsx
@@ -19,7 +19,7 @@
import React from 'react';
import { render } from 'react-testing-library';
-import { RetentionDialog } from './retention-dialog';
+import { reorderArray, RetentionDialog } from './retention-dialog';
describe('retention dialog', () => {
it('matches snapshot', () => {
@@ -33,7 +33,42 @@ describe('retention dialog', () => {
onSave={() => null}
/>
);
- const { container } = render(retentionDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ render(retentionDialog);
+ expect(document.body.lastChild).toMatchSnapshot();
+ });
+
+ describe('reorderArray', () => {
+ it('works when nothing changes', () => {
+ const array = ['a', 'b', 'c', 'd', 'e'];
+
+ const newArray = reorderArray(array, 0, 0);
+
+ expect(newArray).toEqual(['a', 'b', 'c', 'd', 'e']);
+ expect(array).toEqual(['a', 'b', 'c', 'd', 'e']);
+ });
+
+ it('works upward', () => {
+ const array = ['a', 'b', 'c', 'd', 'e'];
+
+ let newArray = reorderArray(array, 2, 1);
+ expect(newArray).toEqual(['a', 'c', 'b', 'd', 'e']);
+ expect(array).toEqual(['a', 'b', 'c', 'd', 'e']);
+
+ newArray = reorderArray(array, 2, 0);
+ expect(newArray).toEqual(['c', 'a', 'b', 'd', 'e']);
+ expect(array).toEqual(['a', 'b', 'c', 'd', 'e']);
+ });
+
+ it('works downward', () => {
+ const array = ['a', 'b', 'c', 'd', 'e'];
+
+ let newArray = reorderArray(array, 2, 3);
+ expect(newArray).toEqual(['a', 'b', 'c', 'd', 'e']);
+ expect(array).toEqual(['a', 'b', 'c', 'd', 'e']);
+
+ newArray = reorderArray(array, 2, 4);
+ expect(newArray).toEqual(['a', 'b', 'd', 'c', 'e']);
+ expect(array).toEqual(['a', 'b', 'c', 'd', 'e']);
+ });
});
});
diff --git a/web-console/src/dialogs/retention-dialog/retention-dialog.tsx b/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
index 1c7cfc8..f75a3d1 100644
--- a/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
+++ b/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
@@ -21,7 +21,7 @@ import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import React from 'react';
-import { Rule, RuleEditor } from '../../components';
+import { RuleEditor } from '../../components';
import { QueryManager } from '../../utils';
import { SnitchDialog } from '../snitch-dialog/snitch-dialog';
@@ -37,7 +37,7 @@ export function reorderArray<T>(items: T[], oldIndex: number, newIndex: number):
return newItems;
}
-export interface RetentionDialogProps extends React.Props<any> {
+export interface RetentionDialogProps {
datasource: string;
rules: any[];
tiers: string[];
@@ -64,23 +64,23 @@ export class RetentionDialog extends React.PureComponent<
currentRules: props.rules,
historyRecords: [],
};
- }
- componentDidMount() {
- const { datasource } = this.props;
this.historyQueryManager = new QueryManager({
- processQuery: async query => {
+ processQuery: async datasource => {
const historyResp = await axios(`/druid/coordinator/v1/rules/${datasource}/history`);
return historyResp.data;
},
- onStateChange: ({ result, loading, error }) => {
+ onStateChange: ({ result }) => {
this.setState({
historyRecords: result,
});
},
});
+ }
- this.historyQueryManager.runQuery(`dummy`);
+ componentDidMount() {
+ const { datasource } = this.props;
+ this.historyQueryManager.runQuery(datasource);
}
private save = (comment: string) => {
@@ -106,7 +106,7 @@ export class RetentionDialog extends React.PureComponent<
onDeleteRule = (index: number) => {
const { currentRules } = this.state;
- const newRules = (currentRules || []).filter((r, i) => i !== index);
+ const newRules = (currentRules || []).filter((_r, i) => i !== index);
this.setState({
currentRules: newRules,
diff --git a/web-console/src/dialogs/segments-table-action-dialog/__snapshots__/segment-table-action-dialog.spec.tsx.snap b/web-console/src/dialogs/segments-table-action-dialog/__snapshots__/segment-table-action-dialog.spec.tsx.snap
index 6e695a9..5cf64b7 100644
--- a/web-console/src/dialogs/segments-table-action-dialog/__snapshots__/segment-table-action-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/segments-table-action-dialog/__snapshots__/segment-table-action-dialog.spec.tsx.snap
@@ -148,7 +148,18 @@ exports[`task table action dialog matches snapshot 1`] = `
>
<div
class="footer-actions-left"
- />
+ >
+ <button
+ class="bp3-button"
+ type="button"
+ >
+ <span
+ class="bp3-button-text"
+ >
+ test
+ </span>
+ </button>
+ </div>
<div
class="bp3-dialog-footer-actions"
>
diff --git a/web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.spec.tsx b/web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.spec.tsx
index 52decd2..00bf06b 100644
--- a/web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.spec.tsx
+++ b/web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.spec.tsx
@@ -21,19 +21,18 @@ import { render } from 'react-testing-library';
import { SegmentTableActionDialog } from './segment-table-action-dialog';
-const basicAction = { title: 'test', onAction: () => null };
describe('task table action dialog', () => {
it('matches snapshot', () => {
const taskTableActionDialog = (
<SegmentTableActionDialog
dataSourceId="test"
segmentId="test"
- actions={[]}
+ actions={[{ title: 'test', onAction: () => null }]}
onClose={() => null}
isOpen
/>
);
- const { container } = render(taskTableActionDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ render(taskTableActionDialog);
+ expect(document.body.lastChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.tsx b/web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.tsx
index 1ae592b..3dbfef8 100644
--- a/web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.tsx
+++ b/web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.tsx
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-import { IDialogProps, TextArea } from '@blueprintjs/core';
+import { IDialogProps } from '@blueprintjs/core';
import React from 'react';
import { ShowJson } from '../../components';
diff --git a/web-console/src/dialogs/show-value-dialog/__snapshots__/show-value-dialog.spec.tsx.snap b/web-console/src/dialogs/show-value-dialog/__snapshots__/show-value-dialog.spec.tsx.snap
index 144f880..88a1d05 100644
--- a/web-console/src/dialogs/show-value-dialog/__snapshots__/show-value-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/show-value-dialog/__snapshots__/show-value-dialog.spec.tsx.snap
@@ -24,7 +24,7 @@ exports[`clipboard dialog matches snapshot 1`] = `
<h4
class="bp3-heading"
>
- Show value
+ Full value
</h4>
<button
aria-label="Close"
diff --git a/web-console/src/dialogs/show-value-dialog/show-value-dialog.scss b/web-console/src/dialogs/show-value-dialog/show-value-dialog.scss
index 1ef9e94..8527164 100644
--- a/web-console/src/dialogs/show-value-dialog/show-value-dialog.scss
+++ b/web-console/src/dialogs/show-value-dialog/show-value-dialog.scss
@@ -16,18 +16,17 @@
* limitations under the License.
*/
-.show-value-dialog{
- &.bp3-dialog{
+.show-value-dialog {
+ &.bp3-dialog {
padding-bottom: 10px;
}
- .bp3-input{
+ .bp3-input {
margin: 10px;
height: 400px;
}
- .bp3-dialog-footer-actions{
+ .bp3-dialog-footer-actions {
padding-right: 10px;
-
}
}
diff --git a/web-console/src/dialogs/show-value-dialog/show-value-dialog.spec.tsx b/web-console/src/dialogs/show-value-dialog/show-value-dialog.spec.tsx
index d8dc72d..2d41656 100644
--- a/web-console/src/dialogs/show-value-dialog/show-value-dialog.spec.tsx
+++ b/web-console/src/dialogs/show-value-dialog/show-value-dialog.spec.tsx
@@ -31,7 +31,7 @@ describe('clipboard dialog', () => {
}
/>
);
- const { container } = render(compactionDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ render(compactionDialog);
+ expect(document.body.lastChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/dialogs/show-value-dialog/show-value-dialog.tsx b/web-console/src/dialogs/show-value-dialog/show-value-dialog.tsx
index 4a92aa8..9aed8b0 100644
--- a/web-console/src/dialogs/show-value-dialog/show-value-dialog.tsx
+++ b/web-console/src/dialogs/show-value-dialog/show-value-dialog.tsx
@@ -15,16 +15,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Button, Classes, Dialog, IconName, Intent, TextArea } from '@blueprintjs/core';
+import { Button, Classes, Dialog, Intent, TextArea } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
-import copy = require('copy-to-clipboard');
+import copy from 'copy-to-clipboard';
import React from 'react';
import { AppToaster } from '../../singletons/toaster';
import './show-value-dialog.scss';
-export interface ShowValueDialogProps extends React.Props<any> {
+export interface ShowValueDialogProps {
onClose: () => void;
str: string;
}
@@ -39,23 +39,22 @@ export class ShowValueDialog extends React.PureComponent<ShowValueDialogProps> {
const { onClose, str } = this.props;
return (
- <Dialog className="show-value-dialog" isOpen onClose={onClose} title={'Show value'}>
+ <Dialog className="show-value-dialog" isOpen onClose={onClose} title="Full value">
<TextArea value={str} />
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
- <Button
- icon={IconNames.DUPLICATE}
- text={'Copy'}
- onClick={() => {
- copy(str, { format: 'text/plain' });
- AppToaster.show({
- message: 'Value copied to clipboard',
- intent: Intent.SUCCESS,
- });
- }}
- />
- <Button text={'Close'} intent={'primary'} onClick={onClose} />
+ <Button icon={IconNames.DUPLICATE} text={'Copy'} onClick={this.handleCopy} />
+ <Button text={'Close'} intent={Intent.PRIMARY} onClick={onClose} />
</div>
</Dialog>
);
}
+
+ private handleCopy = () => {
+ const { str } = this.props;
+ copy(str, { format: 'text/plain' });
+ AppToaster.show({
+ message: 'Value copied to clipboard',
+ intent: Intent.SUCCESS,
+ });
+ };
}
diff --git a/web-console/src/dialogs/snitch-dialog/snitch-dialog.spec.tsx b/web-console/src/dialogs/snitch-dialog/snitch-dialog.spec.tsx
index 09d073e..3ecfbf7 100644
--- a/web-console/src/dialogs/snitch-dialog/snitch-dialog.spec.tsx
+++ b/web-console/src/dialogs/snitch-dialog/snitch-dialog.spec.tsx
@@ -24,7 +24,7 @@ import { SnitchDialog } from './snitch-dialog';
describe('snitch dialog', () => {
it('matches snapshot', () => {
const snitchDialog = <SnitchDialog onSave={() => null} isOpen />;
- const { container } = render(snitchDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ render(snitchDialog);
+ expect(document.body.lastChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/dialogs/snitch-dialog/snitch-dialog.tsx b/web-console/src/dialogs/snitch-dialog/snitch-dialog.tsx
index 17f813d..742ad55 100644
--- a/web-console/src/dialogs/snitch-dialog/snitch-dialog.tsx
+++ b/web-console/src/dialogs/snitch-dialog/snitch-dialog.tsx
@@ -44,7 +44,6 @@ export interface SnitchDialogState {
comment: string;
showFinalStep?: boolean;
- saveDisabled?: boolean;
showHistory?: boolean;
}
@@ -55,7 +54,6 @@ export class SnitchDialog extends React.PureComponent<SnitchDialogProps, SnitchD
this.state = {
comment: '',
- saveDisabled: true,
};
}
@@ -68,11 +66,8 @@ export class SnitchDialog extends React.PureComponent<SnitchDialogProps, SnitchD
};
changeComment(newComment: string) {
- const { comment } = this.state;
-
this.setState({
comment: newComment,
- saveDisabled: !newComment,
});
}
@@ -95,15 +90,14 @@ export class SnitchDialog extends React.PureComponent<SnitchDialogProps, SnitchD
});
};
- goToHistory = () => {
+ handleGoToHistory = () => {
this.setState({
showHistory: true,
});
};
renderFinalStep() {
- const { onClose, children } = this.props;
- const { saveDisabled, comment } = this.state;
+ const { comment } = this.state;
return (
<Dialog {...this.props}>
@@ -117,14 +111,14 @@ export class SnitchDialog extends React.PureComponent<SnitchDialogProps, SnitchD
/>
</FormGroup>
</div>
-
- <div className={Classes.DIALOG_FOOTER}>{this.renderActions(saveDisabled)}</div>
+ <div className={Classes.DIALOG_FOOTER}>{this.renderActions(!comment)}</div>
</Dialog>
);
}
renderHistoryDialog() {
const { historyRecords } = this.props;
+ if (!historyRecords) return;
return (
<HistoryDialog {...this.props} className="history-dialog" historyRecords={historyRecords}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
@@ -142,8 +136,13 @@ export class SnitchDialog extends React.PureComponent<SnitchDialogProps, SnitchD
return (
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
- {showFinalStep || historyRecords === undefined ? null : (
- <Button className="left-align-button" minimal text="History" onClick={this.goToHistory} />
+ {!showFinalStep && historyRecords && (
+ <Button
+ className="left-align-button"
+ minimal
+ text="History"
+ onClick={this.handleGoToHistory}
+ />
)}
{showFinalStep ? (
@@ -178,7 +177,7 @@ export class SnitchDialog extends React.PureComponent<SnitchDialogProps, SnitchD
}
render() {
- const { onClose, className, children, saveDisabled } = this.props;
+ const { children, saveDisabled } = this.props;
const { showFinalStep, showHistory } = this.state;
if (showFinalStep) return this.renderFinalStep();
@@ -189,7 +188,6 @@ export class SnitchDialog extends React.PureComponent<SnitchDialogProps, SnitchD
return (
<Dialog isOpen {...propsClone}>
<div className={Classes.DIALOG_BODY}>{children}</div>
-
<div className={Classes.DIALOG_FOOTER}>{this.renderActions(saveDisabled)}</div>
</Dialog>
);
diff --git a/web-console/src/dialogs/spec-dialog/spec-dialog.spec.tsx b/web-console/src/dialogs/spec-dialog/spec-dialog.spec.tsx
index 35d7214..44e37d6 100644
--- a/web-console/src/dialogs/spec-dialog/spec-dialog.spec.tsx
+++ b/web-console/src/dialogs/spec-dialog/spec-dialog.spec.tsx
@@ -23,10 +23,8 @@ import { SpecDialog } from './spec-dialog';
describe('spec dialog', () => {
it('matches snapshot', () => {
- const specDialog = (
- <SpecDialog onSubmit={(spec: JSON) => null} onClose={() => null} title={'test'} />
- );
- const { container } = render(specDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ const specDialog = <SpecDialog onSubmit={() => null} onClose={() => null} title={'test'} />;
+ render(specDialog);
+ expect(document.body.lastChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/dialogs/spec-dialog/spec-dialog.tsx b/web-console/src/dialogs/spec-dialog/spec-dialog.tsx
index 827d1ea..5cee087 100644
--- a/web-console/src/dialogs/spec-dialog/spec-dialog.tsx
+++ b/web-console/src/dialogs/spec-dialog/spec-dialog.tsx
@@ -22,7 +22,7 @@ import AceEditor from 'react-ace';
import './spec-dialog.scss';
-export interface SpecDialogProps extends React.Props<any> {
+export interface SpecDialogProps {
onSubmit: (spec: JSON) => void;
onClose: () => void;
title: string;
@@ -84,10 +84,7 @@ export class SpecDialog extends React.PureComponent<SpecDialogProps, SpecDialogS
value={spec}
width="100%"
setOptions={{
- enableBasicAutocompletion: true,
- enableLiveAutocompletion: true,
showLineNumbers: true,
- enableSnippets: true,
tabSize: 2,
}}
style={{}}
diff --git a/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.spec.tsx b/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.spec.tsx
index dfe54ee..d5fcdd4 100644
--- a/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.spec.tsx
+++ b/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.spec.tsx
@@ -32,7 +32,7 @@ describe('supervisor table action dialog', () => {
isOpen
/>
);
- const { container } = render(supervisorTableActionDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ render(supervisorTableActionDialog);
+ expect(document.body.lastChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/dialogs/table-action-dialog/table-action-dialog.scss b/web-console/src/dialogs/table-action-dialog/table-action-dialog.scss
index ca89d1c..4af0a88 100644
--- a/web-console/src/dialogs/table-action-dialog/table-action-dialog.scss
+++ b/web-console/src/dialogs/table-action-dialog/table-action-dialog.scss
@@ -75,7 +75,7 @@ $side-bar-width: 120px;
left: $side-bar-width;
right: 0;
height: 100%;
- padding: 10px 20px;
+ padding: 10px 20px 15px 20px;
}
}
diff --git a/web-console/src/dialogs/table-action-dialog/table-action-dialog.spec.tsx b/web-console/src/dialogs/table-action-dialog/table-action-dialog.spec.tsx
index 33b939b..1476924 100644
--- a/web-console/src/dialogs/table-action-dialog/table-action-dialog.spec.tsx
+++ b/web-console/src/dialogs/table-action-dialog/table-action-dialog.spec.tsx
@@ -30,7 +30,7 @@ describe('table action dialog', () => {
isOpen
/>
);
- const { container } = render(tableActionDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ render(tableActionDialog);
+ expect(document.body.lastChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/dialogs/table-action-dialog/table-action-dialog.tsx b/web-console/src/dialogs/table-action-dialog/table-action-dialog.tsx
index 6795f97..b8cd596 100644
--- a/web-console/src/dialogs/table-action-dialog/table-action-dialog.tsx
+++ b/web-console/src/dialogs/table-action-dialog/table-action-dialog.tsx
@@ -47,11 +47,11 @@ export class TableActionDialog extends React.PureComponent<TableActionDialogProp
<Dialog className="table-action-dialog" isOpen={isOpen} onClose={onClose} title={title}>
<div className={Classes.DIALOG_BODY}>
<div className="side-bar">
- {sideButtonMetadata.map((d: SideButtonMetaData) => (
+ {sideButtonMetadata.map((d, i) => (
<Button
className="tab-button"
icon={<Icon icon={d.icon} iconSize={20} />}
- key={d.text}
+ key={i}
text={d.text}
intent={d.active ? Intent.PRIMARY : Intent.NONE}
minimal={!d.active}
diff --git a/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.spec.tsx b/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.spec.tsx
index 1b1d862..646b57d 100644
--- a/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.spec.tsx
+++ b/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.spec.tsx
@@ -33,7 +33,7 @@ describe('task table action dialog', () => {
isOpen
/>
);
- const { container } = render(taskTableActionDialog, { container: document.body });
- expect(container.firstChild).toMatchSnapshot();
+ render(taskTableActionDialog);
+ expect(document.body.lastChild).toMatchSnapshot();
});
});
diff --git a/web-console/src/utils/general.tsx b/web-console/src/utils/general.tsx
index 5480ef9..2e9aeac 100644
--- a/web-console/src/utils/general.tsx
+++ b/web-console/src/utils/general.tsx
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-import { Button, HTMLSelect, InputGroup, Intent } from '@blueprintjs/core';
+import { Button, HTMLSelect, InputGroup } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import FileSaver from 'file-saver';
import hasOwnProp from 'has-own-prop';
@@ -232,11 +232,6 @@ export function pluralIfNeeded(n: number, singular: string, plural?: string): st
return `${formatNumber(n)} ${n === 1 ? singular : plural}`;
}
-export function getHeadProp(results: Record<string, any>[], prop: string): any {
- if (!results || !results.length) return null;
- return results[0][prop] || null;
-}
-
// ----------------------------
export function parseJson(json: string): any {
@@ -274,10 +269,6 @@ export function parseStringToJSON(s: string): JSON | null {
}
}
-export function selectDefined<T, Q>(xs: (Q | null | undefined)[]): Q[] {
- return xs.filter(Boolean) as any;
-}
-
export function filterMap<T, Q>(xs: T[], f: (x: T, i?: number) => Q | null | undefined): Q[] {
return (xs.map(f) as any).filter(Boolean);
}
diff --git a/web-console/src/utils/ingestion-spec.tsx b/web-console/src/utils/ingestion-spec.tsx
index dc115cf..c31e12b 100644
--- a/web-console/src/utils/ingestion-spec.tsx
+++ b/web-console/src/utils/ingestion-spec.tsx
@@ -17,7 +17,6 @@
*/
import { Code } from '@blueprintjs/core';
-import { number } from 'prop-types';
import React from 'react';
import { Field } from '../components/auto-form/auto-form';
@@ -184,11 +183,12 @@ export interface ParseSpec {
export function hasParallelAbility(spec: IngestionSpec): boolean {
const specType = getSpecType(spec);
- return spec.type === 'index' || spec.type === 'index_parallel';
+ return specType === 'index' || specType === 'index_parallel';
}
export function isParallel(spec: IngestionSpec): boolean {
- return spec.type === 'index_parallel';
+ const specType = getSpecType(spec);
+ return specType === 'index_parallel';
}
export type DimensionMode = 'specific' | 'auto-detect';
@@ -1483,6 +1483,45 @@ const TUNING_CONFIG_FORM_FIELDS: Field<TuningConfig>[] = [
info: <>Used in determining when intermediate persists to disk should occur.</>,
},
{
+ name: 'indexSpec.bitmap.type',
+ label: 'Index bitmap type',
+ type: 'string',
+ defaultValue: 'concise',
+ suggestions: ['concise', 'roaring'],
+ info: <>Compression format for bitmap indexes.</>,
+ },
+ {
+ name: 'indexSpec.dimensionCompression',
+ label: 'Index dimension compression',
+ type: 'string',
+ defaultValue: 'lz4',
+ suggestions: ['lz4', 'lzf', 'uncompressed'],
+ info: <>Compression format for dimension columns.</>,
+ },
+ {
+ name: 'indexSpec.metricCompression',
+ label: 'Index metric compression',
+ type: 'string',
+ defaultValue: 'lz4',
+ suggestions: ['lz4', 'lzf', 'uncompressed'],
+ info: <>Compression format for metric columns.</>,
+ },
+ {
+ name: 'indexSpec.longEncoding',
+ label: 'Index long encoding',
+ type: 'string',
+ defaultValue: 'longs',
+ suggestions: ['longs', 'auto'],
+ info: (
+ <>
+ Encoding format for long-typed columns. Applies regardless of whether they are dimensions or
+ metrics. <Code>auto</Code> encodes the values using offset or lookup table depending on
+ column cardinality, and store them with variable size. <Code>longs</Code> stores the value
+ as-is with 8 bytes each.
+ </>
+ ),
+ },
+ {
name: 'intermediatePersistPeriod',
type: 'duration',
defaultValue: 'PT10M',
@@ -1514,10 +1553,6 @@ const TUNING_CONFIG_FORM_FIELDS: Field<TuningConfig>[] = [
),
},
{
- name: 'forceExtendableShardSpecs',
- type: 'boolean',
- },
- {
name: 'pushTimeout',
type: 'number',
defaultValue: 0,
diff --git a/web-console/src/utils/local-storage-backed-array.tsx b/web-console/src/utils/local-storage-backed-array.tsx
index daf227d..5383b4a 100644
--- a/web-console/src/utils/local-storage-backed-array.tsx
+++ b/web-console/src/utils/local-storage-backed-array.tsx
@@ -25,24 +25,21 @@ export class LocalStorageBackedArray<T> {
constructor(key: LocalStorageKeys, array?: T[]) {
this.key = key;
if (!Array.isArray(array)) {
- this.getDataFromStorage();
+ this.storedArray = this.getDataFromStorage();
} else {
this.storedArray = array;
this.setDataInStorage();
}
}
- private getDataFromStorage(): void {
- let possibleArray: any;
+ private getDataFromStorage(): T[] {
try {
- possibleArray = JSON.parse(String(localStorageGet(this.key)));
+ const possibleArray: any = JSON.parse(String(localStorageGet(this.key)));
+ if (!Array.isArray(possibleArray)) return [];
+ return possibleArray;
} catch {
- // show all columns by default
- possibleArray = [];
+ return [];
}
- if (!Array.isArray(possibleArray)) possibleArray = [];
-
- this.storedArray = possibleArray;
}
private setDataInStorage(): void {
diff --git a/web-console/src/utils/local-storage-keys.tsx b/web-console/src/utils/local-storage-keys.tsx
index cf70584..1aa50b7 100644
--- a/web-console/src/utils/local-storage-keys.tsx
+++ b/web-console/src/utils/local-storage-keys.tsx
@@ -32,6 +32,7 @@ export const LocalStorageKeys = {
SEGMENTS_REFRESH_RATE: 'segments-refresh-rate' as 'segments-refresh-rate',
SERVERS_REFRESH_RATE: 'servers-refresh-rate' as 'servers-refresh-rate',
SUPERVISORS_REFRESH_RATE: 'supervisors-refresh-rate' as 'supervisors-refresh-rate',
+ LOOKUPS_REFRESH_RATE: 'lookups-refresh-rate' as 'lookups-refresh-rate',
};
export type LocalStorageKeys = typeof LocalStorageKeys[keyof typeof LocalStorageKeys];
diff --git a/web-console/src/utils/query-manager.tsx b/web-console/src/utils/query-manager.tsx
index bf5e07a..8938067 100644
--- a/web-console/src/utils/query-manager.tsx
+++ b/web-console/src/utils/query-manager.tsx
@@ -36,8 +36,8 @@ export class QueryManager<Q, R> {
private onStateChange?: (queryResolve: QueryStateInt<R>) => void;
private terminated = false;
- private nextQuery: Q;
- private lastQuery: Q;
+ private nextQuery: Q | undefined;
+ private lastQuery: Q | undefined;
private actuallyLoading = false;
private state: QueryStateInt<R> = {
result: null,
@@ -73,6 +73,7 @@ export class QueryManager<Q, R> {
private run() {
this.lastQuery = this.nextQuery;
+ if (typeof this.lastQuery === 'undefined') return;
this.currentQueryId++;
const myQueryId = this.currentQueryId;
@@ -134,7 +135,7 @@ export class QueryManager<Q, R> {
}
}
- public getLastQuery(): Q {
+ public getLastQuery(): Q | undefined {
return this.lastQuery;
}
diff --git a/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap b/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap
index 64e34ba..dac1512 100755
--- a/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap
+++ b/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap
@@ -26,6 +26,7 @@ exports[`data source view matches snapshot 1`] = `
Array [
"Datasource",
"Availability",
+ "Segment load/drop",
"Retention",
"Compaction",
"Size",
@@ -112,6 +113,14 @@ exports[`data source view matches snapshot 1`] = `
},
Object {
"Cell": [Function],
+ "Header": "Segment load/drop",
+ "accessor": "num_segments_to_load",
+ "filterable": false,
+ "id": "load-drop",
+ "show": true,
+ },
+ Object {
+ "Cell": [Function],
"Header": "Retention",
"accessor": [Function],
"filterable": false,
diff --git a/web-console/src/views/datasource-view/datasource-view.spec.tsx b/web-console/src/views/datasource-view/datasource-view.spec.tsx
index cf78dbb..9bb0484 100644
--- a/web-console/src/views/datasource-view/datasource-view.spec.tsx
+++ b/web-console/src/views/datasource-view/datasource-view.spec.tsx
@@ -24,11 +24,7 @@ import { DatasourcesView } from './datasource-view';
describe('data source view', () => {
it('matches snapshot', () => {
const dataSourceView = shallow(
- <DatasourcesView
- goToQuery={(initSql: string) => {}}
- goToSegments={(datasource: string, onlyUnavailable?: boolean) => {}}
- noSqlMode={false}
- />,
+ <DatasourcesView goToQuery={() => {}} goToSegments={() => {}} noSqlMode={false} />,
);
expect(dataSourceView).toMatchSnapshot();
});
diff --git a/web-console/src/views/datasource-view/datasource-view.tsx b/web-console/src/views/datasource-view/datasource-view.tsx
index db14776..ab6f1b4 100644
--- a/web-console/src/views/datasource-view/datasource-view.tsx
+++ b/web-console/src/views/datasource-view/datasource-view.tsx
@@ -16,16 +16,7 @@
* limitations under the License.
*/
-import {
- Button,
- FormGroup,
- Icon,
- InputGroup,
- Intent,
- Popover,
- Position,
- Switch,
-} from '@blueprintjs/core';
+import { Button, FormGroup, InputGroup, Intent, Switch } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import React from 'react';
@@ -55,12 +46,14 @@ import {
} from '../../utils';
import { BasicAction } from '../../utils/basic-action';
import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array';
+import { deepGet } from '../../utils/object-change';
import './datasource-view.scss';
const tableColumns: string[] = [
'Datasource',
'Availability',
+ 'Segment load/drop',
'Retention',
'Compaction',
'Size',
@@ -70,13 +63,25 @@ const tableColumns: string[] = [
const tableColumnsNoSql: string[] = [
'Datasource',
'Availability',
+ 'Segment load/drop',
'Retention',
'Compaction',
'Size',
ActionCell.COLUMN_LABEL,
];
-export interface DatasourcesViewProps extends React.Props<any> {
+function formatLoadDrop(segmentsToLoad: number, segmentsToDrop: number): string {
+ const loadDrop: string[] = [];
+ if (segmentsToLoad) {
+ loadDrop.push(`${segmentsToLoad} segments to load`);
+ }
+ if (segmentsToDrop) {
+ loadDrop.push(`${segmentsToDrop} segments to drop`);
+ }
+ return loadDrop.join(', ') || 'No segments to load/drop';
+}
+
+export interface DatasourcesViewProps {
goToQuery: (initSql: string) => void;
goToSegments: (datasource: string, onlyUnavailable?: boolean) => void;
noSqlMode: boolean;
@@ -90,10 +95,12 @@ interface Datasource {
interface DatasourceQueryResultRow {
datasource: string;
- num_available_segments: number;
- num_rows: number;
num_segments: number;
+ num_available_segments: number;
+ num_segments_to_load: number;
+ num_segments_to_drop: number;
size: number;
+ num_rows: number;
}
export interface DatasourcesViewState {
@@ -124,6 +131,17 @@ export class DatasourcesView extends React.PureComponent<
static FULLY_AVAILABLE_COLOR = '#57d500';
static PARTIALLY_AVAILABLE_COLOR = '#ffbf00';
+ static DATASOURCE_SQL = `SELECT
+ datasource,
+ COUNT(*) FILTER (WHERE (is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AS num_segments,
+ COUNT(*) FILTER (WHERE is_available = 1 AND ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1)) AS num_available_segments,
+ COUNT(*) FILTER (WHERE is_published = 1 AND is_overshadowed = 0 AND is_available = 0) AS num_segments_to_load,
+ COUNT(*) FILTER (WHERE is_available = 1 AND NOT ((is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1)) AS num_segments_to_drop,
+ SUM("size") FILTER (WHERE (is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AS size,
+ SUM("num_rows") FILTER (WHERE (is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1) AS num_rows
+FROM sys.segments
+GROUP BY 1`;
+
static formatRules(rules: any[]): string {
if (rules.length === 0) {
return 'No rules';
@@ -135,7 +153,7 @@ export class DatasourcesView extends React.PureComponent<
}
private datasourceQueryManager: QueryManager<
- string,
+ boolean,
{ tiers: string[]; defaultRules: any[]; datasources: Datasource[] }
>;
@@ -162,30 +180,31 @@ export class DatasourcesView extends React.PureComponent<
LocalStorageKeys.DATASOURCE_TABLE_COLUMN_SELECTION,
),
};
- }
-
- componentDidMount(): void {
- const { noSqlMode } = this.props;
- const { hiddenColumns } = this.state;
this.datasourceQueryManager = new QueryManager({
- processQuery: async (query: string) => {
+ processQuery: async noSqlMode => {
let datasources: DatasourceQueryResultRow[];
if (!noSqlMode) {
- datasources = await queryDruidSql({ query });
+ datasources = await queryDruidSql({ query: DatasourcesView.DATASOURCE_SQL });
} else {
const datasourcesResp = await axios.get('/druid/coordinator/v1/datasources?simple');
const loadstatusResp = await axios.get('/druid/coordinator/v1/loadstatus?simple');
const loadstatus = loadstatusResp.data;
- datasources = datasourcesResp.data.map((d: any) => {
- return {
- datasource: d.name,
- num_available_segments: d.properties.segments.count,
- size: d.properties.segments.size,
- num_segments: d.properties.segments.count + loadstatus[d.name],
- num_rows: -1,
- };
- });
+ datasources = datasourcesResp.data.map(
+ (d: any): DatasourceQueryResultRow => {
+ const segmentsToLoad = Number(loadstatus[d.name] || 0);
+ const availableSegments = Number(deepGet(d, 'properties.segments.count'));
+ return {
+ datasource: d.name,
+ num_available_segments: availableSegments,
+ num_segments: availableSegments + segmentsToLoad,
+ num_segments_to_load: segmentsToLoad,
+ num_segments_to_drop: 0,
+ size: d.properties.segments.size,
+ num_rows: -1,
+ };
+ },
+ );
}
const seen = countBy(datasources, (x: any) => x.datasource);
@@ -234,15 +253,11 @@ export class DatasourcesView extends React.PureComponent<
});
},
});
+ }
- this.datasourceQueryManager.runQuery(`SELECT
- datasource,
- COUNT(*) AS num_segments,
- SUM(is_available) AS num_available_segments,
- SUM("size") AS size,
- SUM("num_rows") AS num_rows
-FROM sys.segments
-GROUP BY 1`);
+ componentDidMount(): void {
+ const { noSqlMode } = this.props;
+ this.datasourceQueryManager.runQuery(noSqlMode);
}
componentWillUnmount(): void {
@@ -581,7 +596,7 @@ GROUP BY 1`);
}
filterable
filtered={datasourcesFilter}
- onFilteredChange={(filtered, column) => {
+ onFilteredChange={filtered => {
this.setState({ datasourcesFilter: filtered });
}}
columns={[
@@ -670,6 +685,17 @@ GROUP BY 1`);
show: hiddenColumns.exists('Availability'),
},
{
+ Header: 'Segment load/drop',
+ id: 'load-drop',
+ accessor: 'num_segments_to_load',
+ filterable: false,
+ Cell: row => {
+ const { num_segments_to_load, num_segments_to_drop } = row.original;
+ return formatLoadDrop(num_segments_to_load, num_segments_to_drop);
+ },
+ show: hiddenColumns.exists('Segment load/drop'),
+ },
+ {
Header: 'Retention',
id: 'retention',
accessor: row => row.rules.length,
@@ -789,7 +815,7 @@ GROUP BY 1`);
<Button
icon={IconNames.APPLICATION}
text="Go to SQL"
- onClick={() => goToQuery(this.datasourceQueryManager.getLastQuery())}
+ onClick={() => goToQuery(DatasourcesView.DATASOURCE_SQL)}
/>
)}
<Switch
diff --git a/web-console/src/views/home-view/home-view.tsx b/web-console/src/views/home-view/home-view.tsx
index 44daa88..1babeb3 100644
--- a/web-console/src/views/home-view/home-view.tsx
+++ b/web-console/src/views/home-view/home-view.tsx
@@ -19,10 +19,12 @@
import { Card, H5, Icon } from '@blueprintjs/core';
import { IconName, IconNames } from '@blueprintjs/icons';
import axios from 'axios';
+import { sum } from 'd3-array';
import React from 'react';
import { UrlBaser } from '../../singletons/url-baser';
-import { getHeadProp, lookupBy, pluralIfNeeded, queryDruidSql, QueryManager } from '../../utils';
+import { lookupBy, pluralIfNeeded, queryDruidSql, QueryManager } from '../../utils';
+import { deepGet } from '../../utils/object-change';
import './home-view.scss';
@@ -35,7 +37,7 @@ export interface CardOptions {
error?: string | null;
}
-export interface HomeViewProps extends React.Props<any> {
+export interface HomeViewProps {
noSqlMode: boolean;
}
@@ -50,6 +52,7 @@ export interface HomeViewState {
segmentCountLoading: boolean;
segmentCount: number;
+ unavailableSegmentCount: number;
segmentCountError: string | null;
supervisorCountLoading: boolean;
@@ -77,12 +80,12 @@ export interface HomeViewState {
}
export class HomeView extends React.PureComponent<HomeViewProps, HomeViewState> {
- private statusQueryManager: QueryManager<string, any>;
- private datasourceQueryManager: QueryManager<string, any>;
- private segmentQueryManager: QueryManager<string, any>;
- private supervisorQueryManager: QueryManager<string, any>;
- private taskQueryManager: QueryManager<string, any>;
- private serverQueryManager: QueryManager<string, any>;
+ private statusQueryManager: QueryManager<null, any>;
+ private datasourceQueryManager: QueryManager<boolean, any>;
+ private segmentQueryManager: QueryManager<boolean, any>;
+ private supervisorQueryManager: QueryManager<null, any>;
+ private taskQueryManager: QueryManager<boolean, any>;
+ private serverQueryManager: QueryManager<boolean, any>;
constructor(props: HomeViewProps, context: any) {
super(props, context);
@@ -97,6 +100,7 @@ export class HomeView extends React.PureComponent<HomeViewProps, HomeViewState>
segmentCountLoading: false,
segmentCount: 0,
+ unavailableSegmentCount: 0,
segmentCountError: null,
supervisorCountLoading: false,
@@ -122,13 +126,9 @@ export class HomeView extends React.PureComponent<HomeViewProps, HomeViewState>
peonCount: 0,
serverCountError: null,
};
- }
-
- componentDidMount(): void {
- const { noSqlMode } = this.props;
this.statusQueryManager = new QueryManager({
- processQuery: async query => {
+ processQuery: async () => {
const statusResp = await axios.get('/status');
return statusResp.data;
},
@@ -141,15 +141,13 @@ export class HomeView extends React.PureComponent<HomeViewProps, HomeViewState>
},
});
- this.statusQueryManager.runQuery(`dummy`);
-
- // -------------------------
-
this.datasourceQueryManager = new QueryManager({
- processQuery: async query => {
+ processQuery: async noSqlMode => {
let datasources: string[];
if (!noSqlMode) {
- datasources = await queryDruidSql({ query });
+ datasources = await queryDruidSql({
+ query: `SELECT datasource FROM sys.segments GROUP BY 1`,
+ });
} else {
const datasourcesResp = await axios.get('/druid/coordinator/v1/datasources');
datasources = datasourcesResp.data;
@@ -165,45 +163,45 @@ export class HomeView extends React.PureComponent<HomeViewProps, HomeViewState>
},
});
- this.datasourceQueryManager.runQuery(`SELECT datasource FROM sys.segments GROUP BY 1`);
-
- // -------------------------
-
this.segmentQueryManager = new QueryManager({
- processQuery: async query => {
+ processQuery: async noSqlMode => {
if (noSqlMode) {
const loadstatusResp = await axios.get('/druid/coordinator/v1/loadstatus?simple');
const loadstatus = loadstatusResp.data;
- const unavailableSegmentNum = Object.keys(loadstatus).reduce((sum, key) => {
- return sum + loadstatus[key];
- }, 0);
+ const unavailableSegmentNum = sum(Object.keys(loadstatus), key => loadstatus[key]);
const datasourcesMetaResp = await axios.get('/druid/coordinator/v1/datasources?simple');
const datasourcesMeta = datasourcesMetaResp.data;
- const availableSegmentNum = datasourcesMeta.reduce((sum: number, curr: any) => {
- return sum + curr.properties.segments.count;
- }, 0);
+ const availableSegmentNum = sum(datasourcesMeta, (curr: any) =>
+ deepGet(curr, 'properties.segments.count'),
+ );
- return availableSegmentNum + unavailableSegmentNum;
+ return {
+ count: availableSegmentNum + unavailableSegmentNum,
+ unavailable: unavailableSegmentNum,
+ };
} else {
- const segments = await queryDruidSql({ query });
- return getHeadProp(segments, 'count') || 0;
+ const segments = await queryDruidSql({
+ query: `SELECT
+ COUNT(*) as "count",
+ COUNT(*) FILTER (WHERE is_available = 0) as "unavailable"
+FROM sys.segments`,
+ });
+ return segments.length === 1 ? segments[0] : null;
}
},
onStateChange: ({ result, loading, error }) => {
this.setState({
segmentCountLoading: loading,
- segmentCount: result,
+ segmentCount: result ? result.count : 0,
+ unavailableSegmentCount: result ? result.unavailable : 0,
segmentCountError: error,
});
},
});
- this.segmentQueryManager.runQuery(`SELECT COUNT(*) as "count" FROM sys.segments`);
-
- // -------------------------
this.supervisorQueryManager = new QueryManager({
- processQuery: async (query: string) => {
+ processQuery: async () => {
const resp = await axios.get('/druid/indexer/v1/supervisor?full');
const data = resp.data;
const runningSupervisorCount = data.filter((d: any) => d.spec.suspended === false).length;
@@ -223,12 +221,8 @@ export class HomeView extends React.PureComponent<HomeViewProps, HomeViewState>
},
});
- this.supervisorQueryManager.runQuery('dummy');
-
- // -------------------------
-
this.taskQueryManager = new QueryManager({
- processQuery: async query => {
+ processQuery: async noSqlMode => {
if (noSqlMode) {
const completeTasksResp = await axios.get('/druid/indexer/v1/completeTasks');
const runningTasksResp = await axios.get('/druid/indexer/v1/runningTasks');
@@ -243,7 +237,11 @@ export class HomeView extends React.PureComponent<HomeViewProps, HomeViewState>
};
} else {
const taskCountsFromQuery: { status: string; count: number }[] = await queryDruidSql({
- query,
+ query: `SELECT
+ CASE WHEN "status" = 'RUNNING' THEN "runner_status" ELSE "status" END AS "status",
+ COUNT (*) AS "count"
+FROM sys.tasks
+GROUP BY 1`,
});
return lookupBy(taskCountsFromQuery, x => x.status, x => x.count);
}
@@ -261,16 +259,8 @@ export class HomeView extends React.PureComponent<HomeViewProps, HomeViewState>
},
});
- this.taskQueryManager.runQuery(`SELECT
- CASE WHEN "status" = 'RUNNING' THEN "runner_status" ELSE "status" END AS "status",
- COUNT (*) AS "count"
-FROM sys.tasks
-GROUP BY 1`);
-
- // -------------------------
-
this.serverQueryManager = new QueryManager({
- processQuery: async query => {
+ processQuery: async noSqlMode => {
if (noSqlMode) {
const serversResp = await axios.get('/druid/coordinator/v1/servers?simple');
const middleManagerResp = await axios.get('/druid/indexer/v1/workers');
@@ -283,7 +273,9 @@ GROUP BY 1`);
const serverCountsFromQuery: {
server_type: string;
count: number;
- }[] = await queryDruidSql({ query });
+ }[] = await queryDruidSql({
+ query: `SELECT server_type, COUNT(*) as "count" FROM sys.servers GROUP BY 1`,
+ });
return lookupBy(serverCountsFromQuery, x => x.server_type, x => x.count);
}
},
@@ -301,10 +293,17 @@ GROUP BY 1`);
});
},
});
+ }
- this.serverQueryManager.runQuery(
- `SELECT server_type, COUNT(*) as "count" FROM sys.servers GROUP BY 1`,
- );
+ componentDidMount(): void {
+ const { noSqlMode } = this.props;
+
+ this.statusQueryManager.runQuery(null);
+ this.datasourceQueryManager.runQuery(noSqlMode);
+ this.segmentQueryManager.runQuery(noSqlMode);
+ this.supervisorQueryManager.runQuery(null);
+ this.taskQueryManager.runQuery(noSqlMode);
+ this.serverQueryManager.runQuery(noSqlMode);
}
componentWillUnmount(): void {
@@ -362,7 +361,14 @@ GROUP BY 1`);
icon: IconNames.STACKED_CHART,
title: 'Segments',
loading: state.segmentCountLoading,
- content: pluralIfNeeded(state.segmentCount, 'segment'),
+ content: (
+ <>
+ <p>{pluralIfNeeded(state.segmentCount, 'segment')}</p>
+ {Boolean(state.unavailableSegmentCount) && (
+ <p>{pluralIfNeeded(state.unavailableSegmentCount, 'unavailable segment')}</p>
+ )}
+ </>
+ ),
error: state.datasourceCountError,
})}
{this.renderCard({
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 9718f80..c7e8108 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
@@ -153,7 +153,7 @@ exports[`load data view matches snapshot 1`] = `
</div>
</div>
<div
- className="main"
+ className="main bp3-input"
/>
<div
className="control"
diff --git a/web-console/src/views/load-data-view/filter-table/filter-table.tsx b/web-console/src/views/load-data-view/filter-table/filter-table.tsx
index eacd9b5..25756f7 100644
--- a/web-console/src/views/load-data-view/filter-table/filter-table.tsx
+++ b/web-console/src/views/load-data-view/filter-table/filter-table.tsx
@@ -22,12 +22,12 @@ import ReactTable from 'react-table';
import { TableCell } from '../../../components';
import { caseInsensitiveContains, filterMap } from '../../../utils';
-import { DruidFilter, Transform } from '../../../utils/ingestion-spec';
-import { HeaderAndRows } from '../../../utils/sampler';
+import { DruidFilter } from '../../../utils/ingestion-spec';
+import { HeaderAndRows, SampleEntry } from '../../../utils/sampler';
import './filter-table.scss';
-export interface FilterTableProps extends React.Props<any> {
+export interface FilterTableProps {
sampleData: HeaderAndRows;
columnFilter: string;
dimensionFilters: DruidFilter[];
@@ -82,7 +82,7 @@ export class FilterTable extends React.PureComponent<FilterTableProps> {
headerClassName: columnClassName,
className: columnClassName,
id: String(i),
- accessor: row => (row.parsed ? row.parsed[columnName] : null),
+ accessor: (row: SampleEntry) => (row.parsed ? row.parsed[columnName] : null),
Cell: row => <TableCell value={row.value} timestamp={timestamp} />,
};
})}
diff --git a/web-console/src/views/load-data-view/load-data-view.scss b/web-console/src/views/load-data-view/load-data-view.scss
index 847bea4..b94e892 100644
--- a/web-console/src/views/load-data-view/load-data-view.scss
+++ b/web-console/src/views/load-data-view/load-data-view.scss
@@ -19,7 +19,7 @@
.load-data-view {
height: 100%;
display: grid;
- grid-gap: 10px 5px;
+ grid-gap: 15px 5px;
grid-template-columns: 1fr 280px;
grid-template-rows: 55px 1fr 28px;
grid-template-areas:
@@ -29,7 +29,8 @@
&.welcome {
.main {
- margin-left: -10px;
+ height: 100%;
+ padding: 0;
.bp3-card {
position: relative;
@@ -37,8 +38,8 @@
vertical-align: top;
width: 250px;
height: 140px;
- margin-left: 15px;
- margin-bottom: 15px;
+ margin-top: 10px;
+ margin-left: 10px;
font-size: 16px;
text-align: center;
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 399a7a4..7ff02da 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
@@ -23,7 +23,7 @@ import { LoadDataView } from './load-data-view';
describe('load data view', () => {
it('matches snapshot', () => {
- const loadDataView = shallow(<LoadDataView goToTask={(taskId: string | null) => {}} />);
+ const loadDataView = shallow(<LoadDataView goToTask={() => {}} />);
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 38f24e6..85200b7 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
@@ -25,8 +25,6 @@ import {
Card,
Classes,
Code,
- Dialog,
- Elevation,
FormGroup,
H5,
HTMLSelect,
@@ -221,10 +219,10 @@ const VIEW_TITLE: Record<Step, string> = {
loading: 'Loading',
};
-export interface LoadDataViewProps extends React.Props<any> {
+export interface LoadDataViewProps {
initSupervisorId?: string | null;
initTaskId?: string | null;
- goToTask: (taskId: string | null, supervisor?: string) => void;
+ goToTask: (taskId: string | undefined, supervisor?: string) => void;
}
export interface LoadDataViewState {
@@ -524,7 +522,7 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
return (
<>
- <div className="main">
+ <div className="main bp3-input">
{this.renderIngestionCard('kafka')}
{this.renderIngestionCard('kinesis')}
{this.renderIngestionCard('index:static-s3')}
@@ -679,7 +677,7 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
<Button
text="Submit task"
rightIcon={IconNames.ARROW_RIGHT}
- onClick={() => goToTask(null, 'task')}
+ onClick={() => goToTask(undefined, 'task')}
intent={Intent.PRIMARY}
/>
</FormGroup>
@@ -695,7 +693,7 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
<Button
text="Submit supervisor"
rightIcon={IconNames.ARROW_RIGHT}
- onClick={() => goToTask(null, 'supervisor')}
+ onClick={() => goToTask(undefined, 'supervisor')}
intent={Intent.PRIMARY}
/>
</FormGroup>
@@ -703,7 +701,7 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
<Button
text="Submit task"
rightIcon={IconNames.ARROW_RIGHT}
- onClick={() => goToTask(null, 'task')}
+ onClick={() => goToTask(undefined, 'task')}
intent={Intent.PRIMARY}
/>
</FormGroup>
@@ -1602,15 +1600,44 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
return;
}
+ if (sampleResponse.data.length) {
+ this.setState({
+ cacheKey: sampleResponse.cacheKey,
+ filterQueryState: new QueryState({
+ data: headerAndRowsFromSampleResponse(
+ sampleResponse,
+ undefined,
+ ['__time'].concat(parserColumns),
+ true,
+ ),
+ }),
+ });
+ return;
+ }
+
+ // The filters matched no data
+ let sampleResponseNoFilter: SampleResponse;
+ try {
+ const specNoFilter = deepSet(spec, 'dataSchema.transformSpec.filter', null);
+ sampleResponseNoFilter = await sampleForFilter(specNoFilter, sampleStrategy, cacheKey);
+ } catch (e) {
+ this.setState({
+ filterQueryState: new QueryState({ error: e.message }),
+ });
+ return;
+ }
+
+ const headerAndRowsNoFilter = headerAndRowsFromSampleResponse(
+ sampleResponseNoFilter,
+ undefined,
+ ['__time'].concat(parserColumns),
+ true,
+ );
+
this.setState({
- cacheKey: sampleResponse.cacheKey,
+ cacheKey: sampleResponseNoFilter.cacheKey,
filterQueryState: new QueryState({
- data: headerAndRowsFromSampleResponse(
- sampleResponse,
- undefined,
- ['__time'].concat(parserColumns),
- true,
- ),
+ data: deepSet(headerAndRowsNoFilter, 'rows', []),
}),
});
}
@@ -2684,7 +2711,7 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
});
setTimeout(() => {
- goToTask(null);
+ goToTask(undefined); // Can we get the supervisor ID here?
}, 1000);
}
}}
diff --git a/web-console/src/views/load-data-view/parse-data-table/parse-data-table.tsx b/web-console/src/views/load-data-view/parse-data-table/parse-data-table.tsx
index 0cee551..b30d08b 100644
--- a/web-console/src/views/load-data-view/parse-data-table/parse-data-table.tsx
+++ b/web-console/src/views/load-data-view/parse-data-table/parse-data-table.tsx
@@ -27,7 +27,7 @@ import { HeaderAndRows, SampleEntry } from '../../../utils/sampler';
import './parse-data-table.scss';
-export interface ParseDataTableProps extends React.Props<any> {
+export interface ParseDataTableProps {
sampleData: HeaderAndRows;
columnFilter: string;
canFlatten: boolean;
diff --git a/web-console/src/views/load-data-view/parse-time-table/parse-time-table.scss b/web-console/src/views/load-data-view/parse-time-table/parse-time-table.scss
index f9910e1..3122c37 100644
--- a/web-console/src/views/load-data-view/parse-time-table/parse-time-table.scss
+++ b/web-console/src/views/load-data-view/parse-time-table/parse-time-table.scss
@@ -17,4 +17,5 @@
*/
.parse-time-table {
+ position: relative;
}
diff --git a/web-console/src/views/load-data-view/parse-time-table/parse-time-table.tsx b/web-console/src/views/load-data-view/parse-time-table/parse-time-table.tsx
index 7bdb79e..4a8c3e1 100644
--- a/web-console/src/views/load-data-view/parse-time-table/parse-time-table.tsx
+++ b/web-console/src/views/load-data-view/parse-time-table/parse-time-table.tsx
@@ -32,7 +32,7 @@ import { HeaderAndRows, SampleEntry } from '../../../utils/sampler';
import './parse-time-table.scss';
-export interface ParseTimeTableProps extends React.Props<any> {
+export interface ParseTimeTableProps {
sampleBundle: {
headerAndRows: HeaderAndRows;
timestampSpec: TimestampSpec;
diff --git a/web-console/src/views/load-data-view/schema-table/schema-table.tsx b/web-console/src/views/load-data-view/schema-table/schema-table.tsx
index 554fff7..aac4e56 100644
--- a/web-console/src/views/load-data-view/schema-table/schema-table.tsx
+++ b/web-console/src/views/load-data-view/schema-table/schema-table.tsx
@@ -21,12 +21,7 @@ import React from 'react';
import ReactTable from 'react-table';
import { TableCell } from '../../../components';
-import {
- alphanumericCompare,
- caseInsensitiveContains,
- filterMap,
- sortWithPrefixSuffix,
-} from '../../../utils';
+import { caseInsensitiveContains, filterMap, sortWithPrefixSuffix } from '../../../utils';
import {
DimensionSpec,
DimensionsSpec,
@@ -35,13 +30,12 @@ import {
getMetricSpecName,
inflateDimensionSpec,
MetricSpec,
- TimestampSpec,
} from '../../../utils/ingestion-spec';
import { HeaderAndRows, SampleEntry } from '../../../utils/sampler';
import './schema-table.scss';
-export interface SchemaTableProps extends React.Props<any> {
+export interface SchemaTableProps {
sampleBundle: {
headerAndRows: HeaderAndRows;
dimensionsSpec: DimensionsSpec;
@@ -103,7 +97,7 @@ export class SchemaTable extends React.PureComponent<SchemaTableProps> {
headerClassName: columnClassName,
className: columnClassName,
id: String(i),
- accessor: row => (row.parsed ? row.parsed[columnName] : null),
+ accessor: (row: SampleEntry) => (row.parsed ? row.parsed[columnName] : null),
Cell: row => <TableCell value={row.value} />,
};
} else {
diff --git a/web-console/src/views/load-data-view/transform-table/transform-table.tsx b/web-console/src/views/load-data-view/transform-table/transform-table.tsx
index 83b7ed5..36e721e 100644
--- a/web-console/src/views/load-data-view/transform-table/transform-table.tsx
+++ b/web-console/src/views/load-data-view/transform-table/transform-table.tsx
@@ -24,11 +24,11 @@ import { TableCell } from '../../../components';
import { caseInsensitiveContains, filterMap } from '../../../utils';
import { escapeColumnName } from '../../../utils/druid-expression';
import { Transform } from '../../../utils/ingestion-spec';
-import { HeaderAndRows } from '../../../utils/sampler';
+import { HeaderAndRows, SampleEntry } from '../../../utils/sampler';
import './transform-table.scss';
-export interface TransformTableProps extends React.Props<any> {
+export interface TransformTableProps {
sampleData: HeaderAndRows;
columnFilter: string;
transformedColumnsOnly: boolean;
@@ -91,7 +91,7 @@ export class TransformTable extends React.PureComponent<TransformTableProps> {
headerClassName: columnClassName,
className: columnClassName,
id: String(i),
- accessor: row => (row.parsed ? row.parsed[columnName] : null),
+ accessor: (row: SampleEntry) => (row.parsed ? row.parsed[columnName] : null),
Cell: row => <TableCell value={row.value} timestamp={timestamp} />,
};
})}
diff --git a/web-console/src/views/lookups-view/__snapshots__/lookups-view.spec.tsx.snap b/web-console/src/views/lookups-view/__snapshots__/lookups-view.spec.tsx.snap
index a4a93cb..228fed1 100755
--- a/web-console/src/views/lookups-view/__snapshots__/lookups-view.spec.tsx.snap
+++ b/web-console/src/views/lookups-view/__snapshots__/lookups-view.spec.tsx.snap
@@ -7,10 +7,9 @@ exports[`lookups view matches snapshot 1`] = `
<ViewControlBar
label="Lookups"
>
- <Blueprint3.Button
- icon="refresh"
- onClick={[Function]}
- text="Refresh"
+ <RefreshButton
+ localStorageKey="lookups-refresh-rate"
+ onRefresh={[Function]}
/>
<Blueprint3.Button
icon="plus"
@@ -90,28 +89,28 @@ exports[`lookups view matches snapshot 1`] = `
Array [
Object {
"Header": "Lookup name",
- "accessor": [Function],
+ "accessor": "id",
"filterable": true,
"id": "lookup_name",
"show": true,
},
Object {
"Header": "Tier",
- "accessor": [Function],
+ "accessor": "tier",
"filterable": true,
"id": "tier",
"show": true,
},
Object {
"Header": "Type",
- "accessor": [Function],
+ "accessor": "spec.type",
"filterable": true,
"id": "type",
"show": true,
},
Object {
"Header": "Version",
- "accessor": [Function],
+ "accessor": "version",
"filterable": true,
"id": "version",
"show": true,
diff --git a/web-console/src/views/lookups-view/lookups-view.tsx b/web-console/src/views/lookups-view/lookups-view.tsx
index 182ea93..a792cbb 100644
--- a/web-console/src/views/lookups-view/lookups-view.tsx
+++ b/web-console/src/views/lookups-view/lookups-view.tsx
@@ -16,18 +16,17 @@
* limitations under the License.
*/
-import { Button, Icon, Intent, Popover, Position } from '@blueprintjs/core';
+import { Button, Intent } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
-import classNames from 'classnames';
import React from 'react';
import ReactTable from 'react-table';
-import { ActionCell, TableColumnSelector, ViewControlBar } from '../../components';
+import { ActionCell, RefreshButton, TableColumnSelector, ViewControlBar } from '../../components';
import { AsyncActionDialog, LookupEditDialog } from '../../dialogs/';
import { AppToaster } from '../../singletons/toaster';
import { getDruidErrorMessage, LocalStorageKeys, QueryManager } from '../../utils';
-import { BasicAction, basicActionsToMenu } from '../../utils/basic-action';
+import { BasicAction } from '../../utils/basic-action';
import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array';
import './lookups-view.scss';
@@ -36,7 +35,7 @@ const tableColumns: string[] = ['Lookup name', 'Tier', 'Type', 'Version', Action
const DEFAULT_LOOKUP_TIER: string = '__default';
-export interface LookupsViewProps extends React.Props<any> {}
+export interface LookupsViewProps {}
export interface LookupsViewState {
lookups: {}[] | null;
@@ -58,7 +57,7 @@ export interface LookupsViewState {
}
export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsViewState> {
- private lookupsGetQueryManager: QueryManager<string, { lookupEntries: any[]; tiers: string[] }>;
+ private lookupsQueryManager: QueryManager<null, { lookupEntries: any[]; tiers: string[] }>;
constructor(props: LookupsViewProps, context: any) {
super(props, context);
@@ -82,11 +81,9 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
LocalStorageKeys.LOOKUP_TABLE_COLUMN_SELECTION,
),
};
- }
- componentDidMount(): void {
- this.lookupsGetQueryManager = new QueryManager({
- processQuery: async (query: string) => {
+ this.lookupsQueryManager = new QueryManager({
+ processQuery: async () => {
const tiersResp = await axios.get('/druid/coordinator/v1/lookups/config?discover=true');
const tiers =
tiersResp.data && tiersResp.data.length > 0 ? tiersResp.data : [DEFAULT_LOOKUP_TIER];
@@ -121,18 +118,20 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
});
},
});
+ }
- this.lookupsGetQueryManager.runQuery('dummy');
+ componentDidMount(): void {
+ this.lookupsQueryManager.runQuery(null);
}
componentWillUnmount(): void {
- this.lookupsGetQueryManager.terminate();
+ this.lookupsQueryManager.terminate();
}
private async initializeLookup() {
try {
await axios.post(`/druid/coordinator/v1/lookups/config`, {});
- this.lookupsGetQueryManager.rerunLastQuery();
+ this.lookupsQueryManager.rerunLastQuery();
} catch (e) {
AppToaster.show({
icon: IconNames.ERROR,
@@ -208,7 +207,7 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
this.setState({
lookupEditDialogOpen: false,
});
- this.lookupsGetQueryManager.rerunLastQuery();
+ this.lookupsQueryManager.rerunLastQuery();
} catch (e) {
AppToaster.show({
icon: IconNames.ERROR,
@@ -254,7 +253,7 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
intent={Intent.DANGER}
onClose={success => {
this.setState({ deleteLookupTier: null, deleteLookupName: null });
- if (success) this.lookupsGetQueryManager.rerunLastQuery();
+ if (success) this.lookupsQueryManager.rerunLastQuery();
}}
>
<p>{`Are you sure you want to delete the lookup '${deleteLookupName}'?`}</p>
@@ -296,28 +295,28 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
{
Header: 'Lookup name',
id: 'lookup_name',
- accessor: (row: any) => row.id,
+ accessor: 'id',
filterable: true,
show: hiddenColumns.exists('Lookup name'),
},
{
Header: 'Tier',
id: 'tier',
- accessor: (row: any) => row.tier,
+ accessor: 'tier',
filterable: true,
show: hiddenColumns.exists('Tier'),
},
{
Header: 'Type',
id: 'type',
- accessor: (row: any) => row.spec.type,
+ accessor: 'spec.type',
filterable: true,
show: hiddenColumns.exists('Type'),
},
{
Header: 'Version',
id: 'version',
- accessor: (row: any) => row.version,
+ accessor: 'version',
filterable: true,
show: hiddenColumns.exists('Version'),
},
@@ -325,7 +324,7 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
Header: ActionCell.COLUMN_LABEL,
id: ActionCell.COLUMN_ID,
width: ActionCell.COLUMN_WIDTH,
- accessor: row => ({ id: row.id, tier: row.tier }),
+ accessor: (row: any) => ({ id: row.id, tier: row.tier }),
filterable: false,
Cell: (row: any) => {
const lookupId = row.value.id;
@@ -375,10 +374,9 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
return (
<div className="lookups-view app-view">
<ViewControlBar label="Lookups">
- <Button
- icon={IconNames.REFRESH}
- text="Refresh"
- onClick={() => this.lookupsGetQueryManager.rerunLastQuery()}
+ <RefreshButton
+ onRefresh={() => this.lookupsQueryManager.rerunLastQuery()}
+ localStorageKey={LocalStorageKeys.LOOKUPS_REFRESH_RATE}
/>
{!lookupsError && (
<Button
diff --git a/web-console/src/views/query-view/column-tree/column-tree.tsx b/web-console/src/views/query-view/column-tree/column-tree.tsx
index c540026..f95708a 100644
--- a/web-console/src/views/query-view/column-tree/column-tree.tsx
+++ b/web-console/src/views/query-view/column-tree/column-tree.tsx
@@ -26,7 +26,7 @@ import { ColumnMetadata } from '../../../utils/column-metadata';
import './column-tree.scss';
-export interface ColumnTreeProps extends React.Props<any> {
+export interface ColumnTreeProps {
columnMetadataLoading: boolean;
columnMetadata: ColumnMetadata[] | null;
onQueryStringChange: (queryString: string) => void;
@@ -150,11 +150,7 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
);
}
- private handleNodeClick = (
- nodeData: ITreeNode,
- nodePath: number[],
- e: React.MouseEvent<HTMLElement>,
- ) => {
+ private handleNodeClick = (nodeData: ITreeNode, nodePath: number[]) => {
const { onQueryStringChange } = this.props;
const { columnTree, selectedTreeIndex } = this.state;
if (!columnTree) return;
diff --git a/web-console/src/views/query-view/query-extra-info/query-extra-info.tsx b/web-console/src/views/query-view/query-extra-info/query-extra-info.tsx
index d682195..d4dbbd6 100644
--- a/web-console/src/views/query-view/query-extra-info/query-extra-info.tsx
+++ b/web-console/src/views/query-view/query-extra-info/query-extra-info.tsx
@@ -44,7 +44,7 @@ export interface QueryExtraInfoData {
wrappedLimit?: number;
}
-export interface QueryExtraInfoProps extends React.Props<any> {
+export interface QueryExtraInfoProps {
queryExtraInfo: QueryExtraInfoData;
onDownload: (filename: string, format: string) => void;
}
diff --git a/web-console/src/views/query-view/query-input/query-input.tsx b/web-console/src/views/query-view/query-input/query-input.tsx
index 407b1c4..6df25e8 100644
--- a/web-console/src/views/query-view/query-input/query-input.tsx
+++ b/web-console/src/views/query-view/query-input/query-input.tsx
@@ -33,7 +33,7 @@ import './query-input.scss';
const langTools = ace.acequire('ace/ext/language_tools');
-export interface QueryInputProps extends React.Props<any> {
+export interface QueryInputProps {
queryString: string;
onQueryStringChange: (newQueryString: string) => void;
runeMode: boolean;
@@ -71,7 +71,7 @@ export class QueryInput extends React.PureComponent<QueryInputProps, QueryInputS
);
langTools.addCompleter({
- getCompletions: (editor: any, session: any, pos: any, prefix: any, callback: any) => {
+ getCompletions: (_editor: any, _session: any, _pos: any, _prefix: any, callback: any) => {
callback(null, completions);
},
});
@@ -103,7 +103,7 @@ export class QueryInput extends React.PureComponent<QueryInputProps, QueryInputS
);
const keywordCompleter = {
- getCompletions: (editor: any, session: any, pos: any, prefix: any, callback: any) => {
+ getCompletions: (_editor: any, _session: any, _pos: any, _prefix: any, callback: any) => {
return callback(null, keywordList);
},
};
@@ -137,7 +137,7 @@ export class QueryInput extends React.PureComponent<QueryInputProps, QueryInputS
});
langTools.addCompleter({
- getCompletions: (editor: any, session: any, pos: any, prefix: any, callback: any) => {
+ getCompletions: (_editor: any, _session: any, _pos: any, _prefix: any, callback: any) => {
callback(null, functionList);
},
getDocTooltip: (item: any) => {
diff --git a/web-console/src/views/query-view/query-output/query-output.tsx b/web-console/src/views/query-view/query-output/query-output.tsx
index ad3fdbc..e35856a 100644
--- a/web-console/src/views/query-view/query-output/query-output.tsx
+++ b/web-console/src/views/query-view/query-output/query-output.tsx
@@ -24,7 +24,7 @@ import { HeaderRows } from '../../../utils';
import './query-output.scss';
-export interface QueryOutputProps extends React.Props<any> {
+export interface QueryOutputProps {
loading: boolean;
result: HeaderRows | null;
error: string | null;
diff --git a/web-console/src/views/query-view/query-view.tsx b/web-console/src/views/query-view/query-view.tsx
index 3e16b84..6395409 100644
--- a/web-console/src/views/query-view/query-view.tsx
+++ b/web-console/src/views/query-view/query-view.tsx
@@ -56,8 +56,8 @@ interface QueryWithContext {
wrapQuery?: boolean;
}
-export interface QueryViewProps extends React.Props<any> {
- initQuery: string | null;
+export interface QueryViewProps {
+ initQuery: string | undefined;
}
export interface QueryViewState {
@@ -119,7 +119,7 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
}
}
- private metadataQueryManager: QueryManager<string, ColumnMetadata[]>;
+ private metadataQueryManager: QueryManager<null, ColumnMetadata[]>;
private sqlQueryManager: QueryManager<QueryWithContext, QueryResult>;
private explainQueryManager: QueryManager<
QueryWithContext,
@@ -146,11 +146,9 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
explainResult: null,
explainError: null,
};
- }
- componentDidMount(): void {
this.metadataQueryManager = new QueryManager({
- processQuery: async (query: string) => {
+ processQuery: async () => {
return await queryDruidSql<ColumnMetadata>({
query: `SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS`,
});
@@ -170,10 +168,8 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
},
});
- this.metadataQueryManager.runQuery('dummy');
-
this.sqlQueryManager = new QueryManager({
- processQuery: async (queryWithContext: QueryWithContext) => {
+ processQuery: async (queryWithContext: QueryWithContext): Promise<QueryResult> => {
const { queryString, queryContext, wrapQuery } = queryWithContext;
let queryId: string | null = null;
let sqlQueryId: string | null = null;
@@ -274,6 +270,10 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
});
}
+ componentDidMount(): void {
+ this.metadataQueryManager.runQuery(null);
+ }
+
componentWillUnmount(): void {
this.metadataQueryManager.terminate();
this.sqlQueryManager.terminate();
diff --git a/web-console/src/views/query-view/run-button/run-button.tsx b/web-console/src/views/query-view/run-button/run-button.tsx
index d750614..f987c13 100644
--- a/web-console/src/views/query-view/run-button/run-button.tsx
+++ b/web-console/src/views/query-view/run-button/run-button.tsx
@@ -43,7 +43,7 @@ import {
} from '../../../utils/query-context';
import { DRUID_DOCS_RUNE, DRUID_DOCS_SQL } from '../../../variables';
-export interface RunButtonProps extends React.Props<any> {
+export interface RunButtonProps {
runeMode: boolean;
queryContext: QueryContext;
onQueryContextChange: (newQueryContext: QueryContext) => void;
diff --git a/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap b/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap
index 9be7408..efd5139 100755
--- a/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap
+++ b/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap
@@ -13,7 +13,7 @@ exports[`segments-view matches snapshot 1`] = `
onRefresh={[Function]}
/>
<Blueprint3.Button
- hidden={false}
+ disabled={true}
icon="application"
onClick={[Function]}
text="Go to SQL"
diff --git a/web-console/src/views/segments-view/segments-view.spec.tsx b/web-console/src/views/segments-view/segments-view.spec.tsx
index 464ce2e..fc8b4d8 100644
--- a/web-console/src/views/segments-view/segments-view.spec.tsx
+++ b/web-console/src/views/segments-view/segments-view.spec.tsx
@@ -27,7 +27,7 @@ describe('segments-view', () => {
<SegmentsView
datasource={'test'}
onlyUnavailable={false}
- goToQuery={(initSql: string) => {}}
+ goToQuery={() => {}}
noSqlMode={false}
/>,
);
diff --git a/web-console/src/views/segments-view/segments-view.tsx b/web-console/src/views/segments-view/segments-view.tsx
index 5c0b397..8be5c05 100644
--- a/web-console/src/views/segments-view/segments-view.tsx
+++ b/web-console/src/views/segments-view/segments-view.tsx
@@ -32,7 +32,6 @@ import {
formatNumber,
LocalStorageKeys,
makeBooleanFilter,
- parseList,
queryDruidSql,
QueryManager,
sqlQueryCustomTableFilter,
@@ -68,10 +67,10 @@ const tableColumnsNoSql: string[] = [
'Size',
];
-export interface SegmentsViewProps extends React.Props<any> {
+export interface SegmentsViewProps {
goToQuery: (initSql: string) => void;
- datasource: string | null;
- onlyUnavailable: boolean | null;
+ datasource: string | undefined;
+ onlyUnavailable: boolean | undefined;
noSqlMode: boolean;
}
@@ -113,7 +112,7 @@ interface SegmentQueryResultRow {
export class SegmentsView extends React.PureComponent<SegmentsViewProps, SegmentsViewState> {
private segmentsSqlQueryManager: QueryManager<QueryAndSkip, SegmentQueryResultRow[]>;
- private segmentsJsonQueryManager: QueryManager<any, SegmentQueryResultRow[]>;
+ private segmentsNoSqlQueryManager: QueryManager<null, SegmentQueryResultRow[]>;
constructor(props: SegmentsViewProps, context: any) {
super(props, context);
@@ -158,8 +157,8 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
},
});
- this.segmentsJsonQueryManager = new QueryManager({
- processQuery: async (query: any) => {
+ this.segmentsNoSqlQueryManager = new QueryManager({
+ processQuery: async () => {
const datasourceList = (await axios.get('/druid/coordinator/v1/metadata/datasources')).data;
const nestedResults: SegmentQueryResultRow[][] = await Promise.all(
datasourceList.map(async (d: string) => {
@@ -185,7 +184,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
});
}),
);
- const results: SegmentQueryResultRow[] = [].concat
+ const results: SegmentQueryResultRow[] = ([] as SegmentQueryResultRow[]).concat
.apply([], nestedResults)
.sort((d1: any, d2: any) => {
return d2.start.localeCompare(d1.start);
@@ -205,16 +204,16 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
componentDidMount(): void {
if (this.props.noSqlMode) {
- this.segmentsJsonQueryManager.runQuery('init');
+ this.segmentsNoSqlQueryManager.runQuery(null);
}
}
componentWillUnmount(): void {
this.segmentsSqlQueryManager.terminate();
- this.segmentsJsonQueryManager.terminate();
+ this.segmentsNoSqlQueryManager.terminate();
}
- private fetchData = (state: any, instance: any) => {
+ private fetchData = (state: any) => {
const { page, pageSize, filtered, sorted } = state;
const totalQuerySize = (page + 1) * pageSize;
@@ -256,7 +255,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
});
};
- private fecthClientSideData = (state: any, instance: any) => {
+ private fetchClientSideData = (state: any) => {
const { page, pageSize, filtered, sorted } = state;
const { allSegments } = this.state;
if (allSegments == null) return;
@@ -312,10 +311,10 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
filterable
filtered={segmentFilter}
defaultSorted={[{ id: 'start', desc: true }]}
- onFilteredChange={(filtered, column) => {
+ onFilteredChange={filtered => {
this.setState({ segmentFilter: filtered });
}}
- onFetchData={noSqlMode ? this.fecthClientSideData : this.fetchData}
+ onFetchData={noSqlMode ? this.fetchClientSideData : this.fetchData}
showPageJump={false}
ofText=""
columns={[
@@ -407,7 +406,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
accessor: 'num_rows',
filterable: false,
defaultSortDesc: true,
- Cell: row => formatNumber(row.value),
+ Cell: row => (row.original.is_available ? formatNumber(row.value) : <em>(unknown)</em>),
show: !noSqlMode && hiddenColumns.exists('Num rows'),
},
{
@@ -456,8 +455,6 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
if (row.aggregated) return '';
const id = row.value;
const datasource = row.row.datasource;
- const dimensions = parseList(row.original.payload.dimensions);
- const metrics = parseList(row.original.payload.metrics);
return (
<ActionCell
onDetail={() => {
@@ -471,7 +468,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
/>
);
},
- Aggregated: row => '',
+ Aggregated: () => '',
show: hiddenColumns.exists(ActionCell.COLUMN_LABEL),
},
]}
@@ -503,7 +500,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
onClose={success => {
this.setState({ terminateSegmentId: null });
if (success) {
- this.segmentsJsonQueryManager.rerunLastQuery();
+ this.segmentsNoSqlQueryManager.rerunLastQuery();
this.segmentsSqlQueryManager.rerunLastQuery();
}
}}
@@ -522,6 +519,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
hiddenColumns,
} = this.state;
const { goToQuery, noSqlMode } = this.props;
+ const lastSegmentsQuery = this.segmentsSqlQueryManager.getLastQuery();
return (
<>
@@ -530,7 +528,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
<RefreshButton
onRefresh={auto =>
noSqlMode
- ? this.segmentsJsonQueryManager.rerunLastQueryInBackground(auto)
+ ? this.segmentsNoSqlQueryManager.rerunLastQueryInBackground(auto)
: this.segmentsSqlQueryManager.rerunLastQueryInBackground(auto)
}
localStorageKey={LocalStorageKeys.SEGMENTS_REFRESH_RATE}
@@ -539,8 +537,11 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
<Button
icon={IconNames.APPLICATION}
text="Go to SQL"
- hidden={noSqlMode}
- onClick={() => goToQuery(this.segmentsSqlQueryManager.getLastQuery().query)}
+ disabled={!lastSegmentsQuery}
+ onClick={() => {
+ if (!lastSegmentsQuery) return;
+ goToQuery(lastSegmentsQuery.query);
+ }}
/>
)}
<TableColumnSelector
diff --git a/web-console/src/views/servers-view/servers-view.spec.tsx b/web-console/src/views/servers-view/servers-view.spec.tsx
index 0e3ae2e..f96987c 100644
--- a/web-console/src/views/servers-view/servers-view.spec.tsx
+++ b/web-console/src/views/servers-view/servers-view.spec.tsx
@@ -26,8 +26,8 @@ describe('servers view', () => {
const serversView = shallow(
<ServersView
middleManager={'test'}
- goToQuery={(initSql: string) => {}}
- goToTask={(taskId: string) => {}}
+ goToQuery={() => {}}
+ goToTask={() => {}}
noSqlMode={false}
/>,
);
diff --git a/web-console/src/views/servers-view/servers-view.tsx b/web-console/src/views/servers-view/servers-view.tsx
index e8a0433..01c8319 100644
--- a/web-console/src/views/servers-view/servers-view.tsx
+++ b/web-console/src/views/servers-view/servers-view.tsx
@@ -74,8 +74,8 @@ function formatQueues(
return queueParts.join(', ') || 'Empty load/drop queues';
}
-export interface ServersViewProps extends React.Props<any> {
- middleManager: string | null;
+export interface ServersViewProps {
+ middleManager: string | undefined;
goToQuery: (initSql: string) => void;
goToTask: (taskId: string) => void;
noSqlMode: boolean;
@@ -133,25 +133,33 @@ interface ServerResultRow
Partial<MiddleManagerQueryResultRow> {}
export class ServersView extends React.PureComponent<ServersViewProps, ServersViewState> {
- private serverQueryManager: QueryManager<string, ServerQueryResultRow[]>;
-
- constructor(props: ServersViewProps, context: any) {
- super(props, context);
- this.state = {
- serversLoading: true,
- servers: null,
- serversError: null,
- serverFilter: [],
- groupServersBy: null,
-
- middleManagerDisableWorkerHost: null,
- middleManagerEnableWorkerHost: null,
-
- hiddenColumns: new LocalStorageBackedArray<string>(
- LocalStorageKeys.SERVER_TABLE_COLUMN_SELECTION,
- ),
- };
- }
+ private serverQueryManager: QueryManager<boolean, ServerResultRow[]>;
+
+ // Ranking
+ // coordinator => 7
+ // overlord => 6
+ // router => 5
+ // broker => 4
+ // historical => 3
+ // middle_manager => 2
+ // peon => 1
+
+ static SERVER_SQL = `SELECT
+ "server", "server_type", "tier", "host", "plaintext_port", "tls_port", "curr_size", "max_size",
+ (
+ CASE "server_type"
+ WHEN 'coordinator' THEN 7
+ WHEN 'overlord' THEN 6
+ WHEN 'router' THEN 5
+ WHEN 'broker' THEN 4
+ WHEN 'historical' THEN 3
+ WHEN 'middle_manager' THEN 2
+ WHEN 'peon' THEN 1
+ ELSE 0
+ END
+ ) AS "rank"
+FROM sys.servers
+ORDER BY "rank" DESC, "server" DESC`;
static async getServers(): Promise<ServerQueryResultRow[]> {
const allServerResp = await axios.get('/druid/coordinator/v1/servers?simple');
@@ -170,15 +178,28 @@ export class ServersView extends React.PureComponent<ServersViewProps, ServersVi
});
}
- componentDidMount(): void {
- const { noSqlMode } = this.props;
- const { hiddenColumns } = this.state;
+ constructor(props: ServersViewProps, context: any) {
+ super(props, context);
+ this.state = {
+ serversLoading: true,
+ servers: null,
+ serversError: null,
+ serverFilter: [],
+ groupServersBy: null,
+
+ middleManagerDisableWorkerHost: null,
+ middleManagerEnableWorkerHost: null,
+
+ hiddenColumns: new LocalStorageBackedArray<string>(
+ LocalStorageKeys.SERVER_TABLE_COLUMN_SELECTION,
+ ),
+ };
this.serverQueryManager = new QueryManager({
- processQuery: async (query: string) => {
+ processQuery: async noSqlMode => {
let servers: ServerQueryResultRow[];
if (!noSqlMode) {
- servers = await queryDruidSql({ query });
+ servers = await queryDruidSql({ query: ServersView.SERVER_SQL });
} else {
servers = await ServersView.getServers();
}
@@ -229,32 +250,11 @@ export class ServersView extends React.PureComponent<ServersViewProps, ServersVi
});
},
});
+ }
- // Ranking
- // coordinator => 7
- // overlord => 6
- // router => 5
- // broker => 4
- // historical => 3
- // middle_manager => 2
- // peon => 1
-
- this.serverQueryManager.runQuery(`SELECT
- "server", "server_type", "tier", "host", "plaintext_port", "tls_port", "curr_size", "max_size",
- (
- CASE "server_type"
- WHEN 'coordinator' THEN 7
- WHEN 'overlord' THEN 6
- WHEN 'router' THEN 5
- WHEN 'broker' THEN 4
- WHEN 'historical' THEN 3
- WHEN 'middle_manager' THEN 2
- WHEN 'peon' THEN 1
- ELSE 0
- END
- ) AS "rank"
-FROM sys.servers
-ORDER BY "rank" DESC, "server" DESC`);
+ componentDidMount(): void {
+ const { noSqlMode } = this.props;
+ this.serverQueryManager.runQuery(noSqlMode);
}
componentWillUnmount(): void {
@@ -291,7 +291,7 @@ ORDER BY "rank" DESC, "server" DESC`);
}
filterable
filtered={serverFilter}
- onFilteredChange={(filtered, column) => {
+ onFilteredChange={filtered => {
this.setState({ serverFilter: filtered });
}}
pivotBy={groupServersBy ? [groupServersBy] : []}
@@ -301,7 +301,7 @@ ORDER BY "rank" DESC, "server" DESC`);
Header: 'Server',
accessor: 'server',
width: 300,
- Aggregated: row => '',
+ Aggregated: () => '',
show: hiddenColumns.exists('Server'),
},
{
@@ -654,7 +654,7 @@ ORDER BY "rank" DESC, "server" DESC`);
<Button
icon={IconNames.APPLICATION}
text="Go to SQL"
- onClick={() => goToQuery(this.serverQueryManager.getLastQuery())}
+ onClick={() => goToQuery(ServersView.SERVER_SQL)}
/>
)}
<TableColumnSelector
diff --git a/web-console/src/views/task-view/tasks-view.spec.tsx b/web-console/src/views/task-view/tasks-view.spec.tsx
index 137dfe9..254f2ce 100644
--- a/web-console/src/views/task-view/tasks-view.spec.tsx
+++ b/web-console/src/views/task-view/tasks-view.spec.tsx
@@ -27,8 +27,8 @@ describe('tasks view', () => {
<TasksView
openDialog={'test'}
taskId={'test'}
- goToQuery={(initSql: string) => null}
- goToMiddleManager={(middleManager: string) => null}
+ goToQuery={() => null}
+ goToMiddleManager={() => null}
goToLoadDataView={() => null}
noSqlMode={false}
/>,
diff --git a/web-console/src/views/task-view/tasks-view.tsx b/web-console/src/views/task-view/tasks-view.tsx
index 3ab7293..39bb7f9 100644
--- a/web-console/src/views/task-view/tasks-view.tsx
+++ b/web-console/src/views/task-view/tasks-view.tsx
@@ -45,7 +45,6 @@ import { AppToaster } from '../../singletons/toaster';
import {
addFilter,
booleanCustomTableFilter,
- countBy,
formatDuration,
getDruidErrorMessage,
localStorageGet,
@@ -77,9 +76,9 @@ const taskTableColumns: string[] = [
ActionCell.COLUMN_LABEL,
];
-export interface TasksViewProps extends React.Props<any> {
- taskId: string | null;
- openDialog: string | null;
+export interface TasksViewProps {
+ taskId: string | undefined;
+ openDialog: string | undefined;
goToQuery: (initSql: string) => void;
goToMiddleManager: (middleManager: string) => void;
goToLoadDataView: (supervisorId?: string, taskId?: string) => void;
@@ -172,8 +171,8 @@ function stateToColor(status: string): string {
}
export class TasksView extends React.PureComponent<TasksViewProps, TasksViewState> {
- private supervisorQueryManager: QueryManager<string, SupervisorQueryResultRow[]>;
- private taskQueryManager: QueryManager<string, TaskQueryResultRow[]>;
+ private supervisorQueryManager: QueryManager<null, SupervisorQueryResultRow[]>;
+ private taskQueryManager: QueryManager<boolean, TaskQueryResultRow[]>;
static statusRanking: Record<string, number> = {
RUNNING: 4,
PENDING: 3,
@@ -182,6 +181,18 @@ export class TasksView extends React.PureComponent<TasksViewProps, TasksViewStat
FAILED: 1,
};
+ static TASK_SQL = `SELECT
+ "task_id", "type", "datasource", "created_time", "location", "duration", "error_msg",
+ CASE WHEN "status" = 'RUNNING' THEN "runner_status" ELSE "status" END AS "status",
+ (
+ CASE WHEN "status" = 'RUNNING' THEN
+ (CASE "runner_status" WHEN 'RUNNING' THEN 4 WHEN 'PENDING' THEN 3 ELSE 2 END)
+ ELSE 1
+ END
+ ) AS "rank"
+FROM sys.tasks
+ORDER BY "rank" DESC, "created_time" DESC`;
+
constructor(props: TasksViewProps, context: any) {
super(props, context);
this.state = {
@@ -220,34 +231,9 @@ export class TasksView extends React.PureComponent<TasksViewProps, TasksViewStat
LocalStorageKeys.SUPERVISOR_TABLE_COLUMN_SELECTION,
),
};
- }
-
- static parseTasks = (data: any[]): TaskQueryResultRow[] => {
- return data.map((d: any) => {
- return {
- created_time: d.createdTime,
- datasource: d.dataSource,
- duration: d.duration ? d.duration : 0,
- error_msg: d.errorMsg,
- location: d.location.host ? `${d.location.host}:${d.location.port}` : null,
- status: d.statusCode === 'RUNNING' ? d.runnerStatusCode : d.statusCode,
- task_id: d.id,
- type: d.typTasksView,
- rank:
- TasksView.statusRanking[d.statusCode === 'RUNNING' ? d.runnerStatusCode : d.statusCode],
- };
- });
- };
-
- private onSecondaryPaneSizeChange(secondaryPaneSize: number) {
- localStorageSet(LocalStorageKeys.TASKS_VIEW_PANE_SIZE, String(secondaryPaneSize));
- }
-
- componentDidMount(): void {
- const { noSqlMode } = this.props;
this.supervisorQueryManager = new QueryManager({
- processQuery: async (query: string) => {
+ processQuery: async () => {
const resp = await axios.get('/druid/indexer/v1/supervisor?full');
return resp.data;
},
@@ -260,12 +246,12 @@ export class TasksView extends React.PureComponent<TasksViewProps, TasksViewStat
},
});
- this.supervisorQueryManager.runQuery('dummy');
-
this.taskQueryManager = new QueryManager({
- processQuery: async (query: string) => {
+ processQuery: async noSqlMode => {
if (!noSqlMode) {
- return await queryDruidSql({ query });
+ return await queryDruidSql({
+ query: TasksView.TASK_SQL,
+ });
} else {
const taskEndpoints: string[] = [
'completeTasks',
@@ -279,7 +265,7 @@ export class TasksView extends React.PureComponent<TasksViewProps, TasksViewStat
return TasksView.parseTasks(resp.data);
}),
);
- return [].concat.apply([], result);
+ return ([] as TaskQueryResultRow[]).concat.apply([], result);
}
},
onStateChange: ({ result, loading, error }) => {
@@ -290,25 +276,34 @@ export class TasksView extends React.PureComponent<TasksViewProps, TasksViewStat
});
},
});
+ }
- // Ranking
- // RUNNING => 4
- // PENDING => 3
- // WAITING => 2
- // SUCCESS => 1
- // FAILED => 1
+ static parseTasks = (data: any[]): TaskQueryResultRow[] => {
+ return data.map((d: any) => {
+ return {
+ created_time: d.createdTime,
+ datasource: d.dataSource,
+ duration: d.duration ? d.duration : 0,
+ error_msg: d.errorMsg,
+ location: d.location.host ? `${d.location.host}:${d.location.port}` : null,
+ status: d.statusCode === 'RUNNING' ? d.runnerStatusCode : d.statusCode,
+ task_id: d.id,
+ type: d.typTasksView,
+ rank:
+ TasksView.statusRanking[d.statusCode === 'RUNNING' ? d.runnerStatusCode : d.statusCode],
+ };
+ });
+ };
- this.taskQueryManager.runQuery(`SELECT
- "task_id", "type", "datasource", "created_time", "location", "duration", "error_msg",
- CASE WHEN "status" = 'RUNNING' THEN "runner_status" ELSE "status" END AS "status",
- (
- CASE WHEN "status" = 'RUNNING' THEN
- (CASE "runner_status" WHEN 'RUNNING' THEN 4 WHEN 'PENDING' THEN 3 ELSE 2 END)
- ELSE 1
- END
- ) AS "rank"
-FROM sys.tasks
-ORDER BY "rank" DESC, "created_time" DESC`);
+ private onSecondaryPaneSizeChange(secondaryPaneSize: number) {
+ localStorageSet(LocalStorageKeys.TASKS_VIEW_PANE_SIZE, String(secondaryPaneSize));
+ }
+
+ componentDidMount(): void {
+ const { noSqlMode } = this.props;
+
+ this.supervisorQueryManager.runQuery(null);
+ this.taskQueryManager.runQuery(noSqlMode);
}
componentWillUnmount(): void {
@@ -687,7 +682,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
noDataText={!tasksLoading && tasks && !tasks.length ? 'No tasks' : tasksError || ''}
filterable
filtered={taskFilter}
- onFilteredChange={(filtered, column) => {
+ onFilteredChange={filtered => {
this.setState({ taskFilter: filtered });
}}
defaultSorted={[{ id: 'status', desc: true }]}
@@ -697,7 +692,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
Header: 'Task ID',
accessor: 'task_id',
width: 300,
- Aggregated: row => '',
+ Aggregated: () => '',
show: hiddenTaskColumns.exists('Task ID'),
},
{
@@ -738,7 +733,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
{
Header: 'Location',
accessor: 'location',
- Aggregated: row => '',
+ Aggregated: () => '',
filterMethod: (filter: Filter, row: any) => {
return booleanCustomTableFilter(filter, row.location);
},
@@ -748,7 +743,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
Header: 'Created time',
accessor: 'created_time',
width: 120,
- Aggregated: row => '',
+ Aggregated: () => '',
show: hiddenTaskColumns.exists('Created time'),
},
{
@@ -844,7 +839,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
/>
);
},
- Aggregated: row => '',
+ Aggregated: () => '',
show: hiddenTaskColumns.exists(ActionCell.COLUMN_LABEL),
},
]}
@@ -969,7 +964,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
<Button
icon={IconNames.APPLICATION}
text="Go to SQL"
- onClick={() => goToQuery(this.taskQueryManager.getLastQuery())}
+ onClick={() => goToQuery(TasksView.TASK_SQL)}
/>
)}
<Popover content={submitTaskMenu} position={Position.BOTTOM_LEFT}>
diff --git a/web-console/tsconfig.json b/web-console/tsconfig.json
index 510a2ea..255116f 100644
--- a/web-console/tsconfig.json
+++ b/web-console/tsconfig.json
@@ -1,17 +1,18 @@
{
"compilerOptions": {
+ "forceConsistentCasingInFileNames": true,
"experimentalDecorators": true,
- "noImplicitAny": true,
"noEmitOnError": true,
"declaration": false,
"removeComments": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
- "strictNullChecks": true,
- "strictFunctionTypes": false,
+ "strict": true,
"skipLibCheck": true,
"importHelpers": true,
"esModuleInterop": true,
+ "noUnusedParameters": true,
+ "noUnusedLocals": true,
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
diff --git a/web-console/tslint.json b/web-console/tslint.json
index eb60339..4b3e15e 100644
--- a/web-console/tslint.json
+++ b/web-console/tslint.json
@@ -8,7 +8,6 @@
"no-string-literal": false,
"object-literal-shorthand": false,
"arrow-return-shorthand": false,
- "no-var-requires": false,
"no-unused-variable": false
},
"linterOptions": {
diff --git a/web-console/unified-console.html b/web-console/unified-console.html
index 2567875..b1c8fd4 100644
--- a/web-console/unified-console.html
+++ b/web-console/unified-console.html
@@ -27,6 +27,6 @@
<body class="bp3-dark mouse-mode">
<div class="app-container"></div>
<script src="console-config.js"></script>
- <script src="public/web-console-0.15.0.js"></script>
+ <script src="public/web-console-0.16.0.js"></script>
</body>
</html>
diff --git a/web-console/webpack.config.js b/web-console/webpack.config.js
index 6c67603..22456f0 100644
--- a/web-console/webpack.config.js
+++ b/web-console/webpack.config.js
@@ -23,6 +23,15 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl
const { version } = require('./package.json');
+function friendlyErrorFormatter(e, colors) {
+ //const messageColor = error.severity === "warning" ? colors.bold.yellow : colors.bold.red;
+ // return (
+ // "Does not compute.... " +
+ // messageColor(Object.keys(error).map(key => `${key}: ${error[key]}`))
+ // );
+ return `${e.severity}: ${e.content} [TS${e.code}]\n at (${e.file}:${e.line}:${e.character})`;
+}
+
module.exports = (env) => {
let druidUrl = ((env || {}).druid_host || process.env.druid_host || 'localhost');
if (!druidUrl.startsWith('http')) druidUrl = 'http://' + druidUrl;
@@ -77,8 +86,15 @@ module.exports = (env) => {
},
{
test: /\.tsx?$/,
- use: 'ts-loader',
- exclude: /node_modules/
+ exclude: /node_modules/,
+ use: [
+ {
+ loader: 'ts-loader',
+ options: {
+ errorFormatter: friendlyErrorFormatter
+ }
+ }
+ ]
},
{
test: /\.s?css$/,
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org