diff --git a/dolphinscheduler-ui/public/images/task-icons/external_system.png b/dolphinscheduler-ui/public/images/task-icons/external_system.png
new file mode 100644
index 000000000000..a564ebcc33a4
Binary files /dev/null and b/dolphinscheduler-ui/public/images/task-icons/external_system.png differ
diff --git a/dolphinscheduler-ui/public/images/task-icons/external_system_hover.png b/dolphinscheduler-ui/public/images/task-icons/external_system_hover.png
new file mode 100644
index 000000000000..d379f195216f
Binary files /dev/null and b/dolphinscheduler-ui/public/images/task-icons/external_system_hover.png differ
diff --git a/dolphinscheduler-ui/src/locales/en_US/index.ts b/dolphinscheduler-ui/src/locales/en_US/index.ts
index 0e289642de67..08c9d265d8aa 100644
--- a/dolphinscheduler-ui/src/locales/en_US/index.ts
+++ b/dolphinscheduler-ui/src/locales/en_US/index.ts
@@ -29,6 +29,7 @@ import project from '@/locales/en_US/project'
import resource from '@/locales/en_US/resource'
import security from '@/locales/en_US/security'
import theme from '@/locales/en_US/theme'
+import thirdparty_api_source from '@/locales/en_US/thirdparty-api-source'
import user_dropdown from '@/locales/en_US/user-dropdown'
import ui_setting from '@/locales/en_US/ui_setting'
import about from '@/locales/en_US/about'
@@ -48,6 +49,7 @@ export default {
project,
security,
datasource,
+ thirdparty_api_source,
crontab,
ui_setting,
input_search
diff --git a/dolphinscheduler-ui/src/locales/en_US/project.ts b/dolphinscheduler-ui/src/locales/en_US/project.ts
index 916893b02a0c..d033b7eda13f 100644
--- a/dolphinscheduler-ui/src/locales/en_US/project.ts
+++ b/dolphinscheduler-ui/src/locales/en_US/project.ts
@@ -547,6 +547,8 @@ export default {
target_task_name: 'Target Task Name',
datasource_type: 'Datasource types',
datasource_instances: 'Datasource instances',
+ external_systems: 'External Systems',
+ external_system_tasks: 'External System Tasks',
sql_type: 'SQL Type',
sql_type_query: 'Query',
sql_type_non_query: 'Non Query',
diff --git a/dolphinscheduler-ui/src/locales/en_US/security.ts b/dolphinscheduler-ui/src/locales/en_US/security.ts
index 15e1c1fc2c42..e23ec17cfeac 100644
--- a/dolphinscheduler-ui/src/locales/en_US/security.ts
+++ b/dolphinscheduler-ui/src/locales/en_US/security.ts
@@ -153,12 +153,14 @@ export default {
file_resource: 'File Resource',
datasource: 'Datasource',
namespace: 'Namespace',
+ thirdparty: 'Thirdparty',
revoke_auth: 'Revoke',
grant_read: 'Grant Read',
grant_all: 'Grant All',
authorize_project: 'Project Authorize',
authorize_namespace: 'Namespace Authorize',
authorize_datasource: 'Datasource Authorize',
+ authorize_thirdparty: 'Thirdparty Authorize',
username: 'Username',
username_exists: 'The username already exists',
username_tips: 'Please enter username',
diff --git a/dolphinscheduler-ui/src/locales/en_US/thirdparty-api-source.ts b/dolphinscheduler-ui/src/locales/en_US/thirdparty-api-source.ts
new file mode 100644
index 000000000000..03185fbc3494
--- /dev/null
+++ b/dolphinscheduler-ui/src/locales/en_US/thirdparty-api-source.ts
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+export default {
+ // Route titles
+ thirdparty_api_source: 'Thirdparty API Source',
+ create_thirdparty_api_source: 'Create Thirdparty API Source',
+ edit_thirdparty_api_source: 'Edit Thirdparty API Source',
+
+ // Basic Information
+ basic_info: 'Basic Information',
+ id: 'ID',
+ system_name: 'System Name',
+ system_name_tips: 'Please enter system name',
+ service_address: 'Service Address',
+ service_address_tips: 'Please enter service address',
+ field_mapping: 'Field Mapping',
+ external_field: 'External Field',
+ internal_field: 'Internal Field',
+ create_time: 'Create Time',
+ update_time: 'Update Time',
+
+ // Authentication Configuration
+ auth_config: 'Authentication Configuration',
+ auth_type: 'Authentication Type',
+ auth_type_tips: 'Please select authentication type',
+ basic_auth: 'Basic Authentication',
+ oauth2: 'OAuth2 Authentication',
+ jwt: 'JWT Authentication',
+ username: 'Username',
+ username_tips: 'Please enter username',
+ password: 'Password',
+ password_tips: 'Please enter password',
+ jwt_token: 'JWT Token',
+ jwt_token_tips: 'Please enter JWT token',
+ oauth2_token_url: 'Token URL',
+ oauth2_token_url_tips: 'Please enter Token URL',
+ oauth2_client_id: 'Client ID',
+ oauth2_client_id_tips: 'Please enter Client ID',
+ oauth2_client_secret: 'Client Secret',
+ oauth2_client_secret_tips: 'Please enter Client Secret',
+ oauth2_grant_type: 'Grant Type',
+ oauth2_grant_type_tips: 'Please enter Grant Type',
+ oauth2_username: 'OAuth2 Username',
+ oauth2_username_tips: 'Please enter OAuth2 username',
+ oauth2_password: 'OAuth2 Password',
+ oauth2_password_tips: 'Please enter OAuth2 password',
+ additional_params: 'Additional Parameters',
+ add_param: 'Add Param',
+ add_extract_field: 'Add Extract Field',
+ key: 'key',
+ value: 'value',
+
+ // Interface Configuration
+ interface_config: 'Interface Configuration',
+ input_interface: 'Input Interface',
+ input_interface_tips: 'Please enter interface address',
+ submit_interface: 'Submit Interface',
+ submit_interface_tips: 'Please enter interface address',
+ query_interface: 'Query Interface',
+ query_interface_tips: 'Please enter interface address',
+ stop_interface: 'Stop Interface',
+ stop_interface_tips: 'Please enter interface address',
+ parameters: 'Parameters',
+ param_location: 'Parameter Location',
+ param_location_tips: 'Please select parameter location',
+ param_name_tips: 'Please enter parameter name',
+ param_value_tips: 'Please enter parameter value',
+ extract_response_data: 'Please enter response data jsonPath',
+ extract_field: 'Please enter extract field',
+ json_path_list: 'Please enter, e.g: $.data[*].id',
+ json_path: 'Please enter, e.g: $.data.taskInstanceId',
+ header_prefix: 'Authorization Token Prefix',
+ header_prefix_tips: 'Authorization header before token,e.g.Bearer',
+ system_field_tips: 'Please select system field',
+ request_body: 'Request Body',
+ request_body_placeholder: 'Please enter JSON format request body',
+ header: 'Header',
+ query: 'Query',
+ get: 'GET',
+ post: 'POST',
+ put: 'PUT',
+ interface_timeout: 'Interface Timeout',
+ interface_timeout_tips: 'Please enter interface timeout',
+ millisecond: 'millisecond',
+
+ interface_timeout_description:
+ 'Set the interface request timeout, default is 120000 milliseconds (2 minutes)',
+
+ // Polling Configuration
+ polling_config: 'Polling Configuration',
+ success_condition: 'Success Condition',
+ success_field: 'Success Field',
+ success_field_tips: 'Please enter success field JSONPath,e.g.$.data.status',
+ success_value: 'Success Value',
+ success_value_tips:
+ 'Please enter all enum values for success,e.g.SUCCESS,FINISHED',
+ failure_condition: 'Failure Condition',
+ failure_field: 'Failure Field',
+ failure_field_tips: 'Please enter failure field JSONPath,e.g.$.data.status',
+ failure_value: 'Failure Value',
+ failure_value_tips:
+ 'Please enter all enum values for failure,e.g.CANCELED, FAILED',
+
+ // Buttons and Operations
+ cancel: 'Cancel',
+ submit: 'Submit',
+ test: 'Test',
+ search: 'Search',
+ create: 'Create',
+ edit: 'Edit',
+ delete: 'Delete',
+ update: 'Update',
+
+ // Message Tips
+ create_success: 'Create successful',
+ edit_success: 'Edit successful',
+ delete_success: 'Delete successful',
+ test_success: 'Test successful',
+ create_failed: 'Create failed',
+ edit_failed: 'Edit failed',
+ delete_failed: 'Delete failed',
+ test_failed: 'Test failed',
+ submit_failed: 'Submit failed, please check form content',
+
+ // Form validation messages
+ system_name_required: 'System name is required',
+ service_address_required: 'Service address is required',
+ auth_type_required: 'Authentication type is required',
+ username_required: 'Username is required',
+ password_required: 'Password is required',
+ jwt_token_required: 'JWT token is required',
+ oauth2_token_url_required: 'Token URL is required',
+ oauth2_client_id_required: 'Client ID is required',
+ oauth2_client_secret_required: 'Client Secret is required',
+ oauth2_grant_type_required: 'Grant Type is required',
+ input_interface_url_required: 'Input interface address is required',
+ submit_interface_url_required: 'Submit interface address is required',
+ query_interface_url_required: 'Query interface address is required',
+ stop_interface_url_required: 'Stop interface address is required',
+ success_condition_required:
+ 'Success condition field and value cannot be empty',
+ failure_condition_required:
+ 'Failure condition field and value cannot be empty',
+
+ external_system_required: 'External system is required',
+ external_system_task_required: 'External system task is required',
+ id_jsonpath_required: 'ID field and JSONPath is required',
+ name_jsonpath_required: 'Name field and JSONPath is required',
+ taskinstanceid_jsonpath_required:
+ 'TaskInstanceId field and JSONPath is required'
+}
diff --git a/dolphinscheduler-ui/src/locales/zh_CN/index.ts b/dolphinscheduler-ui/src/locales/zh_CN/index.ts
index fdd36e1a5cb0..15de405c6a99 100644
--- a/dolphinscheduler-ui/src/locales/zh_CN/index.ts
+++ b/dolphinscheduler-ui/src/locales/zh_CN/index.ts
@@ -29,6 +29,7 @@ import project from '@/locales/zh_CN/project'
import resource from '@/locales/zh_CN/resource'
import security from '@/locales/zh_CN/security'
import theme from '@/locales/zh_CN/theme'
+import thirdparty_api_source from '@/locales/zh_CN/thirdparty-api-source'
import user_dropdown from '@/locales/zh_CN/user-dropdown'
import ui_setting from '@/locales/zh_CN/ui_setting'
import about from '@/locales/zh_CN/about'
@@ -48,6 +49,7 @@ export default {
project,
security,
datasource,
+ thirdparty_api_source,
crontab,
ui_setting,
input_search
diff --git a/dolphinscheduler-ui/src/locales/zh_CN/project.ts b/dolphinscheduler-ui/src/locales/zh_CN/project.ts
index 46fd12e348fd..27bbac05ec05 100644
--- a/dolphinscheduler-ui/src/locales/zh_CN/project.ts
+++ b/dolphinscheduler-ui/src/locales/zh_CN/project.ts
@@ -530,6 +530,8 @@ export default {
target_task_name: '目标任务名',
datasource_type: '数据源类型',
datasource_instances: '数据源实例',
+ external_systems: '第三方系统列表',
+ external_system_tasks: '第三方系统任务列表',
sql_type: 'SQL类型',
sql_type_query: '查询',
sql_type_non_query: '非查询',
diff --git a/dolphinscheduler-ui/src/locales/zh_CN/security.ts b/dolphinscheduler-ui/src/locales/zh_CN/security.ts
index fc0a00f5e1a1..0efc75747e08 100644
--- a/dolphinscheduler-ui/src/locales/zh_CN/security.ts
+++ b/dolphinscheduler-ui/src/locales/zh_CN/security.ts
@@ -151,12 +151,14 @@ export default {
file_resource: '文件资源',
datasource: '数据源',
namespace: '命名空间',
+ thirdparty: '第三方系统',
revoke_auth: '撤销权限',
grant_read: '授予读权限',
grant_all: '授予所有权限',
authorize_project: '项目授权',
authorize_namespace: '命名空间授权',
authorize_datasource: '数据源授权',
+ authorize_thirdparty: '第三方系统授权',
username: '用户名',
username_exists: '用户名已存在',
username_tips: '请输入用户名',
diff --git a/dolphinscheduler-ui/src/locales/zh_CN/thirdparty-api-source.ts b/dolphinscheduler-ui/src/locales/zh_CN/thirdparty-api-source.ts
new file mode 100644
index 000000000000..04e58317c1fa
--- /dev/null
+++ b/dolphinscheduler-ui/src/locales/zh_CN/thirdparty-api-source.ts
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+export default {
+ thirdparty_api_source: '第三方系统API',
+ create_thirdparty_api_source: '创建第三方系统API',
+ edit_thirdparty_api_source: '编辑第三方系统API',
+
+ basic_info: '基本信息',
+ id: 'ID',
+ system_name: '系统名称',
+ system_name_tips: '请输入系统名称',
+ service_address: '服务地址',
+ service_address_tips: '请输入服务地址',
+ field_mapping: '字段映射',
+ internal_field: '系统字段',
+ external_field: '注册系统字段',
+ create_time: '创建时间',
+ update_time: '更新时间',
+
+ auth_config: '认证配置',
+ auth_type: '认证类型',
+ auth_type_tips: '请选择认证类型',
+ basic_auth: '基础认证',
+ oauth2: 'OAuth2认证',
+ jwt: 'JWT认证',
+ username: '用户名',
+ username_tips: '请输入用户名',
+ password: '密码',
+ password_tips: '请输入密码',
+ jwt_token: 'JWT串',
+ jwt_token_tips: '请输入JWT串',
+ oauth2_token_url: 'Token URL',
+ oauth2_token_url_tips: '请输入Token URL',
+ oauth2_client_id: 'Client ID',
+ oauth2_client_id_tips: '请输入Client ID',
+ oauth2_client_secret: 'Client Secret',
+ oauth2_client_secret_tips: '请输入Client Secret',
+ oauth2_grant_type: 'Grant Type',
+ oauth2_grant_type_tips: '请输入Grant Type',
+ oauth2_username: 'OAuth2用户名',
+ oauth2_username_tips: '请输入OAuth2用户名',
+ oauth2_password: 'OAuth2密码',
+ oauth2_password_tips: '请输入OAuth2密码',
+ additional_params: '补充参数',
+ add_param: '添加参数',
+ add_extract_field: '添加提取参数',
+ key: '键名',
+ value: '键值',
+
+ interface_config: '接口配置',
+ input_interface: '查询任务列表接口',
+ input_interface_tips: '请输入接口地址',
+ submit_interface: '启动任务接口',
+ submit_interface_tips: '请输入接口地址',
+ query_interface: '查询任务状态接口',
+ query_interface_tips: '请输入接口地址',
+ stop_interface: '停止任务接口',
+ stop_interface_tips: '请输入接口地址',
+ parameters: '参数',
+ param_location: '参数位置',
+ param_location_tips: '请选择参数位置',
+ param_name_tips: '请输入参数名',
+ param_value_tips: '请输入参数值',
+ extract_response_data: '提取响应并存储变量',
+ extract_field: '请输入提取参数',
+ json_path_list: '请输入,例如:$.data[*].id',
+ json_path: '请输入,例如:$.data.taskInstanceId',
+ header_prefix: 'Authorization Token前缀',
+ header_prefix_tips: 'Authorization Token前缀,e.g.Bearer',
+ system_field_tips: '请选择系统字段',
+ request_body: '请求Body',
+ request_body_placeholder: '请输入JSON格式的请求体',
+ header: '请求头',
+ query: '查询参数',
+ get: 'GET',
+ post: 'POST',
+ put: 'PUT',
+ interface_timeout: '接口超时时间',
+ interface_timeout_tips: '请输入接口超时时间',
+ millisecond: '毫秒',
+ interface_timeout_description:
+ '设置接口请求超时时间,默认为120000毫秒(2分钟)',
+
+ polling_config: '轮询配置',
+ success_condition: '成功条件',
+ success_field: '成功字段',
+ success_field_tips: '成功字段JSONPath:$.data.status',
+ success_value: '成功值',
+ success_value_tips: '成功值枚举值:SUCCESS,FINISHED',
+ failure_condition: '失败条件',
+ failure_field: '失败字段',
+ failure_field_tips: '失败字段JSONPath:$.data.status',
+ failure_value: '失败值',
+ failure_value_tips: '失败值所有枚举值:CANCELED,FAILED',
+
+ cancel: '取消',
+ submit: '确定',
+ test: '测试连接',
+ search: '搜索',
+ create: '新建',
+ update: '更新',
+ edit: '编辑',
+ delete: '删除',
+
+ create_success: '创建成功',
+ edit_success: '编辑成功',
+ delete_success: '删除成功',
+ test_success: '测试成功',
+ create_failed: '创建失败',
+ edit_failed: '编辑失败',
+ delete_failed: '删除失败',
+ test_failed: '测试失败',
+ submit_failed: '提交失败,请检查表单内容',
+
+ system_name_required: '系统名称为必填项',
+ service_address_required: '服务地址为必填项',
+ auth_type_required: '认证类型为必选项',
+ username_required: '用户名为必填项',
+ password_required: '密码为必填项',
+ jwt_token_required: 'JWT串为必填项',
+ oauth2_token_url_required: 'Token URL为必填项',
+ oauth2_client_id_required: 'Client ID为必填项',
+ oauth2_client_secret_required: 'Client Secret为必填项',
+ oauth2_grant_type_required: 'Grant Type为必填项',
+ input_interface_url_required: '入参接口地址为必填项',
+ submit_interface_url_required: '提交接口地址为必填项',
+ query_interface_url_required: '查询接口地址为必填项',
+ stop_interface_url_required: '停止接口地址为必填项',
+ success_condition_required: '成功条件字段路径和值不能为空',
+ failure_condition_required: '失败条件字段路径和值不能为空',
+
+ external_system_required: '第三方系统不能为空',
+ external_system_task_required: '第三方系统任务不能为空',
+ id_jsonpath_required: '字段id以及JSONPath为必填项',
+ name_jsonpath_required: '字段name以及JSONPath为必填项'
+}
diff --git a/dolphinscheduler-ui/src/service/modules/data-source/index.ts b/dolphinscheduler-ui/src/service/modules/data-source/index.ts
index f629384dac43..dfa805b25ac2 100644
--- a/dolphinscheduler-ui/src/service/modules/data-source/index.ts
+++ b/dolphinscheduler-ui/src/service/modules/data-source/index.ts
@@ -167,3 +167,13 @@ export function getDatasourceTableColumnsById(
}
})
}
+
+export function queryExternalSystemTasks(externalSystemId: number): any {
+ return axios({
+ url: '/external-systems/queryExternalSystemTasks',
+ method: 'get',
+ params: {
+ externalSystemId
+ }
+ })
+}
diff --git a/dolphinscheduler-ui/src/service/modules/data-source/types.ts b/dolphinscheduler-ui/src/service/modules/data-source/types.ts
index cd6badccb4c6..9460dff3a6ca 100644
--- a/dolphinscheduler-ui/src/service/modules/data-source/types.ts
+++ b/dolphinscheduler-ui/src/service/modules/data-source/types.ts
@@ -44,6 +44,7 @@ type IDataBase =
| 'K8S'
| 'ALIYUN_SERVERLESS_SPARK'
| 'DOLPHINDB'
+ | 'THIRDPARTY_SYSTEM_CONNECTOR'
type IDataBaseLabel =
| 'MYSQL'
@@ -101,6 +102,94 @@ interface IDataSource {
accessKeySecret?: string
regionId?: string
endpoint?: string
+ // THIRDPARTY_SYSTEM_CONNECTOR fields
+ serviceAddress?: string
+ interfaceTimeout?: number
+ authConfig?: {
+ authType: string
+ basicUsername?: string
+ basicPassword?: string
+ jwtToken?: string
+ oauth2TokenUrl?: string
+ oauth2ClientId?: string
+ oauth2ClientSecret?: string
+ oauth2GrantType?: string
+ oauth2Username?: string
+ oauth2Password?: string
+ headerPrefix?: string
+ authMappings?: {
+ key: string
+ value: string
+ }[]
+ }
+ selectInterface?: {
+ url: string
+ method: string
+ parameters?: {
+ paramName: string
+ paramValue: string
+ location: string
+ }[]
+ body: string
+ responseParameters?: {
+ key: string
+ jsonPath: string
+ disabled?: boolean
+ }[]
+ }
+ submitInterface?: {
+ url: string
+ method: string
+ parameters?: {
+ paramName: string
+ paramValue: string
+ location: string
+ }[]
+ body: string
+ responseParameters?: {
+ key: string
+ jsonPath: string
+ disabled?: boolean
+ }[]
+ }
+ pollStatusInterface?: {
+ url: string
+ method: string
+ parameters?: {
+ paramName: string
+ paramValue: string
+ location: string
+ }[]
+ body: string
+ pollingSuccessConfig: {
+ successField: string
+ successValue: string
+ }
+ pollingFailureConfig: {
+ failureField: string
+ failureValue: string
+ }
+ responseParameters?: {
+ key: string
+ jsonPath: string
+ disabled?: boolean
+ }[]
+ }
+ stopInterface?: {
+ url: string
+ method: string
+ parameters?: {
+ paramName: string
+ paramValue: string
+ location: string
+ }[]
+ body: string
+ responseParameters?: {
+ key: string
+ jsonPath: string
+ disabled?: boolean
+ }[]
+ }
}
interface ListReq {
diff --git a/dolphinscheduler-ui/src/store/project/task-type.ts b/dolphinscheduler-ui/src/store/project/task-type.ts
index b6de8e6e5a23..77d1ba616cd9 100644
--- a/dolphinscheduler-ui/src/store/project/task-type.ts
+++ b/dolphinscheduler-ui/src/store/project/task-type.ts
@@ -126,6 +126,10 @@ export const TASK_TYPES_MAP = {
helperLinkDisable: true,
taskExecuteType: 'STREAM'
},
+ EXTERNAL_SYSTEM: {
+ alias: 'EXTERNAL_SYSTEM',
+ helperLinkDisable: true
+ },
HIVECLI: {
alias: 'HIVECLI',
helperLinkDisable: true
diff --git a/dolphinscheduler-ui/src/store/project/types.ts b/dolphinscheduler-ui/src/store/project/types.ts
index ba7773fa5b14..bd71c9a160d3 100644
--- a/dolphinscheduler-ui/src/store/project/types.ts
+++ b/dolphinscheduler-ui/src/store/project/types.ts
@@ -49,6 +49,7 @@ type TaskType =
| 'SAGEMAKER'
| 'CHUNJUN'
| 'FLINK_STREAM'
+ | 'EXTERNAL_SYSTEM'
| 'HIVECLI'
| 'DMS'
| 'DATASYNC'
diff --git a/dolphinscheduler-ui/src/views/datasource/list/detail.tsx b/dolphinscheduler-ui/src/views/datasource/list/detail.tsx
index 6f12176f7940..acfb94c86ea0 100644
--- a/dolphinscheduler-ui/src/views/datasource/list/detail.tsx
+++ b/dolphinscheduler-ui/src/views/datasource/list/detail.tsx
@@ -116,6 +116,25 @@ const DetailModal = defineComponent({
props.show && props.id && setFieldsValue(await queryById(props.id))
}
)
+ // Monitor authType change, update headerPrefix
+ watch(
+ () => state.detailForm.authConfig?.authType,
+ (newAuthType) => {
+ if (
+ state.detailForm.type === 'THIRDPARTY_SYSTEM_CONNECTOR' &&
+ state.detailForm.authConfig
+ ) {
+ if (newAuthType === 'BASIC_AUTH') {
+ state.detailForm.authConfig.headerPrefix = 'Basic'
+ } else if (newAuthType === 'JWT' || newAuthType === 'OAUTH2') {
+ state.detailForm.authConfig.headerPrefix = 'Bearer'
+ } else {
+ state.detailForm.authConfig.headerPrefix = ''
+ }
+ }
+ },
+ { immediate: true }
+ )
watch(
() => props.selectType,
@@ -375,7 +394,7 @@ const DetailModal = defineComponent({
placeholder={t('datasource.krb5_conf_tips')}
/>
- {/* 验证条件选择 */}
+ {/* validation */}
+ {/* THIRDPARTY_SYSTEM_CONNECTOR */}
+ {detailForm.type === 'THIRDPARTY_SYSTEM_CONNECTOR' && (
+ <>
+
+
+
+
+
+ {{
+ suffix: () => t('thirdparty_api_source.millisecond')
+ }}
+
+
+
+ {
+ if (detailForm.authConfig) {
+ detailForm.authConfig.authType = value
+ }
+ }}
+ options={[
+ {
+ label: t('thirdparty_api_source.basic_auth'),
+ value: 'BASIC_AUTH'
+ },
+ {
+ label: t('thirdparty_api_source.oauth2'),
+ value: 'OAUTH2'
+ },
+ {
+ label: t('thirdparty_api_source.jwt'),
+ value: 'JWT'
+ }
+ ]}
+ />
+
+
+ {
+ if (detailForm.authConfig) {
+ detailForm.authConfig.headerPrefix = value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.header_prefix_tips'
+ )}
+ />
+
+ {detailForm.authConfig?.authType === 'BASIC_AUTH' && (
+ <>
+
+ {
+ if (detailForm.authConfig) {
+ detailForm.authConfig.basicUsername = value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.username_tips'
+ )}
+ />
+
+
+ {
+ if (detailForm.authConfig) {
+ detailForm.authConfig.basicPassword = value
+ }
+ }}
+ type='password'
+ showPasswordOn='click'
+ placeholder={t(
+ 'thirdparty_api_source.password_tips'
+ )}
+ />
+
+ >
+ )}
+ {detailForm.authConfig?.authType === 'OAUTH2' && (
+ <>
+
+ {
+ if (detailForm.authConfig) {
+ detailForm.authConfig.oauth2TokenUrl = value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.oauth2_token_url_tips'
+ )}
+ />
+
+
+ {
+ if (detailForm.authConfig) {
+ detailForm.authConfig.oauth2ClientId = value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.oauth2_client_id_tips'
+ )}
+ />
+
+
+ {
+ if (detailForm.authConfig) {
+ detailForm.authConfig.oauth2ClientSecret = value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.oauth2_client_secret_tips'
+ )}
+ />
+
+
+ {
+ if (detailForm.authConfig) {
+ detailForm.authConfig.oauth2GrantType = value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.oauth2_grant_type_tips'
+ )}
+ />
+
+
+ {
+ if (detailForm.authConfig) {
+ detailForm.authConfig.oauth2Username = value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.oauth2_username_tips'
+ )}
+ />
+
+
+ {
+ if (detailForm.authConfig) {
+ detailForm.authConfig.oauth2Password = value
+ }
+ }}
+ type='password'
+ showPasswordOn='click'
+ placeholder={t(
+ 'thirdparty_api_source.oauth2_password_tips'
+ )}
+ />
+
+ >
+ )}
+ {detailForm.authConfig?.authType === 'JWT' && (
+
+ {
+ if (detailForm.authConfig) {
+ detailForm.authConfig.jwtToken = value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.jwt_token_tips'
+ )}
+ />
+
+ )}
+ {/* additional params */}
+
+
+ {/* add button */}
+
{
+ if (!detailForm.authConfig) {
+ detailForm.authConfig = {
+ authType: 'BASIC_AUTH',
+ basicUsername: '',
+ basicPassword: '',
+ jwtToken: '',
+ oauth2TokenUrl: '',
+ oauth2ClientId: '',
+ oauth2ClientSecret: '',
+ oauth2GrantType: '',
+ oauth2Username: '',
+ oauth2Password: '',
+ headerPrefix: 'Basic',
+ authMappings: []
+ }
+ }
+ if (!detailForm.authConfig.authMappings) {
+ detailForm.authConfig.authMappings = []
+ }
+ detailForm.authConfig.authMappings.push({
+ key: '',
+ value: ''
+ })
+ }}
+ style={{ marginBottom: '10px' }}
+ >
+ {t('thirdparty_api_source.add_param')}
+
+
+ {/* param list */}
+ {detailForm.authConfig?.authMappings &&
+ detailForm.authConfig.authMappings.map(
+ (
+ param: { key: string; value: string },
+ index: number
+ ) => (
+
+
+ (param.key = value)
+ }
+ placeholder={t('thirdparty_api_source.key')}
+ style={{ width: '40%' }}
+ />
+
+ (param.value = value)
+ }
+ placeholder={t('thirdparty_api_source.value')}
+ style={{ width: '40%', marginLeft: '10px' }}
+ />
+ {
+ detailForm.authConfig?.authMappings?.splice(
+ index,
+ 1
+ )
+ }}
+ style={{ width: '20%', marginLeft: '10px' }}
+ size='small'
+ >
+ {t('thirdparty_api_source.delete')}
+
+
+ )
+ )}
+
+
+
+
+ {
+ if (detailForm.selectInterface) {
+ detailForm.selectInterface.url = value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.input_interface_tips'
+ )}
+ style={{ flex: 1 }}
+ />
+ {
+ if (detailForm.selectInterface) {
+ detailForm.selectInterface.method = value
+ }
+ }}
+ options={[
+ {
+ label: t('thirdparty_api_source.get'),
+ value: 'GET'
+ },
+ {
+ label: t('thirdparty_api_source.post'),
+ value: 'POST'
+ },
+ {
+ label: t('thirdparty_api_source.put'),
+ value: 'PUT'
+ }
+ ]}
+ style={{ width: '120px', marginLeft: '10px' }}
+ />
+
+
+
+
+ {/* add Button*/}
+
{
+ if (!detailForm.selectInterface) {
+ detailForm.selectInterface = {
+ url: '',
+ method: 'GET',
+ parameters: [],
+ body: '',
+ responseParameters: []
+ }
+ }
+ if (!detailForm.selectInterface.parameters) {
+ detailForm.selectInterface.parameters = []
+ }
+ detailForm.selectInterface.parameters.push({
+ paramName: '',
+ paramValue: '',
+ location: 'HEADER'
+ })
+ }}
+ style={{ marginBottom: '10px' }}
+ >
+ {t('thirdparty_api_source.add_param')}
+
+
+ {/* parameter list */}
+ {detailForm.selectInterface?.parameters &&
+ detailForm.selectInterface.parameters.map(
+ (
+ param: {
+ paramName: string
+ paramValue: string
+ location: string
+ },
+ index: number
+ ) => (
+
+
+ (param.location = value)
+ }
+ options={[
+ { label: 'Header', value: 'HEADER' },
+ { label: 'Param', value: 'PARAM' }
+ ]}
+ placeholder={t(
+ 'thirdparty_api_source.param_location_tips'
+ )}
+ style={{ width: '120px' }}
+ />
+
+ (param.paramName = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.param_name_tips'
+ )}
+ style={{ flex: 1, marginLeft: '10px' }}
+ />
+
+ (param.paramValue = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.param_value_tips'
+ )}
+ style={{ flex: 1, marginLeft: '10px' }}
+ />
+ {
+ detailForm.selectInterface?.parameters?.splice(
+ index,
+ 1
+ )
+ }}
+ style={{ marginLeft: '10px' }}
+ >
+ {t('thirdparty_api_source.delete')}
+
+
+ )
+ )}
+
+
+ {(detailForm.selectInterface?.method === 'POST' ||
+ detailForm.selectInterface?.method === 'PUT') && (
+
+ {
+ if (detailForm.selectInterface) {
+ detailForm.selectInterface.body = value
+ }
+ }}
+ type='textarea'
+ autosize={{
+ minRows: 4,
+ maxRows: 10
+ }}
+ placeholder='请输入JSON格式的请求体'
+ />
+
+ )}
+
+
+ {/* add Button */}
+
{
+ if (!detailForm.selectInterface) {
+ detailForm.selectInterface = {
+ url: '',
+ method: 'GET',
+ parameters: [],
+ body: '',
+ responseParameters: []
+ }
+ }
+ if (
+ !detailForm.selectInterface.responseParameters
+ ) {
+ detailForm.selectInterface.responseParameters = []
+ }
+ detailForm.selectInterface.responseParameters.push({
+ key: '',
+ jsonPath: '',
+ disabled: false
+ })
+ }}
+ style={{ marginBottom: '10px' }}
+ >
+ {t('thirdparty_api_source.add_extract_field')}
+
+
+ {/* responseParameters */}
+ {detailForm.selectInterface?.responseParameters &&
+ detailForm.selectInterface.responseParameters.map(
+ (
+ param: {
+ key: string
+ jsonPath: string
+ disabled?: boolean
+ },
+ index: number
+ ) => (
+
+
+ (param.key = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.extract_field'
+ )}
+ style={{ flex: 1 }}
+ disabled={param.disabled}
+ />
+
+ (param.jsonPath = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.json_path_list'
+ )}
+ style={{ flex: 1, marginLeft: '10px' }}
+ />
+ {
+ detailForm.selectInterface?.responseParameters?.splice(
+ index,
+ 1
+ )
+ }}
+ style={{ marginLeft: '10px' }}
+ >
+ {t('thirdparty_api_source.delete')}
+
+
+ )
+ )}
+
+
+
+
+ {
+ if (detailForm.submitInterface) {
+ detailForm.submitInterface.url = value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.submit_interface_tips'
+ )}
+ style={{ flex: 1 }}
+ />
+ {
+ if (detailForm.submitInterface) {
+ detailForm.submitInterface.method = value
+ }
+ }}
+ options={[
+ {
+ label: t('thirdparty_api_source.get'),
+ value: 'GET'
+ },
+ {
+ label: t('thirdparty_api_source.post'),
+ value: 'POST'
+ },
+ {
+ label: t('thirdparty_api_source.put'),
+ value: 'PUT'
+ }
+ ]}
+ style={{ width: '120px', marginLeft: '10px' }}
+ />
+
+
+
+
+ {/* add button */}
+
{
+ if (!detailForm.submitInterface) {
+ detailForm.submitInterface = {
+ url: '',
+ method: 'POST',
+ parameters: [],
+ body: '',
+ responseParameters: []
+ }
+ }
+ if (!detailForm.submitInterface.parameters) {
+ detailForm.submitInterface.parameters = []
+ }
+ detailForm.submitInterface.parameters.push({
+ paramName: '',
+ paramValue: '',
+ location: 'HEADER'
+ })
+ }}
+ style={{ marginBottom: '10px' }}
+ >
+ {t('thirdparty_api_source.add_param')}
+
+
+ {/* parameter list */}
+ {detailForm.submitInterface?.parameters &&
+ detailForm.submitInterface.parameters.map(
+ (
+ param: {
+ paramName: string
+ paramValue: string
+ location: string
+ },
+ index: number
+ ) => (
+
+
+ (param.location = value)
+ }
+ options={[
+ { label: 'Header', value: 'HEADER' },
+ { label: 'Param', value: 'PARAM' }
+ ]}
+ placeholder={t(
+ 'thirdparty_api_source.param_location_tips'
+ )}
+ style={{ width: '120px' }}
+ />
+
+ (param.paramName = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.param_name_tips'
+ )}
+ style={{ flex: 1, marginLeft: '10px' }}
+ />
+
+ (param.paramValue = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.param_value_tips'
+ )}
+ style={{ flex: 1, marginLeft: '10px' }}
+ />
+ {
+ detailForm.submitInterface?.parameters?.splice(
+ index,
+ 1
+ )
+ }}
+ style={{ marginLeft: '10px' }}
+ >
+ {t('thirdparty_api_source.delete')}
+
+
+ )
+ )}
+
+
+ {(detailForm.submitInterface?.method === 'POST' ||
+ detailForm.submitInterface?.method === 'PUT') && (
+
+ {
+ if (detailForm.submitInterface) {
+ detailForm.submitInterface.body = value
+ }
+ }}
+ type='textarea'
+ autosize={{
+ minRows: 4,
+ maxRows: 10
+ }}
+ placeholder='请输入JSON格式的请求体'
+ />
+
+ )}
+
+
+ {/* add button */}
+
{
+ if (!detailForm.submitInterface) {
+ detailForm.submitInterface = {
+ url: '',
+ method: 'POST',
+ parameters: [],
+ body: '',
+ responseParameters: []
+ }
+ }
+ if (
+ !detailForm.submitInterface.responseParameters
+ ) {
+ detailForm.submitInterface.responseParameters = []
+ }
+ detailForm.submitInterface.responseParameters.push({
+ key: '',
+ jsonPath: '',
+ disabled: false
+ })
+ }}
+ style={{ marginBottom: '10px' }}
+ >
+ {t('thirdparty_api_source.add_extract_field')}
+
+
+ {/* param list */}
+ {detailForm.submitInterface?.responseParameters &&
+ detailForm.submitInterface.responseParameters.map(
+ (
+ param: {
+ key: string
+ jsonPath: string
+ disabled?: boolean
+ },
+ index: number
+ ) => (
+
+
+ (param.key = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.extract_field'
+ )}
+ style={{ width: '180px' }}
+ disabled={param.disabled}
+ />
+
+ (param.jsonPath = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.json_path'
+ )}
+ style={{ width: '180px' }}
+ disabled={param.disabled}
+ />
+ {
+ detailForm.submitInterface?.responseParameters?.splice(
+ index,
+ 1
+ )
+ }}
+ >
+ {t('thirdparty_api_source.delete')}
+
+
+ )
+ )}
+
+
+
+
+ {
+ if (detailForm.pollStatusInterface) {
+ detailForm.pollStatusInterface.url = value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.query_interface_tips'
+ )}
+ style={{ flex: 1 }}
+ />
+ {
+ if (detailForm.pollStatusInterface) {
+ detailForm.pollStatusInterface.method = value
+ }
+ }}
+ options={[
+ {
+ label: t('thirdparty_api_source.get'),
+ value: 'GET'
+ },
+ {
+ label: t('thirdparty_api_source.post'),
+ value: 'POST'
+ },
+ {
+ label: t('thirdparty_api_source.put'),
+ value: 'PUT'
+ }
+ ]}
+ style={{ width: '120px', marginLeft: '10px' }}
+ />
+
+
+
+
+ {/* add button */}
+
{
+ if (!detailForm.pollStatusInterface) {
+ detailForm.pollStatusInterface = {
+ url: '',
+ method: 'GET',
+ parameters: [],
+ body: '',
+ pollingSuccessConfig: {
+ successField: '',
+ successValue: ''
+ },
+ pollingFailureConfig: {
+ failureField: '',
+ failureValue: ''
+ },
+ responseParameters: []
+ }
+ }
+ if (!detailForm.pollStatusInterface.parameters) {
+ detailForm.pollStatusInterface.parameters = []
+ }
+ detailForm.pollStatusInterface.parameters.push({
+ paramName: '',
+ paramValue: '',
+ location: 'HEADER'
+ })
+ }}
+ style={{ marginBottom: '10px' }}
+ >
+ {t('thirdparty_api_source.add_param')}
+
+
+ {/* param list */}
+ {detailForm.pollStatusInterface?.parameters &&
+ detailForm.pollStatusInterface.parameters.map(
+ (
+ param: {
+ paramName: string
+ paramValue: string
+ location: string
+ },
+ index: number
+ ) => (
+
+
+ (param.location = value)
+ }
+ options={[
+ { label: 'Header', value: 'HEADER' },
+ { label: 'Param', value: 'PARAM' }
+ ]}
+ placeholder={t(
+ 'thirdparty_api_source.param_location_tips'
+ )}
+ style={{ width: '120px' }}
+ />
+
+ (param.paramName = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.param_name_tips'
+ )}
+ style={{ flex: 1, marginLeft: '10px' }}
+ />
+
+ (param.paramValue = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.param_value_tips'
+ )}
+ style={{ flex: 1, marginLeft: '10px' }}
+ />
+ {
+ detailForm.pollStatusInterface?.parameters?.splice(
+ index,
+ 1
+ )
+ }}
+ style={{ marginLeft: '10px' }}
+ >
+ {t('thirdparty_api_source.delete')}
+
+
+ )
+ )}
+
+
+ {(detailForm.pollStatusInterface?.method === 'POST' ||
+ detailForm.pollStatusInterface?.method === 'PUT') && (
+
+ {
+ if (detailForm.pollStatusInterface) {
+ detailForm.pollStatusInterface.body = value
+ }
+ }}
+ type='textarea'
+ autosize={{
+ minRows: 4,
+ maxRows: 10
+ }}
+ placeholder='请输入JSON格式的请求体'
+ />
+
+ )}
+
+
+ {/* add button */}
+
{
+ if (!detailForm.pollStatusInterface) {
+ detailForm.pollStatusInterface = {
+ url: '',
+ method: 'GET',
+ parameters: [],
+ body: '',
+ pollingSuccessConfig: {
+ successField: '',
+ successValue: ''
+ },
+ pollingFailureConfig: {
+ failureField: '',
+ failureValue: ''
+ },
+ responseParameters: []
+ }
+ }
+ if (
+ !detailForm.pollStatusInterface.responseParameters
+ ) {
+ detailForm.pollStatusInterface.responseParameters =
+ []
+ }
+ detailForm.pollStatusInterface.responseParameters.push(
+ { key: '', jsonPath: '', disabled: false }
+ )
+ }}
+ style={{ marginBottom: '10px' }}
+ >
+ {t('thirdparty_api_source.add_extract_field')}
+
+
+ {/* param list */}
+ {detailForm.pollStatusInterface?.responseParameters &&
+ detailForm.pollStatusInterface.responseParameters.map(
+ (
+ param: {
+ key: string
+ jsonPath: string
+ disabled?: boolean
+ },
+ index: number
+ ) => (
+
+
+ (param.key = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.extract_field'
+ )}
+ style={{ flex: 1 }}
+ disabled={param.disabled}
+ />
+
+ (param.jsonPath = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.json_path'
+ )}
+ style={{ flex: 1, marginLeft: '10px' }}
+ />
+ {
+ detailForm.pollStatusInterface?.responseParameters?.splice(
+ index,
+ 1
+ )
+ }}
+ style={{ marginLeft: '10px' }}
+ >
+ {t('thirdparty_api_source.delete')}
+
+
+ )
+ )}
+
+
+
+
+ {
+ if (
+ detailForm.pollStatusInterface
+ ?.pollingSuccessConfig
+ ) {
+ detailForm.pollStatusInterface.pollingSuccessConfig.successField =
+ value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.success_field_tips'
+ )}
+ style={{ flex: 1 }}
+ />
+ {
+ if (
+ detailForm.pollStatusInterface
+ ?.pollingSuccessConfig
+ ) {
+ detailForm.pollStatusInterface.pollingSuccessConfig.successValue =
+ value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.success_value_tips'
+ )}
+ style={{ flex: 1, marginLeft: '10px' }}
+ />
+
+
+
+
+ {
+ if (
+ detailForm.pollStatusInterface
+ ?.pollingFailureConfig
+ ) {
+ detailForm.pollStatusInterface.pollingFailureConfig.failureField =
+ value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.failure_field_tips'
+ )}
+ style={{ flex: 1 }}
+ />
+ {
+ if (
+ detailForm.pollStatusInterface
+ ?.pollingFailureConfig
+ ) {
+ detailForm.pollStatusInterface.pollingFailureConfig.failureValue =
+ value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.failure_value_tips'
+ )}
+ style={{ flex: 1, marginLeft: '10px' }}
+ />
+
+
+
+
+ {
+ if (detailForm.stopInterface) {
+ detailForm.stopInterface.url = value
+ }
+ }}
+ placeholder={t(
+ 'thirdparty_api_source.stop_interface_tips'
+ )}
+ style={{ flex: 1 }}
+ />
+ {
+ if (detailForm.stopInterface) {
+ detailForm.stopInterface.method = value
+ }
+ }}
+ options={[
+ {
+ label: t('thirdparty_api_source.get'),
+ value: 'GET'
+ },
+ {
+ label: t('thirdparty_api_source.post'),
+ value: 'POST'
+ },
+ {
+ label: t('thirdparty_api_source.put'),
+ value: 'PUT'
+ }
+ ]}
+ style={{ width: '120px', marginLeft: '10px' }}
+ />
+
+
+
+
+ {/* add button */}
+
{
+ if (!detailForm.stopInterface) {
+ detailForm.stopInterface = {
+ url: '',
+ method: 'POST',
+ parameters: [],
+ body: '',
+ responseParameters: []
+ }
+ }
+ if (!detailForm.stopInterface.parameters) {
+ detailForm.stopInterface.parameters = []
+ }
+ detailForm.stopInterface.parameters.push({
+ paramName: '',
+ paramValue: '',
+ location: 'HEADER'
+ })
+ }}
+ style={{ marginBottom: '10px' }}
+ >
+ {t('thirdparty_api_source.add_param')}
+
+
+ {/* param list */}
+ {detailForm.stopInterface?.parameters &&
+ detailForm.stopInterface.parameters.map(
+ (
+ param: {
+ paramName: string
+ paramValue: string
+ location: string
+ },
+ index: number
+ ) => (
+
+
+ (param.location = value)
+ }
+ options={[
+ { label: 'Header', value: 'HEADER' },
+ { label: 'Param', value: 'PARAM' }
+ ]}
+ placeholder={t(
+ 'thirdparty_api_source.param_location_tips'
+ )}
+ style={{ width: '120px' }}
+ />
+
+ (param.paramName = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.param_name_tips'
+ )}
+ style={{ flex: 1, marginLeft: '10px' }}
+ />
+
+ (param.paramValue = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.param_value_tips'
+ )}
+ style={{ flex: 1, marginLeft: '10px' }}
+ />
+ {
+ detailForm.stopInterface?.parameters?.splice(
+ index,
+ 1
+ )
+ }}
+ style={{ marginLeft: '10px' }}
+ >
+ {t('thirdparty_api_source.delete')}
+
+
+ )
+ )}
+
+
+ {(detailForm.stopInterface?.method === 'POST' ||
+ detailForm.stopInterface?.method === 'PUT') && (
+
+ {
+ if (detailForm.stopInterface) {
+ detailForm.stopInterface.body = value
+ }
+ }}
+ type='textarea'
+ autosize={{
+ minRows: 4,
+ maxRows: 10
+ }}
+ placeholder='请输入JSON格式的请求体'
+ />
+
+ )}
+
+
+ {/* add button */}
+
{
+ if (!detailForm.stopInterface) {
+ detailForm.stopInterface = {
+ url: '',
+ method: 'POST',
+ parameters: [],
+ body: '',
+ responseParameters: []
+ }
+ }
+ if (!detailForm.stopInterface.responseParameters) {
+ detailForm.stopInterface.responseParameters = []
+ }
+ detailForm.stopInterface.responseParameters.push({
+ key: '',
+ jsonPath: '',
+ disabled: false
+ })
+ }}
+ style={{ marginBottom: '10px' }}
+ >
+ {t('thirdparty_api_source.add_extract_field')}
+
+
+ {/* param list */}
+ {detailForm.stopInterface?.responseParameters &&
+ detailForm.stopInterface.responseParameters.map(
+ (
+ param: {
+ key: string
+ jsonPath: string
+ disabled?: boolean
+ },
+ index: number
+ ) => (
+
+
+ (param.key = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.extract_field'
+ )}
+ style={{ flex: 1 }}
+ disabled={param.disabled}
+ />
+
+ (param.jsonPath = value)
+ }
+ placeholder={t(
+ 'thirdparty_api_source.json_path'
+ )}
+ style={{ flex: 1, marginLeft: '10px' }}
+ />
+ {
+ detailForm.stopInterface?.responseParameters?.splice(
+ index,
+ 1
+ )
+ }}
+ style={{ marginLeft: '10px' }}
+ >
+ {t('thirdparty_api_source.delete')}
+
+
+ )
+ )}
+
+
+ >
+ )}
),
diff --git a/dolphinscheduler-ui/src/views/datasource/list/types.ts b/dolphinscheduler-ui/src/views/datasource/list/types.ts
index a3dc8b3c40e9..8dbc7c9cfceb 100644
--- a/dolphinscheduler-ui/src/views/datasource/list/types.ts
+++ b/dolphinscheduler-ui/src/views/datasource/list/types.ts
@@ -22,8 +22,72 @@ import type {
import type { TableColumns } from 'naive-ui/es/data-table/src/interface'
import type { SelectBaseOption } from 'naive-ui/es/select/src/interface'
+// THIRDPARTY_SYSTEM_CONNECTOR
+interface AuthMapping {
+ key: string
+ value: string
+}
+
+interface AuthConfig {
+ authType: string
+ basicUsername?: string
+ basicPassword?: string
+ jwtToken?: string
+ oauth2TokenUrl?: string
+ oauth2ClientId?: string
+ oauth2ClientSecret?: string
+ oauth2GrantType?: string
+ oauth2Username?: string
+ oauth2Password?: string
+ headerPrefix?: string
+ authMappings?: AuthMapping[]
+}
+
+interface InterfaceParameter {
+ paramName: string
+ paramValue: string
+ location: string
+}
+
+interface ResponseParameter {
+ key: string
+ jsonPath: string
+ disabled?: boolean
+}
+
+interface InterfaceConfig {
+ url: string
+ method: string
+ parameters: InterfaceParameter[]
+ body: string
+ responseParameters?: ResponseParameter[]
+}
+
+interface PollingSuccessConfig {
+ successField: string
+ successValue: string
+}
+
+interface PollingFailureConfig {
+ failureField: string
+ failureValue: string
+}
+
+interface PollStatusInterfaceConfig extends InterfaceConfig {
+ pollingSuccessConfig: PollingSuccessConfig
+ pollingFailureConfig: PollingFailureConfig
+}
+
interface IDataSourceDetail extends Omit {
other?: string
+ // THIRDPARTY_SYSTEM_CONNECTOR
+ serviceAddress?: string
+ interfaceTimeout?: number
+ authConfig?: AuthConfig
+ selectInterface?: InterfaceConfig
+ submitInterface?: InterfaceConfig
+ pollStatusInterface?: PollStatusInterfaceConfig
+ stopInterface?: InterfaceConfig
}
interface IDataBaseOption extends SelectBaseOption {
diff --git a/dolphinscheduler-ui/src/views/datasource/list/use-detail.ts b/dolphinscheduler-ui/src/views/datasource/list/use-detail.ts
index 3f533d00e719..9e66478485b3 100644
--- a/dolphinscheduler-ui/src/views/datasource/list/use-detail.ts
+++ b/dolphinscheduler-ui/src/views/datasource/list/use-detail.ts
@@ -38,10 +38,171 @@ export function useDetail(getFieldsValue: Function) {
const formatParams = (): IDataSource => {
const values = getFieldsValue()
- return {
+ const params: IDataSource = {
...values,
other: values.other ? JSON.parse(values.other) : null
}
+
+ if (values.type === 'THIRDPARTY_SYSTEM_CONNECTOR') {
+ if (params.authConfig?.authMappings) {
+ params.authConfig.authMappings =
+ params.authConfig?.authMappings?.filter(
+ (mapping: { key: string; value: string }) =>
+ mapping.key || mapping.value
+ )
+ }
+
+ if (!params.selectInterface) {
+ params.selectInterface = {
+ url: '',
+ method: 'GET',
+ parameters: [],
+ body: '',
+ responseParameters: [
+ { key: 'id', jsonPath: '' },
+ { key: 'name', jsonPath: '' }
+ ]
+ }
+ } else {
+ if (params.selectInterface.parameters) {
+ params.selectInterface.parameters =
+ params.selectInterface.parameters.filter(
+ (param: { paramName: string; paramValue: string }) =>
+ param.paramName || param.paramValue
+ )
+ }
+
+ if (params.selectInterface.responseParameters) {
+ params.selectInterface.responseParameters =
+ params.selectInterface.responseParameters.filter(
+ (param: { key: string; jsonPath: string }) =>
+ param.key || param.jsonPath
+ )
+ }
+ }
+
+ if (!params.submitInterface) {
+ params.submitInterface = {
+ url: '',
+ method: 'POST',
+ parameters: [],
+ body: '',
+ responseParameters: [{ key: 'taskInstanceId', jsonPath: '' }]
+ }
+ } else {
+ if (params.submitInterface.parameters) {
+ params.submitInterface.parameters =
+ params.submitInterface.parameters.filter(
+ (param: { paramName: string; paramValue: string }) =>
+ param.paramName || param.paramValue
+ )
+ }
+
+ if (params.submitInterface.responseParameters) {
+ params.submitInterface.responseParameters =
+ params.submitInterface.responseParameters.filter(
+ (param: { key: string; jsonPath: string }) =>
+ param.key || param.jsonPath
+ )
+ }
+ }
+
+ if (!params.pollStatusInterface) {
+ params.pollStatusInterface = {
+ url: '',
+ method: 'GET',
+ parameters: [],
+ body: '',
+ pollingSuccessConfig: {
+ successField: '',
+ successValue: ''
+ },
+ pollingFailureConfig: {
+ failureField: '',
+ failureValue: ''
+ },
+ responseParameters: []
+ }
+ } else {
+ if (params.pollStatusInterface.parameters) {
+ params.pollStatusInterface.parameters =
+ params.pollStatusInterface.parameters.filter(
+ (param: { paramName: string; paramValue: string }) =>
+ param.paramName || param.paramValue
+ )
+ }
+
+ if (!params.pollStatusInterface.pollingSuccessConfig) {
+ params.pollStatusInterface.pollingSuccessConfig = {
+ successField: '',
+ successValue: ''
+ }
+ }
+
+ if (!params.pollStatusInterface.pollingFailureConfig) {
+ params.pollStatusInterface.pollingFailureConfig = {
+ failureField: '',
+ failureValue: ''
+ }
+ }
+
+ if (params.pollStatusInterface.responseParameters) {
+ params.pollStatusInterface.responseParameters =
+ params.pollStatusInterface.responseParameters.filter(
+ (param: { key: string; jsonPath: string }) =>
+ param.key || param.jsonPath
+ )
+ }
+ }
+
+ if (!params.stopInterface) {
+ params.stopInterface = {
+ url: '',
+ method: 'POST',
+ parameters: [],
+ body: '',
+ responseParameters: []
+ }
+ } else {
+ if (params.stopInterface.parameters) {
+ params.stopInterface.parameters =
+ params.stopInterface.parameters.filter(
+ (param: { paramName: string; paramValue: string }) =>
+ param.paramName || param.paramValue
+ )
+ }
+
+ if (params.stopInterface.responseParameters) {
+ params.stopInterface.responseParameters =
+ params.stopInterface.responseParameters.filter(
+ (param: { key: string; jsonPath: string }) =>
+ param.key || param.jsonPath
+ )
+ }
+ }
+
+ if (!params.authConfig) {
+ params.authConfig = {
+ authType: 'BASIC_AUTH',
+ basicUsername: '',
+ basicPassword: '',
+ jwtToken: '',
+ oauth2TokenUrl: '',
+ oauth2ClientId: '',
+ oauth2ClientSecret: '',
+ oauth2GrantType: '',
+ oauth2Username: '',
+ oauth2Password: '',
+ headerPrefix: 'Basic',
+ authMappings: []
+ }
+ }
+
+ delete params.userName
+ delete params.password
+ }
+
+ return params
}
const queryById = async (id: number) => {
diff --git a/dolphinscheduler-ui/src/views/datasource/list/use-form.ts b/dolphinscheduler-ui/src/views/datasource/list/use-form.ts
index 9701c79381a8..4ead40b0fff5 100644
--- a/dolphinscheduler-ui/src/views/datasource/list/use-form.ts
+++ b/dolphinscheduler-ui/src/views/datasource/list/use-form.ts
@@ -51,7 +51,63 @@ export function useForm(id?: number) {
endpoint: '',
MSIClientId: '',
dbUser: '',
- datawarehouse: ''
+ datawarehouse: '',
+ // THIRDPARTY_SYSTEM_CONNECTOR
+ serviceAddress: 'http://',
+ interfaceTimeout: 120000,
+ authConfig: {
+ authType: 'BASIC_AUTH',
+ basicUsername: '',
+ basicPassword: '',
+ jwtToken: '',
+ oauth2TokenUrl: '',
+ oauth2ClientId: '',
+ oauth2ClientSecret: '',
+ oauth2GrantType: '',
+ oauth2Username: '',
+ oauth2Password: '',
+ headerPrefix: 'Basic',
+ authMappings: [] as { key: string; value: string }[]
+ },
+ selectInterface: {
+ url: '',
+ method: 'GET',
+ parameters: [],
+ body: '',
+ responseParameters: [
+ { key: 'id', jsonPath: '' },
+ { key: 'name', jsonPath: '' }
+ ]
+ },
+ submitInterface: {
+ url: '',
+ method: 'POST',
+ parameters: [],
+ body: '',
+ responseParameters: [{ key: 'taskInstanceId', jsonPath: '' }]
+ },
+ pollStatusInterface: {
+ url: '',
+ method: 'GET',
+ parameters: [],
+ body: '',
+ responseParameters: [],
+ pollingSuccessConfig: {
+ successField: '',
+ successValue: ''
+ },
+ pollingFailureConfig: {
+ failureField: '',
+ failureValue: ''
+ }
+ },
+ stopInterface: {
+ url: '',
+ method: 'POST',
+ parameters: [],
+ body: '',
+ responseParameters: []
+ }
} as IDataSourceDetail
const state = reactive({
@@ -68,13 +124,14 @@ export function useForm(id?: number) {
showMode: false,
showDataBaseName: true,
showJDBCConnectParameters: true,
- showPrivateKey: false,
+ showPublicKey: false,
showNamespace: false,
showKubeConfig: false,
showAccessKeyId: false,
showAccessKeySecret: false,
showRegionId: false,
showEndpoint: false,
+ showPrivateKey: false,
rules: {
name: {
trigger: ['input'],
@@ -126,12 +183,26 @@ export function useForm(id?: number) {
!state.detailForm.userName &&
state.detailForm.type !== 'AZURESQL' &&
state.detailForm.type !== 'K8S' &&
- state.detailForm.type !== 'ALIYUN_SERVERLESS_SPARK'
+ state.detailForm.type !== 'ALIYUN_SERVERLESS_SPARK' &&
+ state.detailForm.type !== 'THIRDPARTY_SYSTEM_CONNECTOR'
) {
return new Error(t('datasource.user_name_tips'))
}
}
},
+ password: {
+ trigger: ['input'],
+ validator() {
+ if (
+ !state.detailForm.password &&
+ state.detailForm.type !== 'K8S' &&
+ state.detailForm.type !== 'ALIYUN_SERVERLESS_SPARK' &&
+ state.detailForm.type !== 'THIRDPARTY_SYSTEM_CONNECTOR'
+ ) {
+ return new Error(t('datasource.user_password_tips'))
+ }
+ }
+ },
awsRegion: {
trigger: ['input'],
validator() {
@@ -196,15 +267,183 @@ export function useForm(id?: number) {
return new Error(t('datasource.IAM-accessKey'))
}
}
+ },
+ // THIRDPARTY_SYSTEM_CONNECTOR check rule
+ serviceAddress: {
+ trigger: ['input'],
+ validator() {
+ if (
+ state.detailForm.type === 'THIRDPARTY_SYSTEM_CONNECTOR' &&
+ !state.detailForm.serviceAddress
+ ) {
+ return new Error(
+ t('thirdparty_api_source.service_address_required')
+ )
+ }
+ }
+ },
+ 'authConfig.authType': {
+ trigger: ['change'],
+ validator() {
+ if (
+ state.detailForm.type === 'THIRDPARTY_SYSTEM_CONNECTOR' &&
+ !state.detailForm.authConfig?.authType
+ ) {
+ return new Error(t('thirdparty_api_source.auth_type_required'))
+ }
+ }
+ },
+ 'authConfig.basicUsername': {
+ trigger: ['blur'],
+ validator() {
+ if (
+ state.detailForm.type === 'THIRDPARTY_SYSTEM_CONNECTOR' &&
+ state.detailForm.authConfig?.authType === 'BASIC_AUTH' &&
+ !state.detailForm.authConfig?.basicUsername
+ ) {
+ return new Error(t('thirdparty_api_source.username_required'))
+ }
+ return true
+ }
+ },
+ 'authConfig.basicPassword': {
+ trigger: ['blur'],
+ validator() {
+ if (
+ state.detailForm.type === 'THIRDPARTY_SYSTEM_CONNECTOR' &&
+ state.detailForm.authConfig?.authType === 'BASIC_AUTH' &&
+ !state.detailForm.authConfig?.basicPassword
+ ) {
+ return new Error(t('thirdparty_api_source.password_required'))
+ }
+ return true
+ }
+ },
+ 'authConfig.oauth2TokenUrl': {
+ trigger: ['blur'],
+ validator() {
+ if (
+ state.detailForm.type === 'THIRDPARTY_SYSTEM_CONNECTOR' &&
+ state.detailForm.authConfig?.authType === 'OAUTH2' &&
+ !state.detailForm.authConfig?.oauth2TokenUrl
+ ) {
+ return new Error(
+ t('thirdparty_api_source.oauth2_token_url_required')
+ )
+ }
+ return true
+ }
+ },
+ 'authConfig.oauth2ClientId': {
+ trigger: ['blur'],
+ validator() {
+ if (
+ state.detailForm.type === 'THIRDPARTY_SYSTEM_CONNECTOR' &&
+ state.detailForm.authConfig?.authType === 'OAUTH2' &&
+ !state.detailForm.authConfig?.oauth2ClientId
+ ) {
+ return new Error(
+ t('thirdparty_api_source.oauth2_client_id_required')
+ )
+ }
+ return true
+ }
+ },
+ 'authConfig.oauth2ClientSecret': {
+ trigger: ['blur'],
+ validator() {
+ if (
+ state.detailForm.type === 'THIRDPARTY_SYSTEM_CONNECTOR' &&
+ state.detailForm.authConfig?.authType === 'OAUTH2' &&
+ !state.detailForm.authConfig?.oauth2ClientSecret
+ ) {
+ return new Error(
+ t('thirdparty_api_source.oauth2_client_secret_required')
+ )
+ }
+ return true
+ }
+ },
+ 'authConfig.oauth2GrantType': {
+ trigger: ['blur'],
+ validator() {
+ if (
+ state.detailForm.type === 'THIRDPARTY_SYSTEM_CONNECTOR' &&
+ state.detailForm.authConfig?.authType === 'OAUTH2' &&
+ !state.detailForm.authConfig?.oauth2GrantType
+ ) {
+ return new Error(
+ t('thirdparty_api_source.oauth2_grant_type_required')
+ )
+ }
+ return true
+ }
+ },
+ 'authConfig.jwtToken': {
+ trigger: ['blur'],
+ validator() {
+ if (
+ state.detailForm.type === 'THIRDPARTY_SYSTEM_CONNECTOR' &&
+ state.detailForm.authConfig?.authType === 'JWT' &&
+ !state.detailForm.authConfig?.jwtToken
+ ) {
+ return new Error(t('thirdparty_api_source.jwt_token_required'))
+ }
+ return true
+ }
+ },
+ 'selectInterface.url': {
+ trigger: ['blur', 'change'],
+ validator() {
+ if (
+ state.detailForm.type === 'THIRDPARTY_SYSTEM_CONNECTOR' &&
+ !state.detailForm.selectInterface?.url
+ ) {
+ return new Error(
+ t('thirdparty_api_source.input_interface_url_required')
+ )
+ }
+ }
+ },
+ 'submitInterface.url': {
+ trigger: ['blur', 'change'],
+ validator() {
+ if (
+ state.detailForm.type === 'THIRDPARTY_SYSTEM_CONNECTOR' &&
+ !state.detailForm.submitInterface?.url
+ ) {
+ return new Error(
+ t('thirdparty_api_source.submit_interface_url_required')
+ )
+ }
+ }
+ },
+ 'pollStatusInterface.url': {
+ trigger: ['blur', 'change'],
+ validator() {
+ if (
+ state.detailForm.type === 'THIRDPARTY_SYSTEM_CONNECTOR' &&
+ !state.detailForm.pollStatusInterface?.url
+ ) {
+ return new Error(
+ t('thirdparty_api_source.query_interface_url_required')
+ )
+ }
+ }
+ },
+ 'stopInterface.url': {
+ trigger: ['blur', 'change'],
+ validator() {
+ if (
+ state.detailForm.type === 'THIRDPARTY_SYSTEM_CONNECTOR' &&
+ !state.detailForm.stopInterface?.url
+ ) {
+ return new Error(
+ t('thirdparty_api_source.stop_interface_url_required')
+ )
+ }
+ }
}
- // databaseUserName: {
- // trigger: ['input'],
- // validator() {
- // if (!state.detailForm.userName) {
- // return new Error(t('datasource.user_name_tips'))
- // }
- // }
- // },
} as FormRules,
modeOptions: [
{
@@ -277,18 +516,19 @@ export function useForm(id?: number) {
type === 'SAGEMAKER' ||
type === 'K8S' ||
type === 'ALIYUN_SERVERLESS_SPARK' ||
- type === 'DOLPHINDB'
+ type === 'DOLPHINDB' ||
+ type === 'THIRDPARTY_SYSTEM_CONNECTOR'
) {
state.showDataBaseName = false
state.requiredDataBase = false
state.showJDBCConnectParameters = false
- state.showPrivateKey = false
+ state.showPublicKey = false
if (type === 'DOLPHINDB') {
state.showJDBCConnectParameters = true
- state.showPrivateKey = false
+ state.showPublicKey = false
}
if (type === 'SSH') {
- state.showPrivateKey = true
+ state.showPublicKey = true
}
if (type === 'ZEPPELIN') {
state.showHost = false
@@ -300,7 +540,8 @@ export function useForm(id?: number) {
if (
type === 'SAGEMAKER' ||
type === 'K8S' ||
- type == 'ALIYUN_SERVERLESS_SPARK'
+ type == 'ALIYUN_SERVERLESS_SPARK' ||
+ type === 'THIRDPARTY_SYSTEM_CONNECTOR'
) {
state.showHost = false
state.showPort = false
@@ -323,11 +564,60 @@ export function useForm(id?: number) {
state.showRegionId = false
state.showEndpoint = false
}
+ // 处理THIRDPARTY_SYSTEM_CONNECTOR特殊字段显示
+ if (type === 'THIRDPARTY_SYSTEM_CONNECTOR') {
+ state.showHost = false
+ state.showPort = false
+ state.showJDBCConnectParameters = false
+ state.showDataBaseName = false
+ state.requiredDataBase = false
+
+ // make sure authConfig \ authMappings inited
+ if (!state.detailForm.authConfig) {
+ state.detailForm.authConfig = {
+ authType: 'BASIC_AUTH',
+ basicUsername: '',
+ basicPassword: '',
+ jwtToken: '',
+ oauth2TokenUrl: '',
+ oauth2ClientId: '',
+ oauth2ClientSecret: '',
+ oauth2GrantType: '',
+ oauth2Username: '',
+ oauth2Password: '',
+ headerPrefix: 'Basic',
+ authMappings: []
+ }
+ } else if (!state.detailForm.authConfig?.authMappings) {
+ state.detailForm.authConfig.authMappings = []
+ }
+ }
+
+ // init THIRDPARTY_SYSTEM_CONNECTOR authConfig
+ if (
+ type === 'THIRDPARTY_SYSTEM_CONNECTOR' &&
+ !state.detailForm.authConfig
+ ) {
+ state.detailForm.authConfig = {
+ authType: 'BASIC_AUTH',
+ basicUsername: '',
+ basicPassword: '',
+ jwtToken: '',
+ oauth2TokenUrl: '',
+ oauth2ClientId: '',
+ oauth2ClientSecret: '',
+ oauth2GrantType: '',
+ oauth2Username: '',
+ oauth2Password: '',
+ headerPrefix: 'Basic',
+ authMappings: []
+ }
+ }
} else {
state.showDataBaseName = true
state.requiredDataBase = true
state.showJDBCConnectParameters = true
- state.showPrivateKey = false
+ state.showPublicKey = false
state.showRestEndpoint = false
state.showNamespace = false
state.showKubeConfig = false
@@ -349,10 +639,201 @@ export function useForm(id?: number) {
}
const setFieldsValue = (values: IDataSource) => {
+ const processedValues = {
+ ...values,
+ other: values.other ? JSON.stringify(values.other) : values.other,
+ selectInterface: values.selectInterface
+ ? {
+ ...values.selectInterface,
+ parameters: values.selectInterface.parameters || [],
+ responseParameters: values.selectInterface.responseParameters || []
+ }
+ : undefined,
+ submitInterface: values.submitInterface
+ ? {
+ ...values.submitInterface,
+ parameters: values.submitInterface.parameters || [],
+ responseParameters: values.submitInterface.responseParameters || []
+ }
+ : undefined,
+ pollStatusInterface: values.pollStatusInterface
+ ? {
+ ...values.pollStatusInterface,
+ parameters: values.pollStatusInterface.parameters || [],
+ responseParameters:
+ values.pollStatusInterface.responseParameters || []
+ }
+ : undefined,
+ stopInterface: values.stopInterface
+ ? {
+ ...values.stopInterface,
+ parameters: values.stopInterface.parameters || [],
+ responseParameters: values.stopInterface.responseParameters || []
+ }
+ : undefined
+ }
+
state.detailForm = {
...state.detailForm,
- ...values,
- other: values.other ? JSON.stringify(values.other) : values.other
+ ...processedValues
+ }
+
+ // check THIRDPARTY_SYSTEM_CONNECTOR has right authConfig
+ if (values.type === 'THIRDPARTY_SYSTEM_CONNECTOR') {
+ if (!state.detailForm.authConfig) {
+ state.detailForm.authConfig = {
+ authType: 'BASIC_AUTH',
+ basicUsername: '',
+ basicPassword: '',
+ jwtToken: '',
+ oauth2TokenUrl: '',
+ oauth2ClientId: '',
+ oauth2ClientSecret: '',
+ oauth2GrantType: '',
+ oauth2Username: '',
+ oauth2Password: '',
+ headerPrefix: 'Basic',
+ authMappings: []
+ }
+ } else {
+ state.detailForm.authConfig = {
+ authType: values.authConfig?.authType || 'BASIC_AUTH',
+ basicUsername: values.authConfig?.basicUsername || '',
+ basicPassword: values.authConfig?.basicPassword || '',
+ jwtToken: values.authConfig?.jwtToken || '',
+ oauth2TokenUrl: values.authConfig?.oauth2TokenUrl || '',
+ oauth2ClientId: values.authConfig?.oauth2ClientId || '',
+ oauth2ClientSecret: values.authConfig?.oauth2ClientSecret || '',
+ oauth2GrantType: values.authConfig?.oauth2GrantType || '',
+ oauth2Username: values.authConfig?.oauth2Username || '',
+ oauth2Password: values.authConfig?.oauth2Password || '',
+ headerPrefix: values.authConfig?.headerPrefix || 'Basic',
+ authMappings: values.authConfig?.authMappings
+ ? [...values.authConfig.authMappings]
+ : []
+ }
+ }
+
+ if (!state.detailForm.selectInterface) {
+ state.detailForm.selectInterface = {
+ url: '',
+ method: 'GET',
+ parameters: [],
+ body: '',
+ responseParameters: [
+ { key: 'id', jsonPath: '' },
+ { key: 'name', jsonPath: '' }
+ ]
+ }
+ } else {
+ state.detailForm.selectInterface = {
+ url: values.selectInterface?.url || '',
+ method: values.selectInterface?.method || 'GET',
+ parameters: values.selectInterface?.parameters
+ ? [...values.selectInterface.parameters]
+ : [],
+ body: values.selectInterface?.body || '',
+ responseParameters: values.selectInterface?.responseParameters
+ ? [...values.selectInterface.responseParameters]
+ : [
+ { key: 'id', jsonPath: '' },
+ { key: 'name', jsonPath: '' }
+ ]
+ }
+ }
+
+ if (!state.detailForm.submitInterface) {
+ state.detailForm.submitInterface = {
+ url: '',
+ method: 'POST',
+ parameters: [],
+ body: '',
+ responseParameters: [{ key: 'taskInstanceId', jsonPath: '' }]
+ }
+ } else {
+ state.detailForm.submitInterface = {
+ url: values.submitInterface?.url || '',
+ method: values.submitInterface?.method || 'POST',
+ parameters: values.submitInterface?.parameters
+ ? [...values.submitInterface.parameters]
+ : [],
+ body: values.submitInterface?.body || '',
+ responseParameters: values.submitInterface?.responseParameters
+ ? [...values.submitInterface.responseParameters]
+ : [{ key: 'taskInstanceId', jsonPath: '' }]
+ }
+ }
+
+ if (!state.detailForm.pollStatusInterface) {
+ state.detailForm.pollStatusInterface = {
+ url: '',
+ method: 'GET',
+ parameters: [],
+ body: '',
+ pollingSuccessConfig: {
+ successField: '',
+ successValue: ''
+ },
+ pollingFailureConfig: {
+ failureField: '',
+ failureValue: ''
+ },
+ responseParameters: []
+ }
+ } else {
+ state.detailForm.pollStatusInterface = {
+ url: values.pollStatusInterface?.url || '',
+ method: values.pollStatusInterface?.method || 'GET',
+ parameters: values.pollStatusInterface?.parameters
+ ? [...values.pollStatusInterface.parameters]
+ : [],
+ body: values.pollStatusInterface?.body || '',
+ pollingSuccessConfig: {
+ successField:
+ values.pollStatusInterface?.pollingSuccessConfig?.successField ||
+ '',
+ successValue:
+ values.pollStatusInterface?.pollingSuccessConfig?.successValue ||
+ ''
+ },
+ pollingFailureConfig: {
+ failureField:
+ values.pollStatusInterface?.pollingFailureConfig?.failureField ||
+ '',
+ failureValue:
+ values.pollStatusInterface?.pollingFailureConfig?.failureValue ||
+ ''
+ },
+ responseParameters: values.pollStatusInterface?.responseParameters
+ ? [...values.pollStatusInterface.responseParameters]
+ : []
+ }
+ }
+
+ if (!state.detailForm.stopInterface) {
+ state.detailForm.stopInterface = {
+ url: '',
+ method: 'POST',
+ parameters: [],
+ body: '',
+ responseParameters: []
+ }
+ } else {
+ state.detailForm.stopInterface = {
+ url: values.stopInterface?.url || '',
+ method: values.stopInterface?.method || 'POST',
+ parameters: values.stopInterface?.parameters
+ ? [...values.stopInterface.parameters]
+ : [],
+ body: values.stopInterface?.body || '',
+ responseParameters: values.stopInterface?.responseParameters
+ ? [...values.stopInterface.responseParameters]
+ : []
+ }
+ }
+
+ delete state.detailForm.userName
+ delete state.detailForm.password
}
}
@@ -508,6 +989,11 @@ export const datasourceType: IDataBaseOptionKeys = {
value: 'DOLPHINDB',
label: 'DOLPHINDB',
defaultPort: 8848
+ },
+ THIRDPARTY_SYSTEM_CONNECTOR: {
+ value: 'THIRDPARTY_SYSTEM_CONNECTOR',
+ label: 'THIRDPARTY_SYSTEM_CONNECTOR',
+ defaultPort: 80
}
}
diff --git a/dolphinscheduler-ui/src/views/projects/components/dependencies/dependencies-modal.tsx b/dolphinscheduler-ui/src/views/projects/components/dependencies/dependencies-modal.tsx
index 207673a2a837..e405f720a346 100644
--- a/dolphinscheduler-ui/src/views/projects/components/dependencies/dependencies-modal.tsx
+++ b/dolphinscheduler-ui/src/views/projects/components/dependencies/dependencies-modal.tsx
@@ -59,7 +59,7 @@ export default defineComponent({
}
const cancelToHandle = () => {
- ctx.emit('update:show', showRef)
+ ctx.emit('update:show', showRef.value)
}
const renderDownstreamDependencies = () => {
diff --git a/dolphinscheduler-ui/src/views/projects/task/components/node/fields/index.ts b/dolphinscheduler-ui/src/views/projects/task/components/node/fields/index.ts
index 43c1ed66d138..946965aedf88 100644
--- a/dolphinscheduler-ui/src/views/projects/task/components/node/fields/index.ts
+++ b/dolphinscheduler-ui/src/views/projects/task/components/node/fields/index.ts
@@ -51,6 +51,7 @@ export { useMainJar } from './use-main-jar'
export { useResources } from './use-resources'
export { useTaskDefinition } from './use-task-definition'
export { useJavaTaskMainJar } from './use-java-task-main-jar'
+export { useExternalSystem } from './use-external-system'
export { useShell } from './use-shell'
export { useSpark } from './use-spark'
diff --git a/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-datasource.ts b/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-datasource.ts
index 950c7418eaa2..bfaaf8ef22cd 100644
--- a/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-datasource.ts
+++ b/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-datasource.ts
@@ -34,7 +34,9 @@ export function useDatasource(
const { t } = useI18n()
const options = ref([] as { label: string; value: string }[])
- const datasourceOptions = ref([] as { label: string; value: number }[])
+ const datasourceOptions = ref(
+ [] as { label: string; value: number | string }[]
+ )
const datasourceTypes = [
{
@@ -166,6 +168,11 @@ export function useDatasource(
id: 28,
code: 'DOLPHINDB',
disabled: false
+ },
+ {
+ id: 29,
+ code: 'THIRDPARTY_SYSTEM_CONNECTOR',
+ disabled: false
}
]
diff --git a/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-external-system.ts b/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-external-system.ts
new file mode 100644
index 000000000000..0a11ba7edd03
--- /dev/null
+++ b/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-external-system.ts
@@ -0,0 +1,146 @@
+/*
+ * 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 { ref, onMounted, nextTick, Ref } from 'vue'
+import { useI18n } from 'vue-i18n'
+import {
+ queryDataSourceList,
+ queryExternalSystemTasks
+} from '@/service/modules/data-source'
+import { find } from 'lodash'
+import type { IJsonItem } from '../types'
+import type {
+ TypeReq,
+ IDataBase as unusedIDataBase
+} from '@/service/modules/data-source/types'
+
+export function useExternalSystem(
+ model: { [field: string]: any },
+ params: {
+ externalSystemField?: string
+ taskField?: string
+ span?: Ref | number
+ } = {}
+): IJsonItem[] {
+ const { t } = useI18n()
+
+ const datasourceOptions = ref([] as { label: string; value: string }[])
+ const taskOptions = ref([] as { label: string; value: string }[])
+
+ const getDataSources = async () => {
+ try {
+ const parameters = {
+ type: 'THIRDPARTY_SYSTEM_CONNECTOR'
+ } as TypeReq
+
+ const res = await queryDataSourceList(parameters)
+ datasourceOptions.value = res.map((item: any) => ({
+ label: item.name,
+ value: String(item.id)
+ }))
+ } catch (error) {
+ // Error handling is done by the calling function
+ }
+ }
+
+ const refreshTasks = async () => {
+ const datasourceId = model[params.externalSystemField || 'datasource']
+ if (!datasourceId) return
+
+ try {
+ const res = await queryExternalSystemTasks(datasourceId)
+ taskOptions.value = res.map((item: any) => ({
+ label: item.name,
+ value: String(item.id)
+ }))
+ } catch (error) {
+ // Error handling is done by the calling function
+ }
+
+ const taskField = params.taskField || 'task'
+ if (!taskOptions.value.length && model[taskField]) model[taskField] = null
+ if (taskOptions.value.length && model[taskField]) {
+ const item = find(taskOptions.value, { value: model[taskField] })
+ if (!item) {
+ model[taskField] = null
+ }
+ }
+ }
+
+ const onChange = () => {
+ taskOptions.value = []
+ const taskField = params.taskField || 'externalTaskId'
+ model[taskField] = null
+ model.externalTaskName = ''
+ refreshTasks()
+ }
+
+ const onTaskChange = (value: string) => {
+ if (value) {
+ const taskItem = taskOptions.value.find((item) => item.value === value)
+ if (taskItem) {
+ model.externalTaskName = taskItem.label // Set the name based on the selected task
+ }
+ } else {
+ model.externalTaskName = ''
+ }
+ }
+
+ onMounted(async () => {
+ await getDataSources()
+ await nextTick()
+ refreshTasks()
+ })
+
+ return [
+ {
+ type: 'select',
+ field: params.externalSystemField || 'datasource',
+ span: params.span || 24,
+ name: t('project.node.datasource_instances'),
+ props: { 'on-update:value': onChange },
+ options: datasourceOptions,
+ validate: {
+ trigger: ['input', 'blur'],
+ required: true,
+ validator(unuse: any, value) {
+ if (!value) {
+ return new Error(t('project.node.datasource_instances_required'))
+ }
+ }
+ }
+ },
+ {
+ type: 'select',
+ field: params.taskField || 'externalTaskId',
+ span: params.span || 24,
+ name: t('project.node.external_system_tasks'),
+ props: { 'on-update:value': onTaskChange },
+ options: taskOptions,
+ validate: {
+ trigger: ['input', 'blur'],
+ required: true,
+ validator(unuse: any, value) {
+ if (!value) {
+ return new Error(
+ t('thirdparty_api_source.external_system_task_required')
+ )
+ }
+ }
+ }
+ }
+ ]
+}
diff --git a/dolphinscheduler-ui/src/views/projects/task/components/node/format-data.ts b/dolphinscheduler-ui/src/views/projects/task/components/node/format-data.ts
index 8fc67906775c..17a5f76a817f 100644
--- a/dolphinscheduler-ui/src/views/projects/task/components/node/format-data.ts
+++ b/dolphinscheduler-ui/src/views/projects/task/components/node/format-data.ts
@@ -115,6 +115,12 @@ export function formatParams(data: INodeData): {
taskParams.connectTimeout = data.connectTimeout
taskParams.socketTimeout = data.socketTimeout
}
+ if (data.taskType === 'EXTERNAL_SYSTEM') {
+ taskParams.type = data.type
+ taskParams.datasource = data.datasource
+ taskParams.externalTaskId = data.externalTaskId
+ taskParams.externalTaskName = data.externalTaskName
+ }
if (data.taskType === 'SQOOP') {
taskParams.jobType = data.isCustomTask ? 'CUSTOM' : 'TEMPLATE'
diff --git a/dolphinscheduler-ui/src/views/projects/task/components/node/tasks/index.ts b/dolphinscheduler-ui/src/views/projects/task/components/node/tasks/index.ts
index 3db6bb96ea5b..3380678cf1fa 100644
--- a/dolphinscheduler-ui/src/views/projects/task/components/node/tasks/index.ts
+++ b/dolphinscheduler-ui/src/views/projects/task/components/node/tasks/index.ts
@@ -31,6 +31,7 @@ import { useSeaTunnel } from './use-sea-tunnel'
import { useSwitch } from './use-switch'
import { useConditions } from './use-conditions'
import { useDataX } from './use-datax'
+import { useExternalSystem } from './use-external-system'
import { useDependent } from './use-dependent'
import { useEmr } from './use-emr'
import { useZeppelin } from './use-zeppelin'
@@ -80,6 +81,7 @@ export default {
SAGEMAKER: userSagemaker,
CHUNJUN: useChunjun,
FLINK_STREAM: useFlinkStream,
+ EXTERNAL_SYSTEM: useExternalSystem,
JAVA: useJava,
HIVECLI: useHiveCli,
DMS: useDms,
diff --git a/dolphinscheduler-ui/src/views/projects/task/components/node/tasks/use-external-system.ts b/dolphinscheduler-ui/src/views/projects/task/components/node/tasks/use-external-system.ts
new file mode 100644
index 000000000000..52a0b2b435cd
--- /dev/null
+++ b/dolphinscheduler-ui/src/views/projects/task/components/node/tasks/use-external-system.ts
@@ -0,0 +1,74 @@
+/*
+ * 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 { reactive } from 'vue'
+import * as Fields from '../fields/index'
+import type { IJsonItem, INodeData, ITaskData } from '../types'
+
+export function useExternalSystem({
+ projectCode,
+ from = 0,
+ readonly,
+ data
+}: {
+ projectCode: number
+ from?: number
+ readonly?: boolean
+ data?: ITaskData
+}) {
+ const model = reactive({
+ taskType: 'EXTERNAL_SYSTEM',
+ name: '',
+ flag: 'YES',
+ description: '',
+ timeoutFlag: false,
+ timeoutNotifyStrategy: ['WARN'],
+ timeout: 30,
+ localParams: [],
+ environmentCode: null,
+ failRetryInterval: 1,
+ failRetryTimes: 0,
+ workerGroup: 'default',
+ cpuQuota: -1,
+ memoryMax: -1,
+ delayTime: 0,
+ datasource: '',
+ type: 'THIRDPARTY_SYSTEM_CONNECTOR',
+ externalTaskId: '',
+ externalTaskName: ''
+ } as INodeData)
+
+ return {
+ json: [
+ Fields.useName(from),
+ ...Fields.useTaskDefinition({ projectCode, from, readonly, data, model }),
+ Fields.useRunFlag(),
+ Fields.useDescription(),
+ Fields.useTaskPriority(),
+ Fields.useWorkerGroup(projectCode),
+ Fields.useEnvironmentName(model, !data?.id),
+ ...Fields.useTaskGroup(model, projectCode),
+ ...Fields.useFailed(),
+ ...Fields.useResourceLimit(),
+ Fields.useDelayTime(model),
+ ...Fields.useTimeoutAlarm(model),
+ ...Fields.useExternalSystem(model),
+ Fields.usePreTasks()
+ ] as IJsonItem[],
+ model
+ }
+}
diff --git a/dolphinscheduler-ui/src/views/projects/task/components/node/types.ts b/dolphinscheduler-ui/src/views/projects/task/components/node/types.ts
index 76dcde23ee2d..e69122c507b6 100644
--- a/dolphinscheduler-ui/src/views/projects/task/components/node/types.ts
+++ b/dolphinscheduler-ui/src/views/projects/task/components/node/types.ts
@@ -302,6 +302,8 @@ interface ITaskParams {
connectTimeout?: number
socketTimeout?: number
type?: string
+ externalTaskName?: string
+ externalTaskId?: string
datasource?: string
sql?: string
sqlType?: string
diff --git a/dolphinscheduler-ui/src/views/projects/task/constants/task-type.ts b/dolphinscheduler-ui/src/views/projects/task/constants/task-type.ts
index ce0db268f4f8..79045898fcff 100644
--- a/dolphinscheduler-ui/src/views/projects/task/constants/task-type.ts
+++ b/dolphinscheduler-ui/src/views/projects/task/constants/task-type.ts
@@ -51,6 +51,7 @@ export type TaskType =
| 'DATA_FACTORY'
| 'REMOTESHELL'
| 'ALIYUN_SERVERLESS_SPARK'
+ | 'EXTERNAL_SYSTEM'
export type TaskExecuteType = 'STREAM' | 'BATCH'
@@ -155,6 +156,10 @@ export const TASK_TYPES_MAP = {
helperLinkDisable: true,
taskExecuteType: 'STREAM'
},
+ EXTERNAL_SYSTEM: {
+ alias: 'EXTERNAL_SYSTEM',
+ helperLinkDisable: true
+ },
HIVECLI: {
alias: 'HIVECLI',
helperLinkDisable: true
diff --git a/dolphinscheduler-ui/src/views/projects/workflow/components/dag/dag.module.scss b/dolphinscheduler-ui/src/views/projects/workflow/components/dag/dag.module.scss
index 809bc6fae3cf..d2b5e81f93b6 100644
--- a/dolphinscheduler-ui/src/views/projects/workflow/components/dag/dag.module.scss
+++ b/dolphinscheduler-ui/src/views/projects/workflow/components/dag/dag.module.scss
@@ -117,6 +117,9 @@ $bgLight: #ffffff;
&.icon-flink_stream {
background-image: url('/images/task-icons/flink.png');
}
+ &.icon-external_system {
+ background-image: url('/images/task-icons/external_system.png');
+ }
&.icon-mr {
background-image: url('/images/task-icons/mr.png');
}
@@ -227,6 +230,9 @@ $bgLight: #ffffff;
&.icon-flink_stream {
background-image: url('/images/task-icons/flink_hover.png');
}
+ &.icon-external_system {
+ background-image: url('/images/task-icons/external_system_hover.png');
+ }
&.icon-mr {
background-image: url('/images/task-icons/mr_hover.png');
}
diff --git a/dolphinscheduler-ui/src/views/projects/workflow/definition/tree/index.tsx b/dolphinscheduler-ui/src/views/projects/workflow/definition/tree/index.tsx
index 26d6540eb6b3..8bf9fa90f27a 100644
--- a/dolphinscheduler-ui/src/views/projects/workflow/definition/tree/index.tsx
+++ b/dolphinscheduler-ui/src/views/projects/workflow/definition/tree/index.tsx
@@ -157,6 +157,13 @@ export default defineComponent({
taskType: 'FLINK_STREAM',
color: '#d68f5b',
image: `${import.meta.env.BASE_URL}images/task-icons/flink.png`
+ },
+ {
+ taskType: 'EXTERNAL_SYSTEM',
+ color: '#d68f5b',
+ image: `${
+ import.meta.env.BASE_URL
+ }images/task-icons/external_system.png`
}
])
diff --git a/dolphinscheduler-ui/src/views/thirdparty-api-source/index.module.scss b/dolphinscheduler-ui/src/views/thirdparty-api-source/index.module.scss
new file mode 100644
index 000000000000..d32b36e059c6
--- /dev/null
+++ b/dolphinscheduler-ui/src/views/thirdparty-api-source/index.module.scss
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+.thirdparty-modal {
+ position: relative;
+ width: 700px;
+ max-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.thirdparty-modal :global(.n-modal__close),
+.thirdparty-modal :global(.n-modal-header__close),
+.thirdparty-modal :global(.n-modal-header .n-button),
+.thirdparty-modal :global(.n-modal-header .n-icon),
+.thirdparty-modal :global(.n-modal-header .n-base-icon),
+.thirdparty-modal :global(.n-modal-header .n-button--icon-only),
+.thirdparty-modal :global(.n-base-close),
+.thirdparty-modal :global(.n-base-close--absolute),
+.thirdparty-modal :global(.n-card-header__close) {
+ display: none !important;
+}
+
+.modal-content {
+ flex: 1;
+ max-height: calc(90vh - 120px);
+ overflow-y: auto;
+ overflow-x: hidden;
+ width: 100%;
+ padding: 0 16px;
+ box-sizing: border-box;
+}
+
+.param-location {
+ width: 100px;
+ min-width: 100px;
+}
+
+.param-name {
+ width: 150px;
+ min-width: 150px;
+}
+
+.param-value {
+ width: 180px;
+ min-width: 180px;
+}
+
+.method-select,
+.auth-type-select {
+ width: 120px;
+ min-width: 120px;
+}
+
+.condition-field {
+ width: calc(50% - 4px);
+ min-width: 200px;
+}
+
+.condition-value {
+ width: calc(50% - 4px);
+ min-width: 200px;
+ margin-left: 8px;
+}
+
+.submit-url {
+ flex: 1;
+ min-width: 300px;
+}
+
+.submit-method {
+ width: 120px;
+ min-width: 120px;
+}
+
+.timeout-input {
+ width: 200px;
+}
+
+.timeout-description {
+ font-size: 12px;
+ color: #999;
+ margin-top: 4px;
+ line-height: 1.4;
+}
+
+.key-input,
+.value-input {
+ width: 200px;
+ min-width: 150px;
+}
+
+.modal-footer {
+ position: sticky;
+ bottom: 0;
+ background: #fff;
+ padding-top: 16px;
+ z-index: 10;
+ display: flex;
+ justify-content: flex-end;
+ border-top: 1px solid #f0f0f0;
+ margin-top: 16px;
+ box-sizing: border-box;
+ width: 100%;
+}
+
+.modal-footer :global(.n-space) {
+ margin-right: 15px;
+ gap: 8px;
+}
+
+.modal-footer :global(.n-button) {
+ min-width: 80px;
+ height: 32px;
+ padding: 0 14px;
+}
+
+@media screen and (min-width: 1920px) {
+ .thirdparty-modal {
+ width: 700px;
+ }
+
+ .modal-content {
+ max-height: calc(90vh - 120px);
+ }
+}
\ No newline at end of file
diff --git a/dolphinscheduler-ui/src/views/thirdparty-api-source/modal.tsx b/dolphinscheduler-ui/src/views/thirdparty-api-source/modal.tsx
new file mode 100644
index 000000000000..9f7b9dfd2995
--- /dev/null
+++ b/dolphinscheduler-ui/src/views/thirdparty-api-source/modal.tsx
@@ -0,0 +1,1499 @@
+/*
+ * 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 { defineComponent, PropType, reactive, watch, computed, ref } from 'vue'
+import type { FormInst } from 'naive-ui'
+import { useI18n } from 'vue-i18n'
+import styles from './index.module.scss'
+import {
+ NModal,
+ NForm,
+ NFormItem,
+ NInput,
+ NInputNumber,
+ NButton,
+ NSpace,
+ NDivider,
+ NDynamicInput,
+ NSelect,
+ NTooltip,
+ NIcon
+} from 'naive-ui'
+import MonacoEditor from '@/components/monaco-editor'
+import { InfoCircleOutlined } from '@vicons/antd'
+
+export default defineComponent({
+ name: 'ThirdpartyApiSourceModal',
+ props: {
+ show: Boolean,
+ data: {
+ type: Object as PropType,
+ default: () => null
+ },
+ operationType: {
+ type: String as PropType<'create' | 'edit'>,
+ default: 'create'
+ }
+ },
+ emits: ['close', 'submit', 'test'],
+ setup(props, { emit }) {
+ const { t } = useI18n()
+
+ const authTypeOptions = computed(() => [
+ { label: t('thirdparty_api_source.basic_auth'), value: 'BASIC_AUTH' },
+ { label: t('thirdparty_api_source.oauth2'), value: 'OAUTH2' },
+ { label: t('thirdparty_api_source.jwt'), value: 'JWT' }
+ ])
+
+ const methodOptions = computed(() => [
+ { label: t('thirdparty_api_source.get'), value: 'GET' },
+ { label: t('thirdparty_api_source.post'), value: 'POST' },
+ { label: t('thirdparty_api_source.put'), value: 'PUT' }
+ ])
+
+ const form = reactive({
+ serviceAddress: '',
+ interfaceTimeout: 120000, // default 2 min
+ authConfig: {
+ authType: 'BASIC_AUTH',
+ basicUsername: '',
+ basicPassword: '',
+ jwtToken: '',
+ oauth2TokenUrl: '',
+ oauth2ClientId: '',
+ oauth2ClientSecret: '',
+ oauth2GrantType: '',
+ oauth2Username: '',
+ oauth2Password: '',
+ headerPrefix: 'Basic',
+ authMappings: [] as any[]
+ },
+ selectInterface: {
+ url: '',
+ method: 'GET',
+ parameters: [] as any[],
+ body: '',
+ responseParameters: [
+ { key: 'id', jsonPath: '' },
+ { key: 'name', jsonPath: '' }
+ ]
+ },
+ submitInterface: {
+ url: '',
+ method: 'POST',
+ parameters: [] as any[],
+ body: '',
+ responseParameters: [{ key: 'taskInstanceId', jsonPath: '' }]
+ },
+ pollStatusInterface: {
+ url: '',
+ method: 'GET',
+ parameters: [] as any[],
+ body: '',
+ pollingSuccessConfig: {
+ successField: '',
+ successValue: ''
+ },
+ pollingFailureConfig: {
+ failureField: '',
+ failureValue: ''
+ }
+ },
+ stopInterface: {
+ url: '',
+ method: 'POST',
+ parameters: [] as any[],
+ body: ''
+ }
+ })
+
+ // Form validation rules
+ const rules = {
+ serviceAddress: [
+ {
+ required: true,
+ message: t('thirdparty_api_source.service_address_required'),
+ trigger: 'blur'
+ }
+ ],
+ 'authConfig.authType': [
+ {
+ required: true,
+ message: t('thirdparty_api_source.auth_type_required'),
+ trigger: 'change'
+ }
+ ],
+ 'authConfig.basicUsername': [
+ {
+ validator: (rule: any, value: any) => {
+ if (form.authConfig.authType === 'BASIC_AUTH' && !value) {
+ return new Error(t('thirdparty_api_source.username_required'))
+ }
+ return true
+ },
+ trigger: 'blur'
+ }
+ ],
+ 'authConfig.basicPassword': [
+ {
+ validator: (rule: any, value: any) => {
+ if (form.authConfig.authType === 'BASIC_AUTH' && !value) {
+ return new Error(t('thirdparty_api_source.password_required'))
+ }
+ return true
+ },
+ trigger: 'blur'
+ }
+ ],
+ 'authConfig.oauth2TokenUrl': [
+ {
+ validator: (rule: any, value: any) => {
+ if (form.authConfig.authType === 'OAUTH2' && !value) {
+ return new Error(
+ t('thirdparty_api_source.oauth2_token_url_required')
+ )
+ }
+ return true
+ },
+ trigger: 'blur'
+ }
+ ],
+ 'authConfig.oauth2ClientId': [
+ {
+ validator: (rule: any, value: any) => {
+ if (form.authConfig.authType === 'OAUTH2' && !value) {
+ return new Error(
+ t('thirdparty_api_source.oauth2_client_id_required')
+ )
+ }
+ return true
+ },
+ trigger: 'blur'
+ }
+ ],
+ 'authConfig.oauth2ClientSecret': [
+ {
+ validator: (rule: any, value: any) => {
+ if (form.authConfig.authType === 'OAUTH2' && !value) {
+ return new Error(
+ t('thirdparty_api_source.oauth2_client_secret_required')
+ )
+ }
+ return true
+ },
+ trigger: 'blur'
+ }
+ ],
+ 'authConfig.oauth2GrantType': [
+ {
+ validator: (rule: any, value: any) => {
+ if (form.authConfig.authType === 'OAUTH2' && !value) {
+ return new Error(
+ t('thirdparty_api_source.oauth2_grant_type_required')
+ )
+ }
+ return true
+ },
+ trigger: 'blur'
+ }
+ ],
+ 'authConfig.jwtToken': [
+ {
+ validator: (rule: any, value: any) => {
+ if (form.authConfig.authType === 'JWT' && !value) {
+ return new Error(t('thirdparty_api_source.jwt_token_required'))
+ }
+ return true
+ },
+ trigger: 'blur'
+ }
+ ],
+ 'selectInterface.url': [
+ {
+ required: true,
+ message: t('thirdparty_api_source.input_interface_url_required'),
+ trigger: ['blur', 'change']
+ }
+ ],
+ 'submitInterface.url': [
+ {
+ required: true,
+ message: t('thirdparty_api_source.submit_interface_url_required'),
+ trigger: ['blur', 'change']
+ }
+ ],
+ 'pollStatusInterface.url': [
+ {
+ required: true,
+ message: t('thirdparty_api_source.query_interface_url_required'),
+ trigger: ['blur', 'change']
+ }
+ ],
+ 'stopInterface.url': [
+ {
+ required: true,
+ message: t('thirdparty_api_source.stop_interface_url_required'),
+ trigger: ['blur', 'change']
+ }
+ ],
+ 'pollStatusInterface.pollingSuccessConfig': [
+ {
+ validator: (rule: any, value: any) => {
+ if (!value.successField || !value.successValue) {
+ return new Error(
+ t('thirdparty_api_source.success_condition_required')
+ )
+ }
+ return true
+ },
+ trigger: ['blur', 'change']
+ }
+ ],
+ 'pollStatusInterface.pollingFailureConfig': [
+ {
+ validator: (rule: any, value: any) => {
+ if (!value.failureField || !value.failureValue) {
+ return new Error(
+ t('thirdparty_api_source.failure_condition_required')
+ )
+ }
+ return true
+ },
+ trigger: ['blur', 'change']
+ }
+ ],
+ 'selectInterface.responseParameters': [
+ {
+ validator: (rule: any, value: any) => {
+ const idField = value.find((item: any) => item.key === 'id')
+ const nameField = value.find((item: any) => item.key === 'name')
+
+ if (!idField || !idField.jsonPath) {
+ return new Error(t('thirdparty_api_source.id_jsonpath_required'))
+ }
+ if (!nameField || !nameField.jsonPath) {
+ return new Error(
+ t('thirdparty_api_source.name_jsonpath_required')
+ )
+ }
+ return true
+ },
+ trigger: ['blur', 'change']
+ }
+ ]
+ }
+
+ const formRef = ref(null)
+ const isEditMode = computed(() => props.operationType === 'edit')
+
+ // Define the initial state of the form
+ const getInitialFormState = () => ({
+ serviceAddress: 'http://',
+ interfaceTimeout: 120000, // default 2 minutes
+ authConfig: {
+ authType: '',
+ headerPrefix: '',
+ basicUsername: '',
+ basicPassword: '',
+ jwtToken: '',
+ oauth2TokenUrl: '',
+ oauth2ClientId: '',
+ oauth2ClientSecret: '',
+ oauth2GrantType: '',
+ oauth2Username: '',
+ oauth2Password: '',
+ authMappings: []
+ },
+ selectInterface: {
+ url: '',
+ method: 'GET',
+ parameters: [] as any[],
+ body: '',
+ responseParameters: [
+ { key: 'id', jsonPath: '' },
+ { key: 'name', jsonPath: '' }
+ ]
+ },
+ submitInterface: {
+ url: '',
+ method: 'POST',
+ parameters: [] as any[],
+ body: '',
+ responseParameters: [{ key: 'taskInstanceId', jsonPath: '' }]
+ },
+ pollStatusInterface: {
+ url: '',
+ method: 'GET',
+ parameters: [] as any[],
+ body: '',
+ pollingSuccessConfig: { successField: '', successValue: '' },
+ pollingFailureConfig: { failureField: '', failureValue: '' }
+ },
+ stopInterface: {
+ url: '',
+ method: 'POST',
+ parameters: [] as any[],
+ body: ''
+ }
+ })
+
+ // Function to reset form data
+ const resetForm = () => {
+ const initialState = getInitialFormState()
+ Object.keys(form).forEach((key) => {
+ delete (form as any)[key]
+ })
+ Object.assign(form, initialState)
+ formRef.value?.restoreValidation?.()
+ }
+
+ // Save original edit data for testing connection
+ const originalEditData = ref(null)
+ // Listen for modal visibility and data changes
+ watch(
+ [() => props.show, () => props.data, () => props.operationType],
+ ([show, data, operationType]) => {
+ if (show) {
+ if (data && operationType === 'edit') {
+ originalEditData.value = JSON.parse(JSON.stringify(data))
+ resetForm()
+ const editData = originalEditData.value
+ // Use data returned from backend completely
+ Object.assign(form, editData)
+ } else {
+ originalEditData.value = null
+ resetForm()
+ // Only set default values in create mode
+ form.authConfig.authType = 'BASIC_AUTH'
+ form.authConfig.headerPrefix = 'Basic'
+ }
+ }
+ },
+ { immediate: true }
+ )
+
+ watch(
+ () => form.authConfig.authType,
+ (newAuthType) => {
+ // Only automatically set headerPrefix in create mode
+ if (!isEditMode.value) {
+ if (newAuthType === 'BASIC_AUTH') {
+ form.authConfig.headerPrefix = 'Basic'
+ } else if (newAuthType === 'JWT' || newAuthType === 'OAUTH2') {
+ form.authConfig.headerPrefix = 'Bearer'
+ } else {
+ form.authConfig.headerPrefix = ''
+ }
+ }
+ }
+ )
+
+ const handleClose = () => {
+ resetForm()
+ emit('close')
+ }
+
+ const handleSubmit = () => {
+ ;(formRef.value as any)?.validate((errors: any) => {
+ if (!errors) {
+ if (isEditMode.value && originalEditData.value) {
+ const submitData = JSON.parse(
+ JSON.stringify(originalEditData.value)
+ )
+ const initialState = getInitialFormState()
+ Object.keys(initialState).forEach((key) => {
+ if (form.hasOwnProperty(key)) {
+ submitData[key] = (form as any)[key]
+ }
+ })
+ emit('submit', submitData)
+ } else {
+ emit('submit', JSON.parse(JSON.stringify(form)))
+ }
+ }
+ })
+ }
+
+ const handleTest = () => {
+ ;(formRef.value as any)?.validate((errors: any) => {
+ if (!errors) {
+ if (isEditMode.value && originalEditData.value) {
+ const testData = JSON.parse(JSON.stringify(originalEditData.value))
+ const initialState = getInitialFormState()
+ Object.keys(initialState).forEach((key) => {
+ if (form.hasOwnProperty(key)) {
+ testData[key] = (form as any)[key]
+ }
+ })
+ emit('test', testData)
+ } else {
+ emit('test', JSON.parse(JSON.stringify(form)))
+ }
+ }
+ })
+ }
+
+ // Location dropdown options linked with method
+ const getLocationOptions = (unusedMethod: string) => {
+ return [
+ { label: 'Header', value: 'HEADER' },
+ { label: 'Param', value: 'PARAM' }
+ ]
+ }
+
+ return () => (
+
+
+
+
+
+
+
+ {/* Interface timeout */}
+
+
+ {{
+ suffix: () => t('thirdparty_api_source.millisecond')
+ }}
+
+
+ {t('thirdparty_api_source.interface_timeout_description')}
+
+
+
+
+
+ {/* authType */}
+
+ {{
+ label: () => (
+
+ {t('thirdparty_api_source.auth_type')}
+
+ {{
+ trigger: () => (
+
+
+
+ ),
+ default: () =>
+ t('thirdparty_api_source.auth_type_detail_info')
+ }}
+
+
+ ),
+ default: () => (
+
+ )
+ }}
+
+
+
+
+
+
+ {/* BASIC_AUTH */}
+
+
+
+
+
+
+
+ {/* OAUTH2 */}
+
+ {{
+ label: () => (
+
+ {t('thirdparty_api_source.oauth2_token_url')}
+
+ {{
+ trigger: () => (
+
+
+
+ ),
+ default: () =>
+ t('thirdparty_api_source.oauth2_url_info')
+ }}
+
+
+ ),
+ default: () => (
+
+ )
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* JWT */}
+
+
+
+
+ {/* additional params */}
+
+ ({ key: '', value: '' })}
+ style={{ width: '100%' }}
+ >
+ {{
+ default: ({
+ value
+ }: {
+ value: { key: string; value: string }
+ }) => (
+
+
+
+
+ )
+ }}
+
+
+
+
+
+ {/* selectInterface */}
+
+ {{
+ label: () => (
+
+ {t('thirdparty_api_source.input_interface')}
+
+ {{
+ trigger: () => (
+
+
+
+ ),
+ default: () =>
+ t('thirdparty_api_source.input_interface_detail_info')
+ }}
+
+
+ ),
+ default: () => (
+ <>
+ formRef.value?.validate?.()}
+ />
+
+ >
+ )
+ }}
+
+
+
+ ({
+ paramName: '',
+ paramValue: '',
+ location: 'HEADER'
+ })}
+ style={{ width: '100%' }}
+ >
+ {{
+ default: ({
+ value
+ }: {
+ value: {
+ paramName: string
+ paramValue: string
+ location: string
+ }
+ }) => (
+
+
+
+
+
+ )
+ }}
+
+
+
+ {(form.selectInterface.method === 'POST' ||
+ form.selectInterface.method === 'PUT') && (
+
+ {{
+ label: () => (
+
+ {t('thirdparty_api_source.request_body')}
+
+ {{
+ trigger: () => (
+
+
+
+ ),
+ default: () =>
+ t('thirdparty_api_source.input_interface_body_info')
+ }}
+
+
+ ),
+ default: () => (
+
+ )
+ }}
+
+ )}
+
+
+ {{
+ label: () => (
+
+
+ {t('thirdparty_api_source.extract_response_data')}
+
+
+ {{
+ trigger: () => (
+
+
+
+ ),
+ default: () =>
+ t(
+ 'thirdparty_api_source.input_interface_extract_info'
+ )
+ }}
+
+
+ ),
+ default: () => (
+ ({
+ key: '',
+ jsonPath: '',
+ disabled: false
+ })}
+ style={{ width: '100%' }}
+ >
+ {{
+ default: ({
+ value
+ }: {
+ value: {
+ key: string
+ jsonPath: string
+ disabled: boolean
+ }
+ }) => (
+
+
+
+
+ )
+ }}
+
+ )
+ }}
+
+
+
+
+ {/* submitInterface */}
+
+ {{
+ label: () => (
+
+ {t('thirdparty_api_source.submit_interface')}
+
+ {{
+ trigger: () => (
+
+
+
+ ),
+ default: () =>
+ t(
+ 'thirdparty_api_source.submit_interface_detail_info'
+ )
+ }}
+
+
+ ),
+ default: () => (
+ <>
+ formRef.value?.validate?.()}
+ />
+
+ >
+ )
+ }}
+
+
+
+ ({
+ paramName: '',
+ paramValue: '',
+ location: 'HEADER'
+ })}
+ style={{ width: '100%' }}
+ >
+ {{
+ default: ({
+ value
+ }: {
+ value: {
+ paramName: string
+ paramValue: string
+ location: string
+ }
+ }) => (
+
+
+
+
+
+ )
+ }}
+
+
+
+ {(form.submitInterface.method === 'POST' ||
+ form.submitInterface.method === 'PUT') && (
+
+ {{
+ label: () => (
+
+ {t('thirdparty_api_source.request_body')}
+
+ {{
+ trigger: () => (
+
+
+
+ ),
+ default: () =>
+ t(
+ 'thirdparty_api_source.submit_interface_body_info'
+ )
+ }}
+
+
+ ),
+ default: () => (
+
+ )
+ }}
+
+ )}
+
+
+ {{
+ label: () => (
+
+
+ {t('thirdparty_api_source.extract_response_data')}
+
+
+ {{
+ trigger: () => (
+
+
+
+ ),
+ default: () =>
+ t(
+ 'thirdparty_api_source.submit_interface_extract_info'
+ )
+ }}
+
+
+ ),
+ default: () => (
+ ({
+ key: '',
+ jsonPath: '',
+ disabled: false
+ })}
+ style={{ width: '100%' }}
+ >
+ {{
+ default: ({
+ value
+ }: {
+ value: {
+ key: string
+ jsonPath: string
+ disabled: boolean
+ }
+ }) => (
+
+
+
+
+ )
+ }}
+
+ )
+ }}
+
+
+
+
+ {/* pollStatusInterface */}
+
+ {{
+ label: () => (
+
+ {t('thirdparty_api_source.query_interface')}
+
+ {{
+ trigger: () => (
+
+
+
+ ),
+ default: () =>
+ t('thirdparty_api_source.query_interface_detail_info')
+ }}
+
+
+ ),
+ default: () => (
+ <>
+ formRef.value?.validate?.()}
+ />
+
+ >
+ )
+ }}
+
+
+
+ ({
+ paramName: '',
+ paramValue: '',
+ location: 'HEADER'
+ })}
+ style={{ width: '100%' }}
+ >
+ {{
+ default: ({
+ value
+ }: {
+ value: {
+ paramName: string
+ paramValue: string
+ location: string
+ }
+ }) => (
+
+
+
+
+
+ )
+ }}
+
+
+
+ {(form.pollStatusInterface.method === 'POST' ||
+ form.pollStatusInterface.method === 'PUT') && (
+
+ {{
+ label: () => (
+
+ {t('thirdparty_api_source.request_body')}
+
+ {{
+ trigger: () => (
+
+
+
+ ),
+ default: () =>
+ t('thirdparty_api_source.query_interface_body_info')
+ }}
+
+
+ ),
+ default: () => (
+
+ )
+ }}
+
+ )}
+
+
+ {{
+ label: () => (
+
+ {t('thirdparty_api_source.success_condition')}
+
+ {{
+ trigger: () => (
+
+
+
+ ),
+ default: () =>
+ t(
+ 'thirdparty_api_source.query_interface_success_info'
+ )
+ }}
+
+
+ ),
+ default: () => (
+ <>
+ formRef.value?.validate?.()}
+ />
+ formRef.value?.validate?.()}
+ />
+ >
+ )
+ }}
+
+
+
+ {{
+ label: () => (
+
+ {t('thirdparty_api_source.failure_condition')}
+
+ {{
+ trigger: () => (
+
+
+
+ ),
+ default: () =>
+ t('thirdparty_api_source.query_interface_failed_info')
+ }}
+
+
+ ),
+ default: () => (
+ <>
+ formRef.value?.validate?.()}
+ />
+ formRef.value?.validate?.()}
+ />
+ >
+ )
+ }}
+
+
+
+
+ {/* stopInterface */}
+
+ {{
+ label: () => (
+
+ {t('thirdparty_api_source.stop_interface')}
+
+ {{
+ trigger: () => (
+
+
+
+ ),
+ default: () =>
+ t('thirdparty_api_source.stop_interface_detail_info')
+ }}
+
+
+ ),
+ default: () => (
+ <>
+ formRef.value?.validate?.()}
+ />
+
+ >
+ )
+ }}
+
+
+
+ ({
+ paramName: '',
+ paramValue: '',
+ location: 'HEADER'
+ })}
+ style={{ width: '100%' }}
+ >
+ {{
+ default: ({
+ value
+ }: {
+ value: {
+ paramName: string
+ paramValue: string
+ location: string
+ }
+ }) => (
+
+
+
+
+
+ )
+ }}
+
+
+
+ {(form.stopInterface.method === 'POST' ||
+ form.stopInterface.method === 'PUT') && (
+
+ {{
+ label: () => (
+
+ {t('thirdparty_api_source.request_body')}
+
+ {{
+ trigger: () => (
+
+
+
+ ),
+ default: () =>
+ t('thirdparty_api_source.stop_interface_body_info')
+ }}
+
+
+ ),
+ default: () => (
+
+ )
+ }}
+
+ )}
+
+
+
+
+
+ )
+ }
+})
diff --git a/dolphinscheduler-ui/src/views/thirdparty-api-source/use-table.ts b/dolphinscheduler-ui/src/views/thirdparty-api-source/use-table.ts
new file mode 100644
index 000000000000..976bc55ca283
--- /dev/null
+++ b/dolphinscheduler-ui/src/views/thirdparty-api-source/use-table.ts
@@ -0,0 +1,72 @@
+/*
+ * 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 { reactive, ref } from 'vue'
+import {
+ queryDataSourceListPaging,
+ deleteDataSource
+} from '@/service/modules/data-source'
+
+export function useTable() {
+ const data = reactive({
+ page: 1,
+ pageSize: 10,
+ itemCount: 0,
+ searchVal: ref(''),
+ list: [],
+ loading: false
+ })
+
+ const getList = async () => {
+ if (data.loading) return
+ data.loading = true
+
+ const listRes = await queryDataSourceListPaging({
+ pageNo: data.page,
+ pageSize: data.pageSize,
+ searchVal: data.searchVal
+ })
+ data.loading = false
+ data.list = listRes.totalList
+ data.itemCount = listRes.total
+ }
+
+ const updateList = () => {
+ if (data.list.length === 1 && data.page > 1) {
+ --data.page
+ }
+ getList()
+ }
+
+ const deleteRecord = async (id: number) => {
+ const ignored = await deleteDataSource(id)
+ updateList()
+ }
+
+ const changePage = (page: number) => {
+ data.page = page
+ getList()
+ }
+
+ const changePageSize = (pageSize: number) => {
+ data.page = 1
+ data.pageSize = pageSize
+ getList()
+ }
+
+ return { data, changePage, changePageSize, deleteRecord, updateList }
+}