You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by vo...@apache.org on 2023/03/06 19:52:39 UTC

[druid] branch master updated: Web console: Compaction history dialog (#13861)

This is an automated email from the ASF dual-hosted git repository.

vogievetsky pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/master by this push:
     new 38b6373bf7 Web console: Compaction history dialog (#13861)
38b6373bf7 is described below

commit 38b6373bf78165c1ed95198d2e0cffecfc6eb06f
Author: Vadim Ogievetsky <va...@ogievetsky.com>
AuthorDate: Mon Mar 6 11:52:25 2023 -0800

    Web console: Compaction history dialog (#13861)
    
    * initial renames
    
    * add comaction history diff
    
    * final fixes
    
    * update snapshots
    
    * use maps
    
    * update test
---
 licenses.yaml                                      |   2 +-
 .../e2e-tests/component/datasources/overview.ts    |   2 +-
 web-console/package-lock.json                      |  14 +-
 web-console/package.json                           |   2 +-
 web-console/script/licenses                        |   6 +-
 web-console/src/components/index.ts                |   1 +
 .../__snapshots__/show-value.spec.tsx.snap         |   2 +-
 .../src/components/show-value/show-value.tsx       |   2 +-
 .../supervisor-history-panel.spec.tsx.snap}        |   2 +-
 .../supervisor-history-panel.scss}                 |   2 +-
 .../supervisor-history-panel.spec.tsx}             |   6 +-
 .../supervisor-history-panel.tsx}                  |  37 +++---
 .../compaction-config-dialog.spec.tsx.snap}        |  40 ++++--
 .../compaction-config-dialog.scss}                 |  13 +-
 .../compaction-config-dialog.spec.tsx}             |  12 +-
 .../compaction-config-dialog.tsx}                  |  24 +++-
 .../compaction-history-dialog.scss}                |  22 +++-
 .../compaction-history-dialog.tsx                  | 145 +++++++++++++++++++++
 .../__snapshots__/history-dialog.spec.tsx.snap     |   2 +-
 web-console/src/dialogs/index.ts                   |   2 +-
 .../supervisor-table-action-dialog.tsx             |  10 +-
 web-console/src/utils/sampler.ts                   |  13 +-
 .../views/datasources-view/datasources-view.tsx    |  12 +-
 23 files changed, 285 insertions(+), 88 deletions(-)

diff --git a/licenses.yaml b/licenses.yaml
index 7d68a4d8f5..6c9e4a6e49 100644
--- a/licenses.yaml
+++ b/licenses.yaml
@@ -5693,7 +5693,7 @@ license_category: binary
 module: web-console
 license_name: Apache License version 2.0
 copyright: Imply Data
-version: 0.17.4
+version: 0.17.5
 
 ---
 
diff --git a/web-console/e2e-tests/component/datasources/overview.ts b/web-console/e2e-tests/component/datasources/overview.ts
index 95d6d630c9..9a6db40fcd 100644
--- a/web-console/e2e-tests/component/datasources/overview.ts
+++ b/web-console/e2e-tests/component/datasources/overview.ts
@@ -99,7 +99,7 @@ export class DatasourcesOverview {
   private async openCompactionConfigurationDialog(datasourceName: string): Promise<void> {
     await this.openEditActions(datasourceName);
     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> {
diff --git a/web-console/package-lock.json b/web-console/package-lock.json
index 8e9f115fd8..0285118311 100644
--- a/web-console/package-lock.json
+++ b/web-console/package-lock.json
@@ -22,7 +22,7 @@
         "d3-axis": "^2.1.0",
         "d3-scale": "^3.3.0",
         "d3-selection": "^2.0.0",
-        "druid-query-toolkit": "^0.17.4",
+        "druid-query-toolkit": "^0.17.5",
         "file-saver": "^2.0.2",
         "follow-redirects": "^1.14.7",
         "fontsource-open-sans": "^3.0.9",
@@ -8542,9 +8542,9 @@
       }
     },
     "node_modules/druid-query-toolkit": {
-      "version": "0.17.4",
-      "resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.17.4.tgz",
-      "integrity": "sha512-d/mNJ9ausAfxQaxgGWfP4dHpwcIqjkbdz1NNmTk6DHMbwlskk96MnXrvPsOCmeoKMb0iYVeVtq9CbB1vNaPZ5A==",
+      "version": "0.17.5",
+      "resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.17.5.tgz",
+      "integrity": "sha512-N+kqu6xy2Gd3zQwNbBxQXG+qjU7jzbCXvE84uxsIh5gxRbiKAEOsyWtKrWF/DZZ91g/WSIMQih5klkmmcjlVfQ==",
       "dependencies": {
         "tslib": "^2.3.1"
       },
@@ -33236,9 +33236,9 @@
       }
     },
     "druid-query-toolkit": {
-      "version": "0.17.4",
-      "resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.17.4.tgz",
-      "integrity": "sha512-d/mNJ9ausAfxQaxgGWfP4dHpwcIqjkbdz1NNmTk6DHMbwlskk96MnXrvPsOCmeoKMb0iYVeVtq9CbB1vNaPZ5A==",
+      "version": "0.17.5",
+      "resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.17.5.tgz",
+      "integrity": "sha512-N+kqu6xy2Gd3zQwNbBxQXG+qjU7jzbCXvE84uxsIh5gxRbiKAEOsyWtKrWF/DZZ91g/WSIMQih5klkmmcjlVfQ==",
       "requires": {
         "tslib": "^2.3.1"
       }
diff --git a/web-console/package.json b/web-console/package.json
index 12bb391cf0..fde4d2a57a 100644
--- a/web-console/package.json
+++ b/web-console/package.json
@@ -79,7 +79,7 @@
     "d3-axis": "^2.1.0",
     "d3-scale": "^3.3.0",
     "d3-selection": "^2.0.0",
-    "druid-query-toolkit": "^0.17.4",
+    "druid-query-toolkit": "^0.17.5",
     "file-saver": "^2.0.2",
     "follow-redirects": "^1.14.7",
     "fontsource-open-sans": "^3.0.9",
diff --git a/web-console/script/licenses b/web-console/script/licenses
index 46df1fdb48..35aa2f5b58 100755
--- a/web-console/script/licenses
+++ b/web-console/script/licenses
@@ -90,7 +90,7 @@ checker.init(
 
     Object.assign(packages, extraPackages);
 
-    const seen = {};
+    const seen = new Map();
     const mapped = Object.keys(packages)
       .sort()
       .map(p => {
@@ -98,8 +98,8 @@ checker.init(
         if (!m) throw new Error(`Malformed name@version`);
         const name = m[1];
         if (name === 'web-console') return; // This is me!
-        if (seen[name]) return; // Dedupe
-        seen[name] = true;
+        if (seen.has(name)) return; // Dedupe
+        seen.set(name, true);
 
         const version = m[2];
         const meta = packages[p];
diff --git a/web-console/src/components/index.ts b/web-console/src/components/index.ts
index 56da967599..a84dc063f8 100644
--- a/web-console/src/components/index.ts
+++ b/web-console/src/components/index.ts
@@ -51,6 +51,7 @@ export * from './show-json/show-json';
 export * from './show-log/show-log';
 export * from './show-value/show-value';
 export * from './suggestion-menu/suggestion-menu';
+export * from './supervisor-history-panel/supervisor-history-panel';
 export * from './table-cell/table-cell';
 export * from './table-cell-unparseable/table-cell-unparseable';
 export * from './table-clickable-cell/table-clickable-cell';
diff --git a/web-console/src/components/show-value/__snapshots__/show-value.spec.tsx.snap b/web-console/src/components/show-value/__snapshots__/show-value.spec.tsx.snap
index a2bae8643c..c01065774a 100644
--- a/web-console/src/components/show-value/__snapshots__/show-value.spec.tsx.snap
+++ b/web-console/src/components/show-value/__snapshots__/show-value.spec.tsx.snap
@@ -2,7 +2,7 @@
 
 exports[`ShowValue matches snapshot 1`] = `
 <div
-  class="show-json"
+  class="show-value"
 >
   <div
     class="top-actions"
diff --git a/web-console/src/components/show-value/show-value.tsx b/web-console/src/components/show-value/show-value.tsx
index d04219d869..14c76c9277 100644
--- a/web-console/src/components/show-value/show-value.tsx
+++ b/web-console/src/components/show-value/show-value.tsx
@@ -32,7 +32,7 @@ export interface ShowValueProps {
 export const ShowValue = React.memo(function ShowValue(props: ShowValueProps) {
   const { jsonValue, onDiffWithPrevious, downloadFilename } = props;
   return (
-    <div className="show-json">
+    <div className="show-value">
       {(onDiffWithPrevious || downloadFilename) && (
         <div className="top-actions">
           <ButtonGroup className="right-buttons">
diff --git a/web-console/src/components/show-history/__snapshots__/show-history.spec.tsx.snap b/web-console/src/components/supervisor-history-panel/__snapshots__/supervisor-history-panel.spec.tsx.snap
similarity index 95%
rename from web-console/src/components/show-history/__snapshots__/show-history.spec.tsx.snap
rename to web-console/src/components/supervisor-history-panel/__snapshots__/supervisor-history-panel.spec.tsx.snap
index dc505ddbd3..6fc2308a29 100644
--- a/web-console/src/components/show-history/__snapshots__/show-history.spec.tsx.snap
+++ b/web-console/src/components/supervisor-history-panel/__snapshots__/supervisor-history-panel.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`ShowHistory matches snapshot 1`] = `
+exports[`SupervisorHistoryPanel matches snapshot 1`] = `
 <div
   class="loader"
 >
diff --git a/web-console/src/components/show-history/show-history.scss b/web-console/src/components/supervisor-history-panel/supervisor-history-panel.scss
similarity index 97%
copy from web-console/src/components/show-history/show-history.scss
copy to web-console/src/components/supervisor-history-panel/supervisor-history-panel.scss
index e062642464..2d35ad870a 100644
--- a/web-console/src/components/show-history/show-history.scss
+++ b/web-console/src/components/supervisor-history-panel/supervisor-history-panel.scss
@@ -18,7 +18,7 @@
 
 @import '../../variables';
 
-.show-history {
+.supervisor-history-panel {
   position: relative;
   height: 100%;
 
diff --git a/web-console/src/components/show-history/show-history.spec.tsx b/web-console/src/components/supervisor-history-panel/supervisor-history-panel.spec.tsx
similarity index 84%
rename from web-console/src/components/show-history/show-history.spec.tsx
rename to web-console/src/components/supervisor-history-panel/supervisor-history-panel.spec.tsx
index 678fd22e91..e8414feb9a 100644
--- a/web-console/src/components/show-history/show-history.spec.tsx
+++ b/web-console/src/components/supervisor-history-panel/supervisor-history-panel.spec.tsx
@@ -19,11 +19,11 @@
 import { render } from '@testing-library/react';
 import React from 'react';
 
-import { ShowHistory } from './show-history';
+import { SupervisorHistoryPanel } from './supervisor-history-panel';
 
-describe('ShowHistory', () => {
+describe('SupervisorHistoryPanel', () => {
   it('matches snapshot', () => {
-    const showJson = <ShowHistory endpoint="test" downloadFilenamePrefix="test" />;
+    const showJson = <SupervisorHistoryPanel supervisorId="test" />;
     const { container } = render(showJson);
     expect(container.firstChild).toMatchSnapshot();
   });
diff --git a/web-console/src/components/show-history/show-history.tsx b/web-console/src/components/supervisor-history-panel/supervisor-history-panel.tsx
similarity index 74%
rename from web-console/src/components/show-history/show-history.tsx
rename to web-console/src/components/supervisor-history-panel/supervisor-history-panel.tsx
index 72ae421d33..e3fd38b916 100644
--- a/web-console/src/components/show-history/show-history.tsx
+++ b/web-console/src/components/supervisor-history-panel/supervisor-history-panel.tsx
@@ -29,29 +29,34 @@ import { deepSet } from '../../utils';
 import { Loader } from '../loader/loader';
 import { ShowValue } from '../show-value/show-value';
 
-import './show-history.scss';
+import './supervisor-history-panel.scss';
 
-export interface VersionSpec {
+export interface SupervisorHistoryEntry {
   version: string;
   spec: IngestionSpec;
 }
 
-export interface ShowHistoryProps {
-  endpoint: string;
-  downloadFilenamePrefix?: string;
+export interface SupervisorHistoryPanelProps {
+  supervisorId: string;
 }
 
-export const ShowHistory = React.memo(function ShowHistory(props: ShowHistoryProps) {
-  const { downloadFilenamePrefix, endpoint } = props;
+export const SupervisorHistoryPanel = React.memo(function SupervisorHistoryPanel(
+  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)));
+  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)),
+      );
     },
-    initQuery: endpoint,
   });
-  const [diffIndex, setDiffIndex] = useState(-1);
 
   if (historyState.loading) return <Loader />;
 
@@ -59,21 +64,21 @@ export const ShowHistory = React.memo(function ShowHistory(props: ShowHistoryPro
   if (!historyData) return null;
 
   return (
-    <div className="show-history">
+    <div className="supervisor-history-panel">
       <Tabs animate renderActiveTabPanelOnly vertical defaultSelectedTabId={0}>
         {historyData.map((pastSupervisor, i) => (
           <Tab
             id={i}
             key={i}
             title={pastSupervisor.version}
+            panelClassName="panel"
             panel={
               <ShowValue
                 jsonValue={JSONBig.stringify(pastSupervisor.spec, undefined, 2)}
                 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 />
diff --git a/web-console/src/dialogs/compaction-dialog/__snapshots__/compaction-dialog.spec.tsx.snap b/web-console/src/dialogs/compaction-config-dialog/__snapshots__/compaction-config-dialog.spec.tsx.snap
similarity index 97%
rename from web-console/src/dialogs/compaction-dialog/__snapshots__/compaction-dialog.spec.tsx.snap
rename to web-console/src/dialogs/compaction-config-dialog/__snapshots__/compaction-config-dialog.spec.tsx.snap
index c632071fe5..0d7c477442 100644
--- a/web-console/src/dialogs/compaction-dialog/__snapshots__/compaction-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/compaction-config-dialog/__snapshots__/compaction-config-dialog.spec.tsx.snap
@@ -1,9 +1,9 @@
 // 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
   canOutsideClickClose={false}
-  className="compaction-dialog"
+  className="compaction-config-dialog"
   isOpen={true}
   onClose={[Function]}
   title="Compaction config: test1"
@@ -356,6 +356,12 @@ exports[`CompactionDialog matches snapshot with compactionConfig (dynamic partit
     <div
       className="bp4-dialog-footer-actions"
     >
+      <Blueprint4.Button
+        className="history-button"
+        minimal={true}
+        onClick={[Function]}
+        text="History"
+      />
       <Blueprint4.Button
         intent="danger"
         onClick={[Function]}
@@ -376,10 +382,10 @@ exports[`CompactionDialog matches snapshot with compactionConfig (dynamic partit
 </Blueprint4.Dialog>
 `;
 
-exports[`CompactionDialog matches snapshot with compactionConfig (hashed partitionsSpec) 1`] = `
+exports[`CompactionConfigDialog matches snapshot with compactionConfig (hashed partitionsSpec) 1`] = `
 <Blueprint4.Dialog
   canOutsideClickClose={false}
-  className="compaction-dialog"
+  className="compaction-config-dialog"
   isOpen={true}
   onClose={[Function]}
   title="Compaction config: test1"
@@ -732,6 +738,12 @@ exports[`CompactionDialog matches snapshot with compactionConfig (hashed partiti
     <div
       className="bp4-dialog-footer-actions"
     >
+      <Blueprint4.Button
+        className="history-button"
+        minimal={true}
+        onClick={[Function]}
+        text="History"
+      />
       <Blueprint4.Button
         intent="danger"
         onClick={[Function]}
@@ -752,10 +764,10 @@ exports[`CompactionDialog matches snapshot with compactionConfig (hashed partiti
 </Blueprint4.Dialog>
 `;
 
-exports[`CompactionDialog matches snapshot with compactionConfig (range partitionsSpec) 1`] = `
+exports[`CompactionConfigDialog matches snapshot with compactionConfig (range partitionsSpec) 1`] = `
 <Blueprint4.Dialog
   canOutsideClickClose={false}
-  className="compaction-dialog"
+  className="compaction-config-dialog"
   isOpen={true}
   onClose={[Function]}
   title="Compaction config: test1"
@@ -1108,6 +1120,12 @@ exports[`CompactionDialog matches snapshot with compactionConfig (range partitio
     <div
       className="bp4-dialog-footer-actions"
     >
+      <Blueprint4.Button
+        className="history-button"
+        minimal={true}
+        onClick={[Function]}
+        text="History"
+      />
       <Blueprint4.Button
         intent="danger"
         onClick={[Function]}
@@ -1128,10 +1146,10 @@ exports[`CompactionDialog matches snapshot with compactionConfig (range partitio
 </Blueprint4.Dialog>
 `;
 
-exports[`CompactionDialog matches snapshot without compactionConfig 1`] = `
+exports[`CompactionConfigDialog matches snapshot without compactionConfig 1`] = `
 <Blueprint4.Dialog
   canOutsideClickClose={false}
-  className="compaction-dialog"
+  className="compaction-config-dialog"
   isOpen={true}
   onClose={[Function]}
   title="Compaction config: test1"
@@ -1484,6 +1502,12 @@ exports[`CompactionDialog matches snapshot without compactionConfig 1`] = `
     <div
       className="bp4-dialog-footer-actions"
     >
+      <Blueprint4.Button
+        className="history-button"
+        minimal={true}
+        onClick={[Function]}
+        text="History"
+      />
       <Blueprint4.Button
         onClick={[Function]}
         text="Close"
diff --git a/web-console/src/dialogs/compaction-dialog/compaction-dialog.scss b/web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.scss
similarity index 84%
rename from web-console/src/dialogs/compaction-dialog/compaction-dialog.scss
rename to web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.scss
index 499df985c9..bdb6c04b42 100644
--- a/web-console/src/dialogs/compaction-dialog/compaction-dialog.scss
+++ b/web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.scss
@@ -18,7 +18,7 @@
 
 @import '../../variables';
 
-.compaction-dialog {
+.compaction-config-dialog {
   &.#{$bp-ns}-dialog {
     height: 80vh;
   }
@@ -32,6 +32,17 @@
     margin: 15px;
   }
 
+  .#{$bp-ns}-dialog-footer-actions {
+    position: relative;
+
+    .history-button {
+      position: absolute;
+      top: 0;
+      left: 0;
+      margin: 0;
+    }
+  }
+
   .content {
     margin: 0 15px 10px 0;
     padding: 0 5px 0 15px;
diff --git a/web-console/src/dialogs/compaction-dialog/compaction-dialog.spec.tsx b/web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.spec.tsx
similarity index 91%
rename from web-console/src/dialogs/compaction-dialog/compaction-dialog.spec.tsx
rename to web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.spec.tsx
index 20b4b01470..84d1566225 100644
--- a/web-console/src/dialogs/compaction-dialog/compaction-dialog.spec.tsx
+++ b/web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.spec.tsx
@@ -20,12 +20,12 @@ import React from 'react';
 
 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', () => {
     const compactionDialog = shallow(
-      <CompactionDialog
+      <CompactionConfigDialog
         onClose={() => {}}
         onSave={() => {}}
         onDelete={() => {}}
@@ -38,7 +38,7 @@ describe('CompactionDialog', () => {
 
   it('matches snapshot with compactionConfig (dynamic partitionsSpec)', () => {
     const compactionDialog = shallow(
-      <CompactionDialog
+      <CompactionConfigDialog
         onClose={() => {}}
         onSave={() => {}}
         onDelete={() => {}}
@@ -54,7 +54,7 @@ describe('CompactionDialog', () => {
 
   it('matches snapshot with compactionConfig (hashed partitionsSpec)', () => {
     const compactionDialog = shallow(
-      <CompactionDialog
+      <CompactionConfigDialog
         onClose={() => {}}
         onSave={() => {}}
         onDelete={() => {}}
@@ -70,7 +70,7 @@ describe('CompactionDialog', () => {
 
   it('matches snapshot with compactionConfig (range partitionsSpec)', () => {
     const compactionDialog = shallow(
-      <CompactionDialog
+      <CompactionConfigDialog
         onClose={() => {}}
         onSave={() => {}}
         onDelete={() => {}}
diff --git a/web-console/src/dialogs/compaction-dialog/compaction-dialog.tsx b/web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.tsx
similarity index 85%
rename from web-console/src/dialogs/compaction-dialog/compaction-dialog.tsx
rename to web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.tsx
index f11132860c..7c70d22571 100644
--- a/web-console/src/dialogs/compaction-dialog/compaction-dialog.tsx
+++ b/web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.tsx
@@ -27,10 +27,11 @@ import {
   compactionConfigHasLegacyInputSegmentSizeBytesSet,
 } from '../../druid-models';
 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;
   onSave: (compactionConfig: CompactionConfig) => void | Promise<void>;
   onDelete: () => void;
@@ -38,9 +39,12 @@ export interface CompactionDialogProps {
   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 [showHistory, setShowHistory] = useState(false);
   const [currentTab, setCurrentTab] = useState<FormJsonTabs>('form');
   const [currentConfig, setCurrentConfig] = useState<CompactionConfig>(
     compactionConfig || {
@@ -53,9 +57,15 @@ export const CompactionDialog = React.memo(function CompactionDialog(props: Comp
   const issueWithCurrentConfig = AutoForm.issueWithModel(currentConfig, COMPACTION_CONFIG_FIELDS);
   const disableSubmit = Boolean(jsonError || issueWithCurrentConfig);
 
+  if (showHistory) {
+    return (
+      <CompactionHistoryDialog datasource={datasource} onClose={() => setShowHistory(false)} />
+    );
+  }
+
   return (
     <Dialog
-      className="compaction-dialog"
+      className="compaction-config-dialog"
       isOpen
       onClose={onClose}
       canOutsideClickClose={false}
@@ -100,6 +110,12 @@ export const CompactionDialog = React.memo(function CompactionDialog(props: Comp
       </div>
       <div className={Classes.DIALOG_FOOTER}>
         <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} />}
           <Button text="Close" onClick={onClose} />
           <Button
diff --git a/web-console/src/components/show-history/show-history.scss b/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.scss
similarity index 76%
rename from web-console/src/components/show-history/show-history.scss
rename to web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.scss
index e062642464..620e7f6ff2 100644
--- a/web-console/src/components/show-history/show-history.scss
+++ b/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.scss
@@ -18,9 +18,11 @@
 
 @import '../../variables';
 
-.show-history {
-  position: relative;
-  height: 100%;
+.compaction-history-dialog {
+  &.#{$bp-ns}-dialog {
+    width: 70vw;
+    height: 80vh;
+  }
 
   .#{$bp-ns}-tabs {
     position: relative;
@@ -30,9 +32,19 @@
   .panel {
     position: relative;
     width: 100%;
+
+    .global-info {
+      position: absolute;
+      bottom: 10px;
+      left: 30px;
+      right: 10px;
+      width: auto;
+      white-space: pre;
+      background: $gray1;
+    }
   }
 
-  .#{$bp-ns}-tab-list {
-    overflow-y: auto;
+  .loader {
+    position: relative;
   }
 }
diff --git a/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.tsx b/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.tsx
new file mode 100644
index 0000000000..4cdc916ee7
--- /dev/null
+++ b/web-console/src/dialogs/compaction-history-dialog/compaction-history-dialog.tsx
@@ -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>
+  );
+});
diff --git a/web-console/src/dialogs/history-dialog/__snapshots__/history-dialog.spec.tsx.snap b/web-console/src/dialogs/history-dialog/__snapshots__/history-dialog.spec.tsx.snap
index 6ee82f354b..03cc0ac3ad 100644
--- a/web-console/src/dialogs/history-dialog/__snapshots__/history-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/history-dialog/__snapshots__/history-dialog.spec.tsx.snap
@@ -112,7 +112,7 @@ exports[`HistoryDialog matches snapshot 1`] = `
               role="tabpanel"
             >
               <div
-                class="show-json"
+                class="show-value"
               >
                 <div
                   class="top-actions"
diff --git a/web-console/src/dialogs/index.ts b/web-console/src/dialogs/index.ts
index 588257c84e..1f7d9b30ea 100644
--- a/web-console/src/dialogs/index.ts
+++ b/web-console/src/dialogs/index.ts
@@ -18,7 +18,7 @@
 
 export * from './about-dialog/about-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 './diff-dialog/diff-dialog';
 export * from './doctor-dialog/doctor-dialog';
diff --git a/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.tsx b/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.tsx
index 2262859664..02d9e3c28b 100644
--- a/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.tsx
+++ b/web-console/src/dialogs/supervisor-table-action-dialog/supervisor-table-action-dialog.tsx
@@ -18,8 +18,7 @@
 
 import React, { useState } from 'react';
 
-import { ShowJson } from '../../components';
-import { ShowHistory } from '../../components/show-history/show-history';
+import { ShowJson, SupervisorHistoryPanel } from '../../components';
 import { cleanSpec } from '../../druid-models';
 import { Api } from '../../singletons';
 import { deepGet } from '../../utils';
@@ -96,12 +95,7 @@ export const SupervisorTableActionDialog = React.memo(function SupervisorTableAc
           downloadFilename={`supervisor-payload-${supervisorId}.json`}
         />
       )}
-      {activeTab === 'history' && (
-        <ShowHistory
-          endpoint={`${supervisorEndpointBase}/history`}
-          downloadFilenamePrefix={`supervisor-${supervisorId}`}
-        />
-      )}
+      {activeTab === 'history' && <SupervisorHistoryPanel supervisorId={supervisorId} />}
     </TableActionDialog>
   );
 });
diff --git a/web-console/src/utils/sampler.ts b/web-console/src/utils/sampler.ts
index 46f456d0f0..1860d4fa20 100644
--- a/web-console/src/utils/sampler.ts
+++ b/web-console/src/utils/sampler.ts
@@ -16,6 +16,7 @@
  * limitations under the License.
  */
 
+import { dedupe } from 'druid-query-toolkit';
 import * as JSONBig from 'json-bigint-native';
 
 import type {
@@ -91,18 +92,6 @@ export interface ExampleManifest {
   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 {
   return filterMap(sampleResponse.data, d => d.input).slice(0, 20);
 }
diff --git a/web-console/src/views/datasources-view/datasources-view.tsx b/web-console/src/views/datasources-view/datasources-view.tsx
index e109b02ac2..16a6aadf74 100644
--- a/web-console/src/views/datasources-view/datasources-view.tsx
+++ b/web-console/src/views/datasources-view/datasources-view.tsx
@@ -39,7 +39,7 @@ import {
 } from '../../components';
 import {
   AsyncActionDialog,
-  CompactionDialog,
+  CompactionConfigDialog,
   KillDatasourceDialog,
   RetentionDialog,
 } from '../../dialogs';
@@ -233,7 +233,7 @@ interface RetentionDialogOpenOn {
   readonly rules: Rule[];
 }
 
-interface CompactionDialogOpenOn {
+interface CompactionConfigDialogOpenOn {
   readonly datasource: string;
   readonly compactionConfig?: CompactionConfig;
 }
@@ -254,7 +254,7 @@ export interface DatasourcesViewState {
 
   showUnused: boolean;
   retentionDialogOpenOn?: RetentionDialogOpenOn;
-  compactionDialogOpenOn?: CompactionDialogOpenOn;
+  compactionDialogOpenOn?: CompactionConfigDialogOpenOn;
   datasourceToMarkAsUnusedAllSegmentsIn?: string;
   datasourceToMarkAllNonOvershadowedSegmentsAsUsedIn?: string;
   killDatasource?: string;
@@ -981,12 +981,12 @@ ORDER BY 1`;
     );
   }
 
-  private renderCompactionDialog() {
+  private renderCompactionConfigDialog() {
     const { datasourcesAndDefaultRulesState, compactionDialogOpenOn } = this.state;
     if (!compactionDialogOpenOn || !datasourcesAndDefaultRulesState.data) return;
 
     return (
-      <CompactionDialog
+      <CompactionConfigDialog
         datasource={compactionDialogOpenOn.datasource}
         compactionConfig={compactionDialogOpenOn.compactionConfig}
         onClose={() => this.setState({ compactionDialogOpenOn: undefined })}
@@ -1571,7 +1571,7 @@ ORDER BY 1`;
         {this.renderUseUnuseActionByInterval()}
         {this.renderKillAction()}
         {this.renderRetentionDialog()}
-        {this.renderCompactionDialog()}
+        {this.renderCompactionConfigDialog()}
         {this.renderForceCompactAction()}
       </div>
     );


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org