mirror of https://github.com/apache/druid.git
Web console: Compaction history dialog (#13861)
* initial renames * add comaction history diff * final fixes * update snapshots * use maps * update test
This commit is contained in:
parent
cd4ad5123a
commit
38b6373bf7
|
@ -5693,7 +5693,7 @@ license_category: binary
|
||||||
module: web-console
|
module: web-console
|
||||||
license_name: Apache License version 2.0
|
license_name: Apache License version 2.0
|
||||||
copyright: Imply Data
|
copyright: Imply Data
|
||||||
version: 0.17.4
|
version: 0.17.5
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ export class DatasourcesOverview {
|
||||||
private async openCompactionConfigurationDialog(datasourceName: string): Promise<void> {
|
private async openCompactionConfigurationDialog(datasourceName: string): Promise<void> {
|
||||||
await this.openEditActions(datasourceName);
|
await this.openEditActions(datasourceName);
|
||||||
await this.clickMenuItem('Edit compaction configuration');
|
await this.clickMenuItem('Edit compaction configuration');
|
||||||
await this.page.waitForSelector('div.compaction-dialog');
|
await this.page.waitForSelector('div.compaction-config-dialog');
|
||||||
}
|
}
|
||||||
|
|
||||||
private async clickMenuItem(text: string): Promise<void> {
|
private async clickMenuItem(text: string): Promise<void> {
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
"d3-axis": "^2.1.0",
|
"d3-axis": "^2.1.0",
|
||||||
"d3-scale": "^3.3.0",
|
"d3-scale": "^3.3.0",
|
||||||
"d3-selection": "^2.0.0",
|
"d3-selection": "^2.0.0",
|
||||||
"druid-query-toolkit": "^0.17.4",
|
"druid-query-toolkit": "^0.17.5",
|
||||||
"file-saver": "^2.0.2",
|
"file-saver": "^2.0.2",
|
||||||
"follow-redirects": "^1.14.7",
|
"follow-redirects": "^1.14.7",
|
||||||
"fontsource-open-sans": "^3.0.9",
|
"fontsource-open-sans": "^3.0.9",
|
||||||
|
@ -8542,9 +8542,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/druid-query-toolkit": {
|
"node_modules/druid-query-toolkit": {
|
||||||
"version": "0.17.4",
|
"version": "0.17.5",
|
||||||
"resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.17.4.tgz",
|
"resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.17.5.tgz",
|
||||||
"integrity": "sha512-d/mNJ9ausAfxQaxgGWfP4dHpwcIqjkbdz1NNmTk6DHMbwlskk96MnXrvPsOCmeoKMb0iYVeVtq9CbB1vNaPZ5A==",
|
"integrity": "sha512-N+kqu6xy2Gd3zQwNbBxQXG+qjU7jzbCXvE84uxsIh5gxRbiKAEOsyWtKrWF/DZZ91g/WSIMQih5klkmmcjlVfQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.1"
|
"tslib": "^2.3.1"
|
||||||
},
|
},
|
||||||
|
@ -33236,9 +33236,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"druid-query-toolkit": {
|
"druid-query-toolkit": {
|
||||||
"version": "0.17.4",
|
"version": "0.17.5",
|
||||||
"resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.17.4.tgz",
|
"resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.17.5.tgz",
|
||||||
"integrity": "sha512-d/mNJ9ausAfxQaxgGWfP4dHpwcIqjkbdz1NNmTk6DHMbwlskk96MnXrvPsOCmeoKMb0iYVeVtq9CbB1vNaPZ5A==",
|
"integrity": "sha512-N+kqu6xy2Gd3zQwNbBxQXG+qjU7jzbCXvE84uxsIh5gxRbiKAEOsyWtKrWF/DZZ91g/WSIMQih5klkmmcjlVfQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^2.3.1"
|
"tslib": "^2.3.1"
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@
|
||||||
"d3-axis": "^2.1.0",
|
"d3-axis": "^2.1.0",
|
||||||
"d3-scale": "^3.3.0",
|
"d3-scale": "^3.3.0",
|
||||||
"d3-selection": "^2.0.0",
|
"d3-selection": "^2.0.0",
|
||||||
"druid-query-toolkit": "^0.17.4",
|
"druid-query-toolkit": "^0.17.5",
|
||||||
"file-saver": "^2.0.2",
|
"file-saver": "^2.0.2",
|
||||||
"follow-redirects": "^1.14.7",
|
"follow-redirects": "^1.14.7",
|
||||||
"fontsource-open-sans": "^3.0.9",
|
"fontsource-open-sans": "^3.0.9",
|
||||||
|
|
|
@ -90,7 +90,7 @@ checker.init(
|
||||||
|
|
||||||
Object.assign(packages, extraPackages);
|
Object.assign(packages, extraPackages);
|
||||||
|
|
||||||
const seen = {};
|
const seen = new Map();
|
||||||
const mapped = Object.keys(packages)
|
const mapped = Object.keys(packages)
|
||||||
.sort()
|
.sort()
|
||||||
.map(p => {
|
.map(p => {
|
||||||
|
@ -98,8 +98,8 @@ checker.init(
|
||||||
if (!m) throw new Error(`Malformed name@version`);
|
if (!m) throw new Error(`Malformed name@version`);
|
||||||
const name = m[1];
|
const name = m[1];
|
||||||
if (name === 'web-console') return; // This is me!
|
if (name === 'web-console') return; // This is me!
|
||||||
if (seen[name]) return; // Dedupe
|
if (seen.has(name)) return; // Dedupe
|
||||||
seen[name] = true;
|
seen.set(name, true);
|
||||||
|
|
||||||
const version = m[2];
|
const version = m[2];
|
||||||
const meta = packages[p];
|
const meta = packages[p];
|
||||||
|
|
|
@ -51,6 +51,7 @@ export * from './show-json/show-json';
|
||||||
export * from './show-log/show-log';
|
export * from './show-log/show-log';
|
||||||
export * from './show-value/show-value';
|
export * from './show-value/show-value';
|
||||||
export * from './suggestion-menu/suggestion-menu';
|
export * from './suggestion-menu/suggestion-menu';
|
||||||
|
export * from './supervisor-history-panel/supervisor-history-panel';
|
||||||
export * from './table-cell/table-cell';
|
export * from './table-cell/table-cell';
|
||||||
export * from './table-cell-unparseable/table-cell-unparseable';
|
export * from './table-cell-unparseable/table-cell-unparseable';
|
||||||
export * from './table-clickable-cell/table-clickable-cell';
|
export * from './table-clickable-cell/table-clickable-cell';
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
exports[`ShowValue matches snapshot 1`] = `
|
exports[`ShowValue matches snapshot 1`] = `
|
||||||
<div
|
<div
|
||||||
class="show-json"
|
class="show-value"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="top-actions"
|
class="top-actions"
|
||||||
|
|
|
@ -32,7 +32,7 @@ export interface ShowValueProps {
|
||||||
export const ShowValue = React.memo(function ShowValue(props: ShowValueProps) {
|
export const ShowValue = React.memo(function ShowValue(props: ShowValueProps) {
|
||||||
const { jsonValue, onDiffWithPrevious, downloadFilename } = props;
|
const { jsonValue, onDiffWithPrevious, downloadFilename } = props;
|
||||||
return (
|
return (
|
||||||
<div className="show-json">
|
<div className="show-value">
|
||||||
{(onDiffWithPrevious || downloadFilename) && (
|
{(onDiffWithPrevious || downloadFilename) && (
|
||||||
<div className="top-actions">
|
<div className="top-actions">
|
||||||
<ButtonGroup className="right-buttons">
|
<ButtonGroup className="right-buttons">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`ShowHistory matches snapshot 1`] = `
|
exports[`SupervisorHistoryPanel matches snapshot 1`] = `
|
||||||
<div
|
<div
|
||||||
class="loader"
|
class="loader"
|
||||||
>
|
>
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
@import '../../variables';
|
@import '../../variables';
|
||||||
|
|
||||||
.show-history {
|
.supervisor-history-panel {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -19,11 +19,11 @@
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { ShowHistory } from './show-history';
|
import { SupervisorHistoryPanel } from './supervisor-history-panel';
|
||||||
|
|
||||||
describe('ShowHistory', () => {
|
describe('SupervisorHistoryPanel', () => {
|
||||||
it('matches snapshot', () => {
|
it('matches snapshot', () => {
|
||||||
const showJson = <ShowHistory endpoint="test" downloadFilenamePrefix="test" />;
|
const showJson = <SupervisorHistoryPanel supervisorId="test" />;
|
||||||
const { container } = render(showJson);
|
const { container } = render(showJson);
|
||||||
expect(container.firstChild).toMatchSnapshot();
|
expect(container.firstChild).toMatchSnapshot();
|
||||||
});
|
});
|
|
@ -29,29 +29,34 @@ import { deepSet } from '../../utils';
|
||||||
import { Loader } from '../loader/loader';
|
import { Loader } from '../loader/loader';
|
||||||
import { ShowValue } from '../show-value/show-value';
|
import { ShowValue } from '../show-value/show-value';
|
||||||
|
|
||||||
import './show-history.scss';
|
import './supervisor-history-panel.scss';
|
||||||
|
|
||||||
export interface VersionSpec {
|
export interface SupervisorHistoryEntry {
|
||||||
version: string;
|
version: string;
|
||||||
spec: IngestionSpec;
|
spec: IngestionSpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShowHistoryProps {
|
export interface SupervisorHistoryPanelProps {
|
||||||
endpoint: string;
|
supervisorId: string;
|
||||||
downloadFilenamePrefix?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ShowHistory = React.memo(function ShowHistory(props: ShowHistoryProps) {
|
export const SupervisorHistoryPanel = React.memo(function SupervisorHistoryPanel(
|
||||||
const { downloadFilenamePrefix, endpoint } = props;
|
props: SupervisorHistoryPanelProps,
|
||||||
|
) {
|
||||||
|
const { supervisorId } = props;
|
||||||
|
|
||||||
const [historyState] = useQueryManager<string, VersionSpec[]>({
|
|
||||||
processQuery: async (endpoint: string) => {
|
|
||||||
const resp = await Api.instance.get(endpoint);
|
|
||||||
return resp.data.map((vs: VersionSpec) => deepSet(vs, 'spec', cleanSpec(vs.spec, true)));
|
|
||||||
},
|
|
||||||
initQuery: endpoint,
|
|
||||||
});
|
|
||||||
const [diffIndex, setDiffIndex] = useState(-1);
|
const [diffIndex, setDiffIndex] = useState(-1);
|
||||||
|
const [historyState] = useQueryManager<string, SupervisorHistoryEntry[]>({
|
||||||
|
initQuery: supervisorId,
|
||||||
|
processQuery: async supervisorId => {
|
||||||
|
const resp = await Api.instance.get(
|
||||||
|
`/druid/indexer/v1/supervisor/${Api.encodePath(supervisorId)}/history`,
|
||||||
|
);
|
||||||
|
return resp.data.map((vs: SupervisorHistoryEntry) =>
|
||||||
|
deepSet(vs, 'spec', cleanSpec(vs.spec, true)),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (historyState.loading) return <Loader />;
|
if (historyState.loading) return <Loader />;
|
||||||
|
|
||||||
|
@ -59,21 +64,21 @@ export const ShowHistory = React.memo(function ShowHistory(props: ShowHistoryPro
|
||||||
if (!historyData) return null;
|
if (!historyData) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="show-history">
|
<div className="supervisor-history-panel">
|
||||||
<Tabs animate renderActiveTabPanelOnly vertical defaultSelectedTabId={0}>
|
<Tabs animate renderActiveTabPanelOnly vertical defaultSelectedTabId={0}>
|
||||||
{historyData.map((pastSupervisor, i) => (
|
{historyData.map((pastSupervisor, i) => (
|
||||||
<Tab
|
<Tab
|
||||||
id={i}
|
id={i}
|
||||||
key={i}
|
key={i}
|
||||||
title={pastSupervisor.version}
|
title={pastSupervisor.version}
|
||||||
|
panelClassName="panel"
|
||||||
panel={
|
panel={
|
||||||
<ShowValue
|
<ShowValue
|
||||||
jsonValue={JSONBig.stringify(pastSupervisor.spec, undefined, 2)}
|
jsonValue={JSONBig.stringify(pastSupervisor.spec, undefined, 2)}
|
||||||
onDiffWithPrevious={i < historyData.length - 1 ? () => setDiffIndex(i) : undefined}
|
onDiffWithPrevious={i < historyData.length - 1 ? () => setDiffIndex(i) : undefined}
|
||||||
downloadFilename={`${downloadFilenamePrefix}-version-${pastSupervisor.version}.json`}
|
downloadFilename={`supervisor-${supervisorId}-version-${pastSupervisor.version}.json`}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
panelClassName="panel"
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<Tabs.Expander />
|
<Tabs.Expander />
|
|
@ -1,9 +1,9 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`CompactionDialog matches snapshot with compactionConfig (dynamic partitionsSpec) 1`] = `
|
exports[`CompactionConfigDialog matches snapshot with compactionConfig (dynamic partitionsSpec) 1`] = `
|
||||||
<Blueprint4.Dialog
|
<Blueprint4.Dialog
|
||||||
canOutsideClickClose={false}
|
canOutsideClickClose={false}
|
||||||
className="compaction-dialog"
|
className="compaction-config-dialog"
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
onClose={[Function]}
|
onClose={[Function]}
|
||||||
title="Compaction config: test1"
|
title="Compaction config: test1"
|
||||||
|
@ -356,6 +356,12 @@ exports[`CompactionDialog matches snapshot with compactionConfig (dynamic partit
|
||||||
<div
|
<div
|
||||||
className="bp4-dialog-footer-actions"
|
className="bp4-dialog-footer-actions"
|
||||||
>
|
>
|
||||||
|
<Blueprint4.Button
|
||||||
|
className="history-button"
|
||||||
|
minimal={true}
|
||||||
|
onClick={[Function]}
|
||||||
|
text="History"
|
||||||
|
/>
|
||||||
<Blueprint4.Button
|
<Blueprint4.Button
|
||||||
intent="danger"
|
intent="danger"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
@ -376,10 +382,10 @@ exports[`CompactionDialog matches snapshot with compactionConfig (dynamic partit
|
||||||
</Blueprint4.Dialog>
|
</Blueprint4.Dialog>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`CompactionDialog matches snapshot with compactionConfig (hashed partitionsSpec) 1`] = `
|
exports[`CompactionConfigDialog matches snapshot with compactionConfig (hashed partitionsSpec) 1`] = `
|
||||||
<Blueprint4.Dialog
|
<Blueprint4.Dialog
|
||||||
canOutsideClickClose={false}
|
canOutsideClickClose={false}
|
||||||
className="compaction-dialog"
|
className="compaction-config-dialog"
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
onClose={[Function]}
|
onClose={[Function]}
|
||||||
title="Compaction config: test1"
|
title="Compaction config: test1"
|
||||||
|
@ -732,6 +738,12 @@ exports[`CompactionDialog matches snapshot with compactionConfig (hashed partiti
|
||||||
<div
|
<div
|
||||||
className="bp4-dialog-footer-actions"
|
className="bp4-dialog-footer-actions"
|
||||||
>
|
>
|
||||||
|
<Blueprint4.Button
|
||||||
|
className="history-button"
|
||||||
|
minimal={true}
|
||||||
|
onClick={[Function]}
|
||||||
|
text="History"
|
||||||
|
/>
|
||||||
<Blueprint4.Button
|
<Blueprint4.Button
|
||||||
intent="danger"
|
intent="danger"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
@ -752,10 +764,10 @@ exports[`CompactionDialog matches snapshot with compactionConfig (hashed partiti
|
||||||
</Blueprint4.Dialog>
|
</Blueprint4.Dialog>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`CompactionDialog matches snapshot with compactionConfig (range partitionsSpec) 1`] = `
|
exports[`CompactionConfigDialog matches snapshot with compactionConfig (range partitionsSpec) 1`] = `
|
||||||
<Blueprint4.Dialog
|
<Blueprint4.Dialog
|
||||||
canOutsideClickClose={false}
|
canOutsideClickClose={false}
|
||||||
className="compaction-dialog"
|
className="compaction-config-dialog"
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
onClose={[Function]}
|
onClose={[Function]}
|
||||||
title="Compaction config: test1"
|
title="Compaction config: test1"
|
||||||
|
@ -1108,6 +1120,12 @@ exports[`CompactionDialog matches snapshot with compactionConfig (range partitio
|
||||||
<div
|
<div
|
||||||
className="bp4-dialog-footer-actions"
|
className="bp4-dialog-footer-actions"
|
||||||
>
|
>
|
||||||
|
<Blueprint4.Button
|
||||||
|
className="history-button"
|
||||||
|
minimal={true}
|
||||||
|
onClick={[Function]}
|
||||||
|
text="History"
|
||||||
|
/>
|
||||||
<Blueprint4.Button
|
<Blueprint4.Button
|
||||||
intent="danger"
|
intent="danger"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
@ -1128,10 +1146,10 @@ exports[`CompactionDialog matches snapshot with compactionConfig (range partitio
|
||||||
</Blueprint4.Dialog>
|
</Blueprint4.Dialog>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`CompactionDialog matches snapshot without compactionConfig 1`] = `
|
exports[`CompactionConfigDialog matches snapshot without compactionConfig 1`] = `
|
||||||
<Blueprint4.Dialog
|
<Blueprint4.Dialog
|
||||||
canOutsideClickClose={false}
|
canOutsideClickClose={false}
|
||||||
className="compaction-dialog"
|
className="compaction-config-dialog"
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
onClose={[Function]}
|
onClose={[Function]}
|
||||||
title="Compaction config: test1"
|
title="Compaction config: test1"
|
||||||
|
@ -1484,6 +1502,12 @@ exports[`CompactionDialog matches snapshot without compactionConfig 1`] = `
|
||||||
<div
|
<div
|
||||||
className="bp4-dialog-footer-actions"
|
className="bp4-dialog-footer-actions"
|
||||||
>
|
>
|
||||||
|
<Blueprint4.Button
|
||||||
|
className="history-button"
|
||||||
|
minimal={true}
|
||||||
|
onClick={[Function]}
|
||||||
|
text="History"
|
||||||
|
/>
|
||||||
<Blueprint4.Button
|
<Blueprint4.Button
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
text="Close"
|
text="Close"
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
@import '../../variables';
|
@import '../../variables';
|
||||||
|
|
||||||
.compaction-dialog {
|
.compaction-config-dialog {
|
||||||
&.#{$bp-ns}-dialog {
|
&.#{$bp-ns}-dialog {
|
||||||
height: 80vh;
|
height: 80vh;
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,17 @@
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.#{$bp-ns}-dialog-footer-actions {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.history-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
margin: 0 15px 10px 0;
|
margin: 0 15px 10px 0;
|
||||||
padding: 0 5px 0 15px;
|
padding: 0 5px 0 15px;
|
|
@ -20,12 +20,12 @@ import React from 'react';
|
||||||
|
|
||||||
import { shallow } from '../../utils/shallow-renderer';
|
import { shallow } from '../../utils/shallow-renderer';
|
||||||
|
|
||||||
import { CompactionDialog } from './compaction-dialog';
|
import { CompactionConfigDialog } from './compaction-config-dialog';
|
||||||
|
|
||||||
describe('CompactionDialog', () => {
|
describe('CompactionConfigDialog', () => {
|
||||||
it('matches snapshot without compactionConfig', () => {
|
it('matches snapshot without compactionConfig', () => {
|
||||||
const compactionDialog = shallow(
|
const compactionDialog = shallow(
|
||||||
<CompactionDialog
|
<CompactionConfigDialog
|
||||||
onClose={() => {}}
|
onClose={() => {}}
|
||||||
onSave={() => {}}
|
onSave={() => {}}
|
||||||
onDelete={() => {}}
|
onDelete={() => {}}
|
||||||
|
@ -38,7 +38,7 @@ describe('CompactionDialog', () => {
|
||||||
|
|
||||||
it('matches snapshot with compactionConfig (dynamic partitionsSpec)', () => {
|
it('matches snapshot with compactionConfig (dynamic partitionsSpec)', () => {
|
||||||
const compactionDialog = shallow(
|
const compactionDialog = shallow(
|
||||||
<CompactionDialog
|
<CompactionConfigDialog
|
||||||
onClose={() => {}}
|
onClose={() => {}}
|
||||||
onSave={() => {}}
|
onSave={() => {}}
|
||||||
onDelete={() => {}}
|
onDelete={() => {}}
|
||||||
|
@ -54,7 +54,7 @@ describe('CompactionDialog', () => {
|
||||||
|
|
||||||
it('matches snapshot with compactionConfig (hashed partitionsSpec)', () => {
|
it('matches snapshot with compactionConfig (hashed partitionsSpec)', () => {
|
||||||
const compactionDialog = shallow(
|
const compactionDialog = shallow(
|
||||||
<CompactionDialog
|
<CompactionConfigDialog
|
||||||
onClose={() => {}}
|
onClose={() => {}}
|
||||||
onSave={() => {}}
|
onSave={() => {}}
|
||||||
onDelete={() => {}}
|
onDelete={() => {}}
|
||||||
|
@ -70,7 +70,7 @@ describe('CompactionDialog', () => {
|
||||||
|
|
||||||
it('matches snapshot with compactionConfig (range partitionsSpec)', () => {
|
it('matches snapshot with compactionConfig (range partitionsSpec)', () => {
|
||||||
const compactionDialog = shallow(
|
const compactionDialog = shallow(
|
||||||
<CompactionDialog
|
<CompactionConfigDialog
|
||||||
onClose={() => {}}
|
onClose={() => {}}
|
||||||
onSave={() => {}}
|
onSave={() => {}}
|
||||||
onDelete={() => {}}
|
onDelete={() => {}}
|
|
@ -27,10 +27,11 @@ import {
|
||||||
compactionConfigHasLegacyInputSegmentSizeBytesSet,
|
compactionConfigHasLegacyInputSegmentSizeBytesSet,
|
||||||
} from '../../druid-models';
|
} from '../../druid-models';
|
||||||
import { deepDelete, formatBytesCompact } from '../../utils';
|
import { deepDelete, formatBytesCompact } from '../../utils';
|
||||||
|
import { CompactionHistoryDialog } from '../compaction-history-dialog/compaction-history-dialog';
|
||||||
|
|
||||||
import './compaction-dialog.scss';
|
import './compaction-config-dialog.scss';
|
||||||
|
|
||||||
export interface CompactionDialogProps {
|
export interface CompactionConfigDialogProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSave: (compactionConfig: CompactionConfig) => void | Promise<void>;
|
onSave: (compactionConfig: CompactionConfig) => void | Promise<void>;
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
|
@ -38,9 +39,12 @@ export interface CompactionDialogProps {
|
||||||
compactionConfig: CompactionConfig | undefined;
|
compactionConfig: CompactionConfig | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CompactionDialog = React.memo(function CompactionDialog(props: CompactionDialogProps) {
|
export const CompactionConfigDialog = React.memo(function CompactionConfigDialog(
|
||||||
|
props: CompactionConfigDialogProps,
|
||||||
|
) {
|
||||||
const { datasource, compactionConfig, onSave, onClose, onDelete } = props;
|
const { datasource, compactionConfig, onSave, onClose, onDelete } = props;
|
||||||
|
|
||||||
|
const [showHistory, setShowHistory] = useState(false);
|
||||||
const [currentTab, setCurrentTab] = useState<FormJsonTabs>('form');
|
const [currentTab, setCurrentTab] = useState<FormJsonTabs>('form');
|
||||||
const [currentConfig, setCurrentConfig] = useState<CompactionConfig>(
|
const [currentConfig, setCurrentConfig] = useState<CompactionConfig>(
|
||||||
compactionConfig || {
|
compactionConfig || {
|
||||||
|
@ -53,9 +57,15 @@ export const CompactionDialog = React.memo(function CompactionDialog(props: Comp
|
||||||
const issueWithCurrentConfig = AutoForm.issueWithModel(currentConfig, COMPACTION_CONFIG_FIELDS);
|
const issueWithCurrentConfig = AutoForm.issueWithModel(currentConfig, COMPACTION_CONFIG_FIELDS);
|
||||||
const disableSubmit = Boolean(jsonError || issueWithCurrentConfig);
|
const disableSubmit = Boolean(jsonError || issueWithCurrentConfig);
|
||||||
|
|
||||||
|
if (showHistory) {
|
||||||
|
return (
|
||||||
|
<CompactionHistoryDialog datasource={datasource} onClose={() => setShowHistory(false)} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
className="compaction-dialog"
|
className="compaction-config-dialog"
|
||||||
isOpen
|
isOpen
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
canOutsideClickClose={false}
|
canOutsideClickClose={false}
|
||||||
|
@ -100,6 +110,12 @@ export const CompactionDialog = React.memo(function CompactionDialog(props: Comp
|
||||||
</div>
|
</div>
|
||||||
<div className={Classes.DIALOG_FOOTER}>
|
<div className={Classes.DIALOG_FOOTER}>
|
||||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||||
|
<Button
|
||||||
|
className="history-button"
|
||||||
|
text="History"
|
||||||
|
minimal
|
||||||
|
onClick={() => setShowHistory(true)}
|
||||||
|
/>
|
||||||
{compactionConfig && <Button text="Delete" intent={Intent.DANGER} onClick={onDelete} />}
|
{compactionConfig && <Button text="Delete" intent={Intent.DANGER} onClick={onDelete} />}
|
||||||
<Button text="Close" onClick={onClose} />
|
<Button text="Close" onClick={onClose} />
|
||||||
<Button
|
<Button
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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 '../../variables';
|
||||||
|
|
||||||
|
.compaction-history-dialog {
|
||||||
|
&.#{$bp-ns}-dialog {
|
||||||
|
width: 70vw;
|
||||||
|
height: 80vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.#{$bp-ns}-tabs {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.global-info {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 30px;
|
||||||
|
right: 10px;
|
||||||
|
width: auto;
|
||||||
|
white-space: pre;
|
||||||
|
background: $gray1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
/*
|
||||||
|
* 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 { Button, Callout, Classes, Code, Dialog, Tab, Tabs } from '@blueprintjs/core';
|
||||||
|
import * as JSONBig from 'json-bigint-native';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
import { Loader, ShowValue } from '../../components';
|
||||||
|
import type { CompactionConfig } from '../../druid-models';
|
||||||
|
import { useQueryManager } from '../../hooks';
|
||||||
|
import { Api } from '../../singletons';
|
||||||
|
import { formatInteger, formatPercent } from '../../utils';
|
||||||
|
import { DiffDialog } from '../diff-dialog/diff-dialog';
|
||||||
|
|
||||||
|
import './compaction-history-dialog.scss';
|
||||||
|
|
||||||
|
interface CompactionHistoryEntry {
|
||||||
|
auditTime: string;
|
||||||
|
auditInfo: any;
|
||||||
|
globalConfig?: GlobalConfig;
|
||||||
|
compactionConfig: CompactionConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GlobalConfig {
|
||||||
|
compactionTaskSlotRatio: number;
|
||||||
|
maxCompactionTaskSlots: number;
|
||||||
|
useAutoScaleSlots: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatGlobalConfig(globalConfig: GlobalConfig): string {
|
||||||
|
return [
|
||||||
|
`compactionTaskSlotRatio: ${formatPercent(globalConfig.compactionTaskSlotRatio)}`,
|
||||||
|
`maxCompactionTaskSlots: ${formatInteger(globalConfig.maxCompactionTaskSlots)}`,
|
||||||
|
`useAutoScaleSlots: ${globalConfig.useAutoScaleSlots}`,
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CompactionHistoryDialogProps {
|
||||||
|
datasource: string;
|
||||||
|
onClose(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CompactionHistoryDialog = React.memo(function CompactionHistoryDialog(
|
||||||
|
props: CompactionHistoryDialogProps,
|
||||||
|
) {
|
||||||
|
const { datasource, onClose } = props;
|
||||||
|
|
||||||
|
const [diffIndex, setDiffIndex] = useState(-1);
|
||||||
|
const [historyState] = useQueryManager<string, CompactionHistoryEntry[]>({
|
||||||
|
initQuery: datasource,
|
||||||
|
processQuery: async datasource => {
|
||||||
|
try {
|
||||||
|
const resp = await Api.instance.get(
|
||||||
|
`/druid/coordinator/v1/config/compaction/${Api.encodePath(datasource)}/history?count=20`,
|
||||||
|
);
|
||||||
|
return resp.data;
|
||||||
|
} catch (e) {
|
||||||
|
if (e.response?.status === 404) return [];
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const historyData = historyState.data;
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
className="compaction-history-dialog"
|
||||||
|
isOpen
|
||||||
|
onClose={onClose}
|
||||||
|
canOutsideClickClose={false}
|
||||||
|
title={`Compaction history: ${datasource}`}
|
||||||
|
>
|
||||||
|
<div className={Classes.DIALOG_BODY}>
|
||||||
|
{historyData ? (
|
||||||
|
historyData.length ? (
|
||||||
|
<Tabs animate renderActiveTabPanelOnly vertical defaultSelectedTabId={0}>
|
||||||
|
{historyData.map((historyEntry, i) => (
|
||||||
|
<Tab
|
||||||
|
id={i}
|
||||||
|
key={i}
|
||||||
|
title={historyEntry.auditTime}
|
||||||
|
panelClassName="panel"
|
||||||
|
panel={
|
||||||
|
<>
|
||||||
|
<ShowValue
|
||||||
|
jsonValue={JSONBig.stringify(historyEntry.compactionConfig, undefined, 2)}
|
||||||
|
onDiffWithPrevious={
|
||||||
|
i < historyData.length - 1 ? () => setDiffIndex(i) : undefined
|
||||||
|
}
|
||||||
|
downloadFilename={`compaction-history-${datasource}-version-${historyEntry.auditTime}.json`}
|
||||||
|
/>
|
||||||
|
{historyEntry.globalConfig && (
|
||||||
|
<Callout className="global-info">
|
||||||
|
{formatGlobalConfig(historyEntry.globalConfig)}
|
||||||
|
</Callout>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<Tabs.Expander />
|
||||||
|
</Tabs>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
There is no compaction history for <Code>{datasource}</Code>.
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
) : historyState.loading ? (
|
||||||
|
<Loader />
|
||||||
|
) : (
|
||||||
|
<div>{historyState.getErrorMessage()}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={Classes.DIALOG_FOOTER}>
|
||||||
|
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||||
|
<Button text="Close" onClick={onClose} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{diffIndex !== -1 && historyData && (
|
||||||
|
<DiffDialog
|
||||||
|
title="Compaction config diff"
|
||||||
|
versions={historyData.map(s => ({ label: s.auditTime, value: s.compactionConfig }))}
|
||||||
|
initLeftIndex={diffIndex + 1}
|
||||||
|
initRightIndex={diffIndex}
|
||||||
|
onClose={() => setDiffIndex(-1)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
});
|
|
@ -112,7 +112,7 @@ exports[`HistoryDialog matches snapshot 1`] = `
|
||||||
role="tabpanel"
|
role="tabpanel"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="show-json"
|
class="show-value"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="top-actions"
|
class="top-actions"
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
export * from './about-dialog/about-dialog';
|
export * from './about-dialog/about-dialog';
|
||||||
export * from './async-action-dialog/async-action-dialog';
|
export * from './async-action-dialog/async-action-dialog';
|
||||||
export * from './compaction-dialog/compaction-dialog';
|
export * from './compaction-config-dialog/compaction-config-dialog';
|
||||||
export * from './coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog';
|
export * from './coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog';
|
||||||
export * from './diff-dialog/diff-dialog';
|
export * from './diff-dialog/diff-dialog';
|
||||||
export * from './doctor-dialog/doctor-dialog';
|
export * from './doctor-dialog/doctor-dialog';
|
||||||
|
|
|
@ -18,8 +18,7 @@
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
import { ShowJson } from '../../components';
|
import { ShowJson, SupervisorHistoryPanel } from '../../components';
|
||||||
import { ShowHistory } from '../../components/show-history/show-history';
|
|
||||||
import { cleanSpec } from '../../druid-models';
|
import { cleanSpec } from '../../druid-models';
|
||||||
import { Api } from '../../singletons';
|
import { Api } from '../../singletons';
|
||||||
import { deepGet } from '../../utils';
|
import { deepGet } from '../../utils';
|
||||||
|
@ -96,12 +95,7 @@ export const SupervisorTableActionDialog = React.memo(function SupervisorTableAc
|
||||||
downloadFilename={`supervisor-payload-${supervisorId}.json`}
|
downloadFilename={`supervisor-payload-${supervisorId}.json`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{activeTab === 'history' && (
|
{activeTab === 'history' && <SupervisorHistoryPanel supervisorId={supervisorId} />}
|
||||||
<ShowHistory
|
|
||||||
endpoint={`${supervisorEndpointBase}/history`}
|
|
||||||
downloadFilenamePrefix={`supervisor-${supervisorId}`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</TableActionDialog>
|
</TableActionDialog>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { dedupe } from 'druid-query-toolkit';
|
||||||
import * as JSONBig from 'json-bigint-native';
|
import * as JSONBig from 'json-bigint-native';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
@ -91,18 +92,6 @@ export interface ExampleManifest {
|
||||||
spec: any;
|
spec: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
function dedupe(xs: string[]): string[] {
|
|
||||||
const seen: Record<string, boolean> = {};
|
|
||||||
return xs.filter(x => {
|
|
||||||
if (seen[x]) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
seen[x] = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCacheRowsFromSampleResponse(sampleResponse: SampleResponse): CacheRows {
|
export function getCacheRowsFromSampleResponse(sampleResponse: SampleResponse): CacheRows {
|
||||||
return filterMap(sampleResponse.data, d => d.input).slice(0, 20);
|
return filterMap(sampleResponse.data, d => d.input).slice(0, 20);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ import {
|
||||||
} from '../../components';
|
} from '../../components';
|
||||||
import {
|
import {
|
||||||
AsyncActionDialog,
|
AsyncActionDialog,
|
||||||
CompactionDialog,
|
CompactionConfigDialog,
|
||||||
KillDatasourceDialog,
|
KillDatasourceDialog,
|
||||||
RetentionDialog,
|
RetentionDialog,
|
||||||
} from '../../dialogs';
|
} from '../../dialogs';
|
||||||
|
@ -233,7 +233,7 @@ interface RetentionDialogOpenOn {
|
||||||
readonly rules: Rule[];
|
readonly rules: Rule[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CompactionDialogOpenOn {
|
interface CompactionConfigDialogOpenOn {
|
||||||
readonly datasource: string;
|
readonly datasource: string;
|
||||||
readonly compactionConfig?: CompactionConfig;
|
readonly compactionConfig?: CompactionConfig;
|
||||||
}
|
}
|
||||||
|
@ -254,7 +254,7 @@ export interface DatasourcesViewState {
|
||||||
|
|
||||||
showUnused: boolean;
|
showUnused: boolean;
|
||||||
retentionDialogOpenOn?: RetentionDialogOpenOn;
|
retentionDialogOpenOn?: RetentionDialogOpenOn;
|
||||||
compactionDialogOpenOn?: CompactionDialogOpenOn;
|
compactionDialogOpenOn?: CompactionConfigDialogOpenOn;
|
||||||
datasourceToMarkAsUnusedAllSegmentsIn?: string;
|
datasourceToMarkAsUnusedAllSegmentsIn?: string;
|
||||||
datasourceToMarkAllNonOvershadowedSegmentsAsUsedIn?: string;
|
datasourceToMarkAllNonOvershadowedSegmentsAsUsedIn?: string;
|
||||||
killDatasource?: string;
|
killDatasource?: string;
|
||||||
|
@ -981,12 +981,12 @@ ORDER BY 1`;
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderCompactionDialog() {
|
private renderCompactionConfigDialog() {
|
||||||
const { datasourcesAndDefaultRulesState, compactionDialogOpenOn } = this.state;
|
const { datasourcesAndDefaultRulesState, compactionDialogOpenOn } = this.state;
|
||||||
if (!compactionDialogOpenOn || !datasourcesAndDefaultRulesState.data) return;
|
if (!compactionDialogOpenOn || !datasourcesAndDefaultRulesState.data) return;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CompactionDialog
|
<CompactionConfigDialog
|
||||||
datasource={compactionDialogOpenOn.datasource}
|
datasource={compactionDialogOpenOn.datasource}
|
||||||
compactionConfig={compactionDialogOpenOn.compactionConfig}
|
compactionConfig={compactionDialogOpenOn.compactionConfig}
|
||||||
onClose={() => this.setState({ compactionDialogOpenOn: undefined })}
|
onClose={() => this.setState({ compactionDialogOpenOn: undefined })}
|
||||||
|
@ -1571,7 +1571,7 @@ ORDER BY 1`;
|
||||||
{this.renderUseUnuseActionByInterval()}
|
{this.renderUseUnuseActionByInterval()}
|
||||||
{this.renderKillAction()}
|
{this.renderKillAction()}
|
||||||
{this.renderRetentionDialog()}
|
{this.renderRetentionDialog()}
|
||||||
{this.renderCompactionDialog()}
|
{this.renderCompactionConfigDialog()}
|
||||||
{this.renderForceCompactAction()}
|
{this.renderForceCompactAction()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue