mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-05 07:03:07 +00:00
fixes 10357: Update Partitionning Setting Flow for Profiler (#10743)
* initial commit for #10357 * added form based on partition condition * localization sync * added unit test * updated sql editor with common component * updated form based on switch * addressing comment * added form type and provided to form instance * added default value for partitionValues in initialValue field
This commit is contained in:
parent
65297e5b1d
commit
4b260f1736
@ -14,7 +14,7 @@
|
|||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { Button, Form, Input, InputNumber, Select, Switch } from 'antd';
|
import { Button, Form, Input, InputNumber, Select, Switch } from 'antd';
|
||||||
import 'codemirror/addon/fold/foldgutter.css';
|
import 'codemirror/addon/fold/foldgutter.css';
|
||||||
import { SUPPORTED_PARTITION_TYPE } from 'constants/profiler.constant';
|
import { SUPPORTED_PARTITION_TYPE_FOR_DATE_TIME } from 'constants/profiler.constant';
|
||||||
import { isUndefined } from 'lodash';
|
import { isUndefined } from 'lodash';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -46,7 +46,9 @@ const ParameterForm: React.FC<ParameterFormProps> = ({ definition, table }) => {
|
|||||||
) {
|
) {
|
||||||
const partitionColumnOptions = table.columns.reduce(
|
const partitionColumnOptions = table.columns.reduce(
|
||||||
(result, column) => {
|
(result, column) => {
|
||||||
if (SUPPORTED_PARTITION_TYPE.includes(column.dataType)) {
|
if (
|
||||||
|
SUPPORTED_PARTITION_TYPE_FOR_DATE_TIME.includes(column.dataType)
|
||||||
|
) {
|
||||||
return [
|
return [
|
||||||
...result,
|
...result,
|
||||||
{
|
{
|
||||||
|
@ -11,21 +11,18 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { cleanup, render, screen } from '@testing-library/react';
|
import {
|
||||||
|
act,
|
||||||
|
cleanup,
|
||||||
|
fireEvent,
|
||||||
|
render,
|
||||||
|
screen,
|
||||||
|
} from '@testing-library/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MOCK_TABLE } from '../../../mocks/TableData.mock';
|
import { MOCK_TABLE } from '../../../mocks/TableData.mock';
|
||||||
import { ProfilerSettingsModalProps } from '../TableProfiler.interface';
|
import { ProfilerSettingsModalProps } from '../TableProfiler.interface';
|
||||||
import ProfilerSettingsModal from './ProfilerSettingsModal';
|
import ProfilerSettingsModal from './ProfilerSettingsModal';
|
||||||
|
|
||||||
jest.mock('antd/lib/grid', () => ({
|
|
||||||
Row: jest.fn().mockImplementation(({ children }) => <div>{children}</div>),
|
|
||||||
Col: jest
|
|
||||||
.fn()
|
|
||||||
.mockImplementation(({ children, ...props }) => (
|
|
||||||
<div data-testid={props['data-testid']}>{children}</div>
|
|
||||||
)),
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('rest/tableAPI', () => ({
|
jest.mock('rest/tableAPI', () => ({
|
||||||
getTableProfilerConfig: jest
|
getTableProfilerConfig: jest
|
||||||
.fn()
|
.fn()
|
||||||
@ -55,11 +52,51 @@ describe('Test ProfilerSettingsModal component', () => {
|
|||||||
const sqlEditor = await screen.findByTestId('sql-editor-container');
|
const sqlEditor = await screen.findByTestId('sql-editor-container');
|
||||||
const includeSelect = await screen.findByTestId('include-column-container');
|
const includeSelect = await screen.findByTestId('include-column-container');
|
||||||
const excludeSelect = await screen.findByTestId('exclude-column-container');
|
const excludeSelect = await screen.findByTestId('exclude-column-container');
|
||||||
|
const partitionSwitch = await screen.findByTestId(
|
||||||
|
'enable-partition-switch'
|
||||||
|
);
|
||||||
|
const intervalType = await screen.findByTestId('interval-type');
|
||||||
|
const columnName = await screen.findByTestId('column-name');
|
||||||
|
|
||||||
expect(modal).toBeInTheDocument();
|
expect(modal).toBeInTheDocument();
|
||||||
expect(sampleContainer).toBeInTheDocument();
|
expect(sampleContainer).toBeInTheDocument();
|
||||||
expect(sqlEditor).toBeInTheDocument();
|
expect(sqlEditor).toBeInTheDocument();
|
||||||
expect(includeSelect).toBeInTheDocument();
|
expect(includeSelect).toBeInTheDocument();
|
||||||
expect(excludeSelect).toBeInTheDocument();
|
expect(excludeSelect).toBeInTheDocument();
|
||||||
|
expect(partitionSwitch).toBeInTheDocument();
|
||||||
|
expect(intervalType).toBeInTheDocument();
|
||||||
|
expect(columnName).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Interval Type and Column Name field should be disabled, when partition switch is off', async () => {
|
||||||
|
render(<ProfilerSettingsModal {...mockProps} />);
|
||||||
|
const partitionSwitch = await screen.findByTestId(
|
||||||
|
'enable-partition-switch'
|
||||||
|
);
|
||||||
|
const intervalType = await screen.findByTestId('interval-type');
|
||||||
|
const columnName = await screen.findByTestId('column-name');
|
||||||
|
|
||||||
|
expect(partitionSwitch).toHaveAttribute('aria-checked', 'false');
|
||||||
|
expect(intervalType).toHaveClass('ant-select-disabled');
|
||||||
|
expect(columnName).toHaveClass('ant-select-disabled');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Interval Type and Column Name field should be enabled, when partition switch is on', async () => {
|
||||||
|
render(<ProfilerSettingsModal {...mockProps} />);
|
||||||
|
const partitionSwitch = await screen.findByTestId(
|
||||||
|
'enable-partition-switch'
|
||||||
|
);
|
||||||
|
const intervalType = await screen.findByTestId('interval-type');
|
||||||
|
const columnName = await screen.findByTestId('column-name');
|
||||||
|
|
||||||
|
expect(partitionSwitch).toHaveAttribute('aria-checked', 'false');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
fireEvent.click(partitionSwitch);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(partitionSwitch).toHaveAttribute('aria-checked', 'true');
|
||||||
|
expect(intervalType).not.toHaveClass('ant-select-disabled');
|
||||||
|
expect(columnName).not.toHaveClass('ant-select-disabled');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
Input,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
Modal,
|
Modal,
|
||||||
Select,
|
Select,
|
||||||
@ -27,7 +28,10 @@ import { Col, Row } from 'antd/lib/grid';
|
|||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import 'codemirror/addon/fold/foldgutter.css';
|
import 'codemirror/addon/fold/foldgutter.css';
|
||||||
import { isEmpty, isEqual, isUndefined, omit, startCase } from 'lodash';
|
import SchemaEditor from 'components/schema-editor/SchemaEditor';
|
||||||
|
import { CSMode } from 'enums/codemirror.enum';
|
||||||
|
import { PartitionIntervalType } from 'generated/api/data/createTable';
|
||||||
|
import { isEmpty, isEqual, isNil, isUndefined, pick, startCase } from 'lodash';
|
||||||
import React, {
|
import React, {
|
||||||
Reducer,
|
Reducer,
|
||||||
useCallback,
|
useCallback,
|
||||||
@ -36,17 +40,17 @@ import React, {
|
|||||||
useReducer,
|
useReducer,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { Controlled as CodeMirror } from 'react-codemirror2';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { getTableProfilerConfig, putTableProfileConfig } from 'rest/tableAPI';
|
import { getTableProfilerConfig, putTableProfileConfig } from 'rest/tableAPI';
|
||||||
import {
|
import {
|
||||||
codeMirrorOption,
|
|
||||||
DEFAULT_INCLUDE_PROFILE,
|
DEFAULT_INCLUDE_PROFILE,
|
||||||
INTERVAL_TYPE_OPTIONS,
|
INTERVAL_TYPE_OPTIONS,
|
||||||
INTERVAL_UNIT_OPTIONS,
|
INTERVAL_UNIT_OPTIONS,
|
||||||
PROFILER_METRIC,
|
PROFILER_METRIC,
|
||||||
|
PROFILER_MODAL_LABEL_STYLE,
|
||||||
PROFILE_SAMPLE_OPTIONS,
|
PROFILE_SAMPLE_OPTIONS,
|
||||||
SUPPORTED_PARTITION_TYPE,
|
SUPPORTED_COLUMN_DATA_TYPE_FOR_INTERVAL,
|
||||||
|
TIME_BASED_PARTITION,
|
||||||
} from '../../../constants/profiler.constant';
|
} from '../../../constants/profiler.constant';
|
||||||
import {
|
import {
|
||||||
ProfileSampleType,
|
ProfileSampleType,
|
||||||
@ -58,6 +62,7 @@ import SVGIcons, { Icons } from '../../../utils/SvgUtils';
|
|||||||
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
|
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
|
||||||
import SliderWithInput from '../../SliderWithInput/SliderWithInput';
|
import SliderWithInput from '../../SliderWithInput/SliderWithInput';
|
||||||
import {
|
import {
|
||||||
|
ProfilerForm,
|
||||||
ProfilerSettingModalState,
|
ProfilerSettingModalState,
|
||||||
ProfilerSettingsModalProps,
|
ProfilerSettingsModalProps,
|
||||||
} from '../TableProfiler.interface';
|
} from '../TableProfiler.interface';
|
||||||
@ -70,7 +75,7 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
|||||||
onVisibilityChange,
|
onVisibilityChange,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm<ProfilerForm>();
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
@ -121,9 +126,14 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
|||||||
return metricsOptions;
|
return metricsOptions;
|
||||||
}, [columns]);
|
}, [columns]);
|
||||||
|
|
||||||
const { partitionColumnOptions, isPartitionDisabled } = useMemo(() => {
|
const partitionIntervalType = Form.useWatch(['partitionIntervalType'], form);
|
||||||
|
|
||||||
|
const partitionColumnOptions = useMemo(() => {
|
||||||
const partitionColumnOptions = columns.reduce((result, column) => {
|
const partitionColumnOptions = columns.reduce((result, column) => {
|
||||||
if (SUPPORTED_PARTITION_TYPE.includes(column.dataType)) {
|
const filter = partitionIntervalType
|
||||||
|
? SUPPORTED_COLUMN_DATA_TYPE_FOR_INTERVAL[partitionIntervalType]
|
||||||
|
: [];
|
||||||
|
if (filter.includes(column.dataType)) {
|
||||||
return [
|
return [
|
||||||
...result,
|
...result,
|
||||||
{
|
{
|
||||||
@ -135,13 +145,9 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}, [] as { value: string; label: string }[]);
|
}, [] as { value: string; label: string }[]);
|
||||||
const isPartitionDisabled = partitionColumnOptions.length === 0;
|
|
||||||
|
|
||||||
return {
|
return partitionColumnOptions;
|
||||||
partitionColumnOptions,
|
}, [columns, partitionIntervalType]);
|
||||||
isPartitionDisabled,
|
|
||||||
};
|
|
||||||
}, [columns]);
|
|
||||||
|
|
||||||
const updateInitialConfig = (tableProfilerConfig: TableProfilerConfig) => {
|
const updateInitialConfig = (tableProfilerConfig: TableProfilerConfig) => {
|
||||||
const {
|
const {
|
||||||
@ -193,7 +199,9 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
|||||||
enablePartition: partitioning.enablePartitioning || false,
|
enablePartition: partitioning.enablePartitioning || false,
|
||||||
});
|
});
|
||||||
|
|
||||||
form.setFieldsValue({ ...partitioning });
|
form.setFieldsValue({
|
||||||
|
...partitioning,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -259,19 +267,23 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
|||||||
const profileConfig: TableProfilerConfig = {
|
const profileConfig: TableProfilerConfig = {
|
||||||
excludeColumns: excludeCol.length > 0 ? excludeCol : undefined,
|
excludeColumns: excludeCol.length > 0 ? excludeCol : undefined,
|
||||||
profileQuery: !isEmpty(sqlQuery) ? sqlQuery : undefined,
|
profileQuery: !isEmpty(sqlQuery) ? sqlQuery : undefined,
|
||||||
...{
|
|
||||||
profileSample:
|
profileSample:
|
||||||
profileSampleType === ProfileSampleType.Percentage
|
profileSampleType === ProfileSampleType.Percentage
|
||||||
? profileSamplePercentage
|
? profileSamplePercentage
|
||||||
: profileSampleRows,
|
: profileSampleRows,
|
||||||
profileSampleType: profileSampleType,
|
profileSampleType: profileSampleType,
|
||||||
},
|
|
||||||
includeColumns: !isEqual(includeCol, DEFAULT_INCLUDE_PROFILE)
|
includeColumns: !isEqual(includeCol, DEFAULT_INCLUDE_PROFILE)
|
||||||
? getIncludesColumns()
|
? getIncludesColumns()
|
||||||
: undefined,
|
: undefined,
|
||||||
partitioning: enablePartition
|
partitioning: enablePartition
|
||||||
? {
|
? {
|
||||||
...partitionData,
|
...partitionData,
|
||||||
|
partitionValues:
|
||||||
|
partitionIntervalType === PartitionIntervalType.ColumnValue
|
||||||
|
? partitionData?.partitionValues?.filter(
|
||||||
|
(value) => !isEmpty(value)
|
||||||
|
)
|
||||||
|
: undefined,
|
||||||
enablePartitioning: enablePartition,
|
enablePartitioning: enablePartition,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
@ -320,19 +332,42 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCodeMirrorChange = useCallback(
|
const handleCodeMirrorChange = useCallback((value) => {
|
||||||
(_Editor, _EditorChange, value) => {
|
|
||||||
handleStateChange({
|
handleStateChange({
|
||||||
sqlQuery: value,
|
sqlQuery: value,
|
||||||
});
|
});
|
||||||
},
|
}, []);
|
||||||
[]
|
|
||||||
);
|
const handleIncludeColumnsProfiler = useCallback((changedValues, data) => {
|
||||||
|
const { partitionIntervalType, enablePartitioning } = changedValues;
|
||||||
|
if (partitionIntervalType || !isNil(enablePartitioning)) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
partitionColumnName: undefined,
|
||||||
|
partitionIntegerRangeStart: undefined,
|
||||||
|
partitionIntegerRangeEnd: undefined,
|
||||||
|
partitionIntervalUnit: undefined,
|
||||||
|
partitionInterval: undefined,
|
||||||
|
partitionValues: [''],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!isNil(enablePartitioning)) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
partitionIntervalType: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const handleIncludeColumnsProfiler = useCallback((_, data) => {
|
|
||||||
handleStateChange({
|
handleStateChange({
|
||||||
includeCol: data.includeColumns,
|
includeCol: data.includeColumns,
|
||||||
partitionData: omit(data, 'includeColumns'),
|
partitionData: pick(
|
||||||
|
data,
|
||||||
|
'partitionColumnName',
|
||||||
|
'partitionIntegerRangeEnd',
|
||||||
|
'partitionIntegerRangeStart',
|
||||||
|
'partitionInterval',
|
||||||
|
'partitionIntervalType',
|
||||||
|
'partitionIntervalUnit',
|
||||||
|
'partitionValues'
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -437,12 +472,15 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
|||||||
type: t('label.query'),
|
type: t('label.query'),
|
||||||
})}{' '}
|
})}{' '}
|
||||||
</p>
|
</p>
|
||||||
<CodeMirror
|
|
||||||
|
<SchemaEditor
|
||||||
className="profiler-setting-sql-editor"
|
className="profiler-setting-sql-editor"
|
||||||
data-testid="profiler-setting-sql-editor"
|
data-testid="profiler-setting-sql-editor"
|
||||||
options={codeMirrorOption}
|
mode={{ name: CSMode.SQL }}
|
||||||
value={state?.sqlQuery}
|
options={{
|
||||||
onBeforeChange={handleCodeMirrorChange}
|
readOnly: false,
|
||||||
|
}}
|
||||||
|
value={state?.sqlQuery || ''}
|
||||||
onChange={handleCodeMirrorChange}
|
onChange={handleCodeMirrorChange}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
@ -469,6 +507,7 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
|||||||
id="profiler-setting-form"
|
id="profiler-setting-form"
|
||||||
initialValues={{
|
initialValues={{
|
||||||
includeColumns: state?.includeCol,
|
includeColumns: state?.includeCol,
|
||||||
|
partitionData: [''],
|
||||||
...state?.data?.partitioning,
|
...state?.data?.partitioning,
|
||||||
}}
|
}}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
@ -546,54 +585,18 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</List>
|
</List>
|
||||||
<Form.Item className="m-b-xs">
|
<Row gutter={[16, 16]}>
|
||||||
<Space size={12}>
|
<Col span={24}>
|
||||||
|
<Space align="center" size={12}>
|
||||||
<p>{t('label.enable-partition')}</p>
|
<p>{t('label.enable-partition')}</p>
|
||||||
|
<Form.Item className="m-b-0" name="enablePartitioning">
|
||||||
<Switch
|
<Switch
|
||||||
checked={state?.enablePartition}
|
checked={state?.enablePartition}
|
||||||
data-testid="enable-partition-switch"
|
data-testid="enable-partition-switch"
|
||||||
disabled={isPartitionDisabled}
|
|
||||||
onChange={handleEnablePartition}
|
onChange={handleEnablePartition}
|
||||||
/>
|
/>
|
||||||
|
</Form.Item>
|
||||||
</Space>
|
</Space>
|
||||||
</Form.Item>
|
|
||||||
<Row gutter={[16, 16]}>
|
|
||||||
<Col span={12}>
|
|
||||||
<Form.Item
|
|
||||||
className="m-b-0"
|
|
||||||
label={
|
|
||||||
<span className="text-xs">
|
|
||||||
{t('label.column-entity', {
|
|
||||||
entity: t('label.name'),
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
labelCol={{
|
|
||||||
style: {
|
|
||||||
paddingBottom: 8,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
name="partitionColumnName"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: state?.enablePartition,
|
|
||||||
message: t('message.field-text-is-required', {
|
|
||||||
fieldText: t('label.column-entity', {
|
|
||||||
entity: t('label.name'),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
<Select
|
|
||||||
allowClear
|
|
||||||
className="w-full"
|
|
||||||
data-testid="column-name"
|
|
||||||
disabled={!state?.enablePartition}
|
|
||||||
options={partitionColumnOptions}
|
|
||||||
placeholder={t('message.select-column-name')}
|
|
||||||
size="middle"
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@ -601,11 +604,7 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
|||||||
label={
|
label={
|
||||||
<span className="text-xs">{t('label.interval-type')}</span>
|
<span className="text-xs">{t('label.interval-type')}</span>
|
||||||
}
|
}
|
||||||
labelCol={{
|
labelCol={PROFILER_MODAL_LABEL_STYLE}
|
||||||
style: {
|
|
||||||
paddingBottom: 8,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
name="partitionIntervalType"
|
name="partitionIntervalType"
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
@ -629,12 +628,46 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
|||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
className="m-b-0"
|
className="m-b-0"
|
||||||
label={<span className="text-xs">{t('label.interval')}</span>}
|
label={
|
||||||
labelCol={{
|
<span className="text-xs">
|
||||||
style: {
|
{t('label.column-entity', {
|
||||||
paddingBottom: 8,
|
entity: t('label.name'),
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
labelCol={PROFILER_MODAL_LABEL_STYLE}
|
||||||
|
name="partitionColumnName"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: state?.enablePartition,
|
||||||
|
message: t('message.field-text-is-required', {
|
||||||
|
fieldText: t('label.column-entity', {
|
||||||
|
entity: t('label.name'),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
}}
|
]}>
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
className="w-full"
|
||||||
|
data-testid="column-name"
|
||||||
|
disabled={!state?.enablePartition}
|
||||||
|
options={partitionColumnOptions}
|
||||||
|
placeholder={t('message.select-column-name')}
|
||||||
|
size="middle"
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
{partitionIntervalType &&
|
||||||
|
TIME_BASED_PARTITION.includes(partitionIntervalType) ? (
|
||||||
|
<>
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item
|
||||||
|
className="m-b-0"
|
||||||
|
label={
|
||||||
|
<span className="text-xs">{t('label.interval')}</span>
|
||||||
|
}
|
||||||
|
labelCol={PROFILER_MODAL_LABEL_STYLE}
|
||||||
name="partitionInterval"
|
name="partitionInterval"
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
@ -657,13 +690,11 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
className="m-b-0"
|
className="m-b-0"
|
||||||
label={
|
label={
|
||||||
<span className="text-xs">{t('label.interval-unit')}</span>
|
<span className="text-xs">
|
||||||
|
{t('label.interval-unit')}
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
labelCol={{
|
labelCol={PROFILER_MODAL_LABEL_STYLE}
|
||||||
style: {
|
|
||||||
paddingBottom: 8,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
name="partitionIntervalUnit"
|
name="partitionIntervalUnit"
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
@ -684,6 +715,144 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
|||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{PartitionIntervalType.IntegerRange === partitionIntervalType ? (
|
||||||
|
<>
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item
|
||||||
|
className="m-b-0"
|
||||||
|
label={
|
||||||
|
<span className="text-xs">
|
||||||
|
{t('label.start-entity', {
|
||||||
|
entity: t('label.range'),
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
labelCol={PROFILER_MODAL_LABEL_STYLE}
|
||||||
|
name="partitionIntegerRangeStart"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: state?.enablePartition,
|
||||||
|
message: t('message.field-text-is-required', {
|
||||||
|
fieldText: t('label.start-entity', {
|
||||||
|
entity: t('label.range'),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<InputNumber
|
||||||
|
className="w-full"
|
||||||
|
data-testid="start-range"
|
||||||
|
placeholder={t('message.enter-a-field', {
|
||||||
|
field: t('label.start-entity', {
|
||||||
|
entity: t('label.range'),
|
||||||
|
}),
|
||||||
|
})}
|
||||||
|
size="middle"
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item
|
||||||
|
className="m-b-0"
|
||||||
|
label={
|
||||||
|
<span className="text-xs">
|
||||||
|
{t('label.end-entity', {
|
||||||
|
entity: t('label.range'),
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
labelCol={PROFILER_MODAL_LABEL_STYLE}
|
||||||
|
name="partitionIntegerRangeEnd"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: state?.enablePartition,
|
||||||
|
message: t('message.field-text-is-required', {
|
||||||
|
fieldText: t('label.end-entity', {
|
||||||
|
entity: t('label.range'),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<InputNumber
|
||||||
|
className="w-full"
|
||||||
|
data-testid="end-range"
|
||||||
|
placeholder={t('message.enter-a-field', {
|
||||||
|
field: t('label.end-entity', {
|
||||||
|
entity: t('label.range'),
|
||||||
|
}),
|
||||||
|
})}
|
||||||
|
size="middle"
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{PartitionIntervalType.ColumnValue === partitionIntervalType ? (
|
||||||
|
<Col span={24}>
|
||||||
|
<List name="partitionValues">
|
||||||
|
{(fields, { add, remove }) => (
|
||||||
|
<>
|
||||||
|
<div className="flex items-center tw-mb-1.5">
|
||||||
|
<p className="w-form-label text-xs m-r-sm">
|
||||||
|
{`${t('label.value')}:`}
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
className="include-columns-add-button"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
onClick={() => add()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{fields.map(({ key, name, ...restField }) => (
|
||||||
|
<Row gutter={16} key={key}>
|
||||||
|
<Col className="flex" span={24}>
|
||||||
|
<Form.Item
|
||||||
|
className="w-full m-b-md"
|
||||||
|
{...restField}
|
||||||
|
name={name}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: state?.enablePartition,
|
||||||
|
message: t(
|
||||||
|
'message.field-text-is-required',
|
||||||
|
{
|
||||||
|
fieldText: t('label.value'),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<Input
|
||||||
|
className="w-full"
|
||||||
|
data-testid="partition-value"
|
||||||
|
placeholder={t('message.enter-a-field', {
|
||||||
|
field: t('label.value'),
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Button
|
||||||
|
icon={
|
||||||
|
<SVGIcons
|
||||||
|
alt={t('label.delete')}
|
||||||
|
className="w-4"
|
||||||
|
icon={Icons.DELETE}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
type="text"
|
||||||
|
onClick={() => remove(name)}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</List>
|
||||||
|
</Col>
|
||||||
|
) : null}
|
||||||
</Row>
|
</Row>
|
||||||
</Form>
|
</Form>
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -95,3 +95,13 @@ export interface ProfilerSettingModalState {
|
|||||||
partitionData: PartitionProfilerConfig | undefined;
|
partitionData: PartitionProfilerConfig | undefined;
|
||||||
selectedProfileSampleType: ProfileSampleType | undefined;
|
selectedProfileSampleType: ProfileSampleType | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProfilerForm extends PartitionProfilerConfig {
|
||||||
|
profileSample: number | undefined;
|
||||||
|
selectedProfileSampleType: ProfileSampleType | undefined;
|
||||||
|
enablePartition: boolean;
|
||||||
|
profileSampleType: ProfileSampleType | undefined;
|
||||||
|
profileSamplePercentage: number;
|
||||||
|
profileSampleRows: number | undefined;
|
||||||
|
includeColumns: ColumnProfilerConfig[];
|
||||||
|
}
|
||||||
|
@ -314,13 +314,20 @@ export const STEPS_FOR_ADD_TEST_CASE: Array<StepperStepType> = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const SUPPORTED_PARTITION_TYPE = [
|
export const SUPPORTED_PARTITION_TYPE_FOR_DATE_TIME = [
|
||||||
DataType.Timestamp,
|
DataType.Timestamp,
|
||||||
DataType.Date,
|
DataType.Date,
|
||||||
DataType.Datetime,
|
DataType.Datetime,
|
||||||
DataType.Timestampz,
|
DataType.Timestampz,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const SUPPORTED_COLUMN_DATA_TYPE_FOR_INTERVAL = {
|
||||||
|
[PartitionIntervalType.IngestionTime]: SUPPORTED_PARTITION_TYPE_FOR_DATE_TIME,
|
||||||
|
[PartitionIntervalType.TimeUnit]: SUPPORTED_PARTITION_TYPE_FOR_DATE_TIME,
|
||||||
|
[PartitionIntervalType.IntegerRange]: [DataType.Int, DataType.Bigint],
|
||||||
|
[PartitionIntervalType.ColumnValue]: [DataType.Varchar],
|
||||||
|
};
|
||||||
|
|
||||||
export const INTERVAL_TYPE_OPTIONS = Object.values(PartitionIntervalType).map(
|
export const INTERVAL_TYPE_OPTIONS = Object.values(PartitionIntervalType).map(
|
||||||
(value) => ({
|
(value) => ({
|
||||||
value,
|
value,
|
||||||
@ -353,3 +360,14 @@ export const DEFAULT_HISTOGRAM_DATA = {
|
|||||||
boundaries: [],
|
boundaries: [],
|
||||||
frequencies: [],
|
frequencies: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const PROFILER_MODAL_LABEL_STYLE = {
|
||||||
|
style: {
|
||||||
|
paddingBottom: 8,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TIME_BASED_PARTITION = [
|
||||||
|
PartitionIntervalType.IngestionTime,
|
||||||
|
PartitionIntervalType.TimeUnit,
|
||||||
|
];
|
||||||
|
@ -256,6 +256,7 @@
|
|||||||
"enable-partition": "Enable Partition",
|
"enable-partition": "Enable Partition",
|
||||||
"end-date": "End Date",
|
"end-date": "End Date",
|
||||||
"end-date-time-zone": "End Date: ({{timeZone}})",
|
"end-date-time-zone": "End Date: ({{timeZone}})",
|
||||||
|
"end-entity": "End {{entity}}",
|
||||||
"endpoint": "Endpoint",
|
"endpoint": "Endpoint",
|
||||||
"endpoint-url": "Endpoint URL",
|
"endpoint-url": "Endpoint URL",
|
||||||
"endpoint-url-for-aws": "EndPoint URL for the AWS",
|
"endpoint-url-for-aws": "EndPoint URL for the AWS",
|
||||||
@ -589,6 +590,7 @@
|
|||||||
"query-log-duration": "Query Log Duration",
|
"query-log-duration": "Query Log Duration",
|
||||||
"query-lowercase": "query",
|
"query-lowercase": "query",
|
||||||
"query-plural": "Queries",
|
"query-plural": "Queries",
|
||||||
|
"range": "Range",
|
||||||
"re-deploy": "Re Deploy",
|
"re-deploy": "Re Deploy",
|
||||||
"re-enter-new-password": "Re-enter New Password",
|
"re-enter-new-password": "Re-enter New Password",
|
||||||
"re-index-all": "Re-Index All",
|
"re-index-all": "Re-Index All",
|
||||||
|
@ -256,6 +256,7 @@
|
|||||||
"enable-partition": "Activer Partition",
|
"enable-partition": "Activer Partition",
|
||||||
"end-date": "Date de Fin",
|
"end-date": "Date de Fin",
|
||||||
"end-date-time-zone": "End Date: ({{timeZone}})",
|
"end-date-time-zone": "End Date: ({{timeZone}})",
|
||||||
|
"end-entity": "End {{entity}}",
|
||||||
"endpoint": "Point de Terminaison",
|
"endpoint": "Point de Terminaison",
|
||||||
"endpoint-url": "Point de terminaison $t(label.url-uppercase)",
|
"endpoint-url": "Point de terminaison $t(label.url-uppercase)",
|
||||||
"endpoint-url-for-aws": "EndPoint URL for the AWS",
|
"endpoint-url-for-aws": "EndPoint URL for the AWS",
|
||||||
@ -589,6 +590,7 @@
|
|||||||
"query-log-duration": "Query Log Duration",
|
"query-log-duration": "Query Log Duration",
|
||||||
"query-lowercase": "query",
|
"query-lowercase": "query",
|
||||||
"query-plural": "Requêtes",
|
"query-plural": "Requêtes",
|
||||||
|
"range": "Range",
|
||||||
"re-deploy": "Re-Déployer",
|
"re-deploy": "Re-Déployer",
|
||||||
"re-enter-new-password": "Re-enter New Password",
|
"re-enter-new-password": "Re-enter New Password",
|
||||||
"re-index-all": "Re Index All",
|
"re-index-all": "Re Index All",
|
||||||
|
@ -256,6 +256,7 @@
|
|||||||
"enable-partition": "パーティションを有効化",
|
"enable-partition": "パーティションを有効化",
|
||||||
"end-date": "終了日時",
|
"end-date": "終了日時",
|
||||||
"end-date-time-zone": "終了日時: ({{timeZone}})",
|
"end-date-time-zone": "終了日時: ({{timeZone}})",
|
||||||
|
"end-entity": "End {{entity}}",
|
||||||
"endpoint": "エンドポイント",
|
"endpoint": "エンドポイント",
|
||||||
"endpoint-url": "エンドポイントURL",
|
"endpoint-url": "エンドポイントURL",
|
||||||
"endpoint-url-for-aws": "AWSのエンドポイントURL",
|
"endpoint-url-for-aws": "AWSのエンドポイントURL",
|
||||||
@ -589,6 +590,7 @@
|
|||||||
"query-log-duration": "クエリログの時間",
|
"query-log-duration": "クエリログの時間",
|
||||||
"query-lowercase": "クエリ",
|
"query-lowercase": "クエリ",
|
||||||
"query-plural": "クエリ",
|
"query-plural": "クエリ",
|
||||||
|
"range": "Range",
|
||||||
"re-deploy": "再デプロイ",
|
"re-deploy": "再デプロイ",
|
||||||
"re-enter-new-password": "新しいパスワードを再度入力してください",
|
"re-enter-new-password": "新しいパスワードを再度入力してください",
|
||||||
"re-index-all": "Re-Index All",
|
"re-index-all": "Re-Index All",
|
||||||
|
@ -256,6 +256,7 @@
|
|||||||
"enable-partition": "Enable Partition",
|
"enable-partition": "Enable Partition",
|
||||||
"end-date": "结束日期",
|
"end-date": "结束日期",
|
||||||
"end-date-time-zone": "结束日期: ({{timeZone}})",
|
"end-date-time-zone": "结束日期: ({{timeZone}})",
|
||||||
|
"end-entity": "End {{entity}}",
|
||||||
"endpoint": "终点",
|
"endpoint": "终点",
|
||||||
"endpoint-url": "终点 URL",
|
"endpoint-url": "终点 URL",
|
||||||
"endpoint-url-for-aws": "EndPoint URL for the AWS",
|
"endpoint-url-for-aws": "EndPoint URL for the AWS",
|
||||||
@ -589,6 +590,7 @@
|
|||||||
"query-log-duration": "Query Log Duration",
|
"query-log-duration": "Query Log Duration",
|
||||||
"query-lowercase": "查询",
|
"query-lowercase": "查询",
|
||||||
"query-plural": "查询",
|
"query-plural": "查询",
|
||||||
|
"range": "Range",
|
||||||
"re-deploy": "Re Deploy",
|
"re-deploy": "Re Deploy",
|
||||||
"re-enter-new-password": "Re-enter New Password",
|
"re-enter-new-password": "Re-enter New Password",
|
||||||
"re-index-all": "Re Index All",
|
"re-index-all": "Re Index All",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user