From a171b4a399d395b673a6f84854fef90d0f834a4e Mon Sep 17 00:00:00 2001
From: mcbrewster <37322608+mcbrewster@users.noreply.github.com>
Date: Tue, 25 Jun 2019 20:14:06 -0700
Subject: [PATCH] Web-console: Add action column to segments view (#7954)
* add actions column to segments view
* add sements action column
---
.idea/inspectionProfiles/Druid.xml | 2 +-
.../segment-table-action-dialog.spec.tsx.snap | 171 +++++
.../segment-table-action-dialog.spec.tsx | 39 ++
.../segment-table-action-dialog.tsx | 78 +++
web-console/src/utils/index.tsx | 1 -
.../src/utils/local-storage-backed-array.tsx | 65 ++
.../utils/table-column-selection-handler.tsx | 61 --
.../views/datasource-view/datasource-view.tsx | 36 +-
.../src/views/lookups-view/lookups-view.tsx | 45 +-
.../__snapshots__/segments-view.spec.tsx.snap | 601 +++++++++---------
.../src/views/segments-view/segments-view.tsx | 218 +++++--
.../src/views/servers-view/servers-view.tsx | 53 +-
.../src/views/task-view/tasks-view.tsx | 82 +--
13 files changed, 935 insertions(+), 517 deletions(-)
create mode 100644 web-console/src/dialogs/segments-table-action-dialog/__snapshots__/segment-table-action-dialog.spec.tsx.snap
create mode 100644 web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.spec.tsx
create mode 100644 web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.tsx
create mode 100644 web-console/src/utils/local-storage-backed-array.tsx
delete mode 100644 web-console/src/utils/table-column-selection-handler.tsx
diff --git a/.idea/inspectionProfiles/Druid.xml b/.idea/inspectionProfiles/Druid.xml
index ed890c86ec7..4de1e05a5b6 100644
--- a/.idea/inspectionProfiles/Druid.xml
+++ b/.idea/inspectionProfiles/Druid.xml
@@ -405,4 +405,4 @@
-
+
\ No newline at end of file
diff --git a/web-console/src/dialogs/segments-table-action-dialog/__snapshots__/segment-table-action-dialog.spec.tsx.snap b/web-console/src/dialogs/segments-table-action-dialog/__snapshots__/segment-table-action-dialog.spec.tsx.snap
new file mode 100644
index 00000000000..6e695a9e874
--- /dev/null
+++ b/web-console/src/dialogs/segments-table-action-dialog/__snapshots__/segment-table-action-dialog.spec.tsx.snap
@@ -0,0 +1,171 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`task table action dialog matches snapshot 1`] = `
+
+`;
diff --git a/web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.spec.tsx b/web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.spec.tsx
new file mode 100644
index 00000000000..52decd237e2
--- /dev/null
+++ b/web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.spec.tsx
@@ -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 = (
+ null}
+ isOpen
+ />
+ );
+ const { container } = render(taskTableActionDialog, { container: document.body });
+ expect(container.firstChild).toMatchSnapshot();
+ });
+});
diff --git a/web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.tsx b/web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.tsx
new file mode 100644
index 00000000000..1ae592b7173
--- /dev/null
+++ b/web-console/src/dialogs/segments-table-action-dialog/segment-table-action-dialog.tsx
@@ -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 (
+
+ {activeTab === 'metadata' && (
+
+ )}
+
+ );
+ }
+}
diff --git a/web-console/src/utils/index.tsx b/web-console/src/utils/index.tsx
index 0a4c9f954db..f1d18b0614b 100644
--- a/web-console/src/utils/index.tsx
+++ b/web-console/src/utils/index.tsx
@@ -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';
diff --git a/web-console/src/utils/local-storage-backed-array.tsx b/web-console/src/utils/local-storage-backed-array.tsx
new file mode 100644
index 00000000000..daf227d5fd9
--- /dev/null
+++ b/web-console/src/utils/local-storage-backed-array.tsx
@@ -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 {
+ 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 {
+ let toggledArray;
+ if (this.storedArray.includes(value)) {
+ toggledArray = this.storedArray.filter(c => c !== value);
+ } else {
+ toggledArray = this.storedArray.concat(value);
+ }
+ return new LocalStorageBackedArray(this.key, toggledArray);
+ }
+
+ exists(value: T): boolean {
+ return !this.storedArray.includes(value);
+ }
+}
diff --git a/web-console/src/utils/table-column-selection-handler.tsx b/web-console/src/utils/table-column-selection-handler.tsx
deleted file mode 100644
index 4decef59401..00000000000
--- a/web-console/src/utils/table-column-selection-handler.tsx
+++ /dev/null
@@ -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);
- }
-}
diff --git a/web-console/src/views/datasource-view/datasource-view.tsx b/web-console/src/views/datasource-view/datasource-view.tsx
index bf7f60a3f6b..db14776eb24 100644
--- a/web-console/src/views/datasource-view/datasource-view.tsx
+++ b/web-console/src/views/datasource-view/datasource-view.tsx
@@ -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;
}
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: '',
+ hiddenColumns: new LocalStorageBackedArray(
+ LocalStorageKeys.DATASOURCE_TABLE_COLUMN_SELECTION,
+ ),
};
-
- this.tableColumnSelectionHandler = new TableColumnSelectionHandler(
- 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`);
);
},
- 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`);
);
},
- show: tableColumnSelectionHandler.showColumn('Retention'),
+ show: hiddenColumns.exists('Retention'),
},
{
Header: 'Compaction',
@@ -730,7 +729,7 @@ GROUP BY 1`);
);
},
- 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 ;
},
- 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 (
@@ -801,8 +799,8 @@ GROUP BY 1`);
/>
tableColumnSelectionHandler.changeTableColumnSelector(column)}
- tableColumnsHidden={tableColumnSelectionHandler.hiddenColumns}
+ onChange={column => this.setState({ hiddenColumns: hiddenColumns.toggle(column) })}
+ tableColumnsHidden={hiddenColumns.storedArray}
/>
{this.renderDatasourceTable()}
diff --git a/web-console/src/views/lookups-view/lookups-view.tsx b/web-console/src/views/lookups-view/lookups-view.tsx
index 8efc35a1893..182ea931de4 100644
--- a/web-console/src/views/lookups-view/lookups-view.tsx
+++ b/web-console/src/views/lookups-view/lookups-view.tsx
@@ -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;
}
export class LookupsView extends React.PureComponent {
private lookupsGetQueryManager: QueryManager;
- private tableColumnSelectionHandler: TableColumnSelectionHandler;
constructor(props: LookupsViewProps, context: any) {
super(props, context);
@@ -80,11 +77,11 @@ export class LookupsView extends React.PureComponent(
+ LocalStorageKeys.LOOKUP_TABLE_COLUMN_SELECTION,
+ ),
};
- this.tableColumnSelectionHandler = new TableColumnSelectionHandler(
- LocalStorageKeys.LOOKUP_TABLE_COLUMN_SELECTION,
- () => this.setState({}),
- );
}
componentDidMount(): void {
@@ -266,8 +263,13 @@ export class LookupsView extends React.PureComponent 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;
},
- show: tableColumnSelectionHandler.showColumn(ActionCell.COLUMN_LABEL),
+ show: hiddenColumns.exists(ActionCell.COLUMN_LABEL),
},
]}
defaultPageSize={50}
@@ -368,8 +370,7 @@ export class LookupsView extends React.PureComponent
@@ -388,8 +389,8 @@ export class LookupsView extends React.PureComponent tableColumnSelectionHandler.changeTableColumnSelector(column)}
- tableColumnsHidden={tableColumnSelectionHandler.hiddenColumns}
+ onChange={column => this.setState({ hiddenColumns: hiddenColumns.toggle(column) })}
+ tableColumnsHidden={hiddenColumns.storedArray}
/>
{this.renderLookupsTable()}
diff --git a/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap b/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap
index dae9fbe2c8e..9be740804b4 100755
--- a/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap
+++ b/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap
@@ -1,297 +1,324 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`segments-view matches snapshot 1`] = `
-
+
+
+ Are you sure you want to drop segment 'null'?
+
+
+ This action is not reversible.
+
+
+
`;
diff --git a/web-console/src/views/segments-view/segments-view.tsx b/web-console/src/views/segments-view/segments-view.tsx
index 942caa2c2de..5c0b3975b50 100644
--- a/web-console/src/views/segments-view/segments-view.tsx
+++ b/web-console/src/views/segments-view/segments-view.tsx
@@ -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;
}
interface QueryAndSkip {
@@ -105,7 +114,6 @@ interface SegmentQueryResultRow {
export class SegmentsView extends React.PureComponent {
private segmentsSqlQueryManager: QueryManager;
private segmentsJsonQueryManager: QueryManager;
- private tableColumnSelectionHandler: TableColumnSelectionHandler;
constructor(props: SegmentsViewProps, context: any) {
super(props, context);
@@ -115,10 +123,18 @@ export class SegmentsView extends React.PureComponent(
+ LocalStorageKeys.SEGMENT_TABLE_COLUMN_SELECTION,
+ ),
};
this.segmentsSqlQueryManager = new QueryManager({
@@ -185,11 +201,6 @@ export class SegmentsView extends React.PureComponent this.setState({}),
- );
}
componentDidMount(): void {
@@ -274,10 +285,20 @@ export class SegmentsView extends React.PureComponent 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 (
);
},
- show: tableColumnSelectionHandler.showColumn('Datasource'),
+ show: hiddenColumns.exists('Datasource'),
},
{
Header: 'Start',
@@ -338,7 +359,7 @@ export class SegmentsView extends React.PureComponent
);
},
- show: tableColumnSelectionHandler.showColumn('Start'),
+ show: hiddenColumns.exists('Start'),
},
{
Header: 'End',
@@ -357,21 +378,21 @@ export class SegmentsView extends React.PureComponent
);
},
- 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 formatBytes(row.value),
- show: tableColumnSelectionHandler.showColumn('Size'),
+ show: hiddenColumns.exists('Size'),
},
{
Header: 'Num rows',
@@ -387,7 +408,7 @@ export class SegmentsView extends React.PureComponent formatNumber(row.value),
- show: !noSqlMode && tableColumnSelectionHandler.showColumn('Num rows'),
+ show: !noSqlMode && hiddenColumns.exists('Num rows'),
},
{
Header: 'Replicas',
@@ -395,89 +416,152 @@ export class SegmentsView extends React.PureComponent 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 (
+ {
+ 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 (
-
-
Segment ID
-
{original.segment_id}
-
{`Dimensions (${dimensions.length})`}
-
{dimensions.join(', ') || 'No dimension'}
-
{`Metrics (${metrics.length})`}
-
{metrics.join(', ') || 'No metrics'}
-
- );
- }}
/>
);
}
- render() {
- const { goToQuery, noSqlMode } = this.props;
- const { tableColumnSelectionHandler } = this;
+ renderTerminateSegmentAction() {
+ const { terminateSegmentId, terminateDatasourceId } = this.state;
return (
-
-
-
- noSqlMode
- ? this.segmentsJsonQueryManager.rerunLastQueryInBackground(auto)
- : this.segmentsSqlQueryManager.rerunLastQueryInBackground(auto)
- }
- localStorageKey={LocalStorageKeys.SEGMENTS_REFRESH_RATE}
- />
- {!noSqlMode && (
-
- {this.renderSegmentsTable()}
-
+ )}
+ >
);
}
}
diff --git a/web-console/src/views/servers-view/servers-view.tsx b/web-console/src/views/servers-view/servers-view.tsx
index af02784c723..e8a0433ce3c 100644
--- a/web-console/src/views/servers-view/servers-view.tsx
+++ b/web-console/src/views/servers-view/servers-view.tsx
@@ -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;
}
interface ServerQueryResultRow {
@@ -132,7 +134,6 @@ interface ServerResultRow
export class ServersView extends React.PureComponent {
private serverQueryManager: QueryManager;
- private serverTableColumnSelectionHandler: TableColumnSelectionHandler;
constructor(props: ServersViewProps, context: any) {
super(props, context);
@@ -145,12 +146,11 @@ export class ServersView extends React.PureComponent this.setState({}),
- );
+ hiddenColumns: new LocalStorageBackedArray(
+ LocalStorageKeys.SERVER_TABLE_COLUMN_SELECTION,
+ ),
+ };
}
static async getServers(): Promise {
@@ -172,6 +172,8 @@ export class ServersView extends React.PureComponent {
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`);
);
},
- show: serverTableColumnSelectionHandler.showColumn('Type'),
+ show: hiddenColumns.exists('Type'),
},
{
Header: 'Tier',
@@ -329,13 +337,13 @@ ORDER BY "rank" DESC, "server" DESC`);
);
},
- 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 ;
},
- 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 (
@@ -652,8 +659,8 @@ ORDER BY "rank" DESC, "server" DESC`);
)}
serverTableColumnSelectionHandler.changeTableColumnSelector(column)}
- tableColumnsHidden={serverTableColumnSelectionHandler.hiddenColumns}
+ onChange={column => this.setState({ hiddenColumns: hiddenColumns.toggle(column) })}
+ tableColumnsHidden={hiddenColumns.storedArray}
/>
{this.renderServersTable()}
diff --git a/web-console/src/views/task-view/tasks-view.tsx b/web-console/src/views/task-view/tasks-view.tsx
index 0f7505d25cd..3ab7293b35a 100644
--- a/web-console/src/views/task-view/tasks-view.tsx
+++ b/web-console/src/views/task-view/tasks-view.tsx
@@ -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;
+ hiddenSupervisorColumns: LocalStorageBackedArray;
}
interface TaskQueryResultRow {
@@ -172,8 +174,6 @@ function stateToColor(status: string): string {
export class TasksView extends React.PureComponent {
private supervisorQueryManager: QueryManager;
private taskQueryManager: QueryManager;
- private supervisorTableColumnSelectionHandler: TableColumnSelectionHandler;
- private taskTableColumnSelectionHandler: TableColumnSelectionHandler;
static statusRanking: Record = {
RUNNING: 4,
PENDING: 3,
@@ -192,6 +192,7 @@ export class TasksView extends React.PureComponent(
+ LocalStorageKeys.TASK_TABLE_COLUMN_SELECTION,
+ ),
+ hiddenSupervisorColumns: new LocalStorageBackedArray(
+ LocalStorageKeys.SUPERVISOR_TABLE_COLUMN_SELECTION,
+ ),
};
-
- this.supervisorTableColumnSelectionHandler = new TableColumnSelectionHandler(
- LocalStorageKeys.SUPERVISOR_TABLE_COLUMN_SELECTION,
- () => this.setState({}),
- );
-
- this.taskTableColumnSelectionHandler = new TableColumnSelectionHandler(
- LocalStorageKeys.TASK_TABLE_COLUMN_SELECTION,
- () => this.setState({}),
- );
}
static parseTasks = (data: any[]): TaskQueryResultRow[] => {
@@ -248,6 +245,7 @@ export class TasksView extends React.PureComponent {
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 (
<>
);
},
- 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`);
);
},
- show: taskTableColumnSelectionHandler.showColumn('Type'),
+ show: hiddenTaskColumns.exists('Type'),
},
{
Header: 'Datasource',
@@ -724,8 +732,9 @@ ORDER BY "rank" DESC, "created_time" DESC`);
);
},
- 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 = (