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"
}
},
"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": {
"version": "24.1.0",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.1.0.tgz",
@ -1372,6 +1431,12 @@
"integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
"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": {
"version": "3.0.0",
"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",
"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": {
"version": "0.0.0",
"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-loader": "^5.3.3",
"ts-node": "^8.0.2",
"tslint": "^5.14.0",
"tslint-loader": "^3.5.4",
"tslint-react": "^3.6.0",
"typescript": "^3.2.4",
"webpack": "^4.29.0",
"webpack-cli": "^3.2.1",

View File

@ -21,8 +21,35 @@ writefile='lib/sql-function-doc.ts'
> "$writefile"
echo -e "// This file is auto generated and should not be modified\n" > "$writefile"
echo -e 'export const SQLFunctionDoc: any[] = [' >> "$writefile"
cat > "$writefile" <<- EOM
/*
* 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
@ -47,4 +74,4 @@ while read -r line; do
fi
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.
*/
import { Button } from "@blueprintjs/core";
import * as React from 'react';
import { Filter, ReactTableDefaults } from "react-table";
import { Button } from "@blueprintjs/core";
import { Loader } from '../components/loader';
import { countBy, makeTextFilter } from '../utils';

View File

@ -16,11 +16,10 @@
* limitations under the License.
*/
import { resolveSrv } from 'dns';
import * as React from 'react';
import axios from 'axios';
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 {
name: string;
@ -31,8 +30,8 @@ interface Field {
export interface AutoFormProps<T> extends React.Props<any> {
fields: Field[];
model: T | null,
onChange: (newValue: T) => void
model: T | null;
onChange: (newValue: T) => void;
}
export interface AutoFormState<T> {
@ -48,7 +47,7 @@ export class AutoForm<T> extends React.Component<AutoFormProps<T>, AutoFormState
constructor(props: AutoFormProps<T>) {
super(props);
this.state = {
}
};
}
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="False">False</option>
</HTMLSelect>
</HTMLSelect>;
}
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);
return <FormGroup label={label} key={field.name}>
{this.renderFieldInput(field)}
</FormGroup>
</FormGroup>;
}
render() {
@ -135,6 +134,6 @@ export class AutoForm<T> extends React.Component<AutoFormProps<T>, AutoFormState
return <div className="auto-form">
{model && fields.map(field => this.renderField(field))}
</div>
</div>;
}
}

View File

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

View File

@ -16,13 +16,13 @@
* 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 { Button, Classes, AnchorButton, Popover, Position, Menu, MenuItem } from "@blueprintjs/core";
import { IconNames, NavbarGroup, Alignment, NavbarDivider, Navbar } from "../components/filler";
import * as React from 'react';
import { Alignment, IconNames, Navbar, NavbarDivider, NavbarGroup } from "../components/filler";
import { AboutDialog } from "../dialogs/about-dialog";
import { CoordinatorDynamicConfigDialog } from '../dialogs/coordinator-dynamic-config';
import "./header-bar.scss";
import {
DRUID_DOCS,
DRUID_GITHUB,
@ -31,6 +31,8 @@ import {
LEGACY_OVERLORD_CONSOLE
} from '../variables';
import "./header-bar.scss";
export type HeaderActiveTab = null | 'datasources' | 'segments' | 'tasks' | 'servers' | 'sql' | 'lookups';
export interface HeaderBarProps extends React.Props<any> {
@ -54,30 +56,48 @@ export class HeaderBar extends React.Component<HeaderBarProps, HeaderBarState> {
renderLogo() {
return <div className="logo">
<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
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"/>
<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
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
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"/>
<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
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
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"/>
<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
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
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"/>
<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
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
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
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
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
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
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
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>
</div>;
}

View File

@ -17,6 +17,7 @@
*/
import * as React from 'react';
import './loader.scss';
export interface LoaderProps extends React.Props<any> {
@ -31,23 +32,35 @@ export class Loader extends React.Component<LoaderProps, LoaderState> {
render() {
const { loadingText, loading } = this.props;
if (loading === false) return null;
if (!loading) return null;
return <div className="loader">
<div className="loader-logo">
<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,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
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
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"/>
<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
C46.4,69.2,45.8,69.8,45.1,69.8z"/>
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
C46.4,69.2,45.8,69.8,45.1,69.8z"
/>
</svg>
{loadingText ? <div className="label">{loadingText}</div> : null}
</div>
</div>
</div>;
}
}

View File

@ -16,10 +16,12 @@
* limitations under the License.
*/
import * as React from 'react';
import { Button, Collapse, InputGroup } from "@blueprintjs/core";
import axios from 'axios';
import { Button, InputGroup, Collapse } from "@blueprintjs/core";
import { IconNames, FormGroup, HTMLSelect, Card, ControlGroup, NumericInput, TagInput } from "../components/filler";
import * as React from 'react';
import { Card, ControlGroup, FormGroup, HTMLSelect, IconNames, NumericInput, TagInput } from "../components/filler";
import './rule-editor.scss';
export interface Rule {
@ -107,7 +109,7 @@ export class RuleEditor extends React.Component<RuleEditorProps, RuleEditorState
super(props);
this.state = {
isOpen: true
}
};
}
private removeTier = (key: string) => {
@ -126,9 +128,9 @@ export class RuleEditor extends React.Component<RuleEditorProps, RuleEditorState
let newTierName = tiers[0];
if (rule.tieredReplicants) {
for (let i = 0; i < tiers.length; i++) {
if (rule.tieredReplicants[tiers[i]] === undefined) {
newTierName = tiers[i];
for (const tier of tiers) {
if (rule.tieredReplicants[tier] === undefined) {
newTierName = tier;
break;
}
}
@ -160,7 +162,7 @@ export class RuleEditor extends React.Component<RuleEditorProps, RuleEditorState
/>
<Button className="pt-minimal" style={{pointerEvents: 'none'}}>Tier:</Button>
<HTMLSelect
fill={true}
fill
value={tier}
onChange={(e: any) => onChange(RuleEditor.changeTier(rule, tier, e.target.value))}
>
@ -194,7 +196,7 @@ export class RuleEditor extends React.Component<RuleEditorProps, RuleEditorState
return <FormGroup label="Colocated datasources:">
<TagInput
values={rule.colocatedDataSources || []}
onChange={(v: any) => onChange(RuleEditor.changeColocatedDataSources(rule, v)) }
onChange={(v: any) => onChange(RuleEditor.changeColocatedDataSources(rule, v))}
fill
/>
</FormGroup>;
@ -240,21 +242,21 @@ export class RuleEditor extends React.Component<RuleEditorProps, RuleEditorState
<option value="ByPeriod">by period</option>
<option value="ByInterval">by interval</option>
</HTMLSelect>
{ ruleTimeType === 'ByPeriod' && <InputGroup value={rule.period || ''} onChange={(e: any) => onChange(RuleEditor.changePeriod(rule, e.target.value as any))}/>}
{ ruleTimeType === 'ByInterval' && <InputGroup value={rule.interval || ''} onChange={(e: any) => onChange(RuleEditor.changeInterval(rule, e.target.value as any))}/>}
{ruleTimeType === 'ByPeriod' && <InputGroup value={rule.period || ''} onChange={(e: any) => onChange(RuleEditor.changePeriod(rule, e.target.value as any))}/>}
{ruleTimeType === 'ByInterval' && <InputGroup value={rule.interval || ''} onChange={(e: any) => onChange(RuleEditor.changeInterval(rule, e.target.value as any))}/>}
</ControlGroup>
</FormGroup>
{
ruleLoadType === 'load' &&
<FormGroup>
{ this.renderTiers() }
{ this.renderTierAdder() }
{this.renderTiers()}
{this.renderTierAdder()}
</FormGroup>
}
{
ruleLoadType === 'broadcast' &&
<FormGroup>
{ this.renderColocatedDataSources() }
{this.renderColocatedDataSources()}
</FormGroup>
}
</Card>

View File

@ -16,21 +16,24 @@
* limitations under the License.
*/
import * as React from 'react';
import * as ReactDOMServer from 'react-dom/server';
import { Button, Checkbox, Classes, Intent, Popover, Position } from "@blueprintjs/core";
import axios from "axios";
import * as classNames from 'classnames';
import * as ace from 'brace'
import AceEditor from "react-ace";
import 'brace/mode/sql';
import 'brace/mode/hjson';
import 'brace/theme/solarized_dark';
import * as ace from 'brace';
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 { AppToaster } from "../singletons/toaster";
import { IconNames } from './filler';
import './sql-control.scss'
import {AppToaster} from "../singletons/toaster";
import './sql-control.scss';
const langTools = ace.acequire('ace/ext/language_tools');
@ -55,9 +58,8 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
};
}
private addDatasourceAutoCompleter = async (): Promise<any> =>{
const datasourceResp = await axios.post("/druid/v2/sql", { query: `SELECT datasource FROM sys.segments GROUP BY 1`})
private addDatasourceAutoCompleter = async (): Promise<any> => {
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 datasourceName: string = d.datasource;
return {
@ -68,7 +70,7 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
});
const completer = {
getCompletions: (editor:any , session: any, pos: any, prefix: any, callback: any) => {
getCompletions: (editor: any, session: any, pos: any, prefix: any, callback: any) => {
callback(null, datasourceList);
}
};
@ -77,7 +79,7 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
}
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 columnName: string = d.COLUMN_NAME;
return {
@ -88,7 +90,7 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
});
const completer = {
getCompletions: (editor:any , session: any, pos: any, prefix: any, callback: any) => {
getCompletions: (editor: any, session: any, pos: any, prefix: any, callback: any) => {
callback(null, columnNameList);
}
};
@ -97,9 +99,9 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
}
private addFunctionAutoCompleter = (): void => {
const functionList: any[]= SQLFunctionDoc.map((entry: any) => {
let funcName: string = entry.syntax.replace(/\(.*\)/,"()");
if (!funcName.includes("(")) funcName = funcName.substr(0,10);
const functionList: any[] = SQLFunctionDoc.map((entry: any) => {
let funcName: string = entry.syntax.replace(/\(.*\)/, "()");
if (!funcName.includes("(")) funcName = funcName.substr(0, 10);
return {
value: funcName,
score: 80,
@ -107,22 +109,22 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
syntax: entry.syntax,
description: entry.description,
completer: {
insertMatch: (editor:any, data:any) => {
insertMatch: (editor: any, data: any) => {
editor.completer.insertMatch({value: data.caption});
const pos = editor.getCursorPosition();
editor.gotoLine(pos.row+1, pos.column-1);
editor.gotoLine(pos.row + 1, pos.column - 1);
}
}
};
});
const completer = {
getCompletions: (editor:any , session: any, pos: any, prefix: any, callback: any) => {
getCompletions: (editor: any, session: any, pos: any, prefix: any, callback: any) => {
callback(null, functionList);
},
getDocTooltip: (item: any) => {
if (item.meta === "function") {
const functionName = item.caption.slice(0,-2);
const functionName = item.caption.slice(0, -2);
item.docHTML = ReactDOMServer.renderToStaticMarkup((
<div className={"function-doc"}>
<div className={"function-doc-name"}><b>{functionName}</b></div>
@ -133,7 +135,7 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
<div><b>Description:</b></div>
<div>{item.description}</div>
</div>
))
));
}
}
};
@ -160,7 +162,7 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
private handleChange = (newValue: string): void => {
this.setState({
query: newValue
})
});
}
render() {
@ -186,7 +188,7 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
theme="solarized_dark"
name="ace-editor"
onChange={this.handleChange}
focus={true}
focus
fontSize={14}
width={'100%'}
height={"30vh"}
@ -199,7 +201,7 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
enableBasicAutocompletion: isRune ? false : autoCompleteOn,
enableLiveAutocompletion: isRune ? false : autoCompleteOn,
showLineNumbers: true,
tabSize: 2,
tabSize: 2
}}
/>
<div className="buttons">
@ -213,4 +215,3 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
</div>;
}
}

View File

@ -16,22 +16,23 @@
* 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 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 { HeaderBar, HeaderActiveTab } from './components/header-bar';
import { localStorageGet, localStorageSet } from './utils';
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 { HomeView } from './views/home-view';
import { LookupsView } from "./views/lookups-view";
import { SegmentsView } from './views/segments-view';
import { ServersView } from './views/servers-view';
import { TasksView } from './views/tasks-view';
import { SqlView } from './views/sql-view';
import { LookupsView } from "./views/lookups-view";
import { TasksView } from './views/tasks-view';
import "./console-application.scss";
export interface ConsoleApplicationProps extends React.Props<any> {
@ -63,14 +64,16 @@ export class ConsoleApplication extends React.Component<ConsoleApplicationProps,
iconName: 'error',
intent: Intent.DANGER,
timeout: 120000,
/* tslint:disable:jsx-alignment */
message: <>
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={LEGACY_COORDINATOR_CONSOLE}>coordinator</a> and <a
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;
}
@ -142,30 +145,49 @@ export class ConsoleApplication extends React.Component<ConsoleApplicationProps,
return <HashRouter hashType="noslash">
<div className="console-application">
<Switch>
<Route path="/datasources" component={() => {
<Route
path="/datasources"
component={() => {
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}/>);
}} />
<Route path="/tasks" component={() => {
}}
/>
<Route
path="/tasks"
component={() => {
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);
}} />
<Route path="/sql" component={() => {
}}
/>
<Route
path="/sql"
component={() => {
return wrapInViewContainer('sql', <SqlView initSql={this.initSql}/>);
}} />
<Route path="/lookups" component={() => {
}}
/>
<Route
path="/lookups"
component={() => {
return wrapInViewContainer('lookups', <LookupsView />);
}} />
<Route component={() => {
return wrapInViewContainer(null, <HomeView/>)
}} />
}}
/>
<Route
component={() => {
return wrapInViewContainer(null, <HomeView/>);
}}
/>
</Switch>
</div>
</HashRouter>;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,17 +16,17 @@
* limitations under the License.
*/
import * as React from 'react';
import {
Button,
InputGroup,
Classes,
Dialog,
IDialogProps,
Classes,
Intent,
InputGroup,
Intent
} 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 {
onSave: (author: string, comment: string) => void;
@ -50,7 +50,7 @@ export class SnitchDialog extends React.Component<SnitchDialogProps, SnitchDialo
comment: "",
author: "",
saveDisabled: true
}
};
}
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="Next" onClick={this.goToFinalStep} intent={Intent.PRIMARY as any} rightIconName={IconNames.ARROW_RIGHT}/>
}
</div>
</div>;
}
render() {

View File

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

View File

@ -19,13 +19,14 @@
import 'es6-shim/es6-shim';
import 'es7-shim'; // Webpack with automatically pick browser.js which does the shim()
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 "./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 "./entry.scss";
const container = document.getElementsByClassName('app-container')[0];
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({
className: "recipe-toaster",
position: Position.TOP,
position: Position.TOP
});

View File

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

View File

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

View File

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

View File

@ -17,10 +17,12 @@
*/
import axios from 'axios';
import * as React from 'react';
import * as classNames from 'classnames';
import { H5, Card, Icon, IconNames } from "../components/filler";
import { QueryManager, pluralIfNeeded, queryDruidSql, getHeadProp } from '../utils';
import * as React from 'react';
import { Card, H5, Icon, IconNames } from "../components/filler";
import { getHeadProp, pluralIfNeeded, queryDruidSql, QueryManager } from '../utils';
import './home-view.scss';
export interface CardOptions {
@ -164,14 +166,14 @@ export class HomeView extends React.Component<HomeViewProps, HomeViewState> {
this.taskQueryManager = new QueryManager({
processQuery: async (query) => {
const taskCountsFromSql = await queryDruidSql({ query });
let taskCounts = {
const taskCounts = {
successTaskCount: 0,
failedTaskCount: 0,
runningTaskCount: 0,
waitingTaskCount: 0,
pendingTaskCount: 0
};
for (let dataStatus of taskCountsFromSql) {
for (const dataStatus of taskCountsFromSql) {
if (dataStatus.status === "SUCCESS") {
taskCounts.successTaskCount = dataStatus.count;
} else if (dataStatus.status === "FAILED") {
@ -254,7 +256,7 @@ GROUP BY 1`);
renderCard(cardOptions: CardOptions): JSX.Element {
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>
{cardOptions.loading ? <p>Loading...</p> : (cardOptions.error ? `Error: ${cardOptions.error}` : cardOptions.content)}
</Card>
@ -298,11 +300,11 @@ GROUP BY 1`);
title: "Tasks",
loading: state.taskCountLoading,
content: <>
{ Boolean(state.runningTaskCount) && <p>{pluralIfNeeded(state.runningTaskCount, 'running task')}</p> }
{ Boolean(state.pendingTaskCount) && <p>{pluralIfNeeded(state.pendingTaskCount, 'pending task')}</p> }
{ Boolean(state.successTaskCount) && <p>{pluralIfNeeded(state.successTaskCount, 'successful task')}</p> }
{ Boolean(state.waitingTaskCount) && <p>{pluralIfNeeded(state.waitingTaskCount, 'waiting task')}</p> }
{ Boolean(state.failedTaskCount) && <p>{pluralIfNeeded(state.failedTaskCount, 'failed task')}</p> }
{Boolean(state.runningTaskCount) && <p>{pluralIfNeeded(state.runningTaskCount, 'running task')}</p>}
{Boolean(state.pendingTaskCount) && <p>{pluralIfNeeded(state.pendingTaskCount, 'pending task')}</p>}
{Boolean(state.successTaskCount) && <p>{pluralIfNeeded(state.successTaskCount, 'successful task')}</p>}
{Boolean(state.waitingTaskCount) && <p>{pluralIfNeeded(state.waitingTaskCount, 'waiting task')}</p>}
{Boolean(state.failedTaskCount) && <p>{pluralIfNeeded(state.failedTaskCount, 'failed task')}</p>}
{ !(state.runningTaskCount + state.pendingTaskCount + state.successTaskCount + state.waitingTaskCount + state.failedTaskCount) &&
<p>There are no tasks</p>
}
@ -321,7 +323,6 @@ GROUP BY 1`);
</>,
error: state.dataServerCountError
})}
</div>
</div>;
}
}

View File

@ -16,15 +16,17 @@
* limitations under the License.
*/
import { Button, Intent } from "@blueprintjs/core";
import axios from 'axios';
import * as React from 'react';
import * as classNames from 'classnames';
import * as React from 'react';
import ReactTable 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 { getDruidErrorMessage, QueryManager } from "../utils";
import "./lookups-view.scss";
export interface LookupsViewProps extends React.Props<any> {
@ -32,16 +34,16 @@ export interface LookupsViewProps extends React.Props<any> {
}
export interface LookupsViewState {
lookups: {}[],
loadingLookups: boolean,
lookupsError: string | null,
lookupEditDialogOpen: boolean,
lookupEditName: string,
lookupEditTier: string,
lookupEditVersion: string,
lookupEditSpec: string,
isEdit: boolean,
allLookupTiers: string[]
lookups: {}[];
loadingLookups: boolean;
lookupsError: string | null;
lookupEditDialogOpen: boolean;
lookupEditName: string;
lookupEditTier: string;
lookupEditVersion: string;
lookupEditSpec: string;
isEdit: boolean;
allLookupTiers: string[];
}
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 tiers = tiersResp.data;
let lookupEntries: {}[] = [];
const lookupEntries: {}[] = [];
const lookupResp = await axios.get("/druid/coordinator/v1/lookups/config/all");
const lookupData = lookupResp.data;
Object.keys(lookupData).map((tier: string) => {
const lookupIds = lookupData[tier];
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 {
lookupEntries,
tiers
@ -123,11 +125,11 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
intent: Intent.DANGER,
message: getDruidErrorMessage(e)
}
)
);
}
}
private async openLookupEditDialog(tier:string, id: string) {
private async openLookupEditDialog(tier: string, id: string) {
const { lookups, allLookupTiers } = this.state;
const target: any = lookups.find((lookupEntry: any) => {
return lookupEntry.tier === tier && lookupEntry.id === id;
@ -156,7 +158,7 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
private changeLookup(field: string, value: string) {
this.setState({
[field]: value
} as any)
} as any);
}
private async submitLookupEdit() {
@ -184,20 +186,20 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
await axios.post(endpoint, dataJSON);
this.setState({
lookupEditDialogOpen: false
})
});
this.lookupsGetQueryManager.rerunLastQuery();
} catch(e) {
} catch (e) {
AppToaster.show(
{
iconName: 'error',
intent: Intent.DANGER,
message: getDruidErrorMessage(e)
}
)
);
}
}
private deleteLookup(tier:string, name: string): void {
private deleteLookup(tier: string, name: string): void {
const url = `/druid/coordinator/v1/lookups/config/${tier}/${name}`;
this.lookupDeleteQueryManager.runQuery(url);
}
@ -211,52 +213,52 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
text="Initialize Lookup"
onClick={() => this.initializeLookup()}
/>
</div>
</div>;
}
return <>
<ReactTable
data={lookups}
loading={loadingLookups}
noDataText={!loadingLookups && lookups && !lookups.length ? 'No lookups' : (lookupsError || '')}
filterable={true}
filterable
columns={[
{
Header: "Lookup Name",
id: "lookup_name",
accessor: (row: any) => row.id,
filterable: true,
filterable: true
},
{
Header: "Tier",
id: "tier",
accessor: (row: any) => row.tier,
filterable: true,
filterable: true
},
{
Header: "Type",
id: "type",
accessor: (row: any) => row.spec.type,
filterable: true,
filterable: true
},
{
Header: "Version",
id: "version",
accessor: (row: any) => row.version,
filterable: true,
filterable: true
},
{
Header: "Config",
id: "config",
accessor: row => {return {id: row.id, tier: row.tier};},
accessor: row => ({id: row.id, tier: row.tier}),
filterable: false,
Cell: (row: any) => {
const lookupId = row.value.id;
const lookupTier = row.value.tier;
return <div>
<a onClick={() => this.openLookupEditDialog(lookupTier,lookupId)}>Edit</a>
<a onClick={() => this.openLookupEditDialog(lookupTier, lookupId)}>Edit</a>
&nbsp;&nbsp;&nbsp;
<a onClick={() => this.deleteLookup(lookupTier,lookupId)}>Delete</a>
</div>
<a onClick={() => this.deleteLookup(lookupTier, lookupId)}>Delete</a>
</div>;
}
}
]}
@ -266,21 +268,21 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
</>;
}
renderLookupEditDialog () {
const { lookupEditDialogOpen, allLookupTiers, lookupEditSpec, lookupEditTier, lookupEditName, lookupEditVersion, isEdit } = this.state
renderLookupEditDialog() {
const { lookupEditDialogOpen, allLookupTiers, lookupEditSpec, lookupEditTier, lookupEditName, lookupEditVersion, isEdit } = this.state;
return <LookupEditDialog
isOpen={lookupEditDialogOpen}
onClose={() => this.setState({ lookupEditDialogOpen: false })}
onSubmit={() => this.submitLookupEdit()}
onChange={(field: string, value: string) => this.changeLookup(field, value)}
lookupSpec= {lookupEditSpec}
lookupSpec={lookupEditSpec}
lookupName={lookupEditName}
lookupTier={lookupEditTier}
lookupVersion = {lookupEditVersion}
lookupVersion={lookupEditVersion}
isEdit={isEdit}
allLookupTiers={allLookupTiers}
/>
/>;
}
render() {
@ -301,6 +303,6 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
</div>
{this.renderLookupsTable()}
{this.renderLookupEditDialog()}
</div>
</div>;
}
}

View File

@ -16,22 +16,24 @@
* limitations under the License.
*/
import { Button } from "@blueprintjs/core";
import axios from 'axios';
import * as React from 'react';
import * as classNames from 'classnames';
import * as React from 'react';
import ReactTable from "react-table";
import { Filter } from "react-table";
import { Button } from "@blueprintjs/core";
import { H5, IconNames } from "../components/filler";
import {
addFilter,
makeBooleanFilter,
QueryManager,
formatBytes,
formatNumber,
makeBooleanFilter,
parseList,
queryDruidSql
queryDruidSql,
QueryManager
} from "../utils";
import "./segments-view.scss";
export interface SegmentsViewProps extends React.Props<any> {
@ -88,7 +90,7 @@ export class SegmentsView extends React.Component<SegmentsViewProps, SegmentsVie
segmentsError: error
});
}
})
});
}
componentWillUnmount(): void {
@ -99,7 +101,7 @@ export class SegmentsView extends React.Component<SegmentsViewProps, SegmentsVie
const { page, pageSize, filtered, sorted } = state;
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"`,
`FROM sys.segments`
];
@ -114,7 +116,7 @@ export class SegmentsView extends React.Component<SegmentsViewProps, SegmentsVie
}).filter(Boolean);
if (whereParts.length) {
queryParts.push('WHERE ' + whereParts.join(' AND '))
queryParts.push('WHERE ' + whereParts.join(' AND '));
}
if (sorted.length) {
@ -160,7 +162,7 @@ export class SegmentsView extends React.Component<SegmentsViewProps, SegmentsVie
accessor: "datasource",
Cell: row => {
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,
Cell: row => {
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,
Cell: row => {
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",
accessor: "version",
defaultSortDesc: true,
width: 120,
width: 120
},
{
Header: "Partition",
@ -272,6 +274,6 @@ export class SegmentsView extends React.Component<SegmentsViewProps, SegmentsVie
/>
</div>
{this.renderSegmentsTable()}
</div>
</div>;
}
}

View File

@ -16,19 +16,21 @@
* limitations under the License.
*/
import { Button, Switch } from "@blueprintjs/core";
import axios from 'axios';
import * as React from 'react';
import * as classNames from 'classnames';
import { sum } from "d3-array";
import * as React from 'react';
import ReactTable 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 { addFilter, formatBytes, formatBytesCompact, QueryManager, queryDruidSql } from "../utils";
import { addFilter, formatBytes, formatBytesCompact, queryDruidSql, QueryManager } from "../utils";
import "./servers-view.scss";
function formatQueues(segmentsToLoad: number, segmentsToLoadSize: number, segmentsToDrop: number, segmentsToDropSize: number): string {
let queueParts: string[] = [];
const queueParts: string[] = [];
if (segmentsToLoad) {
queueParts.push(`${segmentsToLoad} segments to load (${formatBytesCompact(segmentsToLoadSize)})`);
}
@ -73,7 +75,7 @@ export class ServersView extends React.Component<ServersViewProps, ServersViewSt
middleManagersLoading: true,
middleManagers: 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) => {
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;
return servers.map((s: any) => {
@ -143,7 +145,7 @@ WHERE "server_type" = 'historical'`);
data={servers || []}
loading={serversLoading}
noDataText={!serversLoading && servers && !servers.length ? 'No historicals' : (serversError || '')}
filterable={true}
filterable
filtered={serverFilter}
onFilteredChange={(filtered, column) => {
this.setState({ serverFilter: filtered });
@ -161,7 +163,7 @@ WHERE "server_type" = 'historical'`);
accessor: "tier",
Cell: row => {
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 segmentsToDropSize = sum(originals, s => s.segmentsToDropSize);
return formatQueues(segmentsToLoad, segmentsToLoadSize, segmentsToDrop, segmentsToDropSize);
},
}
},
{
Header: "Host",
@ -245,7 +247,7 @@ WHERE "server_type" = 'historical'`);
Header: "Port",
id: 'port',
accessor: (row) => {
let ports: string[] = [];
const ports: string[] = [];
if (row.plaintext_port !== -1) {
ports.push(`${row.plaintext_port} (plain)`);
}
@ -255,7 +257,7 @@ WHERE "server_type" = 'historical'`);
return ports.join(', ') || 'No port';
},
Aggregated: () => ''
},
}
]}
defaultPageSize={10}
className="-striped -highlight"
@ -270,7 +272,7 @@ WHERE "server_type" = 'historical'`);
data={middleManagers || []}
loading={middleManagersLoading}
noDataText={!middleManagersLoading && middleManagers && !middleManagers.length ? 'No MiddleManagers' : (middleManagersError || '')}
filterable={true}
filterable
filtered={middleManagerFilter}
onFilteredChange={(filtered, column) => {
this.setState({ middleManagerFilter: filtered });
@ -282,7 +284,7 @@ WHERE "server_type" = 'historical'`);
accessor: (row) => row.worker.host,
Cell: row => {
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",
width: 60,
accessor: (row) => row.availabilityGroups.length,
filterable: false,
filterable: false
},
{
Header: "Last completed task time",
@ -317,7 +319,7 @@ WHERE "server_type" = 'historical'`);
runningTasks.length ?
<>
<span>Running tasks:</span>
<ul>{ runningTasks.map((t: string) => <li key={t}>{t}&nbsp;<a onClick={() => goToTask(t)}>&#x279A;</a></li>) }</ul>
<ul>{runningTasks.map((t: string) => <li key={t}>{t}&nbsp;<a onClick={() => goToTask(t)}>&#x279A;</a></li>)}</ul>
</> :
<span>No running tasks</span>
}
@ -362,7 +364,6 @@ WHERE "server_type" = 'historical'`);
/>
</div>
{this.renderMiddleManagerTable()}
</div>
</div>;
}
}

View File

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

View File

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