mirror of
https://github.com/apache/druid.git
synced 2025-02-17 07:25:02 +00:00
Web console: Force intervals config (#8514)
* make sure intervals are required * all truncated values everywhere * continue to spec when going from tasks table * remove unused thigns * fix alert
This commit is contained in:
parent
0145642d8b
commit
a6eca5e935
@ -16,7 +16,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TextArea } from '@blueprintjs/core';
|
||||
import { Intent, TextArea } from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
|
||||
import { compact } from '../../utils';
|
||||
@ -28,6 +28,7 @@ export interface ArrayInputProps {
|
||||
placeholder?: string;
|
||||
large?: boolean;
|
||||
disabled?: boolean;
|
||||
intent?: Intent;
|
||||
}
|
||||
|
||||
export class ArrayInput extends React.PureComponent<ArrayInputProps, { stringValue: string }> {
|
||||
@ -51,7 +52,7 @@ export class ArrayInput extends React.PureComponent<ArrayInputProps, { stringVal
|
||||
};
|
||||
|
||||
render(): JSX.Element {
|
||||
const { className, placeholder, large, disabled } = this.props;
|
||||
const { className, placeholder, large, disabled, intent } = this.props;
|
||||
const { stringValue } = this.state;
|
||||
return (
|
||||
<TextArea
|
||||
@ -61,6 +62,7 @@ export class ArrayInput extends React.PureComponent<ArrayInputProps, { stringVal
|
||||
placeholder={placeholder}
|
||||
large={large}
|
||||
disabled={disabled}
|
||||
intent={intent}
|
||||
fill
|
||||
/>
|
||||
);
|
||||
|
@ -244,15 +244,21 @@ export class AutoForm<T extends Record<string, any>> extends React.PureComponent
|
||||
|
||||
private renderStringArrayInput(field: Field<T>): JSX.Element {
|
||||
const { model, large } = this.props;
|
||||
const modelValue = deepGet(model as any, field.name);
|
||||
return (
|
||||
<ArrayInput
|
||||
values={deepGet(model as any, field.name) || []}
|
||||
values={modelValue || []}
|
||||
onChange={(v: any) => {
|
||||
this.fieldChange(field, v);
|
||||
}}
|
||||
placeholder={field.placeholder}
|
||||
large={large}
|
||||
disabled={AutoForm.evaluateFunctor(field.disabled, model)}
|
||||
intent={
|
||||
AutoForm.evaluateFunctor(field.required, model) && modelValue == null
|
||||
? Intent.PRIMARY
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -19,15 +19,19 @@
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import React from 'react';
|
||||
|
||||
import { ShowValueDialog } from '../../dialogs/show-value-dialog/show-value-dialog';
|
||||
import { ActionIcon } from '../action-icon/action-icon';
|
||||
|
||||
import './table-cell.scss';
|
||||
|
||||
export interface NullTableCellProps {
|
||||
export interface TableCellProps {
|
||||
value?: any;
|
||||
timestamp?: boolean;
|
||||
unparseable?: boolean;
|
||||
openModal?: (str: string) => void;
|
||||
}
|
||||
|
||||
export interface TableCellState {
|
||||
showValue?: string;
|
||||
}
|
||||
|
||||
interface ShortParts {
|
||||
@ -36,26 +40,9 @@ interface ShortParts {
|
||||
suffix: string;
|
||||
}
|
||||
|
||||
export class TableCell extends React.PureComponent<NullTableCellProps> {
|
||||
export class TableCell extends React.PureComponent<TableCellProps, TableCellState> {
|
||||
static MAX_CHARS_TO_SHOW = 50;
|
||||
|
||||
possiblyTruncate(str: string): React.ReactNode {
|
||||
if (str.length <= TableCell.MAX_CHARS_TO_SHOW) return str;
|
||||
|
||||
const { prefix, omitted, suffix } = TableCell.shortenString(str);
|
||||
return (
|
||||
<span className="table-cell truncated">
|
||||
{prefix}
|
||||
<span className="omitted">{omitted}</span>
|
||||
{suffix}
|
||||
<ActionIcon
|
||||
icon={IconNames.MORE}
|
||||
onClick={() => (this.props.openModal ? this.props.openModal(str) : null)}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
static shortenString(str: string): ShortParts {
|
||||
// Print something like:
|
||||
// BAAAArAAEiQKpDAEAACwZCBAGSBgiSEAAAAQpAIDwAg...23 omitted...gwiRoQBJIC
|
||||
@ -69,6 +56,35 @@ export class TableCell extends React.PureComponent<NullTableCellProps> {
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props: TableCellProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
private renderShowValueDialog(): JSX.Element | undefined {
|
||||
const { showValue } = this.state;
|
||||
if (!showValue) return;
|
||||
|
||||
return (
|
||||
<ShowValueDialog onClose={() => this.setState({ showValue: undefined })} str={showValue} />
|
||||
);
|
||||
}
|
||||
|
||||
private renderTruncated(str: string): React.ReactNode {
|
||||
if (str.length <= TableCell.MAX_CHARS_TO_SHOW) return str;
|
||||
|
||||
const { prefix, omitted, suffix } = TableCell.shortenString(str);
|
||||
return (
|
||||
<span className="table-cell truncated">
|
||||
{prefix}
|
||||
<span className="omitted">{omitted}</span>
|
||||
{suffix}
|
||||
<ActionIcon icon={IconNames.MORE} onClick={() => this.setState({ showValue: str })} />
|
||||
{this.renderShowValueDialog()}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
const { value, timestamp, unparseable } = this.props;
|
||||
if (unparseable) {
|
||||
@ -81,9 +97,9 @@ export class TableCell extends React.PureComponent<NullTableCellProps> {
|
||||
</span>
|
||||
);
|
||||
} else if (Array.isArray(value)) {
|
||||
return this.possiblyTruncate(`[${value.join(', ')}]`);
|
||||
return this.renderTruncated(`[${value.join(', ')}]`);
|
||||
} else {
|
||||
return this.possiblyTruncate(String(value));
|
||||
return this.renderTruncated(String(value));
|
||||
}
|
||||
} else {
|
||||
if (timestamp) {
|
||||
|
@ -588,7 +588,7 @@ export interface GranularitySpec {
|
||||
queryGranularity?: string;
|
||||
segmentGranularity?: string;
|
||||
rollup?: boolean;
|
||||
intervals?: string;
|
||||
intervals?: string | string[];
|
||||
}
|
||||
|
||||
export interface MetricSpec {
|
||||
@ -1541,11 +1541,11 @@ export interface TuningConfig {
|
||||
fetchThreads?: number;
|
||||
}
|
||||
|
||||
export function invalidTuningConfig(tuningConfig: TuningConfig): boolean {
|
||||
export function invalidTuningConfig(tuningConfig: TuningConfig, intervals: any): boolean {
|
||||
return Boolean(
|
||||
tuningConfig.type === 'index_parallel' &&
|
||||
tuningConfig.forceGuaranteedRollup &&
|
||||
!tuningConfig.numShards,
|
||||
(!tuningConfig.numShards || !intervals),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,6 @@ import {
|
||||
Loader,
|
||||
} from '../../components';
|
||||
import { AsyncActionDialog } from '../../dialogs';
|
||||
import { ShowValueDialog } from '../../dialogs/show-value-dialog/show-value-dialog';
|
||||
import { AppToaster } from '../../singletons/toaster';
|
||||
import { UrlBaser } from '../../singletons/url-baser';
|
||||
import {
|
||||
@ -251,8 +250,6 @@ export interface LoadDataViewState {
|
||||
showResetConfirm: boolean;
|
||||
newRollup?: boolean;
|
||||
newDimensionMode?: DimensionMode;
|
||||
showViewValueModal: boolean;
|
||||
str: string;
|
||||
|
||||
// welcome
|
||||
overlordModules?: string[];
|
||||
@ -317,8 +314,6 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
|
||||
|
||||
// dialogs / modals
|
||||
showResetConfirm: false,
|
||||
showViewValueModal: false,
|
||||
str: '',
|
||||
|
||||
// general
|
||||
sampleStrategy: 'start',
|
||||
@ -507,7 +502,6 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
|
||||
{step === 'loading' && this.renderLoading()}
|
||||
|
||||
{this.renderResetConfirm()}
|
||||
{this.renderViewValueModal()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -651,15 +645,6 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
|
||||
);
|
||||
}
|
||||
|
||||
renderViewValueModal(): JSX.Element | undefined {
|
||||
const { showViewValueModal, str } = this.state;
|
||||
if (!showViewValueModal) return;
|
||||
|
||||
return (
|
||||
<ShowValueDialog onClose={() => this.setState({ showViewValueModal: false })} str={str} />
|
||||
);
|
||||
}
|
||||
|
||||
renderWelcomeStepMessage(): JSX.Element | undefined {
|
||||
const { selectedComboType, exampleManifests } = this.state;
|
||||
|
||||
@ -1225,7 +1210,6 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
|
||||
)}
|
||||
</div>
|
||||
<ParseDataTable
|
||||
openModal={str => this.setState({ showViewValueModal: true, str: str })}
|
||||
sampleData={parserQueryState.data}
|
||||
columnFilter={columnFilter}
|
||||
canFlatten={canFlatten}
|
||||
@ -2649,6 +2633,25 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
|
||||
model={tuningConfig}
|
||||
onChange={t => this.updateSpec(deepSet(spec, 'tuningConfig', t))}
|
||||
/>
|
||||
<AutoForm
|
||||
fields={[
|
||||
{
|
||||
name: 'dataSchema.granularitySpec.intervals',
|
||||
label: 'Time intervals',
|
||||
type: 'string-array',
|
||||
placeholder: 'ex: 2018-01-01/2018-06-01',
|
||||
required: s => Boolean(deepGet(s, 'tuningConfig.forceGuaranteedRollup')),
|
||||
info: (
|
||||
<>
|
||||
A comma separated list of intervals for the raw data being ingested. Ignored for
|
||||
real-time ingestion.
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
model={spec}
|
||||
onChange={s => this.updateSpec(s)}
|
||||
/>
|
||||
</div>
|
||||
<div className="control">
|
||||
<Callout className="intro">
|
||||
@ -2658,7 +2661,7 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
|
||||
{this.renderParallelPickerIfNeeded()}
|
||||
</div>
|
||||
{this.renderNextBar({
|
||||
disabled: invalidTuningConfig(tuningConfig),
|
||||
disabled: invalidTuningConfig(tuningConfig, granularitySpec.intervals),
|
||||
})}
|
||||
</>
|
||||
);
|
||||
@ -2864,6 +2867,7 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
|
||||
try {
|
||||
const resp = await axios.get(`/druid/indexer/v1/supervisor/${initSupervisorId}`);
|
||||
this.updateSpec(resp.data);
|
||||
this.setState({ continueToSpec: true });
|
||||
this.updateStep('spec');
|
||||
} catch (e) {
|
||||
AppToaster.show({
|
||||
@ -2879,6 +2883,7 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
|
||||
try {
|
||||
const resp = await axios.get(`/druid/indexer/v1/task/${initTaskId}`);
|
||||
this.updateSpec(resp.data.payload);
|
||||
this.setState({ continueToSpec: true });
|
||||
this.updateStep('spec');
|
||||
} catch (e) {
|
||||
AppToaster.show({
|
||||
|
@ -35,7 +35,6 @@ describe('parse data table', () => {
|
||||
|
||||
const parseDataTable = (
|
||||
<ParseDataTable
|
||||
openModal={() => {}}
|
||||
sampleData={sampleData}
|
||||
columnFilter=""
|
||||
canFlatten={false}
|
||||
|
@ -34,7 +34,6 @@ export interface ParseDataTableProps {
|
||||
flattenedColumnsOnly: boolean;
|
||||
flattenFields: FlattenField[];
|
||||
onFlattenFieldSelect: (field: FlattenField, index: number) => void;
|
||||
openModal: (str: string) => void;
|
||||
}
|
||||
|
||||
export class ParseDataTable extends React.PureComponent<ParseDataTableProps> {
|
||||
@ -78,7 +77,7 @@ export class ParseDataTable extends React.PureComponent<ParseDataTableProps> {
|
||||
if (row.original.unparseable) {
|
||||
return <TableCell unparseable />;
|
||||
}
|
||||
return <TableCell value={row.value} openModal={str => this.props.openModal(str)} />;
|
||||
return <TableCell value={row.value} />;
|
||||
},
|
||||
headerClassName: classNames({
|
||||
flattened: flattenField,
|
||||
|
Loading…
x
Reference in New Issue
Block a user