From e47ee134d00caaf7d44091e1eba920c973857c67 Mon Sep 17 00:00:00 2001 From: Jesse Rodriguez <54310591+1pone@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:51:29 +0800 Subject: [PATCH] Feat node transform (#648) * fix: node transform * fix: node transform * feat: getTransformMethod * feat: system advanced setting comparePluginUrl * feat: getTransformMethod * feat: system advanced setting comparePluginUrl * fix: get root json value (#646) Co-authored-by: onePone * Fix bug 0321 (#647) * fix: report Select suffixIcon is not propagable * feat: persist collection * feat: persist collection * fix: replay case filter * fix: replay case filter * fix: collection initial * fix: await create case inherit from interface * feat: collection search * feat: dynamic class tooltip massage * feat: user avatar * fix: get root json value (#646) Co-authored-by: onePone --------- Co-authored-by: onePone --------- Co-authored-by: onePone --- packages/arex/src/i18n/locales/cn/common.json | 1 + .../arex/src/i18n/locales/cn/components.json | 14 +- packages/arex/src/i18n/locales/en/common.json | 1 + .../arex/src/i18n/locales/en/components.json | 14 +- .../CompareConfig/NodesIgnore/IgnoreTree.tsx | 10 +- .../CompareConfig/NodesIgnore/index.tsx | 2 + .../NodesTransform/TransformCard.tsx | 208 ++++++++++++++ .../CompareConfig/NodesTransform/index.tsx | 259 ++++++++++++++++++ .../panes/AppSetting/CompareConfig/index.tsx | 22 ++ .../panes/SystemSetting/Advanced/index.tsx | 18 ++ .../ComparisonService/deleteTransformNode.ts | 8 + .../ComparisonService/getTransformMethod.ts | 8 + .../src/services/ComparisonService/index.ts | 5 + .../ComparisonService/insertTransformNode.ts | 16 ++ .../ComparisonService/queryTransformNode.ts | 35 +++ .../ComparisonService/updateTransformNode.ts | 16 ++ .../services/SystemService/getSystemConfig.ts | 4 + 17 files changed, 635 insertions(+), 6 deletions(-) create mode 100644 packages/arex/src/panes/AppSetting/CompareConfig/NodesTransform/TransformCard.tsx create mode 100644 packages/arex/src/panes/AppSetting/CompareConfig/NodesTransform/index.tsx create mode 100644 packages/arex/src/services/ComparisonService/deleteTransformNode.ts create mode 100644 packages/arex/src/services/ComparisonService/getTransformMethod.ts create mode 100644 packages/arex/src/services/ComparisonService/insertTransformNode.ts create mode 100644 packages/arex/src/services/ComparisonService/queryTransformNode.ts create mode 100644 packages/arex/src/services/ComparisonService/updateTransformNode.ts diff --git a/packages/arex/src/i18n/locales/cn/common.json b/packages/arex/src/i18n/locales/cn/common.json index ac2d6d27f..b0da6a07d 100644 --- a/packages/arex/src/i18n/locales/cn/common.json +++ b/packages/arex/src/i18n/locales/cn/common.json @@ -14,6 +14,7 @@ "replay": "回放", "title": "标题", "untitled": "未命名", + "select": "选择", "close": "关闭", "cancel": "取消", "clearAll": "全部清除", diff --git a/packages/arex/src/i18n/locales/cn/components.json b/packages/arex/src/i18n/locales/cn/components.json index 3cbad8a68..997d0ce3f 100644 --- a/packages/arex/src/i18n/locales/cn/components.json +++ b/packages/arex/src/i18n/locales/cn/components.json @@ -266,6 +266,7 @@ "nodesSort": "数组节点", "nodesDesensitization": "加密节点", "categoryIgnore": "类型忽略", + "nodesTransform": "转换节点", "global": "全局", "interface": "接口", "dependency": "依赖", @@ -360,7 +361,16 @@ "chooseCategoryType": "请选择忽略类型(可多选)", "categoryTypePlaceholder": "请选择依赖类型", "operationNamePlaceholder" : "请输入依赖名称", - "environment": "环境" + "environment": "环境", + "originalNode":"原始节点", + "transformedNode":"转换节点", + "addTransformNode":"添加转换节点", + "transformNodePath": "节点路径", + "transformMethodName":"方法名称", + "transformMethodArgs":"方法参数", + "selectNodePath": "选择节点路径", + "clickToSelectNodePath": "点击选择转换节点路径", + "deleteTransformConfirm": "确定删除该转换节点?" }, "env": { "searchEnvironment": "搜索环境", @@ -448,6 +458,8 @@ "jarFileUrlPlaceholder": "请输入 Jar 包地址", "replayCallback": "回放回调", "replayCallbackPlaceholder": "请输入回放回调地址", + "comparePlugin": "对比插件", + "comparePluginPlaceholder": "请输入对比插件 Jar 包地址", "systemLogs": "系统日志", "openSystemLogs": "打开系统日志", "application": "应用", diff --git a/packages/arex/src/i18n/locales/en/common.json b/packages/arex/src/i18n/locales/en/common.json index d5793891e..a27e6b86f 100644 --- a/packages/arex/src/i18n/locales/en/common.json +++ b/packages/arex/src/i18n/locales/en/common.json @@ -15,6 +15,7 @@ "title": "Title", "untitled": "Untitled", "help": "Help", + "select": "Select", "close": "Close", "cancel": "Cancel", "clearAll": "Clear All", diff --git a/packages/arex/src/i18n/locales/en/components.json b/packages/arex/src/i18n/locales/en/components.json index ead6a323d..dae9e1b95 100644 --- a/packages/arex/src/i18n/locales/en/components.json +++ b/packages/arex/src/i18n/locales/en/components.json @@ -267,6 +267,7 @@ "nodesSort": "Nodes Sort", "nodesDesensitization": "Nodes Desensitization", "categoryIgnore": "Category Ignore", + "nodesTransform": "Nodes Transform", "global": "Global", "interface": "Interfaces", "dependency": "Dependency", @@ -362,7 +363,16 @@ "chooseCategoryType": "Please select the type of ignore (multiple)", "categoryTypePlaceholder": "Please select the type of category", "operationNamePlaceholder" : "Please select the name of operation", - "environment": "Environment" + "environment": "Environment", + "originalNode":"Original Node", + "transformedNode":"Transformed Node", + "addTransformNode":"Add Transform Node", + "transformNodePath": "Node Path", + "transformMethodName":"Method name", + "transformMethodArgs":"Method args", + "selectNodePath": "Select Node Path", + "clickToSelectNodePath": "Click to select transformed node path", + "deleteTransformConfirm": "Are you sure to delete this transform node?" }, "env": { "searchEnvironment": "Search Environment", @@ -450,6 +460,8 @@ "jarFileUrlPlaceholder": "Please enter Jar file Url", "replayCallback": "Replay Callback", "replayCallbackPlaceholder": "Please enter replay callback Url", + "comparePlugin": "Compare Plugin", + "comparePluginPlaceholder": "Please enter compare plugin Jar file Url", "systemLogs": "System Logs", "openSystemLogs": "Open System Logs", "application": "Application", diff --git a/packages/arex/src/panes/AppSetting/CompareConfig/NodesIgnore/IgnoreTree.tsx b/packages/arex/src/panes/AppSetting/CompareConfig/NodesIgnore/IgnoreTree.tsx index e049b0300..cc39232fd 100644 --- a/packages/arex/src/panes/AppSetting/CompareConfig/NodesIgnore/IgnoreTree.tsx +++ b/packages/arex/src/panes/AppSetting/CompareConfig/NodesIgnore/IgnoreTree.tsx @@ -6,13 +6,15 @@ import React, { FC, useMemo } from 'react'; import { getIgnoreNodes } from './utils'; type IgnoreTreeProps = Omit & { + title?: React.ReactNode; loading?: boolean; treeData: object; + lineThrough?: boolean; }; -const IgnoreTreeWrapper = styled.div` +const IgnoreTreeWrapper = styled.div<{ lineThrough?: boolean }>` .ant-tree-node-selected { - text-decoration: line-through; + text-decoration: ${(props) => (props.lineThrough ? 'line-through' : 'none')}; } `; @@ -21,8 +23,8 @@ const IgnoreTree: FC = (props) => { const treeData = useMemo(() => getIgnoreNodes(props.treeData, ''), [props.treeData]); return ( - - + + = (props) => { > ; + options?: SelectProps['options']; + onNodePathChange?: (path: string[]) => void; + onPathLocationClick?: () => void; + onMethodNameChange?: (value: string, methodIndex: number) => void; + onMethodArgsChange?: (value: string, methodIndex: number) => void; + onInsertBefore?: (methodIndex: number) => void; + onInsertAfter?: (methodIndex: number) => void; + onDrop?: (methodIndex: number) => void; + onAdd?: () => void; + onSave?: () => void; + onEdit?: () => void; + onCancel?: () => void; + onDelete?: (id: string) => void; +} + +const TransformCard: FC = (props) => { + const { t } = useTranslation('components'); + const { token } = theme.useToken(); + + const nodePathRef = useRef(null); + useEffect(() => { + props.edit && nodePathRef.current?.focus(); + }, [props.edit]); + + const items: { title: ReactNode; description?: ReactNode }[] = [ + { + title: t('appSetting.originalNode'), + description: ( + + props.onNodePathChange?.(e.target.value.split('/'))} + placeholder={t('appSetting.transformNodePath') as string} + style={{ + display: 'inline-block', + textAlign: 'center', + height: '18px', + width: 'auto', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + color: token.colorTextDescription, + }} + /> + {props.edit && ( + } + onClick={props.onPathLocationClick} + style={{ color: token.colorTextDescription, marginLeft: '4px' }} + /> + )} + + ), + }, + ]; + + items.push( + ...props.data.transformDetail!.transformMethods.map((method, methodIndex) => ({ + title: props.edit ? ( + + ), + description: ( + props.onMethodArgsChange?.(e.target.value, methodIndex)} + style={{ + display: 'inline-block', + textAlign: 'center', + color: token.colorTextDescription, + width: '112px', + height: '18px', + }} + /> + ), + })), + ); + + items.push({ title: t('appSetting.transformedNode') }); + + return ( + + ( + <> + {props.edit && !!methodIndex && methodIndex < (items?.length || 0) - 1 && ( + + } + onClick={() => props.onInsertBefore?.(methodIndex)} + /> + } + onClick={() => props.onDrop?.(methodIndex)} + /> + } + onClick={() => props.onInsertAfter?.(methodIndex)} + /> + + )} + +
{dot}
+ + )} + style={{ marginTop: '20px' }} + /> + + + {props.edit ? ( + <> + + + + + + ) : ( + + )} + + + {props.edit && ( + props.onDelete?.(props.data.id!)} + > + + + )} +
+ ); +}; + +export default TransformCard; diff --git a/packages/arex/src/panes/AppSetting/CompareConfig/NodesTransform/index.tsx b/packages/arex/src/panes/AppSetting/CompareConfig/NodesTransform/index.tsx new file mode 100644 index 000000000..44a23fe7b --- /dev/null +++ b/packages/arex/src/panes/AppSetting/CompareConfig/NodesTransform/index.tsx @@ -0,0 +1,259 @@ +import { SyncOutlined } from '@ant-design/icons'; +import { PaneDrawer, SpaceBetweenWrapper, useTranslation } from '@arextest/arex-core'; +import { useAutoAnimate } from '@formkit/auto-animate/react'; +import { useRequest } from 'ahooks'; +import { App, Button, Space, Typography } from 'antd'; +import React, { FC, useState } from 'react'; +import { useImmer } from 'use-immer'; + +import { EditAreaPlaceholder, Icon } from '@/components'; +import IgnoreTree from '@/panes/AppSetting/CompareConfig/NodesIgnore/IgnoreTree'; +import TransformCard from '@/panes/AppSetting/CompareConfig/NodesTransform/TransformCard'; +import { ComparisonService } from '@/services'; +import { DependencyParams, TransformDetail, TransformNode } from '@/services/ComparisonService'; + +import { CONFIG_TARGET } from '../index'; + +export type NodesTransformProps = { + appId?: string; + operationId?: string; + dependency?: DependencyParams; + configTarget: CONFIG_TARGET; + contractParsed?: { [key: string]: any }; + syncing?: boolean; + onSync?: () => void; + loadingContract?: boolean; +}; + +const TemporaryId = '_temporary_id_'; + +const NodesTransform: FC = (props) => { + const { message } = App.useApp(); + const { t } = useTranslation(['components', 'common']); + + const [wrapperRef] = useAutoAnimate(); + + const [transformData, setTransformData] = useImmer[]>([]); + const [edit, setEdit] = useState(); + + const [openIndex, setOpenIndex] = useState(-1); + const [nodePath, setNodePath] = useState(); + + const { data: transformOptions = [] } = useRequest(ComparisonService.getTransformMethod, { + ready: !!props.appId, + defaultParams: [props.appId!], + }); + + const { data = [], run: queryTransformNode } = useRequest( + () => + ComparisonService.queryTransformNode({ + appId: props.appId!, + operationId: props.operationId, + ...(props.dependency || {}), + }), + { + ready: !!props.appId, + refreshDeps: [props.appId, props.operationId, props.dependency], + onSuccess: (data) => { + setTransformData(data); + }, + }, + ); + + const { run: insertTransformNode } = useRequest( + (transformDetail: TransformDetail) => + ComparisonService.insertTransformNode({ + appId: props.appId!, + operationId: props.operationId!, + ...(props.dependency || {}), + transformDetail, + }), + { + manual: true, + ready: !!props.appId, + onSuccess: (success) => { + if (success) { + message.success(t('message.createSuccess', { ns: 'common' })); + setEdit(undefined); + } else message.error(t('message.createFailed', { ns: 'common' })); + }, + }, + ); + + const { run: updateTransformNode } = useRequest(ComparisonService.updateTransformNode, { + manual: true, + onSuccess: (success) => { + if (success) { + message.success(t('message.updateSuccess', { ns: 'common' })); + setEdit(undefined); + } else message.error(t('message.updateFailed', { ns: 'common' })); + }, + }); + + const { run: deleteTransformNode } = useRequest(ComparisonService.deleteTransformNode, { + manual: true, + onSuccess: (success) => { + if (success) { + message.success(t('message.delSuccess', { ns: 'common' })); + setEdit(undefined); + setNodePath(undefined); + queryTransformNode(); + } else message.error(t('message.delFailed', { ns: 'common' })); + }, + }); + + return ( + <> +
+ {transformData.map((item, dataIndex) => { + return ( + ({ label: method, value: method }))} + onNodePathChange={(path) => + setTransformData((draft) => { + draft[dataIndex].transformDetail!.nodePath = path; + }) + } + onPathLocationClick={() => setOpenIndex(dataIndex)} + onMethodNameChange={(value, methodIndex) => + setTransformData((draft) => { + draft[dataIndex].transformDetail!.transformMethods[methodIndex].methodName = + value; + }) + } + onMethodArgsChange={(value, methodIndex) => + setTransformData((draft) => { + draft[dataIndex].transformDetail!.transformMethods[methodIndex].methodArgs = + value; + }) + } + onInsertBefore={(methodIndex) => + setTransformData((draft) => { + draft[dataIndex].transformDetail?.transformMethods.splice(methodIndex - 1, 0, {}); + }) + } + onInsertAfter={(methodIndex) => + setTransformData((draft) => { + draft[dataIndex].transformDetail?.transformMethods.splice(methodIndex, 0, {}); + }) + } + onDrop={(methodIndex) => + setTransformData((draft) => { + draft[dataIndex].transformDetail?.transformMethods.splice(methodIndex - 1, 1); + }) + } + onAdd={() => + setTransformData((draft) => { + draft[dataIndex].transformDetail?.transformMethods.push({}); + }) + } + onSave={() => { + if (item.id && item.id !== TemporaryId) { + updateTransformNode({ + id: item.id, + transformDetail: item.transformDetail!, + }); + } else { + insertTransformNode(item.transformDetail!); + } + }} + onEdit={() => { + setTransformData(data); + setEdit(item.id); + }} + onCancel={() => { + setTransformData(data); + setEdit(undefined); + }} + onDelete={deleteTransformNode} + /> + ); + })} + + +
+ + + + + {t('appSetting.selectNodePath')} + + + + + + + + } + open={openIndex > -1} + onClose={() => { + setOpenIndex(-1); + }} + > + + { + setNodePath(target.node.key as string); + }} + /> + + + + ); +}; + +export default NodesTransform; diff --git a/packages/arex/src/panes/AppSetting/CompareConfig/index.tsx b/packages/arex/src/panes/AppSetting/CompareConfig/index.tsx index 5d4b581ed..46d8bbd84 100644 --- a/packages/arex/src/panes/AppSetting/CompareConfig/index.tsx +++ b/packages/arex/src/panes/AppSetting/CompareConfig/index.tsx @@ -16,6 +16,7 @@ import { DependencyParams } from '@/services/ComparisonService'; import CategoryIgnore from './CategoryIgnore'; import NodesIgnore from './NodesIgnore'; import NodesSort from './NodesSort'; +import NodesTransform from './NodesTransform'; import SyncContract from './SyncContract'; export enum CONFIG_TARGET { @@ -29,6 +30,7 @@ export enum CONFIG_TYPE { NODE_SORT, // NODE_Desensitization, // TODO CATEGORY_IGNORE, + NODE_TRANSFORM, } // TODO 类型定义抽离封装 @@ -104,6 +106,12 @@ const CompareConfig: FC = (props) => { }); } + if (configTargetValue !== CONFIG_TARGET.GLOBAL) { + options.push({ + label: t('appSetting.nodesTransform'), + value: CONFIG_TYPE.NODE_TRANSFORM, + }); + } return options; }, [t, configTargetValue]); const [configTypeValue, setConfigTypeValue] = useState( @@ -461,6 +469,20 @@ const CompareConfig: FC = (props) => { configTarget={configTargetValue} /> )} + + {configTypeValue === CONFIG_TYPE.NODE_TRANSFORM && ( + + )} ); diff --git a/packages/arex/src/panes/SystemSetting/Advanced/index.tsx b/packages/arex/src/panes/SystemSetting/Advanced/index.tsx index ae241e224..854e29815 100644 --- a/packages/arex/src/panes/SystemSetting/Advanced/index.tsx +++ b/packages/arex/src/panes/SystemSetting/Advanced/index.tsx @@ -11,6 +11,7 @@ import SystemLogs from './SystemLogs'; type SystemConfigForm = { callbackUrl: string; jarUrl: string; + comparePluginUrl?: string; }; const Advanced: FC = () => { @@ -27,6 +28,7 @@ const Advanced: FC = () => { form.setFieldsValue({ callbackUrl: res.callbackUrl, jarUrl: res.desensitizationJar?.jarUrl, + comparePluginUrl: res.comparePluginInfo?.comparePluginUrl || '', }); } }, @@ -50,6 +52,9 @@ const Advanced: FC = () => { desensitizationJar: { jarUrl: value.jarUrl, }, + comparePluginInfo: { + comparePluginUrl: value.comparePluginUrl || '', + }, }, }); }; @@ -106,6 +111,19 @@ const Advanced: FC = () => { /> + + + + {/* form submit button */}