mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-27 10:26:09 +00:00
* Fixed Add Partition Setting in UI for Profiler #8624 * addressing comments
This commit is contained in:
parent
4d15696d71
commit
9bd0dbb67d
@ -12,14 +12,23 @@
|
||||
*/
|
||||
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, InputNumber, Modal, Select, Slider, TreeSelect } from 'antd';
|
||||
import {
|
||||
Button,
|
||||
InputNumber,
|
||||
Modal,
|
||||
Select,
|
||||
Slider,
|
||||
Space,
|
||||
Switch,
|
||||
TreeSelect,
|
||||
} from 'antd';
|
||||
import Form from 'antd/lib/form';
|
||||
import { List } from 'antd/lib/form/Form';
|
||||
import { Col, Row } from 'antd/lib/grid';
|
||||
import { AxiosError } from 'axios';
|
||||
import classNames from 'classnames';
|
||||
import 'codemirror/addon/fold/foldgutter.css';
|
||||
import { isEmpty, isEqual, isUndefined, startCase } from 'lodash';
|
||||
import { isEmpty, isEqual, isUndefined, omit, startCase } from 'lodash';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { Controlled as CodeMirror } from 'react-codemirror2';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -30,10 +39,14 @@ import {
|
||||
import {
|
||||
codeMirrorOption,
|
||||
DEFAULT_INCLUDE_PROFILE,
|
||||
INTERVAL_TYPE_OPTIONS,
|
||||
INTERVAL_UNIT_OPTIONS,
|
||||
PROFILER_METRIC,
|
||||
SUPPORTED_PARTITION_TYPE,
|
||||
} from '../../../constants/profiler.constant';
|
||||
import {
|
||||
ColumnProfilerConfig,
|
||||
PartitionProfilerConfig,
|
||||
TableProfilerConfig,
|
||||
} from '../../../generated/entity/data/table';
|
||||
import jsonData from '../../../jsons/en';
|
||||
@ -59,6 +72,9 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
||||
);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const [enablePartition, setEnablePartition] = useState(false);
|
||||
const [partitionData, setPartitionData] = useState<PartitionProfilerConfig>();
|
||||
|
||||
const selectOptions = useMemo(() => {
|
||||
return columns.map(({ name }) => ({
|
||||
label: name,
|
||||
@ -82,8 +98,30 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
||||
return metricsOptions;
|
||||
}, [columns]);
|
||||
|
||||
const { partitionColumnOptions, isPartitionDisabled } = useMemo(() => {
|
||||
const partitionColumnOptions = columns.reduce((result, column) => {
|
||||
if (SUPPORTED_PARTITION_TYPE.includes(column.dataType)) {
|
||||
return [
|
||||
...result,
|
||||
{
|
||||
value: column.name,
|
||||
label: column.name,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return result;
|
||||
}, [] as { value: string; label: string }[]);
|
||||
const isPartitionDisabled = partitionColumnOptions.length === 0;
|
||||
|
||||
return {
|
||||
partitionColumnOptions,
|
||||
isPartitionDisabled,
|
||||
};
|
||||
}, [columns]);
|
||||
|
||||
const updateInitialConfig = (tableProfilerConfig: TableProfilerConfig) => {
|
||||
const { includeColumns } = tableProfilerConfig;
|
||||
const { includeColumns, partitioning } = tableProfilerConfig;
|
||||
setSqlQuery(tableProfilerConfig.profileQuery || '');
|
||||
setProfileSample(tableProfilerConfig.profileSample || 100);
|
||||
setExcludeCol(tableProfilerConfig.excludeColumns || []);
|
||||
@ -101,6 +139,10 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
||||
form.setFieldsValue({ includeColumns: includeColValue });
|
||||
setIncludeCol(includeColValue);
|
||||
}
|
||||
if (partitioning) {
|
||||
setEnablePartition(partitioning.enablePartitioning || false);
|
||||
form.setFieldsValue({ ...partitioning });
|
||||
}
|
||||
};
|
||||
|
||||
const fetchProfileConfig = async () => {
|
||||
@ -151,8 +193,13 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
||||
includeColumns: !isEqual(includeCol, DEFAULT_INCLUDE_PROFILE)
|
||||
? getIncludesColumns()
|
||||
: undefined,
|
||||
partitioning: enablePartition
|
||||
? {
|
||||
...partitionData,
|
||||
enablePartitioning: enablePartition,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
|
||||
try {
|
||||
const data = await putTableProfileConfig(tableId, profileConfig);
|
||||
if (data) {
|
||||
@ -186,6 +233,10 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
||||
<Modal
|
||||
centered
|
||||
destroyOnClose
|
||||
bodyStyle={{
|
||||
maxHeight: 600,
|
||||
overflowY: 'scroll',
|
||||
}}
|
||||
cancelButtonProps={{
|
||||
type: 'link',
|
||||
}}
|
||||
@ -193,12 +244,15 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
||||
confirmLoading={isLoading}
|
||||
data-testid="profiler-settings-modal"
|
||||
maskClosable={false}
|
||||
okButtonProps={{
|
||||
form: 'profiler-setting-form',
|
||||
htmlType: 'submit',
|
||||
}}
|
||||
okText={t('label.save')}
|
||||
title={t('label.settings')}
|
||||
visible={visible}
|
||||
width={630}
|
||||
onCancel={handleCancel}
|
||||
onOk={handleSave}>
|
||||
onCancel={handleCancel}>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col data-testid="profile-sample-container" span={24}>
|
||||
<p>{t('label.profile-sample-percentage')}</p>
|
||||
@ -271,13 +325,17 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
||||
<Form
|
||||
autoComplete="off"
|
||||
form={form}
|
||||
id="profiler-setting-form"
|
||||
initialValues={{
|
||||
includeColumns: includeCol,
|
||||
...data?.partitioning,
|
||||
}}
|
||||
layout="vertical"
|
||||
name="includeColumnsProfiler"
|
||||
onFinish={handleSave}
|
||||
onValuesChange={(_, data) => {
|
||||
setIncludeCol(data.includeColumns);
|
||||
setPartitionData(omit(data, 'includeColumns'));
|
||||
}}>
|
||||
<List name="includeColumns">
|
||||
{(fields, { add, remove }) => (
|
||||
@ -296,53 +354,182 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
||||
</div>
|
||||
<div
|
||||
className={classNames({
|
||||
'tw-h-40 tw-overflow-auto': includeCol.length > 1,
|
||||
'tw-max-h-40 tw-overflow-y-auto': includeCol.length > 1,
|
||||
})}
|
||||
data-testid="include-column-container">
|
||||
{fields.map(({ key, name, ...restField }) => (
|
||||
<div className="tw-flex tw-gap-2 tw-w-full" key={key}>
|
||||
<Form.Item
|
||||
className="tw-w-11/12 tw-mb-4"
|
||||
{...restField}
|
||||
name={[name, 'columnName']}>
|
||||
<Select
|
||||
className="tw-w-full"
|
||||
data-testid="exclude-column-select"
|
||||
options={selectOptions}
|
||||
placeholder={t('label.select-column-include')}
|
||||
size="middle"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
className="tw-w-11/12 tw-mb-4"
|
||||
{...restField}
|
||||
name={[name, 'metrics']}>
|
||||
<TreeSelect
|
||||
treeCheckable
|
||||
className="tw-w-full"
|
||||
maxTagCount={2}
|
||||
placeholder={t('label.please-select')}
|
||||
showCheckedStrategy="SHOW_PARENT"
|
||||
treeData={metricsOptions}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button
|
||||
icon={
|
||||
<SVGIcons
|
||||
alt={t('label.delete')}
|
||||
className="tw-w-4"
|
||||
icon={Icons.DELETE}
|
||||
<Row gutter={16} key={key}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
className="w-full m-b-md"
|
||||
{...restField}
|
||||
name={[name, 'columnName']}>
|
||||
<Select
|
||||
className="w-full"
|
||||
data-testid="exclude-column-select"
|
||||
options={selectOptions}
|
||||
placeholder={t('label.select-column-include')}
|
||||
size="middle"
|
||||
/>
|
||||
}
|
||||
type="text"
|
||||
onClick={() => remove(name)}
|
||||
/>
|
||||
</div>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col className="flex" span={12}>
|
||||
<Form.Item
|
||||
className="w-full m-b-md"
|
||||
{...restField}
|
||||
name={[name, 'metrics']}>
|
||||
<TreeSelect
|
||||
treeCheckable
|
||||
className="w-full"
|
||||
maxTagCount={2}
|
||||
placeholder={t('label.please-select')}
|
||||
showCheckedStrategy="SHOW_PARENT"
|
||||
treeData={metricsOptions}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button
|
||||
icon={
|
||||
<SVGIcons
|
||||
alt={t('label.delete')}
|
||||
className="w-4"
|
||||
icon={Icons.DELETE}
|
||||
/>
|
||||
}
|
||||
type="text"
|
||||
onClick={() => remove(name)}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</List>
|
||||
<Form.Item className="m-b-xs">
|
||||
<Space size={12}>
|
||||
<p>{t('label.enable-partition')}</p>
|
||||
<Switch
|
||||
checked={enablePartition}
|
||||
data-testid="enable-partition-switch"
|
||||
disabled={isPartitionDisabled}
|
||||
onChange={(value) => setEnablePartition(value)}
|
||||
/>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
className="m-b-0"
|
||||
label={
|
||||
<span className="text-xs">{t('label.column-name')}</span>
|
||||
}
|
||||
labelCol={{
|
||||
style: {
|
||||
paddingBottom: 8,
|
||||
},
|
||||
}}
|
||||
name="partitionColumnName"
|
||||
rules={[
|
||||
{
|
||||
required: isPartitionDisabled || enablePartition,
|
||||
message: t('message.column-name-required'),
|
||||
},
|
||||
]}>
|
||||
<Select
|
||||
allowClear
|
||||
className="w-full"
|
||||
data-testid="column-name"
|
||||
disabled={isPartitionDisabled || !enablePartition}
|
||||
options={partitionColumnOptions}
|
||||
placeholder={t('message.select-column-name')}
|
||||
size="middle"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
className="m-b-0"
|
||||
label={
|
||||
<span className="text-xs">{t('label.interval-type')}</span>
|
||||
}
|
||||
labelCol={{
|
||||
style: {
|
||||
paddingBottom: 8,
|
||||
},
|
||||
}}
|
||||
name="partitionIntervalType"
|
||||
rules={[
|
||||
{
|
||||
required: isPartitionDisabled || enablePartition,
|
||||
message: t('message.interval-type-required'),
|
||||
},
|
||||
]}>
|
||||
<Select
|
||||
allowClear
|
||||
className="w-full"
|
||||
data-testid="interval-type"
|
||||
disabled={isPartitionDisabled || !enablePartition}
|
||||
options={INTERVAL_TYPE_OPTIONS}
|
||||
placeholder={t('message.select-type-required')}
|
||||
size="middle"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
className="m-b-0"
|
||||
label={<span className="text-xs">{t('label.interval')}</span>}
|
||||
labelCol={{
|
||||
style: {
|
||||
paddingBottom: 8,
|
||||
},
|
||||
}}
|
||||
name="partitionInterval"
|
||||
rules={[
|
||||
{
|
||||
required: isPartitionDisabled || enablePartition,
|
||||
message: t('message.interval-required'),
|
||||
},
|
||||
]}>
|
||||
<InputNumber
|
||||
className="w-full"
|
||||
data-testid="interval-required"
|
||||
disabled={isPartitionDisabled || !enablePartition}
|
||||
placeholder={t('message.enter-interval')}
|
||||
size="middle"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
className="m-b-0"
|
||||
label={
|
||||
<span className="text-xs">{t('label.interval-unit')}</span>
|
||||
}
|
||||
labelCol={{
|
||||
style: {
|
||||
paddingBottom: 8,
|
||||
},
|
||||
}}
|
||||
name="partitionIntervalUnit"
|
||||
rules={[
|
||||
{
|
||||
required: isPartitionDisabled || enablePartition,
|
||||
message: t('message.interval-unit-required'),
|
||||
},
|
||||
]}>
|
||||
<Select
|
||||
allowClear
|
||||
className="w-full"
|
||||
data-testid="select-interval-unit"
|
||||
disabled={isPartitionDisabled || !enablePartition}
|
||||
options={INTERVAL_UNIT_OPTIONS}
|
||||
placeholder={t('message.select-interval-unit')}
|
||||
size="middle"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</Col>
|
||||
</Row>
|
||||
|
@ -13,7 +13,12 @@
|
||||
|
||||
import { StepperStepType } from 'Models';
|
||||
import { CSMode } from '../enums/codemirror.enum';
|
||||
import { ColumnProfilerConfig } from '../generated/entity/data/table';
|
||||
import {
|
||||
ColumnProfilerConfig,
|
||||
DataType,
|
||||
PartitionIntervalType,
|
||||
PartitionIntervalUnit,
|
||||
} from '../generated/entity/data/table';
|
||||
import { TestCaseStatus } from '../generated/tests/testCase';
|
||||
import { JSON_TAB_SIZE } from './constants';
|
||||
|
||||
@ -206,3 +211,23 @@ export const STEPS_FOR_ADD_TEST_CASE: Array<StepperStepType> = [
|
||||
{ name: 'Select/Add Test Suite', step: 1 },
|
||||
{ name: 'Configure Test Case', step: 2 },
|
||||
];
|
||||
|
||||
export const SUPPORTED_PARTITION_TYPE = [
|
||||
DataType.Timestamp,
|
||||
DataType.Date,
|
||||
DataType.Datetime,
|
||||
DataType.Timestampz,
|
||||
];
|
||||
|
||||
export const INTERVAL_TYPE_OPTIONS = Object.values(PartitionIntervalType).map(
|
||||
(value) => ({
|
||||
value,
|
||||
label: value,
|
||||
})
|
||||
);
|
||||
export const INTERVAL_UNIT_OPTIONS = Object.values(PartitionIntervalUnit).map(
|
||||
(value) => ({
|
||||
value,
|
||||
label: value,
|
||||
})
|
||||
);
|
||||
|
@ -245,7 +245,12 @@
|
||||
"total-data-assets": "Total data assets",
|
||||
"data-assets-with-field": "Data assets with {{field}}",
|
||||
"total-data-assets-with-tiers": "Total Data assets with tiers",
|
||||
"most-active-user": "Most active user"
|
||||
"most-active-user": "Most active user",
|
||||
"enable-partition": "Enable Partition",
|
||||
"column-name": "Column Name",
|
||||
"interval-type": "Interval Type",
|
||||
"interval": "Interval",
|
||||
"interval-unit": "Interval Unit"
|
||||
},
|
||||
"message": {
|
||||
"service-email-required": "Service account Email is required",
|
||||
@ -272,7 +277,15 @@
|
||||
"total-entity-insight": "Display the total of data assets by type.",
|
||||
"active-users": "Display the number of users active.",
|
||||
"most-active-users": "Displays the most active users on the platform based on page views.",
|
||||
"most-viewed-data-assets": "Displays the most viewed data assets."
|
||||
"most-viewed-data-assets": "Displays the most viewed data assets.",
|
||||
"column-name-required": "Column name is required",
|
||||
"select-column-name": "Select column name",
|
||||
"interval-type-required": "Interval type is required",
|
||||
"select-type-required": "Select interval type",
|
||||
"interval-required": "Interval is required",
|
||||
"enter-interval": "Enter interval",
|
||||
"interval-unit-required": "Interval unit is required",
|
||||
"select-interval-unit": "Select interval unit"
|
||||
},
|
||||
"server": {
|
||||
"no-followed-entities": "You have not followed anything yet.",
|
||||
|
@ -362,6 +362,9 @@
|
||||
.p-b-xs {
|
||||
padding-bottom: @padding-xs;
|
||||
}
|
||||
.p-b-xss {
|
||||
padding-bottom: @padding-xss;
|
||||
}
|
||||
.p-b-sm {
|
||||
padding-bottom: @padding-sm;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user