This is an automated email from the ASF dual-hosted git repository. dockerzhang pushed a commit to branch branch-1.4 in repository https://gitbox.apache.org/repos/asf/inlong.git
commit 88ba6d5e3e1ff58c05684144c5ab23b1beb65779 Author: Lizhen <88174078+bluew...@users.noreply.github.com> AuthorDate: Fri Nov 11 16:58:04 2022 +0800 [INLONG-6504][Dashboard] Support stream to view execution log and execute workflow (#6509) --- inlong-dashboard/src/locales/cn.json | 3 + inlong-dashboard/src/locales/en.json | 3 + .../GroupDetail/DataStream/ExecutionLogModal.tsx | 219 +++++++++++++++++++++ .../src/pages/GroupDetail/DataStream/index.tsx | 50 +++++ 4 files changed, 275 insertions(+) diff --git a/inlong-dashboard/src/locales/cn.json b/inlong-dashboard/src/locales/cn.json index 60f5a5af0..7be671dba 100644 --- a/inlong-dashboard/src/locales/cn.json +++ b/inlong-dashboard/src/locales/cn.json @@ -302,6 +302,9 @@ "meta.Stream.Status.Pending": "配置中", "meta.Stream.Status.Error": "配置失败", "meta.Stream.Status.Success": "配置成功", + "meta.Stream.ExecuteWorkflow": "执行工作流", + "meta.Stream.ExecuteConfirm": "确认执行工作流吗?", + "meta.Stream.ExecuteSuccess": "执行成功", "meta.Consume.ConsumerGroupName": "消费组名称", "meta.Consume.ConsumerGroupNameRules": "只能包含小写字母、数字、中划线、下划线", "meta.Consume.TopicName": "Topic名称", diff --git a/inlong-dashboard/src/locales/en.json b/inlong-dashboard/src/locales/en.json index fffa3bb68..cc17fd9f9 100644 --- a/inlong-dashboard/src/locales/en.json +++ b/inlong-dashboard/src/locales/en.json @@ -302,6 +302,9 @@ "meta.Stream.Status.Pending": "Pending", "meta.Stream.Status.Error": "Error", "meta.Stream.Status.Success": "Success", + "meta.Stream.ExecuteWorkflow": "ExecuteWorkflow", + "meta.Stream.ExecuteConfirm": "Are you sure to execute the workflow?", + "meta.Stream.ExecuteSuccess": "Execution Success", "meta.Consume.ConsumerGroupName": "Consumer Group Name", "meta.Consume.TopicName": "Topic Name", "meta.Consume.MQType": "MQ Type", diff --git a/inlong-dashboard/src/pages/GroupDetail/DataStream/ExecutionLogModal.tsx b/inlong-dashboard/src/pages/GroupDetail/DataStream/ExecutionLogModal.tsx new file mode 100644 index 000000000..6445135c8 --- /dev/null +++ b/inlong-dashboard/src/pages/GroupDetail/DataStream/ExecutionLogModal.tsx @@ -0,0 +1,219 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { useCallback, useState } from 'react'; +import { Modal, message, Button, Collapse, Popover, Timeline, Pagination, Empty } from 'antd'; +import { ModalProps } from 'antd/es/modal'; +import HighTable from '@/components/HighTable'; +import request from '@/utils/request'; +import { useTranslation } from 'react-i18next'; +import { useRequest, useUpdateEffect } from '@/hooks'; +import { timestampFormat } from '@/utils'; +import StatusTag from '@/components/StatusTag'; + +const { Panel } = Collapse; + +export interface Props extends ModalProps { + inlongGroupId?: string; + inlongStreamId: string; +} + +const Comp: React.FC<Props> = ({ inlongGroupId, inlongStreamId, ...modalProps }) => { + const { t } = useTranslation(); + + const [options, setOptions] = useState({ + pageNum: 1, + pageSize: 5, + }); + + const { run: getData, data } = useRequest( + { + url: '/workflow/listTaskLogs', + params: { + ...options, + inlongGroupId: inlongGroupId, + inlongStreamId: inlongStreamId, + processNames: 'CREATE_GROUP_RESOURCE,CREATE_STREAM_RESOURCE', + taskType: 'ServiceTask', + }, + }, + { + manual: true, + }, + ); + + const onChange = useCallback((pageNum, pageSize) => { + setOptions(prev => ({ + ...prev, + pageNum, + pageSize, + })); + }, []); + + const reRun = useCallback( + ({ taskId }) => { + Modal.confirm({ + title: t('pages.GroupDashboard.ExecutionLogModal.ConfirmThatItIsRe-executed'), + onOk: async () => { + await request({ + url: `/workflow/complete/` + taskId, + method: 'POST', + data: { + remark: '', + }, + }); + await getData(inlongGroupId); + message.success(t('pages.GroupDashboard.ExecutionLogModal.Re-executingSuccess')); + }, + }); + }, + [getData, inlongGroupId, t], + ); + + useUpdateEffect(() => { + if (modalProps.visible) { + getData(); + } + }, [options]); + + useUpdateEffect(() => { + if (modalProps.visible) { + getData(); + } else { + setOptions(prev => ({ + ...prev, + pageNum: 1, + })); + } + }, [modalProps.visible]); + + const columns = [ + { + title: t('pages.GroupDashboard.ExecutionLogModal.TaskType'), + dataIndex: 'taskDisplayName', + }, + { + title: t('pages.GroupDashboard.ExecutionLogModal.RunResults'), + dataIndex: 'status', + render: (text, record) => ( + <> + <div> + {record.status === 'COMPLETED' ? ( + <StatusTag + type={'success'} + title={t('pages.GroupDashboard.ExecutionLogModal.Success')} + /> + ) : record.status === 'FAILED' ? ( + <StatusTag type={'error'} title={t('pages.GroupDashboard.ExecutionLogModal.Fail')} /> + ) : record.status === 'SKIPPED' ? ( + <StatusTag + type={'primary'} + title={t('pages.GroupDashboard.ExecutionLogModal.Skip')} + /> + ) : ( + <StatusTag + type={'warning'} + title={t('pages.GroupDashboard.ExecutionLogModal.Processing')} + /> + )} + </div> + </> + ), + }, + { + title: t('pages.GroupDashboard.ExecutionLogModal.ExecuteLog'), + dataIndex: 'listenerExecuteLogs', + width: 400, + render: text => + text?.length ? ( + <Popover + content={ + <Timeline mode={'left'} style={{ marginBottom: -20 }}> + {text.map(item => ( + <Timeline.Item key={item.id} color={item.state === -1 ? 'red' : 'blue'}> + {item.description} + </Timeline.Item> + ))} + </Timeline> + } + overlayStyle={{ maxWidth: 750, maxHeight: 300, overflow: 'auto' }} + > + <div style={{ height: 45, overflow: 'hidden' }}>{text[0]?.description}</div> + </Popover> + ) : null, + }, + { + title: t('pages.GroupDashboard.ExecutionLogModal.EndTime'), + dataIndex: 'endTime', + render: (text, record) => record.endTime && timestampFormat(record.endTime), + }, + { + title: t('basic.Operating'), + dataIndex: 'actions', + render: (text, record) => ( + <> + {record?.status && record.status === 'FAILED' && ( + <Button type="link" onClick={() => reRun(record)}> + {t('pages.GroupDashboard.ExecutionLogModal.CarriedOut')} + </Button> + )} + </> + ), + }, + ]; + return ( + <Modal + {...modalProps} + title={t('pages.GroupDashboard.ExecutionLogModal.ExecuteLog')} + width={1024} + footer={null} + > + {data?.list?.length ? ( + <> + <Collapse accordion defaultActiveKey={[data.list[0]?.processId]}> + {data.list.map(item => ( + <Panel header={item.processDisplayName} key={item.processId}> + <HighTable + table={{ + columns, + rowKey: 'taskId', + size: 'small', + dataSource: item.taskExecuteLogs, + }} + /> + </Panel> + ))} + </Collapse> + <Pagination + size="small" + pageSize={options.pageSize} + current={options.pageNum} + total={data.total} + onChange={onChange} + style={{ textAlign: 'right', marginTop: 10 }} + /> + </> + ) : ( + <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> + )} + </Modal> + ); +}; + +export default Comp; diff --git a/inlong-dashboard/src/pages/GroupDetail/DataStream/index.tsx b/inlong-dashboard/src/pages/GroupDetail/DataStream/index.tsx index 634127bd9..065710d64 100644 --- a/inlong-dashboard/src/pages/GroupDetail/DataStream/index.tsx +++ b/inlong-dashboard/src/pages/GroupDetail/DataStream/index.tsx @@ -28,6 +28,7 @@ import { useLoadMeta, useDefaultMeta, StreamMetaType } from '@/metas'; import { CommonInterface } from '../common'; import StreamItemModal from './StreamItemModal'; import { getFilterFormContent } from './config'; +import ExecutionLogModal from './ExecutionLogModal'; type Props = CommonInterface; @@ -47,6 +48,12 @@ const Comp = ({ inlongGroupId, readonly, mqType }: Props, ref) => { inlongGroupId, }); + const [executionLogModal, setExecutionLogModal] = useState({ + visible: false, + inlongGroupId, + inlongStreamId: '', + }); + const { data, loading, @@ -85,6 +92,14 @@ const Comp = ({ inlongGroupId, readonly, mqType }: Props, ref) => { setStreamItemModal(prev => ({ ...prev, visible: true, inlongStreamId: record.inlongStreamId })); }; + const openModal = record => { + setExecutionLogModal({ + visible: true, + inlongGroupId: inlongGroupId, + inlongStreamId: record.inlongStreamId, + }); + }; + const onDelete = record => { Modal.confirm({ title: t('basic.DeleteConfirm'), @@ -103,6 +118,23 @@ const Comp = ({ inlongGroupId, readonly, mqType }: Props, ref) => { }); }; + const onWorkflow = record => { + Modal.confirm({ + title: t('meta.Stream.ExecuteConfirm'), + onOk: async () => { + await request({ + url: `/stream/startProcess/${inlongGroupId}/${record?.inlongStreamId}`, + method: 'POST', + params: { + sync: false, + }, + }); + await getList(); + message.success(t('meta.Stream.ExecuteSuccess')); + }, + }); + }; + const onChange = ({ current: pageNum, pageSize }) => { setOptions(prev => ({ ...prev, @@ -146,6 +178,16 @@ const Comp = ({ inlongGroupId, readonly, mqType }: Props, ref) => { <Button type="link" onClick={() => onDelete(record)}> {t('basic.Delete')} </Button> + {record?.status && (record?.status === 120 || record?.status === 130) && ( + <Button type="link" onClick={() => onWorkflow(record)}> + {t('meta.Stream.ExecuteWorkflow')} + </Button> + )} + {record?.status && (record?.status === 120 || record?.status === 130) && ( + <Button type="link" onClick={() => openModal(record)}> + {t('pages.GroupDashboard.config.ExecuteLog')} + </Button> + )} </> ), }, @@ -184,6 +226,14 @@ const Comp = ({ inlongGroupId, readonly, mqType }: Props, ref) => { }} onCancel={() => setStreamItemModal(prev => ({ ...prev, visible: false }))} /> + + <ExecutionLogModal + {...executionLogModal} + onOk={() => setExecutionLogModal({ visible: false, inlongGroupId: '', inlongStreamId: '' })} + onCancel={() => + setExecutionLogModal({ visible: false, inlongGroupId: '', inlongStreamId: '' }) + } + /> </> ); };