From b95708f389a4a86b0f3ec4a2cc5d53d157a27d03 Mon Sep 17 00:00:00 2001
From: Vadim Ogievetsky
Date: Fri, 21 Apr 2023 17:21:00 -0700
Subject: [PATCH] quick fix the tier selector (#14143)
---
.../__snapshots__/rule-editor.spec.tsx.snap | 570 +++++++++---------
.../components/rule-editor/rule-editor.tsx | 54 +-
.../retention-dialog.spec.tsx.snap | 168 +++---
.../retention-dialog/retention-dialog.scss | 4 +
.../retention-dialog.spec.tsx | 4 +-
.../retention-dialog/retention-dialog.tsx | 104 +++-
.../datasources-view/datasources-view.tsx | 29 +-
7 files changed, 475 insertions(+), 458 deletions(-)
diff --git a/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap b/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap
index 3db574c8fe0..58a3de27e45 100644
--- a/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap
+++ b/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap
@@ -174,6 +174,66 @@ exports[`RuleEditor matches snapshot no tier in rule 1`] = `
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
-
-
- Tier:
-
-
-
-
-
-
-
-
-
diff --git a/web-console/src/dialogs/retention-dialog/retention-dialog.scss b/web-console/src/dialogs/retention-dialog/retention-dialog.scss
index 7fa00021ada..df521abbd11 100644
--- a/web-console/src/dialogs/retention-dialog/retention-dialog.scss
+++ b/web-console/src/dialogs/retention-dialog/retention-dialog.scss
@@ -22,9 +22,13 @@
&.#{$bp-ns}-dialog {
top: 5%;
width: 750px;
+ height: 80vh;
}
.#{$bp-ns}-dialog-body {
+ display: flex;
+ flex-direction: column;
+
.rule-editor {
margin-bottom: 15px;
}
diff --git a/web-console/src/dialogs/retention-dialog/retention-dialog.spec.tsx b/web-console/src/dialogs/retention-dialog/retention-dialog.spec.tsx
index afee143a06e..1e5f09fd00a 100644
--- a/web-console/src/dialogs/retention-dialog/retention-dialog.spec.tsx
+++ b/web-console/src/dialogs/retention-dialog/retention-dialog.spec.tsx
@@ -19,6 +19,8 @@
import { render } from '@testing-library/react';
import React from 'react';
+import { Capabilities } from '../../helpers';
+
import { RetentionDialog } from './retention-dialog';
describe('RetentionDialog', () => {
@@ -35,7 +37,7 @@ describe('RetentionDialog', () => {
},
]}
defaultRules={[{ tieredReplicants: { _default_tier: 2 }, type: 'loadForever' }]}
- tiers={['tier1', 'tier2']}
+ capabilities={Capabilities.FULL}
onEditDefaults={() => {}}
onCancel={() => {}}
onSave={() => {}}
diff --git a/web-console/src/dialogs/retention-dialog/retention-dialog.tsx b/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
index 723bf23b9be..4d50d84c3e1 100644
--- a/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
+++ b/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
@@ -16,43 +16,75 @@
* limitations under the License.
*/
-import { Button, Divider, FormGroup } from '@blueprintjs/core';
+import { Button, Divider, FormGroup, Intent } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import React, { useState } from 'react';
-import { ExternalLink, RuleEditor } from '../../components';
+import type { FormJsonTabs } from '../../components';
+import { ExternalLink, FormJsonSelector, JsonInput, RuleEditor } from '../../components';
+import type { Capabilities } from '../../helpers';
import { useQueryManager } from '../../hooks';
import { getLink } from '../../links';
import { Api } from '../../singletons';
-import { swapElements } from '../../utils';
+import { filterMap, queryDruidSql, swapElements } from '../../utils';
import type { Rule } from '../../utils/load-rule';
import { RuleUtil } from '../../utils/load-rule';
import { SnitchDialog } from '..';
import './retention-dialog.scss';
+const CLUSTER_DEFAULT_FAKE_DATASOURCE = '_default';
+
export interface RetentionDialogProps {
datasource: string;
rules: Rule[];
defaultRules: Rule[];
- tiers: string[];
- onEditDefaults: () => void;
- onCancel: () => void;
- onSave: (datasource: string, newRules: Rule[], comment: string) => void | Promise;
+ capabilities: Capabilities;
+ onEditDefaults(): void;
+ onCancel(): void;
+ onSave(datasource: string, newRules: Rule[], comment: string): void | Promise;
}
export const RetentionDialog = React.memo(function RetentionDialog(props: RetentionDialogProps) {
- const { datasource, onCancel, onEditDefaults, rules, defaultRules, tiers } = props;
+ const { datasource, onCancel, onEditDefaults, rules, defaultRules, capabilities } = props;
+ const [currentTab, setCurrentTab] = useState('form');
const [currentRules, setCurrentRules] = useState(props.rules);
+ const [jsonError, setJsonError] = useState();
+
+ const [tiersState] = useQueryManager({
+ initQuery: capabilities,
+ processQuery: async capabilities => {
+ if (capabilities.hasSql()) {
+ const sqlResp = await queryDruidSql<{ tier: string }>({
+ query: `SELECT "tier"
+FROM "sys"."servers"
+WHERE "server_type" = 'historical'
+GROUP BY 1
+ORDER BY 1`,
+ });
+
+ return sqlResp.map(d => d.tier);
+ } else if (capabilities.hasCoordinatorAccess()) {
+ const allServiceResp = await Api.instance.get('/druid/coordinator/v1/servers?simple');
+ return filterMap(allServiceResp.data, (s: any) =>
+ s.type === 'historical' ? s.tier : undefined,
+ );
+ } else {
+ throw new Error(`must have sql or coordinator access`);
+ }
+ },
+ });
+
+ const tiers = tiersState.data || [];
const [historyQueryState] = useQueryManager({
+ initQuery: props.datasource,
processQuery: async datasource => {
const historyResp = await Api.instance.get(
`/druid/coordinator/v1/rules/${Api.encodePath(datasource)}/history`,
);
return historyResp.data;
},
- initQuery: props.datasource,
});
const historyRecords = historyQueryState.data || [];
@@ -108,10 +140,10 @@ export const RetentionDialog = React.memo(function RetentionDialog(props: Retent
return (
setCurrentRules(rules)}
onSave={saveHandler}
@@ -125,21 +157,41 @@ export const RetentionDialog = React.memo(function RetentionDialog(props: Retent
.
-
- {currentRules.length ? (
- currentRules.map(renderRule)
- ) : datasource !== '_default' ? (
-
- This datasource currently has no rules, it will use the cluster defaults.
-
- ) : undefined}
-
-
- New rule
-
-
-
- {datasource !== '_default' && (
+ {
+ setJsonError(undefined);
+ setCurrentTab(t);
+ }}
+ />
+ {currentTab === 'form' ? (
+
+ {currentRules.length ? (
+ currentRules.map(renderRule)
+ ) : datasource !== CLUSTER_DEFAULT_FAKE_DATASOURCE ? (
+
+ This datasource currently has no rules, it will use the cluster defaults.
+
+ ) : undefined}
+
+
+ New rule
+
+
+
+ ) : (
+
+ )}
+ {datasource !== CLUSTER_DEFAULT_FAKE_DATASOURCE && (
<>
diff --git a/web-console/src/views/datasources-view/datasources-view.tsx b/web-console/src/views/datasources-view/datasources-view.tsx
index 16a6aadf744..58a5a13e9d1 100644
--- a/web-console/src/views/datasources-view/datasources-view.tsx
+++ b/web-console/src/views/datasources-view/datasources-view.tsx
@@ -250,8 +250,6 @@ export interface DatasourcesViewState {
datasourceFilter: Filter[];
datasourcesAndDefaultRulesState: QueryState;
- tiersState: QueryState;
-
showUnused: boolean;
retentionDialogOpenOn?: RetentionDialogOpenOn;
compactionDialogOpenOn?: CompactionConfigDialogOpenOn;
@@ -349,8 +347,6 @@ ORDER BY 1`;
DatasourcesAndDefaultRules
>;
- private readonly tiersQueryManager: QueryManager;
-
constructor(props: DatasourcesViewProps, context: any) {
super(props, context);
@@ -363,8 +359,6 @@ ORDER BY 1`;
datasourceFilter,
datasourcesAndDefaultRulesState: QueryState.INIT,
- tiersState: QueryState.INIT,
-
showUnused: false,
useUnuseAction: 'unuse',
useUnuseInterval: '',
@@ -525,26 +519,11 @@ ORDER BY 1`;
});
},
});
-
- this.tiersQueryManager = new QueryManager({
- processQuery: async capabilities => {
- if (capabilities.hasCoordinatorAccess()) {
- const tiersResp = await Api.instance.get('/druid/coordinator/v1/tiers');
- return tiersResp.data;
- } else {
- throw new Error(`must have coordinator access`);
- }
- },
- onStateChange: tiersState => {
- this.setState({ tiersState });
- },
- });
}
private readonly refresh = (auto: boolean): void => {
if (auto && hasPopoverOpen()) return;
this.datasourceQueryManager.rerunLastQuery(auto);
- this.tiersQueryManager.rerunLastQuery(auto);
};
private fetchDatasourceData() {
@@ -554,14 +533,11 @@ ORDER BY 1`;
}
componentDidMount(): void {
- const { capabilities } = this.props;
this.fetchDatasourceData();
- this.tiersQueryManager.runQuery(capabilities);
}
componentWillUnmount(): void {
this.datasourceQueryManager.terminate();
- this.tiersQueryManager.terminate();
}
renderUnuseAction() {
@@ -964,7 +940,8 @@ ORDER BY 1`;
}
private renderRetentionDialog(): JSX.Element | undefined {
- const { retentionDialogOpenOn, tiersState, datasourcesAndDefaultRulesState } = this.state;
+ const { capabilities } = this.props;
+ const { retentionDialogOpenOn, datasourcesAndDefaultRulesState } = this.state;
const defaultRules = datasourcesAndDefaultRulesState.data?.defaultRules;
if (!retentionDialogOpenOn || !defaultRules) return;
@@ -972,7 +949,7 @@ ORDER BY 1`;
this.setState({ retentionDialogOpenOn: undefined })}