diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkflowInstanceServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkflowInstanceServiceImpl.java index 4059b17b0700..af44786055b9 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkflowInstanceServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkflowInstanceServiceImpl.java @@ -784,31 +784,18 @@ public Map viewVariables(long projectCode, Integer workflowInsta timezone = commandParam.getTimeZone(); } - Map timeParams = BusinessTimeUtils + Map parameterMap = BusinessTimeUtils .getBusinessTime(workflowInstance.getCmdTypeIfComplement(), workflowInstance.getScheduleTime(), timezone); - String userDefinedParams = workflowInstance.getGlobalParams(); - // global params - List globalParams = new ArrayList<>(); - // global param string - String globalParamStr = - ParameterUtils.convertParameterPlaceholders(GlobalParameterUtils.serializeGlobalParameter(globalParams), - timeParams); - globalParams = GlobalParameterUtils.deserializeGlobalParameter(globalParamStr); - for (Property property : globalParams) { - timeParams.put(property.getProp(), property.getValue()); - } - - if (userDefinedParams != null && userDefinedParams.length() > 0) { - globalParams = GlobalParameterUtils.deserializeGlobalParameter(userDefinedParams); - } + // finalGlobalParams + List finalGlobalParams = processGlobalParams(workflowInstance, parameterMap); - Map> localUserDefParams = getLocalParams(workflowInstance, timeParams); + // localUserDefParams + Map> localUserDefParams = processLocalParams(workflowInstance, parameterMap); Map resultMap = new HashMap<>(); - - resultMap.put(GLOBAL_PARAMS, globalParams); + resultMap.put(GLOBAL_PARAMS, finalGlobalParams); resultMap.put(LOCAL_PARAMS, localUserDefParams); result.put(DATA_LIST, resultMap); @@ -817,25 +804,63 @@ public Map viewVariables(long projectCode, Integer workflowInsta } /** - * get local params + * Process global parameters: resolve placeholders and merge into context. + * + * @param workflowInstance The workflow instance. + * @param parameterMap Context parameters for placeholder replacement and merging + * @return Deserialized global properties list + */ + private List processGlobalParams(WorkflowInstance workflowInstance, Map parameterMap) { + List finalGlobalParams = new ArrayList<>(); + + String globalParamsJson = workflowInstance.getGlobalParams(); + if (StringUtils.isNotEmpty(globalParamsJson)) { + // Replace placeholders + String replacedJsonStr = ParameterUtils.convertParameterPlaceholders(globalParamsJson, parameterMap); + finalGlobalParams = GlobalParameterUtils.deserializeGlobalParameter(replacedJsonStr); + + // Merge into context map + if (finalGlobalParams != null) { + for (Property property : finalGlobalParams) { + if (property.getProp() != null && property.getValue() != null) { + parameterMap.put(property.getProp(), property.getValue()); + } + } + } + } + return finalGlobalParams; + } + + /** + * Process local parameters for tasks within a workflow instance. + * + * @param workflowInstance The workflow instance. + * @param parameterMap Context parameters for placeholder replacement. + * @return Map of task name to its local parameters and type. */ - private Map> getLocalParams(WorkflowInstance workflowInstance, - Map timeParams) { + private Map> processLocalParams(WorkflowInstance workflowInstance, + Map parameterMap) { Map> localUserDefParams = new HashMap<>(); + + // Fetch valid task instances for the workflow List taskInstanceList = taskInstanceMapper.findValidTaskListByWorkflowInstanceId(workflowInstance.getId(), Flag.YES); + for (TaskInstance taskInstance : taskInstanceList) { TaskDefinitionLog taskDefinitionLog = taskDefinitionLogMapper.queryByDefinitionCodeAndVersion( taskInstance.getTaskCode(), taskInstance.getTaskDefinitionVersion()); String localParams = JSONUtils.getNodeString(taskDefinitionLog.getTaskParams(), LOCAL_PARAMS); + if (!StringUtils.isEmpty(localParams)) { - localParams = ParameterUtils.convertParameterPlaceholders(localParams, timeParams); + // Replace placeholders and deserialize + localParams = ParameterUtils.convertParameterPlaceholders(localParams, parameterMap); List localParamsList = JSONUtils.toList(localParams, Property.class); Map localParamsMap = new HashMap<>(); localParamsMap.put(TASK_TYPE, taskDefinitionLog.getTaskType()); localParamsMap.put(LOCAL_PARAMS_LIST, localParamsList); + if (CollectionUtils.isNotEmpty(localParamsList)) { localUserDefParams.put(taskDefinitionLog.getName(), localParamsMap); } diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/WorkflowInstanceServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/WorkflowInstanceServiceTest.java index 790a23955b28..37b2d3740369 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/WorkflowInstanceServiceTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/WorkflowInstanceServiceTest.java @@ -42,6 +42,7 @@ import org.apache.dolphinscheduler.common.model.TaskNodeRelation; import org.apache.dolphinscheduler.common.utils.DateUtils; import org.apache.dolphinscheduler.common.utils.JSONUtils; +import org.apache.dolphinscheduler.dao.AlertDao; import org.apache.dolphinscheduler.dao.entity.DependentResultTaskInstanceContext; import org.apache.dolphinscheduler.dao.entity.Project; import org.apache.dolphinscheduler.dao.entity.TaskDefinition; @@ -56,6 +57,7 @@ import org.apache.dolphinscheduler.dao.entity.WorkflowInstance; import org.apache.dolphinscheduler.dao.mapper.ProjectMapper; import org.apache.dolphinscheduler.dao.mapper.TaskDefinitionMapper; +import org.apache.dolphinscheduler.dao.mapper.TaskInstanceMapper; import org.apache.dolphinscheduler.dao.mapper.TenantMapper; import org.apache.dolphinscheduler.dao.mapper.WorkflowDefinitionLogMapper; import org.apache.dolphinscheduler.dao.mapper.WorkflowDefinitionMapper; @@ -63,6 +65,7 @@ import org.apache.dolphinscheduler.dao.repository.TaskInstanceContextDao; import org.apache.dolphinscheduler.dao.repository.TaskInstanceDao; import org.apache.dolphinscheduler.dao.repository.WorkflowInstanceDao; +import org.apache.dolphinscheduler.dao.repository.WorkflowInstanceMapDao; import org.apache.dolphinscheduler.extract.master.command.RunWorkflowCommandParam; import org.apache.dolphinscheduler.plugin.task.api.TaskPluginManager; import org.apache.dolphinscheduler.plugin.task.api.enums.DataType; @@ -70,12 +73,14 @@ import org.apache.dolphinscheduler.plugin.task.api.enums.Direct; import org.apache.dolphinscheduler.plugin.task.api.enums.TaskExecutionStatus; import org.apache.dolphinscheduler.plugin.task.api.model.Property; +import org.apache.dolphinscheduler.service.expand.CuringParamsService; import org.apache.dolphinscheduler.service.model.TaskNode; import org.apache.dolphinscheduler.service.process.ProcessService; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -145,6 +150,21 @@ public class WorkflowInstanceServiceTest { @Mock private TaskInstanceContextDao taskInstanceContextDao; + @Mock + TaskInstanceMapper taskInstanceMapper; + + @Mock + CuringParamsService curingGlobalParamsService; + + @Mock + TaskInstanceService taskInstanceService; + + @Mock + WorkflowInstanceMapDao workflowInstanceMapDao; + + @Mock + AlertDao alertDao; + private String shellJson = "[{\"name\":\"\",\"preTaskCode\":0,\"preTaskVersion\":0,\"postTaskCode\":123456789," + "\"postTaskVersion\":1,\"conditionType\":0,\"conditionParams\":\"{}\"},{\"name\":\"\",\"preTaskCode\":123456789," + "\"preTaskVersion\":1,\"postTaskCode\":123451234,\"postTaskVersion\":1,\"conditionType\":0,\"conditionParams\":\"{}\"}]"; @@ -768,6 +788,89 @@ public void testViewVariables() { Assertions.assertEquals(Status.WORKFLOW_INSTANCE_NOT_EXIST, processNotExist.get(Constants.STATUS)); } + @Test + public void testViewVariables_WithTimePlaceholders() { + String globalParamsJson = "[{\"prop\":\"biz_date\",\"value\":\"$[yyyyMMdd]\",\"type\":\"VARCHAR\"}," + + "{\"prop\":\"env\",\"value\":\"${ENV_TYPE}\",\"type\":\"VARCHAR\"}]"; + + WorkflowInstance workflowInstance = getProcessInstance(); + workflowInstance.setId(1); + workflowInstance.setCommandType(CommandType.SCHEDULER); + + Calendar calendar = Calendar.getInstance(); + calendar.set(2026, Calendar.MARCH, 13, 10, 0, 0); + workflowInstance.setScheduleTime(calendar.getTime()); + workflowInstance.setGlobalParams(globalParamsJson); + workflowInstance.setWorkflowDefinitionCode(100L); + + when(workflowInstanceMapper.queryDetailById(1)).thenReturn(workflowInstance); + + WorkflowDefinition workflowDefinition = new WorkflowDefinition(); + workflowDefinition.setCode(100L); + workflowDefinition.setProjectCode(1L); + when(workflowDefinitionMapper.queryByCode(100L)).thenReturn(workflowDefinition); + + Map result = workflowInstanceService.viewVariables(1L, 1); + + Assertions.assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); + + Map dataList = (Map) result.get(Constants.DATA_LIST); + Assertions.assertNotNull(dataList); + + List globalParams = (List) dataList.get(Constants.GLOBAL_PARAMS); + Assertions.assertNotNull(globalParams); + Assertions.assertEquals(2, globalParams.size()); + + Property dateParam = globalParams.stream() + .filter(p -> "biz_date".equals(p.getProp())) + .findFirst() + .orElse(null); + Assertions.assertNotNull(dateParam); + Assertions.assertEquals("20260313", dateParam.getValue(), + "Time placeholder $[yyyyMMdd] should be replaced with schedule time"); + + Property envParam = globalParams.stream() + .filter(p -> "env".equals(p.getProp())) + .findFirst() + .orElse(null); + Assertions.assertNotNull(envParam); + } + + @Test + public void testViewVariables_InstanceNotFound() { + when(workflowInstanceMapper.queryDetailById(999)).thenReturn(null); + + Map result = workflowInstanceService.viewVariables(1L, 999); + + Assertions.assertEquals(Status.WORKFLOW_INSTANCE_NOT_EXIST, result.get(Constants.STATUS)); + Assertions.assertNull(result.get(Constants.DATA_LIST)); + } + + @Test + public void testViewVariables_EmptyGlobalParams() { + WorkflowInstance workflowInstance = getProcessInstance(); + workflowInstance.setId(2); + workflowInstance.setCommandType(CommandType.START_PROCESS); + workflowInstance.setScheduleTime(new Date()); + workflowInstance.setGlobalParams(""); + workflowInstance.setWorkflowDefinitionCode(101L); + + when(workflowInstanceMapper.queryDetailById(2)).thenReturn(workflowInstance); + + WorkflowDefinition workflowDefinition = new WorkflowDefinition(); + workflowDefinition.setCode(101L); + workflowDefinition.setProjectCode(1L); + when(workflowDefinitionMapper.queryByCode(101L)).thenReturn(workflowDefinition); + + Map result = workflowInstanceService.viewVariables(1L, 2); + + Assertions.assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); + + Map dataList = (Map) result.get(Constants.DATA_LIST); + List globalParams = (List) dataList.get(Constants.GLOBAL_PARAMS); + Assertions.assertTrue(globalParams.isEmpty(), "Global params list should be empty when input is empty string"); + } + @Test public void testViewGantt() throws Exception { WorkflowInstance workflowInstance = getProcessInstance();