Web console: improve how code is imported, use API instance (#10597)

* fix imports

* clean up imports

* update DQT to fix escaping
This commit is contained in:
Vadim Ogievetsky 2020-12-01 13:16:14 -08:00 committed by GitHub
parent d47d6cf081
commit 5b06c7a3a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 306 additions and 215 deletions

View File

@ -4751,7 +4751,7 @@ license_category: binary
module: web-console module: web-console
license_name: Apache License version 2.0 license_name: Apache License version 2.0
copyright: Imply Data copyright: Imply Data
version: 0.9.15 version: 0.10.4
--- ---

View File

@ -4265,11 +4265,18 @@
} }
}, },
"druid-query-toolkit": { "druid-query-toolkit": {
"version": "0.9.15", "version": "0.10.4",
"resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.9.15.tgz", "resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.10.4.tgz",
"integrity": "sha512-HiJVqa6X6z/LeEU3GVxbkaP/X8JCn/9n8txoG1tSgk6edc1BZXj7F7hZpaNlAThVcZcFOYeE1gFOHzMMjK4U0A==", "integrity": "sha512-feIRTC2paOkGpWvymseMs/wn+8XfbLjlcBsXJXKxgsJtqMKBYy3f8YiN3SV/xv6CQP9Vv4nBMEoa5q8OM5KHsg==",
"requires": { "requires": {
"tslib": "^1.10.0" "tslib": "^2.0.2"
},
"dependencies": {
"tslib": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
}
} }
}, },
"duplexer": { "duplexer": {

View File

@ -68,7 +68,7 @@
"d3-axis": "^1.0.12", "d3-axis": "^1.0.12",
"d3-scale": "^3.2.0", "d3-scale": "^3.2.0",
"d3-selection": "^1.4.0", "d3-selection": "^1.4.0",
"druid-query-toolkit": "^0.9.15", "druid-query-toolkit": "^0.10.4",
"file-saver": "^2.0.2", "file-saver": "^2.0.2",
"fontsource-open-sans": "^3.0.9", "fontsource-open-sans": "^3.0.9",
"has-own-prop": "^2.0.0", "has-own-prop": "^2.0.0",

View File

@ -61,7 +61,7 @@ const readDoc = async () => {
); );
} }
// Make sure there are at least 5 data types for sanity // Make sure there are at least 10 data types for sanity
if (dataTypeDocs.length < 10) { if (dataTypeDocs.length < 10) {
throw new Error( throw new Error(
`Did not find enough data type entries did the structure of '${readfile}' change? (found ${dataTypeDocs.length})`, `Did not find enough data type entries did the structure of '${readfile}' change? (found ${dataTypeDocs.length})`,

View File

@ -0,0 +1,24 @@
/*
* 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 'brace'; // Import Ace editor and all the sub components used in the app
import 'brace/ext/language_tools';
import 'brace/theme/solarized_dark';
import '../ace-modes/dsql';
import '../ace-modes/hjson';

View File

@ -24,13 +24,14 @@ import { booleanCustomTableFilter, countBy, makeTextFilter } from '../utils';
import { ReactTableCustomPagination } from './react-table-custom-pagination'; import { ReactTableCustomPagination } from './react-table-custom-pagination';
export const NoData = React.memo(function NoData(props) { const NoData = React.memo(function NoData(props) {
const { children } = props; const { children } = props;
if (!children) return null; if (!children) return null;
return <div className="rt-noData">{children}</div>; return <div className="rt-noData">{children}</div>;
}); });
Object.assign(ReactTableDefaults, { export function bootstrapReactTable() {
Object.assign(ReactTableDefaults, {
className: '-striped -highlight', className: '-striped -highlight',
defaultFilterMethod: (filter: Filter, row: any) => { defaultFilterMethod: (filter: Filter, row: any) => {
const id = filter.pivotId || filter.id; const id = filter.pivotId || filter.id;
@ -57,4 +58,5 @@ Object.assign(ReactTableDefaults, {
); );
}, },
defaultPageSize: 20, defaultPageSize: 20,
}); });
}

View File

@ -19,7 +19,7 @@
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import React from 'react'; import React from 'react';
import { Capabilities } from '../../utils/capabilities'; import { Capabilities } from '../../utils';
import { HeaderBar } from './header-bar'; import { HeaderBar } from './header-bar';

View File

@ -39,7 +39,7 @@ import {
OverlordDynamicConfigDialog, OverlordDynamicConfigDialog,
} from '../../dialogs'; } from '../../dialogs';
import { getLink } from '../../links'; import { getLink } from '../../links';
import { Capabilities } from '../../utils/capabilities'; import { Capabilities } from '../../utils';
import { ExternalLink } from '../external-link/external-link'; import { ExternalLink } from '../external-link/external-link';
import { PopoverText } from '../popover-text/popover-text'; import { PopoverText } from '../popover-text/popover-text';

View File

@ -21,7 +21,7 @@ import { mount } from 'enzyme';
import React from 'react'; import React from 'react';
import { QueryManager } from '../../utils'; import { QueryManager } from '../../utils';
import { Capabilities } from '../../utils/capabilities'; import { Capabilities } from '../../utils';
import { SegmentTimeline } from './segment-timeline'; import { SegmentTimeline } from './segment-timeline';

View File

@ -17,12 +17,12 @@
*/ */
import { FormGroup, HTMLSelect, Radio, RadioGroup } from '@blueprintjs/core'; import { FormGroup, HTMLSelect, Radio, RadioGroup } from '@blueprintjs/core';
import axios from 'axios';
import { AxisScale } from 'd3-axis'; import { AxisScale } from 'd3-axis';
import { scaleLinear, scaleTime } from 'd3-scale'; import { scaleLinear, scaleTime } from 'd3-scale';
import React from 'react'; import React from 'react';
import { Capabilities } from '../../utils/capabilities'; import { Api } from '../../singletons';
import { Capabilities } from '../../utils';
import { formatBytes, queryDruidSql, QueryManager, uniq } from '../../utils/index'; import { formatBytes, queryDruidSql, QueryManager, uniq } from '../../utils/index';
import { StackedBarChart } from '../../visualization/stacked-bar-chart'; import { StackedBarChart } from '../../visualization/stacked-bar-chart';
import { Loader } from '../loader/loader'; import { Loader } from '../loader/loader';
@ -266,10 +266,10 @@ ORDER BY "start" DESC`;
before.setMonth(before.getMonth() - timeSpan); before.setMonth(before.getMonth() - timeSpan);
const beforeIso = before.toISOString(); const beforeIso = before.toISOString();
datasources = (await axios.get(`/druid/coordinator/v1/datasources`)).data; datasources = (await Api.instance.get(`/druid/coordinator/v1/datasources`)).data;
intervals = (await Promise.all( intervals = (await Promise.all(
datasources.map(async datasource => { datasources.map(async datasource => {
const intervalMap = (await axios.get( const intervalMap = (await Api.instance.get(
`/druid/coordinator/v1/datasources/${datasource}/intervals?simple`, `/druid/coordinator/v1/datasources/${datasource}/intervals?simple`,
)).data; )).data;

View File

@ -17,10 +17,10 @@
*/ */
import { Tab, Tabs } from '@blueprintjs/core'; import { Tab, Tabs } from '@blueprintjs/core';
import axios from 'axios';
import React from 'react'; import React from 'react';
import { useQueryManager } from '../../hooks'; import { useQueryManager } from '../../hooks';
import { Api } from '../../singletons';
import { Loader } from '../loader/loader'; import { Loader } from '../loader/loader';
import { ShowValue } from '../show-value/show-value'; import { ShowValue } from '../show-value/show-value';
@ -41,7 +41,7 @@ export const ShowHistory = React.memo(function ShowHistory(props: ShowHistoryPro
const [historyState] = useQueryManager<string, VersionSpec[]>({ const [historyState] = useQueryManager<string, VersionSpec[]>({
processQuery: async (endpoint: string) => { processQuery: async (endpoint: string) => {
const resp = await axios.get(endpoint); const resp = await Api.instance.get(endpoint);
return resp.data; return resp.data;
}, },
initQuery: endpoint, initQuery: endpoint,

View File

@ -17,13 +17,11 @@
*/ */
import { Button, ButtonGroup, Intent, TextArea } from '@blueprintjs/core'; import { Button, ButtonGroup, Intent, TextArea } from '@blueprintjs/core';
import axios from 'axios';
import copy from 'copy-to-clipboard'; import copy from 'copy-to-clipboard';
import React from 'react'; import React from 'react';
import { useQueryManager } from '../../hooks'; import { useQueryManager } from '../../hooks';
import { AppToaster } from '../../singletons/toaster'; import { Api, AppToaster, UrlBaser } from '../../singletons';
import { UrlBaser } from '../../singletons/url-baser';
import { downloadFile } from '../../utils'; import { downloadFile } from '../../utils';
import { Loader } from '../loader/loader'; import { Loader } from '../loader/loader';
@ -40,7 +38,7 @@ export const ShowJson = React.memo(function ShowJson(props: ShowJsonProps) {
const [jsonState] = useQueryManager<null, string>({ const [jsonState] = useQueryManager<null, string>({
processQuery: async () => { processQuery: async () => {
const resp = await axios.get(endpoint); const resp = await Api.instance.get(endpoint);
let data = resp.data; let data = resp.data;
if (transform) data = transform(data); if (transform) data = transform(data);
return typeof data === 'string' ? data : JSON.stringify(data, undefined, 2); return typeof data === 'string' ? data : JSON.stringify(data, undefined, 2);

View File

@ -17,13 +17,11 @@
*/ */
import { AnchorButton, Button, ButtonGroup, Intent, Switch } from '@blueprintjs/core'; import { AnchorButton, Button, ButtonGroup, Intent, Switch } from '@blueprintjs/core';
import axios from 'axios';
import copy from 'copy-to-clipboard'; import copy from 'copy-to-clipboard';
import React from 'react'; import React from 'react';
import { Loader } from '../../components'; import { Loader } from '../../components';
import { AppToaster } from '../../singletons/toaster'; import { Api, AppToaster, UrlBaser } from '../../singletons';
import { UrlBaser } from '../../singletons/url-baser';
import { QueryManager, QueryState } from '../../utils'; import { QueryManager, QueryState } from '../../utils';
import './show-log.scss'; import './show-log.scss';
@ -65,7 +63,9 @@ export class ShowLog extends React.PureComponent<ShowLogProps, ShowLogState> {
this.showLogQueryManager = new QueryManager({ this.showLogQueryManager = new QueryManager({
processQuery: async () => { processQuery: async () => {
const { endpoint, tailOffset } = this.props; const { endpoint, tailOffset } = this.props;
const resp = await axios.get(endpoint + (tailOffset ? `?offset=-${tailOffset}` : '')); const resp = await Api.instance.get(
endpoint + (tailOffset ? `?offset=-${tailOffset}` : ''),
);
const data = resp.data; const data = resp.data;
let logValue = typeof data === 'string' ? data : JSON.stringify(data, undefined, 2); let logValue = typeof data === 'string' ? data : JSON.stringify(data, undefined, 2);

View File

@ -19,7 +19,7 @@
import { Button, ButtonGroup, TextArea } from '@blueprintjs/core'; import { Button, ButtonGroup, TextArea } from '@blueprintjs/core';
import React from 'react'; import React from 'react';
import { UrlBaser } from '../../singletons/url-baser'; import { UrlBaser } from '../../singletons';
import { downloadFile } from '../../utils'; import { downloadFile } from '../../utils';
import './show-value.scss'; import './show-value.scss';

View File

@ -17,12 +17,11 @@
*/ */
import { Button, ButtonGroup } from '@blueprintjs/core'; import { Button, ButtonGroup } from '@blueprintjs/core';
import axios from 'axios';
import React from 'react'; import React from 'react';
import ReactTable, { CellInfo, Column } from 'react-table'; import ReactTable, { CellInfo, Column } from 'react-table';
import { useQueryManager } from '../../hooks'; import { useQueryManager } from '../../hooks';
import { UrlBaser } from '../../singletons/url-baser'; import { Api, UrlBaser } from '../../singletons';
import { deepGet } from '../../utils'; import { deepGet } from '../../utils';
import { Loader } from '../loader/loader'; import { Loader } from '../loader/loader';
@ -65,7 +64,7 @@ export const SupervisorStatisticsTable = React.memo(function SupervisorStatistic
const [supervisorStatisticsState] = useQueryManager<null, SupervisorStatisticsTableRow[]>({ const [supervisorStatisticsState] = useQueryManager<null, SupervisorStatisticsTableRow[]>({
processQuery: async () => { processQuery: async () => {
const resp = await axios.get(endpoint); const resp = await Api.instance.get(endpoint);
return normalizeSupervisorStatisticsResults(resp.data); return normalizeSupervisorStatisticsResults(resp.data);
}, },
initQuery: null, initQuery: null,

View File

@ -23,9 +23,8 @@ import React from 'react';
import { HashRouter, Route, Switch } from 'react-router-dom'; import { HashRouter, Route, Switch } from 'react-router-dom';
import { HeaderActiveTab, HeaderBar, Loader } from './components'; import { HeaderActiveTab, HeaderBar, Loader } from './components';
import { AppToaster } from './singletons/toaster'; import { AppToaster } from './singletons';
import { QueryManager } from './utils'; import { Capabilities, QueryManager } from './utils';
import { Capabilities } from './utils/capabilities';
import { import {
DatasourcesView, DatasourcesView,
HomeView, HomeView,

View File

@ -31,7 +31,7 @@ import classNames from 'classnames';
import React, { ReactNode, useState } from 'react'; import React, { ReactNode, useState } from 'react';
import { WarningChecklist } from '../../components/warning-checklist/warning-checklist'; import { WarningChecklist } from '../../components/warning-checklist/warning-checklist';
import { AppToaster } from '../../singletons/toaster'; import { AppToaster } from '../../singletons';
import './async-action-dialog.scss'; import './async-action-dialog.scss';

View File

@ -18,7 +18,6 @@
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { SnitchDialog } from '..'; import { SnitchDialog } from '..';
@ -32,7 +31,7 @@ import {
import { COORDINATOR_DYNAMIC_CONFIG_FIELDS, CoordinatorDynamicConfig } from '../../druid-models'; import { COORDINATOR_DYNAMIC_CONFIG_FIELDS, CoordinatorDynamicConfig } from '../../druid-models';
import { useQueryManager } from '../../hooks'; import { useQueryManager } from '../../hooks';
import { getLink } from '../../links'; import { getLink } from '../../links';
import { AppToaster } from '../../singletons/toaster'; import { Api, AppToaster } from '../../singletons';
import { getDruidErrorMessage } from '../../utils'; import { getDruidErrorMessage } from '../../utils';
import './coordinator-dynamic-config-dialog.scss'; import './coordinator-dynamic-config-dialog.scss';
@ -50,7 +49,7 @@ export const CoordinatorDynamicConfigDialog = React.memo(function CoordinatorDyn
const [historyRecordsState] = useQueryManager<null, any[]>({ const [historyRecordsState] = useQueryManager<null, any[]>({
processQuery: async () => { processQuery: async () => {
const historyResp = await axios(`/druid/coordinator/v1/config/history?count=100`); const historyResp = await Api.instance.get(`/druid/coordinator/v1/config/history?count=100`);
return historyResp.data; return historyResp.data;
}, },
initQuery: null, initQuery: null,
@ -59,7 +58,7 @@ export const CoordinatorDynamicConfigDialog = React.memo(function CoordinatorDyn
useQueryManager<null, Record<string, any>>({ useQueryManager<null, Record<string, any>>({
processQuery: async () => { processQuery: async () => {
try { try {
const configResp = await axios.get('/druid/coordinator/v1/config'); const configResp = await Api.instance.get('/druid/coordinator/v1/config');
setDynamicConfig(configResp.data || {}); setDynamicConfig(configResp.data || {});
} catch (e) { } catch (e) {
AppToaster.show({ AppToaster.show({
@ -77,7 +76,7 @@ export const CoordinatorDynamicConfigDialog = React.memo(function CoordinatorDyn
async function saveConfig(comment: string) { async function saveConfig(comment: string) {
try { try {
await axios.post('/druid/coordinator/v1/config', dynamicConfig, { await Api.instance.post('/druid/coordinator/v1/config', dynamicConfig, {
headers: { headers: {
'X-Druid-Author': 'console', 'X-Druid-Author': 'console',
'X-Druid-Comment': comment, 'X-Druid-Comment': comment,

View File

@ -16,8 +16,7 @@
* limitations under the License. * limitations under the License.
*/ */
import axios from 'axios'; import { Api } from '../../singletons';
import { deepGet, pluralIfNeeded, queryDruidSql } from '../../utils'; import { deepGet, pluralIfNeeded, queryDruidSql } from '../../utils';
import { postToSampler } from '../../utils/sampler'; import { postToSampler } from '../../utils/sampler';
@ -56,7 +55,7 @@ export const DOCTOR_CHECKS: DoctorCheck[] = [
// Make sure that the router responds to /status and gives some valid info back // Make sure that the router responds to /status and gives some valid info back
let status: any; let status: any;
try { try {
status = (await axios.get(`/status`)).data; status = (await Api.instance.get(`/status`)).data;
} catch (e) { } catch (e) {
controls.addIssue( controls.addIssue(
`Did not get a /status response from the Router service. Try confirming that it is running and accessible. Got: ${e.message}`, `Did not get a /status response from the Router service. Try confirming that it is running and accessible. Got: ${e.message}`,
@ -76,7 +75,7 @@ export const DOCTOR_CHECKS: DoctorCheck[] = [
// Make sure that everything in /status/properties is above board // Make sure that everything in /status/properties is above board
let properties: Record<string, string>; let properties: Record<string, string>;
try { try {
properties = (await axios.get(`/status/properties`)).data; properties = (await Api.instance.get(`/status/properties`)).data;
} catch (e) { } catch (e) {
controls.addIssue( controls.addIssue(
`Did not get a /status/properties response from the Router. Message: ${e.message}`, `Did not get a /status/properties response from the Router. Message: ${e.message}`,
@ -126,14 +125,14 @@ export const DOCTOR_CHECKS: DoctorCheck[] = [
// Make sure that everything in Coordinator's /status is good // Make sure that everything in Coordinator's /status is good
let myStatus: any; let myStatus: any;
try { try {
myStatus = (await axios.get(`/status`)).data; myStatus = (await Api.instance.get(`/status`)).data;
} catch { } catch {
return; return;
} }
let coordinatorStatus: any; let coordinatorStatus: any;
try { try {
coordinatorStatus = (await axios.get(`/proxy/coordinator/status`)).data; coordinatorStatus = (await Api.instance.get(`/proxy/coordinator/status`)).data;
} catch (e) { } catch (e) {
controls.addIssue( controls.addIssue(
'Did not get a /status response from the Coordinator service. Try confirming that it is running and accessible.', 'Did not get a /status response from the Coordinator service. Try confirming that it is running and accessible.',
@ -143,7 +142,7 @@ export const DOCTOR_CHECKS: DoctorCheck[] = [
let overlordStatus: any; let overlordStatus: any;
try { try {
overlordStatus = (await axios.get(`/proxy/overlord/status`)).data; overlordStatus = (await Api.instance.get(`/proxy/overlord/status`)).data;
} catch (e) { } catch (e) {
controls.addIssue( controls.addIssue(
'Did not get a /status response from the Overlord service. Try confirming that it is running and accessible.', 'Did not get a /status response from the Overlord service. Try confirming that it is running and accessible.',
@ -170,14 +169,15 @@ export const DOCTOR_CHECKS: DoctorCheck[] = [
// Make sure that everything in coordinator and overlord /status/properties is good and matches where needed // Make sure that everything in coordinator and overlord /status/properties is good and matches where needed
let myProperties: Record<string, string>; let myProperties: Record<string, string>;
try { try {
myProperties = (await axios.get(`/status/properties`)).data; myProperties = (await Api.instance.get(`/status/properties`)).data;
} catch { } catch {
return; return;
} }
let coordinatorProperties: Record<string, string>; let coordinatorProperties: Record<string, string>;
try { try {
coordinatorProperties = (await axios.get(`/proxy/coordinator/status/properties`)).data; coordinatorProperties = (await Api.instance.get(`/proxy/coordinator/status/properties`))
.data;
} catch (e) { } catch (e) {
controls.addIssue( controls.addIssue(
'Did not get a /status response from the coordinator. Try confirming that it is running and accessible.', 'Did not get a /status response from the coordinator. Try confirming that it is running and accessible.',
@ -187,7 +187,7 @@ export const DOCTOR_CHECKS: DoctorCheck[] = [
let overlordProperties: Record<string, string>; let overlordProperties: Record<string, string>;
try { try {
overlordProperties = (await axios.get(`/proxy/overlord/status/properties`)).data; overlordProperties = (await Api.instance.get(`/proxy/overlord/status/properties`)).data;
} catch (e) { } catch (e) {
controls.addIssue( controls.addIssue(
'Did not get a /status response from the overlord. Try confirming that it is running and accessible.', 'Did not get a /status response from the overlord. Try confirming that it is running and accessible.',
@ -384,7 +384,8 @@ ORDER BY "num_bad_time_chunks"`,
// Grab the auto-compaction definitions and ignore dataSources that already have auto-compaction // Grab the auto-compaction definitions and ignore dataSources that already have auto-compaction
let compactionResult: any; let compactionResult: any;
try { try {
compactionResult = (await axios.get('/druid/coordinator/v1/config/compaction')).data; compactionResult = (await Api.instance.get('/druid/coordinator/v1/config/compaction'))
.data;
} catch (e) { } catch (e) {
controls.addIssue(`Could not get compaction config. Something is wrong.`); controls.addIssue(`Could not get compaction config. Something is wrong.`);
return; return;

View File

@ -18,7 +18,6 @@
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { SnitchDialog } from '..'; import { SnitchDialog } from '..';
@ -26,7 +25,7 @@ import { AutoForm, ExternalLink } from '../../components';
import { OVERLORD_DYNAMIC_CONFIG_FIELDS, OverlordDynamicConfig } from '../../druid-models'; import { OVERLORD_DYNAMIC_CONFIG_FIELDS, OverlordDynamicConfig } from '../../druid-models';
import { useQueryManager } from '../../hooks'; import { useQueryManager } from '../../hooks';
import { getLink } from '../../links'; import { getLink } from '../../links';
import { AppToaster } from '../../singletons/toaster'; import { Api, AppToaster } from '../../singletons';
import { getDruidErrorMessage } from '../../utils'; import { getDruidErrorMessage } from '../../utils';
import './overlord-dynamic-config-dialog.scss'; import './overlord-dynamic-config-dialog.scss';
@ -43,7 +42,7 @@ export const OverlordDynamicConfigDialog = React.memo(function OverlordDynamicCo
const [historyRecordsState] = useQueryManager<null, any[]>({ const [historyRecordsState] = useQueryManager<null, any[]>({
processQuery: async () => { processQuery: async () => {
const historyResp = await axios(`/druid/indexer/v1/worker/history?count=100`); const historyResp = await Api.instance.get(`/druid/indexer/v1/worker/history?count=100`);
return historyResp.data; return historyResp.data;
}, },
initQuery: null, initQuery: null,
@ -52,7 +51,7 @@ export const OverlordDynamicConfigDialog = React.memo(function OverlordDynamicCo
useQueryManager<null, Record<string, any>>({ useQueryManager<null, Record<string, any>>({
processQuery: async () => { processQuery: async () => {
try { try {
const configResp = await axios(`/druid/indexer/v1/worker`); const configResp = await Api.instance.get(`/druid/indexer/v1/worker`);
setDynamicConfig(configResp.data || {}); setDynamicConfig(configResp.data || {});
} catch (e) { } catch (e) {
AppToaster.show({ AppToaster.show({
@ -70,7 +69,7 @@ export const OverlordDynamicConfigDialog = React.memo(function OverlordDynamicCo
async function saveConfig(comment: string) { async function saveConfig(comment: string) {
try { try {
await axios.post('/druid/indexer/v1/worker', dynamicConfig, { await Api.instance.post('/druid/indexer/v1/worker', dynamicConfig, {
headers: { headers: {
'X-Druid-Author': 'console', 'X-Druid-Author': 'console',
'X-Druid-Comment': comment, 'X-Druid-Comment': comment,

View File

@ -18,13 +18,13 @@
import { Button, Divider, FormGroup } from '@blueprintjs/core'; import { Button, Divider, FormGroup } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { SnitchDialog } from '..'; import { SnitchDialog } from '..';
import { ExternalLink, RuleEditor } from '../../components'; import { ExternalLink, RuleEditor } from '../../components';
import { useQueryManager } from '../../hooks'; import { useQueryManager } from '../../hooks';
import { getLink } from '../../links'; import { getLink } from '../../links';
import { Api } from '../../singletons';
import { swapElements } from '../../utils'; import { swapElements } from '../../utils';
import { Rule, RuleUtil } from '../../utils/load-rule'; import { Rule, RuleUtil } from '../../utils/load-rule';
@ -46,7 +46,9 @@ export const RetentionDialog = React.memo(function RetentionDialog(props: Retent
const [historyQueryState] = useQueryManager<string, any[]>({ const [historyQueryState] = useQueryManager<string, any[]>({
processQuery: async datasource => { processQuery: async datasource => {
const historyResp = await axios(`/druid/coordinator/v1/rules/${datasource}/history`); const historyResp = await Api.instance.get(
`/druid/coordinator/v1/rules/${datasource}/history`,
);
return historyResp.data; return historyResp.data;
}, },
initQuery: props.datasource, initQuery: props.datasource,

View File

@ -21,7 +21,7 @@ import { IconNames } from '@blueprintjs/icons';
import copy from 'copy-to-clipboard'; import copy from 'copy-to-clipboard';
import React from 'react'; import React from 'react';
import { AppToaster } from '../../singletons/toaster'; import { AppToaster } from '../../singletons';
import './show-value-dialog.scss'; import './show-value-dialog.scss';

View File

@ -17,13 +17,12 @@
*/ */
import { Button, Classes, Dialog, Intent } from '@blueprintjs/core'; import { Button, Classes, Dialog, Intent } from '@blueprintjs/core';
import axios from 'axios';
import React from 'react'; import React from 'react';
import ReactTable, { Filter } from 'react-table'; import ReactTable, { Filter } from 'react-table';
import { Loader } from '../../components'; import { Loader } from '../../components';
import { useQueryManager } from '../../hooks'; import { useQueryManager } from '../../hooks';
import { UrlBaser } from '../../singletons/url-baser'; import { Api, UrlBaser } from '../../singletons';
import './status-dialog.scss'; import './status-dialog.scss';
@ -50,7 +49,7 @@ export const StatusDialog = React.memo(function StatusDialog(props: StatusDialog
const { onClose } = props; const { onClose } = props;
const [responseState] = useQueryManager<null, StatusResponse>({ const [responseState] = useQueryManager<null, StatusResponse>({
processQuery: async () => { processQuery: async () => {
const resp = await axios.get(`/status`); const resp = await Api.instance.get(`/status`);
return resp.data; return resp.data;
}, },
initQuery: null, initQuery: null,

View File

@ -16,24 +16,22 @@
* limitations under the License. * limitations under the License.
*/ */
import axios from 'axios'; import { AxiosRequestConfig } from 'axios';
import 'brace'; // Import Ace editor and all the sub components used in the app
import 'brace/ext/language_tools';
import 'brace/theme/solarized_dark';
import 'core-js/stable'; import 'core-js/stable';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import 'regenerator-runtime/runtime'; import 'regenerator-runtime/runtime';
import './ace-modes/dsql'; import './bootstrap/ace';
import './ace-modes/hjson'; import { bootstrapReactTable } from './bootstrap/react-table-defaults';
import './bootstrap/react-table-defaults';
import { ConsoleApplication } from './console-application'; import { ConsoleApplication } from './console-application';
import { Links, setLinkOverrides } from './links'; import { Links, setLinkOverrides } from './links';
import { UrlBaser } from './singletons/url-baser'; import { Api, UrlBaser } from './singletons';
import './entry.scss'; import './entry.scss';
bootstrapReactTable();
const container = document.getElementsByClassName('app-container')[0]; const container = document.getElementsByClassName('app-container')[0];
if (!container) throw new Error('container not found'); if (!container) throw new Error('container not found');
@ -69,16 +67,23 @@ if (typeof consoleConfig.title === 'string') {
window.document.title = consoleConfig.title; window.document.title = consoleConfig.title;
} }
const apiConfig: AxiosRequestConfig = {
headers: {},
};
if (consoleConfig.baseURL) { if (consoleConfig.baseURL) {
axios.defaults.baseURL = consoleConfig.baseURL; apiConfig.baseURL = consoleConfig.baseURL;
UrlBaser.baseUrl = consoleConfig.baseURL; UrlBaser.baseUrl = consoleConfig.baseURL;
} }
if (consoleConfig.customHeaderName && consoleConfig.customHeaderValue) { if (consoleConfig.customHeaderName && consoleConfig.customHeaderValue) {
axios.defaults.headers.common[consoleConfig.customHeaderName] = consoleConfig.customHeaderValue; apiConfig.headers.common[consoleConfig.customHeaderName] = consoleConfig.customHeaderValue;
} }
if (consoleConfig.customHeaders) { if (consoleConfig.customHeaders) {
Object.assign(axios.defaults.headers, consoleConfig.customHeaders); Object.assign(apiConfig.headers, consoleConfig.customHeaders);
} }
Api.initialize(apiConfig);
if (consoleConfig.linkOverrides) { if (consoleConfig.linkOverrides) {
setLinkOverrides(consoleConfig.linkOverrides); setLinkOverrides(consoleConfig.linkOverrides);
} }

View File

@ -20,7 +20,7 @@ import 'core-js/stable';
import { configure } from 'enzyme'; import { configure } from 'enzyme';
import enzymeAdapterReact16 from 'enzyme-adapter-react-16'; import enzymeAdapterReact16 from 'enzyme-adapter-react-16';
import { UrlBaser } from './singletons/url-baser'; import { UrlBaser } from './singletons';
configure({ adapter: new (enzymeAdapterReact16 as any)() }); configure({ adapter: new (enzymeAdapterReact16 as any)() });

View File

@ -0,0 +1,27 @@
/*
* 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, { AxiosInstance, AxiosRequestConfig } from 'axios';
export class Api {
static instance: AxiosInstance;
static initialize(config?: AxiosRequestConfig): void {
Api.instance = axios.create(config);
}
}

View File

@ -0,0 +1,21 @@
/*
* 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.
*/
export * from './api';
export * from './toaster';
export * from './url-baser';

View File

@ -16,7 +16,7 @@
* limitations under the License. * limitations under the License.
*/ */
import axios from 'axios'; import { Api } from '../singletons';
import { localStorageGetJson, LocalStorageKeys } from './local-storage-keys'; import { localStorageGetJson, LocalStorageKeys } from './local-storage-keys';
@ -51,7 +51,7 @@ export class Capabilities {
static async detectQueryType(): Promise<QueryType | undefined> { static async detectQueryType(): Promise<QueryType | undefined> {
// Check SQL endpoint // Check SQL endpoint
try { try {
await axios.post( await Api.instance.post(
'/druid/v2/sql', '/druid/v2/sql',
{ query: 'SELECT 1337', context: { timeout: Capabilities.STATUS_TIMEOUT } }, { query: 'SELECT 1337', context: { timeout: Capabilities.STATUS_TIMEOUT } },
{ timeout: Capabilities.STATUS_TIMEOUT }, { timeout: Capabilities.STATUS_TIMEOUT },
@ -62,14 +62,14 @@ export class Capabilities {
return; // other failure return; // other failure
} }
try { try {
await axios.get('/status', { timeout: Capabilities.STATUS_TIMEOUT }); await Api.instance.get('/status', { timeout: Capabilities.STATUS_TIMEOUT });
} catch (e) { } catch (e) {
return; // total failure return; // total failure
} }
// Status works but SQL 405s => the SQL endpoint is disabled // Status works but SQL 405s => the SQL endpoint is disabled
try { try {
await axios.post( await Api.instance.post(
'/druid/v2', '/druid/v2',
{ {
queryType: 'dataSourceMetadata', queryType: 'dataSourceMetadata',
@ -94,7 +94,7 @@ export class Capabilities {
static async detectNode(node: 'coordinator' | 'overlord'): Promise<boolean | undefined> { static async detectNode(node: 'coordinator' | 'overlord'): Promise<boolean | undefined> {
try { try {
await axios.get(`/druid/${node === 'overlord' ? 'indexer' : node}/v1/isLeader`, { await Api.instance.get(`/druid/${node === 'overlord' ? 'indexer' : node}/v1/isLeader`, {
timeout: Capabilities.STATUS_TIMEOUT, timeout: Capabilities.STATUS_TIMEOUT,
}); });
} catch (e) { } catch (e) {

View File

@ -16,8 +16,9 @@
* limitations under the License. * limitations under the License.
*/ */
import axios from 'axios'; import axios, { AxiosResponse } from 'axios';
import { AxiosResponse } from 'axios';
import { Api } from '../singletons';
import { assemble } from './general'; import { assemble } from './general';
import { RowColumn } from './query-cursor'; import { RowColumn } from './query-cursor';
@ -213,7 +214,7 @@ export class DruidError extends Error {
export async function queryDruidRune(runeQuery: Record<string, any>): Promise<any> { export async function queryDruidRune(runeQuery: Record<string, any>): Promise<any> {
let runeResultResp: AxiosResponse<any>; let runeResultResp: AxiosResponse<any>;
try { try {
runeResultResp = await axios.post('/druid/v2', runeQuery); runeResultResp = await Api.instance.post('/druid/v2', runeQuery);
} catch (e) { } catch (e) {
throw new Error(getDruidErrorMessage(e)); throw new Error(getDruidErrorMessage(e));
} }
@ -223,7 +224,7 @@ export async function queryDruidRune(runeQuery: Record<string, any>): Promise<an
export async function queryDruidSql<T = any>(sqlQueryPayload: Record<string, any>): Promise<T[]> { export async function queryDruidSql<T = any>(sqlQueryPayload: Record<string, any>): Promise<T[]> {
let sqlResultResp: AxiosResponse<any>; let sqlResultResp: AxiosResponse<any>;
try { try {
sqlResultResp = await axios.post('/druid/v2/sql', sqlQueryPayload); sqlResultResp = await Api.instance.post('/druid/v2/sql', sqlQueryPayload);
} catch (e) { } catch (e) {
throw new Error(getDruidErrorMessage(e)); throw new Error(getDruidErrorMessage(e));
} }

View File

@ -26,7 +26,7 @@ import numeral from 'numeral';
import React from 'react'; import React from 'react';
import { Filter, FilterRender } from 'react-table'; import { Filter, FilterRender } from 'react-table';
import { AppToaster } from '../singletons/toaster'; import { AppToaster } from '../singletons';
// These constants are used to make sure that they are not constantly recreated thrashing the pure components // These constants are used to make sure that they are not constantly recreated thrashing the pure components
export const EMPTY_OBJECT: any = {}; export const EMPTY_OBJECT: any = {};

View File

@ -16,8 +16,6 @@
* limitations under the License. * limitations under the License.
*/ */
import axios from 'axios';
import { import {
DimensionsSpec, DimensionsSpec,
getSpecType, getSpecType,
@ -34,6 +32,7 @@ import {
TransformSpec, TransformSpec,
upgradeSpec, upgradeSpec,
} from '../druid-models'; } from '../druid-models';
import { Api } from '../singletons';
import { getDruidErrorMessage, queryDruidRune } from './druid-query'; import { getDruidErrorMessage, queryDruidRune } from './druid-query';
import { import {
@ -181,7 +180,7 @@ export function headerAndRowsFromSampleResponse(
export async function getProxyOverlordModules(): Promise<string[]> { export async function getProxyOverlordModules(): Promise<string[]> {
let statusResp: any; let statusResp: any;
try { try {
statusResp = await axios.get(`/proxy/overlord/status`); statusResp = await Api.instance.get(`/proxy/overlord/status`);
} catch (e) { } catch (e) {
throw new Error(getDruidErrorMessage(e)); throw new Error(getDruidErrorMessage(e));
} }
@ -197,7 +196,7 @@ export async function postToSampler(
let sampleResp: any; let sampleResp: any;
try { try {
sampleResp = await axios.post(`${SAMPLER_URL}?for=${forStr}`, sampleSpec); sampleResp = await Api.instance.post(`${SAMPLER_URL}?for=${forStr}`, sampleSpec);
} catch (e) { } catch (e) {
throw new Error(getDruidErrorMessage(e)); throw new Error(getDruidErrorMessage(e));
} }

View File

@ -19,7 +19,7 @@
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import React from 'react'; import React from 'react';
import { Capabilities } from '../../utils/capabilities'; import { Capabilities } from '../../utils';
import { DatasourcesView } from './datasource-view'; import { DatasourcesView } from './datasource-view';

View File

@ -18,7 +18,6 @@
import { FormGroup, InputGroup, Intent, MenuItem, Switch } from '@blueprintjs/core'; import { FormGroup, InputGroup, Intent, MenuItem, Switch } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import classNames from 'classnames'; import classNames from 'classnames';
import { SqlQuery, SqlRef } from 'druid-query-toolkit'; import { SqlQuery, SqlRef } from 'druid-query-toolkit';
import React from 'react'; import React from 'react';
@ -45,7 +44,7 @@ import {
formatCompactionConfigAndStatus, formatCompactionConfigAndStatus,
zeroCompactionStatus, zeroCompactionStatus,
} from '../../druid-models'; } from '../../druid-models';
import { AppToaster } from '../../singletons/toaster'; import { Api, AppToaster } from '../../singletons';
import { import {
addFilter, addFilter,
Capabilities, Capabilities,
@ -317,8 +316,10 @@ GROUP BY 1`;
if (capabilities.hasSql()) { if (capabilities.hasSql()) {
datasources = await queryDruidSql({ query: DatasourcesView.DATASOURCE_SQL }); datasources = await queryDruidSql({ query: DatasourcesView.DATASOURCE_SQL });
} else if (capabilities.hasCoordinatorAccess()) { } else if (capabilities.hasCoordinatorAccess()) {
const datasourcesResp = await axios.get('/druid/coordinator/v1/datasources?simple'); const datasourcesResp = await Api.instance.get(
const loadstatusResp = await axios.get('/druid/coordinator/v1/loadstatus?simple'); '/druid/coordinator/v1/datasources?simple',
);
const loadstatusResp = await Api.instance.get('/druid/coordinator/v1/loadstatus?simple');
const loadstatus = loadstatusResp.data; const loadstatus = loadstatusResp.data;
datasources = datasourcesResp.data.map( datasources = datasourcesResp.data.map(
(d: any): DatasourceQueryResultRow => { (d: any): DatasourceQueryResultRow => {
@ -367,22 +368,26 @@ GROUP BY 1`;
if (this.state.showUnused) { if (this.state.showUnused) {
// Using 'includeDisabled' parameter for compatibility. // Using 'includeDisabled' parameter for compatibility.
// Should be changed to 'includeUnused' in Druid 0.17 // Should be changed to 'includeUnused' in Druid 0.17
const unusedResp = await axios.get( const unusedResp = await Api.instance.get(
'/druid/coordinator/v1/metadata/datasources?includeDisabled', '/druid/coordinator/v1/metadata/datasources?includeDisabled',
); );
unused = unusedResp.data.filter((d: string) => !seen[d]); unused = unusedResp.data.filter((d: string) => !seen[d]);
} }
const rulesResp = await axios.get('/druid/coordinator/v1/rules'); const rulesResp = await Api.instance.get('/druid/coordinator/v1/rules');
const rules = rulesResp.data; const rules = rulesResp.data;
const compactionConfigsResp = await axios.get('/druid/coordinator/v1/config/compaction'); const compactionConfigsResp = await Api.instance.get(
'/druid/coordinator/v1/config/compaction',
);
const compactionConfigs = lookupBy( const compactionConfigs = lookupBy(
compactionConfigsResp.data.compactionConfigs || [], compactionConfigsResp.data.compactionConfigs || [],
(c: CompactionConfig) => c.dataSource, (c: CompactionConfig) => c.dataSource,
); );
const compactionStatusesResp = await axios.get('/druid/coordinator/v1/compaction/status'); const compactionStatusesResp = await Api.instance.get(
'/druid/coordinator/v1/compaction/status',
);
const compactionStatuses = lookupBy( const compactionStatuses = lookupBy(
compactionStatusesResp.data.latestStatus || [], compactionStatusesResp.data.latestStatus || [],
(c: CompactionStatus) => c.dataSource, (c: CompactionStatus) => c.dataSource,
@ -412,7 +417,7 @@ GROUP BY 1`;
this.tiersQueryManager = new QueryManager({ this.tiersQueryManager = new QueryManager({
processQuery: async capabilities => { processQuery: async capabilities => {
if (capabilities.hasCoordinatorAccess()) { if (capabilities.hasCoordinatorAccess()) {
const tiersResp = await axios.get('/druid/coordinator/v1/tiers'); const tiersResp = await Api.instance.get('/druid/coordinator/v1/tiers');
return tiersResp.data; return tiersResp.data;
} else { } else {
throw new Error(`must have coordinator access`); throw new Error(`must have coordinator access`);
@ -455,7 +460,7 @@ GROUP BY 1`;
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
const resp = await axios.delete( const resp = await Api.instance.delete(
`/druid/coordinator/v1/datasources/${datasourceToMarkAsUnusedAllSegmentsIn}`, `/druid/coordinator/v1/datasources/${datasourceToMarkAsUnusedAllSegmentsIn}`,
{}, {},
); );
@ -486,7 +491,7 @@ GROUP BY 1`;
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
const resp = await axios.post( const resp = await Api.instance.post(
`/druid/coordinator/v1/datasources/${datasourceToMarkAllNonOvershadowedSegmentsAsUsedIn}`, `/druid/coordinator/v1/datasources/${datasourceToMarkAllNonOvershadowedSegmentsAsUsedIn}`,
{}, {},
); );
@ -518,7 +523,7 @@ GROUP BY 1`;
action={async () => { action={async () => {
if (!useUnuseInterval) return; if (!useUnuseInterval) return;
const param = isUse ? 'markUsed' : 'markUnused'; const param = isUse ? 'markUsed' : 'markUnused';
const resp = await axios.post( const resp = await Api.instance.post(
`/druid/coordinator/v1/datasources/${datasourceToMarkSegmentsByIntervalIn}/${param}`, `/druid/coordinator/v1/datasources/${datasourceToMarkSegmentsByIntervalIn}/${param}`,
{ {
interval: useUnuseInterval, interval: useUnuseInterval,
@ -560,7 +565,7 @@ GROUP BY 1`;
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
const resp = await axios.delete( const resp = await Api.instance.delete(
`/druid/coordinator/v1/datasources/${killDatasource}?kill=true&interval=1000/3000`, `/druid/coordinator/v1/datasources/${killDatasource}?kill=true&interval=1000/3000`,
{}, {},
); );
@ -628,7 +633,7 @@ GROUP BY 1`;
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
const resp = await axios.post(`/druid/coordinator/v1/compaction/compact`, {}); const resp = await Api.instance.post(`/druid/coordinator/v1/compaction/compact`, {});
return resp.data; return resp.data;
}} }}
confirmButtonText="Force compaction run" confirmButtonText="Force compaction run"
@ -648,7 +653,7 @@ GROUP BY 1`;
private saveRules = async (datasource: string, rules: Rule[], comment: string) => { private saveRules = async (datasource: string, rules: Rule[], comment: string) => {
try { try {
await axios.post(`/druid/coordinator/v1/rules/${datasource}`, rules, { await Api.instance.post(`/druid/coordinator/v1/rules/${datasource}`, rules, {
headers: { headers: {
'X-Druid-Author': 'console', 'X-Druid-Author': 'console',
'X-Druid-Comment': comment, 'X-Druid-Comment': comment,
@ -689,7 +694,7 @@ GROUP BY 1`;
private saveCompaction = async (compactionConfig: any) => { private saveCompaction = async (compactionConfig: any) => {
if (!compactionConfig) return; if (!compactionConfig) return;
try { try {
await axios.post(`/druid/coordinator/v1/config/compaction`, compactionConfig); await Api.instance.post(`/druid/coordinator/v1/config/compaction`, compactionConfig);
this.setState({ compactionDialogOpenOn: undefined }); this.setState({ compactionDialogOpenOn: undefined });
this.datasourceQueryManager.rerunLastQuery(); this.datasourceQueryManager.rerunLastQuery();
} catch (e) { } catch (e) {
@ -711,7 +716,7 @@ GROUP BY 1`;
text: 'Confirm', text: 'Confirm',
onClick: async () => { onClick: async () => {
try { try {
await axios.delete(`/druid/coordinator/v1/config/compaction/${datasource}`); await Api.instance.delete(`/druid/coordinator/v1/config/compaction/${datasource}`);
this.setState({ compactionDialogOpenOn: undefined }, () => this.setState({ compactionDialogOpenOn: undefined }, () =>
this.datasourceQueryManager.rerunLastQuery(), this.datasourceQueryManager.rerunLastQuery(),
); );

View File

@ -19,7 +19,7 @@
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import React from 'react'; import React from 'react';
import { Capabilities } from '../../../utils/capabilities'; import { Capabilities } from '../../../utils';
import { DatasourcesCard } from './datasources-card'; import { DatasourcesCard } from './datasources-card';

View File

@ -17,12 +17,12 @@
*/ */
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import React from 'react'; import React from 'react';
import { useQueryManager } from '../../../hooks'; import { useQueryManager } from '../../../hooks';
import { Api } from '../../../singletons';
import { pluralIfNeeded, queryDruidSql } from '../../../utils'; import { pluralIfNeeded, queryDruidSql } from '../../../utils';
import { Capabilities } from '../../../utils/capabilities'; import { Capabilities } from '../../../utils';
import { HomeViewCard } from '../home-view-card/home-view-card'; import { HomeViewCard } from '../home-view-card/home-view-card';
export interface DatasourcesCardProps { export interface DatasourcesCardProps {
@ -38,7 +38,7 @@ export const DatasourcesCard = React.memo(function DatasourcesCard(props: Dataso
query: `SELECT datasource FROM sys.segments GROUP BY 1`, query: `SELECT datasource FROM sys.segments GROUP BY 1`,
}); });
} else if (capabilities.hasCoordinatorAccess()) { } else if (capabilities.hasCoordinatorAccess()) {
const datasourcesResp = await axios.get('/druid/coordinator/v1/datasources'); const datasourcesResp = await Api.instance.get('/druid/coordinator/v1/datasources');
datasources = datasourcesResp.data; datasources = datasourcesResp.data;
} else { } else {
throw new Error(`must have SQL or coordinator access`); throw new Error(`must have SQL or coordinator access`);

View File

@ -19,7 +19,7 @@
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import React from 'react'; import React from 'react';
import { Capabilities } from '../../utils/capabilities'; import { Capabilities } from '../../utils';
import { HomeView } from './home-view'; import { HomeView } from './home-view';

View File

@ -18,7 +18,7 @@
import React from 'react'; import React from 'react';
import { Capabilities } from '../../utils/capabilities'; import { Capabilities } from '../../utils';
import { DatasourcesCard } from './datasources-card/datasources-card'; import { DatasourcesCard } from './datasources-card/datasources-card';
import { LookupsCard } from './lookups-card/lookups-card'; import { LookupsCard } from './lookups-card/lookups-card';

View File

@ -19,7 +19,7 @@
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import React from 'react'; import React from 'react';
import { Capabilities } from '../../../utils/capabilities'; import { Capabilities } from '../../../utils';
import { LookupsCard } from './lookups-card'; import { LookupsCard } from './lookups-card';

View File

@ -17,13 +17,13 @@
*/ */
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import { sum } from 'd3-array'; import { sum } from 'd3-array';
import React from 'react'; import React from 'react';
import { useQueryManager } from '../../../hooks'; import { useQueryManager } from '../../../hooks';
import { Api } from '../../../singletons';
import { isLookupsUninitialized, pluralIfNeeded } from '../../../utils'; import { isLookupsUninitialized, pluralIfNeeded } from '../../../utils';
import { Capabilities } from '../../../utils/capabilities'; import { Capabilities } from '../../../utils';
import { HomeViewCard } from '../home-view-card/home-view-card'; import { HomeViewCard } from '../home-view-card/home-view-card';
export interface LookupsCardProps { export interface LookupsCardProps {
@ -34,7 +34,7 @@ export const LookupsCard = React.memo(function LookupsCard(props: LookupsCardPro
const [lookupsCountState] = useQueryManager<Capabilities, number>({ const [lookupsCountState] = useQueryManager<Capabilities, number>({
processQuery: async capabilities => { processQuery: async capabilities => {
if (capabilities.hasCoordinatorAccess()) { if (capabilities.hasCoordinatorAccess()) {
const resp = await axios.get('/druid/coordinator/v1/lookups/status'); const resp = await Api.instance.get('/druid/coordinator/v1/lookups/status');
const data = resp.data; const data = resp.data;
return sum(Object.keys(data).map(k => Object.keys(data[k]).length)); return sum(Object.keys(data).map(k => Object.keys(data[k]).length));
} else { } else {

View File

@ -19,7 +19,7 @@
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import React from 'react'; import React from 'react';
import { Capabilities } from '../../../utils/capabilities'; import { Capabilities } from '../../../utils';
import { SegmentsCard } from './segments-card'; import { SegmentsCard } from './segments-card';

View File

@ -17,11 +17,11 @@
*/ */
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import { sum } from 'd3-array'; import { sum } from 'd3-array';
import React from 'react'; import React from 'react';
import { useQueryManager } from '../../../hooks'; import { useQueryManager } from '../../../hooks';
import { Api } from '../../../singletons';
import { Capabilities, deepGet, pluralIfNeeded, queryDruidSql } from '../../../utils'; import { Capabilities, deepGet, pluralIfNeeded, queryDruidSql } from '../../../utils';
import { HomeViewCard } from '../home-view-card/home-view-card'; import { HomeViewCard } from '../home-view-card/home-view-card';
@ -46,11 +46,13 @@ FROM sys.segments`,
}); });
return segments.length === 1 ? segments[0] : null; return segments.length === 1 ? segments[0] : null;
} else if (capabilities.hasCoordinatorAccess()) { } else if (capabilities.hasCoordinatorAccess()) {
const loadstatusResp = await axios.get('/druid/coordinator/v1/loadstatus?simple'); const loadstatusResp = await Api.instance.get('/druid/coordinator/v1/loadstatus?simple');
const loadstatus = loadstatusResp.data; const loadstatus = loadstatusResp.data;
const unavailableSegmentNum = sum(Object.keys(loadstatus), key => loadstatus[key]); const unavailableSegmentNum = sum(Object.keys(loadstatus), key => loadstatus[key]);
const datasourcesMetaResp = await axios.get('/druid/coordinator/v1/datasources?simple'); const datasourcesMetaResp = await Api.instance.get(
'/druid/coordinator/v1/datasources?simple',
);
const datasourcesMeta = datasourcesMetaResp.data; const datasourcesMeta = datasourcesMetaResp.data;
const availableSegmentNum = sum(datasourcesMeta, (curr: any) => const availableSegmentNum = sum(datasourcesMeta, (curr: any) =>
deepGet(curr, 'properties.segments.count'), deepGet(curr, 'properties.segments.count'),

View File

@ -19,7 +19,7 @@
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import React from 'react'; import React from 'react';
import { Capabilities } from '../../../utils/capabilities'; import { Capabilities } from '../../../utils';
import { ServicesCard } from './services-card'; import { ServicesCard } from './services-card';

View File

@ -17,13 +17,13 @@
*/ */
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import React from 'react'; import React from 'react';
import { PluralPairIfNeeded } from '../../../components/plural-pair-if-needed/plural-pair-if-needed'; import { PluralPairIfNeeded } from '../../../components/plural-pair-if-needed/plural-pair-if-needed';
import { useQueryManager } from '../../../hooks'; import { useQueryManager } from '../../../hooks';
import { Api } from '../../../singletons';
import { lookupBy, queryDruidSql } from '../../../utils'; import { lookupBy, queryDruidSql } from '../../../utils';
import { Capabilities } from '../../../utils/capabilities'; import { Capabilities } from '../../../utils';
import { HomeViewCard } from '../home-view-card/home-view-card'; import { HomeViewCard } from '../home-view-card/home-view-card';
export interface ServiceCounts { export interface ServiceCounts {
@ -53,10 +53,10 @@ export const ServicesCard = React.memo(function ServicesCard(props: ServicesCard
}); });
return lookupBy(serviceCountsFromQuery, x => x.service_type, x => x.count); return lookupBy(serviceCountsFromQuery, x => x.service_type, x => x.count);
} else if (capabilities.hasCoordinatorAccess()) { } else if (capabilities.hasCoordinatorAccess()) {
const services = (await axios.get('/druid/coordinator/v1/servers?simple')).data; const services = (await Api.instance.get('/druid/coordinator/v1/servers?simple')).data;
const middleManager = capabilities.hasOverlordAccess() const middleManager = capabilities.hasOverlordAccess()
? (await axios.get('/druid/indexer/v1/workers')).data ? (await Api.instance.get('/druid/indexer/v1/workers')).data
: []; : [];
return { return {

View File

@ -17,11 +17,11 @@
*/ */
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { StatusDialog } from '../../../dialogs/status-dialog/status-dialog'; import { StatusDialog } from '../../../dialogs/status-dialog/status-dialog';
import { useQueryManager } from '../../../hooks'; import { useQueryManager } from '../../../hooks';
import { Api } from '../../../singletons';
import { pluralIfNeeded } from '../../../utils'; import { pluralIfNeeded } from '../../../utils';
import { HomeViewCard } from '../home-view-card/home-view-card'; import { HomeViewCard } from '../home-view-card/home-view-card';
@ -36,7 +36,7 @@ export const StatusCard = React.memo(function StatusCard(_props: StatusCardProps
const [showStatusDialog, setShowStatusDialog] = useState(false); const [showStatusDialog, setShowStatusDialog] = useState(false);
const [statusSummaryState] = useQueryManager<null, StatusSummary>({ const [statusSummaryState] = useQueryManager<null, StatusSummary>({
processQuery: async () => { processQuery: async () => {
const statusResp = await axios.get('/status'); const statusResp = await Api.instance.get('/status');
return { return {
version: statusResp.data.version, version: statusResp.data.version,
extensionCount: statusResp.data.modules.length, extensionCount: statusResp.data.modules.length,

View File

@ -19,7 +19,7 @@
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import React from 'react'; import React from 'react';
import { Capabilities } from '../../../utils/capabilities'; import { Capabilities } from '../../../utils';
import { SupervisorsCard } from './supervisors-card'; import { SupervisorsCard } from './supervisors-card';

View File

@ -17,12 +17,12 @@
*/ */
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import React from 'react'; import React from 'react';
import { useQueryManager } from '../../../hooks'; import { useQueryManager } from '../../../hooks';
import { Api } from '../../../singletons';
import { pluralIfNeeded, queryDruidSql } from '../../../utils'; import { pluralIfNeeded, queryDruidSql } from '../../../utils';
import { Capabilities } from '../../../utils/capabilities'; import { Capabilities } from '../../../utils';
import { HomeViewCard } from '../home-view-card/home-view-card'; import { HomeViewCard } from '../home-view-card/home-view-card';
export interface SupervisorCounts { export interface SupervisorCounts {
@ -45,7 +45,7 @@ export const SupervisorsCard = React.memo(function SupervisorsCard(props: Superv
FROM sys.supervisors`, FROM sys.supervisors`,
}))[0]; }))[0];
} else if (capabilities.hasOverlordAccess()) { } else if (capabilities.hasOverlordAccess()) {
const resp = await axios.get('/druid/indexer/v1/supervisor?full'); const resp = await Api.instance.get('/druid/indexer/v1/supervisor?full');
const data = resp.data; const data = resp.data;
return { return {
running: data.filter((d: any) => d.spec.suspended === false).length, running: data.filter((d: any) => d.spec.suspended === false).length,

View File

@ -19,7 +19,7 @@
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import React from 'react'; import React from 'react';
import { Capabilities } from '../../../utils/capabilities'; import { Capabilities } from '../../../utils';
import { TasksCard } from './tasks-card'; import { TasksCard } from './tasks-card';

View File

@ -17,13 +17,13 @@
*/ */
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import React from 'react'; import React from 'react';
import { PluralPairIfNeeded } from '../../../components/plural-pair-if-needed/plural-pair-if-needed'; import { PluralPairIfNeeded } from '../../../components/plural-pair-if-needed/plural-pair-if-needed';
import { useQueryManager } from '../../../hooks'; import { useQueryManager } from '../../../hooks';
import { Api } from '../../../singletons';
import { lookupBy, pluralIfNeeded, queryDruidSql } from '../../../utils'; import { lookupBy, pluralIfNeeded, queryDruidSql } from '../../../utils';
import { Capabilities } from '../../../utils/capabilities'; import { Capabilities } from '../../../utils';
import { HomeViewCard } from '../home-view-card/home-view-card'; import { HomeViewCard } from '../home-view-card/home-view-card';
function getTaskStatus(d: any) { function getTaskStatus(d: any) {
@ -55,7 +55,7 @@ GROUP BY 1`,
}); });
return lookupBy(taskCountsFromQuery, x => x.status, x => x.count); return lookupBy(taskCountsFromQuery, x => x.status, x => x.count);
} else if (capabilities.hasOverlordAccess()) { } else if (capabilities.hasOverlordAccess()) {
const tasks: any[] = (await axios.get('/druid/indexer/v1/tasks')).data; const tasks: any[] = (await Api.instance.get('/druid/indexer/v1/tasks')).data;
return { return {
SUCCESS: tasks.filter(d => getTaskStatus(d) === 'SUCCESS').length, SUCCESS: tasks.filter(d => getTaskStatus(d) === 'SUCCESS').length,
FAILED: tasks.filter(d => getTaskStatus(d) === 'FAILED').length, FAILED: tasks.filter(d => getTaskStatus(d) === 'FAILED').length,

View File

@ -19,7 +19,7 @@
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import React from 'react'; import React from 'react';
import { Capabilities } from '../../utils/capabilities'; import { Capabilities } from '../../utils';
import { IngestionView } from './ingestion-view'; import { IngestionView } from './ingestion-view';

View File

@ -18,7 +18,6 @@
import { Alert, Button, ButtonGroup, Intent, Label, MenuItem } from '@blueprintjs/core'; import { Alert, Button, ButtonGroup, Intent, Label, MenuItem } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import React from 'react'; import React from 'react';
import SplitterLayout from 'react-splitter-layout'; import SplitterLayout from 'react-splitter-layout';
import ReactTable from 'react-table'; import ReactTable from 'react-table';
@ -40,7 +39,7 @@ import {
SupervisorTableActionDialog, SupervisorTableActionDialog,
TaskTableActionDialog, TaskTableActionDialog,
} from '../../dialogs'; } from '../../dialogs';
import { AppToaster } from '../../singletons/toaster'; import { Api, AppToaster } from '../../singletons';
import { import {
addFilter, addFilter,
addFilterRaw, addFilterRaw,
@ -56,8 +55,8 @@ import {
QueryManager, QueryManager,
QueryState, QueryState,
} from '../../utils'; } from '../../utils';
import { Capabilities } from '../../utils';
import { BasicAction } from '../../utils/basic-action'; import { BasicAction } from '../../utils/basic-action';
import { Capabilities } from '../../utils/capabilities';
import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array'; import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array';
import './ingestion-view.scss'; import './ingestion-view.scss';
@ -251,7 +250,7 @@ ORDER BY "rank" DESC, "created_time" DESC`;
query: IngestionView.SUPERVISOR_SQL, query: IngestionView.SUPERVISOR_SQL,
}); });
} else if (capabilities.hasOverlordAccess()) { } else if (capabilities.hasOverlordAccess()) {
const supervisors = (await axios.get('/druid/indexer/v1/supervisor?full')).data; const supervisors = (await Api.instance.get('/druid/indexer/v1/supervisor?full')).data;
if (!Array.isArray(supervisors)) throw new Error(`Unexpected results`); if (!Array.isArray(supervisors)) throw new Error(`Unexpected results`);
return supervisors.map((sup: any) => { return supervisors.map((sup: any) => {
return { return {
@ -284,7 +283,7 @@ ORDER BY "rank" DESC, "created_time" DESC`;
query: IngestionView.TASK_SQL, query: IngestionView.TASK_SQL,
}); });
} else if (capabilities.hasOverlordAccess()) { } else if (capabilities.hasOverlordAccess()) {
const resp = await axios.get(`/druid/indexer/v1/tasks`); const resp = await Api.instance.get(`/druid/indexer/v1/tasks`);
return IngestionView.parseTasks(resp.data); return IngestionView.parseTasks(resp.data);
} else { } else {
throw new Error(`must have SQL or overlord access`); throw new Error(`must have SQL or overlord access`);
@ -343,7 +342,7 @@ ORDER BY "rank" DESC, "created_time" DESC`;
private submitSupervisor = async (spec: JSON) => { private submitSupervisor = async (spec: JSON) => {
try { try {
await axios.post('/druid/indexer/v1/supervisor', spec); await Api.instance.post('/druid/indexer/v1/supervisor', spec);
} catch (e) { } catch (e) {
AppToaster.show({ AppToaster.show({
message: `Failed to submit supervisor: ${getDruidErrorMessage(e)}`, message: `Failed to submit supervisor: ${getDruidErrorMessage(e)}`,
@ -361,7 +360,7 @@ ORDER BY "rank" DESC, "created_time" DESC`;
private submitTask = async (spec: JSON) => { private submitTask = async (spec: JSON) => {
try { try {
await axios.post('/druid/indexer/v1/task', spec); await Api.instance.post('/druid/indexer/v1/task', spec);
} catch (e) { } catch (e) {
AppToaster.show({ AppToaster.show({
message: `Failed to submit task: ${getDruidErrorMessage(e)}`, message: `Failed to submit task: ${getDruidErrorMessage(e)}`,
@ -431,7 +430,7 @@ ORDER BY "rank" DESC, "created_time" DESC`;
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
const resp = await axios.post( const resp = await Api.instance.post(
`/druid/indexer/v1/supervisor/${resumeSupervisorId}/resume`, `/druid/indexer/v1/supervisor/${resumeSupervisorId}/resume`,
{}, {},
); );
@ -460,7 +459,7 @@ ORDER BY "rank" DESC, "created_time" DESC`;
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
const resp = await axios.post( const resp = await Api.instance.post(
`/druid/indexer/v1/supervisor/${suspendSupervisorId}/suspend`, `/druid/indexer/v1/supervisor/${suspendSupervisorId}/suspend`,
{}, {},
); );
@ -489,7 +488,7 @@ ORDER BY "rank" DESC, "created_time" DESC`;
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
const resp = await axios.post( const resp = await Api.instance.post(
`/druid/indexer/v1/supervisor/${resetSupervisorId}/reset`, `/druid/indexer/v1/supervisor/${resetSupervisorId}/reset`,
{}, {},
); );
@ -527,7 +526,7 @@ ORDER BY "rank" DESC, "created_time" DESC`;
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
const resp = await axios.post( const resp = await Api.instance.post(
`/druid/indexer/v1/supervisor/${terminateSupervisorId}/terminate`, `/druid/indexer/v1/supervisor/${terminateSupervisorId}/terminate`,
{}, {},
); );
@ -684,7 +683,7 @@ ORDER BY "rank" DESC, "created_time" DESC`;
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
const resp = await axios.post(`/druid/indexer/v1/task/${killTaskId}/shutdown`, {}); const resp = await Api.instance.post(`/druid/indexer/v1/task/${killTaskId}/shutdown`, {});
return resp.data; return resp.data;
}} }}
confirmButtonText="Kill task" confirmButtonText="Kill task"
@ -945,7 +944,7 @@ ORDER BY "rank" DESC, "created_time" DESC`;
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
const resp = await axios.post(`/druid/indexer/v1/supervisor/resumeAll`, {}); const resp = await Api.instance.post(`/druid/indexer/v1/supervisor/resumeAll`, {});
return resp.data; return resp.data;
}} }}
confirmButtonText="Resume all supervisors" confirmButtonText="Resume all supervisors"
@ -971,7 +970,7 @@ ORDER BY "rank" DESC, "created_time" DESC`;
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
const resp = await axios.post(`/druid/indexer/v1/supervisor/suspendAll`, {}); const resp = await Api.instance.post(`/druid/indexer/v1/supervisor/suspendAll`, {});
return resp.data; return resp.data;
}} }}
confirmButtonText="Suspend all supervisors" confirmButtonText="Suspend all supervisors"
@ -997,7 +996,7 @@ ORDER BY "rank" DESC, "created_time" DESC`;
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
const resp = await axios.post(`/druid/indexer/v1/supervisor/terminateAll`, {}); const resp = await Api.instance.post(`/druid/indexer/v1/supervisor/terminateAll`, {});
return resp.data; return resp.data;
}} }}
confirmButtonText="Terminate all supervisors" confirmButtonText="Terminate all supervisors"

View File

@ -39,7 +39,6 @@ import {
TextArea, TextArea,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import classNames from 'classnames'; import classNames from 'classnames';
import memoize from 'memoize-one'; import memoize from 'memoize-one';
import React from 'react'; import React from 'react';
@ -121,8 +120,7 @@ import {
upgradeSpec, upgradeSpec,
} from '../../druid-models'; } from '../../druid-models';
import { getLink } from '../../links'; import { getLink } from '../../links';
import { AppToaster } from '../../singletons/toaster'; import { Api, AppToaster, UrlBaser } from '../../singletons';
import { UrlBaser } from '../../singletons/url-baser';
import { import {
deepDelete, deepDelete,
deepGet, deepGet,
@ -3220,7 +3218,7 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
const { initSupervisorId } = this.props; const { initSupervisorId } = this.props;
try { try {
const resp = await axios.get(`/druid/indexer/v1/supervisor/${initSupervisorId}`); const resp = await Api.instance.get(`/druid/indexer/v1/supervisor/${initSupervisorId}`);
this.updateSpec(cleanSpec(resp.data)); this.updateSpec(cleanSpec(resp.data));
this.setState({ continueToSpec: true }); this.setState({ continueToSpec: true });
this.updateStep('spec'); this.updateStep('spec');
@ -3236,7 +3234,7 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
const { initTaskId } = this.props; const { initTaskId } = this.props;
try { try {
const resp = await axios.get(`/druid/indexer/v1/task/${initTaskId}`); const resp = await Api.instance.get(`/druid/indexer/v1/task/${initTaskId}`);
this.updateSpec(cleanSpec(resp.data.payload)); this.updateSpec(cleanSpec(resp.data.payload));
this.setState({ continueToSpec: true }); this.setState({ continueToSpec: true });
this.updateStep('spec'); this.updateStep('spec');
@ -3302,7 +3300,7 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
if (isTask(spec)) { if (isTask(spec)) {
let taskResp: any; let taskResp: any;
try { try {
taskResp = await axios.post('/druid/indexer/v1/task', spec); taskResp = await Api.instance.post('/druid/indexer/v1/task', spec);
} catch (e) { } catch (e) {
AppToaster.show({ AppToaster.show({
message: `Failed to submit task: ${getDruidErrorMessage(e)}`, message: `Failed to submit task: ${getDruidErrorMessage(e)}`,
@ -3322,7 +3320,7 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
}, 1000); }, 1000);
} else { } else {
try { try {
await axios.post('/druid/indexer/v1/supervisor', spec); await Api.instance.post('/druid/indexer/v1/supervisor', spec);
} catch (e) { } catch (e) {
AppToaster.show({ AppToaster.show({
message: `Failed to submit supervisor: ${getDruidErrorMessage(e)}`, message: `Failed to submit supervisor: ${getDruidErrorMessage(e)}`,

View File

@ -18,7 +18,6 @@
import { Button, Intent } from '@blueprintjs/core'; import { Button, Intent } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import React from 'react'; import React from 'react';
import ReactTable from 'react-table'; import ReactTable from 'react-table';
@ -34,7 +33,7 @@ import {
import { AsyncActionDialog, LookupEditDialog } from '../../dialogs/'; import { AsyncActionDialog, LookupEditDialog } from '../../dialogs/';
import { LookupTableActionDialog } from '../../dialogs/lookup-table-action-dialog/lookup-table-action-dialog'; import { LookupTableActionDialog } from '../../dialogs/lookup-table-action-dialog/lookup-table-action-dialog';
import { LookupSpec } from '../../druid-models'; import { LookupSpec } from '../../druid-models';
import { AppToaster } from '../../singletons/toaster'; import { Api, AppToaster } from '../../singletons';
import { import {
getDruidErrorMessage, getDruidErrorMessage,
isLookupsUninitialized, isLookupsUninitialized,
@ -107,14 +106,16 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
this.lookupsQueryManager = new QueryManager({ this.lookupsQueryManager = new QueryManager({
processQuery: async () => { processQuery: async () => {
const tiersResp = await axios.get('/druid/coordinator/v1/lookups/config?discover=true'); const tiersResp = await Api.instance.get(
'/druid/coordinator/v1/lookups/config?discover=true',
);
const tiers = const tiers =
tiersResp.data && tiersResp.data.length > 0 tiersResp.data && tiersResp.data.length > 0
? tiersResp.data.sort(tierNameCompare) ? tiersResp.data.sort(tierNameCompare)
: [DEFAULT_LOOKUP_TIER]; : [DEFAULT_LOOKUP_TIER];
const lookupEntries: {}[] = []; const lookupEntries: {}[] = [];
const lookupResp = await axios.get('/druid/coordinator/v1/lookups/config/all'); const lookupResp = await Api.instance.get('/druid/coordinator/v1/lookups/config/all');
const lookupData = lookupResp.data; const lookupData = lookupResp.data;
Object.keys(lookupData).map((tier: string) => { Object.keys(lookupData).map((tier: string) => {
const lookupIds = lookupData[tier]; const lookupIds = lookupData[tier];
@ -151,7 +152,7 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
private async initializeLookup() { private async initializeLookup() {
try { try {
await axios.post(`/druid/coordinator/v1/lookups/config`, {}); await Api.instance.post(`/druid/coordinator/v1/lookups/config`, {});
this.lookupsQueryManager.rerunLastQuery(); this.lookupsQueryManager.rerunLastQuery();
} catch (e) { } catch (e) {
AppToaster.show({ AppToaster.show({
@ -228,7 +229,7 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
}; };
} }
try { try {
await axios.post(endpoint, dataJson); await Api.instance.post(endpoint, dataJson);
this.setState({ this.setState({
lookupEdit: undefined, lookupEdit: undefined,
}); });
@ -265,7 +266,7 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
await axios.delete( await Api.instance.delete(
`/druid/coordinator/v1/lookups/config/${deleteLookupTier}/${deleteLookupName}`, `/druid/coordinator/v1/lookups/config/${deleteLookupTier}/${deleteLookupName}`,
); );
}} }}

View File

@ -32,7 +32,7 @@ exports[`sql view matches snapshot 1`] = `
runeMode={false} runeMode={false}
/> />
<div <div
className="control-bar" className="query-control-bar"
> >
<HotkeysTarget(RunButton) <HotkeysTarget(RunButton)
loading={true} loading={true}
@ -109,7 +109,7 @@ exports[`sql view matches snapshot with query 1`] = `
runeMode={false} runeMode={false}
/> />
<div <div
className="control-bar" className="query-control-bar"
> >
<HotkeysTarget(RunButton) <HotkeysTarget(RunButton)
loading={true} loading={true}

View File

@ -94,7 +94,7 @@ function handleColumnClick(options: HandleColumnClickOptions): void {
let newSelectExpressions = query.selectExpressions; let newSelectExpressions = query.selectExpressions;
for (const aggregate of aggregates) { for (const aggregate of aggregates) {
newSelectExpressions = newSelectExpressions.addLast(aggregate); newSelectExpressions = newSelectExpressions.append(aggregate);
} }
onQueryChange( onQueryChange(
@ -339,7 +339,7 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
}} }}
/> />
{parsedQuery && {parsedQuery &&
oneOf(columnData.DATA_TYPE, 'BIGINT', 'FLOAT') && ( oneOf(columnData.DATA_TYPE, 'BIGINT', 'FLOAT', 'DOUBLE') && (
<NumberMenuItems <NumberMenuItems
table={tableName} table={tableName}
schema={schemaName} schema={schemaName}

View File

@ -31,7 +31,7 @@ import copy from 'copy-to-clipboard';
import { QueryResult } from 'druid-query-toolkit'; import { QueryResult } from 'druid-query-toolkit';
import React from 'react'; import React from 'react';
import { AppToaster } from '../../../singletons/toaster'; import { AppToaster } from '../../../singletons';
import { pluralIfNeeded } from '../../../utils'; import { pluralIfNeeded } from '../../../utils';
import './query-extra-info.scss'; import './query-extra-info.scss';

View File

@ -27,6 +27,7 @@ export function dataTypeToIcon(dataType: string): IconName {
return IconNames.FONT; return IconNames.FONT;
case 'BIGINT': case 'BIGINT':
case 'FLOAT': case 'FLOAT':
case 'DOUBLE':
return IconNames.NUMERICAL; return IconNames.NUMERICAL;
default: default:
return IconNames.HELP; return IconNames.HELP;

View File

@ -57,7 +57,7 @@ $nav-width: 250px;
overflow: hidden; overflow: hidden;
} }
.control-bar { .query-control-bar {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
width: 100%; width: 100%;

View File

@ -17,7 +17,6 @@
*/ */
import { Code, Intent, Switch, Tooltip } from '@blueprintjs/core'; import { Code, Intent, Switch, Tooltip } from '@blueprintjs/core';
import axios from 'axios';
import classNames from 'classnames'; import classNames from 'classnames';
import { QueryResult, QueryRunner, SqlQuery } from 'druid-query-toolkit'; import { QueryResult, QueryRunner, SqlQuery } from 'druid-query-toolkit';
import Hjson from 'hjson'; import Hjson from 'hjson';
@ -29,7 +28,7 @@ import { Loader } from '../../components';
import { QueryPlanDialog } from '../../dialogs'; import { QueryPlanDialog } from '../../dialogs';
import { EditContextDialog } from '../../dialogs/edit-context-dialog/edit-context-dialog'; import { EditContextDialog } from '../../dialogs/edit-context-dialog/edit-context-dialog';
import { QueryHistoryDialog } from '../../dialogs/query-history-dialog/query-history-dialog'; import { QueryHistoryDialog } from '../../dialogs/query-history-dialog/query-history-dialog';
import { AppToaster } from '../../singletons/toaster'; import { Api, AppToaster } from '../../singletons';
import { import {
BasicQueryExplanation, BasicQueryExplanation,
ColumnMetadata, ColumnMetadata,
@ -225,7 +224,7 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
}); });
const queryRunner = new QueryRunner((payload, isSql, cancelToken) => { const queryRunner = new QueryRunner((payload, isSql, cancelToken) => {
return axios.post(`/druid/v2${isSql ? '/sql' : ''}`, payload, { cancelToken }); return Api.instance.post(`/druid/v2${isSql ? '/sql' : ''}`, payload, { cancelToken });
}); });
this.queryManager = new QueryManager({ this.queryManager = new QueryManager({
@ -482,7 +481,7 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
runeMode={runeMode} runeMode={runeMode}
columnMetadata={columnMetadataState.data} columnMetadata={columnMetadataState.data}
/> />
<div className="control-bar"> <div className="query-control-bar">
<RunButton <RunButton
onEditContext={() => this.setState({ editContextDialogOpen: true })} onEditContext={() => this.setState({ editContextDialogOpen: true })}
runeMode={runeMode} runeMode={runeMode}

View File

@ -19,7 +19,7 @@
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import React from 'react'; import React from 'react';
import { Capabilities } from '../../utils/capabilities'; import { Capabilities } from '../../utils';
import { SegmentsView } from '../segments-view/segments-view'; import { SegmentsView } from '../segments-view/segments-view';
describe('segments-view', () => { describe('segments-view', () => {

View File

@ -18,7 +18,6 @@
import { Button, ButtonGroup, Intent, Label, MenuItem } from '@blueprintjs/core'; import { Button, ButtonGroup, Intent, Label, MenuItem } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import { SqlExpression, SqlRef } from 'druid-query-toolkit'; import { SqlExpression, SqlRef } from 'druid-query-toolkit';
import React from 'react'; import React from 'react';
import ReactTable, { Filter } from 'react-table'; import ReactTable, { Filter } from 'react-table';
@ -36,6 +35,7 @@ import {
} from '../../components'; } from '../../components';
import { AsyncActionDialog } from '../../dialogs'; import { AsyncActionDialog } from '../../dialogs';
import { SegmentTableActionDialog } from '../../dialogs/segments-table-action-dialog/segment-table-action-dialog'; import { SegmentTableActionDialog } from '../../dialogs/segments-table-action-dialog/segment-table-action-dialog';
import { Api } from '../../singletons';
import { import {
addFilter, addFilter,
compact, compact,
@ -50,8 +50,8 @@ import {
QueryState, QueryState,
sqlQueryCustomTableFilter, sqlQueryCustomTableFilter,
} from '../../utils'; } from '../../utils';
import { Capabilities, CapabilitiesMode } from '../../utils';
import { BasicAction } from '../../utils/basic-action'; import { BasicAction } from '../../utils/basic-action';
import { Capabilities, CapabilitiesMode } from '../../utils/capabilities';
import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array'; import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array';
import './segments-view.scss'; import './segments-view.scss';
@ -295,11 +295,13 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
this.segmentsNoSqlQueryManager = new QueryManager({ this.segmentsNoSqlQueryManager = new QueryManager({
processQuery: async () => { processQuery: async () => {
const datasourceList = (await axios.get('/druid/coordinator/v1/metadata/datasources')).data; const datasourceList = (await Api.instance.get(
'/druid/coordinator/v1/metadata/datasources',
)).data;
const nestedResults: SegmentQueryResultRow[][] = await Promise.all( const nestedResults: SegmentQueryResultRow[][] = await Promise.all(
datasourceList.map(async (d: string) => { datasourceList.map(async (d: string) => {
const segments = (await axios.get(`/druid/coordinator/v1/datasources/${d}?full`)).data const segments = (await Api.instance.get(`/druid/coordinator/v1/datasources/${d}?full`))
.segments; .data.segments;
return segments.map( return segments.map(
(segment: any): SegmentQueryResultRow => { (segment: any): SegmentQueryResultRow => {
@ -634,7 +636,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
const resp = await axios.delete( const resp = await Api.instance.delete(
`/druid/coordinator/v1/datasources/${terminateDatasourceId}/segments/${terminateSegmentId}`, `/druid/coordinator/v1/datasources/${terminateDatasourceId}/segments/${terminateSegmentId}`,
{}, {},
); );

View File

@ -19,7 +19,7 @@
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import React from 'react'; import React from 'react';
import { Capabilities } from '../../utils/capabilities'; import { Capabilities } from '../../utils';
import { ServicesView } from './services-view'; import { ServicesView } from './services-view';

View File

@ -18,7 +18,6 @@
import { Button, ButtonGroup, Intent, Label, MenuItem } from '@blueprintjs/core'; import { Button, ButtonGroup, Intent, Label, MenuItem } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons'; import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import { sum } from 'd3-array'; import { sum } from 'd3-array';
import React from 'react'; import React from 'react';
import ReactTable from 'react-table'; import ReactTable from 'react-table';
@ -35,6 +34,7 @@ import {
ViewControlBar, ViewControlBar,
} from '../../components'; } from '../../components';
import { AsyncActionDialog } from '../../dialogs'; import { AsyncActionDialog } from '../../dialogs';
import { Api } from '../../singletons';
import { import {
addFilter, addFilter,
Capabilities, Capabilities,
@ -182,7 +182,7 @@ FROM sys.servers
ORDER BY "rank" DESC, "service" DESC`; ORDER BY "rank" DESC, "service" DESC`;
static async getServices(): Promise<ServiceQueryResultRow[]> { static async getServices(): Promise<ServiceQueryResultRow[]> {
const allServiceResp = await axios.get('/druid/coordinator/v1/servers?simple'); const allServiceResp = await Api.instance.get('/druid/coordinator/v1/servers?simple');
const allServices = allServiceResp.data; const allServices = allServiceResp.data;
return allServices.map((s: any) => { return allServices.map((s: any) => {
return { return {
@ -221,7 +221,9 @@ ORDER BY "rank" DESC, "service" DESC`;
} }
if (capabilities.hasCoordinatorAccess()) { if (capabilities.hasCoordinatorAccess()) {
const loadQueueResponse = await axios.get('/druid/coordinator/v1/loadqueue?simple'); const loadQueueResponse = await Api.instance.get(
'/druid/coordinator/v1/loadqueue?simple',
);
const loadQueues: Record<string, LoadQueueStatus> = loadQueueResponse.data; const loadQueues: Record<string, LoadQueueStatus> = loadQueueResponse.data;
services = services.map(s => { services = services.map(s => {
const loadQueueInfo = loadQueues[s.service]; const loadQueueInfo = loadQueues[s.service];
@ -235,7 +237,7 @@ ORDER BY "rank" DESC, "service" DESC`;
if (capabilities.hasOverlordAccess()) { if (capabilities.hasOverlordAccess()) {
let middleManagers: MiddleManagerQueryResultRow[]; let middleManagers: MiddleManagerQueryResultRow[];
try { try {
const middleManagerResponse = await axios.get('/druid/indexer/v1/workers'); const middleManagerResponse = await Api.instance.get('/druid/indexer/v1/workers');
middleManagers = middleManagerResponse.data; middleManagers = middleManagerResponse.data;
} catch (e) { } catch (e) {
if ( if (
@ -589,7 +591,7 @@ ORDER BY "rank" DESC, "service" DESC`;
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
const resp = await axios.post( const resp = await Api.instance.post(
`/druid/indexer/v1/worker/${middleManagerDisableWorkerHost}/disable`, `/druid/indexer/v1/worker/${middleManagerDisableWorkerHost}/disable`,
{}, {},
); );
@ -618,7 +620,7 @@ ORDER BY "rank" DESC, "service" DESC`;
return ( return (
<AsyncActionDialog <AsyncActionDialog
action={async () => { action={async () => {
const resp = await axios.post( const resp = await Api.instance.post(
`/druid/indexer/v1/worker/${middleManagerEnableWorkerHost}/enable`, `/druid/indexer/v1/worker/${middleManagerEnableWorkerHost}/enable`,
{}, {},
); );