Fixed Add Partition Setting in UI for Profiler #8624 (#8673)

* Fixed Add Partition Setting in UI for Profiler #8624

* addressing comments
This commit is contained in:
Shailesh Parmar 2022-11-12 19:23:52 +05:30 committed by GitHub
parent 4d15696d71
commit 9bd0dbb67d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 275 additions and 47 deletions

View File

@ -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>

View File

@ -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,
})
);

View File

@ -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.",

View File

@ -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;
}