You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@inlong.apache.org by do...@apache.org on 2022/11/11 09:13:24 UTC

[inlong] 03/05: [INLONG-6504][Dashboard] Support stream to view execution log and execute workflow (#6509)

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 <88...@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: '' })
+        }
+      />
     </>
   );
 };