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-state';
|
||||
export * from './rune-decoder';
|
||||
export * from './table-column-selection-handler';
|
||||
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,
|
||||
queryDruidSql,
|
||||
QueryManager,
|
||||
TableColumnSelectionHandler,
|
||||
} from '../../utils';
|
||||
import { BasicAction } from '../../utils/basic-action';
|
||||
import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array';
|
||||
|
||||
import './datasource-view.scss';
|
||||
|
||||
|
@ -113,6 +113,7 @@ export interface DatasourcesViewState {
|
|||
dropReloadDatasource: string | null;
|
||||
dropReloadAction: 'drop' | 'reload';
|
||||
dropReloadInterval: string;
|
||||
hiddenColumns: LocalStorageBackedArray<string>;
|
||||
}
|
||||
|
||||
export class DatasourcesView extends React.PureComponent<
|
||||
|
@ -137,7 +138,6 @@ export class DatasourcesView extends React.PureComponent<
|
|||
string,
|
||||
{ tiers: string[]; defaultRules: any[]; datasources: Datasource[] }
|
||||
>;
|
||||
private tableColumnSelectionHandler: TableColumnSelectionHandler;
|
||||
|
||||
constructor(props: DatasourcesViewProps, context: any) {
|
||||
super(props, context);
|
||||
|
@ -158,16 +158,15 @@ export class DatasourcesView extends React.PureComponent<
|
|||
dropReloadDatasource: null,
|
||||
dropReloadAction: 'drop',
|
||||
dropReloadInterval: '',
|
||||
};
|
||||
|
||||
this.tableColumnSelectionHandler = new TableColumnSelectionHandler(
|
||||
hiddenColumns: new LocalStorageBackedArray<string>(
|
||||
LocalStorageKeys.DATASOURCE_TABLE_COLUMN_SELECTION,
|
||||
() => this.setState({}),
|
||||
);
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
const { noSqlMode } = this.props;
|
||||
const { hiddenColumns } = this.state;
|
||||
|
||||
this.datasourceQueryManager = new QueryManager({
|
||||
processQuery: async (query: string) => {
|
||||
|
@ -564,8 +563,8 @@ GROUP BY 1`);
|
|||
datasourcesError,
|
||||
datasourcesFilter,
|
||||
showDisabled,
|
||||
hiddenColumns,
|
||||
} = this.state;
|
||||
const { tableColumnSelectionHandler } = this;
|
||||
let data = datasources || [];
|
||||
if (!showDisabled) {
|
||||
data = data.filter(d => !d.disabled);
|
||||
|
@ -604,7 +603,7 @@ GROUP BY 1`);
|
|||
</a>
|
||||
);
|
||||
},
|
||||
show: tableColumnSelectionHandler.showColumn('Datasource'),
|
||||
show: hiddenColumns.exists('Datasource'),
|
||||
},
|
||||
{
|
||||
Header: 'Availability',
|
||||
|
@ -668,7 +667,7 @@ GROUP BY 1`);
|
|||
const percentAvailable2 = d2.num_available / d2.num_total;
|
||||
return percentAvailable1 - percentAvailable2 || d1.num_total - d2.num_total;
|
||||
},
|
||||
show: tableColumnSelectionHandler.showColumn('Availability'),
|
||||
show: hiddenColumns.exists('Availability'),
|
||||
},
|
||||
{
|
||||
Header: 'Retention',
|
||||
|
@ -701,7 +700,7 @@ GROUP BY 1`);
|
|||
</span>
|
||||
);
|
||||
},
|
||||
show: tableColumnSelectionHandler.showColumn('Retention'),
|
||||
show: hiddenColumns.exists('Retention'),
|
||||
},
|
||||
{
|
||||
Header: 'Compaction',
|
||||
|
@ -730,7 +729,7 @@ GROUP BY 1`);
|
|||
</span>
|
||||
);
|
||||
},
|
||||
show: tableColumnSelectionHandler.showColumn('Compaction'),
|
||||
show: hiddenColumns.exists('Compaction'),
|
||||
},
|
||||
{
|
||||
Header: 'Size',
|
||||
|
@ -738,7 +737,7 @@ GROUP BY 1`);
|
|||
filterable: false,
|
||||
width: 100,
|
||||
Cell: row => formatBytes(row.value),
|
||||
show: tableColumnSelectionHandler.showColumn('Size'),
|
||||
show: hiddenColumns.exists('Size'),
|
||||
},
|
||||
{
|
||||
Header: 'Num rows',
|
||||
|
@ -746,7 +745,7 @@ GROUP BY 1`);
|
|||
filterable: false,
|
||||
width: 100,
|
||||
Cell: row => formatNumber(row.value),
|
||||
show: !noSqlMode && tableColumnSelectionHandler.showColumn('Num rows'),
|
||||
show: !noSqlMode && hiddenColumns.exists('Num rows'),
|
||||
},
|
||||
{
|
||||
Header: ActionCell.COLUMN_LABEL,
|
||||
|
@ -760,7 +759,7 @@ GROUP BY 1`);
|
|||
const datasourceActions = this.getDatasourceActions(datasource, disabled);
|
||||
return <ActionCell actions={datasourceActions} />;
|
||||
},
|
||||
show: tableColumnSelectionHandler.showColumn(ActionCell.COLUMN_LABEL),
|
||||
show: hiddenColumns.exists(ActionCell.COLUMN_LABEL),
|
||||
},
|
||||
]}
|
||||
defaultPageSize={50}
|
||||
|
@ -777,8 +776,7 @@ GROUP BY 1`);
|
|||
|
||||
render() {
|
||||
const { goToQuery, noSqlMode } = this.props;
|
||||
const { showDisabled } = this.state;
|
||||
const { tableColumnSelectionHandler } = this;
|
||||
const { showDisabled, hiddenColumns } = this.state;
|
||||
|
||||
return (
|
||||
<div className="data-sources-view app-view">
|
||||
|
@ -801,8 +799,8 @@ GROUP BY 1`);
|
|||
/>
|
||||
<TableColumnSelector
|
||||
columns={noSqlMode ? tableColumnsNoSql : tableColumns}
|
||||
onChange={column => tableColumnSelectionHandler.changeTableColumnSelector(column)}
|
||||
tableColumnsHidden={tableColumnSelectionHandler.hiddenColumns}
|
||||
onChange={column => this.setState({ hiddenColumns: hiddenColumns.toggle(column) })}
|
||||
tableColumnsHidden={hiddenColumns.storedArray}
|
||||
/>
|
||||
</ViewControlBar>
|
||||
{this.renderDatasourceTable()}
|
||||
|
|
|
@ -26,13 +26,9 @@ import ReactTable from 'react-table';
|
|||
import { ActionCell, TableColumnSelector, ViewControlBar } from '../../components';
|
||||
import { AsyncActionDialog, LookupEditDialog } from '../../dialogs/';
|
||||
import { AppToaster } from '../../singletons/toaster';
|
||||
import {
|
||||
getDruidErrorMessage,
|
||||
LocalStorageKeys,
|
||||
QueryManager,
|
||||
TableColumnSelectionHandler,
|
||||
} from '../../utils';
|
||||
import { getDruidErrorMessage, LocalStorageKeys, QueryManager } from '../../utils';
|
||||
import { BasicAction, basicActionsToMenu } from '../../utils/basic-action';
|
||||
import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array';
|
||||
|
||||
import './lookups-view.scss';
|
||||
|
||||
|
@ -57,11 +53,12 @@ export interface LookupsViewState {
|
|||
|
||||
deleteLookupName: string | null;
|
||||
deleteLookupTier: string | null;
|
||||
|
||||
hiddenColumns: LocalStorageBackedArray<string>;
|
||||
}
|
||||
|
||||
export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsViewState> {
|
||||
private lookupsGetQueryManager: QueryManager<string, { lookupEntries: any[]; tiers: string[] }>;
|
||||
private tableColumnSelectionHandler: TableColumnSelectionHandler;
|
||||
|
||||
constructor(props: LookupsViewProps, context: any) {
|
||||
super(props, context);
|
||||
|
@ -80,11 +77,11 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
|
|||
|
||||
deleteLookupTier: null,
|
||||
deleteLookupName: null,
|
||||
};
|
||||
this.tableColumnSelectionHandler = new TableColumnSelectionHandler(
|
||||
|
||||
hiddenColumns: new LocalStorageBackedArray<string>(
|
||||
LocalStorageKeys.LOOKUP_TABLE_COLUMN_SELECTION,
|
||||
() => this.setState({}),
|
||||
);
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
|
@ -266,8 +263,13 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
|
|||
}
|
||||
|
||||
renderLookupsTable() {
|
||||
const { lookups, loadingLookups, lookupsError, lookupsUninitialized } = this.state;
|
||||
const { tableColumnSelectionHandler } = this;
|
||||
const {
|
||||
lookups,
|
||||
loadingLookups,
|
||||
lookupsError,
|
||||
lookupsUninitialized,
|
||||
hiddenColumns,
|
||||
} = this.state;
|
||||
|
||||
if (lookupsUninitialized) {
|
||||
return (
|
||||
|
@ -296,28 +298,28 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
|
|||
id: 'lookup_name',
|
||||
accessor: (row: any) => row.id,
|
||||
filterable: true,
|
||||
show: tableColumnSelectionHandler.showColumn('Lookup name'),
|
||||
show: hiddenColumns.exists('Lookup name'),
|
||||
},
|
||||
{
|
||||
Header: 'Tier',
|
||||
id: 'tier',
|
||||
accessor: (row: any) => row.tier,
|
||||
filterable: true,
|
||||
show: tableColumnSelectionHandler.showColumn('Tier'),
|
||||
show: hiddenColumns.exists('Tier'),
|
||||
},
|
||||
{
|
||||
Header: 'Type',
|
||||
id: 'type',
|
||||
accessor: (row: any) => row.spec.type,
|
||||
filterable: true,
|
||||
show: tableColumnSelectionHandler.showColumn('Type'),
|
||||
show: hiddenColumns.exists('Type'),
|
||||
},
|
||||
{
|
||||
Header: 'Version',
|
||||
id: 'version',
|
||||
accessor: (row: any) => row.version,
|
||||
filterable: true,
|
||||
show: tableColumnSelectionHandler.showColumn('Version'),
|
||||
show: hiddenColumns.exists('Version'),
|
||||
},
|
||||
{
|
||||
Header: ActionCell.COLUMN_LABEL,
|
||||
|
@ -331,7 +333,7 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
|
|||
const lookupActions = this.getLookupActions(lookupTier, lookupId);
|
||||
return <ActionCell actions={lookupActions} />;
|
||||
},
|
||||
show: tableColumnSelectionHandler.showColumn(ActionCell.COLUMN_LABEL),
|
||||
show: hiddenColumns.exists(ActionCell.COLUMN_LABEL),
|
||||
},
|
||||
]}
|
||||
defaultPageSize={50}
|
||||
|
@ -368,8 +370,7 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
|
|||
}
|
||||
|
||||
render() {
|
||||
const { lookupsError } = this.state;
|
||||
const { tableColumnSelectionHandler } = this;
|
||||
const { lookupsError, hiddenColumns } = this.state;
|
||||
|
||||
return (
|
||||
<div className="lookups-view app-view">
|
||||
|
@ -388,8 +389,8 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
|
|||
)}
|
||||
<TableColumnSelector
|
||||
columns={tableColumns}
|
||||
onChange={column => tableColumnSelectionHandler.changeTableColumnSelector(column)}
|
||||
tableColumnsHidden={tableColumnSelectionHandler.hiddenColumns}
|
||||
onChange={column => this.setState({ hiddenColumns: hiddenColumns.toggle(column) })}
|
||||
tableColumnsHidden={hiddenColumns.storedArray}
|
||||
/>
|
||||
</ViewControlBar>
|
||||
{this.renderLookupsTable()}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`segments-view matches snapshot 1`] = `
|
||||
<Fragment>
|
||||
<div
|
||||
className="segments-view app-view"
|
||||
>
|
||||
|
@ -33,6 +34,7 @@ exports[`segments-view matches snapshot 1`] = `
|
|||
"Is realtime",
|
||||
"Is available",
|
||||
"Is overshadowed",
|
||||
"Actions",
|
||||
]
|
||||
}
|
||||
onChange={[Function]}
|
||||
|
@ -49,7 +51,6 @@ exports[`segments-view matches snapshot 1`] = `
|
|||
PaginationComponent={[Function]}
|
||||
PivotValueComponent={[Function]}
|
||||
ResizerComponent={[Function]}
|
||||
SubComponent={[Function]}
|
||||
TableComponent={[Function]}
|
||||
TbodyComponent={[Function]}
|
||||
TdComponent={[Function]}
|
||||
|
@ -191,6 +192,16 @@ exports[`segments-view matches snapshot 1`] = `
|
|||
"id": "is_overshadowed",
|
||||
"show": true,
|
||||
},
|
||||
Object {
|
||||
"Aggregated": [Function],
|
||||
"Cell": [Function],
|
||||
"Header": "Actions",
|
||||
"accessor": "segment_id",
|
||||
"filterable": false,
|
||||
"id": "actions",
|
||||
"show": true,
|
||||
"width": 70,
|
||||
},
|
||||
]
|
||||
}
|
||||
data={Array []}
|
||||
|
@ -294,4 +305,20 @@ exports[`segments-view matches snapshot 1`] = `
|
|||
subRowsKey="_subRows"
|
||||
/>
|
||||
</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 { H5 } from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import axios from 'axios';
|
||||
import React from 'react';
|
||||
import ReactTable 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 {
|
||||
addFilter,
|
||||
formatBytes,
|
||||
|
@ -35,8 +36,9 @@ import {
|
|||
queryDruidSql,
|
||||
QueryManager,
|
||||
sqlQueryCustomTableFilter,
|
||||
TableColumnSelectionHandler,
|
||||
} from '../../utils';
|
||||
import { BasicAction } from '../../utils/basic-action';
|
||||
import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array';
|
||||
|
||||
import './segments-view.scss';
|
||||
|
||||
|
@ -54,6 +56,7 @@ const tableColumns: string[] = [
|
|||
'Is realtime',
|
||||
'Is available',
|
||||
'Is overshadowed',
|
||||
ActionCell.COLUMN_LABEL,
|
||||
];
|
||||
const tableColumnsNoSql: string[] = [
|
||||
'Segment ID',
|
||||
|
@ -78,6 +81,12 @@ export interface SegmentsViewState {
|
|||
segmentsError: string | null;
|
||||
segmentFilter: Filter[];
|
||||
allSegments?: SegmentQueryResultRow[] | null;
|
||||
segmentTableActionDialogId: string | null;
|
||||
datasourceTableActionDialogId: string | null;
|
||||
actions: BasicAction[];
|
||||
terminateSegmentId: string | null;
|
||||
terminateDatasourceId: string | null;
|
||||
hiddenColumns: LocalStorageBackedArray<string>;
|
||||
}
|
||||
|
||||
interface QueryAndSkip {
|
||||
|
@ -105,7 +114,6 @@ interface SegmentQueryResultRow {
|
|||
export class SegmentsView extends React.PureComponent<SegmentsViewProps, SegmentsViewState> {
|
||||
private segmentsSqlQueryManager: QueryManager<QueryAndSkip, SegmentQueryResultRow[]>;
|
||||
private segmentsJsonQueryManager: QueryManager<any, SegmentQueryResultRow[]>;
|
||||
private tableColumnSelectionHandler: TableColumnSelectionHandler;
|
||||
|
||||
constructor(props: SegmentsViewProps, context: any) {
|
||||
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' });
|
||||
|
||||
this.state = {
|
||||
segmentTableActionDialogId: null,
|
||||
datasourceTableActionDialogId: null,
|
||||
actions: [],
|
||||
terminateSegmentId: null,
|
||||
terminateDatasourceId: null,
|
||||
segmentsLoading: true,
|
||||
segments: null,
|
||||
segmentsError: null,
|
||||
segmentFilter,
|
||||
hiddenColumns: new LocalStorageBackedArray<string>(
|
||||
LocalStorageKeys.SEGMENT_TABLE_COLUMN_SELECTION,
|
||||
),
|
||||
};
|
||||
|
||||
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 {
|
||||
|
@ -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() {
|
||||
const { segments, segmentsLoading, segmentsError, segmentFilter } = this.state;
|
||||
const { segments, segmentsLoading, segmentsError, segmentFilter, hiddenColumns } = this.state;
|
||||
const { noSqlMode } = this.props;
|
||||
const { tableColumnSelectionHandler } = this;
|
||||
|
||||
return (
|
||||
<ReactTable
|
||||
|
@ -302,7 +323,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
|||
Header: 'Segment ID',
|
||||
accessor: 'segment_id',
|
||||
width: 300,
|
||||
show: tableColumnSelectionHandler.showColumn('Segment ID'),
|
||||
show: hiddenColumns.exists('Segment ID'),
|
||||
},
|
||||
{
|
||||
Header: 'Datasource',
|
||||
|
@ -319,7 +340,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
|||
</a>
|
||||
);
|
||||
},
|
||||
show: tableColumnSelectionHandler.showColumn('Datasource'),
|
||||
show: hiddenColumns.exists('Datasource'),
|
||||
},
|
||||
{
|
||||
Header: 'Start',
|
||||
|
@ -338,7 +359,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
|||
</a>
|
||||
);
|
||||
},
|
||||
show: tableColumnSelectionHandler.showColumn('Start'),
|
||||
show: hiddenColumns.exists('Start'),
|
||||
},
|
||||
{
|
||||
Header: 'End',
|
||||
|
@ -357,21 +378,21 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
|||
</a>
|
||||
);
|
||||
},
|
||||
show: tableColumnSelectionHandler.showColumn('End'),
|
||||
show: hiddenColumns.exists('End'),
|
||||
},
|
||||
{
|
||||
Header: 'Version',
|
||||
accessor: 'version',
|
||||
defaultSortDesc: true,
|
||||
width: 120,
|
||||
show: tableColumnSelectionHandler.showColumn('Version'),
|
||||
show: hiddenColumns.exists('Version'),
|
||||
},
|
||||
{
|
||||
Header: 'Partition',
|
||||
accessor: 'partition_num',
|
||||
width: 60,
|
||||
filterable: false,
|
||||
show: tableColumnSelectionHandler.showColumn('Partition'),
|
||||
show: hiddenColumns.exists('Partition'),
|
||||
},
|
||||
{
|
||||
Header: 'Size',
|
||||
|
@ -379,7 +400,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
|||
filterable: false,
|
||||
defaultSortDesc: true,
|
||||
Cell: row => formatBytes(row.value),
|
||||
show: tableColumnSelectionHandler.showColumn('Size'),
|
||||
show: hiddenColumns.exists('Size'),
|
||||
},
|
||||
{
|
||||
Header: 'Num rows',
|
||||
|
@ -387,7 +408,7 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
|||
filterable: false,
|
||||
defaultSortDesc: true,
|
||||
Cell: row => formatNumber(row.value),
|
||||
show: !noSqlMode && tableColumnSelectionHandler.showColumn('Num rows'),
|
||||
show: !noSqlMode && hiddenColumns.exists('Num rows'),
|
||||
},
|
||||
{
|
||||
Header: 'Replicas',
|
||||
|
@ -395,63 +416,115 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
|||
width: 60,
|
||||
filterable: false,
|
||||
defaultSortDesc: true,
|
||||
show: !noSqlMode && tableColumnSelectionHandler.showColumn('Replicas'),
|
||||
show: !noSqlMode && hiddenColumns.exists('Replicas'),
|
||||
},
|
||||
{
|
||||
Header: 'Is published',
|
||||
id: 'is_published',
|
||||
accessor: row => String(Boolean(row.is_published)),
|
||||
Filter: makeBooleanFilter(),
|
||||
show: !noSqlMode && tableColumnSelectionHandler.showColumn('Is published'),
|
||||
show: !noSqlMode && hiddenColumns.exists('Is published'),
|
||||
},
|
||||
{
|
||||
Header: 'Is realtime',
|
||||
id: 'is_realtime',
|
||||
accessor: row => String(Boolean(row.is_realtime)),
|
||||
Filter: makeBooleanFilter(),
|
||||
show: !noSqlMode && tableColumnSelectionHandler.showColumn('Is realtime'),
|
||||
show: !noSqlMode && hiddenColumns.exists('Is realtime'),
|
||||
},
|
||||
{
|
||||
Header: 'Is available',
|
||||
id: 'is_available',
|
||||
accessor: row => String(Boolean(row.is_available)),
|
||||
Filter: makeBooleanFilter(),
|
||||
show: !noSqlMode && tableColumnSelectionHandler.showColumn('Is available'),
|
||||
show: !noSqlMode && hiddenColumns.exists('Is available'),
|
||||
},
|
||||
{
|
||||
Header: 'Is overshadowed',
|
||||
id: 'is_overshadowed',
|
||||
accessor: row => String(Boolean(row.is_overshadowed)),
|
||||
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}
|
||||
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() {
|
||||
const { goToQuery, noSqlMode } = this.props;
|
||||
const { tableColumnSelectionHandler } = this;
|
||||
renderTerminateSegmentAction() {
|
||||
const { terminateSegmentId, terminateDatasourceId } = this.state;
|
||||
|
||||
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">
|
||||
<ViewControlBar label="Segments">
|
||||
<RefreshButton
|
||||
|
@ -472,12 +545,23 @@ export class SegmentsView extends React.PureComponent<SegmentsViewProps, Segment
|
|||
)}
|
||||
<TableColumnSelector
|
||||
columns={noSqlMode ? tableColumnsNoSql : tableColumns}
|
||||
onChange={column => tableColumnSelectionHandler.changeTableColumnSelector(column)}
|
||||
tableColumnsHidden={tableColumnSelectionHandler.hiddenColumns}
|
||||
onChange={column => this.setState({ hiddenColumns: hiddenColumns.toggle(column) })}
|
||||
tableColumnsHidden={hiddenColumns.storedArray}
|
||||
/>
|
||||
</ViewControlBar>
|
||||
{this.renderSegmentsTable()}
|
||||
</div>
|
||||
{this.renderTerminateSegmentAction()}
|
||||
{segmentTableActionDialogId && (
|
||||
<SegmentTableActionDialog
|
||||
segmentId={segmentTableActionDialogId}
|
||||
dataSourceId={datasourceTableActionDialogId}
|
||||
actions={actions}
|
||||
onClose={() => this.setState({ segmentTableActionDialogId: null })}
|
||||
isOpen
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,9 +34,9 @@ import {
|
|||
lookupBy,
|
||||
queryDruidSql,
|
||||
QueryManager,
|
||||
TableColumnSelectionHandler,
|
||||
} from '../../utils';
|
||||
import { BasicAction } from '../../utils/basic-action';
|
||||
import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array';
|
||||
import { deepGet } from '../../utils/object-change';
|
||||
|
||||
import './servers-view.scss';
|
||||
|
@ -90,6 +90,8 @@ export interface ServersViewState {
|
|||
|
||||
middleManagerDisableWorkerHost: string | null;
|
||||
middleManagerEnableWorkerHost: string | null;
|
||||
|
||||
hiddenColumns: LocalStorageBackedArray<string>;
|
||||
}
|
||||
|
||||
interface ServerQueryResultRow {
|
||||
|
@ -132,7 +134,6 @@ interface ServerResultRow
|
|||
|
||||
export class ServersView extends React.PureComponent<ServersViewProps, ServersViewState> {
|
||||
private serverQueryManager: QueryManager<string, ServerQueryResultRow[]>;
|
||||
private serverTableColumnSelectionHandler: TableColumnSelectionHandler;
|
||||
|
||||
constructor(props: ServersViewProps, context: any) {
|
||||
super(props, context);
|
||||
|
@ -145,12 +146,11 @@ export class ServersView extends React.PureComponent<ServersViewProps, ServersVi
|
|||
|
||||
middleManagerDisableWorkerHost: null,
|
||||
middleManagerEnableWorkerHost: null,
|
||||
};
|
||||
|
||||
this.serverTableColumnSelectionHandler = new TableColumnSelectionHandler(
|
||||
hiddenColumns: new LocalStorageBackedArray<string>(
|
||||
LocalStorageKeys.SERVER_TABLE_COLUMN_SELECTION,
|
||||
() => this.setState({}),
|
||||
);
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
static async getServers(): Promise<ServerQueryResultRow[]> {
|
||||
|
@ -172,6 +172,8 @@ export class ServersView extends React.PureComponent<ServersViewProps, ServersVi
|
|||
|
||||
componentDidMount(): void {
|
||||
const { noSqlMode } = this.props;
|
||||
const { hiddenColumns } = this.state;
|
||||
|
||||
this.serverQueryManager = new QueryManager({
|
||||
processQuery: async (query: string) => {
|
||||
let servers: ServerQueryResultRow[];
|
||||
|
@ -260,8 +262,14 @@ ORDER BY "rank" DESC, "server" DESC`);
|
|||
}
|
||||
|
||||
renderServersTable() {
|
||||
const { servers, serversLoading, serversError, serverFilter, groupServersBy } = this.state;
|
||||
const { serverTableColumnSelectionHandler } = this;
|
||||
const {
|
||||
servers,
|
||||
serversLoading,
|
||||
serversError,
|
||||
serverFilter,
|
||||
groupServersBy,
|
||||
hiddenColumns,
|
||||
} = this.state;
|
||||
|
||||
const fillIndicator = (value: number) => {
|
||||
let formattedValue = (value * 100).toFixed(1);
|
||||
|
@ -294,7 +302,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
|||
accessor: 'server',
|
||||
width: 300,
|
||||
Aggregated: row => '',
|
||||
show: serverTableColumnSelectionHandler.showColumn('Server'),
|
||||
show: hiddenColumns.exists('Server'),
|
||||
},
|
||||
{
|
||||
Header: 'Type',
|
||||
|
@ -312,7 +320,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
|||
</a>
|
||||
);
|
||||
},
|
||||
show: serverTableColumnSelectionHandler.showColumn('Type'),
|
||||
show: hiddenColumns.exists('Type'),
|
||||
},
|
||||
{
|
||||
Header: 'Tier',
|
||||
|
@ -329,13 +337,13 @@ ORDER BY "rank" DESC, "server" DESC`);
|
|||
</a>
|
||||
);
|
||||
},
|
||||
show: serverTableColumnSelectionHandler.showColumn('Tier'),
|
||||
show: hiddenColumns.exists('Tier'),
|
||||
},
|
||||
{
|
||||
Header: 'Host',
|
||||
accessor: 'host',
|
||||
Aggregated: () => '',
|
||||
show: serverTableColumnSelectionHandler.showColumn('Host'),
|
||||
show: hiddenColumns.exists('Host'),
|
||||
},
|
||||
{
|
||||
Header: 'Port',
|
||||
|
@ -351,7 +359,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
|||
return ports.join(', ') || 'No port';
|
||||
},
|
||||
Aggregated: () => '',
|
||||
show: serverTableColumnSelectionHandler.showColumn('Port'),
|
||||
show: hiddenColumns.exists('Port'),
|
||||
},
|
||||
{
|
||||
Header: 'Curr size',
|
||||
|
@ -370,7 +378,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
|||
if (row.value === null) return '';
|
||||
return formatBytes(row.value);
|
||||
},
|
||||
show: serverTableColumnSelectionHandler.showColumn('Curr size'),
|
||||
show: hiddenColumns.exists('Curr size'),
|
||||
},
|
||||
{
|
||||
Header: 'Max size',
|
||||
|
@ -389,7 +397,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
|||
if (row.value === null) return '';
|
||||
return formatBytes(row.value);
|
||||
},
|
||||
show: serverTableColumnSelectionHandler.showColumn('Max size'),
|
||||
show: hiddenColumns.exists('Max size'),
|
||||
},
|
||||
{
|
||||
Header: 'Usage',
|
||||
|
@ -447,7 +455,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
|||
return '';
|
||||
}
|
||||
},
|
||||
show: serverTableColumnSelectionHandler.showColumn('Usage'),
|
||||
show: hiddenColumns.exists('Usage'),
|
||||
},
|
||||
{
|
||||
Header: 'Detail',
|
||||
|
@ -509,7 +517,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
|||
segmentsToDropSize,
|
||||
);
|
||||
},
|
||||
show: serverTableColumnSelectionHandler.showColumn('Detail'),
|
||||
show: hiddenColumns.exists('Detail'),
|
||||
},
|
||||
{
|
||||
Header: ActionCell.COLUMN_LABEL,
|
||||
|
@ -523,7 +531,7 @@ ORDER BY "rank" DESC, "server" DESC`);
|
|||
const workerActions = this.getWorkerActions(row.value.host, disabled);
|
||||
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() {
|
||||
const { goToQuery, noSqlMode } = this.props;
|
||||
const { groupServersBy } = this.state;
|
||||
const { serverTableColumnSelectionHandler } = this;
|
||||
const { groupServersBy, hiddenColumns } = this.state;
|
||||
|
||||
return (
|
||||
<div className="servers-view app-view">
|
||||
|
@ -652,8 +659,8 @@ ORDER BY "rank" DESC, "server" DESC`);
|
|||
)}
|
||||
<TableColumnSelector
|
||||
columns={serverTableColumns}
|
||||
onChange={column => serverTableColumnSelectionHandler.changeTableColumnSelector(column)}
|
||||
tableColumnsHidden={serverTableColumnSelectionHandler.hiddenColumns}
|
||||
onChange={column => this.setState({ hiddenColumns: hiddenColumns.toggle(column) })}
|
||||
tableColumnsHidden={hiddenColumns.storedArray}
|
||||
/>
|
||||
</ViewControlBar>
|
||||
{this.renderServersTable()}
|
||||
|
|
|
@ -53,9 +53,9 @@ import {
|
|||
localStorageSet,
|
||||
queryDruidSql,
|
||||
QueryManager,
|
||||
TableColumnSelectionHandler,
|
||||
} from '../../utils';
|
||||
import { BasicAction } from '../../utils/basic-action';
|
||||
import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array';
|
||||
|
||||
import './tasks-view.scss';
|
||||
|
||||
|
@ -114,6 +114,8 @@ export interface TasksViewState {
|
|||
taskTableActionDialogActions: BasicAction[];
|
||||
supervisorTableActionDialogId: string | null;
|
||||
supervisorTableActionDialogActions: BasicAction[];
|
||||
hiddenTaskColumns: LocalStorageBackedArray<string>;
|
||||
hiddenSupervisorColumns: LocalStorageBackedArray<string>;
|
||||
}
|
||||
|
||||
interface TaskQueryResultRow {
|
||||
|
@ -172,8 +174,6 @@ function stateToColor(status: string): string {
|
|||
export class TasksView extends React.PureComponent<TasksViewProps, TasksViewState> {
|
||||
private supervisorQueryManager: QueryManager<string, SupervisorQueryResultRow[]>;
|
||||
private taskQueryManager: QueryManager<string, TaskQueryResultRow[]>;
|
||||
private supervisorTableColumnSelectionHandler: TableColumnSelectionHandler;
|
||||
private taskTableColumnSelectionHandler: TableColumnSelectionHandler;
|
||||
static statusRanking: Record<string, number> = {
|
||||
RUNNING: 4,
|
||||
PENDING: 3,
|
||||
|
@ -192,6 +192,7 @@ export class TasksView extends React.PureComponent<TasksViewProps, TasksViewStat
|
|||
resumeSupervisorId: null,
|
||||
suspendSupervisorId: null,
|
||||
resetSupervisorId: null,
|
||||
supervisorTableActionDialogId: null,
|
||||
terminateSupervisorId: null,
|
||||
|
||||
tasksLoading: true,
|
||||
|
@ -210,19 +211,15 @@ export class TasksView extends React.PureComponent<TasksViewProps, TasksViewStat
|
|||
taskTableActionDialogId: null,
|
||||
taskTableActionDialogStatus: null,
|
||||
taskTableActionDialogActions: [],
|
||||
supervisorTableActionDialogId: null,
|
||||
supervisorTableActionDialogActions: [],
|
||||
};
|
||||
|
||||
this.supervisorTableColumnSelectionHandler = new TableColumnSelectionHandler(
|
||||
LocalStorageKeys.SUPERVISOR_TABLE_COLUMN_SELECTION,
|
||||
() => this.setState({}),
|
||||
);
|
||||
|
||||
this.taskTableColumnSelectionHandler = new TableColumnSelectionHandler(
|
||||
hiddenTaskColumns: new LocalStorageBackedArray<string>(
|
||||
LocalStorageKeys.TASK_TABLE_COLUMN_SELECTION,
|
||||
() => this.setState({}),
|
||||
);
|
||||
),
|
||||
hiddenSupervisorColumns: new LocalStorageBackedArray<string>(
|
||||
LocalStorageKeys.SUPERVISOR_TABLE_COLUMN_SELECTION,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
static parseTasks = (data: any[]): TaskQueryResultRow[] => {
|
||||
|
@ -248,6 +245,7 @@ export class TasksView extends React.PureComponent<TasksViewProps, TasksViewStat
|
|||
|
||||
componentDidMount(): void {
|
||||
const { noSqlMode } = this.props;
|
||||
|
||||
this.supervisorQueryManager = new QueryManager({
|
||||
processQuery: async (query: string) => {
|
||||
const resp = await axios.get('/druid/indexer/v1/supervisor?full');
|
||||
|
@ -521,8 +519,12 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
}
|
||||
|
||||
renderSupervisorTable() {
|
||||
const { supervisors, supervisorsLoading, supervisorsError } = this.state;
|
||||
const { supervisorTableColumnSelectionHandler } = this;
|
||||
const {
|
||||
supervisors,
|
||||
supervisorsLoading,
|
||||
supervisorsError,
|
||||
hiddenSupervisorColumns,
|
||||
} = this.state;
|
||||
return (
|
||||
<>
|
||||
<ReactTable
|
||||
|
@ -540,7 +542,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
id: 'datasource',
|
||||
accessor: 'id',
|
||||
width: 300,
|
||||
show: supervisorTableColumnSelectionHandler.showColumn('Datasource'),
|
||||
show: hiddenSupervisorColumns.exists('Datasource'),
|
||||
},
|
||||
{
|
||||
Header: 'Type',
|
||||
|
@ -552,7 +554,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
if (!tuningConfig) return '';
|
||||
return tuningConfig.type;
|
||||
},
|
||||
show: supervisorTableColumnSelectionHandler.showColumn('Type'),
|
||||
show: hiddenSupervisorColumns.exists('Type'),
|
||||
},
|
||||
{
|
||||
Header: 'Topic/Stream',
|
||||
|
@ -564,7 +566,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
if (!ioConfig) return '';
|
||||
return ioConfig.topic || ioConfig.stream || '';
|
||||
},
|
||||
show: supervisorTableColumnSelectionHandler.showColumn('Topic/Stream'),
|
||||
show: hiddenSupervisorColumns.exists('Topic/Stream'),
|
||||
},
|
||||
{
|
||||
Header: 'Status',
|
||||
|
@ -582,7 +584,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
</span>
|
||||
);
|
||||
},
|
||||
show: supervisorTableColumnSelectionHandler.showColumn('Status'),
|
||||
show: hiddenSupervisorColumns.exists('Status'),
|
||||
},
|
||||
{
|
||||
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() {
|
||||
const { goToMiddleManager } = this.props;
|
||||
const { tasks, tasksLoading, tasksError, taskFilter, groupTasksBy } = this.state;
|
||||
const { taskTableColumnSelectionHandler } = this;
|
||||
const {
|
||||
tasks,
|
||||
tasksLoading,
|
||||
tasksError,
|
||||
taskFilter,
|
||||
groupTasksBy,
|
||||
hiddenTaskColumns,
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -690,7 +698,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
accessor: 'task_id',
|
||||
width: 300,
|
||||
Aggregated: row => '',
|
||||
show: taskTableColumnSelectionHandler.showColumn('Task ID'),
|
||||
show: hiddenTaskColumns.exists('Task ID'),
|
||||
},
|
||||
{
|
||||
Header: 'Type',
|
||||
|
@ -707,7 +715,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
</a>
|
||||
);
|
||||
},
|
||||
show: taskTableColumnSelectionHandler.showColumn('Type'),
|
||||
show: hiddenTaskColumns.exists('Type'),
|
||||
},
|
||||
{
|
||||
Header: 'Datasource',
|
||||
|
@ -724,8 +732,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
</a>
|
||||
);
|
||||
},
|
||||
show: taskTableColumnSelectionHandler.showColumn('Datasource'),
|
||||
show: hiddenTaskColumns.exists('Datasource'),
|
||||
},
|
||||
|
||||
{
|
||||
Header: 'Location',
|
||||
accessor: 'location',
|
||||
|
@ -733,14 +742,14 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
filterMethod: (filter: Filter, row: any) => {
|
||||
return booleanCustomTableFilter(filter, row.location);
|
||||
},
|
||||
show: taskTableColumnSelectionHandler.showColumn('Location'),
|
||||
show: hiddenTaskColumns.exists('Location'),
|
||||
},
|
||||
{
|
||||
Header: 'Created time',
|
||||
accessor: 'created_time',
|
||||
width: 120,
|
||||
Aggregated: row => '',
|
||||
show: taskTableColumnSelectionHandler.showColumn('Created time'),
|
||||
show: hiddenTaskColumns.exists('Created time'),
|
||||
},
|
||||
{
|
||||
Header: 'Status',
|
||||
|
@ -800,7 +809,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
filterMethod: (filter: Filter, row: any) => {
|
||||
return booleanCustomTableFilter(filter, row.status.status);
|
||||
},
|
||||
show: taskTableColumnSelectionHandler.showColumn('Status'),
|
||||
show: hiddenTaskColumns.exists('Status'),
|
||||
},
|
||||
{
|
||||
Header: 'Duration',
|
||||
|
@ -808,7 +817,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
filterable: false,
|
||||
Cell: row => (row.value > 0 ? formatDuration(row.value) : ''),
|
||||
Aggregated: () => '',
|
||||
show: taskTableColumnSelectionHandler.showColumn('Duration'),
|
||||
show: hiddenTaskColumns.exists('Duration'),
|
||||
},
|
||||
{
|
||||
Header: ActionCell.COLUMN_LABEL,
|
||||
|
@ -836,7 +845,7 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
);
|
||||
},
|
||||
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,
|
||||
supervisorTableActionDialogActions,
|
||||
taskTableActionDialogStatus,
|
||||
hiddenSupervisorColumns,
|
||||
hiddenTaskColumns,
|
||||
} = this.state;
|
||||
const { supervisorTableColumnSelectionHandler, taskTableColumnSelectionHandler } = this;
|
||||
|
||||
const submitSupervisorMenu = (
|
||||
<Menu>
|
||||
|
@ -915,9 +925,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
<TableColumnSelector
|
||||
columns={supervisorTableColumns}
|
||||
onChange={column =>
|
||||
supervisorTableColumnSelectionHandler.changeTableColumnSelector(column)
|
||||
this.setState({ hiddenSupervisorColumns: hiddenSupervisorColumns.toggle(column) })
|
||||
}
|
||||
tableColumnsHidden={supervisorTableColumnSelectionHandler.hiddenColumns}
|
||||
tableColumnsHidden={hiddenSupervisorColumns.storedArray}
|
||||
/>
|
||||
</ViewControlBar>
|
||||
{this.renderSupervisorTable()}
|
||||
|
@ -968,9 +978,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
|
|||
<TableColumnSelector
|
||||
columns={taskTableColumns}
|
||||
onChange={column =>
|
||||
taskTableColumnSelectionHandler.changeTableColumnSelector(column)
|
||||
this.setState({ hiddenTaskColumns: hiddenTaskColumns.toggle(column) })
|
||||
}
|
||||
tableColumnsHidden={taskTableColumnSelectionHandler.hiddenColumns}
|
||||
tableColumnsHidden={hiddenTaskColumns.storedArray}
|
||||
/>
|
||||
</ViewControlBar>
|
||||
{this.renderTaskTable()}
|
||||
|
|
Loading…
Reference in New Issue