mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-31 04:14:34 +00:00
ui: worked on DQ feedback part 3 (#12149)
* ui: worked on DQ feedback part 3 * updated name of test suite ingestion name - Added description field in test case expand view - Added functionality to update displayname of test case * added testCase in re-indexing * integrated permission in DQ
This commit is contained in:
parent
0010a4fe2a
commit
fecb65c513
@ -116,7 +116,7 @@ const EditTestCaseModal: React.FC<EditTestCaseModalProps> = ({
|
||||
...testCase,
|
||||
parameterValues,
|
||||
description,
|
||||
name: value.name,
|
||||
displayName: value.displayName,
|
||||
};
|
||||
const jsonPatch = compare(testCase, updatedTestCase);
|
||||
|
||||
@ -124,10 +124,10 @@ const EditTestCaseModal: React.FC<EditTestCaseModalProps> = ({
|
||||
try {
|
||||
setIsLoadingOnSave(true);
|
||||
const updateRes = await updateTestCaseById(
|
||||
testCase.id || '',
|
||||
testCase.id ?? '',
|
||||
jsonPatch
|
||||
);
|
||||
onUpdate && onUpdate(updateRes);
|
||||
onUpdate?.(updateRes);
|
||||
showSuccessToast(
|
||||
t('server.update-entity-success', { entity: t('label.test-case') })
|
||||
);
|
||||
@ -216,17 +216,17 @@ const EditTestCaseModal: React.FC<EditTestCaseModalProps> = ({
|
||||
layout="vertical"
|
||||
name="tableTestForm"
|
||||
onFinish={handleFormSubmit}>
|
||||
<Form.Item required label={`${t('label.table')}:`} name="table">
|
||||
<Form.Item required label={`${t('label.table')}`} name="table">
|
||||
<Input disabled />
|
||||
</Form.Item>
|
||||
{isColumn && (
|
||||
<Form.Item required label={`${t('label.column')}:`} name="column">
|
||||
<Form.Item required label={`${t('label.column')}`} name="column">
|
||||
<Input disabled />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item
|
||||
required
|
||||
label={`${t('label.name')}:`}
|
||||
label={`${t('label.name')}`}
|
||||
name="name"
|
||||
rules={[
|
||||
{
|
||||
@ -236,6 +236,9 @@ const EditTestCaseModal: React.FC<EditTestCaseModalProps> = ({
|
||||
]}>
|
||||
<Input disabled placeholder={t('message.enter-test-case-name')} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('label.display-name')} name="displayName">
|
||||
<Input placeholder={t('message.enter-test-case-name')} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
required
|
||||
label={`${t('label.test-entity', {
|
||||
@ -247,7 +250,7 @@ const EditTestCaseModal: React.FC<EditTestCaseModalProps> = ({
|
||||
|
||||
{GenerateParamsField()}
|
||||
|
||||
<Form.Item label={`${t('label.description')}:`} name="description">
|
||||
<Form.Item label={`${t('label.description')}`} name="description">
|
||||
<RichTextEditor
|
||||
height="200px"
|
||||
initialValue={testCase?.description || ''}
|
||||
|
@ -22,6 +22,7 @@ import {
|
||||
deployIngestionPipelineById,
|
||||
updateIngestionPipeline as putIngestionPipeline,
|
||||
} from 'rest/ingestionPipelineAPI';
|
||||
import { getIngestionName } from 'utils/ServiceUtils';
|
||||
import {
|
||||
DEPLOYED_PROGRESS_VAL,
|
||||
INGESTION_PROGRESS_END_VAL,
|
||||
@ -36,7 +37,8 @@ import {
|
||||
import { IngestionPipeline } from '../../generated/entity/services/ingestionPipelines/ingestionPipeline';
|
||||
import {
|
||||
getIngestionFrequency,
|
||||
replaceSpaceWith_,
|
||||
getNameFromFQN,
|
||||
replaceAllSpacialCharWith_,
|
||||
Transi18next,
|
||||
} from '../../utils/CommonUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
@ -113,7 +115,12 @@ const TestSuiteIngestion: React.FC<TestSuiteIngestionProps> = ({
|
||||
};
|
||||
|
||||
const createIngestionPipeline = async (repeatFrequency: string) => {
|
||||
const updatedName = replaceSpaceWith_(testSuite.name);
|
||||
const tableName = replaceAllSpacialCharWith_(
|
||||
getNameFromFQN(
|
||||
testSuite.executableEntityReference?.fullyQualifiedName ?? ''
|
||||
)
|
||||
);
|
||||
const updatedName = getIngestionName(tableName, PipelineType.TestSuite);
|
||||
|
||||
const ingestionPayload: CreateIngestionPipeline = {
|
||||
airflowConfig: {
|
||||
@ -121,10 +128,10 @@ const TestSuiteIngestion: React.FC<TestSuiteIngestionProps> = ({
|
||||
? undefined
|
||||
: repeatFrequency,
|
||||
},
|
||||
name: `${updatedName}_${PipelineType.TestSuite}`,
|
||||
name: updatedName,
|
||||
pipelineType: PipelineType.TestSuite,
|
||||
service: {
|
||||
id: testSuite.id || '',
|
||||
id: testSuite.id ?? '',
|
||||
type: camelCase(PipelineType.TestSuite),
|
||||
},
|
||||
sourceConfig: {
|
||||
|
@ -138,16 +138,18 @@ const TestCaseForm: React.FC<TestCaseFormProps> = ({
|
||||
: value,
|
||||
})
|
||||
);
|
||||
const name =
|
||||
value.testName?.trim() ||
|
||||
`${columnName ? columnName : table.name}_${snakeCase(
|
||||
selectedTestType
|
||||
)}_${cryptoRandomString({
|
||||
length: 4,
|
||||
type: 'alphanumeric',
|
||||
})}`;
|
||||
|
||||
return {
|
||||
name:
|
||||
value.testName?.trim() ||
|
||||
`${columnName ? columnName : table.name}_${snakeCase(
|
||||
selectedTestType
|
||||
)}_${cryptoRandomString({
|
||||
length: 4,
|
||||
type: 'alphanumeric',
|
||||
})}`,
|
||||
name,
|
||||
displayName: name,
|
||||
entityLink: generateEntityLink(
|
||||
isColumnFqn ? `${decodedEntityFQN}.${columnName}` : decodedEntityFQN,
|
||||
isColumnFqn
|
||||
|
@ -10,11 +10,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Checkbox, Col, List, Modal, Row, Space, Typography } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import { Button, Checkbox, Col, List, Row, Space, Typography } from 'antd';
|
||||
import Searchbar from 'components/common/searchbar/Searchbar';
|
||||
import Loader from 'components/Loader/Loader';
|
||||
import { getTableTabPath, PAGE_SIZE_MEDIUM } from 'constants/constants';
|
||||
import { getTableTabPath, PAGE_SIZE } from 'constants/constants';
|
||||
import { SearchIndex } from 'enums/search.enum';
|
||||
import { TestCase } from 'generated/tests/testCase';
|
||||
import {
|
||||
@ -26,13 +25,11 @@ import React, { UIEventHandler, useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { searchQuery } from 'rest/searchAPI';
|
||||
import { addTestCaseToLogicalTestSuite } from 'rest/testAPI';
|
||||
import { getNameFromFQN } from 'utils/CommonUtils';
|
||||
import { getEntityName } from 'utils/EntityUtils';
|
||||
import { getDecodedFqn } from 'utils/StringsUtils';
|
||||
import { getEntityFqnFromEntityLink } from 'utils/TableUtils';
|
||||
import { showErrorToast } from 'utils/ToastUtils';
|
||||
import { AddTestCaseModalProps } from './AddTestCaseModal.interface';
|
||||
import { AddTestCaseModalProps } from './AddTestCaseList.interface';
|
||||
|
||||
// Todo: need to help from backend guys for ES query
|
||||
// export const getQueryFilterToExcludeTest = (testCase: EntityReference[]) => ({
|
||||
@ -47,12 +44,12 @@ import { AddTestCaseModalProps } from './AddTestCaseModal.interface';
|
||||
// },
|
||||
// });
|
||||
|
||||
export const AddTestCaseModal = ({
|
||||
open,
|
||||
export const AddTestCaseList = ({
|
||||
onCancel,
|
||||
existingTest,
|
||||
testSuiteId,
|
||||
onSubmit,
|
||||
cancelText,
|
||||
submitText,
|
||||
}: AddTestCaseModalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [searchTerm, setSearchTerm] = useState<string>('');
|
||||
@ -74,7 +71,7 @@ export const AddTestCaseModal = ({
|
||||
setIsLoading(true);
|
||||
const res = await searchQuery({
|
||||
pageNumber: page,
|
||||
pageSize: PAGE_SIZE_MEDIUM,
|
||||
pageSize: PAGE_SIZE,
|
||||
searchIndex: SearchIndex.TEST_CASE,
|
||||
query: searchText,
|
||||
// queryFilter: getQueryFilterToExcludeTest(existingTest),
|
||||
@ -100,17 +97,8 @@ export const AddTestCaseModal = ({
|
||||
const testCaseIds = [...(selectedItems?.values() ?? [])].map(
|
||||
(test) => test.id ?? ''
|
||||
);
|
||||
|
||||
try {
|
||||
await addTestCaseToLogicalTestSuite({ testCaseIds, testSuiteId });
|
||||
|
||||
onSubmit();
|
||||
onCancel();
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
onSubmit(testCaseIds);
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const onScroll: UIEventHandler<HTMLElement> = useCallback(
|
||||
@ -126,7 +114,7 @@ export const AddTestCaseModal = ({
|
||||
});
|
||||
}
|
||||
},
|
||||
[searchTerm, totalCount, items]
|
||||
[searchTerm, totalCount, items, isLoading]
|
||||
);
|
||||
|
||||
const handleCardClick = (details: TestCase) => {
|
||||
@ -160,99 +148,92 @@ export const AddTestCaseModal = ({
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
fetchTestCases({ searchText: searchTerm });
|
||||
}
|
||||
}, [open, searchTerm]);
|
||||
fetchTestCases({ searchText: searchTerm });
|
||||
}, [searchTerm]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
centered
|
||||
destroyOnClose
|
||||
closable={false}
|
||||
okButtonProps={{
|
||||
loading: isLoading,
|
||||
}}
|
||||
open={open}
|
||||
title={t('label.add-entity', { entity: t('label.test-case-plural') })}
|
||||
width={750}
|
||||
onCancel={onCancel}
|
||||
onOk={handleSubmit}>
|
||||
<Row gutter={[0, 16]}>
|
||||
<Col span={24}>
|
||||
<Searchbar
|
||||
removeMargin
|
||||
showClearSearch
|
||||
showLoadingStatus
|
||||
placeholder={t('label.search-entity', {
|
||||
entity: t('label.test-case-plural'),
|
||||
})}
|
||||
searchValue={searchTerm}
|
||||
onSearch={handleSearch}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<List loading={{ spinning: false, indicator: <Loader /> }}>
|
||||
<VirtualList
|
||||
data={items}
|
||||
height={500}
|
||||
itemKey="id"
|
||||
onScroll={onScroll}>
|
||||
{({ _source: test }) => {
|
||||
const tableFqn = getEntityFqnFromEntityLink(test.entityLink);
|
||||
const tableName = getNameFromFQN(tableFqn);
|
||||
const isColumn = test.entityLink.includes('::columns::');
|
||||
<Row gutter={[0, 16]}>
|
||||
<Col span={24}>
|
||||
<Searchbar
|
||||
removeMargin
|
||||
showClearSearch
|
||||
showLoadingStatus
|
||||
placeholder={t('label.search-entity', {
|
||||
entity: t('label.test-case-plural'),
|
||||
})}
|
||||
searchValue={searchTerm}
|
||||
onSearch={handleSearch}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<List loading={{ spinning: false, indicator: <Loader /> }}>
|
||||
<VirtualList
|
||||
data={items}
|
||||
height={500}
|
||||
itemKey="id"
|
||||
onScroll={onScroll}>
|
||||
{({ _source: test }) => {
|
||||
const tableFqn = getEntityFqnFromEntityLink(test.entityLink);
|
||||
const tableName = getNameFromFQN(tableFqn);
|
||||
const isColumn = test.entityLink.includes('::columns::');
|
||||
|
||||
return (
|
||||
<Space
|
||||
className="m-b-md border rounded-4 p-sm cursor-pointer"
|
||||
direction="vertical"
|
||||
onClick={() => handleCardClick(test)}>
|
||||
<Space className="justify-between w-full">
|
||||
<Typography.Paragraph
|
||||
className="m-0 font-medium text-base"
|
||||
data-testid={test.name}>
|
||||
{getEntityName(test)}
|
||||
</Typography.Paragraph>
|
||||
return (
|
||||
<Space
|
||||
className="m-b-md border rounded-4 p-sm cursor-pointer"
|
||||
direction="vertical"
|
||||
onClick={() => handleCardClick(test)}>
|
||||
<Space className="justify-between w-full">
|
||||
<Typography.Paragraph
|
||||
className="m-0 font-medium text-base"
|
||||
data-testid={test.name}>
|
||||
{getEntityName(test)}
|
||||
</Typography.Paragraph>
|
||||
|
||||
<Checkbox checked={selectedItems?.has(test.id ?? '')} />
|
||||
</Space>
|
||||
<Typography.Paragraph className="m-0">
|
||||
{getEntityName(test.testDefinition)}
|
||||
</Typography.Paragraph>
|
||||
<Typography.Paragraph className="m-0">
|
||||
<Link
|
||||
data-testid="table-link"
|
||||
to={getTableTabPath(tableFqn, 'profiler')}
|
||||
onClick={(e) => e.stopPropagation()}>
|
||||
{tableName}
|
||||
</Link>
|
||||
</Typography.Paragraph>
|
||||
{isColumn && (
|
||||
<Space>
|
||||
<Typography.Text className="font-medium text-xs">{`${t(
|
||||
'label.column'
|
||||
)}:`}</Typography.Text>
|
||||
<Typography.Text className="text-grey-muted text-xs">
|
||||
{getNameFromFQN(
|
||||
getDecodedFqn(
|
||||
getEntityFqnFromEntityLink(
|
||||
test.entityLink,
|
||||
isColumn
|
||||
),
|
||||
true
|
||||
)
|
||||
) ?? '--'}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
)}
|
||||
<Checkbox checked={selectedItems?.has(test.id ?? '')} />
|
||||
</Space>
|
||||
);
|
||||
}}
|
||||
</VirtualList>
|
||||
</List>
|
||||
</Col>
|
||||
</Row>
|
||||
</Modal>
|
||||
<Typography.Paragraph className="m-0">
|
||||
{getEntityName(test.testDefinition)}
|
||||
</Typography.Paragraph>
|
||||
<Typography.Paragraph className="m-0">
|
||||
<Link
|
||||
data-testid="table-link"
|
||||
to={getTableTabPath(tableFqn, 'profiler')}
|
||||
onClick={(e) => e.stopPropagation()}>
|
||||
{tableName}
|
||||
</Link>
|
||||
</Typography.Paragraph>
|
||||
{isColumn && (
|
||||
<Space>
|
||||
<Typography.Text className="font-medium text-xs">{`${t(
|
||||
'label.column'
|
||||
)}:`}</Typography.Text>
|
||||
<Typography.Text className="text-grey-muted text-xs">
|
||||
{getNameFromFQN(
|
||||
getDecodedFqn(
|
||||
getEntityFqnFromEntityLink(
|
||||
test.entityLink,
|
||||
isColumn
|
||||
),
|
||||
true
|
||||
)
|
||||
) ?? '--'}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
)}
|
||||
</Space>
|
||||
);
|
||||
}}
|
||||
</VirtualList>
|
||||
</List>
|
||||
</Col>
|
||||
<Col className="d-flex justify-end items-center p-y-xss" span={24}>
|
||||
<Button type="link" onClick={onCancel}>
|
||||
{cancelText ?? t('label.cancel')}
|
||||
</Button>
|
||||
<Button loading={isLoading} type="primary" onClick={handleSubmit}>
|
||||
{submitText ?? t('label.submit')}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
@ -13,9 +13,9 @@
|
||||
import { EntityReference } from 'generated/tests/testCase';
|
||||
|
||||
export interface AddTestCaseModalProps {
|
||||
open: boolean;
|
||||
onCancel: () => void;
|
||||
onSubmit: () => void;
|
||||
existingTest: EntityReference[];
|
||||
testSuiteId: string;
|
||||
onCancel?: () => void;
|
||||
onSubmit: (testCaseIds: string[]) => void;
|
||||
existingTest?: EntityReference[];
|
||||
cancelText?: string;
|
||||
submitText?: string;
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
import { Col, Row } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import { SummaryCard } from 'components/common/SummaryCard/SummaryCard.component';
|
||||
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
||||
import { TestSummary } from 'generated/tests/testSuite';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -23,6 +24,8 @@ import { SummaryPanelProps } from './SummaryPanel.interface';
|
||||
|
||||
export const SummaryPanel = ({ testSuiteId }: SummaryPanelProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { permissions } = usePermissionProvider();
|
||||
const { testCase: testCasePermission } = permissions;
|
||||
|
||||
const [summary, setSummary] = useState<TestSummary>();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
@ -40,8 +43,10 @@ export const SummaryPanel = ({ testSuiteId }: SummaryPanelProps) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchTestSummary();
|
||||
}, [testSuiteId]);
|
||||
if (testCasePermission?.ViewAll || testCasePermission?.ViewBasic) {
|
||||
fetchTestSummary();
|
||||
}
|
||||
}, [testSuiteId, testCasePermission]);
|
||||
|
||||
return (
|
||||
<Row wrap gutter={[16, 16]}>
|
||||
|
@ -13,6 +13,7 @@
|
||||
import { Col, Row } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import Searchbar from 'components/common/searchbar/Searchbar';
|
||||
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
||||
import DataQualityTab from 'components/ProfilerDashboard/component/DataQualityTab';
|
||||
import { INITIAL_PAGING_VALUE, PAGE_SIZE } from 'constants/constants';
|
||||
import { SearchIndex } from 'enums/search.enum';
|
||||
@ -29,7 +30,11 @@ import QueryString from 'qs';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useHistory, useLocation, useParams } from 'react-router-dom';
|
||||
import { searchQuery } from 'rest/searchAPI';
|
||||
import { getListTestCase, ListTestCaseParams } from 'rest/testAPI';
|
||||
import {
|
||||
getListTestCase,
|
||||
getTestCaseById,
|
||||
ListTestCaseParams,
|
||||
} from 'rest/testAPI';
|
||||
import { showErrorToast } from 'utils/ToastUtils';
|
||||
import { DataQualitySearchParams } from '../DataQuality.interface';
|
||||
import { SummaryPanel } from '../SummaryPannel/SummaryPanel.component';
|
||||
@ -38,6 +43,8 @@ export const TestCases = () => {
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
const { tab } = useParams<{ tab: DataQualityPageTabs }>();
|
||||
const { permissions } = usePermissionProvider();
|
||||
const { testCase: testCasePermission } = permissions;
|
||||
|
||||
const params = useMemo(() => {
|
||||
const search = location.search;
|
||||
@ -115,16 +122,31 @@ export const TestCases = () => {
|
||||
pageSize: PAGE_SIZE,
|
||||
searchIndex: SearchIndex.TEST_CASE,
|
||||
query: searchValue,
|
||||
fetchSource: false,
|
||||
});
|
||||
const hits = (
|
||||
const promise = (
|
||||
response.hits.hits as SearchHitBody<
|
||||
SearchIndex.TEST_CASE,
|
||||
TestCaseSearchSource
|
||||
>[]
|
||||
).map((value) => value._source);
|
||||
).map((value) =>
|
||||
getTestCaseById(value._id ?? '', {
|
||||
fields: 'testDefinition,testCaseResult,testSuite',
|
||||
})
|
||||
);
|
||||
|
||||
const value = await Promise.allSettled(promise);
|
||||
|
||||
const testSuites = value.reduce((prev, curr) => {
|
||||
if (curr.status === 'fulfilled') {
|
||||
return [...prev, curr.value.data];
|
||||
}
|
||||
|
||||
return prev;
|
||||
}, [] as TestCase[]);
|
||||
|
||||
setTestCase({
|
||||
data: hits,
|
||||
data: testSuites,
|
||||
paging: { total: response.hits.total.value ?? 0 },
|
||||
});
|
||||
} catch (error) {
|
||||
@ -151,14 +173,16 @@ export const TestCases = () => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (tab === DataQualityPageTabs.TEST_CASES) {
|
||||
if (searchValue) {
|
||||
searchTestCases();
|
||||
} else {
|
||||
fetchTestCases();
|
||||
if (testCasePermission?.ViewAll || testCasePermission?.ViewBasic) {
|
||||
if (tab === DataQualityPageTabs.TEST_CASES) {
|
||||
if (searchValue) {
|
||||
searchTestCases();
|
||||
} else {
|
||||
fetchTestCases();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [tab, searchValue]);
|
||||
}, [tab, searchValue, testCasePermission]);
|
||||
|
||||
return (
|
||||
<Row className="p-x-lg p-t-md" gutter={[16, 16]}>
|
||||
|
@ -17,6 +17,7 @@ import FilterTablePlaceHolder from 'components/common/error-with-placeholder/Fil
|
||||
import NextPrevious from 'components/common/next-previous/NextPrevious';
|
||||
import { OwnerLabel } from 'components/common/OwnerLabel/OwnerLabel.component';
|
||||
import Searchbar from 'components/common/searchbar/Searchbar';
|
||||
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
||||
import { TableProfilerTab } from 'components/ProfilerDashboard/profilerDashboard.interface';
|
||||
import ProfilerProgressWidget from 'components/TableProfiler/Component/ProfilerProgressWidget';
|
||||
import {
|
||||
@ -55,6 +56,8 @@ export const TestSuites = () => {
|
||||
useParams<{ tab: DataQualityPageTabs }>();
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
const { permissions } = usePermissionProvider();
|
||||
const { testSuite: testSuitePermission } = permissions;
|
||||
|
||||
const [testSuites, setTestSuites] = useState<PagingResponse<TestSuite[]>>({
|
||||
data: [],
|
||||
@ -177,8 +180,10 @@ export const TestSuites = () => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchTestSuites();
|
||||
}, [tab]);
|
||||
if (testSuitePermission?.ViewAll || testSuitePermission?.ViewBasic) {
|
||||
fetchTestSuites();
|
||||
}
|
||||
}, [tab, testSuitePermission]);
|
||||
|
||||
return (
|
||||
<Row className="p-x-lg p-t-md" gutter={[16, 16]}>
|
||||
@ -192,13 +197,14 @@ export const TestSuites = () => {
|
||||
/>
|
||||
</Col>
|
||||
<Col>
|
||||
{tab === DataQualityPageTabs.TEST_SUITES && (
|
||||
<Link to={ROUTES.ADD_TEST_SUITES}>
|
||||
<Button type="primary">
|
||||
{t('label.add-entity', { entity: t('label.test-suite') })}
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
{tab === DataQualityPageTabs.TEST_SUITES &&
|
||||
testSuitePermission?.Create && (
|
||||
<Link to={ROUTES.ADD_TEST_SUITES}>
|
||||
<Button type="primary">
|
||||
{t('label.add-entity', { entity: t('label.test-suite') })}
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
|
@ -11,8 +11,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Col, Row, Typography } from 'antd';
|
||||
import { Button, Col, Row, Space, Typography } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichTextEditorPreviewer';
|
||||
import DatePickerMenu from 'components/DatePickerMenu/DatePickerMenu.component';
|
||||
import {
|
||||
GREEN_3,
|
||||
@ -97,13 +98,13 @@ const TestSummary: React.FC<TestSummaryProps> = ({
|
||||
const values = result.testResultValue?.reduce((acc, curr) => {
|
||||
return {
|
||||
...acc,
|
||||
[curr.name || 'value']: round(parseFloat(curr.value ?? ''), 2) || 0,
|
||||
[curr.name ?? 'value']: round(parseFloat(curr.value ?? ''), 2) || 0,
|
||||
};
|
||||
}, {});
|
||||
|
||||
chartData.push({
|
||||
name: getFormattedDateFromSeconds(result.timestamp as number),
|
||||
status: result.testCaseStatus || '',
|
||||
status: result.testCaseStatus ?? '',
|
||||
...values,
|
||||
});
|
||||
});
|
||||
@ -111,9 +112,9 @@ const TestSummary: React.FC<TestSummaryProps> = ({
|
||||
setChartData({
|
||||
information:
|
||||
currentData[0]?.testResultValue?.map((info, i) => ({
|
||||
label: info.name || '',
|
||||
label: info.name ?? '',
|
||||
color: COLORS[i],
|
||||
})) || [],
|
||||
})) ?? [],
|
||||
data: chartData,
|
||||
});
|
||||
};
|
||||
@ -251,13 +252,16 @@ const TestSummary: React.FC<TestSummaryProps> = ({
|
||||
|
||||
if (isSqlQuery) {
|
||||
return (
|
||||
<Row className="sql-expression-container" gutter={8} key={param.name}>
|
||||
<Col span={showExpandIcon ? 2 : 3}>
|
||||
<Row
|
||||
className="sql-expression-container"
|
||||
gutter={[8, 8]}
|
||||
key={param.name}>
|
||||
<Col span={24}>
|
||||
<Typography.Text className="text-grey-muted">
|
||||
{`${param.name}:`}
|
||||
</Typography.Text>
|
||||
</Col>
|
||||
<Col span={showExpandIcon ? 22 : 21}>
|
||||
<Col span={24}>
|
||||
<SchemaEditor
|
||||
editorClass="table-query-editor"
|
||||
mode={{ name: CSMode.SQL }}
|
||||
@ -344,30 +348,37 @@ const TestSummary: React.FC<TestSummaryProps> = ({
|
||||
)}
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
{showParameters && (
|
||||
<Row align="top" data-testid="params-container" gutter={16}>
|
||||
<Row align="top" data-testid="params-container" gutter={[16, 16]}>
|
||||
{showParameters && (
|
||||
<Col>
|
||||
<Typography.Text className="text-grey-muted">
|
||||
{`${t('label.parameter')}:`}
|
||||
</Typography.Text>
|
||||
</Col>
|
||||
<Col>
|
||||
{!isEmpty(parameterValuesWithoutSqlExpression) ? (
|
||||
<Row className="parameter-value-container" gutter={[4, 4]}>
|
||||
{parameterValuesWithoutSqlExpression?.map(showParamsData)}
|
||||
</Row>
|
||||
) : (
|
||||
<Typography.Text type="secondary">
|
||||
<Typography.Text className="m-l-xs" type="secondary">
|
||||
{t('label.no-parameter-available')}
|
||||
</Typography.Text>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
|
||||
{!isUndefined(parameterValuesWithSqlExpression)
|
||||
? parameterValuesWithSqlExpression.map(showParamsData)
|
||||
: null}
|
||||
)}
|
||||
{!isUndefined(parameterValuesWithSqlExpression) ? (
|
||||
<Col>{parameterValuesWithSqlExpression.map(showParamsData)}</Col>
|
||||
) : null}
|
||||
{data.description && (
|
||||
<Col>
|
||||
<Space direction="vertical" size={4}>
|
||||
<Typography.Text className="text-grey-muted">
|
||||
{`${t('label.description')}:`}
|
||||
</Typography.Text>
|
||||
<RichTextEditorPreviewer markdown={data.description} />
|
||||
</Space>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
|
@ -118,6 +118,7 @@ const AddTestSuiteForm: React.FC<AddTestSuiteFormProps> = ({
|
||||
<Form.Item label={t('label.description')} name="description">
|
||||
<RichTextEditor
|
||||
data-testid="test-suite-description"
|
||||
height="200px"
|
||||
initialValue={testSuite?.description ?? ''}
|
||||
onTextChange={(value) => form.setFieldsValue({ description: value })}
|
||||
/>
|
||||
|
@ -11,34 +11,28 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Col, Form, Row, Select, Space, Typography } from 'antd';
|
||||
import { Col, Row, Space, Typography } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import RightPanel from 'components/AddDataQualityTest/components/RightPanel';
|
||||
import { getRightPanelForAddTestSuitePage } from 'components/AddDataQualityTest/rightPanelData';
|
||||
import { AddTestCaseList } from 'components/AddTestCaseList/AddTestCaseList.component';
|
||||
import ResizablePanels from 'components/common/ResizablePanels/ResizablePanels';
|
||||
import SuccessScreen from 'components/common/success-screen/SuccessScreen';
|
||||
import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component';
|
||||
import IngestionStepper from 'components/IngestionStepper/IngestionStepper.component';
|
||||
import { HTTP_STATUS_CODE } from 'constants/auth.constants';
|
||||
import { PAGE_SIZE_LARGE } from 'constants/constants';
|
||||
import {
|
||||
STEPS_FOR_ADD_TEST_SUITE,
|
||||
TEST_SUITE_STEPPER_BREADCRUMB,
|
||||
} from 'constants/TestSuite.constant';
|
||||
import { FormSubmitType } from 'enums/form.enum';
|
||||
import { OwnerType } from 'enums/user.enum';
|
||||
import { TestCase } from 'generated/tests/testCase';
|
||||
import { TestSuite } from 'generated/tests/testSuite';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
addTestCaseToLogicalTestSuite,
|
||||
createTestSuites,
|
||||
getListTestCase,
|
||||
} from 'rest/testAPI';
|
||||
import { addTestCaseToLogicalTestSuite, createTestSuites } from 'rest/testAPI';
|
||||
import { getCurrentUserId } from 'utils/CommonUtils';
|
||||
import { getEntityName } from 'utils/EntityUtils';
|
||||
import { getTestSuitePath } from 'utils/RouterUtils';
|
||||
import { showErrorToast } from 'utils/ToastUtils';
|
||||
import AddTestSuiteForm from '../AddTestSuiteForm/AddTestSuiteForm';
|
||||
@ -48,11 +42,9 @@ const TestSuiteStepper = () => {
|
||||
const history = useHistory();
|
||||
const [activeServiceStep, setActiveServiceStep] = useState(1);
|
||||
const [testSuiteResponse, setTestSuiteResponse] = useState<TestSuite>();
|
||||
const [testCases, setTestCases] = useState<TestCase[]>([]);
|
||||
const [selectedTestCase, setSelectedTestCase] = useState<string[]>([]);
|
||||
|
||||
const handleViewTestSuiteClick = () => {
|
||||
history.push(getTestSuitePath(testSuiteResponse?.fullyQualifiedName || ''));
|
||||
history.push(getTestSuitePath(testSuiteResponse?.fullyQualifiedName ?? ''));
|
||||
};
|
||||
|
||||
const handleTestSuitNextClick = (data: TestSuite) => {
|
||||
@ -60,7 +52,7 @@ const TestSuiteStepper = () => {
|
||||
setActiveServiceStep(2);
|
||||
};
|
||||
|
||||
const onSubmit = async (data: { testCase: string[] }) => {
|
||||
const onSubmit = async (data: string[]) => {
|
||||
try {
|
||||
const owner = {
|
||||
id: getCurrentUserId(),
|
||||
@ -74,7 +66,7 @@ const TestSuiteStepper = () => {
|
||||
});
|
||||
setTestSuiteResponse(response);
|
||||
await addTestCaseToLogicalTestSuite({
|
||||
testCaseIds: data.testCase,
|
||||
testCaseIds: data,
|
||||
testSuiteId: response.id ?? '',
|
||||
});
|
||||
setActiveServiceStep(3);
|
||||
@ -100,61 +92,15 @@ const TestSuiteStepper = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchTestCases = async () => {
|
||||
try {
|
||||
const response = await getListTestCase({ limit: PAGE_SIZE_LARGE });
|
||||
setTestCases(response.data);
|
||||
} catch (error) {
|
||||
setTestCases([]);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchTestCases();
|
||||
}, []);
|
||||
|
||||
const selectTestCase = useMemo(() => {
|
||||
return (
|
||||
<Form
|
||||
data-testid="test-case-form"
|
||||
initialValues={{ testCase: selectedTestCase }}
|
||||
layout="vertical"
|
||||
name="selectTestCase"
|
||||
onFinish={onSubmit}
|
||||
onValuesChange={({ testCase }) => setSelectedTestCase(testCase)}>
|
||||
<Form.Item label={t('label.test-case-plural')} name="testCase">
|
||||
<Select
|
||||
mode="multiple"
|
||||
placeholder={t('label.please-select-entity', {
|
||||
entity: t('label.test-case-plural'),
|
||||
})}>
|
||||
{testCases.map((test) => (
|
||||
<Select.Option key={test.id}>{getEntityName(test)}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item noStyle>
|
||||
<Space className="w-full justify-end" size={16}>
|
||||
<Button
|
||||
data-testid="back-button"
|
||||
onClick={() => setActiveServiceStep(1)}>
|
||||
{t('label.back')}
|
||||
</Button>
|
||||
<Button
|
||||
data-testid="submit-button"
|
||||
htmlType="submit"
|
||||
type="primary">
|
||||
{t('label.submit')}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
}, [testCases, selectedTestCase, testSuiteResponse]);
|
||||
|
||||
const RenderSelectedTab = useCallback(() => {
|
||||
if (activeServiceStep === 2) {
|
||||
return selectTestCase;
|
||||
return (
|
||||
<AddTestCaseList
|
||||
cancelText={t('label.back')}
|
||||
onCancel={() => setActiveServiceStep(1)}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
);
|
||||
} else if (activeServiceStep === 3) {
|
||||
return (
|
||||
<SuccessScreen
|
||||
|
@ -72,6 +72,10 @@ export const ELASTIC_SEARCH_INDEX_ENTITIES = [
|
||||
value: 'query',
|
||||
label: t('label.query'),
|
||||
},
|
||||
{
|
||||
value: 'testCase',
|
||||
label: t('label.test-case'),
|
||||
},
|
||||
];
|
||||
|
||||
export const ELASTIC_SEARCH_INITIAL_VALUES = {
|
||||
|
@ -11,9 +11,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Col, Row, Space } from 'antd';
|
||||
import { Button, Col, Modal, Row, Space } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import { AddTestCaseModal } from 'components/AddTestCaseModal/AddTestCaseModal.component';
|
||||
import { AddTestCaseList } from 'components/AddTestCaseList/AddTestCaseList.component';
|
||||
import { useAuthContext } from 'components/authentication/auth-provider/AuthProvider';
|
||||
import Description from 'components/common/description/Description';
|
||||
import ManageButton from 'components/common/entityPageInfo/ManageButton/ManageButton';
|
||||
@ -38,6 +38,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import {
|
||||
addTestCaseToLogicalTestSuite,
|
||||
getListTestCase,
|
||||
getTestSuiteByName,
|
||||
ListTestCaseParams,
|
||||
@ -154,6 +155,19 @@ const TestSuiteDetailsPage = () => {
|
||||
fetchTestCases();
|
||||
};
|
||||
|
||||
const handleAddTestCaseSubmit = async (testCaseIds: string[]) => {
|
||||
try {
|
||||
await addTestCaseToLogicalTestSuite({
|
||||
testCaseIds,
|
||||
testSuiteId: testSuite?.id ?? '',
|
||||
});
|
||||
setIsTestCaseModalOpen(false);
|
||||
fetchTestCases();
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchTestSuiteByName = async () => {
|
||||
try {
|
||||
const response = await getTestSuiteByName(testSuiteFQN, {
|
||||
@ -309,11 +323,16 @@ const TestSuiteDetailsPage = () => {
|
||||
titleLinks={slashedBreadCrumb}
|
||||
/>
|
||||
<Space>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => setIsTestCaseModalOpen(true)}>
|
||||
{t('label.add-entity', { entity: t('label.test-case-plural') })}
|
||||
</Button>
|
||||
{(testSuitePermissions.EditAll ||
|
||||
testSuitePermissions.EditTests) && (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => setIsTestCaseModalOpen(true)}>
|
||||
{t('label.add-entity', {
|
||||
entity: t('label.test-case-plural'),
|
||||
})}
|
||||
</Button>
|
||||
)}
|
||||
<ManageButton
|
||||
isRecursiveDelete
|
||||
afterDeleteAction={afterDeleteAction}
|
||||
@ -366,13 +385,22 @@ const TestSuiteDetailsPage = () => {
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<AddTestCaseModal
|
||||
existingTest={testSuite?.tests ?? []}
|
||||
<Modal
|
||||
centered
|
||||
destroyOnClose
|
||||
closable={false}
|
||||
footer={null}
|
||||
open={isTestCaseModalOpen}
|
||||
testSuiteId={testSuite?.id ?? ''}
|
||||
onCancel={() => setIsTestCaseModalOpen(false)}
|
||||
onSubmit={afterSubmitAction}
|
||||
/>
|
||||
title={t('label.add-entity', {
|
||||
entity: t('label.test-case-plural'),
|
||||
})}
|
||||
width={750}>
|
||||
<AddTestCaseList
|
||||
existingTest={testSuite?.tests ?? []}
|
||||
onCancel={() => setIsTestCaseModalOpen(false)}
|
||||
onSubmit={handleAddTestCaseSubmit}
|
||||
/>
|
||||
</Modal>
|
||||
</Col>
|
||||
</Row>
|
||||
</PageLayoutV1>
|
||||
|
@ -113,6 +113,16 @@ export const getTestCaseByFqn = async (
|
||||
|
||||
return response;
|
||||
};
|
||||
export const getTestCaseById = async (
|
||||
id: string,
|
||||
params?: Pick<ListParams, 'fields' | 'include'>
|
||||
) => {
|
||||
const response = await APIClient.get<TestCase>(`${testCaseUrl}/${id}`, {
|
||||
params,
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
export const createTestCase = async (data: CreateTestCase) => {
|
||||
const response = await APIClient.post<
|
||||
|
Loading…
x
Reference in New Issue
Block a user