| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import 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 { 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 { SegmentsView } from './views/segments-view'; |
| import { ServersView } from './views/servers-view'; |
| import { TasksView } from './views/tasks-view'; |
| import { SqlView } from './views/sql-view'; |
| import "./console-application.scss"; |
| |
| export interface ConsoleApplicationProps extends React.Props<any> { |
| version: string; |
| } |
| |
| export interface ConsoleApplicationState { |
| aboutDialogOpen: boolean; |
| } |
| |
| export class ConsoleApplication extends React.Component<ConsoleApplicationProps, ConsoleApplicationState> { |
| static MESSAGE_KEY = 'druid-console-message'; |
| static MESSAGE_DISMISSED = 'dismissed'; |
| |
| static async ensureSql() { |
| try { |
| await axios.post("/druid/v2/sql", { query: 'SELECT 1337' }); |
| } catch (e) { |
| const { response } = e; |
| if (response.status !== 405 || response.statusText !== "Method Not Allowed") return true; // other failure |
| try { |
| await axios.get("/status"); |
| } catch (e) { |
| return true; // total failure |
| } |
| |
| // Status works but SQL 405s => the SQL endpoint is disabled |
| AppToaster.show({ |
| iconName: 'error', |
| intent: Intent.DANGER, |
| timeout: 120000, |
| 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. |
| </> |
| }); |
| return false |
| } |
| return true; |
| } |
| |
| static async shownNotifications() { |
| await ConsoleApplication.ensureSql(); |
| } |
| |
| private taskId: string | null; |
| private datasource: string | null; |
| private onlyUnavailable: boolean | null; |
| private initSql: string | null; |
| private middleManager: string | null; |
| |
| constructor(props: ConsoleApplicationProps, context: any) { |
| super(props, context); |
| this.state = { |
| aboutDialogOpen: false |
| }; |
| } |
| |
| componentDidMount(): void { |
| ConsoleApplication.shownNotifications(); |
| } |
| |
| private resetInitialsDelay() { |
| setTimeout(() => { |
| this.taskId = null; |
| this.datasource = null; |
| this.onlyUnavailable = null; |
| this.initSql = null; |
| this.middleManager = null; |
| }, 50); |
| } |
| |
| private goToTask = (taskId: string) => { |
| this.taskId = taskId; |
| window.location.hash = 'tasks'; |
| this.resetInitialsDelay(); |
| } |
| |
| private goToSegments = (datasource: string, onlyUnavailable = false) => { |
| this.datasource = datasource; |
| this.onlyUnavailable = onlyUnavailable; |
| window.location.hash = 'segments'; |
| this.resetInitialsDelay(); |
| } |
| |
| private goToMiddleManager = (middleManager: string) => { |
| this.middleManager = middleManager; |
| window.location.hash = 'servers'; |
| this.resetInitialsDelay(); |
| } |
| |
| private goToSql = (initSql: string) => { |
| this.initSql = initSql; |
| window.location.hash = 'sql'; |
| this.resetInitialsDelay(); |
| } |
| |
| render() { |
| const wrapInViewContainer = (active: HeaderActiveTab, el: JSX.Element, scrollable = false) => { |
| return <> |
| <HeaderBar active={active}/> |
| <div className={classNames('view-container', { scrollable })}>{el}</div> |
| </>; |
| }; |
| |
| return <HashRouter hashType="noslash"> |
| <div className="console-application"> |
| <Switch> |
| <Route path="/datasources" component={() => { |
| return wrapInViewContainer('datasources', <DatasourcesView goToSql={this.goToSql} goToSegments={this.goToSegments}/>); |
| }} /> |
| <Route path="/segments" component={() => { |
| return wrapInViewContainer('segments', <SegmentsView datasource={this.datasource} onlyUnavailable={this.onlyUnavailable} goToSql={this.goToSql}/>); |
| }} /> |
| <Route path="/tasks" component={() => { |
| return wrapInViewContainer('tasks', <TasksView taskId={this.taskId} goToSql={this.goToSql} goToMiddleManager={this.goToMiddleManager}/>, true); |
| }} /> |
| <Route path="/servers" component={() => { |
| return wrapInViewContainer('servers', <ServersView middleManager={this.middleManager} goToSql={this.goToSql} goToTask={this.goToTask}/>, true); |
| }} /> |
| <Route path="/sql" component={() => { |
| return wrapInViewContainer('sql', <SqlView initSql={this.initSql}/>); |
| }} /> |
| <Route component={() => { |
| return wrapInViewContainer(null, <HomeView/>) |
| }} /> |
| </Switch> |
| </div> |
| </HashRouter>; |
| } |
| } |
| |