Added tslint to web console (#7280)

* added tslint to web console

* added react linting and made rules stricter

* order imports

* update package-lock
This commit is contained in:
Vadim Ogievetsky 2019-03-17 09:23:17 -07:00 committed by Fangjin Yang
parent 1b6b40e511
commit b6b1e6160c
33 changed files with 741 additions and 347 deletions

View File

@ -944,6 +944,65 @@
"is-buffer": "^1.1.5" "is-buffer": "^1.1.5"
} }
}, },
"babel-code-frame": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
"dev": true,
"requires": {
"chalk": "^1.1.3",
"esutils": "^2.0.2",
"js-tokens": "^3.0.2"
},
"dependencies": {
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
},
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
"dev": true
},
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
}
},
"js-tokens": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
"dev": true
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
"ansi-regex": "^2.0.0"
}
},
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
"dev": true
}
}
},
"babel-jest": { "babel-jest": {
"version": "24.1.0", "version": "24.1.0",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.1.0.tgz", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.1.0.tgz",
@ -1372,6 +1431,12 @@
"integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
"dev": true "dev": true
}, },
"builtin-modules": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
"integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
"dev": true
},
"builtin-status-codes": { "builtin-status-codes": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
@ -9636,6 +9701,58 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
}, },
"tslint": {
"version": "5.14.0",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.14.0.tgz",
"integrity": "sha512-IUla/ieHVnB8Le7LdQFRGlVJid2T/gaJe5VkjzRVSRR6pA2ODYrnfR1hmxi+5+au9l50jBwpbBL34txgv4NnTQ==",
"dev": true,
"requires": {
"babel-code-frame": "^6.22.0",
"builtin-modules": "^1.1.1",
"chalk": "^2.3.0",
"commander": "^2.12.1",
"diff": "^3.2.0",
"glob": "^7.1.1",
"js-yaml": "^3.7.0",
"minimatch": "^3.0.4",
"mkdirp": "^0.5.1",
"resolve": "^1.3.2",
"semver": "^5.3.0",
"tslib": "^1.8.0",
"tsutils": "^2.29.0"
}
},
"tslint-loader": {
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/tslint-loader/-/tslint-loader-3.5.4.tgz",
"integrity": "sha512-jBHNNppXut6SgZ7CsTBh+6oMwVum9n8azbmcYSeMlsABhWWoHwjq631vIFXef3VSd75cCdX3rc6kstsB7rSVVw==",
"dev": true,
"requires": {
"loader-utils": "^1.0.2",
"mkdirp": "^0.5.1",
"object-assign": "^4.1.1",
"rimraf": "^2.4.4",
"semver": "^5.3.0"
}
},
"tslint-react": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/tslint-react/-/tslint-react-3.6.0.tgz",
"integrity": "sha512-AIv1QcsSnj7e9pFir6cJ6vIncTqxfqeFF3Lzh8SuuBljueYzEAtByuB6zMaD27BL0xhMEqsZ9s5eHuCONydjBw==",
"dev": true,
"requires": {
"tsutils": "^2.13.1"
}
},
"tsutils": {
"version": "2.29.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
"integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
"dev": true,
"requires": {
"tslib": "^1.8.1"
}
},
"tty-browserify": { "tty-browserify": {
"version": "0.0.0", "version": "0.0.0",
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",

View File

@ -70,6 +70,9 @@
"ts-jest": "^23.10.5", "ts-jest": "^23.10.5",
"ts-loader": "^5.3.3", "ts-loader": "^5.3.3",
"ts-node": "^8.0.2", "ts-node": "^8.0.2",
"tslint": "^5.14.0",
"tslint-loader": "^3.5.4",
"tslint-react": "^3.6.0",
"typescript": "^3.2.4", "typescript": "^3.2.4",
"webpack": "^4.29.0", "webpack": "^4.29.0",
"webpack-cli": "^3.2.1", "webpack-cli": "^3.2.1",

View File

@ -21,8 +21,35 @@ writefile='lib/sql-function-doc.ts'
> "$writefile" > "$writefile"
echo -e "// This file is auto generated and should not be modified\n" > "$writefile" cat > "$writefile" <<- EOM
echo -e 'export const SQLFunctionDoc: any[] = [' >> "$writefile" /*
* 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.
*/
// This file is auto generated and should not be modified
export interface FunctionDescription {
syntax: string;
description: string;
}
/* tslint:disable */
export const SQLFunctionDoc: FunctionDescription[] = [
EOM
isFunction=false isFunction=false
@ -47,4 +74,4 @@ while read -r line; do
fi fi
done < "$readfile" done < "$readfile"
echo -e ']' >> "$writefile" echo -e '];' >> "$writefile"

View File

@ -0,0 +1,25 @@
/*
* 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.
*/
// Trick blueprint 1.0.1 into accepting React 16 as React 15.
// This is broken into its own file to make linting and import sorting easy
// This file "a" to make sure it is imported before console-application in entry.ts
// tslint:disable
import * as React from 'react';
(React as any).PropTypes = require('prop-types');

View File

@ -16,10 +16,10 @@
* limitations under the License. * limitations under the License.
*/ */
import { Button } from "@blueprintjs/core";
import * as React from 'react'; import * as React from 'react';
import { Filter, ReactTableDefaults } from "react-table"; import { Filter, ReactTableDefaults } from "react-table";
import { Button } from "@blueprintjs/core";
import { Loader } from '../components/loader'; import { Loader } from '../components/loader';
import { countBy, makeTextFilter } from '../utils'; import { countBy, makeTextFilter } from '../utils';

View File

@ -16,11 +16,10 @@
* limitations under the License. * limitations under the License.
*/ */
import { resolveSrv } from 'dns';
import * as React from 'react';
import axios from 'axios';
import { InputGroup } from "@blueprintjs/core"; import { InputGroup } from "@blueprintjs/core";
import { HTMLSelect, FormGroup, NumericInput, TagInput } from "../components/filler"; import * as React from 'react';
import { FormGroup, HTMLSelect, NumericInput, TagInput } from "../components/filler";
interface Field { interface Field {
name: string; name: string;
@ -31,8 +30,8 @@ interface Field {
export interface AutoFormProps<T> extends React.Props<any> { export interface AutoFormProps<T> extends React.Props<any> {
fields: Field[]; fields: Field[];
model: T | null, model: T | null;
onChange: (newValue: T) => void onChange: (newValue: T) => void;
} }
export interface AutoFormState<T> { export interface AutoFormState<T> {
@ -48,7 +47,7 @@ export class AutoForm<T> extends React.Component<AutoFormProps<T>, AutoFormState
constructor(props: AutoFormProps<T>) { constructor(props: AutoFormProps<T>) {
super(props); super(props);
this.state = { this.state = {
} };
} }
private renderNumberInput(field: Field): JSX.Element { private renderNumberInput(field: Field): JSX.Element {
@ -97,7 +96,7 @@ export class AutoForm<T> extends React.Component<AutoFormProps<T>, AutoFormState
> >
<option value="True">True</option> <option value="True">True</option>
<option value="False">False</option> <option value="False">False</option>
</HTMLSelect> </HTMLSelect>;
} }
private renderStringArrayInput(field: Field): JSX.Element { private renderStringArrayInput(field: Field): JSX.Element {
@ -127,7 +126,7 @@ export class AutoForm<T> extends React.Component<AutoFormProps<T>, AutoFormState
const label = field.label || AutoForm.makeLabelName(field.name); const label = field.label || AutoForm.makeLabelName(field.name);
return <FormGroup label={label} key={field.name}> return <FormGroup label={label} key={field.name}>
{this.renderFieldInput(field)} {this.renderFieldInput(field)}
</FormGroup> </FormGroup>;
} }
render() { render() {
@ -135,6 +134,6 @@ export class AutoForm<T> extends React.Component<AutoFormProps<T>, AutoFormState
return <div className="auto-form"> return <div className="auto-form">
{model && fields.map(field => this.renderField(field))} {model && fields.map(field => this.renderField(field))}
</div> </div>;
} }
} }

View File

@ -17,10 +17,10 @@
*/ */
import { Button } from '@blueprintjs/core'; import { Button } from '@blueprintjs/core';
import * as React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import './filler.scss'; import * as React from 'react';
import './filler.scss';
export const IconNames = { export const IconNames = {
ERROR: "error" as "error", ERROR: "error" as "error",
@ -109,10 +109,9 @@ export class FormGroup extends React.Component<{ className?: string, label?: str
} }
} }
export const Alignment = { export const Alignment = {
LEFT: "left" as "left", LEFT: "left" as "left",
RIGHT: "right" as "right", RIGHT: "right" as "right"
}; };
export type Alignment = typeof Alignment[keyof typeof Alignment]; export type Alignment = typeof Alignment[keyof typeof Alignment];
@ -166,7 +165,7 @@ export interface NumericInputProps {
min?: number; min?: number;
max?: number; max?: number;
stepSize?: number; stepSize?: number;
majorStepSize?: number majorStepSize?: number;
} }
export class NumericInput extends React.Component<NumericInputProps, { stringValue: string }> { export class NumericInput extends React.Component<NumericInputProps, { stringValue: string }> {
@ -174,20 +173,20 @@ export class NumericInput extends React.Component<NumericInputProps, { stringVal
static defaultProps = { static defaultProps = {
stepSize: 1, stepSize: 1,
majorStepSize: 10 majorStepSize: 10
} };
constructor(props: NumericInputProps) { constructor(props: NumericInputProps) {
super(props); super(props);
this.state = { this.state = {
stringValue: typeof props.value === 'number' ? String(props.value) : '' stringValue: typeof props.value === 'number' ? String(props.value) : ''
} };
} }
private constrain(n: number): number { private constrain(n: number): number {
const { min, max } = this.props; const { min, max } = this.props;
if (typeof min === 'number') n = Math.max(n, min); if (typeof min === 'number') n = Math.max(n, min);
if (typeof max === 'number') n = Math.min(n, max); if (typeof max === 'number') n = Math.min(n, max);
return n return n;
} }
private handleChange = (e: any) => { private handleChange = (e: any) => {
@ -236,13 +235,13 @@ export class TagInput extends React.Component<TagInputProps, { stringValue: stri
super(props); super(props);
this.state = { this.state = {
stringValue: Array.isArray(props.values) ? props.values.join(', ') : '' stringValue: Array.isArray(props.values) ? props.values.join(', ') : ''
} };
} }
handleChange = (e: any) => { handleChange = (e: any) => {
let stringValue = e.target.value; const stringValue = e.target.value;
let newValues = stringValue.split(',').map((v: string) => v.trim()); const newValues = stringValue.split(',').map((v: string) => v.trim());
let newValuesFiltered = newValues.filter(Boolean); const newValuesFiltered = newValues.filter(Boolean);
this.setState({ this.setState({
stringValue: newValues.length === newValuesFiltered.length ? newValues.join(', ') : stringValue stringValue: newValues.length === newValuesFiltered.length ? newValues.join(', ') : stringValue
}); });

View File

@ -16,13 +16,13 @@
* limitations under the License. * limitations under the License.
*/ */
import * as React from 'react'; import { AnchorButton, Button, Classes, Menu, MenuItem, Popover, Position } from "@blueprintjs/core";
import classNames from 'classnames'; import classNames from 'classnames';
import { Button, Classes, AnchorButton, Popover, Position, Menu, MenuItem } from "@blueprintjs/core"; import * as React from 'react';
import { IconNames, NavbarGroup, Alignment, NavbarDivider, Navbar } from "../components/filler";
import { Alignment, IconNames, Navbar, NavbarDivider, NavbarGroup } from "../components/filler";
import { AboutDialog } from "../dialogs/about-dialog"; import { AboutDialog } from "../dialogs/about-dialog";
import { CoordinatorDynamicConfigDialog } from '../dialogs/coordinator-dynamic-config'; import { CoordinatorDynamicConfigDialog } from '../dialogs/coordinator-dynamic-config';
import "./header-bar.scss";
import { import {
DRUID_DOCS, DRUID_DOCS,
DRUID_GITHUB, DRUID_GITHUB,
@ -31,6 +31,8 @@ import {
LEGACY_OVERLORD_CONSOLE LEGACY_OVERLORD_CONSOLE
} from '../variables'; } from '../variables';
import "./header-bar.scss";
export type HeaderActiveTab = null | 'datasources' | 'segments' | 'tasks' | 'servers' | 'sql' | 'lookups'; export type HeaderActiveTab = null | 'datasources' | 'segments' | 'tasks' | 'servers' | 'sql' | 'lookups';
export interface HeaderBarProps extends React.Props<any> { export interface HeaderBarProps extends React.Props<any> {
@ -54,30 +56,48 @@ export class HeaderBar extends React.Component<HeaderBarProps, HeaderBarState> {
renderLogo() { renderLogo() {
return <div className="logo"> return <div className="logo">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 288 134"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 288 134">
<path fill="#FFFFFF" d="M136.7,67.5c0.5-6.1,5-10.4,10.6-10.4c3.9,0,6.5,2,7.4,4.3l1.1-12.4c0-0.1,0.3-0.2,0.7-0.2 <path
fill="#FFFFFF"
d="M136.7,67.5c0.5-6.1,5-10.4,10.6-10.4c3.9,0,6.5,2,7.4,4.3l1.1-12.4c0-0.1,0.3-0.2,0.7-0.2
c0.7,0,1.3,0.4,1.2,2l-2.3,25.9c-0.1,0.7-0.5,1-1,1h-0.2c-0.6,0-0.9-0.3-0.8-1l0.3-3.2c-1.7,2.7-4.5,4.5-8.3,4.5 c0.7,0,1.3,0.4,1.2,2l-2.3,25.9c-0.1,0.7-0.5,1-1,1h-0.2c-0.6,0-0.9-0.3-0.8-1l0.3-3.2c-1.7,2.7-4.5,4.5-8.3,4.5
C139.9,77.9,136.2,73.7,136.7,67.5z M154,68.9l0.4-4.7c-0.9-3.3-3.3-5.4-7.2-5.4c-4.5,0-8.1,3.6-8.5,8.6 C139.9,77.9,136.2,73.7,136.7,67.5z M154,68.9l0.4-4.7c-0.9-3.3-3.3-5.4-7.2-5.4c-4.5,0-8.1,3.6-8.5,8.6
c-0.4,5.1,2.5,8.7,6.9,8.7C150,76.1,153.7,72.9,154,68.9z"/> c-0.4,5.1,2.5,8.7,6.9,8.7C150,76.1,153.7,72.9,154,68.9z"
<path fill="#FFFFFF" d="M161.2,76.6l1.7-19.1c0,0,0.3-0.2,0.7-0.2c0.7,0,1.3,0.4,1.1,2l-0.2,2.5c1.1-3.3,3.3-4.8,6-4.8 />
<path
fill="#FFFFFF"
d="M161.2,76.6l1.7-19.1c0,0,0.3-0.2,0.7-0.2c0.7,0,1.3,0.4,1.1,2l-0.2,2.5c1.1-3.3,3.3-4.8,6-4.8
c1.6,0,2.7,0.7,2.6,1.7c-0.1,0.8-0.6,1.1-0.7,1.1c-0.5-0.5-1.3-0.8-2.3-0.8c-3.6,0-5.6,3.6-6.1,9l-0.8,8.7c-0.1,0.7-0.5,1-1,1 c1.6,0,2.7,0.7,2.6,1.7c-0.1,0.8-0.6,1.1-0.7,1.1c-0.5-0.5-1.3-0.8-2.3-0.8c-3.6,0-5.6,3.6-6.1,9l-0.8,8.7c-0.1,0.7-0.5,1-1,1
h-0.2C161.5,77.6,161.2,77.4,161.2,76.6z"/> h-0.2C161.5,77.6,161.2,77.4,161.2,76.6z"
<path fill="#FFFFFF" d="M175.6,69l0.9-10.7c0.1-0.8,0.5-1,1-1h0.3c0.5,0,0.9,0.2,0.8,1l-0.9,10.5c-0.4,4.4,1.5,7.2,5.5,7.2 />
<path
fill="#FFFFFF"
d="M175.6,69l0.9-10.7c0.1-0.8,0.5-1,1-1h0.3c0.5,0,0.9,0.2,0.8,1l-0.9,10.5c-0.4,4.4,1.5,7.2,5.5,7.2
c3.3,0,6-1.9,7.5-4.7l1.1-13c0.1-0.8,0.5-1,1-1h0.3c0.5,0,0.9,0.2,0.8,1l-1.7,19.1c0,0-0.4,0.2-0.7,0.2c-0.7,0-1.2-0.4-1.1-2 c3.3,0,6-1.9,7.5-4.7l1.1-13c0.1-0.8,0.5-1,1-1h0.3c0.5,0,0.9,0.2,0.8,1l-1.7,19.1c0,0-0.4,0.2-0.7,0.2c-0.7,0-1.2-0.4-1.1-2
l0.2-1.8c-1.6,2.4-4.2,4.1-7.6,4.1C177.6,77.9,175.2,74.4,175.6,69z"/> l0.2-1.8c-1.6,2.4-4.2,4.1-7.6,4.1C177.6,77.9,175.2,74.4,175.6,69z"
<path fill="#FFFFFF" d="M200.1,50.7c0.1-1,0.6-1.4,1.6-1.4c0.9,0,1.4,0.5,1.3,1.4c-0.1,0.9-0.6,1.4-1.6,1.4 />
<path
fill="#FFFFFF"
d="M200.1,50.7c0.1-1,0.6-1.4,1.6-1.4c0.9,0,1.4,0.5,1.3,1.4c-0.1,0.9-0.6,1.4-1.6,1.4
C200.5,52.1,200,51.6,200.1,50.7z M198.2,76.6l1.6-18.3c0.1-0.8,0.5-1,1-1h0.3c0.5,0,0.9,0.2,0.8,1l-1.6,18.3 C200.5,52.1,200,51.6,200.1,50.7z M198.2,76.6l1.6-18.3c0.1-0.8,0.5-1,1-1h0.3c0.5,0,0.9,0.2,0.8,1l-1.6,18.3
c-0.1,0.8-0.5,1-1,1H199C198.5,77.6,198.2,77.4,198.2,76.6z"/> c-0.1,0.8-0.5,1-1,1H199C198.5,77.6,198.2,77.4,198.2,76.6z"
<path fill="#FFFFFF" d="M205.8,67.5c0.5-6.1,5-10.4,10.6-10.4c3.9,0,6.5,2,7.4,4.3l1.1-12.4c0-0.1,0.3-0.2,0.7-0.2 />
<path
fill="#FFFFFF"
d="M205.8,67.5c0.5-6.1,5-10.4,10.6-10.4c3.9,0,6.5,2,7.4,4.3l1.1-12.4c0-0.1,0.3-0.2,0.7-0.2
c0.7,0,1.3,0.4,1.2,2l-2.3,25.9c-0.1,0.7-0.5,1-1,1h-0.2c-0.5,0-0.9-0.3-0.8-1l0.3-3.2c-1.7,2.7-4.5,4.5-8.3,4.5 c0.7,0,1.3,0.4,1.2,2l-2.3,25.9c-0.1,0.7-0.5,1-1,1h-0.2c-0.5,0-0.9-0.3-0.8-1l0.3-3.2c-1.7,2.7-4.5,4.5-8.3,4.5
C209,77.9,205.2,73.7,205.8,67.5z M223.1,68.9l0.4-4.7c-0.9-3.3-3.3-5.4-7.2-5.4c-4.5,0-8.1,3.6-8.5,8.6 C209,77.9,205.2,73.7,205.8,67.5z M223.1,68.9l0.4-4.7c-0.9-3.3-3.3-5.4-7.2-5.4c-4.5,0-8.1,3.6-8.5,8.6
c-0.4,5.1,2.5,8.7,6.9,8.7C219,76.1,222.7,72.9,223.1,68.9z"/> c-0.4,5.1,2.5,8.7,6.9,8.7C219,76.1,222.7,72.9,223.1,68.9z"
<path fill="#2CEEFB" d="M96.2,89.8h-2.7c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3h2.7c11.5,0,23.8-7.4,23.8-23.7 />
<path
fill="#2CEEFB"
d="M96.2,89.8h-2.7c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3h2.7c11.5,0,23.8-7.4,23.8-23.7
c0-9.1-6.9-15.8-16.4-15.8H80c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3h23.6c5.3,0,10.1,1.9,13.6,5.3 c0-9.1-6.9-15.8-16.4-15.8H80c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3h23.6c5.3,0,10.1,1.9,13.6,5.3
c3.5,3.4,5.4,8,5.4,13.1c0,6.6-2.3,13-6.3,17.7C111.5,86.8,104.5,89.8,96.2,89.8z M87.1,89.8h-5.8c-0.7,0-1.3-0.6-1.3-1.3 c3.5,3.4,5.4,8,5.4,13.1c0,6.6-2.3,13-6.3,17.7C111.5,86.8,104.5,89.8,96.2,89.8z M87.1,89.8h-5.8c-0.7,0-1.3-0.6-1.3-1.3
c0-0.7,0.6-1.3,1.3-1.3h5.8c0.7,0,1.3,0.6,1.3,1.3C88.4,89.2,87.8,89.8,87.1,89.8z M97.7,79.5h-26c-0.7,0-1.3-0.6-1.3-1.3 c0-0.7,0.6-1.3,1.3-1.3h5.8c0.7,0,1.3,0.6,1.3,1.3C88.4,89.2,87.8,89.8,87.1,89.8z M97.7,79.5h-26c-0.7,0-1.3-0.6-1.3-1.3
c0-0.7,0.6-1.3,1.3-1.3h26c7.5,0,11.5-5.8,11.5-11.5c0-4.2-3.2-7.3-7.7-7.3h-26c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3 c0-0.7,0.6-1.3,1.3-1.3h26c7.5,0,11.5-5.8,11.5-11.5c0-4.2-3.2-7.3-7.7-7.3h-26c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3
h26c5.9,0,10.3,4.3,10.3,9.9c0,3.7-1.3,7.2-3.7,9.8C105.5,78,101.9,79.5,97.7,79.5z M69.2,58h-6.3c-0.7,0-1.3-0.6-1.3-1.3 h26c5.9,0,10.3,4.3,10.3,9.9c0,3.7-1.3,7.2-3.7,9.8C105.5,78,101.9,79.5,97.7,79.5z M69.2,58h-6.3c-0.7,0-1.3-0.6-1.3-1.3
c0-0.7,0.6-1.3,1.3-1.3h6.3c0.7,0,1.3,0.6,1.3,1.3C70.5,57.4,69.9,58,69.2,58z"/> c0-0.7,0.6-1.3,1.3-1.3h6.3c0.7,0,1.3,0.6,1.3,1.3C70.5,57.4,69.9,58,69.2,58z"
/>
</svg> </svg>
</div>; </div>;
} }

View File

@ -17,6 +17,7 @@
*/ */
import * as React from 'react'; import * as React from 'react';
import './loader.scss'; import './loader.scss';
export interface LoaderProps extends React.Props<any> { export interface LoaderProps extends React.Props<any> {
@ -31,23 +32,35 @@ export class Loader extends React.Component<LoaderProps, LoaderState> {
render() { render() {
const { loadingText, loading } = this.props; const { loadingText, loading } = this.props;
if (loading === false) return null; if (!loading) return null;
return <div className="loader"> return <div className="loader">
<div className="loader-logo"> <div className="loader-logo">
<svg viewBox="0 0 100 100"> <svg viewBox="0 0 100 100">
<path className="one" d="M54.2,69.8h-2.7c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3h2.7c11.5,0,23.8-7.4,23.8-23.7 <path
className="one"
d="M54.2,69.8h-2.7c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3h2.7c11.5,0,23.8-7.4,23.8-23.7
c0-9.1-6.9-15.8-16.4-15.8H38c-0.7,0-1.3-0.6-1.3-1.3s0.6-1.3,1.3-1.3h23.6c5.3,0,10.1,1.9,13.6,5.3c3.5,3.4,5.4,8,5.4,13.1 c0-9.1-6.9-15.8-16.4-15.8H38c-0.7,0-1.3-0.6-1.3-1.3s0.6-1.3,1.3-1.3h23.6c5.3,0,10.1,1.9,13.6,5.3c3.5,3.4,5.4,8,5.4,13.1
c0,6.6-2.3,13-6.3,17.7C69.5,66.8,62.5,69.8,54.2,69.8z"/> c0,6.6-2.3,13-6.3,17.7C69.5,66.8,62.5,69.8,54.2,69.8z"
<path className="two" d="M55.7,59.5h-26c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3h26c7.5,0,11.5-5.8,11.5-11.5 />
<path
className="two"
d="M55.7,59.5h-26c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3h26c7.5,0,11.5-5.8,11.5-11.5
c0-4.2-3.2-7.3-7.7-7.3h-26c-0.7,0-1.3-0.6-1.3-1.3s0.6-1.3,1.3-1.3h26c5.9,0,10.3,4.3,10.3,9.9c0,3.7-1.3,7.2-3.7,9.8 c0-4.2-3.2-7.3-7.7-7.3h-26c-0.7,0-1.3-0.6-1.3-1.3s0.6-1.3,1.3-1.3h26c5.9,0,10.3,4.3,10.3,9.9c0,3.7-1.3,7.2-3.7,9.8
C63.5,58,59.9,59.5,55.7,59.5z"/> C63.5,58,59.9,59.5,55.7,59.5z"
<path className="three" d="M27.2,38h-6.3c-0.7,0-1.3-0.6-1.3-1.3s0.6-1.3,1.3-1.3h6.3c0.7,0,1.3,0.6,1.3,1.3S27.9,38,27.2,38z"/> />
<path className="four" d="M45.1,69.8h-5.8c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3h5.8c0.7,0,1.3,0.6,1.3,1.3 <path
C46.4,69.2,45.8,69.8,45.1,69.8z"/> className="three"
d="M27.2,38h-6.3c-0.7,0-1.3-0.6-1.3-1.3s0.6-1.3,1.3-1.3h6.3c0.7,0,1.3,0.6,1.3,1.3S27.9,38,27.2,38z"
/>
<path
className="four"
d="M45.1,69.8h-5.8c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3h5.8c0.7,0,1.3,0.6,1.3,1.3
C46.4,69.2,45.8,69.8,45.1,69.8z"
/>
</svg> </svg>
{loadingText ? <div className="label">{loadingText}</div> : null} {loadingText ? <div className="label">{loadingText}</div> : null}
</div> </div>
</div> </div>;
} }
} }

View File

@ -16,10 +16,12 @@
* limitations under the License. * limitations under the License.
*/ */
import * as React from 'react'; import { Button, Collapse, InputGroup } from "@blueprintjs/core";
import axios from 'axios'; import axios from 'axios';
import { Button, InputGroup, Collapse } from "@blueprintjs/core"; import * as React from 'react';
import { IconNames, FormGroup, HTMLSelect, Card, ControlGroup, NumericInput, TagInput } from "../components/filler";
import { Card, ControlGroup, FormGroup, HTMLSelect, IconNames, NumericInput, TagInput } from "../components/filler";
import './rule-editor.scss'; import './rule-editor.scss';
export interface Rule { export interface Rule {
@ -107,7 +109,7 @@ export class RuleEditor extends React.Component<RuleEditorProps, RuleEditorState
super(props); super(props);
this.state = { this.state = {
isOpen: true isOpen: true
} };
} }
private removeTier = (key: string) => { private removeTier = (key: string) => {
@ -126,9 +128,9 @@ export class RuleEditor extends React.Component<RuleEditorProps, RuleEditorState
let newTierName = tiers[0]; let newTierName = tiers[0];
if (rule.tieredReplicants) { if (rule.tieredReplicants) {
for (let i = 0; i < tiers.length; i++) { for (const tier of tiers) {
if (rule.tieredReplicants[tiers[i]] === undefined) { if (rule.tieredReplicants[tier] === undefined) {
newTierName = tiers[i]; newTierName = tier;
break; break;
} }
} }
@ -160,7 +162,7 @@ export class RuleEditor extends React.Component<RuleEditorProps, RuleEditorState
/> />
<Button className="pt-minimal" style={{pointerEvents: 'none'}}>Tier:</Button> <Button className="pt-minimal" style={{pointerEvents: 'none'}}>Tier:</Button>
<HTMLSelect <HTMLSelect
fill={true} fill
value={tier} value={tier}
onChange={(e: any) => onChange(RuleEditor.changeTier(rule, tier, e.target.value))} onChange={(e: any) => onChange(RuleEditor.changeTier(rule, tier, e.target.value))}
> >

View File

@ -16,22 +16,25 @@
* limitations under the License. * limitations under the License.
*/ */
import * as React from 'react'; import { Button, Checkbox, Classes, Intent, Popover, Position } from "@blueprintjs/core";
import * as ReactDOMServer from 'react-dom/server';
import axios from "axios"; import axios from "axios";
import * as classNames from 'classnames'; import * as ace from 'brace';
import * as ace from 'brace'
import AceEditor from "react-ace";
import 'brace/mode/sql';
import 'brace/mode/hjson';
import 'brace/theme/solarized_dark';
import 'brace/ext/language_tools'; import 'brace/ext/language_tools';
import {Intent, Button, Popover, Checkbox, Classes, Position} from "@blueprintjs/core"; import 'brace/mode/hjson';
import 'brace/mode/sql';
import 'brace/theme/solarized_dark';
import * as classNames from 'classnames';
import * as React from 'react';
import AceEditor from "react-ace";
import * as ReactDOMServer from 'react-dom/server';
import { SQLFunctionDoc } from "../../lib/sql-function-doc"; import { SQLFunctionDoc } from "../../lib/sql-function-doc";
import { IconNames } from './filler';
import './sql-control.scss'
import { AppToaster } from "../singletons/toaster"; import { AppToaster } from "../singletons/toaster";
import { IconNames } from './filler';
import './sql-control.scss';
const langTools = ace.acequire('ace/ext/language_tools'); const langTools = ace.acequire('ace/ext/language_tools');
export interface SqlControlProps extends React.Props<any> { export interface SqlControlProps extends React.Props<any> {
@ -55,9 +58,8 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
}; };
} }
private addDatasourceAutoCompleter = async (): Promise<any> => { private addDatasourceAutoCompleter = async (): Promise<any> => {
const datasourceResp = await axios.post("/druid/v2/sql", { query: `SELECT datasource FROM sys.segments GROUP BY 1`}) const datasourceResp = await axios.post("/druid/v2/sql", { query: `SELECT datasource FROM sys.segments GROUP BY 1`});
const datasourceList: any[] = datasourceResp.data.map((d: any) => { const datasourceList: any[] = datasourceResp.data.map((d: any) => {
const datasourceName: string = d.datasource; const datasourceName: string = d.datasource;
return { return {
@ -77,7 +79,7 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
} }
private addColumnNameAutoCompleter = async (): Promise<any> => { private addColumnNameAutoCompleter = async (): Promise<any> => {
const columnNameResp = await axios.post("/druid/v2/sql", {query: `SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'druid'`}) const columnNameResp = await axios.post("/druid/v2/sql", {query: `SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'druid'`});
const columnNameList: any[] = columnNameResp.data.map((d: any) => { const columnNameList: any[] = columnNameResp.data.map((d: any) => {
const columnName: string = d.COLUMN_NAME; const columnName: string = d.COLUMN_NAME;
return { return {
@ -133,7 +135,7 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
<div><b>Description:</b></div> <div><b>Description:</b></div>
<div>{item.description}</div> <div>{item.description}</div>
</div> </div>
)) ));
} }
} }
}; };
@ -160,7 +162,7 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
private handleChange = (newValue: string): void => { private handleChange = (newValue: string): void => {
this.setState({ this.setState({
query: newValue query: newValue
}) });
} }
render() { render() {
@ -186,7 +188,7 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
theme="solarized_dark" theme="solarized_dark"
name="ace-editor" name="ace-editor"
onChange={this.handleChange} onChange={this.handleChange}
focus={true} focus
fontSize={14} fontSize={14}
width={'100%'} width={'100%'}
height={"30vh"} height={"30vh"}
@ -199,7 +201,7 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
enableBasicAutocompletion: isRune ? false : autoCompleteOn, enableBasicAutocompletion: isRune ? false : autoCompleteOn,
enableLiveAutocompletion: isRune ? false : autoCompleteOn, enableLiveAutocompletion: isRune ? false : autoCompleteOn,
showLineNumbers: true, showLineNumbers: true,
tabSize: 2, tabSize: 2
}} }}
/> />
<div className="buttons"> <div className="buttons">
@ -213,4 +215,3 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
</div>; </div>;
} }
} }

View File

@ -16,22 +16,23 @@
* limitations under the License. * limitations under the License.
*/ */
import axios from 'axios';
import * as React from 'react';
import * as classNames from 'classnames';
import { HashRouter, Route, Switch } from "react-router-dom";
import { Intent } from "@blueprintjs/core"; import { Intent } from "@blueprintjs/core";
import axios from 'axios';
import * as classNames from 'classnames';
import * as React from 'react';
import { HashRouter, Route, Switch } from "react-router-dom";
import { HeaderActiveTab, HeaderBar } from './components/header-bar';
import { AppToaster } from './singletons/toaster'; import { AppToaster } from './singletons/toaster';
import { HeaderBar, HeaderActiveTab } from './components/header-bar';
import { localStorageGet, localStorageSet } from './utils';
import { DRUID_DOCS_SQL, LEGACY_COORDINATOR_CONSOLE, LEGACY_OVERLORD_CONSOLE } from './variables'; import { DRUID_DOCS_SQL, LEGACY_COORDINATOR_CONSOLE, LEGACY_OVERLORD_CONSOLE } from './variables';
import { HomeView } from './views/home-view';
import { DatasourcesView } from './views/datasource-view'; import { DatasourcesView } from './views/datasource-view';
import { HomeView } from './views/home-view';
import { LookupsView } from "./views/lookups-view";
import { SegmentsView } from './views/segments-view'; import { SegmentsView } from './views/segments-view';
import { ServersView } from './views/servers-view'; import { ServersView } from './views/servers-view';
import { TasksView } from './views/tasks-view';
import { SqlView } from './views/sql-view'; import { SqlView } from './views/sql-view';
import { LookupsView } from "./views/lookups-view"; import { TasksView } from './views/tasks-view';
import "./console-application.scss"; import "./console-application.scss";
export interface ConsoleApplicationProps extends React.Props<any> { export interface ConsoleApplicationProps extends React.Props<any> {
@ -63,14 +64,16 @@ export class ConsoleApplication extends React.Component<ConsoleApplicationProps,
iconName: 'error', iconName: 'error',
intent: Intent.DANGER, intent: Intent.DANGER,
timeout: 120000, timeout: 120000,
/* tslint:disable:jsx-alignment */
message: <> message: <>
It appears that the SQL endpoint is disabled. Either <a It appears that the SQL endpoint is disabled. Either <a
href={DRUID_DOCS_SQL}>enable the SQL endpoint</a> or use the old <a href={DRUID_DOCS_SQL}>enable the SQL endpoint</a> or use the old <a
href={LEGACY_COORDINATOR_CONSOLE}>coordinator</a> and <a href={LEGACY_COORDINATOR_CONSOLE}>coordinator</a> and <a
href={LEGACY_OVERLORD_CONSOLE}>overlord</a> consoles that do not rely on the SQL endpoint. href={LEGACY_OVERLORD_CONSOLE}>overlord</a> consoles that do not rely on the SQL endpoint.
</> </>
/* tslint:enable:jsx-alignment */
}); });
return false return false;
} }
return true; return true;
} }
@ -142,30 +145,49 @@ export class ConsoleApplication extends React.Component<ConsoleApplicationProps,
return <HashRouter hashType="noslash"> return <HashRouter hashType="noslash">
<div className="console-application"> <div className="console-application">
<Switch> <Switch>
<Route path="/datasources" component={() => { <Route
path="/datasources"
component={() => {
return wrapInViewContainer('datasources', <DatasourcesView goToSql={this.goToSql} goToSegments={this.goToSegments}/>); return wrapInViewContainer('datasources', <DatasourcesView goToSql={this.goToSql} goToSegments={this.goToSegments}/>);
}} /> }}
<Route path="/segments" component={() => { />
<Route
path="/segments"
component={() => {
return wrapInViewContainer('segments', <SegmentsView datasource={this.datasource} onlyUnavailable={this.onlyUnavailable} goToSql={this.goToSql}/>); return wrapInViewContainer('segments', <SegmentsView datasource={this.datasource} onlyUnavailable={this.onlyUnavailable} goToSql={this.goToSql}/>);
}} /> }}
<Route path="/tasks" component={() => { />
<Route
path="/tasks"
component={() => {
return wrapInViewContainer('tasks', <TasksView taskId={this.taskId} goToSql={this.goToSql} goToMiddleManager={this.goToMiddleManager}/>, true); return wrapInViewContainer('tasks', <TasksView taskId={this.taskId} goToSql={this.goToSql} goToMiddleManager={this.goToMiddleManager}/>, true);
}} /> }}
<Route path="/servers" component={() => { />
<Route
path="/servers"
component={() => {
return wrapInViewContainer('servers', <ServersView middleManager={this.middleManager} goToSql={this.goToSql} goToTask={this.goToTask}/>, true); return wrapInViewContainer('servers', <ServersView middleManager={this.middleManager} goToSql={this.goToSql} goToTask={this.goToTask}/>, true);
}} /> }}
<Route path="/sql" component={() => { />
<Route
path="/sql"
component={() => {
return wrapInViewContainer('sql', <SqlView initSql={this.initSql}/>); return wrapInViewContainer('sql', <SqlView initSql={this.initSql}/>);
}} /> }}
<Route path="/lookups" component={() => { />
<Route
path="/lookups"
component={() => {
return wrapInViewContainer('lookups', <LookupsView />); return wrapInViewContainer('lookups', <LookupsView />);
}} /> }}
<Route component={() => { />
return wrapInViewContainer(null, <HomeView/>) <Route
}} /> component={() => {
return wrapInViewContainer(null, <HomeView/>);
}}
/>
</Switch> </Switch>
</div> </div>
</HashRouter>; </HashRouter>;
} }
} }

View File

@ -16,13 +16,14 @@
* limitations under the License. * limitations under the License.
*/ */
import { AnchorButton, Button, Classes, Dialog, Intent } from "@blueprintjs/core";
import * as React from 'react'; import * as React from 'react';
import { Button, Dialog, Classes, AnchorButton, Intent } from "@blueprintjs/core";
import { IconNames } from "../components/filler"; import { IconNames } from "../components/filler";
import { DRUID_COMMUNITY, DRUID_DEVELOPER_GROUP, DRUID_USER_GROUP, DRUID_WEBSITE } from '../variables'; import { DRUID_COMMUNITY, DRUID_DEVELOPER_GROUP, DRUID_USER_GROUP, DRUID_WEBSITE } from '../variables';
export interface AboutDialogProps extends React.Props<any> { export interface AboutDialogProps extends React.Props<any> {
onClose: () => void onClose: () => void;
} }
export interface AboutDialogState { export interface AboutDialogState {
@ -37,6 +38,7 @@ export class AboutDialog extends React.Component<AboutDialogProps, AboutDialogSt
render() { render() {
const { onClose } = this.props; const { onClose } = this.props;
/* tslint:disable:jsx-alignment */
return <Dialog return <Dialog
iconName={IconNames.GRAPH} iconName={IconNames.GRAPH}
onClose={onClose} onClose={onClose}
@ -75,5 +77,6 @@ export class AboutDialog extends React.Component<AboutDialogProps, AboutDialogSt
</div> </div>
</div> </div>
</Dialog>; </Dialog>;
/* tslint:enable:jsx-alignment */
} }
} }

View File

@ -16,25 +16,25 @@
* limitations under the License. * limitations under the License.
*/ */
import classNames from 'classnames';
import * as React from 'react';
import { import {
Button, Button,
InputGroup,
Dialog,
Classes, Classes,
Dialog,
Intent, Intent,
ProgressBar ProgressBar
} from "@blueprintjs/core"; } from "@blueprintjs/core";
import { Icon, FormGroup, ButtonGroup, NumericInput, TagInput } from '../components/filler'; import classNames from 'classnames';
import * as React from 'react';
import { ButtonGroup, FormGroup, Icon, NumericInput, TagInput } from '../components/filler';
import { AppToaster } from '../singletons/toaster'; import { AppToaster } from '../singletons/toaster';
export interface AsyncAlertDialogProps extends React.Props<any> { export interface AsyncAlertDialogProps extends React.Props<any> {
action: null | (() => Promise<void>), action: null | (() => Promise<void>);
onClose: (success: boolean) => void, onClose: (success: boolean) => void;
confirmButtonText: string; confirmButtonText: string;
cancelButtonText?: string; cancelButtonText?: string;
className?: string, className?: string;
icon?: string; icon?: string;
intent?: Intent; intent?: Intent;
successText: string; successText: string;
@ -42,7 +42,7 @@ export interface AsyncAlertDialogProps extends React.Props<any> {
} }
export interface AsyncAlertDialogState { export interface AsyncAlertDialogState {
working: boolean working: boolean;
} }
export class AsyncActionDialog extends React.Component<AsyncAlertDialogProps, AsyncAlertDialogState> { export class AsyncActionDialog extends React.Component<AsyncAlertDialogProps, AsyncAlertDialogState> {
@ -59,7 +59,7 @@ export class AsyncActionDialog extends React.Component<AsyncAlertDialogProps, As
this.setState({ working: true }); this.setState({ working: true });
try { try {
await action() await action();
} catch (e) { } catch (e) {
AppToaster.show({ AppToaster.show({
message: `${failText}: ${e.message}`, message: `${failText}: ${e.message}`,
@ -103,6 +103,6 @@ export class AsyncActionDialog extends React.Component<AsyncAlertDialogProps, As
<Button text={cancelButtonText || 'Cancel'} onClick={handleClose}/> <Button text={cancelButtonText || 'Cancel'} onClick={handleClose}/>
</div> </div>
} }
</Dialog> </Dialog>;
} }
} }

View File

@ -17,17 +17,20 @@
*/ */
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import * as React from 'react';
import axios from 'axios'; import axios from 'axios';
import { AppToaster } from '../singletons/toaster'; import * as React from 'react';
import { IconNames } from '../components/filler';
import { AutoForm } from '../components/auto-form'; import { AutoForm } from '../components/auto-form';
import { IconNames } from '../components/filler';
import { AppToaster } from '../singletons/toaster';
import { getDruidErrorMessage } from '../utils'; import { getDruidErrorMessage } from '../utils';
import { SnitchDialog } from './snitch-dialog'; import { SnitchDialog } from './snitch-dialog';
import './coordinator-dynamic-config.scss'; import './coordinator-dynamic-config.scss';
export interface CoordinatorDynamicConfigDialogProps extends React.Props<any> { export interface CoordinatorDynamicConfigDialogProps extends React.Props<any> {
onClose: () => void onClose: () => void;
} }
export interface CoordinatorDynamicConfigDialogState { export interface CoordinatorDynamicConfigDialogState {
@ -39,7 +42,7 @@ export class CoordinatorDynamicConfigDialog extends React.Component<CoordinatorD
super(props); super(props);
this.state = { this.state = {
dynamicConfig: null dynamicConfig: null
} };
} }
componentDidMount(): void { componentDidMount(): void {
@ -50,7 +53,7 @@ export class CoordinatorDynamicConfigDialog extends React.Component<CoordinatorD
let config: Record<string, any> | null = null; let config: Record<string, any> | null = null;
try { try {
const configResp = await axios.get("/druid/coordinator/v1/config"); const configResp = await axios.get("/druid/coordinator/v1/config");
config = configResp.data config = configResp.data;
} catch (e) { } catch (e) {
AppToaster.show({ AppToaster.show({
iconName: IconNames.ERROR, iconName: IconNames.ERROR,
@ -66,7 +69,7 @@ export class CoordinatorDynamicConfigDialog extends React.Component<CoordinatorD
private saveClusterConfig = async (author: string, comment: string) => { private saveClusterConfig = async (author: string, comment: string) => {
const { onClose } = this.props; const { onClose } = this.props;
let newState: any = this.state.dynamicConfig; const newState: any = this.state.dynamicConfig;
try { try {
await axios.post("/druid/coordinator/v1/config", newState, { await axios.post("/druid/coordinator/v1/config", newState, {
headers: { headers: {
@ -158,6 +161,6 @@ export class CoordinatorDynamicConfigDialog extends React.Component<CoordinatorD
model={dynamicConfig} model={dynamicConfig}
onChange={m => this.setState({ dynamicConfig: m })} onChange={m => this.setState({ dynamicConfig: m })}
/> />
</SnitchDialog> </SnitchDialog>;
} }
} }

View File

@ -16,24 +16,26 @@
* limitations under the License. * limitations under the License.
*/ */
import { Button, Classes, Dialog, InputGroup, Intent } from "@blueprintjs/core";
import * as React from "react"; import * as React from "react";
import {Button, Classes, Dialog, Intent, InputGroup } from "@blueprintjs/core";
import "./lookup-edit-dialog.scss"
import {validJson} from "../utils";
import AceEditor from "react-ace"; import AceEditor from "react-ace";
import { FormGroup } from "../components/filler"; import { FormGroup } from "../components/filler";
import { validJson } from "../utils";
import "./lookup-edit-dialog.scss";
export interface LookupEditDialogProps extends React.Props<any> { export interface LookupEditDialogProps extends React.Props<any> {
isOpen: boolean, isOpen: boolean;
onClose: () => void, onClose: () => void;
onSubmit: () => void, onSubmit: () => void;
onChange: (field: string, value: string) => void onChange: (field: string, value: string) => void;
lookupName: string, lookupName: string;
lookupTier: string, lookupTier: string;
lookupVersion: string, lookupVersion: string;
lookupSpec: string, lookupSpec: string;
isEdit: boolean, isEdit: boolean;
allLookupTiers: string[] allLookupTiers: string[];
} }
export interface LookupEditDialogState { export interface LookupEditDialogState {
@ -45,7 +47,7 @@ export class LookupEditDialog extends React.Component<LookupEditDialogProps, Loo
super(props); super(props);
this.state = { this.state = {
} };
} }
private addISOVersion = () => { private addISOVersion = () => {
@ -62,21 +64,21 @@ export class LookupEditDialog extends React.Component<LookupEditDialogProps, Loo
<InputGroup <InputGroup
value={lookupTier} value={lookupTier}
onChange={(e: any) => onChange("lookupEditTier", e.target.value)} onChange={(e: any) => onChange("lookupEditTier", e.target.value)}
disabled={true} disabled
/> />
</FormGroup> </FormGroup>;
} else { } else {
return <FormGroup className={"lookup-label"} label={"Tier:"}> return <FormGroup className={"lookup-label"} label={"Tier:"}>
<div className="pt-select"> <div className="pt-select">
<select disabled={isEdit} value={lookupTier} onChange={(e: any) => onChange("lookupEditTier", e.target.value)}> <select disabled={isEdit} value={lookupTier} onChange={(e: any) => onChange("lookupEditTier", e.target.value)}>
{ {
allLookupTiers.map(tier => { allLookupTiers.map(tier => {
return <option key={tier} value={tier}>{tier}</option> return <option key={tier} value={tier}>{tier}</option>;
}) })
} }
</select> </select>
</div> </div>
</FormGroup> </FormGroup>;
} }
} }
@ -131,7 +133,7 @@ export class LookupEditDialog extends React.Component<LookupEditDialogProps, Loo
setOptions={{ setOptions={{
enableBasicAutocompletion: false, enableBasicAutocompletion: false,
enableLiveAutocompletion: false, enableLiveAutocompletion: false,
tabSize: 2, tabSize: 2
}} }}
/> />

View File

@ -16,17 +16,19 @@
* limitations under the License. * limitations under the License.
*/ */
import * as React from 'react';
import axios from 'axios';
import { Button } from "@blueprintjs/core"; import { Button } from "@blueprintjs/core";
import axios from 'axios';
import * as React from 'react';
import { FormGroup, IconNames } from '../components/filler'; import { FormGroup, IconNames } from '../components/filler';
import { RuleEditor, Rule } from '../components/rule-editor'; import { Rule, RuleEditor } from '../components/rule-editor';
import { SnitchDialog } from './snitch-dialog'; import { SnitchDialog } from './snitch-dialog';
import './retention-dialog.scss'; import './retention-dialog.scss';
export function reorderArray<T>(items: T[], oldIndex: number, newIndex: number): T[] { export function reorderArray<T>(items: T[], oldIndex: number, newIndex: number): T[] {
let newItems = items.concat(); const newItems = items.concat();
if (newIndex > oldIndex) newIndex--; if (newIndex > oldIndex) newIndex--;
@ -111,7 +113,7 @@ export class RetentionDialog extends React.Component<RetentionDialogProps, Reten
onDelete={() => this.onDeleteRule(index)} onDelete={() => this.onDeleteRule(index)}
moveUp={index > 0 ? () => this.moveRule(index, -1) : null} moveUp={index > 0 ? () => this.moveRule(index, -1) : null}
moveDown={index < (currentRules || []).length - 1 ? () => this.moveRule(index, 2) : null} moveDown={index < (currentRules || []).length - 1 ? () => this.moveRule(index, 2) : null}
/> />;
} }
reset = () => { reset = () => {

View File

@ -16,17 +16,17 @@
* limitations under the License. * limitations under the License.
*/ */
import * as React from 'react';
import { import {
Button, Button,
InputGroup, Classes,
Dialog, Dialog,
IDialogProps, IDialogProps,
Classes, InputGroup,
Intent, Intent
} from "@blueprintjs/core"; } from "@blueprintjs/core";
import { IconNames, FormGroup } from '../components/filler'; import * as React from 'react';
import { FormGroup, IconNames } from '../components/filler';
export interface SnitchDialogProps extends IDialogProps { export interface SnitchDialogProps extends IDialogProps {
onSave: (author: string, comment: string) => void; onSave: (author: string, comment: string) => void;
@ -50,7 +50,7 @@ export class SnitchDialog extends React.Component<SnitchDialogProps, SnitchDialo
comment: "", comment: "",
author: "", author: "",
saveDisabled: true saveDisabled: true
} };
} }
save = () => { save = () => {
@ -136,7 +136,7 @@ export class SnitchDialog extends React.Component<SnitchDialogProps, SnitchDialo
? <Button disabled={saveDisabled} text="Save" onClick={this.save} intent={Intent.PRIMARY as any} rightIconName={IconNames.TICK}/> ? <Button disabled={saveDisabled} text="Save" onClick={this.save} intent={Intent.PRIMARY as any} rightIconName={IconNames.TICK}/>
: <Button disabled={saveDisabled} text="Next" onClick={this.goToFinalStep} intent={Intent.PRIMARY as any} rightIconName={IconNames.ARROW_RIGHT}/> : <Button disabled={saveDisabled} text="Next" onClick={this.goToFinalStep} intent={Intent.PRIMARY as any} rightIconName={IconNames.ARROW_RIGHT}/>
} }
</div> </div>;
} }
render() { render() {

View File

@ -16,12 +16,13 @@
* limitations under the License. * limitations under the License.
*/ */
import * as React from "react";
import { Button, Classes, Dialog, Intent } from "@blueprintjs/core"; import { Button, Classes, Dialog, Intent } from "@blueprintjs/core";
import "./spec-dialog.scss" import "brace/mode/json";
import AceEditor from "react-ace";
import "brace/theme/solarized_dark"; import "brace/theme/solarized_dark";
import "brace/mode/json" import * as React from "react";
import AceEditor from "react-ace";
import "./spec-dialog.scss";
export interface SpecDialogProps extends React.Props<any> { export interface SpecDialogProps extends React.Props<any> {
onSubmit: (spec: JSON) => void; onSubmit: (spec: JSON) => void;
@ -47,7 +48,7 @@ export class SpecDialog extends React.Component<SpecDialogProps, SpecDialogState
super(props); super(props);
this.state = { this.state = {
spec: "" spec: ""
} };
} }
private postSpec(): void { private postSpec(): void {
@ -72,11 +73,11 @@ export class SpecDialog extends React.Component<SpecDialogProps, SpecDialogState
mode="json" mode="json"
theme="solarized_dark" theme="solarized_dark"
className={"post-spec-dialog-textarea"} className={"post-spec-dialog-textarea"}
onChange={ (e) => {this.setState({ spec: e })} } onChange={(e) => { this.setState({ spec: e }); }}
fontSize={12} fontSize={12}
showPrintMargin={false} showPrintMargin={false}
showGutter={true} showGutter
highlightActiveLine={true} highlightActiveLine
value={spec} value={spec}
width={"100%"} width={"100%"}
setOptions={{ setOptions={{
@ -84,7 +85,7 @@ export class SpecDialog extends React.Component<SpecDialogProps, SpecDialogState
enableLiveAutocompletion: true, enableLiveAutocompletion: true,
showLineNumbers: true, showLineNumbers: true,
enableSnippets: true, enableSnippets: true,
tabSize: 2, tabSize: 2
}} }}
/> />
<div className={Classes.DIALOG_FOOTER}> <div className={Classes.DIALOG_FOOTER}>

View File

@ -19,13 +19,14 @@
import 'es6-shim/es6-shim'; import 'es6-shim/es6-shim';
import 'es7-shim'; // Webpack with automatically pick browser.js which does the shim() import 'es7-shim'; // Webpack with automatically pick browser.js which does the shim()
import * as React from 'react'; import * as React from 'react';
(React as any).PropTypes = require('prop-types'); // Trick blueprint 1.0.1 into accepting React 16 as React 15.
import * as ReactDOM from 'react-dom'; import * as ReactDOM from 'react-dom';
import "./singletons/react-table-defaults";
import "./entry.scss";
import "./bootstrap/a-shim-for-react-props";
import "./bootstrap/react-table-defaults";
import { ConsoleApplication } from './console-application'; import { ConsoleApplication } from './console-application';
import "./entry.scss";
const container = document.getElementsByClassName('app-container')[0]; const container = document.getElementsByClassName('app-container')[0];
if (!container) throw new Error('container not found'); if (!container) throw new Error('container not found');

View File

@ -20,5 +20,5 @@ import { Position, Toaster } from "@blueprintjs/core";
export const AppToaster = Toaster.create({ export const AppToaster = Toaster.create({
className: "recipe-toaster", className: "recipe-toaster",
position: Position.TOP, position: Position.TOP
}); });

View File

@ -17,14 +17,14 @@
*/ */
import { Button, InputGroup, Intent } from '@blueprintjs/core'; import { Button, InputGroup, Intent } from '@blueprintjs/core';
import { IconNames, HTMLSelect } from "../components/filler";
import * as numeral from "numeral"; import * as numeral from "numeral";
import * as React from 'react'; import * as React from 'react';
import { Filter, FilterRender } from 'react-table'; import { Filter, FilterRender } from 'react-table';
import { HTMLSelect, IconNames } from "../components/filler";
export function addFilter(filters: Filter[], id: string, value: string): Filter[] { export function addFilter(filters: Filter[], id: string, value: string): Filter[] {
let currentFilter = filters.find(f => f.id === id); const currentFilter = filters.find(f => f.id === id);
if (currentFilter) { if (currentFilter) {
filters = filters.filter(f => f.id !== id); filters = filters.filter(f => f.id !== id);
if (currentFilter.value !== value) { if (currentFilter.value !== value) {
@ -36,7 +36,7 @@ export function addFilter(filters: Filter[], id: string, value: string): Filter[
return filters; return filters;
} }
export function makeTextFilter(placeholder: string = ''): FilterRender { export function makeTextFilter(placeholder = ''): FilterRender {
return ({ filter, onChange, key }) => { return ({ filter, onChange, key }) => {
const filterValue = filter ? filter.value : ''; const filterValue = filter ? filter.value : '';
return <InputGroup return <InputGroup
@ -45,8 +45,8 @@ export function makeTextFilter(placeholder: string = ''): FilterRender {
value={filterValue} value={filterValue}
rightElement={filterValue ? <Button iconName={IconNames.CROSS} className="pt-minimal" onClick={() => onChange('')} /> : undefined} rightElement={filterValue ? <Button iconName={IconNames.CROSS} className="pt-minimal" onClick={() => onChange('')} /> : undefined}
placeholder={placeholder} placeholder={placeholder}
/> />;
} };
} }
export function makeBooleanFilter(): FilterRender { export function makeBooleanFilter(): FilterRender {
@ -57,13 +57,13 @@ export function makeBooleanFilter(): FilterRender {
style={{ width: '100%' }} style={{ width: '100%' }}
onChange={(event: any) => onChange(event.target.value)} onChange={(event: any) => onChange(event.target.value)}
value={filterValue || "all"} value={filterValue || "all"}
fill={true} fill
> >
<option value="all">Show all</option> <option value="all">Show all</option>
<option value="true">true</option> <option value="true">true</option>
<option value="false">false</option> <option value="false">false</option>
</HTMLSelect>; </HTMLSelect>;
} };
} }
// ---------------------------- // ----------------------------

View File

@ -74,7 +74,7 @@ export class QueryManager<Q, R> {
private run() { private run() {
this.lastQuery = this.nextQuery; this.lastQuery = this.nextQuery;
this.currentQueryId++; this.currentQueryId++;
let myQueryId = this.currentQueryId; const myQueryId = this.currentQueryId;
this.actuallyLoading = true; this.actuallyLoading = true;
this.processQuery(this.lastQuery) this.processQuery(this.lastQuery)
@ -95,9 +95,9 @@ export class QueryManager<Q, R> {
result: null, result: null,
loading: false, loading: false,
error: e.message error: e.message
}) });
} }
) );
} }
private trigger() { private trigger() {

View File

@ -16,7 +16,6 @@
* limitations under the License. * limitations under the License.
*/ */
export interface HeaderRows { export interface HeaderRows {
header: string[]; header: string[];
rows: any[][]; rows: any[][];
@ -86,7 +85,11 @@ export function decodeRune(runeQuery: any, runeResult: any[]): HeaderRows {
throw new Error(`Unsupported query type in treatQueryTypeAs: '${treatQueryTypeAs}'`); throw new Error(`Unsupported query type in treatQueryTypeAs: '${treatQueryTypeAs}'`);
} }
} else { } else {
throw new Error(`Unsupported query type '${queryType}'. Supported query types are: '${SUPPORTED_QUERY_TYPES.join("', '")}'. If this is a custom query you can parse the result as a known query type by setting 'treatQueryTypeAs' in the context to one of the supported types.`); throw new Error([
`Unsupported query type '${queryType}'.`,
`Supported query types are: '${SUPPORTED_QUERY_TYPES.join("', '")}'.`,
`If this is a custom query you can parse the result as a known query type by setting 'treatQueryTypeAs' in the context to one of the supported types.`
].join(' '));
} }
} }

View File

@ -16,25 +16,26 @@
* limitations under the License. * limitations under the License.
*/ */
import { Button, Intent, Switch } from "@blueprintjs/core";
import axios from 'axios'; import axios from 'axios';
import * as React from 'react';
import * as classNames from 'classnames'; import * as classNames from 'classnames';
import * as React from 'react';
import ReactTable from "react-table"; import ReactTable from "react-table";
import { Filter } from "react-table"; import { Filter } from "react-table";
import { Button, Intent, Switch } from "@blueprintjs/core";
import { IconNames } from "../components/filler"; import { IconNames } from "../components/filler";
import { AppToaster } from '../singletons/toaster';
import { RuleEditor } from '../components/rule-editor'; import { RuleEditor } from '../components/rule-editor';
import { AsyncActionDialog } from '../dialogs/async-action-dialog'; import { AsyncActionDialog } from '../dialogs/async-action-dialog';
import { RetentionDialog } from '../dialogs/retention-dialog'; import { RetentionDialog } from '../dialogs/retention-dialog';
import { AppToaster } from '../singletons/toaster';
import { import {
addFilter, addFilter,
formatNumber,
formatBytes,
countBy, countBy,
formatBytes,
formatNumber,
getDruidErrorMessage,
lookupBy, lookupBy,
QueryManager, pluralIfNeeded, queryDruidSql, QueryManager
pluralIfNeeded, queryDruidSql, getDruidErrorMessage
} from "../utils"; } from "../utils";
import "./datasource-view.scss"; import "./datasource-view.scss";
@ -54,7 +55,7 @@ export interface DatasourcesViewState {
datasourcesLoading: boolean; datasourcesLoading: boolean;
datasources: Datasource[] | null; datasources: Datasource[] | null;
tiers: string[]; tiers: string[];
defaultRules: any[] defaultRules: any[];
datasourcesError: string | null; datasourcesError: string | null;
datasourcesFilter: Filter[]; datasourcesFilter: Filter[];
@ -73,7 +74,7 @@ export class DatasourcesView extends React.Component<DatasourcesViewProps, Datas
static formatRules(rules: any[]): string { static formatRules(rules: any[]): string {
if (rules.length === 0) { if (rules.length === 0) {
return 'No rules'; return 'No rules';
} if (rules.length <= 2) { } else if (rules.length <= 2) {
return rules.map(RuleEditor.ruleToString).join(', '); return rules.map(RuleEditor.ruleToString).join(', ');
} else { } else {
return `${RuleEditor.ruleToString(rules[0])} +${rules.length - 1} more rules`; return `${RuleEditor.ruleToString(rules[0])} +${rules.length - 1} more rules`;
@ -161,7 +162,7 @@ GROUP BY 1`);
return <AsyncActionDialog return <AsyncActionDialog
action={ action={
dropDataDatasource ? async () => { dropDataDatasource ? async () => {
const resp = await axios.delete(`/druid/coordinator/v1/datasources/${dropDataDatasource}`, {}) const resp = await axios.delete(`/druid/coordinator/v1/datasources/${dropDataDatasource}`, {});
return resp.data; return resp.data;
} : null } : null
} }
@ -186,7 +187,7 @@ GROUP BY 1`);
return <AsyncActionDialog return <AsyncActionDialog
action={ action={
enableDatasource ? async () => { enableDatasource ? async () => {
const resp = await axios.post(`/druid/coordinator/v1/datasources/${enableDatasource}`, {}) const resp = await axios.post(`/druid/coordinator/v1/datasources/${enableDatasource}`, {});
return resp.data; return resp.data;
} : null } : null
} }
@ -291,7 +292,7 @@ GROUP BY 1`);
let data = datasources || []; let data = datasources || [];
if (!showDisabled) { if (!showDisabled) {
data = data.filter(d => !d.disabled) data = data.filter(d => !d.disabled);
} }
return <> return <>
@ -299,7 +300,7 @@ GROUP BY 1`);
data={data} data={data}
loading={datasourcesLoading} loading={datasourcesLoading}
noDataText={!datasourcesLoading && datasources && !datasources.length ? 'No datasources' : (datasourcesError || '')} noDataText={!datasourcesLoading && datasources && !datasources.length ? 'No datasources' : (datasourcesError || '')}
filterable={true} filterable
filtered={datasourcesFilter} filtered={datasourcesFilter}
onFilteredChange={(filtered, column) => { onFilteredChange={(filtered, column) => {
this.setState({ datasourcesFilter: filtered }); this.setState({ datasourcesFilter: filtered });
@ -311,7 +312,7 @@ GROUP BY 1`);
width: 150, width: 150,
Cell: row => { Cell: row => {
const value = row.value; const value = row.value;
return <a onClick={() => { this.setState({ datasourcesFilter: addFilter(datasourcesFilter, 'datasource', value) }) }}>{value}</a> return <a onClick={() => { this.setState({ datasourcesFilter: addFilter(datasourcesFilter, 'datasource', value) }); }}>{value}</a>;
} }
}, },
{ {
@ -415,11 +416,11 @@ GROUP BY 1`);
return <div> return <div>
<a onClick={() => this.setState({ enableDatasource: datasource })}>Enable</a>&nbsp;&nbsp;&nbsp; <a onClick={() => this.setState({ enableDatasource: datasource })}>Enable</a>&nbsp;&nbsp;&nbsp;
<a onClick={() => this.setState({ killDatasource: datasource })}>Permanently delete</a> <a onClick={() => this.setState({ killDatasource: datasource })}>Permanently delete</a>
</div> </div>;
} else { } else {
return <div> return <div>
<a onClick={() => this.setState({ dropDataDatasource: datasource })}>Drop data</a> <a onClick={() => this.setState({ dropDataDatasource: datasource })}>Drop data</a>
</div> </div>;
} }
} }
} }
@ -458,7 +459,6 @@ GROUP BY 1`);
/> />
</div> </div>
{this.renderDatasourceTable()} {this.renderDatasourceTable()}
</div> </div>;
} }
} }

View File

@ -17,10 +17,12 @@
*/ */
import axios from 'axios'; import axios from 'axios';
import * as React from 'react';
import * as classNames from 'classnames'; import * as classNames from 'classnames';
import { H5, Card, Icon, IconNames } from "../components/filler"; import * as React from 'react';
import { QueryManager, pluralIfNeeded, queryDruidSql, getHeadProp } from '../utils';
import { Card, H5, Icon, IconNames } from "../components/filler";
import { getHeadProp, pluralIfNeeded, queryDruidSql, QueryManager } from '../utils';
import './home-view.scss'; import './home-view.scss';
export interface CardOptions { export interface CardOptions {
@ -164,14 +166,14 @@ export class HomeView extends React.Component<HomeViewProps, HomeViewState> {
this.taskQueryManager = new QueryManager({ this.taskQueryManager = new QueryManager({
processQuery: async (query) => { processQuery: async (query) => {
const taskCountsFromSql = await queryDruidSql({ query }); const taskCountsFromSql = await queryDruidSql({ query });
let taskCounts = { const taskCounts = {
successTaskCount: 0, successTaskCount: 0,
failedTaskCount: 0, failedTaskCount: 0,
runningTaskCount: 0, runningTaskCount: 0,
waitingTaskCount: 0, waitingTaskCount: 0,
pendingTaskCount: 0 pendingTaskCount: 0
}; };
for (let dataStatus of taskCountsFromSql) { for (const dataStatus of taskCountsFromSql) {
if (dataStatus.status === "SUCCESS") { if (dataStatus.status === "SUCCESS") {
taskCounts.successTaskCount = dataStatus.count; taskCounts.successTaskCount = dataStatus.count;
} else if (dataStatus.status === "FAILED") { } else if (dataStatus.status === "FAILED") {
@ -254,7 +256,7 @@ GROUP BY 1`);
renderCard(cardOptions: CardOptions): JSX.Element { renderCard(cardOptions: CardOptions): JSX.Element {
return <a href={cardOptions.href} target={cardOptions.href[0] === '/' ? '_blank' : undefined}> return <a href={cardOptions.href} target={cardOptions.href[0] === '/' ? '_blank' : undefined}>
<Card interactive={true}> <Card interactive>
<H5><Icon color="#bfccd5" icon={cardOptions.icon}/>&nbsp;{cardOptions.title}</H5> <H5><Icon color="#bfccd5" icon={cardOptions.icon}/>&nbsp;{cardOptions.title}</H5>
{cardOptions.loading ? <p>Loading...</p> : (cardOptions.error ? `Error: ${cardOptions.error}` : cardOptions.content)} {cardOptions.loading ? <p>Loading...</p> : (cardOptions.error ? `Error: ${cardOptions.error}` : cardOptions.content)}
</Card> </Card>
@ -321,7 +323,6 @@ GROUP BY 1`);
</>, </>,
error: state.dataServerCountError error: state.dataServerCountError
})} })}
</div> </div>;
} }
} }

View File

@ -16,15 +16,17 @@
* limitations under the License. * limitations under the License.
*/ */
import { Button, Intent } from "@blueprintjs/core";
import axios from 'axios'; import axios from 'axios';
import * as React from 'react';
import * as classNames from 'classnames'; import * as classNames from 'classnames';
import * as React from 'react';
import ReactTable from "react-table"; import ReactTable from "react-table";
import { Filter } from "react-table"; import { Filter } from "react-table";
import { Button, Intent } from "@blueprintjs/core";
import {getDruidErrorMessage, QueryManager} from "../utils";
import { LookupEditDialog } from "../dialogs/lookup-edit-dialog"; import { LookupEditDialog } from "../dialogs/lookup-edit-dialog";
import { AppToaster } from "../singletons/toaster"; import { AppToaster } from "../singletons/toaster";
import { getDruidErrorMessage, QueryManager } from "../utils";
import "./lookups-view.scss"; import "./lookups-view.scss";
export interface LookupsViewProps extends React.Props<any> { export interface LookupsViewProps extends React.Props<any> {
@ -32,16 +34,16 @@ export interface LookupsViewProps extends React.Props<any> {
} }
export interface LookupsViewState { export interface LookupsViewState {
lookups: {}[], lookups: {}[];
loadingLookups: boolean, loadingLookups: boolean;
lookupsError: string | null, lookupsError: string | null;
lookupEditDialogOpen: boolean, lookupEditDialogOpen: boolean;
lookupEditName: string, lookupEditName: string;
lookupEditTier: string, lookupEditTier: string;
lookupEditVersion: string, lookupEditVersion: string;
lookupEditSpec: string, lookupEditSpec: string;
isEdit: boolean, isEdit: boolean;
allLookupTiers: string[] allLookupTiers: string[];
} }
export class LookupsView extends React.Component<LookupsViewProps, LookupsViewState> { export class LookupsView extends React.Component<LookupsViewProps, LookupsViewState> {
@ -70,15 +72,15 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
const tiersResp = await axios.get('/druid/coordinator/v1/tiers'); const tiersResp = await axios.get('/druid/coordinator/v1/tiers');
const tiers = tiersResp.data; const tiers = tiersResp.data;
let lookupEntries: {}[] = []; const lookupEntries: {}[] = [];
const lookupResp = await axios.get("/druid/coordinator/v1/lookups/config/all"); const lookupResp = await axios.get("/druid/coordinator/v1/lookups/config/all");
const lookupData = lookupResp.data; const lookupData = lookupResp.data;
Object.keys(lookupData).map((tier: string) => { Object.keys(lookupData).map((tier: string) => {
const lookupIds = lookupData[tier]; const lookupIds = lookupData[tier];
Object.keys(lookupIds).map((id: string) => { Object.keys(lookupIds).map((id: string) => {
lookupEntries.push({tier: tier, id: id, version:lookupData[tier][id].version, spec: lookupData[tier][id].lookupExtractorFactory},); lookupEntries.push({tier, id, version: lookupData[tier][id].version, spec: lookupData[tier][id].lookupExtractorFactory});
}) });
}) });
return { return {
lookupEntries, lookupEntries,
tiers tiers
@ -123,7 +125,7 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
intent: Intent.DANGER, intent: Intent.DANGER,
message: getDruidErrorMessage(e) message: getDruidErrorMessage(e)
} }
) );
} }
} }
@ -156,7 +158,7 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
private changeLookup(field: string, value: string) { private changeLookup(field: string, value: string) {
this.setState({ this.setState({
[field]: value [field]: value
} as any) } as any);
} }
private async submitLookupEdit() { private async submitLookupEdit() {
@ -184,7 +186,7 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
await axios.post(endpoint, dataJSON); await axios.post(endpoint, dataJSON);
this.setState({ this.setState({
lookupEditDialogOpen: false lookupEditDialogOpen: false
}) });
this.lookupsGetQueryManager.rerunLastQuery(); this.lookupsGetQueryManager.rerunLastQuery();
} catch (e) { } catch (e) {
AppToaster.show( AppToaster.show(
@ -193,7 +195,7 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
intent: Intent.DANGER, intent: Intent.DANGER,
message: getDruidErrorMessage(e) message: getDruidErrorMessage(e)
} }
) );
} }
} }
@ -211,43 +213,43 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
text="Initialize Lookup" text="Initialize Lookup"
onClick={() => this.initializeLookup()} onClick={() => this.initializeLookup()}
/> />
</div> </div>;
} }
return <> return <>
<ReactTable <ReactTable
data={lookups} data={lookups}
loading={loadingLookups} loading={loadingLookups}
noDataText={!loadingLookups && lookups && !lookups.length ? 'No lookups' : (lookupsError || '')} noDataText={!loadingLookups && lookups && !lookups.length ? 'No lookups' : (lookupsError || '')}
filterable={true} filterable
columns={[ columns={[
{ {
Header: "Lookup Name", Header: "Lookup Name",
id: "lookup_name", id: "lookup_name",
accessor: (row: any) => row.id, accessor: (row: any) => row.id,
filterable: true, filterable: true
}, },
{ {
Header: "Tier", Header: "Tier",
id: "tier", id: "tier",
accessor: (row: any) => row.tier, accessor: (row: any) => row.tier,
filterable: true, filterable: true
}, },
{ {
Header: "Type", Header: "Type",
id: "type", id: "type",
accessor: (row: any) => row.spec.type, accessor: (row: any) => row.spec.type,
filterable: true, filterable: true
}, },
{ {
Header: "Version", Header: "Version",
id: "version", id: "version",
accessor: (row: any) => row.version, accessor: (row: any) => row.version,
filterable: true, filterable: true
}, },
{ {
Header: "Config", Header: "Config",
id: "config", id: "config",
accessor: row => {return {id: row.id, tier: row.tier};}, accessor: row => ({id: row.id, tier: row.tier}),
filterable: false, filterable: false,
Cell: (row: any) => { Cell: (row: any) => {
const lookupId = row.value.id; const lookupId = row.value.id;
@ -256,7 +258,7 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
<a onClick={() => this.openLookupEditDialog(lookupTier, lookupId)}>Edit</a> <a onClick={() => this.openLookupEditDialog(lookupTier, lookupId)}>Edit</a>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
<a onClick={() => this.deleteLookup(lookupTier, lookupId)}>Delete</a> <a onClick={() => this.deleteLookup(lookupTier, lookupId)}>Delete</a>
</div> </div>;
} }
} }
]} ]}
@ -267,7 +269,7 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
} }
renderLookupEditDialog() { renderLookupEditDialog() {
const { lookupEditDialogOpen, allLookupTiers, lookupEditSpec, lookupEditTier, lookupEditName, lookupEditVersion, isEdit } = this.state const { lookupEditDialogOpen, allLookupTiers, lookupEditSpec, lookupEditTier, lookupEditName, lookupEditVersion, isEdit } = this.state;
return <LookupEditDialog return <LookupEditDialog
isOpen={lookupEditDialogOpen} isOpen={lookupEditDialogOpen}
@ -280,7 +282,7 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
lookupVersion={lookupEditVersion} lookupVersion={lookupEditVersion}
isEdit={isEdit} isEdit={isEdit}
allLookupTiers={allLookupTiers} allLookupTiers={allLookupTiers}
/> />;
} }
render() { render() {
@ -301,6 +303,6 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
</div> </div>
{this.renderLookupsTable()} {this.renderLookupsTable()}
{this.renderLookupEditDialog()} {this.renderLookupEditDialog()}
</div> </div>;
} }
} }

View File

@ -16,22 +16,24 @@
* limitations under the License. * limitations under the License.
*/ */
import { Button } from "@blueprintjs/core";
import axios from 'axios'; import axios from 'axios';
import * as React from 'react';
import * as classNames from 'classnames'; import * as classNames from 'classnames';
import * as React from 'react';
import ReactTable from "react-table"; import ReactTable from "react-table";
import { Filter } from "react-table"; import { Filter } from "react-table";
import { Button } from "@blueprintjs/core";
import { H5, IconNames } from "../components/filler"; import { H5, IconNames } from "../components/filler";
import { import {
addFilter, addFilter,
makeBooleanFilter,
QueryManager,
formatBytes, formatBytes,
formatNumber, formatNumber,
makeBooleanFilter,
parseList, parseList,
queryDruidSql queryDruidSql,
QueryManager
} from "../utils"; } from "../utils";
import "./segments-view.scss"; import "./segments-view.scss";
export interface SegmentsViewProps extends React.Props<any> { export interface SegmentsViewProps extends React.Props<any> {
@ -88,7 +90,7 @@ export class SegmentsView extends React.Component<SegmentsViewProps, SegmentsVie
segmentsError: error segmentsError: error
}); });
} }
}) });
} }
componentWillUnmount(): void { componentWillUnmount(): void {
@ -99,7 +101,7 @@ export class SegmentsView extends React.Component<SegmentsViewProps, SegmentsVie
const { page, pageSize, filtered, sorted } = state; const { page, pageSize, filtered, sorted } = state;
const totalQuerySize = (page + 1) * pageSize; const totalQuerySize = (page + 1) * pageSize;
let queryParts = [ const queryParts = [
`SELECT "segment_id", "datasource", "start", "end", "size", "version", "partition_num", "num_replicas", "num_rows", "is_published", "is_available", "is_realtime", "payload"`, `SELECT "segment_id", "datasource", "start", "end", "size", "version", "partition_num", "num_replicas", "num_rows", "is_published", "is_available", "is_realtime", "payload"`,
`FROM sys.segments` `FROM sys.segments`
]; ];
@ -114,7 +116,7 @@ export class SegmentsView extends React.Component<SegmentsViewProps, SegmentsVie
}).filter(Boolean); }).filter(Boolean);
if (whereParts.length) { if (whereParts.length) {
queryParts.push('WHERE ' + whereParts.join(' AND ')) queryParts.push('WHERE ' + whereParts.join(' AND '));
} }
if (sorted.length) { if (sorted.length) {
@ -160,7 +162,7 @@ export class SegmentsView extends React.Component<SegmentsViewProps, SegmentsVie
accessor: "datasource", accessor: "datasource",
Cell: row => { Cell: row => {
const value = row.value; const value = row.value;
return <a onClick={() => { this.setState({ segmentFilter: addFilter(segmentFilter, 'datasource', value) }) }}>{value}</a> return <a onClick={() => { this.setState({ segmentFilter: addFilter(segmentFilter, 'datasource', value) }); }}>{value}</a>;
} }
}, },
{ {
@ -170,7 +172,7 @@ export class SegmentsView extends React.Component<SegmentsViewProps, SegmentsVie
defaultSortDesc: true, defaultSortDesc: true,
Cell: row => { Cell: row => {
const value = row.value; const value = row.value;
return <a onClick={() => { this.setState({ segmentFilter: addFilter(segmentFilter, 'start', value) }) }}>{value}</a> return <a onClick={() => { this.setState({ segmentFilter: addFilter(segmentFilter, 'start', value) }); }}>{value}</a>;
} }
}, },
{ {
@ -180,14 +182,14 @@ export class SegmentsView extends React.Component<SegmentsViewProps, SegmentsVie
width: 120, width: 120,
Cell: row => { Cell: row => {
const value = row.value; const value = row.value;
return <a onClick={() => { this.setState({ segmentFilter: addFilter(segmentFilter, 'end', value) }) }}>{value}</a> return <a onClick={() => { this.setState({ segmentFilter: addFilter(segmentFilter, 'end', value) }); }}>{value}</a>;
} }
}, },
{ {
Header: "Version", Header: "Version",
accessor: "version", accessor: "version",
defaultSortDesc: true, defaultSortDesc: true,
width: 120, width: 120
}, },
{ {
Header: "Partition", Header: "Partition",
@ -272,6 +274,6 @@ export class SegmentsView extends React.Component<SegmentsViewProps, SegmentsVie
/> />
</div> </div>
{this.renderSegmentsTable()} {this.renderSegmentsTable()}
</div> </div>;
} }
} }

View File

@ -16,19 +16,21 @@
* limitations under the License. * limitations under the License.
*/ */
import { Button, Switch } from "@blueprintjs/core";
import axios from 'axios'; import axios from 'axios';
import * as React from 'react';
import * as classNames from 'classnames'; import * as classNames from 'classnames';
import { sum } from "d3-array";
import * as React from 'react';
import ReactTable from "react-table"; import ReactTable from "react-table";
import { Filter } from "react-table"; import { Filter } from "react-table";
import { sum } from "d3-array";
import { Button, Switch } from "@blueprintjs/core";
import { IconNames } from '../components/filler'; import { IconNames } from '../components/filler';
import { addFilter, formatBytes, formatBytesCompact, QueryManager, queryDruidSql } from "../utils"; import { addFilter, formatBytes, formatBytesCompact, queryDruidSql, QueryManager } from "../utils";
import "./servers-view.scss"; import "./servers-view.scss";
function formatQueues(segmentsToLoad: number, segmentsToLoadSize: number, segmentsToDrop: number, segmentsToDropSize: number): string { function formatQueues(segmentsToLoad: number, segmentsToLoadSize: number, segmentsToDrop: number, segmentsToDropSize: number): string {
let queueParts: string[] = []; const queueParts: string[] = [];
if (segmentsToLoad) { if (segmentsToLoad) {
queueParts.push(`${segmentsToLoad} segments to load (${formatBytesCompact(segmentsToLoadSize)})`); queueParts.push(`${segmentsToLoad} segments to load (${formatBytesCompact(segmentsToLoadSize)})`);
} }
@ -73,7 +75,7 @@ export class ServersView extends React.Component<ServersViewProps, ServersViewSt
middleManagersLoading: true, middleManagersLoading: true,
middleManagers: null, middleManagers: null,
middleManagersError: null, middleManagersError: null,
middleManagerFilter: props.middleManager ? [{ id: 'host', value: props.middleManager }] : [], middleManagerFilter: props.middleManager ? [{ id: 'host', value: props.middleManager }] : []
}; };
} }
@ -82,7 +84,7 @@ export class ServersView extends React.Component<ServersViewProps, ServersViewSt
processQuery: async (query: string) => { processQuery: async (query: string) => {
const servers = await queryDruidSql({ query }); const servers = await queryDruidSql({ query });
let loadQueueResponse = await axios.get("/druid/coordinator/v1/loadqueue?simple"); const loadQueueResponse = await axios.get("/druid/coordinator/v1/loadqueue?simple");
const loadQueues = loadQueueResponse.data; const loadQueues = loadQueueResponse.data;
return servers.map((s: any) => { return servers.map((s: any) => {
@ -143,7 +145,7 @@ WHERE "server_type" = 'historical'`);
data={servers || []} data={servers || []}
loading={serversLoading} loading={serversLoading}
noDataText={!serversLoading && servers && !servers.length ? 'No historicals' : (serversError || '')} noDataText={!serversLoading && servers && !servers.length ? 'No historicals' : (serversError || '')}
filterable={true} filterable
filtered={serverFilter} filtered={serverFilter}
onFilteredChange={(filtered, column) => { onFilteredChange={(filtered, column) => {
this.setState({ serverFilter: filtered }); this.setState({ serverFilter: filtered });
@ -161,7 +163,7 @@ WHERE "server_type" = 'historical'`);
accessor: "tier", accessor: "tier",
Cell: row => { Cell: row => {
const value = row.value; const value = row.value;
return <a onClick={() => { this.setState({ serverFilter: addFilter(serverFilter, 'tier', value) }) }}>{value}</a> return <a onClick={() => { this.setState({ serverFilter: addFilter(serverFilter, 'tier', value) }); }}>{value}</a>;
} }
}, },
{ {
@ -234,7 +236,7 @@ WHERE "server_type" = 'historical'`);
const segmentsToDrop = sum(originals, s => s.segmentsToDrop); const segmentsToDrop = sum(originals, s => s.segmentsToDrop);
const segmentsToDropSize = sum(originals, s => s.segmentsToDropSize); const segmentsToDropSize = sum(originals, s => s.segmentsToDropSize);
return formatQueues(segmentsToLoad, segmentsToLoadSize, segmentsToDrop, segmentsToDropSize); return formatQueues(segmentsToLoad, segmentsToLoadSize, segmentsToDrop, segmentsToDropSize);
}, }
}, },
{ {
Header: "Host", Header: "Host",
@ -245,7 +247,7 @@ WHERE "server_type" = 'historical'`);
Header: "Port", Header: "Port",
id: 'port', id: 'port',
accessor: (row) => { accessor: (row) => {
let ports: string[] = []; const ports: string[] = [];
if (row.plaintext_port !== -1) { if (row.plaintext_port !== -1) {
ports.push(`${row.plaintext_port} (plain)`); ports.push(`${row.plaintext_port} (plain)`);
} }
@ -255,7 +257,7 @@ WHERE "server_type" = 'historical'`);
return ports.join(', ') || 'No port'; return ports.join(', ') || 'No port';
}, },
Aggregated: () => '' Aggregated: () => ''
}, }
]} ]}
defaultPageSize={10} defaultPageSize={10}
className="-striped -highlight" className="-striped -highlight"
@ -270,7 +272,7 @@ WHERE "server_type" = 'historical'`);
data={middleManagers || []} data={middleManagers || []}
loading={middleManagersLoading} loading={middleManagersLoading}
noDataText={!middleManagersLoading && middleManagers && !middleManagers.length ? 'No MiddleManagers' : (middleManagersError || '')} noDataText={!middleManagersLoading && middleManagers && !middleManagers.length ? 'No MiddleManagers' : (middleManagersError || '')}
filterable={true} filterable
filtered={middleManagerFilter} filtered={middleManagerFilter}
onFilteredChange={(filtered, column) => { onFilteredChange={(filtered, column) => {
this.setState({ middleManagerFilter: filtered }); this.setState({ middleManagerFilter: filtered });
@ -282,7 +284,7 @@ WHERE "server_type" = 'historical'`);
accessor: (row) => row.worker.host, accessor: (row) => row.worker.host,
Cell: row => { Cell: row => {
const value = row.value; const value = row.value;
return <a onClick={() => { this.setState({ middleManagerFilter: addFilter(middleManagerFilter, 'host', value) }) }}>{value}</a> return <a onClick={() => { this.setState({ middleManagerFilter: addFilter(middleManagerFilter, 'host', value) }); }}>{value}</a>;
} }
}, },
{ {
@ -297,7 +299,7 @@ WHERE "server_type" = 'historical'`);
id: "availabilityGroups", id: "availabilityGroups",
width: 60, width: 60,
accessor: (row) => row.availabilityGroups.length, accessor: (row) => row.availabilityGroups.length,
filterable: false, filterable: false
}, },
{ {
Header: "Last completed task time", Header: "Last completed task time",
@ -362,7 +364,6 @@ WHERE "server_type" = 'historical'`);
/> />
</div> </div>
{this.renderMiddleManagerTable()} {this.renderMiddleManagerTable()}
</div> </div>;
} }
} }

View File

@ -17,19 +17,21 @@
*/ */
import axios from 'axios'; import axios from 'axios';
import * as React from 'react';
import * as classNames from 'classnames'; import * as classNames from 'classnames';
import ReactTable from "react-table";
import * as Hjson from "hjson"; import * as Hjson from "hjson";
import * as React from 'react';
import ReactTable from "react-table";
import { SqlControl } from '../components/sql-control'; import { SqlControl } from '../components/sql-control';
import { import {
QueryManager,
localStorageSet,
localStorageGet,
decodeRune, decodeRune,
HeaderRows, HeaderRows,
queryDruidRune, queryDruidSql localStorageGet,
localStorageSet,
queryDruidRune,
queryDruidSql, QueryManager
} from '../utils'; } from '../utils';
import "./sql-view.scss"; import "./sql-view.scss";
export interface SqlViewProps extends React.Props<any> { export interface SqlViewProps extends React.Props<any> {
@ -84,7 +86,7 @@ export class SqlView extends React.Component<SqlViewProps, SqlViewState> {
error error
}); });
} }
}) });
} }
componentWillUnmount(): void { componentWillUnmount(): void {
@ -117,7 +119,6 @@ export class SqlView extends React.Component<SqlViewProps, SqlViewState> {
}} }}
/> />
{this.renderResultTable()} {this.renderResultTable()}
</div> </div>;
} }
} }

View File

@ -16,17 +16,19 @@
* limitations under the License. * limitations under the License.
*/ */
import { Alert, Button, Intent } from "@blueprintjs/core";
import axios from 'axios'; import axios from 'axios';
import * as React from 'react';
import * as classNames from 'classnames'; import * as classNames from 'classnames';
import * as React from 'react';
import ReactTable from "react-table"; import ReactTable from "react-table";
import { Filter } from "react-table"; import { Filter } from "react-table";
import { Button, Intent, Alert } from "@blueprintjs/core";
import { ButtonGroup, Label, IconNames } from "../components/filler"; import { ButtonGroup, IconNames, Label } from "../components/filler";
import { addFilter, QueryManager, getDruidErrorMessage, countBy, formatDuration, queryDruidSql } from "../utils";
import { AsyncActionDialog } from "../dialogs/async-action-dialog"; import { AsyncActionDialog } from "../dialogs/async-action-dialog";
import { SpecDialog } from "../dialogs/spec-dialog"; import { SpecDialog } from "../dialogs/spec-dialog";
import { AppToaster } from '../singletons/toaster'; import { AppToaster } from '../singletons/toaster';
import { addFilter, countBy, formatDuration, getDruidErrorMessage, queryDruidSql, QueryManager } from "../utils";
import "./tasks-view.scss"; import "./tasks-view.scss";
export interface TasksViewProps extends React.Props<any> { export interface TasksViewProps extends React.Props<any> {
@ -102,7 +104,7 @@ export class TasksView extends React.Component<TasksViewProps, TasksViewState> {
componentDidMount(): void { componentDidMount(): void {
this.supervisorQueryManager = new QueryManager({ this.supervisorQueryManager = new QueryManager({
processQuery: async (query: string) => { processQuery: async (query: string) => {
const resp = await axios.get("/druid/indexer/v1/supervisor?full") const resp = await axios.get("/druid/indexer/v1/supervisor?full");
return resp.data; return resp.data;
}, },
onStateChange: ({ result, loading, error }) => { onStateChange: ({ result, loading, error }) => {
@ -299,7 +301,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
data={supervisors || []} data={supervisors || []}
loading={supervisorsLoading} loading={supervisorsLoading}
noDataText={!supervisorsLoading && supervisors && !supervisors.length ? 'No supervisors' : (supervisorsError || '')} noDataText={!supervisorsLoading && supervisors && !supervisors.length ? 'No supervisors' : (supervisorsError || '')}
filterable={true} filterable
columns={[ columns={[
{ {
Header: "Datasource", Header: "Datasource",
@ -338,7 +340,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
return <span> return <span>
<span <span
style={{ color: value === 'Suspended' ? '#d58512' : '#2167d5' }} style={{ color: value === 'Suspended' ? '#d58512' : '#2167d5' }}
>&#x25cf;&nbsp;</span> >
&#x25cf;&nbsp;
</span>
{value} {value}
</span>; </span>;
} }
@ -363,7 +367,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
{suspendResume}&nbsp;&nbsp;&nbsp; {suspendResume}&nbsp;&nbsp;&nbsp;
<a onClick={() => this.setState({ resetSupervisorId: id })}>Reset</a>&nbsp;&nbsp;&nbsp; <a onClick={() => this.setState({ resetSupervisorId: id })}>Reset</a>&nbsp;&nbsp;&nbsp;
<a onClick={() => this.setState({ terminateSupervisorId: id })}>Terminate</a> <a onClick={() => this.setState({ terminateSupervisorId: id })}>Terminate</a>
</div> </div>;
} }
} }
]} ]}
@ -413,7 +417,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
data={tasks || []} data={tasks || []}
loading={tasksLoading} loading={tasksLoading}
noDataText={!tasksLoading && tasks && !tasks.length ? 'No tasks' : (tasksError || '')} noDataText={!tasksLoading && tasks && !tasks.length ? 'No tasks' : (tasksError || '')}
filterable={true} filterable
filtered={taskFilter} filtered={taskFilter}
onFilteredChange={(filtered, column) => { onFilteredChange={(filtered, column) => {
this.setState({ taskFilter: filtered }); this.setState({ taskFilter: filtered });
@ -432,7 +436,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
accessor: "type", accessor: "type",
Cell: row => { Cell: row => {
const value = row.value; const value = row.value;
return <a onClick={() => { this.setState({ taskFilter: addFilter(taskFilter, 'type', value) }) }}>{value}</a> return <a onClick={() => { this.setState({ taskFilter: addFilter(taskFilter, 'type', value) }); }}>{value}</a>;
} }
}, },
{ {
@ -440,7 +444,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
accessor: "datasource", accessor: "datasource",
Cell: row => { Cell: row => {
const value = row.value; const value = row.value;
return <a onClick={() => { this.setState({ taskFilter: addFilter(taskFilter, 'datasource', value) }) }}>{value}</a> return <a onClick={() => { this.setState({ taskFilter: addFilter(taskFilter, 'datasource', value) }); }}>{value}</a>;
} }
}, },
{ {
@ -462,7 +466,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
return <span> return <span>
<span <span
style={{ color: statusToColor(status) }} style={{ color: statusToColor(status) }}
>&#x25cf;&nbsp;</span> >
&#x25cf;&nbsp;
</span>
{status} {status}
{location && <a onClick={() => goToMiddleManager(locationHostname)} title={`Go to: ${locationHostname}`}>&nbsp;&#x279A;</a>} {location && <a onClick={() => goToMiddleManager(locationHostname)} title={`Go to: ${locationHostname}`}>&nbsp;&#x279A;</a>}
{errorMsg && <a onClick={() => this.setState({ alertErrorMsg: errorMsg })} title={errorMsg}>&nbsp;?</a>} {errorMsg && <a onClick={() => this.setState({ alertErrorMsg: errorMsg })} title={errorMsg}>&nbsp;?</a>}
@ -504,7 +510,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
<a href={`/druid/indexer/v1/task/${id}/log`} target="_blank">Log (all)</a>&nbsp;&nbsp;&nbsp; <a href={`/druid/indexer/v1/task/${id}/log`} target="_blank">Log (all)</a>&nbsp;&nbsp;&nbsp;
<a href={`/druid/indexer/v1/task/${id}/log?offset=-8192`} target="_blank">Log (last 8kb)</a>&nbsp;&nbsp;&nbsp; <a href={`/druid/indexer/v1/task/${id}/log?offset=-8192`} target="_blank">Log (last 8kb)</a>&nbsp;&nbsp;&nbsp;
{(status === 'RUNNING' || status === 'WAITING' || status === 'PENDING') && <a onClick={() => this.setState({ killTaskId: id })}>Kill</a>} {(status === 'RUNNING' || status === 'WAITING' || status === 'PENDING') && <a onClick={() => this.setState({ killTaskId: id })}>Kill</a>}
</div> </div>;
}, },
Aggregated: row => '' Aggregated: row => ''
} }
@ -583,7 +589,6 @@ ORDER BY "rank" DESC, "created_time" DESC`);
> >
<p>{alertErrorMsg}</p> <p>{alertErrorMsg}</p>
</Alert> </Alert>
</div> </div>;
} }
} }

125
web-console/tslint.json Normal file
View File

@ -0,0 +1,125 @@
{
"extends": [
"tslint:recommended",
"tslint-react"
],
"rules": {
"align": true,
"array-type": [true, "array"],
"arrow-parens": false,
"ban": [false,
["_", "extend"],
["_", "isNull"],
["_", "isDefined"]
],
"comment-format": [false,
"check-space"
],
"curly": [true, "ignore-same-line"],
"file-header": [true, "Licensed to the Apache Software Foundation \\(ASF\\).+"],
"forin": false,
"indent": [true, "spaces", 2],
"interface-name": [true, "never-prefix"],
"jsdoc-format": true,
"label-position": true,
"max-line-length": [true, 200],
"max-classes-per-file": false,
"member-access": false,
"member-ordering": [true,
{
"order": [
"static-field",
"static-method",
"instance-field",
"constructor",
"instance-method"
]
}
],
"no-any": false,
"no-arg": true,
"no-console": [true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": [true, "allow-empty-catch"],
"no-empty-interface": false,
"no-eval": true,
"no-inferrable-types": true,
"no-null-keyword": false,
"no-parameter-properties": true,
"no-require-imports": false,
"no-shadowed-variable": false,
"no-string-literal": false,
"no-switch-case-fall-through": false,
"no-trailing-whitespace": true,
"no-unused-expression": false,
"no-use-before-declare": false,
"object-literal-sort-keys": false,
"one-line": [true,
"check-catch",
"check-else",
"check-finally",
"check-open-brace",
"check-whitespace"
],
"ordered-imports": [
true,
{
"import-sources-order": "case-insensitive",
"grouped-imports": true,
"groups": [
{ "name": "parent directories", "match": "^\\.\\.", "order": 20 },
{ "name": "styles", "match": ".scss$", "order": 40 },
{ "name": "current directory", "match": "^\\.", "order": 30 },
{ "name": "libraries", "match": ".*", "order": 10 }
]
}
],
"prefer-const": [true, {"destructuring": "all"}],
"quotemark": [false, "double", "jsx-double", "avoid-escape"],
"semicolon": true,
"switch-default": false,
"trailing-comma": [true, {
"singleline": "never",
"multiline": "never"
}],
"triple-equals": [true, "allow-null-check"],
"typedef": false,
"typedef-whitespace": [true, {
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}, {
"call-signature": "onespace",
"index-signature": "onespace",
"parameter": "onespace",
"property-declaration": "onespace",
"variable-declaration": "onespace"
}],
"variable-name": false,
"whitespace": [true,
"check-branch",
"check-decl",
"check-operator",
"check-module",
"check-separator",
"check-type",
"check-type-operator"
],
"jsx-alignment": true,
"jsx-boolean-value": [true, "never"],
"jsx-curly-spacing": [true, "never"],
"jsx-no-lambda": false,
"jsx-no-multiline-js": false,
"jsx-no-string-ref": true,
"jsx-self-close": true,
"jsx-space-before-trailing-slash": false,
"jsx-wrap-multiline": false
}
}

View File

@ -50,6 +50,20 @@ module.exports = (env) => {
}, },
module: { module: {
rules: [ rules: [
{
test: /\.tsx?$/,
enforce: 'pre',
use: [
{
loader: 'tslint-loader',
options: {
configFile: 'tslint.json',
emitErrors: true,
fix: false // Set this to true to auto fix errors
}
}
]
},
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
use: 'ts-loader', use: 'ts-loader',