Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -784,31 +784,18 @@ public Map<String, Object> viewVariables(long projectCode, Integer workflowInsta
timezone = commandParam.getTimeZone();
}

Map<String, String> timeParams = BusinessTimeUtils
Map<String, String> parameterMap = BusinessTimeUtils
.getBusinessTime(workflowInstance.getCmdTypeIfComplement(),
workflowInstance.getScheduleTime(), timezone);
String userDefinedParams = workflowInstance.getGlobalParams();
// global params
List<Property> 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<Property> finalGlobalParams = processGlobalParams(workflowInstance, parameterMap);

Map<String, Map<String, Object>> localUserDefParams = getLocalParams(workflowInstance, timeParams);
// localUserDefParams
Map<String, Map<String, Object>> localUserDefParams = processLocalParams(workflowInstance, parameterMap);

Map<String, Object> resultMap = new HashMap<>();

resultMap.put(GLOBAL_PARAMS, globalParams);
resultMap.put(GLOBAL_PARAMS, finalGlobalParams);
resultMap.put(LOCAL_PARAMS, localUserDefParams);

result.put(DATA_LIST, resultMap);
Expand All @@ -817,25 +804,63 @@ public Map<String, Object> 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<Property> processGlobalParams(WorkflowInstance workflowInstance, Map<String, String> parameterMap) {
List<Property> 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<String, Map<String, Object>> getLocalParams(WorkflowInstance workflowInstance,
Map<String, String> timeParams) {
private Map<String, Map<String, Object>> processLocalParams(WorkflowInstance workflowInstance,
Map<String, String> parameterMap) {
Map<String, Map<String, Object>> localUserDefParams = new HashMap<>();

// Fetch valid task instances for the workflow
List<TaskInstance> 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<Property> localParamsList = JSONUtils.toList(localParams, Property.class);

Map<String, Object> localParamsMap = new HashMap<>();
localParamsMap.put(TASK_TYPE, taskDefinitionLog.getTaskType());
localParamsMap.put(LOCAL_PARAMS_LIST, localParamsList);

if (CollectionUtils.isNotEmpty(localParamsList)) {
localUserDefParams.put(taskDefinitionLog.getName(), localParamsMap);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -56,26 +57,30 @@
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;
import org.apache.dolphinscheduler.dao.mapper.WorkflowInstanceMapper;
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;
import org.apache.dolphinscheduler.plugin.task.api.enums.DependResult;
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;
Expand Down Expand Up @@ -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\":\"{}\"}]";
Expand Down Expand Up @@ -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<String, Object> result = workflowInstanceService.viewVariables(1L, 1);

Assertions.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));

Map<String, Object> dataList = (Map<String, Object>) result.get(Constants.DATA_LIST);
Assertions.assertNotNull(dataList);

List<Property> globalParams = (List<Property>) 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<String, Object> 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<String, Object> result = workflowInstanceService.viewVariables(1L, 2);

Assertions.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));

Map<String, Object> dataList = (Map<String, Object>) result.get(Constants.DATA_LIST);
List<Property> globalParams = (List<Property>) 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();
Expand Down
Loading