diff --git a/packages/base/src/locale/zh-CN/dmsDataExport.ts b/packages/base/src/locale/zh-CN/dmsDataExport.ts index 2c3294732..aca70f8e9 100644 --- a/packages/base/src/locale/zh-CN/dmsDataExport.ts +++ b/packages/base/src/locale/zh-CN/dmsDataExport.ts @@ -45,13 +45,16 @@ export default { '目前,支持 SQL 美化的数据库类型有 {{supportType}}。如果未选择数据源或选择的数据源类型尚未得到支持,进行 SQL 美化可能会导致 SQL 语句语法错误。' } }, + submit: { + buttonText: '提交工单', + onlySupportDDLSqls: '仅支持对DQL语句创建导出工单', + hasExceptionRule: '当前存在审核规则未被校验,请排除问题后重新触发审核' + }, update: { baseTitle: '工单基本信息', sourceTitle: '工单导出对象', methodTitle: '导出方式', - updateInfoAction: '修改工单', - submitAction: '提交工单', - submitTips: '仅支持对DQL语句创建导出工单' + updateInfoAction: '修改工单' }, result: { success: '工单创建成功', diff --git a/packages/base/src/page/CloudBeaver/Drawer/test/__snapshots__/CBSqlOperationAuditDetailDrawer.test.tsx.snap b/packages/base/src/page/CloudBeaver/Drawer/test/__snapshots__/CBSqlOperationAuditDetailDrawer.test.tsx.snap index 34b2b73e8..a17cbcc46 100644 --- a/packages/base/src/page/CloudBeaver/Drawer/test/__snapshots__/CBSqlOperationAuditDetailDrawer.test.tsx.snap +++ b/packages/base/src/page/CloudBeaver/Drawer/test/__snapshots__/CBSqlOperationAuditDetailDrawer.test.tsx.snap @@ -69,7 +69,7 @@ exports[`base/CloudBeaver/CBSqlOperationAuditDetailDrawer should match snap shot
= ({ taskID, projectID, - updateExecuteSQLsTypeIsDQL + onSuccessGetDataExportTaskSqls, + onErrorGetDataExportTaskSqls }) => { const [currentAuditResultRecord, setCurrentAuditResultRecord] = useState(); @@ -63,12 +64,10 @@ const AuditResultTable: React.FC = ({ ready: typeof taskID === 'string', refreshDeps: [pagination, taskID], onSuccess(res) { - updateExecuteSQLsTypeIsDQL?.( - !!res.list?.every((item) => item.export_sql_type === 'dql') - ); + onSuccessGetDataExportTaskSqls?.(res.list ?? []); }, onError() { - updateExecuteSQLsTypeIsDQL?.(true); + onErrorGetDataExportTaskSqls?.(); } } ); diff --git a/packages/base/src/page/DataExportManagement/Common/AuditResultList/Table/index.type.ts b/packages/base/src/page/DataExportManagement/Common/AuditResultList/Table/index.type.ts index 1a6b2e9d5..fff3a6669 100644 --- a/packages/base/src/page/DataExportManagement/Common/AuditResultList/Table/index.type.ts +++ b/packages/base/src/page/DataExportManagement/Common/AuditResultList/Table/index.type.ts @@ -3,7 +3,8 @@ import { IListDataExportTaskSQL } from '@actiontech/shared/lib/api/base/service/ export type AuditResultTableProps = { taskID?: string; projectID: string; - updateExecuteSQLsTypeIsDQL?: (val: boolean) => void; + onSuccessGetDataExportTaskSqls?: (taskSqls: IListDataExportTaskSQL[]) => void; + onErrorGetDataExportTaskSqls?: () => void; }; export type AuditResultDrawerProps = { diff --git a/packages/base/src/page/DataExportManagement/Common/AuditResultList/__snapshots__/index.test.tsx.snap b/packages/base/src/page/DataExportManagement/Common/AuditResultList/__snapshots__/index.test.tsx.snap index afe277558..131fc93ef 100644 --- a/packages/base/src/page/DataExportManagement/Common/AuditResultList/__snapshots__/index.test.tsx.snap +++ b/packages/base/src/page/DataExportManagement/Common/AuditResultList/__snapshots__/index.test.tsx.snap @@ -2795,7 +2795,7 @@ exports[`test DataExport/Common/AuditResultList should match snapshot 4`] = `
{ expect(container).toMatchSnapshot(); }); - it('should execute updateExecuteSQLsTypeIsDQL', async () => { - const updateExecuteSQLsTypeIsDQLSpy = jest.fn(); + it('should execute onSuccessGetDataExportTaskSqlsSpy', async () => { + const onSuccessGetDataExportTaskSqlsSpy = jest.fn(); superRender( ); await act(async () => jest.advanceTimersByTime(3000)); await act(async () => jest.advanceTimersByTime(3000)); - expect(updateExecuteSQLsTypeIsDQLSpy).toHaveBeenCalledTimes(1); - expect(updateExecuteSQLsTypeIsDQLSpy).toHaveBeenCalledWith(true); - - jest.clearAllMocks(); - cleanup(); - - getTaskSQLsSpy.mockImplementation(() => - createSpySuccessResponse({ - data: [ - ...ListDataExportTaskSQLsResponseData, - { - uid: 7, - sql: 'INSERT INTO t1 values (name, "test")', - export_status: '', - export_sql_type: 'dml', - audit_level: '' - } - ] - }) + expect(onSuccessGetDataExportTaskSqlsSpy).toHaveBeenCalledTimes(1); + expect(onSuccessGetDataExportTaskSqlsSpy).toHaveBeenCalledWith( + ListDataExportTaskSQLsResponseData ); - superRender( - - ); - await act(async () => jest.advanceTimersByTime(3000)); - await act(async () => jest.advanceTimersByTime(3000)); - expect(updateExecuteSQLsTypeIsDQLSpy).toHaveBeenCalledTimes(1); - expect(updateExecuteSQLsTypeIsDQLSpy).toHaveBeenCalledWith(false); - - jest.clearAllMocks(); - cleanup(); - - getTaskSQLsSpy.mockImplementation(() => createSpyFailResponse({})); - superRender( - - ); - await act(async () => jest.advanceTimersByTime(3000)); - await act(async () => jest.advanceTimersByTime(3000)); - expect(updateExecuteSQLsTypeIsDQLSpy).toHaveBeenCalledTimes(1); - expect(updateExecuteSQLsTypeIsDQLSpy).toHaveBeenCalledWith(true); }); it('should render sql rewriter button', async () => { diff --git a/packages/base/src/page/DataExportManagement/Common/AuditResultList/index.tsx b/packages/base/src/page/DataExportManagement/Common/AuditResultList/index.tsx index 0a6877489..742cd730f 100644 --- a/packages/base/src/page/DataExportManagement/Common/AuditResultList/index.tsx +++ b/packages/base/src/page/DataExportManagement/Common/AuditResultList/index.tsx @@ -14,7 +14,8 @@ import { AuditTaskResV1AuditLevelEnum } from '@actiontech/shared/lib/api/sqle/se const AuditResultList: React.FC = ({ taskIDs, projectID, - updateExecuteSQLsTypeIsDQL + onErrorGetDataExportTaskSqls, + onSuccessGetDataExportTaskSqls }) => { const [tasks, setTasks] = useState([]); @@ -68,7 +69,8 @@ const AuditResultList: React.FC = ({ ); diff --git a/packages/base/src/page/DataExportManagement/Common/AuditResultList/index.type.ts b/packages/base/src/page/DataExportManagement/Common/AuditResultList/index.type.ts index ba40897f9..685d204be 100644 --- a/packages/base/src/page/DataExportManagement/Common/AuditResultList/index.type.ts +++ b/packages/base/src/page/DataExportManagement/Common/AuditResultList/index.type.ts @@ -1,5 +1,8 @@ +import { IListDataExportTaskSQL } from '@actiontech/shared/lib/api/base/service/common'; + export type AuditResultListProps = { taskIDs: string[]; projectID: string; - updateExecuteSQLsTypeIsDQL?: (val: boolean) => void; + onSuccessGetDataExportTaskSqls?: (taskSqls: IListDataExportTaskSQL[]) => void; + onErrorGetDataExportTaskSqls?: () => void; }; diff --git a/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/SubmitWorkflowButton/__tests__/__snapshots__/index.test.tsx.snap b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/SubmitWorkflowButton/__tests__/__snapshots__/index.test.tsx.snap new file mode 100644 index 000000000..d6b4c79ba --- /dev/null +++ b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/SubmitWorkflowButton/__tests__/__snapshots__/index.test.tsx.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test SubmitWorkflowButton handles button click 1`] = ` +
+ +
+`; diff --git a/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/SubmitWorkflowButton/__tests__/index.test.tsx b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/SubmitWorkflowButton/__tests__/index.test.tsx new file mode 100644 index 000000000..e8a1dc64a --- /dev/null +++ b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/SubmitWorkflowButton/__tests__/index.test.tsx @@ -0,0 +1,77 @@ +import { fireEvent, screen } from '@testing-library/dom'; +import SubmitWorkflowButton from '..'; +import { superRender } from '../../../../../../../testUtils/customRender'; + +describe('test SubmitWorkflowButton', () => { + it('handles button click', () => { + const onClick = jest.fn(); + + const { container } = superRender( + + ); + expect(container).toMatchSnapshot(); + + fireEvent.click(screen.getByText('提交工单')); + expect(onClick).toHaveBeenCalledTimes(1); + }); + + it('displays the tooltip message when executeSQLsIsDQL is false', async () => { + const onClick = jest.fn(); + + superRender( + + ); + fireEvent.click(screen.getByText('提交工单')); + expect(onClick).not.toHaveBeenCalled(); + + fireEvent.mouseOver(screen.getByText('提交工单')); + + await screen.findByText('仅支持对DQL语句创建导出工单'); + }); + + it('disables the button when loading prop is true', () => { + const onClick = jest.fn(); + + superRender( + + ); + fireEvent.click(screen.getByText('提交工单')); + expect(onClick).not.toHaveBeenCalled(); + }); + + it('displays the tooltip message when hasExceptionAuditRule is equal true', async () => { + const onClick = jest.fn(); + + superRender( + + ); + fireEvent.click(screen.getByText('提交工单')); + expect(onClick).not.toHaveBeenCalled(); + + fireEvent.mouseOver(screen.getByText('提交工单')); + + await screen.findByText( + '当前存在审核规则未被校验,请排除问题后重新触发审核' + ); + }); +}); diff --git a/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/SubmitWorkflowButton/index.tsx b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/SubmitWorkflowButton/index.tsx new file mode 100644 index 000000000..4c8d13580 --- /dev/null +++ b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/SubmitWorkflowButton/index.tsx @@ -0,0 +1,50 @@ +import { ActionButton, BasicButton } from '@actiontech/shared'; +import { SubmitWorkflowButtonProps } from './index.type'; +import { useTranslation } from 'react-i18next'; +import { InfoHexagonOutlined } from '@actiontech/icons'; + +const SubmitWorkflowButton: React.FC = ({ + loading, + onClick, + hasExceptionAuditRule, + executeSQLsIsDQL +}) => { + const { t } = useTranslation(); + + if (hasExceptionAuditRule) { + return ( + } + tooltip={{ + title: t('dmsDataExport.create.submit.hasExceptionRule') + }} + text={t('dmsDataExport.create.submit.buttonText')} + /> + ); + } + + if (!executeSQLsIsDQL) { + return ( + } + tooltip={{ + title: t('dmsDataExport.create.submit.onlySupportDDLSqls') + }} + text={t('dmsDataExport.create.submit.buttonText')} + /> + ); + } + + return ( + + {t('dmsDataExport.create.submit.buttonText')} + + ); +}; + +export default SubmitWorkflowButton; diff --git a/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/SubmitWorkflowButton/index.type.ts b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/SubmitWorkflowButton/index.type.ts new file mode 100644 index 000000000..586af8ed8 --- /dev/null +++ b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/SubmitWorkflowButton/index.type.ts @@ -0,0 +1,6 @@ +export type SubmitWorkflowButtonProps = { + executeSQLsIsDQL: boolean; + onClick: () => void; + loading: boolean; + hasExceptionAuditRule: boolean; +}; diff --git a/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/__tests__/index.test.tsx b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/__tests__/index.test.tsx index 50e886873..d4ba3fae9 100644 --- a/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/__tests__/index.test.tsx +++ b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/__tests__/index.test.tsx @@ -86,7 +86,9 @@ describe('test base/DataExport/Create/SubmitWorkflow', () => { superRender(); expect(screen.getByText('修改工单').closest('button')).toBeDisabled(); - expect(screen.getByText('提交工单').closest('button')).toBeDisabled(); + expect(screen.getByText('提交工单').closest('button')).toHaveClass( + 'ant-btn-loading' + ); }); it('should send submit request when clicked submit button', async () => { diff --git a/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/index.tsx b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/index.tsx index 77b4311af..6f65b88b7 100644 --- a/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/index.tsx +++ b/packages/base/src/page/DataExportManagement/Create/components/SubmitWorkflow/index.tsx @@ -1,13 +1,8 @@ import { useCurrentProject } from '@actiontech/shared/lib/global'; import useCreateDataExportReduxManage from '../../hooks/index.redux'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import UpdateInfoDrawer from './UpdateInfoDrawer'; -import { - BasicButton, - BasicToolTip, - EmptyBox, - PageHeader -} from '@actiontech/shared'; +import { BasicButton, PageHeader } from '@actiontech/shared'; import { useTranslation } from 'react-i18next'; import BackToWorkflowList from '../../../Common/BackToWorkflowList'; import { Space } from 'antd'; @@ -17,6 +12,9 @@ import AuditResultList from '../../../Common/AuditResultList'; import DataExportWorkflows from '@actiontech/shared/lib/api/base/service/DataExportWorkflows'; import { ResponseCode } from '@actiontech/shared/lib/enum'; import { CreateDataExportPageEnum } from '../../../../../store/dataExport'; +import useCheckTaskAuditRuleExceptionStatus from '../../hooks/useCheckTaskAuditRuleExceptionStatus'; +import { IListDataExportTaskSQL } from '@actiontech/shared/lib/api/base/service/common'; +import SubmitWorkflowButton from './SubmitWorkflowButton'; const SubmitExportWorkflow: React.FC = () => { const { t } = useTranslation(); @@ -34,7 +32,28 @@ const SubmitExportWorkflow: React.FC = () => { const [executeSQLsIsDQL, updateExecuteSQLsTypeIsDQL] = useState(true); - const submit = () => { + const { + hasExceptionAuditRule, + updateTaskAuditRuleExceptionStatus, + resetTaskAuditRuleExceptionStatus + } = useCheckTaskAuditRuleExceptionStatus(); + + const onSuccessGetDataExportTaskSqls = useCallback( + (taskSqls: IListDataExportTaskSQL[]) => { + updateTaskAuditRuleExceptionStatus(taskSqls); + updateExecuteSQLsTypeIsDQL?.( + taskSqls?.every((item) => item.export_sql_type === 'dql') + ); + }, + [updateTaskAuditRuleExceptionStatus] + ); + + const onErrorGetDataExportTaskSqls = useCallback(() => { + resetTaskAuditRuleExceptionStatus(); + updateExecuteSQLsTypeIsDQL(true); + }, [resetTaskAuditRuleExceptionStatus]); + + const onSubmit = () => { updateSubmitLoading(true); DataExportWorkflows.AddDataExportWorkflow({ project_uid: projectID, @@ -77,26 +96,12 @@ const SubmitExportWorkflow: React.FC = () => { {t('dmsDataExport.create.update.updateInfoAction')} - - - {t('dmsDataExport.create.update.submitAction')} - - - } - > - - {t('dmsDataExport.create.update.submitAction')} - - + } /> @@ -108,7 +113,8 @@ const SubmitExportWorkflow: React.FC = () => { diff --git a/packages/base/src/page/DataExportManagement/Create/hooks/useCheckTaskAuditRuleExceptionStatus.ts b/packages/base/src/page/DataExportManagement/Create/hooks/useCheckTaskAuditRuleExceptionStatus.ts new file mode 100644 index 000000000..3eaffb66f --- /dev/null +++ b/packages/base/src/page/DataExportManagement/Create/hooks/useCheckTaskAuditRuleExceptionStatus.ts @@ -0,0 +1,32 @@ +import { IAuditTaskSQLResV2 } from '@actiontech/shared/lib/api/sqle/service/common'; +import { RuleResV1LevelEnum } from '@actiontech/shared/lib/api/sqle/service/common.enum'; +import { useCallback, useState } from 'react'; + +const useCheckTaskAuditRuleExceptionStatus = () => { + const [hasExceptionAuditRule, setHasExceptionAuditRule] = useState(false); + + const updateTaskAuditRuleExceptionStatus = useCallback( + (taskSqls: IAuditTaskSQLResV2[]) => { + const existsExceptionRule = taskSqls.some((item) => + item.audit_result?.some( + (result) => + !Object.keys(RuleResV1LevelEnum).includes(result.level ?? '') + ) + ); + setHasExceptionAuditRule(existsExceptionRule); + }, + [] + ); + + const resetTaskAuditRuleExceptionStatus = useCallback(() => { + setHasExceptionAuditRule(false); + }, []); + + return { + hasExceptionAuditRule, + updateTaskAuditRuleExceptionStatus, + resetTaskAuditRuleExceptionStatus + }; +}; + +export default useCheckTaskAuditRuleExceptionStatus; diff --git a/packages/sqle/src/components/AuditResultMessage/ResultIconRender.tsx b/packages/sqle/src/components/AuditResultMessage/ResultIconRender.tsx index a99864019..49750bf0e 100644 --- a/packages/sqle/src/components/AuditResultMessage/ResultIconRender.tsx +++ b/packages/sqle/src/components/AuditResultMessage/ResultIconRender.tsx @@ -1,5 +1,4 @@ import React, { useMemo } from 'react'; - import { Space } from 'antd'; import { CheckCircleFilled, @@ -7,16 +6,18 @@ import { InfoHexagonFilled, CloseCircleFilled } from '@actiontech/icons'; -import { EmptyBox } from '@actiontech/shared'; import { useTranslation } from 'react-i18next'; import { ResultIconTagStyleWrapper } from './style'; +import { ResultIconRenderProps } from './index.type'; -export type IResultIconRender = { - iconLevels?: string[]; - isAuditing?: boolean; +const IconLevelDictionary = { + normal: , + notice: , + warn: , + error: }; -const ResultIconRender = (props: IResultIconRender) => { +const ResultIconRender = (props: ResultIconRenderProps) => { const { iconLevels, isAuditing } = props; const { t } = useTranslation(); @@ -25,36 +26,37 @@ const ResultIconRender = (props: IResultIconRender) => { return Array.from(new Set(iconLevels?.filter((icon: string) => icon))); }, [iconLevels]); - const renderIcon = useMemo(() => { - return { - normal: , - notice: , - warn: , - error: - }; - }, []); + if (isAuditing) { + return ( + + {t('components.auditResultMessage.auditing')} + + ); + } + + if (iconLevels?.includes('audit_execution_error')) { + return ( + + + {t('components.auditResultMessage.hasException')} + + ); + } return ( - - {t('sqlAudit.list.status.auditStatus.auditing')} - - } - > - - {!!iconData.length - ? iconData.map((icon) => { - return ( - - {renderIcon[icon as keyof typeof renderIcon] ?? null} - - ); - }) - : renderIcon.normal} - - + + {!!iconData.length + ? iconData.map((icon) => { + return ( + + {IconLevelDictionary[ + icon as keyof typeof IconLevelDictionary + ] ?? null} + + ); + }) + : IconLevelDictionary.normal} + ); }; diff --git a/packages/sqle/src/components/AuditResultMessage/test/ResultIconRender.test.tsx b/packages/sqle/src/components/AuditResultMessage/__tests__/ResultIconRender.test.tsx similarity index 66% rename from packages/sqle/src/components/AuditResultMessage/test/ResultIconRender.test.tsx rename to packages/sqle/src/components/AuditResultMessage/__tests__/ResultIconRender.test.tsx index 26e226623..5631a6baa 100644 --- a/packages/sqle/src/components/AuditResultMessage/test/ResultIconRender.test.tsx +++ b/packages/sqle/src/components/AuditResultMessage/__tests__/ResultIconRender.test.tsx @@ -1,9 +1,10 @@ -import ResultIconRender, { IResultIconRender } from '../ResultIconRender'; - +import ResultIconRender from '../ResultIconRender'; import { renderWithTheme } from '../../../testUtils/customRender'; +import { ResultIconRenderProps } from '../index.type'; +import { screen } from '@testing-library/dom'; describe('sqle/components/AuditResultMessage/ResultIconRender', () => { - const customRender = (params: IResultIconRender) => { + const customRender = (params: ResultIconRenderProps) => { return renderWithTheme(); }; @@ -46,4 +47,20 @@ describe('sqle/components/AuditResultMessage/ResultIconRender', () => { const { baseElement } = customRender({ isAuditing: true }); expect(baseElement).toMatchSnapshot(); }); + + it('should render exception info when levels include "audit_execution_error"', () => { + const { baseElement } = customRender({ + iconLevels: [ + 'audit_execution_error', + 'notice', + 'warn', + 'normal', + 'normal', + 'normal' + ] + }); + + expect(baseElement).toMatchSnapshot(); + expect(screen.getByText('审核存在异常')).toBeInTheDocument(); + }); }); diff --git a/packages/sqle/src/components/AuditResultMessage/test/__snapshots__/ResultIconRender.test.tsx.snap b/packages/sqle/src/components/AuditResultMessage/__tests__/__snapshots__/ResultIconRender.test.tsx.snap similarity index 86% rename from packages/sqle/src/components/AuditResultMessage/test/__snapshots__/ResultIconRender.test.tsx.snap rename to packages/sqle/src/components/AuditResultMessage/__tests__/__snapshots__/ResultIconRender.test.tsx.snap index 14adda219..a9b12305b 100644 --- a/packages/sqle/src/components/AuditResultMessage/test/__snapshots__/ResultIconRender.test.tsx.snap +++ b/packages/sqle/src/components/AuditResultMessage/__tests__/__snapshots__/ResultIconRender.test.tsx.snap @@ -213,3 +213,38 @@ exports[`sqle/components/AuditResultMessage/ResultIconRender render normal icon
`; + +exports[`sqle/components/AuditResultMessage/ResultIconRender should render exception info when levels include "audit_execution_error" 1`] = ` + +
+
+
+ + + +
+
+ + 审核存在异常 + +
+
+
+ +`; diff --git a/packages/sqle/src/components/AuditResultMessage/test/__snapshots__/index.ce.test.tsx.snap b/packages/sqle/src/components/AuditResultMessage/__tests__/__snapshots__/index.ce.test.tsx.snap similarity index 100% rename from packages/sqle/src/components/AuditResultMessage/test/__snapshots__/index.ce.test.tsx.snap rename to packages/sqle/src/components/AuditResultMessage/__tests__/__snapshots__/index.ce.test.tsx.snap diff --git a/packages/sqle/src/components/AuditResultMessage/test/__snapshots__/index.test.tsx.snap b/packages/sqle/src/components/AuditResultMessage/__tests__/__snapshots__/index.test.tsx.snap similarity index 100% rename from packages/sqle/src/components/AuditResultMessage/test/__snapshots__/index.test.tsx.snap rename to packages/sqle/src/components/AuditResultMessage/__tests__/__snapshots__/index.test.tsx.snap diff --git a/packages/sqle/src/components/AuditResultMessage/test/index.ce.test.tsx b/packages/sqle/src/components/AuditResultMessage/__tests__/index.ce.test.tsx similarity index 100% rename from packages/sqle/src/components/AuditResultMessage/test/index.ce.test.tsx rename to packages/sqle/src/components/AuditResultMessage/__tests__/index.ce.test.tsx diff --git a/packages/sqle/src/components/AuditResultMessage/test/index.test.tsx b/packages/sqle/src/components/AuditResultMessage/__tests__/index.test.tsx similarity index 100% rename from packages/sqle/src/components/AuditResultMessage/test/index.test.tsx rename to packages/sqle/src/components/AuditResultMessage/__tests__/index.test.tsx diff --git a/packages/sqle/src/components/AuditResultMessage/index.type.ts b/packages/sqle/src/components/AuditResultMessage/index.type.ts index ed2d5f6de..ffa7c3f5d 100644 --- a/packages/sqle/src/components/AuditResultMessage/index.type.ts +++ b/packages/sqle/src/components/AuditResultMessage/index.type.ts @@ -7,3 +7,8 @@ export type AuditResultMessageProps = { moreBtnLink?: string; isRuleDeleted?: boolean; }; + +export type ResultIconRenderProps = { + iconLevels?: string[]; + isAuditing?: boolean; +}; diff --git a/packages/sqle/src/components/ReportDrawer/__tests__/__snapshots__/index.test.tsx.snap b/packages/sqle/src/components/ReportDrawer/__tests__/__snapshots__/index.test.tsx.snap index 36498f6a3..4cfb19733 100644 --- a/packages/sqle/src/components/ReportDrawer/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/sqle/src/components/ReportDrawer/__tests__/__snapshots__/index.test.tsx.snap @@ -69,7 +69,7 @@ exports[`sqle/components/ReportDrawer render snap is empty 1`] = `
`; +exports[`sqle/components/ReportDrawer render snap when audit rule is exception 1`] = ` + +
+
+
+
+ + +`; + exports[`sqle/components/ReportDrawer render snap when has data 1`] = `
@@ -260,7 +528,7 @@ exports[`sqle/components/ReportDrawer render snap when has data 1`] = `
+ > + + + + @@ -450,7 +730,7 @@ exports[`sqle/components/ReportDrawer render snap when has delete rule 1`] = `
+ > + + + + @@ -522,7 +815,19 @@ exports[`sqle/components/ReportDrawer render snap when has delete rule 1`] = ` > + > + + + + @@ -732,7 +1037,7 @@ exports[`sqle/components/ReportDrawer render snap when has extra 1`] = `
+ > + + + + @@ -922,7 +1239,7 @@ exports[`sqle/components/ReportDrawer render snap when loading is true 1`] = `
+ > + + + + @@ -1176,7 +1505,7 @@ exports[`sqle/components/ReportDrawer render snap when showSourceFile is true 1`
+ > + + + + diff --git a/packages/sqle/src/components/ReportDrawer/__tests__/index.test.tsx b/packages/sqle/src/components/ReportDrawer/__tests__/index.test.tsx index 5c25010db..19f97b32c 100644 --- a/packages/sqle/src/components/ReportDrawer/__tests__/index.test.tsx +++ b/packages/sqle/src/components/ReportDrawer/__tests__/index.test.tsx @@ -53,7 +53,7 @@ describe('sqle/components/ReportDrawer', () => { { rule_name: 'rule a', message: 'message', - level: 'level', + level: 'error', annotation: 'annotation', db_type: 'mysql' } @@ -79,7 +79,7 @@ describe('sqle/components/ReportDrawer', () => { { rule_name: 'rule a', message: 'message', - level: 'level', + level: 'warn', annotation: 'annotation', db_type: 'mysql' } @@ -105,7 +105,7 @@ describe('sqle/components/ReportDrawer', () => { { rule_name: 'rule a', message: 'message', - level: 'level', + level: 'normal', annotation: 'annotation', db_type: 'mysql' } @@ -131,7 +131,7 @@ describe('sqle/components/ReportDrawer', () => { { rule_name: 'rule a', message: 'message1', - level: 'level', + level: 'warn', annotation: 'annotation', db_type: 'mysql', isRuleDeleted: true @@ -139,7 +139,7 @@ describe('sqle/components/ReportDrawer', () => { { rule_name: 'rule b', message: 'message2', - level: 'level', + level: 'error', annotation: 'annotation', db_type: 'mysql' } @@ -163,7 +163,32 @@ describe('sqle/components/ReportDrawer', () => { { rule_name: 'rule a', message: 'message', - level: 'level', + level: 'error', + annotation: 'annotation', + db_type: 'mysql' + } + ] + }, + onClose: jest.fn(), + extra:
extra
+ }); + expect(baseElement).toMatchSnapshot(); + }); + + it.only('render snap when audit rule is exception', () => { + const { baseElement } = customRender({ + open: true, + title: 'this is a title', + showAnnotation: true, + data: { + sql: 'select 1', + sqlSourceFile: 'file_source', + sqlStartLine: 3, + auditResult: [ + { + rule_name: 'rule a', + message: 'message', + level: 'audit_execution_error', annotation: 'annotation', db_type: 'mysql' } diff --git a/packages/sqle/src/components/ReportDrawer/index.tsx b/packages/sqle/src/components/ReportDrawer/index.tsx index a4c216b07..578520a48 100644 --- a/packages/sqle/src/components/ReportDrawer/index.tsx +++ b/packages/sqle/src/components/ReportDrawer/index.tsx @@ -6,15 +6,25 @@ import { EmptyBox, BasicToolTip, SQLRenderer, - BasicTypographyEllipsis + BasicTypographyEllipsis, + parse2ReactRouterPath } from '@actiontech/shared'; import { DetailReportDrawerProps, IAuditResultItem } from './index.type'; -import { AuditReportStyleWrapper } from './style'; +import { + AuditReportStyleWrapper, + AuditResultExceptionStyleWrapper +} from './style'; import AuditResultMessage from '../AuditResultMessage'; import { Typography, Space } from 'antd'; -import { ProfileSquareFilled, EnvironmentFilled } from '@actiontech/icons'; +import { + ProfileSquareFilled, + EnvironmentFilled, + WarningFilled +} from '@actiontech/icons'; import useThemeStyleData from '../../hooks/useThemeStyleData'; import { Spin } from 'antd'; +import { RuleResV1LevelEnum } from '@actiontech/shared/lib/api/sqle/service/common.enum'; +import { ROUTE_PATHS } from '@actiontech/shared/lib/data/routePaths'; const ReportDrawer = ({ open, @@ -34,6 +44,23 @@ const ReportDrawer = ({ onClose(); }; + const { auditResultWithNormalLevel, auditResultWithAuditException } = + useMemo(() => { + const normalLevel: IAuditResultItem[] = []; + const exceptionResult: IAuditResultItem[] = []; + (data?.auditResult ?? []).forEach((item) => { + if (Object.keys(RuleResV1LevelEnum).includes(item.level ?? '')) { + normalLevel.push(item); + } else if (item.level === 'audit_execution_error') { + exceptionResult.push(item); + } + }); + return { + auditResultWithAuditException: exceptionResult, + auditResultWithNormalLevel: normalLevel + }; + }, [data?.auditResult]); + const resultDataIsEmpty = useMemo(() => { return ( (Array.isArray(data?.auditResult) && !data?.auditResult.length) || @@ -63,7 +90,7 @@ const ReportDrawer = ({ {resultDataIsEmpty ? ( ) : ( - (data?.auditResult ?? [])?.map( + auditResultWithNormalLevel.map( (item: IAuditResultItem, index: number) => { if (!showAnnotation || item.isRuleDeleted) { return ( @@ -94,7 +121,15 @@ const ReportDrawer = ({ showAnnotation moreBtnLink={ item?.rule_name && item?.db_type - ? `/sqle/rule/knowledge/${item?.rule_name}/${item?.db_type}` + ? parse2ReactRouterPath( + ROUTE_PATHS.SQLE.RULE_KNOWLEDGE.index, + { + params: { + ruleName: item.rule_name ?? '', + dbType: item.db_type ?? '' + } + } + ) : '' } /> @@ -105,6 +140,29 @@ const ReportDrawer = ({
+ + 0}> + +
+ {t('auditPlan.report.drawer.subTitle.exception')} +
+ {auditResultWithAuditException.map((item, index) => { + return ( +
+ + +
+ ); + })} +
+
+
diff --git a/packages/sqle/src/components/ReportDrawer/style.ts b/packages/sqle/src/components/ReportDrawer/style.ts index d611bdc21..47d48e819 100644 --- a/packages/sqle/src/components/ReportDrawer/style.ts +++ b/packages/sqle/src/components/ReportDrawer/style.ts @@ -13,7 +13,7 @@ export const AuditReportStyleWrapper = styled('div')` font-weight: 600; line-height: 28px; padding: 24px 24px 16px; - color: #292c33; + color: ${({ theme }) => theme.sharedTheme.uiToken.colorText}; } .wrapper-cont { @@ -22,8 +22,10 @@ export const AuditReportStyleWrapper = styled('div')` overflow-y: auto; .result-item { - background: #f7f6f4; - border: 1px solid #f2f1f0; + background: ${({ theme }) => + theme.sharedTheme.uiToken.colorFillTertiary}; + border: 1px solid + ${({ theme }) => theme.sharedTheme.basic.colorGrayLine}; border-radius: 4px; padding: 8px 12px; margin-bottom: 4px; @@ -52,3 +54,39 @@ export const AuditReportStyleWrapper = styled('div')` } } `; + +export const AuditResultExceptionStyleWrapper = styled('div')` + padding: 0 24px; + + .title { + margin-bottom: 0; + font-size: 14px !important; + font-weight: 600; + line-height: 28px; + padding: 24px 0; + color: ${({ theme }) => theme.sharedTheme.uiToken.colorText}; + } + + .exception-item { + display: flex; + background: ${({ theme }) => theme.sharedTheme.uiToken.colorFillTertiary}; + border: 1px solid ${({ theme }) => theme.sharedTheme.basic.colorGrayLine}; + border-radius: 4px; + padding: 8px 12px; + margin-bottom: 4px; + + .exception-item-icon { + width: 20px; + height: 20px; + } + + .exception-item-text { + width: 95%; + margin-left: 12px; + } + + &:last-child { + margin-bottom: 0; + } + } +`; diff --git a/packages/sqle/src/locale/zh-CN/auditPlan.ts b/packages/sqle/src/locale/zh-CN/auditPlan.ts index 80ee496cf..7683af5e7 100644 --- a/packages/sqle/src/locale/zh-CN/auditPlan.ts +++ b/packages/sqle/src/locale/zh-CN/auditPlan.ts @@ -147,6 +147,7 @@ export default { action: '分析语句', subTitle: { result: '审核结果', + exception: '执行异常', sql: 'SQL语句' }, source: '所在文件', diff --git a/packages/sqle/src/locale/zh-CN/components.ts b/packages/sqle/src/locale/zh-CN/components.ts index aa5270843..7f593216d 100644 --- a/packages/sqle/src/locale/zh-CN/components.ts +++ b/packages/sqle/src/locale/zh-CN/components.ts @@ -2,6 +2,8 @@ export default { auditResultMessage: { auditPassed: '审核通过', - ruleDeleted: '该规则已删除' + ruleDeleted: '该规则已删除', + auditing: '审核中', + hasException: '审核存在异常' } }; diff --git a/packages/sqle/src/locale/zh-CN/execWorkflow.ts b/packages/sqle/src/locale/zh-CN/execWorkflow.ts index ad8dbb82b..f7ca11a3d 100644 --- a/packages/sqle/src/locale/zh-CN/execWorkflow.ts +++ b/packages/sqle/src/locale/zh-CN/execWorkflow.ts @@ -126,6 +126,7 @@ export default { clearDuplicate: '数据去重', allLevel: '全部等级', submit: '提交工单', + submitTooltipTitle: '当前存在审核规则未被校验,请排除问题后重新触发审核', updateInfo: '修改工单', continueSubmission: '仍要创建', submitWorkflowConfirmationMessage: diff --git a/packages/sqle/src/page/PluginAudit/Drawer/test/__snapshots__/AuditResultDrawer.test.tsx.snap b/packages/sqle/src/page/PluginAudit/Drawer/test/__snapshots__/AuditResultDrawer.test.tsx.snap index 2eacce642..424d19f4f 100644 --- a/packages/sqle/src/page/PluginAudit/Drawer/test/__snapshots__/AuditResultDrawer.test.tsx.snap +++ b/packages/sqle/src/page/PluginAudit/Drawer/test/__snapshots__/AuditResultDrawer.test.tsx.snap @@ -69,7 +69,7 @@ exports[`sqle/PluginAudit/AuditResultDrawer should match snap shot 1`] = `
-
-
-
-
-
- - - - - - -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 序号 - - 执行语句 - - 审核结果 - - 说明 - - 操作 -
-
-
-
-
-
-
-
-
-
- -`; - -exports[`sqle/ExecWorkflow/Common/AuditResultList/List render snap when get table data 2`] = `
{ return { @@ -77,6 +81,7 @@ describe('sqle/ExecWorkflow/Common/AuditResultList/List', () => { }); it('render snap when get table data', async () => { + const updateTaskRecordCountSpy = jest.fn(); const openSpy = jest.spyOn(window, 'open').mockImplementation(() => null); const { baseElement } = customRender({ noDuplicate: true, @@ -84,11 +89,6 @@ describe('sqle/ExecWorkflow/Common/AuditResultList/List', () => { projectID: 'projectID', auditLevelFilterValue: getAuditTaskSQLsV2FilterAuditLevelEnum.normal }); - - await act(async () => jest.advanceTimersByTime(500)); - expect(baseElement).toMatchSnapshot(); - await act(async () => jest.advanceTimersByTime(2600)); - expect(baseElement).toMatchSnapshot(); expect(requestGetAuditTaskSQLs).toHaveBeenCalled(); expect(requestGetAuditTaskSQLs).toHaveBeenCalledWith({ filter_audit_level: 'normal', @@ -97,6 +97,9 @@ describe('sqle/ExecWorkflow/Common/AuditResultList/List', () => { page_size: '20', task_id: 'taskID' }); + await act(async () => jest.advanceTimersByTime(3000)); + expect(baseElement).toMatchSnapshot(); + expect(updateTaskRecordCountSpy).toHaveBeenCalledTimes(0); const analyzeBtn = screen.getAllByText('分 析'); expect(analyzeBtn.length).toBe(1); @@ -389,6 +392,7 @@ describe('sqle/ExecWorkflow/Common/AuditResultList/List', () => { InstanceTipResV1SupportedBackupStrategyEnum.reverse_sql ] }); + await act(async () => jest.advanceTimersByTime(3000)); expect(requestGetAuditTaskSQLs).toHaveBeenCalledTimes(2); @@ -398,4 +402,61 @@ describe('sqle/ExecWorkflow/Common/AuditResultList/List', () => { }); expect(requestGetAuditTaskSQLs).toHaveBeenCalledTimes(3); }); + + it('should call onSuccess when request succeed', async () => { + const updateTaskRecordCountSpy = jest.fn(); + const updateTaskAuditRuleExceptionStatusSpy = jest.fn(); + customRender({ + noDuplicate: true, + taskID: 'taskID', + projectID: 'projectID', + auditLevelFilterValue: null, + allowSwitchBackupPolicy: true, + supportedBackupPolicies: [ + InstanceTipResV1SupportedBackupStrategyEnum.reverse_sql + ], + updateTaskRecordCount: updateTaskRecordCountSpy, + updateTaskAuditRuleExceptionStatus: updateTaskAuditRuleExceptionStatusSpy + }); + + await act(async () => jest.advanceTimersByTime(3000)); + expect(updateTaskRecordCountSpy).toHaveBeenCalledTimes(1); + expect(updateTaskRecordCountSpy).toHaveBeenNthCalledWith(1, 'taskID', 1); + expect(updateTaskAuditRuleExceptionStatusSpy).toHaveBeenCalledTimes(1); + expect(updateTaskAuditRuleExceptionStatusSpy).toHaveBeenNthCalledWith( + 1, + AuditTaskSQLsData + ); + }); + + it('should call onError when request fails', async () => { + requestGetAuditTaskSQLs.mockImplementation(() => + createSpyErrorResponse({}) + ); + + const updateTaskRecordCountSpy = jest.fn(); + const updateTaskAuditRuleExceptionStatusSpy = jest.fn(); + customRender({ + noDuplicate: true, + taskID: 'taskID', + projectID: 'projectID', + auditLevelFilterValue: null, + allowSwitchBackupPolicy: true, + supportedBackupPolicies: [ + InstanceTipResV1SupportedBackupStrategyEnum.reverse_sql + ], + updateTaskRecordCount: updateTaskRecordCountSpy, + updateTaskAuditRuleExceptionStatus: updateTaskAuditRuleExceptionStatusSpy + }); + + await act(async () => jest.advanceTimersByTime(3000)); + + expect(updateTaskRecordCountSpy).toHaveBeenCalledTimes(1); + expect(updateTaskRecordCountSpy).toHaveBeenNthCalledWith(1, 'taskID', 0); + expect(updateTaskAuditRuleExceptionStatusSpy).toHaveBeenCalledTimes(1); + expect(updateTaskAuditRuleExceptionStatusSpy).toHaveBeenNthCalledWith( + 1, + [] + ); + }); }); diff --git a/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/Table/index.tsx b/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/Table/index.tsx index 2800029e8..c5e19b324 100644 --- a/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/Table/index.tsx +++ b/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/Table/index.tsx @@ -32,7 +32,8 @@ const AuditResultTable: React.FC = ({ updateTaskRecordCount, dbType, allowSwitchBackupPolicy = false, - supportedBackupPolicies + supportedBackupPolicies, + updateTaskAuditRuleExceptionStatus }) => { const { sqlRewrittenOpen, @@ -100,9 +101,11 @@ const AuditResultTable: React.FC = ({ if (auditLevelFilterValue === null) { updateTaskRecordCount?.(taskID ?? '', res.total ?? 0); } + updateTaskAuditRuleExceptionStatus?.(res.list ?? []); }, onError() { updateTaskRecordCount?.(taskID ?? '', 0); + updateTaskAuditRuleExceptionStatus?.([]); } } ); diff --git a/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/Table/index.type.ts b/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/Table/index.type.ts index 0916f75f7..6eb2289ae 100644 --- a/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/Table/index.type.ts +++ b/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/Table/index.type.ts @@ -1,17 +1,22 @@ import { IAuditTaskSQLResV2 } from '@actiontech/shared/lib/api/sqle/service/common'; import { getAuditTaskSQLsV2FilterAuditLevelEnum } from '@actiontech/shared/lib/api/sqle/service/task/index.enum'; import { InstanceTipResV1SupportedBackupStrategyEnum } from '@actiontech/shared/lib/api/sqle/service/common.enum'; +import { AuditResultListProps } from '../index.type'; -export type AuditResultTableProps = { +export interface AuditResultTableProps + extends Pick< + AuditResultListProps, + | 'updateTaskRecordCount' + | 'allowSwitchBackupPolicy' + | 'updateTaskAuditRuleExceptionStatus' + > { noDuplicate: boolean; taskID?: string; auditLevelFilterValue: getAuditTaskSQLsV2FilterAuditLevelEnum | null; projectID: string; - updateTaskRecordCount?: (taskId: string, sqlNumber: number) => void; dbType?: string; - allowSwitchBackupPolicy?: boolean; supportedBackupPolicies?: InstanceTipResV1SupportedBackupStrategyEnum[]; -}; +} export type AuditResultDrawerProps = { open: boolean; diff --git a/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/index.tsx b/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/index.tsx index bd3c409d0..d6f05d562 100644 --- a/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/index.tsx +++ b/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/index.tsx @@ -25,7 +25,8 @@ const AuditResultList: React.FC = ({ showTaskTab = true, allowSwitchBackupPolicy = false, onBatchSwitchBackupPolicy, - tasksSupportedBackupPolicies + tasksSupportedBackupPolicies, + updateTaskAuditRuleExceptionStatus }) => { const { t } = useTranslation(); const { projectID } = useCurrentProject(); @@ -147,6 +148,7 @@ const AuditResultList: React.FC = ({ dbType={currentTask?.instance_db_type} allowSwitchBackupPolicy={allowSwitchBackupPolicy} supportedBackupPolicies={currentTaskSupportedBackupPolicies} + updateTaskAuditRuleExceptionStatus={updateTaskAuditRuleExceptionStatus} /> ); diff --git a/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/index.type.ts b/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/index.type.ts index 062dd9c03..5ca099ddc 100644 --- a/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/index.type.ts +++ b/packages/sqle/src/page/SqlExecWorkflow/Common/AuditResultList/index.type.ts @@ -1,9 +1,13 @@ -import { IAuditTaskResV1 } from '@actiontech/shared/lib/api/sqle/service/common'; +import { + IAuditTaskResV1, + IAuditTaskSQLResV2 +} from '@actiontech/shared/lib/api/sqle/service/common'; import { InstanceTipResV1SupportedBackupStrategyEnum } from '@actiontech/shared/lib/api/sqle/service/common.enum'; export type AuditResultListProps = { tasks: IAuditTaskResV1[]; - updateTaskRecordCount?: (taskId: string, sqlNumber: number) => void; + updateTaskRecordCount?: (taskId: string, sqlCount: number) => void; + updateTaskAuditRuleExceptionStatus?: (taskSqls: IAuditTaskSQLResV2[]) => void; showTaskTab?: boolean; allowSwitchBackupPolicy?: boolean; onBatchSwitchBackupPolicy?: ( diff --git a/packages/sqle/src/page/SqlExecWorkflow/Common/SubmitWorkflowButton/__tests__/index.test.tsx b/packages/sqle/src/page/SqlExecWorkflow/Common/SubmitWorkflowButton/__tests__/index.test.tsx index ed20bdf08..32b8c6c7f 100644 --- a/packages/sqle/src/page/SqlExecWorkflow/Common/SubmitWorkflowButton/__tests__/index.test.tsx +++ b/packages/sqle/src/page/SqlExecWorkflow/Common/SubmitWorkflowButton/__tests__/index.test.tsx @@ -11,6 +11,7 @@ describe('test SubmitWorkflowButton', () => { isConfirmationRequiredForSubmission={false} loading={false} onClick={onClick} + hasExceptionAuditRule={false} /> ); expect(container).toMatchSnapshot(); @@ -28,6 +29,7 @@ describe('test SubmitWorkflowButton', () => { submitWorkflowConfirmationMessage="Confirmation Message" loading={false} onClick={onClick} + hasExceptionAuditRule={false} /> ); fireEvent.click(screen.getByText('提交工单')); @@ -48,9 +50,32 @@ describe('test SubmitWorkflowButton', () => { isConfirmationRequiredForSubmission={false} loading={true} onClick={onClick} + hasExceptionAuditRule={false} /> ); fireEvent.click(screen.getByText('提交工单')); expect(onClick).not.toHaveBeenCalled(); }); + + it('displays the tooltip message when hasExceptionAuditRule is equal true', async () => { + const onClick = jest.fn(); + + superRender( + + ); + fireEvent.click(screen.getByText('提交工单')); + expect(onClick).not.toHaveBeenCalled(); + + fireEvent.mouseOver(screen.getByText('提交工单')); + + await screen.findByText( + '当前存在审核规则未被校验,请排除问题后重新触发审核' + ); + }); }); diff --git a/packages/sqle/src/page/SqlExecWorkflow/Common/SubmitWorkflowButton/index.tsx b/packages/sqle/src/page/SqlExecWorkflow/Common/SubmitWorkflowButton/index.tsx index da7960d6e..66c066123 100644 --- a/packages/sqle/src/page/SqlExecWorkflow/Common/SubmitWorkflowButton/index.tsx +++ b/packages/sqle/src/page/SqlExecWorkflow/Common/SubmitWorkflowButton/index.tsx @@ -1,39 +1,54 @@ -import { BasicButton } from '@actiontech/shared'; +import { ActionButton, BasicButton } from '@actiontech/shared'; import { SubmitWorkflowButtonProps } from './index.type'; import { useTranslation } from 'react-i18next'; import { InfoHexagonOutlined } from '@actiontech/icons'; -import { Popconfirm } from 'antd'; const SubmitWorkflowButton: React.FC = ({ isConfirmationRequiredForSubmission, loading, submitWorkflowConfirmationMessage, - onClick + onClick, + hasExceptionAuditRule }) => { const { t } = useTranslation(); - if (!isConfirmationRequiredForSubmission) { + + if (hasExceptionAuditRule) { return ( - - {t('execWorkflow.create.auditResult.submit')} - + } + tooltip={{ + title: t('execWorkflow.create.auditResult.submitTooltipTitle') + }} + text={t('execWorkflow.create.auditResult.submit')} + /> ); } - return ( - - } - > - {t('execWorkflow.create.auditResult.submit')} - - + text={t('execWorkflow.create.auditResult.submit')} + actionType="confirm" + confirm={{ + title: submitWorkflowConfirmationMessage, + okText: t('execWorkflow.create.auditResult.continueSubmission'), + onConfirm: onClick, + okButtonProps: { loading } + }} + /> + ); + } + + return ( + + {t('execWorkflow.create.auditResult.submit')} + ); }; diff --git a/packages/sqle/src/page/SqlExecWorkflow/Common/SubmitWorkflowButton/index.type.ts b/packages/sqle/src/page/SqlExecWorkflow/Common/SubmitWorkflowButton/index.type.ts index c94053312..61f7e94bb 100644 --- a/packages/sqle/src/page/SqlExecWorkflow/Common/SubmitWorkflowButton/index.type.ts +++ b/packages/sqle/src/page/SqlExecWorkflow/Common/SubmitWorkflowButton/index.type.ts @@ -3,4 +3,5 @@ export type SubmitWorkflowButtonProps = { loading: boolean; submitWorkflowConfirmationMessage?: string; onClick: () => void; + hasExceptionAuditRule: boolean; }; diff --git a/packages/sqle/src/page/SqlExecWorkflow/Create/components/AuditResultStep/__tests__/index.ce.test.tsx b/packages/sqle/src/page/SqlExecWorkflow/Create/components/AuditResultStep/__tests__/index.ce.test.tsx index 3f19e880f..96d801549 100644 --- a/packages/sqle/src/page/SqlExecWorkflow/Create/components/AuditResultStep/__tests__/index.ce.test.tsx +++ b/packages/sqle/src/page/SqlExecWorkflow/Create/components/AuditResultStep/__tests__/index.ce.test.tsx @@ -37,6 +37,7 @@ describe('test AuditResultStep ce', () => { submitWorkflowConfirmationMessage={''} createAction={createAction} auditAction={jest.fn()} + hasExceptionAuditRule={false} {...MockSharedStepDetail} /> ); diff --git a/packages/sqle/src/page/SqlExecWorkflow/Create/components/AuditResultStep/__tests__/index.test.tsx b/packages/sqle/src/page/SqlExecWorkflow/Create/components/AuditResultStep/__tests__/index.test.tsx index b42ee4b93..dbd113ded 100644 --- a/packages/sqle/src/page/SqlExecWorkflow/Create/components/AuditResultStep/__tests__/index.test.tsx +++ b/packages/sqle/src/page/SqlExecWorkflow/Create/components/AuditResultStep/__tests__/index.test.tsx @@ -41,6 +41,7 @@ describe('test AuditResultStep', () => { submitWorkflowConfirmationMessage={''} createAction={createAction} auditAction={jest.fn()} + hasExceptionAuditRule={false} {...MockSharedStepDetail} /> ); diff --git a/packages/sqle/src/page/SqlExecWorkflow/Create/components/AuditResultStep/index.tsx b/packages/sqle/src/page/SqlExecWorkflow/Create/components/AuditResultStep/index.tsx index 5e2d51c8d..2f0cb5ec6 100644 --- a/packages/sqle/src/page/SqlExecWorkflow/Create/components/AuditResultStep/index.tsx +++ b/packages/sqle/src/page/SqlExecWorkflow/Create/components/AuditResultStep/index.tsx @@ -16,13 +16,15 @@ import { InstanceTipResV1SupportedBackupStrategyEnum } from '@actiontech/shared/ const AuditResultStep: React.FC = ({ tasks, - updateTaskRecordCount, baseFormValues, sqlAuditInfoFormValues, isConfirmationRequiredForSubmission, submitWorkflowConfirmationMessage, + hasExceptionAuditRule, createAction, auditAction, + updateTaskRecordCount, + updateTaskAuditRuleExceptionStatus, ...sharedStepDetail }) => { const { t } = useTranslation(); @@ -104,6 +106,7 @@ const AuditResultStep: React.FC = ({ {t('execWorkflow.create.auditResult.updateInfo')} = ({ allowSwitchBackupPolicy onBatchSwitchBackupPolicy={onBatchSwitchBackupPolicy} tasksSupportedBackupPolicies={tasksSupportedBackupPolicies} + updateTaskAuditRuleExceptionStatus={updateTaskAuditRuleExceptionStatus} /> void; +export interface AuditResultStepProps + extends Pick< + SubmitWorkflowButtonProps, + | 'isConfirmationRequiredForSubmission' + | 'submitWorkflowConfirmationMessage' + | 'hasExceptionAuditRule' + >, + SharedStepDetails, + Pick< + AuditResultListProps, + 'updateTaskAuditRuleExceptionStatus' | 'updateTaskRecordCount' | 'tasks' + > { baseFormValues?: WorkflowBaseInfoFormFields; sqlAuditInfoFormValues?: SqlAuditInfoFormFields; - isConfirmationRequiredForSubmission: boolean; - submitWorkflowConfirmationMessage: string; createAction: () => Promise; auditAction: ( value: SqlAuditInfoFormFields, baseInfo?: WorkflowBaseInfoFormFields ) => Promise; -} & SharedStepDetails; +} diff --git a/packages/sqle/src/page/SqlExecWorkflow/Create/hooks/__tests__/useCheckTaskAuditRuleExceptionStatus.test.ts b/packages/sqle/src/page/SqlExecWorkflow/Create/hooks/__tests__/useCheckTaskAuditRuleExceptionStatus.test.ts new file mode 100644 index 000000000..d4714a6a3 --- /dev/null +++ b/packages/sqle/src/page/SqlExecWorkflow/Create/hooks/__tests__/useCheckTaskAuditRuleExceptionStatus.test.ts @@ -0,0 +1,57 @@ +import { renderHook, act } from '@testing-library/react-hooks'; +import { IAuditTaskSQLResV2 } from '@actiontech/shared/lib/api/sqle/service/common'; +import { RuleResV1LevelEnum } from '@actiontech/shared/lib/api/sqle/service/common.enum'; +import useCheckTaskAuditRuleExceptionStatus from '../useCheckTaskAuditRuleExceptionStatus'; +import { + AuditTaskSQLsMockData, + AuditTaskSQLsMockDataWithExceptionRule +} from '../../../../../testUtils/mockApi/task/data'; + +describe('useCheckTaskAuditRuleExceptionStatus', () => { + it('should initialize with no exception audit rules', () => { + const { result } = renderHook(() => useCheckTaskAuditRuleExceptionStatus()); + expect(result.current.hasExceptionAuditRule).toBe(false); + }); + + it('should detect exception audit rules when provided', () => { + const { result } = renderHook(() => useCheckTaskAuditRuleExceptionStatus()); + + act(() => { + result.current.updateTaskAuditRuleExceptionStatus( + AuditTaskSQLsMockDataWithExceptionRule + ); + }); + + expect(result.current.hasExceptionAuditRule).toBe(true); + }); + + it('should not detect exception audit rules when all levels are valid', () => { + const { result } = renderHook(() => useCheckTaskAuditRuleExceptionStatus()); + + act(() => { + result.current.updateTaskAuditRuleExceptionStatus(AuditTaskSQLsMockData); + }); + + expect(result.current.hasExceptionAuditRule).toBe(false); + }); + + it('should handle empty audit results gracefully', () => { + const { result } = renderHook(() => useCheckTaskAuditRuleExceptionStatus()); + + const mockTaskSqlsEmptyResults: IAuditTaskSQLResV2[] = [ + { + number: 3, + exec_sql: 'DELETE FROM table WHERE condition;', + audit_result: [] + } + ]; + + act(() => { + result.current.updateTaskAuditRuleExceptionStatus( + mockTaskSqlsEmptyResults + ); + }); + + expect(result.current.hasExceptionAuditRule).toBe(false); + }); +}); diff --git a/packages/sqle/src/page/SqlExecWorkflow/Create/hooks/useCheckTaskAuditRuleExceptionStatus.ts b/packages/sqle/src/page/SqlExecWorkflow/Create/hooks/useCheckTaskAuditRuleExceptionStatus.ts new file mode 100644 index 000000000..cebcfdc65 --- /dev/null +++ b/packages/sqle/src/page/SqlExecWorkflow/Create/hooks/useCheckTaskAuditRuleExceptionStatus.ts @@ -0,0 +1,26 @@ +import { IAuditTaskSQLResV2 } from '@actiontech/shared/lib/api/sqle/service/common'; +import { RuleResV1LevelEnum } from '@actiontech/shared/lib/api/sqle/service/common.enum'; +import { useState } from 'react'; + +const useCheckTaskAuditRuleExceptionStatus = () => { + const [hasExceptionAuditRule, setHasExceptionAuditRule] = useState(false); + + const updateTaskAuditRuleExceptionStatus = ( + taskSqls: IAuditTaskSQLResV2[] + ) => { + const existsExceptionRule = taskSqls.some((item) => + item.audit_result?.some( + (result) => + !Object.keys(RuleResV1LevelEnum).includes(result.level ?? '') + ) + ); + setHasExceptionAuditRule(existsExceptionRule); + }; + + return { + hasExceptionAuditRule, + updateTaskAuditRuleExceptionStatus + }; +}; + +export default useCheckTaskAuditRuleExceptionStatus; diff --git a/packages/sqle/src/page/SqlExecWorkflow/Create/index.tsx b/packages/sqle/src/page/SqlExecWorkflow/Create/index.tsx index dfb0aae7a..d2aa28ed5 100644 --- a/packages/sqle/src/page/SqlExecWorkflow/Create/index.tsx +++ b/packages/sqle/src/page/SqlExecWorkflow/Create/index.tsx @@ -25,6 +25,7 @@ import { LazyLoadComponent } from '@actiontech/shared'; import { useSelector } from 'react-redux'; import { IReduxState } from '../../../store'; import useCreationMode from './hooks/useCreationMode'; +import useCheckTaskAuditRuleExceptionStatus from './hooks/useCheckTaskAuditRuleExceptionStatus'; const CreateSqlExecWorkflow: React.FC = () => { const { t } = useTranslation(); @@ -35,6 +36,8 @@ const CreateSqlExecWorkflow: React.FC = () => { const createdWorkflowID = useRef(''); const { updateTaskRecordCount, checkTaskCountIsEmpty } = useCheckTaskAuditSqlCount(); + const { hasExceptionAuditRule, updateTaskAuditRuleExceptionStatus } = + useCheckTaskAuditRuleExceptionStatus(); const { isCloneMode, @@ -228,6 +231,10 @@ const CreateSqlExecWorkflow: React.FC = () => {
= ({ currentTasks, @@ -56,6 +57,9 @@ const ModifySqlStatement: React.FC = ({ const { projectName } = useCurrentProject(); const { updateInstanceList, instanceList } = useInstance(); + const { hasExceptionAuditRule, updateTaskAuditRuleExceptionStatus } = + useCheckTaskAuditRuleExceptionStatus(); + const [form] = Form.useForm<{ [key in string]: SqlStatementFields }>(); const { updateTaskRecordCount, checkTaskCountIsEmpty } = @@ -267,6 +271,7 @@ const ModifySqlStatement: React.FC = ({ extra={ = ({ diff --git a/packages/sqle/src/page/SqlExecWorkflow/Detail/components/ModifySqlStatement/index.type.ts b/packages/sqle/src/page/SqlExecWorkflow/Detail/components/ModifySqlStatement/index.type.ts index d5d2cddc8..8b9c35d45 100644 --- a/packages/sqle/src/page/SqlExecWorkflow/Detail/components/ModifySqlStatement/index.type.ts +++ b/packages/sqle/src/page/SqlExecWorkflow/Detail/components/ModifySqlStatement/index.type.ts @@ -5,8 +5,13 @@ import { CreateWorkflowDatabaseInfo } from '../../../Create/index.type'; import React from 'react'; +import { SubmitWorkflowButtonProps } from '../../../Common/SubmitWorkflowButton/index.type'; -export type ModifySqlStatementProps = { +export interface ModifySqlStatementProps + extends Pick< + SubmitWorkflowButtonProps, + 'submitWorkflowConfirmationMessage' | 'isConfirmationRequiredForSubmission' + > { backToDetail: () => void; backToDetailText?: React.ReactNode; isAtRejectStep: boolean; @@ -17,11 +22,9 @@ export type ModifySqlStatementProps = { currentTasks?: IAuditTaskResV1[]; isSameSqlForAll: boolean; modifiedTasks?: IAuditTaskResV1[]; - isConfirmationRequiredForSubmission: boolean; - submitWorkflowConfirmationMessage: string; workflowId: string; refreshWorkflow?: () => void; refreshOverviewAction?: () => void; executeMode?: WorkflowResV2ExecModeEnum; auditExecPanelTabChangeEvent?: (key: string) => void; -}; +} diff --git a/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/Modal/StatusDrawer/__snapshots__/index.test.tsx.snap b/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/Modal/StatusDrawer/__snapshots__/index.test.tsx.snap index dfe472284..297928d3d 100644 --- a/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/Modal/StatusDrawer/__snapshots__/index.test.tsx.snap +++ b/packages/sqle/src/page/SqlManagement/component/SQLEEIndex/Modal/StatusDrawer/__snapshots__/index.test.tsx.snap @@ -86,7 +86,7 @@ exports[`page/SqlManagement/StatusDrawer close modal by click close button 1`] =