This is an automated email from the ASF dual-hosted git repository.

aloyszhang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/inlong.git


The following commit(s) were added to refs/heads/master by this push:
     new 80125f0f63 [INLONG-11178][Dashboard] Added http sink and http node 
(#11311)
80125f0f63 is described below

commit 80125f0f6336d9990ea3dbc0b77cb7330b19648f
Author: kamianlaida <165994047+wohainilao...@users.noreply.github.com>
AuthorDate: Thu Oct 10 09:58:54 2024 +0800

    [INLONG-11178][Dashboard] Added http sink and http node (#11311)
---
 .../src/plugins/clusters/defaults/SortHttp.ts      |  27 +++
 .../src/plugins/clusters/defaults/index.ts         |   5 +
 .../src/plugins/nodes/defaults/Http.ts             |  79 +++++++++
 .../src/plugins/nodes/defaults/index.ts            |   5 +
 .../src/plugins/sinks/defaults/Http.ts             | 186 +++++++++++++++++++++
 .../src/plugins/sinks/defaults/index.ts            |   5 +
 inlong-dashboard/src/ui/locales/cn.json            |  11 ++
 inlong-dashboard/src/ui/locales/en.json            |  11 ++
 .../src/ui/pages/Clusters/CreateModal.tsx          |   3 +-
 9 files changed, 331 insertions(+), 1 deletion(-)

diff --git a/inlong-dashboard/src/plugins/clusters/defaults/SortHttp.ts 
b/inlong-dashboard/src/plugins/clusters/defaults/SortHttp.ts
new file mode 100644
index 0000000000..473d3dcaff
--- /dev/null
+++ b/inlong-dashboard/src/plugins/clusters/defaults/SortHttp.ts
@@ -0,0 +1,27 @@
+/*
+ * 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 { ClusterInfo } from '@/plugins/clusters/common/ClusterInfo';
+import { DataWithBackend } from '@/plugins/DataWithBackend';
+import { RenderRow } from '@/plugins/RenderRow';
+import { RenderList } from '@/plugins/RenderList';
+
+export default class SortHttp
+  extends ClusterInfo
+  implements DataWithBackend, RenderRow, RenderList {}
diff --git a/inlong-dashboard/src/plugins/clusters/defaults/index.ts 
b/inlong-dashboard/src/plugins/clusters/defaults/index.ts
index 1c5e49bb49..0c947572b5 100644
--- a/inlong-dashboard/src/plugins/clusters/defaults/index.ts
+++ b/inlong-dashboard/src/plugins/clusters/defaults/index.ts
@@ -71,4 +71,9 @@ export const allDefaultClusters: 
MetaExportWithBackendList<ClusterMetaType> = [
     value: 'SORT_KAFKA',
     LoadEntity: () => import('./SortKafka'),
   },
+  {
+    label: 'Sort Http',
+    value: 'SORT_HTTP',
+    LoadEntity: () => import('./SortHttp'),
+  },
 ];
diff --git a/inlong-dashboard/src/plugins/nodes/defaults/Http.ts 
b/inlong-dashboard/src/plugins/nodes/defaults/Http.ts
new file mode 100644
index 0000000000..8017394143
--- /dev/null
+++ b/inlong-dashboard/src/plugins/nodes/defaults/Http.ts
@@ -0,0 +1,79 @@
+/*
+ * 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 { DataWithBackend } from '@/plugins/DataWithBackend';
+import { RenderRow } from '@/plugins/RenderRow';
+import { RenderList } from '@/plugins/RenderList';
+import { NodeInfo } from '../common/NodeInfo';
+import i18n from 'i18next';
+import { Input } from 'antd';
+
+const { I18n } = DataWithBackend;
+const { FieldDecorator } = RenderRow;
+
+export default class HttpNodeInfo
+  extends NodeInfo
+  implements DataWithBackend, RenderRow, RenderList
+{
+  @FieldDecorator({
+    type: 'input',
+    rules: [{ required: true }],
+  })
+  @I18n('meta.Nodes.Http.BaseUrl')
+  baseUrl: string;
+
+  @FieldDecorator({
+    type: 'radio',
+    rules: [{ required: true }],
+    initialValue: false,
+    props: {
+      options: [
+        {
+          label: i18n.t('basic.Yes'),
+          value: true,
+        },
+        {
+          label: i18n.t('basic.No'),
+          value: false,
+        },
+      ],
+    },
+  })
+  @I18n('meta.Nodes.Http.EnableCredential')
+  enableCredential: string;
+
+  @FieldDecorator({
+    type: 'input',
+  })
+  @I18n('meta.Nodes.Http.Username')
+  username: string;
+
+  @FieldDecorator({
+    type: Input.Password,
+  })
+  @I18n('meta.Nodes.Http.Password')
+  password: string;
+
+  @FieldDecorator({
+    type: 'inputnumber',
+    initialValue: 1000,
+  })
+  @I18n('meta.Nodes.Http.MaxConnect')
+  maxConnect: number;
+}
diff --git a/inlong-dashboard/src/plugins/nodes/defaults/index.ts 
b/inlong-dashboard/src/plugins/nodes/defaults/index.ts
index 06209fd940..858ac347eb 100644
--- a/inlong-dashboard/src/plugins/nodes/defaults/index.ts
+++ b/inlong-dashboard/src/plugins/nodes/defaults/index.ts
@@ -96,4 +96,9 @@ export const allDefaultNodes: 
MetaExportWithBackendList<NodeMetaType> = [
     value: 'KUDU',
     LoadEntity: () => import('./Kudu'),
   },
+  {
+    label: 'Http',
+    value: 'HTTP',
+    LoadEntity: () => import('./Http'),
+  },
 ];
diff --git a/inlong-dashboard/src/plugins/sinks/defaults/Http.ts 
b/inlong-dashboard/src/plugins/sinks/defaults/Http.ts
new file mode 100644
index 0000000000..2dce2466da
--- /dev/null
+++ b/inlong-dashboard/src/plugins/sinks/defaults/Http.ts
@@ -0,0 +1,186 @@
+/*
+ * 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 { DataWithBackend } from '@/plugins/DataWithBackend';
+import { RenderRow } from '@/plugins/RenderRow';
+import { SinkInfo } from '@/plugins/sinks/common/SinkInfo';
+import { RenderList } from '@/plugins/RenderList';
+import NodeSelect from '@/ui/components/NodeSelect';
+import i18n from '@/i18n';
+import EditableTable from '@/ui/components/EditableTable';
+import {
+  fieldTypes,
+  fieldTypes as sourceFieldsTypes,
+  sourceFields,
+} from '@/plugins/sinks/common/sourceFields';
+
+const { I18n } = DataWithBackend;
+const { FieldDecorator, SyncField, SyncCreateTableField, IngestionField } = 
RenderRow;
+const { ColumnDecorator } = RenderList;
+
+export default class HttpSinkInfo
+  extends SinkInfo
+  implements DataWithBackend, RenderRow, RenderList
+{
+  @FieldDecorator({
+    type: 'input',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110].includes(values?.status),
+    }),
+  })
+  @SyncField()
+  @I18n('meta.Sinks.Http.Path')
+  @IngestionField()
+  path: string;
+
+  @FieldDecorator({
+    type: 'radio',
+    initialValue: 'GET',
+    rules: [{ required: true }],
+    props: values => ({
+      options: [
+        {
+          label: 'GET',
+          value: 'GET',
+        },
+        {
+          label: 'POST',
+          value: 'POST',
+        },
+      ],
+    }),
+  })
+  @SyncField()
+  @IngestionField()
+  @I18n('meta.Sinks.Http.Method')
+  method: string;
+
+  @FieldDecorator({
+    type: NodeSelect,
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110].includes(values?.status),
+      nodeType: 'HTTP',
+    }),
+  })
+  @I18n('meta.Sinks.DataNodeName')
+  @SyncField()
+  @IngestionField()
+  @ColumnDecorator()
+  dataNodeName: string;
+
+  @FieldDecorator({
+    type: EditableTable,
+    props: values => ({
+      size: 'small',
+      canDelete: record => !(record.id && [110].includes(values?.status)),
+      canBatchAdd: true,
+      columns: [
+        {
+          title: i18n.t('meta.Sinks.Http.FieldName'),
+          dataIndex: 'fieldName',
+          props: (text, record) => ({
+            disabled: record.id && [110].includes(values?.status),
+          }),
+        },
+        {
+          title: i18n.t('meta.Sinks.Http.FieldValue'),
+          dataIndex: 'fieldValue',
+          type: 'input',
+        },
+      ],
+    }),
+  })
+  @SyncField()
+  @IngestionField()
+  @I18n('meta.Sinks.Http.Headers')
+  headers: Record<string, string>[];
+
+  @FieldDecorator({
+    type: 'inputnumber',
+    initialValue: '0',
+  })
+  @SyncField()
+  @IngestionField()
+  @I18n('meta.Sinks.Http.MaxRetryTimes')
+  maxRetryTimes: number;
+  @FieldDecorator({
+    type: EditableTable,
+    props: values => ({
+      size: 'small',
+      editing: ![110].includes(values?.status),
+      columns: getFieldListColumns(values),
+      canBatchAdd: true,
+      upsertByFieldKey: true,
+    }),
+  })
+  @IngestionField()
+  sinkFieldList: Record<string, unknown>[];
+}
+
+const getFieldListColumns = sinkValues => {
+  return [
+    ...sourceFields,
+    {
+      title: i18n.t('meta.Sinks.SinkFieldName'),
+      dataIndex: 'fieldName',
+      initialValue: '',
+      rules: [
+        { required: true },
+        {
+          pattern: /^[a-zA-Z_][0-9a-z_]*$/,
+          message: i18n.t('meta.Sinks.SinkFieldNameRule'),
+        },
+      ],
+      props: (text, record, idx, isNew) => ({
+        disabled: [110].includes(sinkValues?.status as number) && !isNew,
+      }),
+    },
+    {
+      title: i18n.t('meta.Sinks.SinkFieldType'),
+      dataIndex: 'fieldType',
+      initialValue: fieldTypes[0].value,
+      type: 'select',
+      props: (text, record, idx, isNew) => ({
+        options: fieldTypes,
+        disabled: [110].includes(sinkValues?.status as number) && !isNew,
+      }),
+      rules: [{ required: true, message: 
`${i18n.t('meta.Sinks.FieldTypeMessage')}` }],
+    },
+    {
+      title: i18n.t('meta.Sinks.Redis.FieldFormat'),
+      dataIndex: 'fieldFormat',
+      initialValue: 0,
+      type: 'autocomplete',
+      props: (text, record, idx, isNew) => ({
+        options: ['MICROSECONDS', 'MILLISECONDS', 'SECONDS', 'SQL', 
'ISO_8601'].map(item => ({
+          label: item,
+          value: item,
+        })),
+      }),
+      visible: (text, record) => ['BIGINT', 'DATE'].includes(record.fieldType 
as string),
+    },
+    {
+      title: i18n.t('meta.Sinks.FieldDescription'),
+      dataIndex: 'fieldComment',
+      initialValue: '',
+    },
+  ];
+};
diff --git a/inlong-dashboard/src/plugins/sinks/defaults/index.ts 
b/inlong-dashboard/src/plugins/sinks/defaults/index.ts
index 0fe788c103..c916f8dcab 100644
--- a/inlong-dashboard/src/plugins/sinks/defaults/index.ts
+++ b/inlong-dashboard/src/plugins/sinks/defaults/index.ts
@@ -126,4 +126,9 @@ export const allDefaultSinks: 
MetaExportWithBackendList<SinkMetaType> = [
     value: 'KUDU',
     LoadEntity: () => import('./Kudu'),
   },
+  {
+    label: 'Http',
+    value: 'HTTP',
+    LoadEntity: () => import('./Http'),
+  },
 ];
diff --git a/inlong-dashboard/src/ui/locales/cn.json 
b/inlong-dashboard/src/ui/locales/cn.json
index b97e66643b..d68a58001c 100644
--- a/inlong-dashboard/src/ui/locales/cn.json
+++ b/inlong-dashboard/src/ui/locales/cn.json
@@ -171,6 +171,12 @@
   "meta.Sinks.EnableCreateResource": "是否创建资源",
   "meta.Sinks.EnableCreateResourceHelp": 
"如果库表已经存在,且无需修改,则选【不创建】,否则请选择【创建】,由系统自动创建资源。",
   "meta.Sinks.DataNodeName": "数据节点",
+  "meta.Sinks.Http.MaxRetryTimes": "最大重试次数",
+  "meta.Sinks.Http.Headers": "请求头",
+  "meta.Sinks.Http.Method": "请求方式",
+  "meta.Sinks.Http.Path": "请求路径",
+  "meta.Sinks.Http.FieldName": "参数名",
+  "meta.Sinks.Http.FieldValue": "参数值",
   "meta.Sinks.FieldTypeMessage": "请输入字段类型",
   "meta.Sinks.Hive.FileFormat": "落地格式",
   "meta.Sinks.Hive.Day": "天",
@@ -532,6 +538,11 @@
   "meta.Nodes.StarRocks.Url": "地址",
   "meta.Nodes.Hive.Username": "用户名",
   "meta.Nodes.Hive.Password": "密码",
+  "meta.Nodes.Http.Username": "用户名",
+  "meta.Nodes.Http.Password": "密码",
+  "meta.Nodes.Http.BaseUrl": "基础路径",
+  "meta.Nodes.Http.EnableCredential": "启用凭证",
+  "meta.Nodes.Http.MaxConnect": "最大连接数",
   "meta.Nodes.Hudi.Username": "用户名",
   "meta.Nodes.Hudi.Password": "密码",
   "meta.Nodes.Hudi.Url": "地址",
diff --git a/inlong-dashboard/src/ui/locales/en.json 
b/inlong-dashboard/src/ui/locales/en.json
index ebf2bbfcb1..144e15e37d 100644
--- a/inlong-dashboard/src/ui/locales/en.json
+++ b/inlong-dashboard/src/ui/locales/en.json
@@ -171,6 +171,12 @@
   "meta.Sinks.EnableCreateResource": "Create resource",
   "meta.Sinks.EnableCreateResourceHelp": "If the library table already exists 
and does not need to be modified, select [Do not create], otherwise select 
[Create], and the system will automatically create the resource.",
   "meta.Sinks.DataNodeName": "Data node",
+  "meta.Sinks.Http.MaxRetryTimes": "Max Retry Times",
+  "meta.Sinks.Http.Headers": "Headers",
+  "meta.Sinks.Http.Method": "Method",
+  "meta.Sinks.Http.Path": "Path",
+  "meta.Sinks.Http.FieldName": "Field Name",
+  "meta.Sinks.Http.FieldValue": "Field Value",
   "meta.Sinks.FieldTypeMessage": "Please enter field type",
   "meta.Sinks.Hive.FileFormat": "File format",
   "meta.Sinks.Hive.Day": "Day(s)",
@@ -542,6 +548,11 @@
   "meta.Nodes.Redis.ClusterMode": "Cluster mode",
   "meta.Nodes.Redis.ClusterModeHelper": "Please select custer mode",
   "meta.Nodes.Redis.PortHelper": "Please select Redis server port, default: 
6379",
+  "meta.Nodes.Http.Username": "Username",
+  "meta.Nodes.Http.Password": "Password",
+  "meta.Nodes.Http.BaseUrl": "Base url",
+  "meta.Nodes.Http.EnableCredential": "EnableCredential",
+  "meta.Nodes.Http.MaxConnect": "MaxConnect",
   "meta.Nodes.Hudi.Username": "Username",
   "meta.Nodes.Hudi.Password": "Password",
   "meta.Nodes.Hudi.Url": "URL",
diff --git a/inlong-dashboard/src/ui/pages/Clusters/CreateModal.tsx 
b/inlong-dashboard/src/ui/pages/Clusters/CreateModal.tsx
index bf5645c58c..988f41613c 100644
--- a/inlong-dashboard/src/ui/pages/Clusters/CreateModal.tsx
+++ b/inlong-dashboard/src/ui/pages/Clusters/CreateModal.tsx
@@ -71,7 +71,8 @@ const Comp: React.FC<Props> = ({ id, defaultType, 
...modalProps }) => {
       values.type === 'SORT_ES' ||
       values.type === 'SORT_CKAFKA' ||
       values.type === 'SORT_KAFKA' ||
-      values.type === 'SORT_PULSAR'
+      values.type === 'SORT_PULSAR' ||
+      values.type === 'SORT_HTTP'
     ) {
       submitData.name = values.displayName;
     }

Reply via email to