quick fix the tier selector (#14143)

This commit is contained in:
Vadim Ogievetsky 2023-04-21 17:21:00 -07:00 committed by GitHub
parent f643abdad9
commit b95708f389
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 475 additions and 458 deletions

View File

@ -174,6 +174,66 @@ exports[`RuleEditor matches snapshot no tier in rule 1`] = `
<div <div
class="bp4-control-group" class="bp4-control-group"
> >
<button
class="bp4-button bp4-minimal"
style="pointer-events: none;"
type="button"
>
<span
class="bp4-button-text"
>
Tier:
</span>
</button>
<div
class="bp4-html-select bp4-fill"
>
<select>
<option
value="test1"
>
test1
</option>
<option
value="test"
>
test
</option>
<option
value="test"
>
test
</option>
<option
value="test"
>
test
</option>
</select>
<span
class="bp4-icon bp4-icon-double-caret-vertical"
icon="double-caret-vertical"
>
<svg
aria-labelledby="iconTitle-3"
data-icon="double-caret-vertical"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
>
<title
id="iconTitle-3"
>
Open dropdown
</title>
<path
d="M5 7h6a1.003 1.003 0 00.71-1.71l-3-3C8.53 2.11 8.28 2 8 2s-.53.11-.71.29l-3 3A1.003 1.003 0 005 7zm6 2H5a1.003 1.003 0 00-.71 1.71l3 3c.18.18.43.29.71.29s.53-.11.71-.29l3-3A1.003 1.003 0 0011 9z"
fill-rule="evenodd"
/>
</svg>
</span>
</div>
<button <button
class="bp4-button bp4-minimal" class="bp4-button bp4-minimal"
style="pointer-events: none;" style="pointer-events: none;"
@ -260,66 +320,6 @@ exports[`RuleEditor matches snapshot no tier in rule 1`] = `
</button> </button>
</div> </div>
</div> </div>
<button
class="bp4-button bp4-minimal"
style="pointer-events: none;"
type="button"
>
<span
class="bp4-button-text"
>
Tier:
</span>
</button>
<div
class="bp4-html-select bp4-fill"
>
<select>
<option
value="test1"
>
test1
</option>
<option
value="test"
>
test
</option>
<option
value="test"
>
test
</option>
<option
value="test"
>
test
</option>
</select>
<span
class="bp4-icon bp4-icon-double-caret-vertical"
icon="double-caret-vertical"
>
<svg
aria-labelledby="iconTitle-5"
data-icon="double-caret-vertical"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
>
<title
id="iconTitle-5"
>
Open dropdown
</title>
<path
d="M5 7h6a1.003 1.003 0 00.71-1.71l-3-3C8.53 2.11 8.28 2 8 2s-.53.11-.71.29l-3 3A1.003 1.003 0 005 7zm6 2H5a1.003 1.003 0 00-.71 1.71l3 3c.18.18.43.29.71.29s.53-.11.71-.29l3-3A1.003 1.003 0 0011 9z"
fill-rule="evenodd"
/>
</svg>
</span>
</div>
<button <button
class="bp4-button bp4-disabled" class="bp4-button bp4-disabled"
disabled="" disabled=""
@ -755,6 +755,61 @@ exports[`RuleEditor matches snapshot with existing tier and non existing tier in
<div <div
class="bp4-control-group" class="bp4-control-group"
> >
<button
class="bp4-button bp4-minimal"
style="pointer-events: none;"
type="button"
>
<span
class="bp4-button-text"
>
Tier:
</span>
</button>
<div
class="bp4-html-select bp4-fill"
>
<select>
<option
value="nonexist"
>
nonexist
</option>
<option
value="test2"
>
test2
</option>
<option
value="test3"
>
test3
</option>
</select>
<span
class="bp4-icon bp4-icon-double-caret-vertical"
icon="double-caret-vertical"
>
<svg
aria-labelledby="iconTitle-27"
data-icon="double-caret-vertical"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
>
<title
id="iconTitle-27"
>
Open dropdown
</title>
<path
d="M5 7h6a1.003 1.003 0 00.71-1.71l-3-3C8.53 2.11 8.28 2 8 2s-.53.11-.71.29l-3 3A1.003 1.003 0 005 7zm6 2H5a1.003 1.003 0 00-.71 1.71l3 3c.18.18.43.29.71.29s.53-.11.71-.29l3-3A1.003 1.003 0 0011 9z"
fill-rule="evenodd"
/>
</svg>
</span>
</div>
<button <button
class="bp4-button bp4-minimal" class="bp4-button bp4-minimal"
style="pointer-events: none;" style="pointer-events: none;"
@ -841,61 +896,6 @@ exports[`RuleEditor matches snapshot with existing tier and non existing tier in
</button> </button>
</div> </div>
</div> </div>
<button
class="bp4-button bp4-minimal"
style="pointer-events: none;"
type="button"
>
<span
class="bp4-button-text"
>
Tier:
</span>
</button>
<div
class="bp4-html-select bp4-fill"
>
<select>
<option
value="nonexist"
>
nonexist
</option>
<option
value="test2"
>
test2
</option>
<option
value="test3"
>
test3
</option>
</select>
<span
class="bp4-icon bp4-icon-double-caret-vertical"
icon="double-caret-vertical"
>
<svg
aria-labelledby="iconTitle-29"
data-icon="double-caret-vertical"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
>
<title
id="iconTitle-29"
>
Open dropdown
</title>
<path
d="M5 7h6a1.003 1.003 0 00.71-1.71l-3-3C8.53 2.11 8.28 2 8 2s-.53.11-.71.29l-3 3A1.003 1.003 0 005 7zm6 2H5a1.003 1.003 0 00-.71 1.71l3 3c.18.18.43.29.71.29s.53-.11.71-.29l3-3A1.003 1.003 0 0011 9z"
fill-rule="evenodd"
/>
</svg>
</span>
</div>
<button <button
class="bp4-button" class="bp4-button"
type="button" type="button"
@ -923,6 +923,61 @@ exports[`RuleEditor matches snapshot with existing tier and non existing tier in
<div <div
class="bp4-control-group" class="bp4-control-group"
> >
<button
class="bp4-button bp4-minimal"
style="pointer-events: none;"
type="button"
>
<span
class="bp4-button-text"
>
Tier:
</span>
</button>
<div
class="bp4-html-select bp4-fill"
>
<select>
<option
value="test1"
>
test1
</option>
<option
value="test2"
>
test2
</option>
<option
value="test3"
>
test3
</option>
</select>
<span
class="bp4-icon bp4-icon-double-caret-vertical"
icon="double-caret-vertical"
>
<svg
aria-labelledby="iconTitle-31"
data-icon="double-caret-vertical"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
>
<title
id="iconTitle-31"
>
Open dropdown
</title>
<path
d="M5 7h6a1.003 1.003 0 00.71-1.71l-3-3C8.53 2.11 8.28 2 8 2s-.53.11-.71.29l-3 3A1.003 1.003 0 005 7zm6 2H5a1.003 1.003 0 00-.71 1.71l3 3c.18.18.43.29.71.29s.53-.11.71-.29l3-3A1.003 1.003 0 0011 9z"
fill-rule="evenodd"
/>
</svg>
</span>
</div>
<button <button
class="bp4-button bp4-minimal" class="bp4-button bp4-minimal"
style="pointer-events: none;" style="pointer-events: none;"
@ -1009,61 +1064,6 @@ exports[`RuleEditor matches snapshot with existing tier and non existing tier in
</button> </button>
</div> </div>
</div> </div>
<button
class="bp4-button bp4-minimal"
style="pointer-events: none;"
type="button"
>
<span
class="bp4-button-text"
>
Tier:
</span>
</button>
<div
class="bp4-html-select bp4-fill"
>
<select>
<option
value="test1"
>
test1
</option>
<option
value="test2"
>
test2
</option>
<option
value="test3"
>
test3
</option>
</select>
<span
class="bp4-icon bp4-icon-double-caret-vertical"
icon="double-caret-vertical"
>
<svg
aria-labelledby="iconTitle-33"
data-icon="double-caret-vertical"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
>
<title
id="iconTitle-33"
>
Open dropdown
</title>
<path
d="M5 7h6a1.003 1.003 0 00.71-1.71l-3-3C8.53 2.11 8.28 2 8 2s-.53.11-.71.29l-3 3A1.003 1.003 0 005 7zm6 2H5a1.003 1.003 0 00-.71 1.71l3 3c.18.18.43.29.71.29s.53-.11.71-.29l3-3A1.003 1.003 0 0011 9z"
fill-rule="evenodd"
/>
</svg>
</span>
</div>
<button <button
class="bp4-button" class="bp4-button"
type="button" type="button"
@ -1316,6 +1316,61 @@ exports[`RuleEditor matches snapshot with existing tier in rule 1`] = `
<div <div
class="bp4-control-group" class="bp4-control-group"
> >
<button
class="bp4-button bp4-minimal"
style="pointer-events: none;"
type="button"
>
<span
class="bp4-button-text"
>
Tier:
</span>
</button>
<div
class="bp4-html-select bp4-fill"
>
<select>
<option
value="test1"
>
test1
</option>
<option
value="test2"
>
test2
</option>
<option
value="test3"
>
test3
</option>
</select>
<span
class="bp4-icon bp4-icon-double-caret-vertical"
icon="double-caret-vertical"
>
<svg
aria-labelledby="iconTitle-19"
data-icon="double-caret-vertical"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
>
<title
id="iconTitle-19"
>
Open dropdown
</title>
<path
d="M5 7h6a1.003 1.003 0 00.71-1.71l-3-3C8.53 2.11 8.28 2 8 2s-.53.11-.71.29l-3 3A1.003 1.003 0 005 7zm6 2H5a1.003 1.003 0 00-.71 1.71l3 3c.18.18.43.29.71.29s.53-.11.71-.29l3-3A1.003 1.003 0 0011 9z"
fill-rule="evenodd"
/>
</svg>
</span>
</div>
<button <button
class="bp4-button bp4-minimal" class="bp4-button bp4-minimal"
style="pointer-events: none;" style="pointer-events: none;"
@ -1402,61 +1457,6 @@ exports[`RuleEditor matches snapshot with existing tier in rule 1`] = `
</button> </button>
</div> </div>
</div> </div>
<button
class="bp4-button bp4-minimal"
style="pointer-events: none;"
type="button"
>
<span
class="bp4-button-text"
>
Tier:
</span>
</button>
<div
class="bp4-html-select bp4-fill"
>
<select>
<option
value="test1"
>
test1
</option>
<option
value="test2"
>
test2
</option>
<option
value="test3"
>
test3
</option>
</select>
<span
class="bp4-icon bp4-icon-double-caret-vertical"
icon="double-caret-vertical"
>
<svg
aria-labelledby="iconTitle-21"
data-icon="double-caret-vertical"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
>
<title
id="iconTitle-21"
>
Open dropdown
</title>
<path
d="M5 7h6a1.003 1.003 0 00.71-1.71l-3-3C8.53 2.11 8.28 2 8 2s-.53.11-.71.29l-3 3A1.003 1.003 0 005 7zm6 2H5a1.003 1.003 0 00-.71 1.71l3 3c.18.18.43.29.71.29s.53-.11.71-.29l3-3A1.003 1.003 0 0011 9z"
fill-rule="evenodd"
/>
</svg>
</span>
</div>
<button <button
class="bp4-button bp4-disabled" class="bp4-button bp4-disabled"
disabled="" disabled=""
@ -1711,6 +1711,66 @@ exports[`RuleEditor matches snapshot with non existing tier in rule 1`] = `
<div <div
class="bp4-control-group" class="bp4-control-group"
> >
<button
class="bp4-button bp4-minimal"
style="pointer-events: none;"
type="button"
>
<span
class="bp4-button-text"
>
Tier:
</span>
</button>
<div
class="bp4-html-select bp4-fill"
>
<select>
<option
value="nonexist"
>
nonexist
</option>
<option
value="test1"
>
test1
</option>
<option
value="test2"
>
test2
</option>
<option
value="test3"
>
test3
</option>
</select>
<span
class="bp4-icon bp4-icon-double-caret-vertical"
icon="double-caret-vertical"
>
<svg
aria-labelledby="iconTitle-11"
data-icon="double-caret-vertical"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
>
<title
id="iconTitle-11"
>
Open dropdown
</title>
<path
d="M5 7h6a1.003 1.003 0 00.71-1.71l-3-3C8.53 2.11 8.28 2 8 2s-.53.11-.71.29l-3 3A1.003 1.003 0 005 7zm6 2H5a1.003 1.003 0 00-.71 1.71l3 3c.18.18.43.29.71.29s.53-.11.71-.29l3-3A1.003 1.003 0 0011 9z"
fill-rule="evenodd"
/>
</svg>
</span>
</div>
<button <button
class="bp4-button bp4-minimal" class="bp4-button bp4-minimal"
style="pointer-events: none;" style="pointer-events: none;"
@ -1797,66 +1857,6 @@ exports[`RuleEditor matches snapshot with non existing tier in rule 1`] = `
</button> </button>
</div> </div>
</div> </div>
<button
class="bp4-button bp4-minimal"
style="pointer-events: none;"
type="button"
>
<span
class="bp4-button-text"
>
Tier:
</span>
</button>
<div
class="bp4-html-select bp4-fill"
>
<select>
<option
value="nonexist"
>
nonexist
</option>
<option
value="test1"
>
test1
</option>
<option
value="test2"
>
test2
</option>
<option
value="test3"
>
test3
</option>
</select>
<span
class="bp4-icon bp4-icon-double-caret-vertical"
icon="double-caret-vertical"
>
<svg
aria-labelledby="iconTitle-13"
data-icon="double-caret-vertical"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
>
<title
id="iconTitle-13"
>
Open dropdown
</title>
<path
d="M5 7h6a1.003 1.003 0 00.71-1.71l-3-3C8.53 2.11 8.28 2 8 2s-.53.11-.71.29l-3 3A1.003 1.003 0 005 7zm6 2H5a1.003 1.003 0 00-.71 1.71l3 3c.18.18.43.29.71.29s.53-.11.71-.29l3-3A1.003 1.003 0 0011 9z"
fill-rule="evenodd"
/>
</svg>
</span>
</div>
<button <button
class="bp4-button bp4-disabled" class="bp4-button bp4-disabled"
disabled="" disabled=""

View File

@ -41,9 +41,9 @@ const PERIOD_SUGGESTIONS: string[] = ['P1D', 'P7D', 'P1M', 'P1Y', 'P1000Y'];
export interface RuleEditorProps { export interface RuleEditorProps {
rule: Rule; rule: Rule;
tiers: any[]; tiers: string[];
onChange: (newRule: Rule) => void; onChange(newRule: Rule): void;
onDelete: () => void; onDelete(): void;
moveUp: (() => void) | undefined; moveUp: (() => void) | undefined;
moveDown: (() => void) | undefined; moveDown: (() => void) | undefined;
} }
@ -83,18 +83,6 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps)
return ruleTiers.map(tier => { return ruleTiers.map(tier => {
return ( return (
<ControlGroup key={tier}> <ControlGroup key={tier}>
<Button minimal style={{ pointerEvents: 'none' }}>
Replicants:
</Button>
<NumericInput
value={tieredReplicants[tier]}
onValueChange={(v: number) => {
if (isNaN(v)) return;
onChange(RuleUtil.addTieredReplicant(rule, tier, v));
}}
min={0}
max={256}
/>
<Button minimal style={{ pointerEvents: 'none' }}> <Button minimal style={{ pointerEvents: 'none' }}>
Tier: Tier:
</Button> </Button>
@ -110,14 +98,24 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps)
</option> </option>
{tiers {tiers
.filter(t => t !== tier && !tieredReplicants[t]) .filter(t => t !== tier && !tieredReplicants[t])
.map(t => { .map(t => (
return ( <option key={t} value={t}>
<option key={t} value={t}> {t}
{t} </option>
</option> ))}
);
})}
</HTMLSelect> </HTMLSelect>
<Button minimal style={{ pointerEvents: 'none' }}>
Replicants:
</Button>
<NumericInput
value={tieredReplicants[tier]}
onValueChange={(v: number) => {
if (isNaN(v)) return;
onChange(RuleUtil.addTieredReplicant(rule, tier, v));
}}
min={0}
max={256}
/>
<Button <Button
disabled={ruleTiers.length === 1} disabled={ruleTiers.length === 1}
onClick={() => removeTier(tier)} onClick={() => removeTier(tier)}
@ -166,13 +164,11 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps)
value={rule.type} value={rule.type}
onChange={(e: any) => onChange(RuleUtil.changeRuleType(rule, e.target.value))} onChange={(e: any) => onChange(RuleUtil.changeRuleType(rule, e.target.value))}
> >
{RuleUtil.TYPES.map(type => { {RuleUtil.TYPES.map(type => (
return ( <option key={type} value={type}>
<option key={type} value={type}> {type}
{type} </option>
</option> ))}
);
})}
</HTMLSelect> </HTMLSelect>
{RuleUtil.hasPeriod(rule) && ( {RuleUtil.hasPeriod(rule) && (
<SuggestibleInput <SuggestibleInput

View File

@ -72,6 +72,38 @@ exports[`RetentionDialog matches snapshot 1`] = `
</a> </a>
. .
</p> </p>
<div
class="bp4-form-group form-json-selector"
>
<div
class="bp4-form-content"
>
<div
class="bp4-button-group bp4-fill"
>
<button
class="bp4-button bp4-active"
type="button"
>
<span
class="bp4-button-text"
>
Form
</span>
</button>
<button
class="bp4-button"
type="button"
>
<span
class="bp4-button-text"
>
JSON
</span>
</button>
</div>
</div>
</div>
<div <div
class="bp4-form-group" class="bp4-form-group"
> >
@ -310,6 +342,51 @@ exports[`RetentionDialog matches snapshot 1`] = `
<div <div
class="bp4-control-group" class="bp4-control-group"
> >
<button
class="bp4-button bp4-minimal"
style="pointer-events: none;"
type="button"
>
<span
class="bp4-button-text"
>
Tier:
</span>
</button>
<div
class="bp4-html-select bp4-fill"
>
<select>
<option
value="_default_tier"
>
_default_tier
</option>
</select>
<span
class="bp4-icon bp4-icon-double-caret-vertical"
icon="double-caret-vertical"
>
<svg
aria-labelledby="iconTitle-5"
data-icon="double-caret-vertical"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
>
<title
id="iconTitle-5"
>
Open dropdown
</title>
<path
d="M5 7h6a1.003 1.003 0 00.71-1.71l-3-3C8.53 2.11 8.28 2 8 2s-.53.11-.71.29l-3 3A1.003 1.003 0 005 7zm6 2H5a1.003 1.003 0 00-.71 1.71l3 3c.18.18.43.29.71.29s.53-.11.71-.29l3-3A1.003 1.003 0 0011 9z"
fill-rule="evenodd"
/>
</svg>
</span>
</div>
<button <button
class="bp4-button bp4-minimal" class="bp4-button bp4-minimal"
style="pointer-events: none;" style="pointer-events: none;"
@ -396,61 +473,6 @@ exports[`RetentionDialog matches snapshot 1`] = `
</button> </button>
</div> </div>
</div> </div>
<button
class="bp4-button bp4-minimal"
style="pointer-events: none;"
type="button"
>
<span
class="bp4-button-text"
>
Tier:
</span>
</button>
<div
class="bp4-html-select bp4-fill"
>
<select>
<option
value="_default_tier"
>
_default_tier
</option>
<option
value="tier1"
>
tier1
</option>
<option
value="tier2"
>
tier2
</option>
</select>
<span
class="bp4-icon bp4-icon-double-caret-vertical"
icon="double-caret-vertical"
>
<svg
aria-labelledby="iconTitle-7"
data-icon="double-caret-vertical"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
>
<title
id="iconTitle-7"
>
Open dropdown
</title>
<path
d="M5 7h6a1.003 1.003 0 00.71-1.71l-3-3C8.53 2.11 8.28 2 8 2s-.53.11-.71.29l-3 3A1.003 1.003 0 005 7zm6 2H5a1.003 1.003 0 00-.71 1.71l3 3c.18.18.43.29.71.29s.53-.11.71-.29l3-3A1.003 1.003 0 0011 9z"
fill-rule="evenodd"
/>
</svg>
</span>
</div>
<button <button
class="bp4-button bp4-disabled" class="bp4-button bp4-disabled"
disabled="" disabled=""
@ -477,42 +499,6 @@ exports[`RetentionDialog matches snapshot 1`] = `
</span> </span>
</button> </button>
</div> </div>
<div
class="bp4-form-group right"
>
<div
class="bp4-form-content"
>
<button
class="bp4-button bp4-minimal"
type="button"
>
<span
aria-hidden="true"
class="bp4-icon bp4-icon-plus"
icon="plus"
>
<svg
data-icon="plus"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
>
<path
d="M13 7H9V3c0-.55-.45-1-1-1s-1 .45-1 1v4H3c-.55 0-1 .45-1 1s.45 1 1 1h4v4c0 .55.45 1 1 1s1-.45 1-1V9h4c.55 0 1-.45 1-1s-.45-1-1-1z"
fill-rule="evenodd"
/>
</svg>
</span>
<span
class="bp4-button-text"
>
Add a tier
</span>
</button>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -22,9 +22,13 @@
&.#{$bp-ns}-dialog { &.#{$bp-ns}-dialog {
top: 5%; top: 5%;
width: 750px; width: 750px;
height: 80vh;
} }
.#{$bp-ns}-dialog-body { .#{$bp-ns}-dialog-body {
display: flex;
flex-direction: column;
.rule-editor { .rule-editor {
margin-bottom: 15px; margin-bottom: 15px;
} }

View File

@ -19,6 +19,8 @@
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import React from 'react'; import React from 'react';
import { Capabilities } from '../../helpers';
import { RetentionDialog } from './retention-dialog'; import { RetentionDialog } from './retention-dialog';
describe('RetentionDialog', () => { describe('RetentionDialog', () => {
@ -35,7 +37,7 @@ describe('RetentionDialog', () => {
}, },
]} ]}
defaultRules={[{ tieredReplicants: { _default_tier: 2 }, type: 'loadForever' }]} defaultRules={[{ tieredReplicants: { _default_tier: 2 }, type: 'loadForever' }]}
tiers={['tier1', 'tier2']} capabilities={Capabilities.FULL}
onEditDefaults={() => {}} onEditDefaults={() => {}}
onCancel={() => {}} onCancel={() => {}}
onSave={() => {}} onSave={() => {}}

View File

@ -16,43 +16,75 @@
* limitations under the License. * 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 { IconNames } from '@blueprintjs/icons';
import React, { useState } from 'react'; 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 { useQueryManager } from '../../hooks';
import { getLink } from '../../links'; import { getLink } from '../../links';
import { Api } from '../../singletons'; import { Api } from '../../singletons';
import { swapElements } from '../../utils'; import { filterMap, queryDruidSql, swapElements } from '../../utils';
import type { Rule } from '../../utils/load-rule'; import type { Rule } from '../../utils/load-rule';
import { RuleUtil } from '../../utils/load-rule'; import { RuleUtil } from '../../utils/load-rule';
import { SnitchDialog } from '..'; import { SnitchDialog } from '..';
import './retention-dialog.scss'; import './retention-dialog.scss';
const CLUSTER_DEFAULT_FAKE_DATASOURCE = '_default';
export interface RetentionDialogProps { export interface RetentionDialogProps {
datasource: string; datasource: string;
rules: Rule[]; rules: Rule[];
defaultRules: Rule[]; defaultRules: Rule[];
tiers: string[]; capabilities: Capabilities;
onEditDefaults: () => void; onEditDefaults(): void;
onCancel: () => void; onCancel(): void;
onSave: (datasource: string, newRules: Rule[], comment: string) => void | Promise<void>; onSave(datasource: string, newRules: Rule[], comment: string): void | Promise<void>;
} }
export const RetentionDialog = React.memo(function RetentionDialog(props: RetentionDialogProps) { 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<FormJsonTabs>('form');
const [currentRules, setCurrentRules] = useState(props.rules); const [currentRules, setCurrentRules] = useState(props.rules);
const [jsonError, setJsonError] = useState<Error | undefined>();
const [tiersState] = useQueryManager<Capabilities, string[]>({
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<string, any[]>({ const [historyQueryState] = useQueryManager<string, any[]>({
initQuery: props.datasource,
processQuery: async datasource => { processQuery: async datasource => {
const historyResp = await Api.instance.get( const historyResp = await Api.instance.get(
`/druid/coordinator/v1/rules/${Api.encodePath(datasource)}/history`, `/druid/coordinator/v1/rules/${Api.encodePath(datasource)}/history`,
); );
return historyResp.data; return historyResp.data;
}, },
initQuery: props.datasource,
}); });
const historyRecords = historyQueryState.data || []; const historyRecords = historyQueryState.data || [];
@ -108,10 +140,10 @@ export const RetentionDialog = React.memo(function RetentionDialog(props: Retent
return ( return (
<SnitchDialog <SnitchDialog
className="retention-dialog" className="retention-dialog"
saveDisabled={false} saveDisabled={Boolean(jsonError)}
onClose={onCancel} onClose={onCancel}
title={`Edit retention rules: ${datasource}${ title={`Edit retention rules: ${datasource}${
datasource === '_default' ? ' (cluster defaults)' : '' datasource === CLUSTER_DEFAULT_FAKE_DATASOURCE ? ' (cluster defaults)' : ''
}`} }`}
onReset={() => setCurrentRules(rules)} onReset={() => setCurrentRules(rules)}
onSave={saveHandler} onSave={saveHandler}
@ -125,21 +157,41 @@ export const RetentionDialog = React.memo(function RetentionDialog(props: Retent
</ExternalLink> </ExternalLink>
. .
</p> </p>
<FormGroup> <FormJsonSelector
{currentRules.length ? ( tab={currentTab}
currentRules.map(renderRule) onChange={t => {
) : datasource !== '_default' ? ( setJsonError(undefined);
<p className="no-rules-message"> setCurrentTab(t);
This datasource currently has no rules, it will use the cluster defaults. }}
</p> />
) : undefined} {currentTab === 'form' ? (
<div> <FormGroup>
<Button icon={IconNames.PLUS} onClick={addRule}> {currentRules.length ? (
New rule currentRules.map(renderRule)
</Button> ) : datasource !== CLUSTER_DEFAULT_FAKE_DATASOURCE ? (
</div> <p className="no-rules-message">
</FormGroup> This datasource currently has no rules, it will use the cluster defaults.
{datasource !== '_default' && ( </p>
) : undefined}
<div>
<Button
icon={IconNames.PLUS}
onClick={addRule}
intent={currentRules.length ? undefined : Intent.PRIMARY}
>
New rule
</Button>
</div>
</FormGroup>
) : (
<JsonInput
value={currentRules}
onChange={setCurrentRules}
onError={setJsonError}
height="100%"
/>
)}
{datasource !== CLUSTER_DEFAULT_FAKE_DATASOURCE && (
<> <>
<Divider /> <Divider />
<FormGroup> <FormGroup>

View File

@ -250,8 +250,6 @@ export interface DatasourcesViewState {
datasourceFilter: Filter[]; datasourceFilter: Filter[];
datasourcesAndDefaultRulesState: QueryState<DatasourcesAndDefaultRules>; datasourcesAndDefaultRulesState: QueryState<DatasourcesAndDefaultRules>;
tiersState: QueryState<string[]>;
showUnused: boolean; showUnused: boolean;
retentionDialogOpenOn?: RetentionDialogOpenOn; retentionDialogOpenOn?: RetentionDialogOpenOn;
compactionDialogOpenOn?: CompactionConfigDialogOpenOn; compactionDialogOpenOn?: CompactionConfigDialogOpenOn;
@ -349,8 +347,6 @@ ORDER BY 1`;
DatasourcesAndDefaultRules DatasourcesAndDefaultRules
>; >;
private readonly tiersQueryManager: QueryManager<Capabilities, string[]>;
constructor(props: DatasourcesViewProps, context: any) { constructor(props: DatasourcesViewProps, context: any) {
super(props, context); super(props, context);
@ -363,8 +359,6 @@ ORDER BY 1`;
datasourceFilter, datasourceFilter,
datasourcesAndDefaultRulesState: QueryState.INIT, datasourcesAndDefaultRulesState: QueryState.INIT,
tiersState: QueryState.INIT,
showUnused: false, showUnused: false,
useUnuseAction: 'unuse', useUnuseAction: 'unuse',
useUnuseInterval: '', 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 => { private readonly refresh = (auto: boolean): void => {
if (auto && hasPopoverOpen()) return; if (auto && hasPopoverOpen()) return;
this.datasourceQueryManager.rerunLastQuery(auto); this.datasourceQueryManager.rerunLastQuery(auto);
this.tiersQueryManager.rerunLastQuery(auto);
}; };
private fetchDatasourceData() { private fetchDatasourceData() {
@ -554,14 +533,11 @@ ORDER BY 1`;
} }
componentDidMount(): void { componentDidMount(): void {
const { capabilities } = this.props;
this.fetchDatasourceData(); this.fetchDatasourceData();
this.tiersQueryManager.runQuery(capabilities);
} }
componentWillUnmount(): void { componentWillUnmount(): void {
this.datasourceQueryManager.terminate(); this.datasourceQueryManager.terminate();
this.tiersQueryManager.terminate();
} }
renderUnuseAction() { renderUnuseAction() {
@ -964,7 +940,8 @@ ORDER BY 1`;
} }
private renderRetentionDialog(): JSX.Element | undefined { 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; const defaultRules = datasourcesAndDefaultRulesState.data?.defaultRules;
if (!retentionDialogOpenOn || !defaultRules) return; if (!retentionDialogOpenOn || !defaultRules) return;
@ -972,7 +949,7 @@ ORDER BY 1`;
<RetentionDialog <RetentionDialog
datasource={retentionDialogOpenOn.datasource} datasource={retentionDialogOpenOn.datasource}
rules={retentionDialogOpenOn.rules} rules={retentionDialogOpenOn.rules}
tiers={tiersState.data || []} capabilities={capabilities}
onEditDefaults={this.editDefaultRules} onEditDefaults={this.editDefaultRules}
defaultRules={defaultRules} defaultRules={defaultRules}
onCancel={() => this.setState({ retentionDialogOpenOn: undefined })} onCancel={() => this.setState({ retentionDialogOpenOn: undefined })}