diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/workflow/TriggerWorkflowRequestTransformer.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/workflow/TriggerWorkflowRequestTransformer.java index c1c85acbcdaa..756dccae208d 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/workflow/TriggerWorkflowRequestTransformer.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/workflow/TriggerWorkflowRequestTransformer.java @@ -23,13 +23,21 @@ import org.apache.dolphinscheduler.api.validator.ITransformer; import org.apache.dolphinscheduler.dao.entity.WorkflowDefinition; import org.apache.dolphinscheduler.dao.repository.WorkflowDefinitionDao; +import org.apache.dolphinscheduler.plugin.task.api.enums.Direct; +import org.apache.dolphinscheduler.plugin.task.api.model.Property; import org.apache.dolphinscheduler.plugin.task.api.utils.PropertyUtils; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.alibaba.druid.util.StringUtils; + @Slf4j @Component public class TriggerWorkflowRequestTransformer implements ITransformer { @@ -39,6 +47,11 @@ public class TriggerWorkflowRequestTransformer implements ITransformer startParamList = + PropertyUtils.startParamsTransformPropertyList(workflowTriggerRequest.getStartParamList()); + + validateStartParamList(startParamList); + TriggerWorkflowDTO triggerWorkflowDTO = TriggerWorkflowDTO.builder() .loginUser(workflowTriggerRequest.getLoginUser()) .startNodes(WorkflowUtils.parseStartNodeList(workflowTriggerRequest.getStartNodes())) @@ -51,8 +64,7 @@ public TriggerWorkflowDTO transform(WorkflowTriggerRequest workflowTriggerReques .workerGroup(workflowTriggerRequest.getWorkerGroup()) .tenantCode(workflowTriggerRequest.getTenantCode()) .environmentCode(workflowTriggerRequest.getEnvironmentCode()) - .startParamList( - PropertyUtils.startParamsTransformPropertyList(workflowTriggerRequest.getStartParamList())) + .startParamList(startParamList) .dryRun(workflowTriggerRequest.getDryRun()) .build(); @@ -64,4 +76,30 @@ public TriggerWorkflowDTO transform(WorkflowTriggerRequest workflowTriggerReques triggerWorkflowDTO.setWorkflowDefinition(workflowDefinition); return triggerWorkflowDTO; } + + private void validateStartParamList(List startParamList) { + if (startParamList == null || startParamList.isEmpty()) { + return; + } + + Set keys = new HashSet<>(); + for (Property param : startParamList) { + // null key + if (StringUtils.isEmpty(param.getProp())) { + throw new ServiceException("Parameter key cannot be empty"); + } + + String key = param.getProp().trim(); + // duplicate keys + if (keys.contains(key)) { + throw new ServiceException("Duplicate parameter key: " + key); + } + keys.add(key); + + // IN-type params require a non-empty value + if (Direct.IN.equals(param.getDirect()) && StringUtils.isEmpty(param.getValue())) { + throw new ServiceException("IN parameter value cannot be empty for key: " + key); + } + } + } } diff --git a/dolphinscheduler-ui/src/locales/en_US/project.ts b/dolphinscheduler-ui/src/locales/en_US/project.ts index 15073a64aeae..b54655956c11 100644 --- a/dolphinscheduler-ui/src/locales/en_US/project.ts +++ b/dolphinscheduler-ui/src/locales/en_US/project.ts @@ -349,8 +349,9 @@ export default { update_directly: 'Whether to update the workflow definition', dag_name_empty: 'DAG graph name cannot be empty', positive_integer: 'Please enter a positive integer greater than 0', - prop_empty: 'prop is empty', - prop_repeat: 'prop is repeat', + prop_key_repeat: 'prop key is repeat', + prop_key_empty: 'prop key is empty', + prop_value_empty: 'prop value is empty', node_not_created: 'Failed to save node not created', copy_name: 'Copy Name', view_variables: 'View Variables', diff --git a/dolphinscheduler-ui/src/locales/zh_CN/project.ts b/dolphinscheduler-ui/src/locales/zh_CN/project.ts index 880c037c96a8..118f61b60478 100644 --- a/dolphinscheduler-ui/src/locales/zh_CN/project.ts +++ b/dolphinscheduler-ui/src/locales/zh_CN/project.ts @@ -344,8 +344,9 @@ export default { update_directly: '是否更新工作流定义', dag_name_empty: 'DAG图名称不能为空', positive_integer: '请输入大于 0 的正整数', - prop_empty: '自定义参数prop不能为空', - prop_repeat: 'prop中有重复', + prop_key_repeat: '自定义参数prop中key有重复', + prop_key_empty: '自定义参数prop中key不能为空', + prop_value_empty: '自定义参数prop中value不能为空', node_not_created: '未创建节点保存失败', copy_name: '复制名称', view_variables: '查看变量', diff --git a/dolphinscheduler-ui/src/views/projects/workflow/components/dag/dag-save-modal.tsx b/dolphinscheduler-ui/src/views/projects/workflow/components/dag/dag-save-modal.tsx index a6b78cda384c..0b38be431c59 100644 --- a/dolphinscheduler-ui/src/views/projects/workflow/components/dag/dag-save-modal.tsx +++ b/dolphinscheduler-ui/src/views/projects/workflow/components/dag/dag-save-modal.tsx @@ -98,23 +98,29 @@ export default defineComponent({ }, globalParams: { validator() { - const props = new Set() + const globalParams = formValue.value.globalParams || [] - const keys = formValue.value.globalParams.map((item) => item.key) - const keysSet = new Set(keys) - if (keysSet.size !== keys.length) { - return new Error(t('project.dag.prop_repeat')) + if (!globalParams || globalParams.length === 0) return true + + const keys = globalParams.map((item) => item.key) + const uniqueKeys = new Set(keys) + if (uniqueKeys.size !== keys.length) { + return new Error(t('project.dag.prop_key_repeat')) } - for (const param of formValue.value.globalParams) { - const prop = param.value - const direct = param.direct - if (direct === 'IN' && !prop) { - return new Error(t('project.dag.prop_empty')) + for (const param of globalParams) { + if (!param.key || param.key.trim() === '') { + return new Error(t('project.dag.prop_key_empty')) } - props.add(prop) + if ( + param.direct === 'IN' && + (!param.value || param.value.trim() === '') + ) { + return new Error(t('project.dag.prop_value_empty')) + } } + return true } } } diff --git a/dolphinscheduler-ui/src/views/projects/workflow/definition/components/start-modal.tsx b/dolphinscheduler-ui/src/views/projects/workflow/definition/components/start-modal.tsx index bb574d2e5045..0034e7279387 100644 --- a/dolphinscheduler-ui/src/views/projects/workflow/definition/components/start-modal.tsx +++ b/dolphinscheduler-ui/src/views/projects/workflow/definition/components/start-modal.tsx @@ -290,6 +290,13 @@ export default defineComponent({ } ) + const formModel = computed(() => ({ + // UI configuration state from form management hook + ...startState.startForm, + // Business data injected from modal operations hook + startParamsList: variables.startParamsList + })) + return { t, showTaskDependType, @@ -302,6 +309,7 @@ export default defineComponent({ removeStartParams, addStartParams, updateParamsList, + formModel, ...toRefs(variables), ...toRefs(startState), ...toRefs(props), @@ -319,7 +327,7 @@ export default defineComponent({ onConfirm={this.handleStart} confirmLoading={this.saving} > - + { return { - key: '', + prop: '', direct: 'IN', type: 'VARCHAR', value: '' diff --git a/dolphinscheduler-ui/src/views/projects/workflow/definition/components/use-form.ts b/dolphinscheduler-ui/src/views/projects/workflow/definition/components/use-form.ts index 357531129535..e52cffb8b902 100644 --- a/dolphinscheduler-ui/src/views/projects/workflow/definition/components/use-form.ts +++ b/dolphinscheduler-ui/src/views/projects/workflow/definition/components/use-form.ts @@ -101,6 +101,34 @@ export const useForm = () => { return new Error(t('project.workflow.warning_group_tip')) } } + }, + startParamsList: { + trigger: ['input', 'blur'], + validator: (rule: any, value: Array) => { + const params = value || [] + + if (!params || params.length === 0) return true + + const keys = params.map((item) => item.prop) + const uniqueKeys = new Set(keys) + if (uniqueKeys.size !== keys.length) { + return new Error(t('project.dag.prop_key_repeat')) + } + + for (const param of params) { + if (!param.prop || param.prop.trim() === '') { + return new Error(t('project.dag.prop_key_empty')) + } + + if ( + param.direct === 'IN' && + (!param.value || param.value.trim() === '') + ) { + return new Error(t('project.dag.prop_value_empty')) + } + } + return true + } } } })