mirror of https://github.com/apache/druid.git
Web-console: Add action column to segments view (#7954)
* add actions column to segments view * add sements action column
This commit is contained in:
parent
9d925f5086
commit
a171b4a399
|
@ -0,0 +1,171 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`task table action dialog matches snapshot 1`] = `
|
||||||
|
<div
|
||||||
|
class="bp3-portal"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="bp3-overlay bp3-overlay-open bp3-overlay-scroll-container"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="bp3-overlay-backdrop bp3-overlay-appear bp3-overlay-appear-active"
|
||||||
|
tabindex="0"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="bp3-dialog-container bp3-overlay-content bp3-overlay-appear bp3-overlay-appear-active"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="bp3-dialog table-action-dialog"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="bp3-dialog-header"
|
||||||
|
>
|
||||||
|
<h4
|
||||||
|
class="bp3-heading"
|
||||||
|
>
|
||||||
|
Segment: test
|
||||||
|
</h4>
|
||||||
|
<button
|
||||||
|
aria-label="Close"
|
||||||
|
class="bp3-button bp3-minimal bp3-dialog-close-button"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="bp3-icon bp3-icon-small-cross"
|
||||||
|
icon="small-cross"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
data-icon="small-cross"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
width="20"
|
||||||
|
>
|
||||||
|
<desc>
|
||||||
|
small-cross
|
||||||
|
</desc>
|
||||||
|
<path
|
||||||
|
d="M11.41 10l3.29-3.29c.19-.18.3-.43.3-.71a1.003 1.003 0 0 0-1.71-.71L10 8.59l-3.29-3.3a1.003 1.003 0 0 0-1.42 1.42L8.59 10 5.3 13.29c-.19.18-.3.43-.3.71a1.003 1.003 0 0 0 1.71.71l3.29-3.3 3.29 3.29c.18.19.43.3.71.3a1.003 1.003 0 0 0 .71-1.71L11.41 10z"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="bp3-dialog-body"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="side-bar"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="bp3-button bp3-intent-primary tab-button"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="bp3-icon bp3-icon-manually-entered-data"
|
||||||
|
icon="manually-entered-data"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
data-icon="manually-entered-data"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
width="20"
|
||||||
|
>
|
||||||
|
<desc>
|
||||||
|
manually-entered-data
|
||||||
|
</desc>
|
||||||
|
<path
|
||||||
|
d="M1 12h4.34l2-2H1c-.55 0-1 .45-1 1s.45 1 1 1zm16.77-3.94l1.65-1.65c.36-.36.58-.86.58-1.41 0-1.1-.9-2-2-2-.55 0-1.05.22-1.41.59l-1.65 1.65 2.83 2.82zM1 4h12.34l2-2H1c-.55 0-1 .45-1 1s.45 1 1 1zM0 15c0 .55.45 1 1 1h.34l2-2H1c-.55 0-1 .45-1 1zm1-7h8.34l2-2H1c-.55 0-1 .45-1 1s.45 1 1 1zm18 2h-.34l-2 2H19c.55 0 1-.45 1-1s-.45-1-1-1zm0 4h-4.34l-2 2H19c.55 0 1-.45 1-1s-.45-1-1-1zM4 19l4.41-1.59-2.81-2.79L4 19zM14.23 5.94l-7.65 7.65 2.83 2.83 7.65-7.65-2.83-2.83z"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="bp3-button-text"
|
||||||
|
>
|
||||||
|
Metadata
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="main-section"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="show-json"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="top-actions"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="bp3-button-group right-buttons"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="bp3-button bp3-minimal"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="bp3-button-text"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="bp3-button bp3-minimal"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="bp3-button-text"
|
||||||
|
>
|
||||||
|
Copy
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="bp3-button bp3-minimal"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="bp3-button-text"
|
||||||
|
>
|
||||||
|
View raw
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="main-area"
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
class="bp3-input"
|
||||||
|
readonly=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="bp3-dialog-footer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="footer-actions-left"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="bp3-dialog-footer-actions"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="bp3-button bp3-intent-primary"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="bp3-button-text"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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 React from 'react';
|
||||||
|
import { render } from 'react-testing-library';
|
||||||
|
|
||||||
|
import { SegmentTableActionDialog } from './segment-table-action-dialog';
|
||||||
|
|
||||||
|
const basicAction = { title: 'test', onAction: () => null };
|
||||||
|
describe('task table action dialog', () => {
|
||||||
|
it('matches snapshot', () => {
|
||||||
|
const taskTableActionDialog = (
|
||||||
|
<SegmentTableActionDialog
|
||||||
|
dataSourceId="test"
|
||||||
|
segmentId="test"
|
||||||
|
actions={[]}
|
||||||
|
onClose={() => null}
|
||||||
|
isOpen
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
const { container } = render(taskTableActionDialog, { container: document.body });
|
||||||
|
expect(container.firstChild).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* 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 { IDialogProps, TextArea } from '@blueprintjs/core';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { ShowJson } from '../../components';
|
||||||
|
import { BasicAction, basicActionsToButtons } from '../../utils/basic-action';
|
||||||
|
import { SideButtonMetaData, TableActionDialog } from '../table-action-dialog/table-action-dialog';
|
||||||
|
|
||||||
|
interface SegmentTableActionDialogProps extends IDialogProps {
|
||||||
|
segmentId: string | null;
|
||||||
|
dataSourceId: string | null;
|
||||||
|
actions: BasicAction[];
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SegmentTableActionDialogState {
|
||||||
|
activeTab: 'metadata';
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SegmentTableActionDialog extends React.PureComponent<
|
||||||
|
SegmentTableActionDialogProps,
|
||||||
|
SegmentTableActionDialogState
|
||||||
|
> {
|
||||||
|
constructor(props: SegmentTableActionDialogProps) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
activeTab: 'metadata',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): React.ReactNode {
|
||||||
|
const { segmentId, onClose, dataSourceId, actions } = this.props;
|
||||||
|
const { activeTab } = this.state;
|
||||||
|
|
||||||
|
const taskTableSideButtonMetadata: SideButtonMetaData[] = [
|
||||||
|
{
|
||||||
|
icon: 'manually-entered-data',
|
||||||
|
text: 'Metadata',
|
||||||
|
active: activeTab === 'metadata',
|
||||||
|
onClick: () => this.setState({ activeTab: 'metadata' }),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableActionDialog
|
||||||
|
isOpen
|
||||||
|
sideButtonMetadata={taskTableSideButtonMetadata}
|
||||||
|
onClose={onClose}
|
||||||
|
title={`Segment: ${segmentId}`}
|
||||||
|
bottomButtons={basicActionsToButtons(actions)}
|
||||||
|
>
|
||||||
|
{activeTab === 'metadata' && (
|
||||||
|
<ShowJson
|
||||||
|
endpoint={`/druid/coordinator/v1/metadata/datasources/${dataSourceId}/segments/${segmentId}`}
|
||||||
|
downloadFilename={`Segment-metadata-${segmentId}.json`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</TableActionDialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,5 +21,4 @@ export * from './druid-query';
|
||||||
export * from './query-manager';
|
export * from './query-manager';
|
||||||
export * from './query-state';
|
export * from './query-state';
|
||||||
export * from './rune-decoder';
|
export * from './rune-decoder';
|
||||||
export * from './table-column-selection-handler';
|
|
||||||
export * from './local-storage-keys';
|
export * from './local-storage-keys';
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* 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 { localStorageGet, LocalStorageKeys, localStorageSet } from '../utils';
|
||||||
|
|
||||||
|
export class LocalStorageBackedArray<T> {
|
||||||
|
key: LocalStorageKeys;
|
||||||
|
storedArray: T[];
|
||||||
|
|
||||||
|
constructor(key: LocalStorageKeys, array?: T[]) {
|
||||||
|
this.key = key;
|
||||||
|
if (!Array.isArray(array)) {
|
||||||
|
this.getDataFromStorage();
|
||||||
|
} else {
|
||||||
|
this.storedArray = array;
|
||||||
|
this.setDataInStorage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getDataFromStorage(): void {
|
||||||
|
let possibleArray: any;
|
||||||
|
try {
|
||||||
|
possibleArray = JSON.parse(String(localStorageGet(this.key)));
|
||||||
|
} catch {
|
||||||
|
// show all columns by default
|
||||||
|
possibleArray = [];
|
||||||
|
}
|
||||||
|
if (!Array.isArray(possibleArray)) possibleArray = [];
|
||||||
|
|
||||||
|
this.storedArray = possibleArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
private setDataInStorage(): void {
|
||||||
|
localStorageSet(this.key, JSON.stringify(this.storedArray));
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle(value: T): LocalStorageBackedArray<T> {
|
||||||
|
let toggledArray;
|
||||||
|
if (this.storedArray.includes(value)) {
|
||||||
|
toggledArray = this.storedArray.filter(c => c !== value);
|
||||||
|
} else {
|
||||||
|
toggledArray = this.storedArray.concat(value);
|
||||||
|
}
|
||||||
|
return new LocalStorageBackedArray<T>(this.key, toggledArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
exists(value: T): boolean {
|
||||||
|
return !this.storedArray.includes(value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 { localStorageGet, LocalStorageKeys, localStorageSet } from '../utils';
|
|
||||||
|
|
||||||
export class TableColumnSelectionHandler {
|
|
||||||
tableName: LocalStorageKeys;
|
|
||||||
hiddenColumns: string[];
|
|
||||||
updateComponent: () => void;
|
|
||||||
|
|
||||||
constructor(tableName: LocalStorageKeys, updateComponent: () => void) {
|
|
||||||
this.tableName = tableName;
|
|
||||||
this.updateComponent = updateComponent;
|
|
||||||
this.getHiddenTableColumns();
|
|
||||||
}
|
|
||||||
|
|
||||||
getHiddenTableColumns(): void {
|
|
||||||
const stringValue: string | null = localStorageGet(this.tableName);
|
|
||||||
try {
|
|
||||||
const selections = JSON.parse(String(stringValue));
|
|
||||||
if (!Array.isArray(selections)) {
|
|
||||||
this.hiddenColumns = [];
|
|
||||||
} else {
|
|
||||||
this.hiddenColumns = selections;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.hiddenColumns = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
changeTableColumnSelector(column: string): void {
|
|
||||||
let newSelections: string[];
|
|
||||||
if (this.hiddenColumns.includes(column)) {
|
|
||||||
newSelections = this.hiddenColumns.filter(c => c !== column);
|
|
||||||
} else {
|
|
||||||
newSelections = this.hiddenColumns.concat(column);
|
|
||||||
}
|
|
||||||
this.hiddenColumns = newSelections;
|
|
||||||
this.updateComponent();
|
|
||||||
localStorageSet(this.tableName, JSON.stringify(newSelections));
|
|
||||||
}
|
|
||||||
|
|
||||||
showColumn(column: string): boolean {
|
|
||||||
return !this.hiddenColumns.includes(column);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -52,9 +52,9 @@ import {
|
||||||
pluralIfNeeded,
|
pluralIfNeeded,
|
||||||
queryDruidSql,
|
queryDruidSql,
|
||||||
QueryManager,
|
QueryManager,
|
||||||
TableColumnSelectionHandler,
|
|
||||||
} from '../../utils';
|
} from '../../utils';
|
||||||
import { BasicAction } from '../../utils/basic-action';
|
import { BasicAction } from '../../utils/basic-action';
|
||||||
|
import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array';
|
||||||
|
|
||||||
import './datasource-view.scss';
|
import './datasource-view.scss';
|
||||||
|
|
||||||
|
@ -113,6 +113,7 @@ export interface DatasourcesViewState {
|
||||||
dropReloadDatasource: string | null;
|
dropReloadDatasource: string | null;
|
||||||
dropReloadAction: 'drop' | 'reload';
|
dropReloadAction: 'drop' | 'reload';
|
||||||
dropReloadInterval: string;
|
dropReloadInterval: string;
|
||||||
|
hiddenColumns: LocalStorageBackedArray<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DatasourcesView extends React.PureComponent<
|
export class DatasourcesView extends React.PureComponent<
|
||||||
|
@ -137,7 +138,6 @@ export class DatasourcesView extends React.PureComponent<
|
||||||
string,
|
string,
|
||||||
{ tiers: string[]; defaultRules: any[]; datasources: Datasource[] }
|
{ tiers: string[]; defaultRules: any[]; datasources: Datasource[] }
|
||||||
>;
|
>;
|
||||||
private tableColumnSelectionHandler: TableColumnSelectionHandler;
|
|
||||||
|
|
||||||
constructor(props: DatasourcesViewProps, context: any) {
|
constructor(props: DatasourcesViewProps, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
@ -158,16 +158,15 @@ export class DatasourcesView extends React.PureComponent<
|
||||||
dropReloadDatasource: null,
|
dropReloadDatasource: null,
|
||||||
dropReloadAction: 'drop',
|
dropReloadAction: 'drop',
|
||||||
dropReloadInterval: '',
|
dropReloadInterval: '',
|
||||||
};
|
hiddenColumns: new LocalStorageBackedArray<string>(
|
||||||
|
|
||||||
this.tableColumnSelectionHandler = new TableColumnSelectionHandler(
|
|
||||||
LocalStorageKeys.DATASOURCE_TABLE_COLUMN_SELECTION,
|
LocalStorageKeys.DATASOURCE_TABLE_COLUMN_SELECTION,
|
||||||
() => this.setState({}),
|
),
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
const { noSqlMode } = this.props;
|
const { noSqlMode } = this.props;
|
||||||
|
const { hiddenColumns } = this.state;
|
||||||
|
|
||||||
this.datasourceQueryManager = new QueryManager({
|
this.datasourceQueryManager = new QueryManager({
|
||||||
processQuery: async (query: string) => {
|
processQuery: async (query: string) => {
|
||||||
|
@ -564,8 +563,8 @@ GROUP BY 1`);
|
||||||
datasourcesError,
|
datasourcesError,
|
||||||
datasourcesFilter,
|
datasourcesFilter,
|
||||||
showDisabled,
|
showDisabled,
|
||||||
|
hiddenColumns,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const { tableColumnSelectionHandler } = this;
|
|
||||||
let data = datasources || [];
|
let data = datasources || [];
|
||||||
if (!showDisabled) {
|
if (!showDisabled) {
|
||||||
data = data.filter(d => !d.disabled);
|
data = data.filter(d => !d.disabled);
|
||||||
|
@ -604,7 +603,7 @@ GROUP BY 1`);
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
show: tableColumnSelectionHandler.showColumn('Datasource'),
|
show: hiddenColumns.exists('Datasource'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Availability',
|
Header: 'Availability',
|
||||||
|
@ -668,7 +667,7 @@ GROUP BY 1`);
|
||||||
const percentAvailable2 = d2.num_available / d2.num_total;
|
const percentAvailable2 = d2.num_available / d2.num_total;
|
||||||
return percentAvailable1 - percentAvailable2 || d1.num_total - d2.num_total;
|
return percentAvailable1 - percentAvailable2 || d1.num_total - d2.num_total;
|
||||||
},
|
},
|
||||||
show: tableColumnSelectionHandler.showColumn('Availability'),
|
show: hiddenColumns.exists('Availability'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Retention',
|
Header: 'Retention',
|
||||||
|
@ -701,7 +700,7 @@ GROUP BY 1`);
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
show: tableColumnSelectionHandler.showColumn('Retention'),
|
show: hiddenColumns.exists('Retention'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Compaction',
|
Header: 'Compaction',
|
||||||
|
@ -730,7 +729,7 @@ GROUP BY 1`);
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
show: tableColumnSelectionHandler.showColumn('Compaction'),
|
show: hiddenColumns.exists('Compaction'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Size',
|
Header: 'Size',
|
||||||
|
@ -738,7 +737,7 @@ GROUP BY 1`);
|
||||||
filterable: false,
|
filterable: false,
|
||||||
width: 100,
|
width: 100,
|
||||||
Cell: row => formatBytes(row.value),
|
Cell: row => formatBytes(row.value),
|
||||||
show: tableColumnSelectionHandler.showColumn('Size'),
|
show: hiddenColumns.exists('Size'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Num rows',
|
Header: 'Num rows',
|
||||||
|
@ -746,7 +745,7 @@ GROUP BY 1`);
|
||||||
filterable: false,
|
filterable: false,
|
||||||
width: 100,
|
width: 100,
|
||||||
Cell: row => formatNumber(row.value),
|
Cell: row => formatNumber(row.value),
|
||||||
show: !noSqlMode && tableColumnSelectionHandler.showColumn('Num rows'),
|
show: !noSqlMode && hiddenColumns.exists('Num rows'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: ActionCell.COLUMN_LABEL,
|
Header: ActionCell.COLUMN_LABEL,
|
||||||
|
@ -760,7 +759,7 @@ GROUP BY 1`);
|
||||||
const datasourceActions = this.getDatasourceActions(datasource, disabled);
|
const datasourceActions = this.getDatasourceActions(datasource, disabled);
|
||||||
return <ActionCell actions={datasourceActions} />;
|
return <ActionCell actions={datasourceActions} />;
|
||||||
},
|
},
|
||||||
show: tableColumnSelectionHandler.showColumn(ActionCell.COLUMN_LABEL),
|
show: hiddenColumns.exists(ActionCell.COLUMN_LABEL),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
defaultPageSize={50}
|
defaultPageSize={50}
|
||||||
|
@ -777,8 +776,7 @@ GROUP BY 1`);
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { goToQuery, noSqlMode } = this.props;
|
const { goToQuery, noSqlMode } = this.props;
|
||||||
const { showDisabled } = this.state;
|
const { showDisabled, hiddenColumns } = this.state;
|
||||||
const { tableColumnSelectionHandler } = this;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="data-sources-view app-view">
|
<div className="data-sources-view app-view">
|
||||||
|
@ -801,8 +799,8 @@ GROUP BY 1`);
|
||||||
/>
|
/>
|
||||||
<TableColumnSelector
|
<TableColumnSelector
|
||||||
columns={noSqlMode ? tableColumnsNoSql : tableColumns}
|
columns={noSqlMode ? tableColumnsNoSql : tableColumns}
|
||||||
onChange={column => tableColumnSelectionHandler.changeTableColumnSelector(column)}
|
onChange={column => this.setState({ hiddenColumns: hiddenColumns.toggle(column) })}
|
||||||
tableColumnsHidden={tableColumnSelectionHandler.hiddenColumns}
|
tableColumnsHidden={hiddenColumns.storedArray}
|
||||||
/>
|
/>
|
||||||
</ViewControlBar>
|
</ViewControlBar>
|
||||||
{this.renderDatasourceTable()}
|
{this.renderDatasourceTable()}
|
||||||
|
|
|
@ -26,13 +26,9 @@ import ReactTable from 'react-table';
|
||||||
import { ActionCell, TableColumnSelector, ViewControlBar } from '../../components';
|
import { ActionCell, TableColumnSelector, ViewControlBar } from '../../components';
|
||||||
import { AsyncActionDialog, LookupEditDialog } from '../../dialogs/';
|
import { AsyncActionDialog, LookupEditDialog } from '../../dialogs/';
|
||||||
import { AppToaster } from '../../singletons/toaster';
|
import { AppToaster } from '../../singletons/toaster';
|
||||||
import {
|
import { getDruidErrorMessage, LocalStorageKeys, QueryManager } from '../../utils';
|
||||||
getDruidErrorMessage,
|
|
||||||
LocalStorageKeys,
|
|
||||||
QueryManager,
|
|
||||||
TableColumnSelectionHandler,
|
|
||||||
} from '../../utils';
|
|
||||||
import { BasicAction, basicActionsToMenu } from '../../utils/basic-action';
|
import { BasicAction, basicActionsToMenu } from '../../utils/basic-action';
|
||||||
|
import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array';
|
||||||
|
|
||||||
import './lookups-view.scss';
|
import './lookups-view.scss';
|
||||||
|
|
||||||
|
@ -57,11 +53,12 @@ export interface LookupsViewState {
|
||||||
|
|
||||||
deleteLookupName: string | null;
|
deleteLookupName: string | null;
|
||||||
deleteLookupTier: string | null;
|
deleteLookupTier: string | null;
|
||||||
|
|
||||||
|
hiddenColumns: LocalStorageBackedArray<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsViewState> {
|
export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsViewState> {
|
||||||
private lookupsGetQueryManager: QueryManager<string, { lookupEntries: any[]; tiers: string[] }>;
|
private lookupsGetQueryManager: QueryManager<string, { lookupEntries: any[]; tiers: string[] }>;
|
||||||
private tableColumnSelectionHandler: TableColumnSelectionHandler;
|
|
||||||
|
|
||||||
constructor(props: LookupsViewProps, context: any) {
|
constructor(props: LookupsViewProps, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
@ -80,11 +77,11 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
|
||||||
|
|
||||||
deleteLookupTier: null,
|
deleteLookupTier: null,
|
||||||
deleteLookupName: null,
|
deleteLookupName: null,
|
||||||
};
|
|
||||||
this.tableColumnSelectionHandler = new TableColumnSelectionHandler(
|
hiddenColumns: new LocalStorageBackedArray<string>(
|
||||||
LocalStorageKeys.LOOKUP_TABLE_COLUMN_SELECTION,
|
LocalStorageKeys.LOOKUP_TABLE_COLUMN_SELECTION,
|
||||||
() => this.setState({}),
|
),
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
|
@ -266,8 +263,13 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLookupsTable() {
|
renderLookupsTable() {
|
||||||
const { lookups, loadingLookups, lookupsError, lookupsUninitialized } = this.state;
|
const {
|
||||||
const { tableColumnSelectionHandler } = this;
|
lookups,
|
||||||
|
loadingLookups,
|
||||||
|
lookupsError,
|
||||||
|
lookupsUninitialized,
|
||||||
|
hiddenColumns,
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
if (lookupsUninitialized) {
|
if (lookupsUninitialized) {
|
||||||
return (
|
return (
|
||||||
|
@ -296,28 +298,28 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
|
||||||
id: 'lookup_name',
|
id: 'lookup_name',
|
||||||
accessor: (row: any) => row.id,
|
accessor: (row: any) => row.id,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
show: tableColumnSelectionHandler.showColumn('Lookup name'),
|
show: hiddenColumns.exists('Lookup name'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Tier',
|
Header: 'Tier',
|
||||||
id: 'tier',
|
id: 'tier',
|
||||||
accessor: (row: any) => row.tier,
|
accessor: (row: any) => row.tier,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
show: tableColumnSelectionHandler.showColumn('Tier'),
|
show: hiddenColumns.exists('Tier'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Type',
|
Header: 'Type',
|
||||||
id: 'type',
|
id: 'type',
|
||||||
accessor: (row: any) => row.spec.type,
|
accessor: (row: any) => row.spec.type,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
show: tableColumnSelectionHandler.showColumn('Type'),
|
show: hiddenColumns.exists('Type'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Version',
|
Header: 'Version',
|
||||||
id: 'version',
|
id: 'version',
|
||||||
accessor: (row: any) => row.version,
|
accessor: (row: any) => row.version,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
show: tableColumnSelectionHandler.showColumn('Version'),
|
show: hiddenColumns.exists('Version'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: ActionCell.COLUMN_LABEL,
|
Header: ActionCell.COLUMN_LABEL,
|
||||||
|
@ -331,7 +333,7 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
|
||||||
const lookupActions = this.getLookupActions(lookupTier, lookupId);
|
const lookupActions = this.getLookupActions(lookupTier, lookupId);
|
||||||
return <ActionCell actions={lookupActions} />;
|
return <ActionCell actions={lookupActions} />;
|
||||||
},
|
},
|
||||||
show: tableColumnSelectionHandler.showColumn(ActionCell.COLUMN_LABEL),
|
show: hiddenColumns.exists(ActionCell.COLUMN_LABEL),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
defaultPageSize={50}
|
defaultPageSize={50}
|
||||||
|
@ -368,8 +370,7 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { lookupsError } = this.state;
|
const { lookupsError, hiddenColumns } = this.state;
|
||||||
const { tableColumnSelectionHandler } = this;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="lookups-view app-view">
|
<div className="lookups-view app-view">
|
||||||
|
@ -388,8 +389,8 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
|
||||||
)}
|
)}
|
||||||
<TableColumnSelector
|
<TableColumnSelector
|
||||||
columns={tableColumns}
|
columns={tableColumns}
|
||||||
onChange={column => tableColumnSelectionHandler.changeTableColumnSelector(column)}
|
onChange={column => this.setState({ hiddenColumns: hiddenColumns.toggle(column) })}
|
||||||
tableColumnsHidden={tableColumnSelectionHandler.hiddenColumns}
|
tableColumnsHidden={hiddenColumns.storedArray}
|
||||||
/>
|
/>
|
||||||
</ViewControlBar>
|
</ViewControlBar>
|
||||||
{this.renderLookupsTable()}
|
{this.renderLookupsTable()}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`segments-view matches snapshot 1`] = `
|
exports[`segments-view matches snapshot 1`] = `
|
||||||
|
<Fragment>
|
||||||
<div
|
<div
|
||||||
className="segments-view app-view"
|
className="segments-view app-view"
|
||||||
>
|
>
|
||||||
|
@ -33,6 +34,7 @@ exports[`segments-view matches snapshot 1`] = `
|
||||||
"Is realtime",
|
"Is realtime",
|
||||||
"Is available",
|
"Is available",
|
||||||
"Is overshadowed",
|
"Is overshadowed",
|
||||||
|
"Actions",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
|
@ -49,7 +51,6 @@ exports[`segments-view matches snapshot 1`] = `
|
||||||
PaginationComponent={[Function]}
|
PaginationComponent={[Function]}
|
||||||
PivotValueComponent={[Function]}
|
PivotValueComponent={[Function]}
|
||||||
ResizerComponent={[Function]}
|
ResizerComponent={[Function]}
|
||||||
SubComponent={[Function]}
|
|
||||||
TableComponent={[Function]}
|
TableComponent={[Function]}
|
||||||
TbodyComponent={[Function]}
|
TbodyComponent={[Function]}
|
||||||
TdComponent={[Function]}
|
TdComponent={[Function]}
|
||||||
|
@ -191,6 +192,16 @@ exports[`segments-view matches snapshot 1`] = `
|
||||||
"id": "is_overshadowed",
|
"id": "is_overshadowed",
|
||||||
"show": true,
|
"show": true,
|
||||||
},
|
},
|
||||||
|
Object {
|
||||||
|
"Aggregated": [Function],
|
||||||
|
"Cell": [Function],
|
||||||
|
"Header": "Actions",
|
||||||
|
"accessor": "segment_id",
|
||||||
|
"filterable": false,
|
||||||
|
"id": "actions",
|
||||||
|
"show": true,
|
||||||
|
"width": 70,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
data={Array []}
|
data={Array []}
|
||||||
|
@ -294,4 +305,20 @@ exports[`segments-view matches snapshot 1`] = `
|
||||||
subRowsKey="_subRows"
|
subRowsKey="_subRows"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<AsyncActionDialog
|
||||||
|
action={null}
|
||||||
|
confirmButtonText="Drop Segment"
|
||||||
|
failText="Could not drop segment"
|
||||||
|
intent="danger"
|
||||||
|
onClose={[Function]}
|
||||||
|
successText="Segment drop request acknowledged, next time the coordinator runs segment will be dropped"
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
Are you sure you want to drop segment 'null'?
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This action is not reversible.
|
||||||
|
</p>
|
||||||
|
</AsyncActionDialog>
|
||||||
|
</Fragment>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -17,14 +17,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Button, Intent } from '@blueprintjs/core';
|
import { Button, Intent } from '@blueprintjs/core';
|
||||||
import { H5 } from '@blueprintjs/core';
|
|
||||||
import { IconNames } from '@blueprintjs/icons';
|
import { IconNames } from '@blueprintjs/icons';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactTable from 'react-table';
|
import ReactTable from 'react-table';
|
||||||
import { Filter } from 'react-table';
|
import { Filter } from 'react-table';
|
||||||
|
|
||||||
import { RefreshButton, TableColumnSelector, ViewControlBar } from '../../components';
|
import { ActionCell, RefreshButton, TableColumnSelector, ViewControlBar } from '../../components';
|
||||||
|
import { AsyncActionDialog } from '../../dialogs';
|
||||||
|
import { SegmentTableActionDialog } from '../../dialogs/segments-table-action-dialog/segment-table-action-dialog';
|
||||||
import {
|
import {
|
||||||
addFilter,
|
addFilter,
|
||||||
formatBytes,
|
formatBytes,
|
||||||
|
@ -35,8 +36,9 @@ import {
|
||||||
queryDruidSql,
|
queryDruidSql,
|
||||||
QueryManager,
|
QueryManager,
|
||||||
sqlQueryCustomTableFilter,
|
sqlQueryCustomTableFilter,
|
||||||
TableColumnSelectionHandler,
|
|
||||||
} from '../../utils';
|
} from '../../utils';
|
||||||
|
import { BasicAction } from '../../utils/basic-action';
|
||||||
|
import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array';
|
||||||
|
|
||||||
import './segments-view.scss';
|
import './segments-view.scss';
|
||||||
|
|
||||||
|
@ -54,6 +56,7 @@ const tableColumns: string[] = [
|
||||||
'Is realtime',
|
'Is realtime',
|
||||||
'Is available',
|
'Is available',
|
||||||
'Is overshadowed',
|
'Is overshadowed',
|
||||||
|
ActionCell.COLUMN_LABEL,
|
||||||
];
|
];
|
||||||
const tableColumnsNoSql: string[] = [
|
const tableColumnsNoSql: string[] = [
|
||||||
'Segment ID',
|
'Segment ID',
|
||||||
|
@ -78,6 +81,12 @@ export interface SegmentsViewState {
|
||||||
segmentsError: string | null;
|
segmentsError: string | null;
|
||||||
segmentFilter: Filter[];
|
segmentFilter: Filter[];
|
||||||
allSegments?: SegmentQueryResultRow[] | null;
|
allSegments?: SegmentQueryResultRow[] | null;
|
||||||
|
segmentTableActionDialogId: string | null;
|
||||||
|
datasourceTableActionDialogId: string | null;
|
||||||
|
actions: BasicAction[];
|
||||||
|
terminateSegmentId: string | null;
|
||||||
|
terminateDatasourceId: string | null;
|
||||||
|
hiddenColumns: LocalStorageBackedArray<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface QueryAndSkip {
|
interface QueryAndSkip {
|
||||||
|
@ -105,7 +114,6 @@ interface SegmentQueryResultRow {
|
||||||
export class SegmentsView extends React.PureComponent<SegmentsViewProps, SegmentsViewState> {
|
export class SegmentsView extends React.PureComponent<SegmentsViewProps, SegmentsViewState> {
|
||||||
private segmentsSqlQueryManager: QueryManager<QueryAndSkip, SegmentQueryResultRow[]>;
|
private segmentsSqlQueryManager: QueryManager<QueryAndSkip, SegmentQueryResultRow[]>;
|
||||||
private segmentsJsonQueryManager: QueryManager<any, SegmentQueryResultRow[]>;
|
private segmentsJsonQueryManager: QueryManager<any, SegmentQueryResultRow[]>;
|
||||||
private tableColumnSelectionHandler: TableColumnSelectionHandler;
|
|
||||||
|
|
||||||
constructor(props: SegmentsViewProps, context: any) {
|
constructor(props: SegmentsViewProps, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
@ -115,10 +123,18 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
||||||
if (props.onlyUnavailable) segmentFilter.push({ id: 'is_available', value: 'false' });
|
if (props.onlyUnavailable) segmentFilter.push({ id: 'is_available', value: 'false' });
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
segmentTableActionDialogId: null,
|
||||||
|
datasourceTableActionDialogId: null,
|
||||||
|
actions: [],
|
||||||
|
terminateSegmentId: null,
|
||||||
|
terminateDatasourceId: null,
|
||||||
segmentsLoading: true,
|
segmentsLoading: true,
|
||||||
segments: null,
|
segments: null,
|
||||||
segmentsError: null,
|
segmentsError: null,
|
||||||
segmentFilter,
|
segmentFilter,
|
||||||
|
hiddenColumns: new LocalStorageBackedArray<string>(
|
||||||
|
LocalStorageKeys.SEGMENT_TABLE_COLUMN_SELECTION,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.segmentsSqlQueryManager = new QueryManager({
|
this.segmentsSqlQueryManager = new QueryManager({
|
||||||
|
@ -185,11 +201,6 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
this.tableColumnSelectionHandler = new TableColumnSelectionHandler(
|
|
||||||
LocalStorageKeys.SEGMENT_TABLE_COLUMN_SELECTION,
|
|
||||||
() => this.setState({}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
|
@ -274,10 +285,20 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private getSegmentActions(id: string, datasource: string): BasicAction[] {
|
||||||
|
const actions: BasicAction[] = [];
|
||||||
|
actions.push({
|
||||||
|
icon: IconNames.IMPORT,
|
||||||
|
title: 'Drop segment (disable)',
|
||||||
|
intent: Intent.DANGER,
|
||||||
|
onAction: () => this.setState({ terminateSegmentId: id, terminateDatasourceId: datasource }),
|
||||||
|
});
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
renderSegmentsTable() {
|
renderSegmentsTable() {
|
||||||
const { segments, segmentsLoading, segmentsError, segmentFilter } = this.state;
|
const { segments, segmentsLoading, segmentsError, segmentFilter, hiddenColumns } = this.state;
|
||||||
const { noSqlMode } = this.props;
|
const { noSqlMode } = this.props;
|
||||||
const { tableColumnSelectionHandler } = this;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactTable
|
<ReactTable
|
||||||
|
@ -302,7 +323,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
||||||
Header: 'Segment ID',
|
Header: 'Segment ID',
|
||||||
accessor: 'segment_id',
|
accessor: 'segment_id',
|
||||||
width: 300,
|
width: 300,
|
||||||
show: tableColumnSelectionHandler.showColumn('Segment ID'),
|
show: hiddenColumns.exists('Segment ID'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Datasource',
|
Header: 'Datasource',
|
||||||
|
@ -319,7 +340,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
show: tableColumnSelectionHandler.showColumn('Datasource'),
|
show: hiddenColumns.exists('Datasource'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Start',
|
Header: 'Start',
|
||||||
|
@ -338,7 +359,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
show: tableColumnSelectionHandler.showColumn('Start'),
|
show: hiddenColumns.exists('Start'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'End',
|
Header: 'End',
|
||||||
|
@ -357,21 +378,21 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
show: tableColumnSelectionHandler.showColumn('End'),
|
show: hiddenColumns.exists('End'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Version',
|
Header: 'Version',
|
||||||
accessor: 'version',
|
accessor: 'version',
|
||||||
defaultSortDesc: true,
|
defaultSortDesc: true,
|
||||||
width: 120,
|
width: 120,
|
||||||
show: tableColumnSelectionHandler.showColumn('Version'),
|
show: hiddenColumns.exists('Version'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Partition',
|
Header: 'Partition',
|
||||||
accessor: 'partition_num',
|
accessor: 'partition_num',
|
||||||
width: 60,
|
width: 60,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
show: tableColumnSelectionHandler.showColumn('Partition'),
|
show: hiddenColumns.exists('Partition'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Size',
|
Header: 'Size',
|
||||||
|
@ -379,7 +400,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
||||||
filterable: false,
|
filterable: false,
|
||||||
defaultSortDesc: true,
|
defaultSortDesc: true,
|
||||||
Cell: row => formatBytes(row.value),
|
Cell: row => formatBytes(row.value),
|
||||||
show: tableColumnSelectionHandler.showColumn('Size'),
|
show: hiddenColumns.exists('Size'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Num rows',
|
Header: 'Num rows',
|
||||||
|
@ -387,7 +408,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
||||||
filterable: false,
|
filterable: false,
|
||||||
defaultSortDesc: true,
|
defaultSortDesc: true,
|
||||||
Cell: row => formatNumber(row.value),
|
Cell: row => formatNumber(row.value),
|
||||||
show: !noSqlMode && tableColumnSelectionHandler.showColumn('Num rows'),
|
show: !noSqlMode && hiddenColumns.exists('Num rows'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Replicas',
|
Header: 'Replicas',
|
||||||
|
@ -395,63 +416,115 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
||||||
width: 60,
|
width: 60,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
defaultSortDesc: true,
|
defaultSortDesc: true,
|
||||||
show: !noSqlMode && tableColumnSelectionHandler.showColumn('Replicas'),
|
show: !noSqlMode && hiddenColumns.exists('Replicas'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Is published',
|
Header: 'Is published',
|
||||||
id: 'is_published',
|
id: 'is_published',
|
||||||
accessor: row => String(Boolean(row.is_published)),
|
accessor: row => String(Boolean(row.is_published)),
|
||||||
Filter: makeBooleanFilter(),
|
Filter: makeBooleanFilter(),
|
||||||
show: !noSqlMode && tableColumnSelectionHandler.showColumn('Is published'),
|
show: !noSqlMode && hiddenColumns.exists('Is published'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Is realtime',
|
Header: 'Is realtime',
|
||||||
id: 'is_realtime',
|
id: 'is_realtime',
|
||||||
accessor: row => String(Boolean(row.is_realtime)),
|
accessor: row => String(Boolean(row.is_realtime)),
|
||||||
Filter: makeBooleanFilter(),
|
Filter: makeBooleanFilter(),
|
||||||
show: !noSqlMode && tableColumnSelectionHandler.showColumn('Is realtime'),
|
show: !noSqlMode && hiddenColumns.exists('Is realtime'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Is available',
|
Header: 'Is available',
|
||||||
id: 'is_available',
|
id: 'is_available',
|
||||||
accessor: row => String(Boolean(row.is_available)),
|
accessor: row => String(Boolean(row.is_available)),
|
||||||
Filter: makeBooleanFilter(),
|
Filter: makeBooleanFilter(),
|
||||||
show: !noSqlMode && tableColumnSelectionHandler.showColumn('Is available'),
|
show: !noSqlMode && hiddenColumns.exists('Is available'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Is overshadowed',
|
Header: 'Is overshadowed',
|
||||||
id: 'is_overshadowed',
|
id: 'is_overshadowed',
|
||||||
accessor: row => String(Boolean(row.is_overshadowed)),
|
accessor: row => String(Boolean(row.is_overshadowed)),
|
||||||
Filter: makeBooleanFilter(),
|
Filter: makeBooleanFilter(),
|
||||||
show: !noSqlMode && tableColumnSelectionHandler.showColumn('Is overshadowed'),
|
show: !noSqlMode && hiddenColumns.exists('Is overshadowed'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: ActionCell.COLUMN_LABEL,
|
||||||
|
id: ActionCell.COLUMN_ID,
|
||||||
|
accessor: 'segment_id',
|
||||||
|
width: ActionCell.COLUMN_WIDTH,
|
||||||
|
filterable: false,
|
||||||
|
Cell: row => {
|
||||||
|
if (row.aggregated) return '';
|
||||||
|
const id = row.value;
|
||||||
|
const datasource = row.row.datasource;
|
||||||
|
const dimensions = parseList(row.original.payload.dimensions);
|
||||||
|
const metrics = parseList(row.original.payload.metrics);
|
||||||
|
return (
|
||||||
|
<ActionCell
|
||||||
|
onDetail={() => {
|
||||||
|
this.setState({
|
||||||
|
segmentTableActionDialogId: id,
|
||||||
|
datasourceTableActionDialogId: datasource,
|
||||||
|
actions: this.getSegmentActions(id, datasource),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
actions={this.getSegmentActions(id, datasource)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
Aggregated: row => '',
|
||||||
|
show: hiddenColumns.exists(ActionCell.COLUMN_LABEL),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
defaultPageSize={50}
|
defaultPageSize={50}
|
||||||
SubComponent={rowInfo => {
|
|
||||||
const { original } = rowInfo;
|
|
||||||
const { payload } = rowInfo.original;
|
|
||||||
const dimensions = parseList(payload.dimensions);
|
|
||||||
const metrics = parseList(payload.metrics);
|
|
||||||
return (
|
|
||||||
<div className="segment-detail">
|
|
||||||
<H5>Segment ID</H5>
|
|
||||||
<p>{original.segment_id}</p>
|
|
||||||
<H5>{`Dimensions (${dimensions.length})`}</H5>
|
|
||||||
<p>{dimensions.join(', ') || 'No dimension'}</p>
|
|
||||||
<H5>{`Metrics (${metrics.length})`}</H5>
|
|
||||||
<p>{metrics.join(', ') || 'No metrics'}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
renderTerminateSegmentAction() {
|
||||||
const { goToQuery, noSqlMode } = this.props;
|
const { terminateSegmentId, terminateDatasourceId } = this.state;
|
||||||
const { tableColumnSelectionHandler } = this;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<AsyncActionDialog
|
||||||
|
action={
|
||||||
|
terminateSegmentId
|
||||||
|
? async () => {
|
||||||
|
const resp = await axios.delete(
|
||||||
|
`/druid/coordinator/v1/datasources/${terminateDatasourceId}/segments/${terminateSegmentId}`,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
return resp.data;
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
confirmButtonText="Drop Segment"
|
||||||
|
successText="Segment drop request acknowledged, next time the coordinator runs segment will be dropped"
|
||||||
|
failText="Could not drop segment"
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
onClose={success => {
|
||||||
|
this.setState({ terminateSegmentId: null });
|
||||||
|
if (success) {
|
||||||
|
this.segmentsJsonQueryManager.rerunLastQuery();
|
||||||
|
this.segmentsSqlQueryManager.rerunLastQuery();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p>{`Are you sure you want to drop segment '${terminateSegmentId}'?`}</p>
|
||||||
|
<p>This action is not reversible.</p>
|
||||||
|
</AsyncActionDialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
segmentTableActionDialogId,
|
||||||
|
datasourceTableActionDialogId,
|
||||||
|
actions,
|
||||||
|
hiddenColumns,
|
||||||
|
} = this.state;
|
||||||
|
const { goToQuery, noSqlMode } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
<div className="segments-view app-view">
|
<div className="segments-view app-view">
|
||||||
<ViewControlBar label="Segments">
|
<ViewControlBar label="Segments">
|
||||||
<RefreshButton
|
<RefreshButton
|
||||||
|
@ -472,12 +545,23 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
||||||
)}
|
)}
|
||||||
<TableColumnSelector
|
<TableColumnSelector
|
||||||
columns={noSqlMode ? tableColumnsNoSql : tableColumns}
|
columns={noSqlMode ? tableColumnsNoSql : tableColumns}
|
||||||
onChange={column => tableColumnSelectionHandler.changeTableColumnSelector(column)}
|
onChange={column => this.setState({ hiddenColumns: hiddenColumns.toggle(column) })}
|
||||||
tableColumnsHidden={tableColumnSelectionHandler.hiddenColumns}
|
tableColumnsHidden={hiddenColumns.storedArray}
|
||||||
/>
|
/>
|
||||||
</ViewControlBar>
|
</ViewControlBar>
|
||||||
{this.renderSegmentsTable()}
|
{this.renderSegmentsTable()}
|
||||||
</div>
|
</div>
|
||||||
|
{this.renderTerminateSegmentAction()}
|
||||||
|
{segmentTableActionDialogId && (
|
||||||
|
<SegmentTableActionDialog
|
||||||
|
segmentId={segmentTableActionDialogId}
|
||||||
|
dataSourceId={datasourceTableActionDialogId}
|
||||||
|
actions={actions}
|
||||||
|
onClose={() => this.setState({ segmentTableActionDialogId: null })}
|
||||||
|
isOpen
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,9 @@ import {
|
||||||
lookupBy,
|
lookupBy,
|
||||||
queryDruidSql,
|
queryDruidSql,
|
||||||
QueryManager,
|
QueryManager,
|
||||||
TableColumnSelectionHandler,
|
|
||||||
} from '../../utils';
|
} from '../../utils';
|
||||||
import { BasicAction } from '../../utils/basic-action';
|
import { BasicAction } from '../../utils/basic-action';
|
||||||
|
import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array';
|
||||||
import { deepGet } from '../../utils/object-change';
|
import { deepGet } from '../../utils/object-change';
|
||||||
|
|
||||||
import './servers-view.scss';
|
import './servers-view.scss';
|
||||||
|
@ -90,6 +90,8 @@ export interface ServersViewState {
|
||||||
|
|
||||||
middleManagerDisableWorkerHost: string | null;
|
middleManagerDisableWorkerHost: string | null;
|
||||||
middleManagerEnableWorkerHost: string | null;
|
middleManagerEnableWorkerHost: string | null;
|
||||||
|
|
||||||
|
hiddenColumns: LocalStorageBackedArray<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ServerQueryResultRow {
|
interface ServerQueryResultRow {
|
||||||
|
@ -132,7 +134,6 @@ interface ServerResultRow
|
||||||
|
|
||||||
export class ServersView extends React.PureComponent<ServersViewProps, ServersViewState> {
|
export class ServersView extends React.PureComponent<ServersViewProps, ServersViewState> {
|
||||||
private serverQueryManager: QueryManager<string, ServerQueryResultRow[]>;
|
private serverQueryManager: QueryManager<string, ServerQueryResultRow[]>;
|
||||||
private serverTableColumnSelectionHandler: TableColumnSelectionHandler;
|
|
||||||
|
|
||||||
constructor(props: ServersViewProps, context: any) {
|
constructor(props: ServersViewProps, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
@ -145,12 +146,11 @@ export class ServersView extends React.PureComponent<ServersViewProps, ServersVi
|
||||||
|
|
||||||
middleManagerDisableWorkerHost: null,
|
middleManagerDisableWorkerHost: null,
|
||||||
middleManagerEnableWorkerHost: null,
|
middleManagerEnableWorkerHost: null,
|
||||||
};
|
|
||||||
|
|
||||||
this.serverTableColumnSelectionHandler = new TableColumnSelectionHandler(
|
hiddenColumns: new LocalStorageBackedArray<string>(
|
||||||
LocalStorageKeys.SERVER_TABLE_COLUMN_SELECTION,
|
LocalStorageKeys.SERVER_TABLE_COLUMN_SELECTION,
|
||||||
() => this.setState({}),
|
),
|
||||||
);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getServers(): Promise<ServerQueryResultRow[]> {
|
static async getServers(): Promise<ServerQueryResultRow[]> {
|
||||||
|
@ -172,6 +172,8 @@ export class ServersView extends React.PureComponent<ServersViewProps, ServersVi
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
const { noSqlMode } = this.props;
|
const { noSqlMode } = this.props;
|
||||||
|
const { hiddenColumns } = this.state;
|
||||||
|
|
||||||
this.serverQueryManager = new QueryManager({
|
this.serverQueryManager = new QueryManager({
|
||||||
processQuery: async (query: string) => {
|
processQuery: async (query: string) => {
|
||||||
let servers: ServerQueryResultRow[];
|
let servers: ServerQueryResultRow[];
|
||||||
|
@ -260,8 +262,14 @@ ORDER BY "rank" DESC, "server" DESC`);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderServersTable() {
|
renderServersTable() {
|
||||||
const { servers, serversLoading, serversError, serverFilter, groupServersBy } = this.state;
|
const {
|
||||||
const { serverTableColumnSelectionHandler } = this;
|
servers,
|
||||||
|
serversLoading,
|
||||||
|
serversError,
|
||||||
|
serverFilter,
|
||||||
|
groupServersBy,
|
||||||
|
hiddenColumns,
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
const fillIndicator = (value: number) => {
|
const fillIndicator = (value: number) => {
|
||||||
let formattedValue = (value * 100).toFixed(1);
|
let formattedValue = (value * 100).toFixed(1);
|
||||||
|
@ -294,7 +302,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
||||||
accessor: 'server',
|
accessor: 'server',
|
||||||
width: 300,
|
width: 300,
|
||||||
Aggregated: row => '',
|
Aggregated: row => '',
|
||||||
show: serverTableColumnSelectionHandler.showColumn('Server'),
|
show: hiddenColumns.exists('Server'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Type',
|
Header: 'Type',
|
||||||
|
@ -312,7 +320,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
show: serverTableColumnSelectionHandler.showColumn('Type'),
|
show: hiddenColumns.exists('Type'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Tier',
|
Header: 'Tier',
|
||||||
|
@ -329,13 +337,13 @@ ORDER BY "rank" DESC, "server" DESC`);
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
show: serverTableColumnSelectionHandler.showColumn('Tier'),
|
show: hiddenColumns.exists('Tier'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Host',
|
Header: 'Host',
|
||||||
accessor: 'host',
|
accessor: 'host',
|
||||||
Aggregated: () => '',
|
Aggregated: () => '',
|
||||||
show: serverTableColumnSelectionHandler.showColumn('Host'),
|
show: hiddenColumns.exists('Host'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Port',
|
Header: 'Port',
|
||||||
|
@ -351,7 +359,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
||||||
return ports.join(', ') || 'No port';
|
return ports.join(', ') || 'No port';
|
||||||
},
|
},
|
||||||
Aggregated: () => '',
|
Aggregated: () => '',
|
||||||
show: serverTableColumnSelectionHandler.showColumn('Port'),
|
show: hiddenColumns.exists('Port'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Curr size',
|
Header: 'Curr size',
|
||||||
|
@ -370,7 +378,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
||||||
if (row.value === null) return '';
|
if (row.value === null) return '';
|
||||||
return formatBytes(row.value);
|
return formatBytes(row.value);
|
||||||
},
|
},
|
||||||
show: serverTableColumnSelectionHandler.showColumn('Curr size'),
|
show: hiddenColumns.exists('Curr size'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Max size',
|
Header: 'Max size',
|
||||||
|
@ -389,7 +397,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
||||||
if (row.value === null) return '';
|
if (row.value === null) return '';
|
||||||
return formatBytes(row.value);
|
return formatBytes(row.value);
|
||||||
},
|
},
|
||||||
show: serverTableColumnSelectionHandler.showColumn('Max size'),
|
show: hiddenColumns.exists('Max size'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Usage',
|
Header: 'Usage',
|
||||||
|
@ -447,7 +455,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
show: serverTableColumnSelectionHandler.showColumn('Usage'),
|
show: hiddenColumns.exists('Usage'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Detail',
|
Header: 'Detail',
|
||||||
|
@ -509,7 +517,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
||||||
segmentsToDropSize,
|
segmentsToDropSize,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
show: serverTableColumnSelectionHandler.showColumn('Detail'),
|
show: hiddenColumns.exists('Detail'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: ActionCell.COLUMN_LABEL,
|
Header: ActionCell.COLUMN_LABEL,
|
||||||
|
@ -523,7 +531,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
||||||
const workerActions = this.getWorkerActions(row.value.host, disabled);
|
const workerActions = this.getWorkerActions(row.value.host, disabled);
|
||||||
return <ActionCell actions={workerActions} />;
|
return <ActionCell actions={workerActions} />;
|
||||||
},
|
},
|
||||||
show: serverTableColumnSelectionHandler.showColumn(ActionCell.COLUMN_LABEL),
|
show: hiddenColumns.exists(ActionCell.COLUMN_LABEL),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
@ -612,8 +620,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { goToQuery, noSqlMode } = this.props;
|
const { goToQuery, noSqlMode } = this.props;
|
||||||
const { groupServersBy } = this.state;
|
const { groupServersBy, hiddenColumns } = this.state;
|
||||||
const { serverTableColumnSelectionHandler } = this;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="servers-view app-view">
|
<div className="servers-view app-view">
|
||||||
|
@ -652,8 +659,8 @@ ORDER BY "rank" DESC, "server" DESC`);
|
||||||
)}
|
)}
|
||||||
<TableColumnSelector
|
<TableColumnSelector
|
||||||
columns={serverTableColumns}
|
columns={serverTableColumns}
|
||||||
onChange={column => serverTableColumnSelectionHandler.changeTableColumnSelector(column)}
|
onChange={column => this.setState({ hiddenColumns: hiddenColumns.toggle(column) })}
|
||||||
tableColumnsHidden={serverTableColumnSelectionHandler.hiddenColumns}
|
tableColumnsHidden={hiddenColumns.storedArray}
|
||||||
/>
|
/>
|
||||||
</ViewControlBar>
|
</ViewControlBar>
|
||||||
{this.renderServersTable()}
|
{this.renderServersTable()}
|
||||||
|
|
|
@ -53,9 +53,9 @@ import {
|
||||||
localStorageSet,
|
localStorageSet,
|
||||||
queryDruidSql,
|
queryDruidSql,
|
||||||
QueryManager,
|
QueryManager,
|
||||||
TableColumnSelectionHandler,
|
|
||||||
} from '../../utils';
|
} from '../../utils';
|
||||||
import { BasicAction } from '../../utils/basic-action';
|
import { BasicAction } from '../../utils/basic-action';
|
||||||
|
import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array';
|
||||||
|
|
||||||
import './tasks-view.scss';
|
import './tasks-view.scss';
|
||||||
|
|
||||||
|
@ -114,6 +114,8 @@ export interface TasksViewState {
|
||||||
taskTableActionDialogActions: BasicAction[];
|
taskTableActionDialogActions: BasicAction[];
|
||||||
supervisorTableActionDialogId: string | null;
|
supervisorTableActionDialogId: string | null;
|
||||||
supervisorTableActionDialogActions: BasicAction[];
|
supervisorTableActionDialogActions: BasicAction[];
|
||||||
|
hiddenTaskColumns: LocalStorageBackedArray<string>;
|
||||||
|
hiddenSupervisorColumns: LocalStorageBackedArray<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TaskQueryResultRow {
|
interface TaskQueryResultRow {
|
||||||
|
@ -172,8 +174,6 @@ function stateToColor(status: string): string {
|
||||||
export class TasksView extends React.PureComponent<TasksViewProps, TasksViewState> {
|
export class TasksView extends React.PureComponent<TasksViewProps, TasksViewState> {
|
||||||
private supervisorQueryManager: QueryManager<string, SupervisorQueryResultRow[]>;
|
private supervisorQueryManager: QueryManager<string, SupervisorQueryResultRow[]>;
|
||||||
private taskQueryManager: QueryManager<string, TaskQueryResultRow[]>;
|
private taskQueryManager: QueryManager<string, TaskQueryResultRow[]>;
|
||||||
private supervisorTableColumnSelectionHandler: TableColumnSelectionHandler;
|
|
||||||
private taskTableColumnSelectionHandler: TableColumnSelectionHandler;
|
|
||||||
static statusRanking: Record<string, number> = {
|
static statusRanking: Record<string, number> = {
|
||||||
RUNNING: 4,
|
RUNNING: 4,
|
||||||
PENDING: 3,
|
PENDING: 3,
|
||||||
|
@ -192,6 +192,7 @@ export class TasksView extends React.PureComponent<TasksViewProps, TasksViewStat
|
||||||
resumeSupervisorId: null,
|
resumeSupervisorId: null,
|
||||||
suspendSupervisorId: null,
|
suspendSupervisorId: null,
|
||||||
resetSupervisorId: null,
|
resetSupervisorId: null,
|
||||||
|
supervisorTableActionDialogId: null,
|
||||||
terminateSupervisorId: null,
|
terminateSupervisorId: null,
|
||||||
|
|
||||||
tasksLoading: true,
|
tasksLoading: true,
|
||||||
|
@ -210,19 +211,15 @@ export class TasksView extends React.PureComponent<TasksViewProps, TasksViewStat
|
||||||
taskTableActionDialogId: null,
|
taskTableActionDialogId: null,
|
||||||
taskTableActionDialogStatus: null,
|
taskTableActionDialogStatus: null,
|
||||||
taskTableActionDialogActions: [],
|
taskTableActionDialogActions: [],
|
||||||
supervisorTableActionDialogId: null,
|
|
||||||
supervisorTableActionDialogActions: [],
|
supervisorTableActionDialogActions: [],
|
||||||
};
|
|
||||||
|
|
||||||
this.supervisorTableColumnSelectionHandler = new TableColumnSelectionHandler(
|
hiddenTaskColumns: new LocalStorageBackedArray<string>(
|
||||||
LocalStorageKeys.SUPERVISOR_TABLE_COLUMN_SELECTION,
|
|
||||||
() => this.setState({}),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.taskTableColumnSelectionHandler = new TableColumnSelectionHandler(
|
|
||||||
LocalStorageKeys.TASK_TABLE_COLUMN_SELECTION,
|
LocalStorageKeys.TASK_TABLE_COLUMN_SELECTION,
|
||||||
() => this.setState({}),
|
),
|
||||||
);
|
hiddenSupervisorColumns: new LocalStorageBackedArray<string>(
|
||||||
|
LocalStorageKeys.SUPERVISOR_TABLE_COLUMN_SELECTION,
|
||||||
|
),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static parseTasks = (data: any[]): TaskQueryResultRow[] => {
|
static parseTasks = (data: any[]): TaskQueryResultRow[] => {
|
||||||
|
@ -248,6 +245,7 @@ export class TasksView extends React.PureComponent<TasksViewProps, TasksViewStat
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
const { noSqlMode } = this.props;
|
const { noSqlMode } = this.props;
|
||||||
|
|
||||||
this.supervisorQueryManager = new QueryManager({
|
this.supervisorQueryManager = new QueryManager({
|
||||||
processQuery: async (query: string) => {
|
processQuery: async (query: string) => {
|
||||||
const resp = await axios.get('/druid/indexer/v1/supervisor?full');
|
const resp = await axios.get('/druid/indexer/v1/supervisor?full');
|
||||||
|
@ -521,8 +519,12 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSupervisorTable() {
|
renderSupervisorTable() {
|
||||||
const { supervisors, supervisorsLoading, supervisorsError } = this.state;
|
const {
|
||||||
const { supervisorTableColumnSelectionHandler } = this;
|
supervisors,
|
||||||
|
supervisorsLoading,
|
||||||
|
supervisorsError,
|
||||||
|
hiddenSupervisorColumns,
|
||||||
|
} = this.state;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ReactTable
|
<ReactTable
|
||||||
|
@ -540,7 +542,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
id: 'datasource',
|
id: 'datasource',
|
||||||
accessor: 'id',
|
accessor: 'id',
|
||||||
width: 300,
|
width: 300,
|
||||||
show: supervisorTableColumnSelectionHandler.showColumn('Datasource'),
|
show: hiddenSupervisorColumns.exists('Datasource'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Type',
|
Header: 'Type',
|
||||||
|
@ -552,7 +554,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
if (!tuningConfig) return '';
|
if (!tuningConfig) return '';
|
||||||
return tuningConfig.type;
|
return tuningConfig.type;
|
||||||
},
|
},
|
||||||
show: supervisorTableColumnSelectionHandler.showColumn('Type'),
|
show: hiddenSupervisorColumns.exists('Type'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Topic/Stream',
|
Header: 'Topic/Stream',
|
||||||
|
@ -564,7 +566,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
if (!ioConfig) return '';
|
if (!ioConfig) return '';
|
||||||
return ioConfig.topic || ioConfig.stream || '';
|
return ioConfig.topic || ioConfig.stream || '';
|
||||||
},
|
},
|
||||||
show: supervisorTableColumnSelectionHandler.showColumn('Topic/Stream'),
|
show: hiddenSupervisorColumns.exists('Topic/Stream'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Status',
|
Header: 'Status',
|
||||||
|
@ -582,7 +584,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
show: supervisorTableColumnSelectionHandler.showColumn('Status'),
|
show: hiddenSupervisorColumns.exists('Status'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: ActionCell.COLUMN_LABEL,
|
Header: ActionCell.COLUMN_LABEL,
|
||||||
|
@ -607,7 +609,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
show: supervisorTableColumnSelectionHandler.showColumn(ActionCell.COLUMN_LABEL),
|
show: hiddenSupervisorColumns.exists(ActionCell.COLUMN_LABEL),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
@ -668,8 +670,14 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
|
|
||||||
renderTaskTable() {
|
renderTaskTable() {
|
||||||
const { goToMiddleManager } = this.props;
|
const { goToMiddleManager } = this.props;
|
||||||
const { tasks, tasksLoading, tasksError, taskFilter, groupTasksBy } = this.state;
|
const {
|
||||||
const { taskTableColumnSelectionHandler } = this;
|
tasks,
|
||||||
|
tasksLoading,
|
||||||
|
tasksError,
|
||||||
|
taskFilter,
|
||||||
|
groupTasksBy,
|
||||||
|
hiddenTaskColumns,
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -690,7 +698,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
accessor: 'task_id',
|
accessor: 'task_id',
|
||||||
width: 300,
|
width: 300,
|
||||||
Aggregated: row => '',
|
Aggregated: row => '',
|
||||||
show: taskTableColumnSelectionHandler.showColumn('Task ID'),
|
show: hiddenTaskColumns.exists('Task ID'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Type',
|
Header: 'Type',
|
||||||
|
@ -707,7 +715,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
show: taskTableColumnSelectionHandler.showColumn('Type'),
|
show: hiddenTaskColumns.exists('Type'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Datasource',
|
Header: 'Datasource',
|
||||||
|
@ -724,8 +732,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
show: taskTableColumnSelectionHandler.showColumn('Datasource'),
|
show: hiddenTaskColumns.exists('Datasource'),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
Header: 'Location',
|
Header: 'Location',
|
||||||
accessor: 'location',
|
accessor: 'location',
|
||||||
|
@ -733,14 +742,14 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
filterMethod: (filter: Filter, row: any) => {
|
filterMethod: (filter: Filter, row: any) => {
|
||||||
return booleanCustomTableFilter(filter, row.location);
|
return booleanCustomTableFilter(filter, row.location);
|
||||||
},
|
},
|
||||||
show: taskTableColumnSelectionHandler.showColumn('Location'),
|
show: hiddenTaskColumns.exists('Location'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Created time',
|
Header: 'Created time',
|
||||||
accessor: 'created_time',
|
accessor: 'created_time',
|
||||||
width: 120,
|
width: 120,
|
||||||
Aggregated: row => '',
|
Aggregated: row => '',
|
||||||
show: taskTableColumnSelectionHandler.showColumn('Created time'),
|
show: hiddenTaskColumns.exists('Created time'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Status',
|
Header: 'Status',
|
||||||
|
@ -800,7 +809,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
filterMethod: (filter: Filter, row: any) => {
|
filterMethod: (filter: Filter, row: any) => {
|
||||||
return booleanCustomTableFilter(filter, row.status.status);
|
return booleanCustomTableFilter(filter, row.status.status);
|
||||||
},
|
},
|
||||||
show: taskTableColumnSelectionHandler.showColumn('Status'),
|
show: hiddenTaskColumns.exists('Status'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Duration',
|
Header: 'Duration',
|
||||||
|
@ -808,7 +817,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
filterable: false,
|
filterable: false,
|
||||||
Cell: row => (row.value > 0 ? formatDuration(row.value) : ''),
|
Cell: row => (row.value > 0 ? formatDuration(row.value) : ''),
|
||||||
Aggregated: () => '',
|
Aggregated: () => '',
|
||||||
show: taskTableColumnSelectionHandler.showColumn('Duration'),
|
show: hiddenTaskColumns.exists('Duration'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: ActionCell.COLUMN_LABEL,
|
Header: ActionCell.COLUMN_LABEL,
|
||||||
|
@ -836,7 +845,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
Aggregated: row => '',
|
Aggregated: row => '',
|
||||||
show: taskTableColumnSelectionHandler.showColumn(ActionCell.COLUMN_LABEL),
|
show: hiddenTaskColumns.exists(ActionCell.COLUMN_LABEL),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
@ -857,8 +866,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
supervisorTableActionDialogId,
|
supervisorTableActionDialogId,
|
||||||
supervisorTableActionDialogActions,
|
supervisorTableActionDialogActions,
|
||||||
taskTableActionDialogStatus,
|
taskTableActionDialogStatus,
|
||||||
|
hiddenSupervisorColumns,
|
||||||
|
hiddenTaskColumns,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const { supervisorTableColumnSelectionHandler, taskTableColumnSelectionHandler } = this;
|
|
||||||
|
|
||||||
const submitSupervisorMenu = (
|
const submitSupervisorMenu = (
|
||||||
<Menu>
|
<Menu>
|
||||||
|
@ -915,9 +925,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
<TableColumnSelector
|
<TableColumnSelector
|
||||||
columns={supervisorTableColumns}
|
columns={supervisorTableColumns}
|
||||||
onChange={column =>
|
onChange={column =>
|
||||||
supervisorTableColumnSelectionHandler.changeTableColumnSelector(column)
|
this.setState({ hiddenSupervisorColumns: hiddenSupervisorColumns.toggle(column) })
|
||||||
}
|
}
|
||||||
tableColumnsHidden={supervisorTableColumnSelectionHandler.hiddenColumns}
|
tableColumnsHidden={hiddenSupervisorColumns.storedArray}
|
||||||
/>
|
/>
|
||||||
</ViewControlBar>
|
</ViewControlBar>
|
||||||
{this.renderSupervisorTable()}
|
{this.renderSupervisorTable()}
|
||||||
|
@ -968,9 +978,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
||||||
<TableColumnSelector
|
<TableColumnSelector
|
||||||
columns={taskTableColumns}
|
columns={taskTableColumns}
|
||||||
onChange={column =>
|
onChange={column =>
|
||||||
taskTableColumnSelectionHandler.changeTableColumnSelector(column)
|
this.setState({ hiddenTaskColumns: hiddenTaskColumns.toggle(column) })
|
||||||
}
|
}
|
||||||
tableColumnsHidden={taskTableColumnSelectionHandler.hiddenColumns}
|
tableColumnsHidden={hiddenTaskColumns.storedArray}
|
||||||
/>
|
/>
|
||||||
</ViewControlBar>
|
</ViewControlBar>
|
||||||
{this.renderTaskTable()}
|
{this.renderTaskTable()}
|
||||||
|
|
Loading…
Reference in New Issue