Adding ability to pass configs in and fixing misc bugs (#7414)

* Adding ability to pass configs in and fixing misc bugs

* update lock file

* remove dead version param
This commit is contained in:
Vadim Ogievetsky 2019-04-05 12:40:43 -07:00 committed by Clint Wylie
parent e2aa6e47f8
commit 0fa122ecf2
10 changed files with 128 additions and 59 deletions

View File

@ -0,0 +1,19 @@
/*
* 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.
*/
window.consoleConfig = { /* future configs may go here */ };

View File

@ -1,6 +1,6 @@
{
"name": "web-console",
"version": "0.14.0",
"version": "0.15.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -20,6 +20,7 @@ echo "Copying web-console files to $1..."
mkdir -p "$1"
cp *.html "$1"
cp *.png "$1"
cp console-config.js "$1"
cp -r coordinator-console "$1"
cp -r old-console "$1"
cp -r pages "$1"

View File

@ -134,6 +134,7 @@ export class AutoForm<T> extends React.Component<AutoFormProps<T>, AutoFormState
onChange={(v: any) => {
onChange(Object.assign({}, model, { [field.name]: v }));
}}
addOnBlur
fill
/>;
}

View File

@ -38,6 +38,7 @@ export type HeaderActiveTab = null | 'datasources' | 'segments' | 'tasks' | 'ser
export interface HeaderBarProps extends React.Props<any> {
active: HeaderActiveTab;
hideLegacy: boolean;
}
export interface HeaderBarState {
@ -106,7 +107,7 @@ export class HeaderBar extends React.Component<HeaderBarProps, HeaderBarState> {
}
render() {
const { active } = this.props;
const { active, hideLegacy } = this.props;
const { aboutDialogOpen, coordinatorDynamicConfigDialogOpen, overlordDynamicConfigDialogOpen } = this.state;
const legacyMenu = <Menu>
@ -144,22 +145,34 @@ export class HeaderBar extends React.Component<HeaderBarProps, HeaderBarState> {
</Popover>
</NavbarGroup>
<NavbarGroup align={Alignment.RIGHT}>
<Popover className="legacy-popover" content={legacyMenu} position={Position.BOTTOM_RIGHT}>
<Button className={Classes.MINIMAL} icon={IconNames.SHARE} text="Legacy" />
</Popover>
{
!hideLegacy &&
<Popover className="legacy-popover" content={legacyMenu} position={Position.BOTTOM_RIGHT}>
<Button className={Classes.MINIMAL} icon={IconNames.SHARE} text="Legacy"/>
</Popover>
}
<Popover className="help-popover" content={helpMenu} position={Position.BOTTOM_RIGHT}>
<Button className={Classes.MINIMAL} icon={IconNames.HELP} text="Help" />
</Popover>
</NavbarGroup>
{ aboutDialogOpen ? <AboutDialog
onClose={() => this.setState({ aboutDialogOpen: false })}
/> : null }
{ coordinatorDynamicConfigDialogOpen ? <CoordinatorDynamicConfigDialog
onClose={() => this.setState({ coordinatorDynamicConfigDialogOpen: false })}
/> : null }
{ overlordDynamicConfigDialogOpen ? <OverlordDynamicConfigDialog
onClose={() => this.setState({ overlordDynamicConfigDialogOpen: false })}
/> : null }
{
aboutDialogOpen &&
<AboutDialog
onClose={() => this.setState({ aboutDialogOpen: false })}
/>
}
{
coordinatorDynamicConfigDialogOpen &&
<CoordinatorDynamicConfigDialog
onClose={() => this.setState({ coordinatorDynamicConfigDialogOpen: false })}
/>
}
{
overlordDynamicConfigDialogOpen &&
<OverlordDynamicConfigDialog
onClose={() => this.setState({ overlordDynamicConfigDialogOpen: false })}
/>
}
</Navbar>;
}
}

View File

@ -24,11 +24,7 @@
// This will be mounted in a portal
.table-column-selection-menu {
.form-group {
.bp3-form-group {
margin-bottom: 0;
.bp3-checkbox:last-child {
margin-bottom: 0;
}
}
}

View File

@ -37,7 +37,8 @@ import { TasksView } from './views/tasks-view';
import "./console-application.scss";
export interface ConsoleApplicationProps extends React.Props<any> {
version: string;
hideLegacy: boolean;
baseURL?: string;
}
export interface ConsoleApplicationState {
@ -94,6 +95,10 @@ export class ConsoleApplication extends React.Component<ConsoleApplicationProps,
this.state = {
aboutDialogOpen: false
};
if (props.baseURL) {
axios.defaults.baseURL = props.baseURL;
}
}
componentDidMount(): void {
@ -136,9 +141,11 @@ export class ConsoleApplication extends React.Component<ConsoleApplicationProps,
}
render() {
const { hideLegacy } = this.props;
const wrapInViewContainer = (active: HeaderActiveTab, el: JSX.Element, scrollable = false) => {
return <>
<HeaderBar active={active}/>
<HeaderBar active={active} hideLegacy={hideLegacy}/>
<div className={classNames('view-container', { scrollable })}>{el}</div>
</>;
};

View File

@ -30,11 +30,18 @@ import "./entry.scss";
const container = document.getElementsByClassName('app-container')[0];
if (!container) throw new Error('container not found');
interface ConsoleConfig {
hideLegacy?: boolean;
baseURL?: string;
}
const consoleConfig: ConsoleConfig = (window as any).consoleConfig;
ReactDOM.render(
React.createElement(
ConsoleApplication,
{
version: '0.0.1'
hideLegacy: Boolean(consoleConfig.hideLegacy),
baseURL: consoleConfig.baseURL
}
) as any,
container

View File

@ -22,9 +22,9 @@ import axios from 'axios';
import * as classNames from 'classnames';
import * as React from 'react';
import ReactTable from "react-table";
import { Filter } from "react-table";
import { TableColumnSelection } from "../components/table-column-selection";
import { AsyncActionDialog } from '../dialogs/async-action-dialog';
import { LookupEditDialog } from "../dialogs/lookup-edit-dialog";
import { AppToaster } from "../singletons/toaster";
import {
@ -35,16 +35,17 @@ import {
import "./lookups-view.scss";
const tableColumns: string[] = ["Lookup Name", "Tier", "Type", "Version", "Config"];
const tableColumns: string[] = ["Lookup name", "Tier", "Type", "Version", "Actions"];
export interface LookupsViewProps extends React.Props<any> {
}
export interface LookupsViewState {
lookups: {}[];
lookups: {}[] | null;
loadingLookups: boolean;
lookupsError: string | null;
lookupsUninitialized: boolean;
lookupEditDialogOpen: boolean;
lookupEditName: string;
lookupEditTier: string;
@ -52,11 +53,13 @@ export interface LookupsViewState {
lookupEditSpec: string;
isEdit: boolean;
allLookupTiers: string[];
deleteLookupTier: string | null;
deleteLookupName: string | null;
}
export class LookupsView extends React.Component<LookupsViewProps, LookupsViewState> {
private lookupsGetQueryManager: QueryManager<string, {lookupEntries: any[], tiers: string[]}>;
private lookupDeleteQueryManager: QueryManager<string, any[]>;
private tableColumnSelectionHandler: TableColumnSelectionHandler;
constructor(props: LookupsViewProps, context: any) {
@ -65,13 +68,17 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
lookups: [],
loadingLookups: true,
lookupsError: null,
lookupsUninitialized: false,
lookupEditDialogOpen: false,
lookupEditTier: "",
lookupEditName: "",
lookupEditVersion: "",
lookupEditSpec: "",
isEdit: false,
allLookupTiers: []
allLookupTiers: [],
deleteLookupTier: null,
deleteLookupName: null
};
this.tableColumnSelectionHandler = new TableColumnSelectionHandler(
LocalStorageKeys.LOOKUP_TABLE_COLUMN_SELECTION, () => this.setState({})
@ -90,9 +97,10 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
Object.keys(lookupData).map((tier: string) => {
const lookupIds = lookupData[tier];
Object.keys(lookupIds).map((id: string) => {
lookupEntries.push({tier, id, version: lookupData[tier][id].version, spec: lookupData[tier][id].lookupExtractorFactory});
lookupEntries.push({tier, id, version: lookupIds[id].version, spec: lookupIds[id].lookupExtractorFactory});
});
});
return {
lookupEntries,
tiers
@ -100,30 +108,20 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
},
onStateChange: ({ result, loading, error }) => {
this.setState({
lookups: result === null ? [] : result.lookupEntries,
lookups: result ? result.lookupEntries : null,
loadingLookups: loading,
lookupsError: error,
lookupsUninitialized: error === 'Request failed with status code 404',
allLookupTiers: result === null ? [] : result.tiers
});
}
});
this.lookupsGetQueryManager.runQuery("dummy");
this.lookupDeleteQueryManager = new QueryManager({
processQuery: async (url: string) => {
const lookupDeleteResp = await axios.delete(url);
return lookupDeleteResp.data;
},
onStateChange: ({}) => {
this.lookupsGetQueryManager.rerunLastQuery();
}
});
}
componentWillUnmount(): void {
this.lookupsGetQueryManager.terminate();
this.lookupDeleteQueryManager.terminate();
}
private async initializeLookup() {
@ -143,6 +141,8 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
private async openLookupEditDialog(tier: string, id: string) {
const { lookups, allLookupTiers } = this.state;
if (!lookups) return;
const target: any = lookups.find((lookupEntry: any) => {
return lookupEntry.tier === tier && lookupEntry.id === id;
});
@ -211,16 +211,35 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
}
}
private deleteLookup(tier: string, name: string): void {
const url = `/druid/coordinator/v1/lookups/config/${tier}/${name}`;
this.lookupDeleteQueryManager.runQuery(url);
renderDeleteLookupAction() {
const { deleteLookupTier, deleteLookupName } = this.state;
return <AsyncActionDialog
action={
deleteLookupTier ? async () => {
await axios.delete(`/druid/coordinator/v1/lookups/config/${deleteLookupTier}/${deleteLookupName}`);
} : null
}
confirmButtonText="Delete lookup"
successText="Lookup was deleted"
failText="Could not delete lookup"
intent={Intent.DANGER}
onClose={(success) => {
this.setState({ deleteLookupTier: null, deleteLookupName: null });
if (success) this.lookupsGetQueryManager.rerunLastQuery();
}}
>
<p>
{`Are you sure you want to delete the lookup '${deleteLookupName}'?`}
</p>
</AsyncActionDialog>;
}
renderLookupsTable() {
const { lookups, loadingLookups, lookupsError } = this.state;
const { lookups, loadingLookups, lookupsError, lookupsUninitialized } = this.state;
const { tableColumnSelectionHandler } = this;
if (lookupsError) {
if (lookupsUninitialized) {
return <div className={"init-div"}>
<Button
icon={IconNames.BUILD}
@ -229,19 +248,20 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
/>
</div>;
}
return <>
<ReactTable
data={lookups}
data={lookups || []}
loading={loadingLookups}
noDataText={!loadingLookups && lookups && !lookups.length ? 'No lookups' : (lookupsError || '')}
filterable
columns={[
{
Header: "Lookup Name",
Header: "Lookup name",
id: "lookup_name",
accessor: (row: any) => row.id,
filterable: true,
show: tableColumnSelectionHandler.showColumn("Lookup Name")
show: tableColumnSelectionHandler.showColumn("Lookup name")
},
{
Header: "Tier",
@ -265,8 +285,8 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
show: tableColumnSelectionHandler.showColumn("Version")
},
{
Header: "Config",
id: "config",
Header: "Actions",
id: "actions",
accessor: row => ({id: row.id, tier: row.tier}),
filterable: false,
Cell: (row: any) => {
@ -275,10 +295,10 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
return <div>
<a onClick={() => this.openLookupEditDialog(lookupTier, lookupId)}>Edit</a>
&nbsp;&nbsp;&nbsp;
<a onClick={() => this.deleteLookup(lookupTier, lookupId)}>Delete</a>
<a onClick={() => this.setState({ deleteLookupTier: lookupTier, deleteLookupName: lookupId })}>Delete</a>
</div>;
},
show: tableColumnSelectionHandler.showColumn("Config")
show: tableColumnSelectionHandler.showColumn("Actions")
}
]}
defaultPageSize={50}
@ -305,7 +325,9 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
}
render() {
const { lookupsError } = this.state;
const { tableColumnSelectionHandler } = this;
return <div className="lookups-view app-view">
<div className="control-bar">
<div className="control-label">Lookups</div>
@ -314,12 +336,13 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
text="Refresh"
onClick={() => this.lookupsGetQueryManager.rerunLastQuery()}
/>
<Button
icon={IconNames.PLUS}
text="Add"
style={{display: this.state.lookupsError !== null ? 'none' : 'inline'}}
onClick={() => this.openLookupEditDialog("", "")}
/>
{ !lookupsError &&
<Button
icon={IconNames.PLUS}
text="Add"
onClick={() => this.openLookupEditDialog("", "")}
/>
}
<TableColumnSelection
columns={tableColumns}
onChange={(column) => tableColumnSelectionHandler.changeTableColumnSelection(column)}
@ -328,6 +351,7 @@ export class LookupsView extends React.Component<LookupsViewProps, LookupsViewSt
</div>
{this.renderLookupsTable()}
{this.renderLookupEditDialog()}
{this.renderDeleteLookupAction()}
</div>;
}
}

View File

@ -22,10 +22,11 @@
<meta charset="utf-8">
<title>Apache Druid</title>
<meta name="description" content="Apache Druid web console">
<link rel="shortcut icon" href="/favicon.png">
<link rel="shortcut icon" href="favicon.png">
</head>
<body class="bp3-dark mouse-mode">
<div class="app-container"></div>
<script src="console-config.js"></script>
<script src="public/web-console-0.15.0.js"></script>
</body>
</html>