Loading dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/BaseDAGService.java +3 −37 Original line number Diff line number Diff line Loading @@ -20,12 +20,11 @@ import org.apache.dolphinscheduler.common.graph.DAG; import org.apache.dolphinscheduler.common.model.TaskNode; import org.apache.dolphinscheduler.common.model.TaskNodeRelation; import org.apache.dolphinscheduler.common.process.ProcessDag; import org.apache.dolphinscheduler.common.utils.CollectionUtils; import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.dao.entity.ProcessData; import org.apache.dolphinscheduler.dao.entity.ProcessInstance; import org.apache.dolphinscheduler.dao.utils.DagHelper; import java.util.ArrayList; import java.util.List; /** Loading @@ -48,41 +47,8 @@ public class BaseDAGService extends BaseService{ List<TaskNode> taskNodeList = processData.getTasks(); List<TaskNodeRelation> taskNodeRelations = new ArrayList<>(); ProcessDag processDag = DagHelper.getProcessDag(taskNodeList); //Traversing node information and building relationships for (TaskNode taskNode : taskNodeList) { String preTasks = taskNode.getPreTasks(); List<String> preTasksList = JSONUtils.toList(preTasks, String.class); //if previous tasks not empty if (preTasksList != null) { for (String depNode : preTasksList) { taskNodeRelations.add(new TaskNodeRelation(depNode, taskNode.getName())); } } } ProcessDag processDag = new ProcessDag(); processDag.setEdges(taskNodeRelations); processDag.setNodes(taskNodeList); // generate detail Dag, to be executed DAG<String, TaskNode, TaskNodeRelation> dag = new DAG<>(); if (CollectionUtils.isNotEmpty(processDag.getNodes())) { for (TaskNode node : processDag.getNodes()) { dag.addNode(node.getName(), node); } } if (CollectionUtils.isNotEmpty(processDag.getEdges())) { for (TaskNodeRelation edge : processDag.getEdges()) { dag.addEdge(edge.getStartNode(), edge.getEndNode()); } } return dag; return DagHelper.buildDagGraph(processDag); } } dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/ProcessDefinitionService.java +26 −67 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.common.utils.StringUtils; import org.apache.dolphinscheduler.dao.entity.*; import org.apache.dolphinscheduler.dao.mapper.*; import org.apache.dolphinscheduler.dao.utils.DagHelper; import org.apache.dolphinscheduler.service.process.ProcessService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; Loading Loading @@ -465,7 +466,7 @@ public class ProcessDefinitionService extends BaseDAGService { ); for(Schedule schedule:scheduleList){ logger.info("set schedule offline, schedule id: {}, process definition id: {}", project.getId(), schedule.getId(), id); logger.info("set schedule offline, project id: {}, schedule id: {}, process definition id: {}", project.getId(), schedule.getId(), id); // set status schedule.setReleaseState(ReleaseState.OFFLINE); scheduleMapper.updateById(schedule); Loading Loading @@ -948,11 +949,16 @@ public class ProcessDefinitionService extends BaseDAGService { return result; } String processDefinitionJson = processDefinition.getProcessDefinitionJson(); ProcessData processData = JSONUtils.parseObject(processDefinitionJson, ProcessData.class); //process data check if (null == processData) { logger.error("process data is null"); putMsg(result,Status.DATA_IS_NOT_VALID, processDefinitionJson); return result; } List<TaskNode> taskNodeList = (processData.getTasks() == null) ? new ArrayList<>() : processData.getTasks(); result.put(Constants.DATA_LIST, taskNodeList); Loading @@ -974,14 +980,13 @@ public class ProcessDefinitionService extends BaseDAGService { Map<Integer, List<TaskNode>> taskNodeMap = new HashMap<>(); String[] idList = defineIdList.split(","); List<String> definitionIdList = Arrays.asList(idList); List<Integer> idIntList = new ArrayList<>(); for(String definitionId : definitionIdList) { for(String definitionId : idList) { idIntList.add(Integer.parseInt(definitionId)); } Integer[] idArray = idIntList.toArray(new Integer[idIntList.size()]); List<ProcessDefinition> processDefinitionList = processDefineMapper.queryDefinitionListByIdList(idArray); if (processDefinitionList == null || processDefinitionList.size() ==0) { if (CollectionUtils.isEmpty(processDefinitionList)) { logger.info("process definition not exists"); putMsg(result, Status.PROCESS_DEFINE_NOT_EXIST, defineIdList); return result; Loading Loading @@ -1031,9 +1036,10 @@ public class ProcessDefinitionService extends BaseDAGService { Map<String, Object> result = new HashMap<>(); ProcessDefinition processDefinition = processDefineMapper.selectById(processId); if (processDefinition == null) { if (null == processDefinition) { logger.info("process define not exists"); throw new RuntimeException("process define not exists"); putMsg(result,Status.PROCESS_DEFINE_NOT_EXIST, processDefinition); return result; } DAG<String, TaskNode, TaskNodeRelation> dag = genDagGraph(processDefinition); /** Loading Loading @@ -1121,10 +1127,10 @@ public class ProcessDefinitionService extends BaseDAGService { pTreeViewDto.getChildren().add(treeViewDto); } postNodeList = dag.getSubsequentNodes(nodeName); if (postNodeList != null && postNodeList.size() > 0) { if (CollectionUtils.isNotEmpty(postNodeList)) { for (String nextNodeName : postNodeList) { List<TreeViewDto> treeViewDtoList = waitingRunningNodeMap.get(nextNodeName); if (treeViewDtoList != null && treeViewDtoList.size() > 0) { if (CollectionUtils.isNotEmpty(treeViewDtoList)) { treeViewDtoList.add(treeViewDto); waitingRunningNodeMap.put(nextNodeName, treeViewDtoList); } else { Loading @@ -1136,7 +1142,6 @@ public class ProcessDefinitionService extends BaseDAGService { } runningNodeMap.remove(nodeName); } if (waitingRunningNodeMap == null || waitingRunningNodeMap.size() == 0) { break; } else { Loading @@ -1161,75 +1166,29 @@ public class ProcessDefinitionService extends BaseDAGService { private DAG<String, TaskNode, TaskNodeRelation> genDagGraph(ProcessDefinition processDefinition) throws Exception { String processDefinitionJson = processDefinition.getProcessDefinitionJson(); ProcessData processData = JSONUtils.parseObject(processDefinitionJson, ProcessData.class); //check process data if (null != processData) { List<TaskNode> taskNodeList = processData.getTasks(); processDefinition.setGlobalParamList(processData.getGlobalParams()); List<TaskNodeRelation> taskNodeRelations = new ArrayList<>(); // Traverse node information and build relationships for (TaskNode taskNode : taskNodeList) { String preTasks = taskNode.getPreTasks(); List<String> preTasksList = JSONUtils.toList(preTasks, String.class); // If the dependency is not empty if (preTasksList != null) { for (String depNode : preTasksList) { taskNodeRelations.add(new TaskNodeRelation(depNode, taskNode.getName())); } } } ProcessDag processDag = new ProcessDag(); processDag.setEdges(taskNodeRelations); processDag.setNodes(taskNodeList); ProcessDag processDag = DagHelper.getProcessDag(taskNodeList); // Generate concrete Dag to be executed return genDagGraph(processDag); return DagHelper.buildDagGraph(processDag); } /** * Generate the DAG of process * * @return DAG */ private DAG<String, TaskNode, TaskNodeRelation> genDagGraph(ProcessDag processDag) { DAG<String, TaskNode, TaskNodeRelation> dag = new DAG<>(); /** * Add the ndoes */ if (CollectionUtils.isNotEmpty(processDag.getNodes())) { for (TaskNode node : processDag.getNodes()) { dag.addNode(node.getName(), node); } return new DAG<>(); } /** * Add the edges */ if (CollectionUtils.isNotEmpty(processDag.getEdges())) { for (TaskNodeRelation edge : processDag.getEdges()) { dag.addEdge(edge.getStartNode(), edge.getEndNode()); } } return dag; } /** * whether the graph has a ring * * @param taskNodeResponseList * @return * @param taskNodeResponseList task node response list * @return if graph has cycle flag */ private boolean graphHasCycle(List<TaskNode> taskNodeResponseList) { DAG<String, TaskNode, String> graph = new DAG<>(); Loading dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProcessDefinitionServiceTest.java +166 −11 Original line number Diff line number Diff line Loading @@ -16,10 +16,8 @@ */ package org.apache.dolphinscheduler.api.service; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.metadata.IPage; import org.apache.dolphinscheduler.api.ApiApplicationServer; import org.apache.dolphinscheduler.api.dto.ProcessMeta; import org.apache.dolphinscheduler.api.enums.Status; Loading Loading @@ -82,6 +80,12 @@ public class ProcessDefinitionServiceTest { @Mock private ProcessService processService; @Mock private ProcessInstanceMapper processInstanceMapper; @Mock private TaskInstanceMapper taskInstanceMapper; private String sqlDependentJson = "{\"globalParams\":[]," + "\"tasks\":[{\"type\":\"SQL\",\"id\":\"tasks-27297\",\"name\":\"sql\"," + "\"params\":{\"type\":\"MYSQL\",\"datasource\":1,\"sql\":\"select * from test\"," + Loading @@ -99,6 +103,12 @@ public class ProcessDefinitionServiceTest { "\"timeout\":{\"strategy\":\"\",\"enable\":false},\"taskInstancePriority\":\"MEDIUM\"," + "\"workerGroupId\":-1,\"preTasks\":[]}],\"tenantId\":1,\"timeout\":0}"; private String shellJson = "{\"globalParams\":[],\"tasks\":[{\"type\":\"SHELL\",\"id\":\"tasks-9527\",\"name\":\"shell-1\"," + "\"params\":{\"resourceList\":[],\"localParams\":[],\"rawScript\":\"#!/bin/bash\\necho \\\"shell-1\\\"\"}," + "\"description\":\"\",\"runFlag\":\"NORMAL\",\"dependence\":{},\"maxRetryTimes\":\"0\",\"retryInterval\":\"1\"," + "\"timeout\":{\"strategy\":\"\",\"interval\":1,\"enable\":false},\"taskInstancePriority\":\"MEDIUM\"," + "\"workerGroupId\":-1,\"preTasks\":[]}],\"tenantId\":1,\"timeout\":0}"; @Test public void testQueryProccessDefinitionList() { String projectName = "project_test1"; Loading Loading @@ -149,7 +159,7 @@ public class ProcessDefinitionServiceTest { } @Test public void testQueryProccessDefinitionById() { public void testQueryProcessDefinitionById() { String projectName = "project_test1"; Mockito.when(projectMapper.queryByName(projectName)).thenReturn(getProject(projectName)); Loading Loading @@ -255,7 +265,7 @@ public class ProcessDefinitionServiceTest { "project_test1", 46); Assert.assertEquals(Status.DELETE_PROCESS_DEFINE_BY_ID_ERROR, deleteFail.get(Constants.STATUS)); //delte success //delete success Mockito.when(processDefineMapper.deleteById(46)).thenReturn(1); Map<String, Object> deleteSuccess = processDefinitionService.deleteProcessDefinitionById(loginUser, "project_test1", 46); Loading Loading @@ -304,6 +314,155 @@ public class ProcessDefinitionServiceTest { Assert.assertEquals(Status.REQUEST_PARAMS_NOT_VALID_ERROR, failRes.get(Constants.STATUS)); } @Test public void testVerifyProcessDefinitionName() { String projectName = "project_test1"; Mockito.when(projectMapper.queryByName(projectName)).thenReturn(getProject(projectName)); Project project = getProject(projectName); User loginUser = new User(); loginUser.setId(-1); loginUser.setUserType(UserType.GENERAL_USER); //project check auth fail Map<String, Object> result = new HashMap<>(5); putMsg(result, Status.PROJECT_NOT_FOUNT, projectName); Mockito.when(projectService.checkProjectAndAuth(loginUser,project,projectName)).thenReturn(result); Map<String, Object> map = processDefinitionService.verifyProccessDefinitionName(loginUser, "project_test1", "test_pdf"); Assert.assertEquals(Status.PROJECT_NOT_FOUNT, map.get(Constants.STATUS)); //project check auth success, process not exist putMsg(result, Status.SUCCESS, projectName); Mockito.when(processDefineMapper.queryByDefineName(project.getId(),"test_pdf")).thenReturn(null); Map<String, Object> processNotExistRes = processDefinitionService.verifyProccessDefinitionName(loginUser, "project_test1", "test_pdf"); Assert.assertEquals(Status.SUCCESS, processNotExistRes.get(Constants.STATUS)); //process exist Mockito.when(processDefineMapper.queryByDefineName(project.getId(),"test_pdf")).thenReturn(getProcessDefinition()); Map<String, Object> processExistRes = processDefinitionService.verifyProccessDefinitionName(loginUser, "project_test1", "test_pdf"); Assert.assertEquals(Status.PROCESS_INSTANCE_EXIST, processExistRes.get(Constants.STATUS)); } @Test public void testCheckProcessNodeList() { Map<String, Object> dataNotValidRes = processDefinitionService.checkProcessNodeList(null, ""); Assert.assertEquals(Status.DATA_IS_NOT_VALID, dataNotValidRes.get(Constants.STATUS)); //task not empty String processDefinitionJson = shellJson; ProcessData processData = JSONUtils.parseObject(processDefinitionJson, ProcessData.class); assert processData != null; Map<String, Object> taskEmptyRes = processDefinitionService.checkProcessNodeList(processData, processDefinitionJson); Assert.assertEquals(Status.SUCCESS, taskEmptyRes.get(Constants.STATUS)); //task empty processData.setTasks(null); Map<String, Object> taskNotEmptyRes = processDefinitionService.checkProcessNodeList(processData, processDefinitionJson); Assert.assertEquals(Status.DATA_IS_NULL, taskNotEmptyRes.get(Constants.STATUS)); //json abnormal String abnormalJson = processDefinitionJson.replaceAll("SHELL",""); processData = JSONUtils.parseObject(abnormalJson, ProcessData.class); Map<String, Object> abnormalTaskRes = processDefinitionService.checkProcessNodeList(processData, abnormalJson); Assert.assertEquals(Status.PROCESS_NODE_S_PARAMETER_INVALID, abnormalTaskRes.get(Constants.STATUS)); } @Test public void testGetTaskNodeListByDefinitionId() throws Exception { //process definition not exist Mockito.when(processDefineMapper.selectById(46)).thenReturn(null); Map<String, Object> processDefinitionNullRes = processDefinitionService.getTaskNodeListByDefinitionId(46); Assert.assertEquals(Status.PROCESS_DEFINE_NOT_EXIST, processDefinitionNullRes.get(Constants.STATUS)); //process data null ProcessDefinition processDefinition = getProcessDefinition(); Mockito.when(processDefineMapper.selectById(46)).thenReturn(processDefinition); Map<String, Object> successRes = processDefinitionService.getTaskNodeListByDefinitionId(46); Assert.assertEquals(Status.DATA_IS_NOT_VALID, successRes.get(Constants.STATUS)); //success processDefinition.setProcessDefinitionJson(shellJson); Mockito.when(processDefineMapper.selectById(46)).thenReturn(processDefinition); Map<String, Object> dataNotValidRes = processDefinitionService.getTaskNodeListByDefinitionId(46); Assert.assertEquals(Status.SUCCESS, dataNotValidRes.get(Constants.STATUS)); } @Test public void testGetTaskNodeListByDefinitionIdList() throws Exception { //process definition not exist String defineIdList = "46"; Integer[] idArray = {46}; Mockito.when(processDefineMapper.queryDefinitionListByIdList(idArray)).thenReturn(null); Map<String, Object> processNotExistRes = processDefinitionService.getTaskNodeListByDefinitionIdList(defineIdList); Assert.assertEquals(Status.PROCESS_DEFINE_NOT_EXIST, processNotExistRes.get(Constants.STATUS)); //process definition exist ProcessDefinition processDefinition = getProcessDefinition(); processDefinition.setProcessDefinitionJson(shellJson); List<ProcessDefinition> processDefinitionList = new ArrayList<>(); processDefinitionList.add(processDefinition); Mockito.when(processDefineMapper.queryDefinitionListByIdList(idArray)).thenReturn(processDefinitionList); Map<String, Object> successRes = processDefinitionService.getTaskNodeListByDefinitionIdList(defineIdList); Assert.assertEquals(Status.SUCCESS, successRes.get(Constants.STATUS)); } @Test public void testQueryProccessDefinitionAllByProjectId() { int projectId = 1; ProcessDefinition processDefinition = getProcessDefinition(); processDefinition.setProcessDefinitionJson(shellJson); List<ProcessDefinition> processDefinitionList = new ArrayList<>(); processDefinitionList.add(processDefinition); Mockito.when(processDefineMapper.queryAllDefinitionList(projectId)).thenReturn(processDefinitionList); Map<String, Object> successRes = processDefinitionService.queryProccessDefinitionAllByProjectId(projectId); Assert.assertEquals(Status.SUCCESS, successRes.get(Constants.STATUS)); } @Test public void testViewTree() throws Exception { //process definition not exist ProcessDefinition processDefinition = getProcessDefinition(); processDefinition.setProcessDefinitionJson(shellJson); Mockito.when(processDefineMapper.selectById(46)).thenReturn(null); Map<String, Object> processDefinitionNullRes = processDefinitionService.viewTree(46, 10); Assert.assertEquals(Status.PROCESS_DEFINE_NOT_EXIST, processDefinitionNullRes.get(Constants.STATUS)); List<ProcessInstance> processInstanceList = new ArrayList<>(); ProcessInstance processInstance = new ProcessInstance(); processInstance.setId(1); processInstance.setName("test_instance"); processInstance.setState(ExecutionStatus.RUNNING_EXEUTION); processInstance.setHost("192.168.xx.xx"); processInstance.setStartTime(new Date()); processInstance.setEndTime(new Date()); processInstanceList.add(processInstance); TaskInstance taskInstance = new TaskInstance(); taskInstance.setStartTime(new Date()); taskInstance.setEndTime(new Date()); taskInstance.setTaskType("SHELL"); taskInstance.setId(1); taskInstance.setName("test_task_instance"); taskInstance.setState(ExecutionStatus.RUNNING_EXEUTION); taskInstance.setHost("192.168.xx.xx"); //task instance not exist Mockito.when(processDefineMapper.selectById(46)).thenReturn(processDefinition); Mockito.when(processInstanceMapper.queryByProcessDefineId(46, 10)).thenReturn(processInstanceList); Mockito.when(taskInstanceMapper.queryByInstanceIdAndName(processInstance.getId(), "shell-1")).thenReturn(null); Map<String, Object> taskNullRes = processDefinitionService.viewTree(46, 10); Assert.assertEquals(Status.SUCCESS, taskNullRes.get(Constants.STATUS)); //task instance exist Mockito.when(taskInstanceMapper.queryByInstanceIdAndName(processInstance.getId(), "shell-1")).thenReturn(taskInstance); Map<String, Object> taskNotNuLLRes = processDefinitionService.viewTree(46, 10); Assert.assertEquals(Status.SUCCESS, taskNotNuLLRes.get(Constants.STATUS)); } /** * add datasource param and dependent when export process * @throws JSONException Loading Loading @@ -334,13 +493,9 @@ public class ProcessDefinitionServiceTest { @Test public void testAddExportTaskNodeSpecialParam() throws JSONException { String shellJson = "{\"globalParams\":[],\"tasks\":[{\"id\":\"tasks-9527\",\"name\":\"shell-1\"," + "\"params\":{\"resourceList\":[],\"localParams\":[],\"rawScript\":\"#!/bin/bash\\necho \\\"shell-1\\\"\"}," + "\"description\":\"\",\"runFlag\":\"NORMAL\",\"dependence\":{},\"maxRetryTimes\":\"0\",\"retryInterval\":\"1\"," + "\"timeout\":{\"strategy\":\"\",\"interval\":1,\"enable\":false},\"taskInstancePriority\":\"MEDIUM\"," + "\"workerGroupId\":-1,\"preTasks\":[]}],\"tenantId\":1,\"timeout\":0}"; String shellData = shellJson; String resultStr = processDefinitionService.addExportTaskNodeSpecialParam(shellJson); String resultStr = processDefinitionService.addExportTaskNodeSpecialParam(shellData); JSONAssert.assertEquals(shellJson, resultStr, false); } Loading Loading @@ -610,7 +765,7 @@ public class ProcessDefinitionServiceTest { private ProcessDefinition getProcessDefinition(){ ProcessDefinition processDefinition = new ProcessDefinition(); processDefinition.setId(46); processDefinition.setName("testProject"); processDefinition.setName("test_pdf"); processDefinition.setProjectId(2); processDefinition.setTenantId(1); processDefinition.setDescription(""); Loading dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/DagHelper.java +29 −6 Original line number Diff line number Diff line Loading @@ -319,18 +319,14 @@ public class DagHelper { DAG<String,TaskNode,TaskNodeRelation> dag = new DAG<>(); /** * add vertex */ //add vertex if (CollectionUtils.isNotEmpty(processDag.getNodes())){ for (TaskNode node : processDag.getNodes()){ dag.addNode(node.getName(),node); } } /** * add edge */ //add edge if (CollectionUtils.isNotEmpty(processDag.getEdges())){ for (TaskNodeRelation edge : processDag.getEdges()){ dag.addEdge(edge.getStartNode(),edge.getEndNode()); Loading @@ -338,4 +334,31 @@ public class DagHelper { } return dag; } /** * get process dag * @param taskNodeList task node list * @return Process dag */ public static ProcessDag getProcessDag(List<TaskNode> taskNodeList) { List<TaskNodeRelation> taskNodeRelations = new ArrayList<>(); // Traverse node information and build relationships for (TaskNode taskNode : taskNodeList) { String preTasks = taskNode.getPreTasks(); List<String> preTasksList = JSONUtils.toList(preTasks, String.class); // If the dependency is not empty if (preTasksList != null) { for (String depNode : preTasksList) { taskNodeRelations.add(new TaskNodeRelation(depNode, taskNode.getName())); } } } ProcessDag processDag = new ProcessDag(); processDag.setEdges(taskNodeRelations); processDag.setNodes(taskNodeList); return processDag; } } dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/utils/DagHelperTest.java +18 −1 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import org.apache.dolphinscheduler.common.graph.DAG; import org.apache.dolphinscheduler.common.model.TaskNode; import org.apache.dolphinscheduler.common.model.TaskNodeRelation; import org.apache.dolphinscheduler.common.process.ProcessDag; import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.dao.entity.ProcessData; import org.apache.dolphinscheduler.dao.entity.TaskInstance; import org.junit.Assert; import org.junit.Test; Loading @@ -37,7 +39,6 @@ import java.util.Map; * dag helper test */ public class DagHelperTest { /** * test task node can submit * @throws JsonProcessingException if error throws JsonProcessingException Loading Loading @@ -131,4 +132,20 @@ public class DagHelperTest { return DagHelper.buildDagGraph(processDag); } @Test public void testBuildDagGraph() { String shellJson = "{\"globalParams\":[],\"tasks\":[{\"type\":\"SHELL\",\"id\":\"tasks-9527\",\"name\":\"shell-1\"," + "\"params\":{\"resourceList\":[],\"localParams\":[],\"rawScript\":\"#!/bin/bash\\necho \\\"shell-1\\\"\"}," + "\"description\":\"\",\"runFlag\":\"NORMAL\",\"dependence\":{},\"maxRetryTimes\":\"0\",\"retryInterval\":\"1\"," + "\"timeout\":{\"strategy\":\"\",\"interval\":1,\"enable\":false},\"taskInstancePriority\":\"MEDIUM\"," + "\"workerGroupId\":-1,\"preTasks\":[]}],\"tenantId\":1,\"timeout\":0}"; ProcessData processData = JSONUtils.parseObject(shellJson, ProcessData.class); assert processData != null; List<TaskNode> taskNodeList = processData.getTasks(); ProcessDag processDag = DagHelper.getProcessDag(taskNodeList); DAG<String, TaskNode, TaskNodeRelation> dag = DagHelper.buildDagGraph(processDag); Assert.assertNotNull(dag); } } Loading
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/BaseDAGService.java +3 −37 Original line number Diff line number Diff line Loading @@ -20,12 +20,11 @@ import org.apache.dolphinscheduler.common.graph.DAG; import org.apache.dolphinscheduler.common.model.TaskNode; import org.apache.dolphinscheduler.common.model.TaskNodeRelation; import org.apache.dolphinscheduler.common.process.ProcessDag; import org.apache.dolphinscheduler.common.utils.CollectionUtils; import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.dao.entity.ProcessData; import org.apache.dolphinscheduler.dao.entity.ProcessInstance; import org.apache.dolphinscheduler.dao.utils.DagHelper; import java.util.ArrayList; import java.util.List; /** Loading @@ -48,41 +47,8 @@ public class BaseDAGService extends BaseService{ List<TaskNode> taskNodeList = processData.getTasks(); List<TaskNodeRelation> taskNodeRelations = new ArrayList<>(); ProcessDag processDag = DagHelper.getProcessDag(taskNodeList); //Traversing node information and building relationships for (TaskNode taskNode : taskNodeList) { String preTasks = taskNode.getPreTasks(); List<String> preTasksList = JSONUtils.toList(preTasks, String.class); //if previous tasks not empty if (preTasksList != null) { for (String depNode : preTasksList) { taskNodeRelations.add(new TaskNodeRelation(depNode, taskNode.getName())); } } } ProcessDag processDag = new ProcessDag(); processDag.setEdges(taskNodeRelations); processDag.setNodes(taskNodeList); // generate detail Dag, to be executed DAG<String, TaskNode, TaskNodeRelation> dag = new DAG<>(); if (CollectionUtils.isNotEmpty(processDag.getNodes())) { for (TaskNode node : processDag.getNodes()) { dag.addNode(node.getName(), node); } } if (CollectionUtils.isNotEmpty(processDag.getEdges())) { for (TaskNodeRelation edge : processDag.getEdges()) { dag.addEdge(edge.getStartNode(), edge.getEndNode()); } } return dag; return DagHelper.buildDagGraph(processDag); } }
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/ProcessDefinitionService.java +26 −67 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.common.utils.StringUtils; import org.apache.dolphinscheduler.dao.entity.*; import org.apache.dolphinscheduler.dao.mapper.*; import org.apache.dolphinscheduler.dao.utils.DagHelper; import org.apache.dolphinscheduler.service.process.ProcessService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; Loading Loading @@ -465,7 +466,7 @@ public class ProcessDefinitionService extends BaseDAGService { ); for(Schedule schedule:scheduleList){ logger.info("set schedule offline, schedule id: {}, process definition id: {}", project.getId(), schedule.getId(), id); logger.info("set schedule offline, project id: {}, schedule id: {}, process definition id: {}", project.getId(), schedule.getId(), id); // set status schedule.setReleaseState(ReleaseState.OFFLINE); scheduleMapper.updateById(schedule); Loading Loading @@ -948,11 +949,16 @@ public class ProcessDefinitionService extends BaseDAGService { return result; } String processDefinitionJson = processDefinition.getProcessDefinitionJson(); ProcessData processData = JSONUtils.parseObject(processDefinitionJson, ProcessData.class); //process data check if (null == processData) { logger.error("process data is null"); putMsg(result,Status.DATA_IS_NOT_VALID, processDefinitionJson); return result; } List<TaskNode> taskNodeList = (processData.getTasks() == null) ? new ArrayList<>() : processData.getTasks(); result.put(Constants.DATA_LIST, taskNodeList); Loading @@ -974,14 +980,13 @@ public class ProcessDefinitionService extends BaseDAGService { Map<Integer, List<TaskNode>> taskNodeMap = new HashMap<>(); String[] idList = defineIdList.split(","); List<String> definitionIdList = Arrays.asList(idList); List<Integer> idIntList = new ArrayList<>(); for(String definitionId : definitionIdList) { for(String definitionId : idList) { idIntList.add(Integer.parseInt(definitionId)); } Integer[] idArray = idIntList.toArray(new Integer[idIntList.size()]); List<ProcessDefinition> processDefinitionList = processDefineMapper.queryDefinitionListByIdList(idArray); if (processDefinitionList == null || processDefinitionList.size() ==0) { if (CollectionUtils.isEmpty(processDefinitionList)) { logger.info("process definition not exists"); putMsg(result, Status.PROCESS_DEFINE_NOT_EXIST, defineIdList); return result; Loading Loading @@ -1031,9 +1036,10 @@ public class ProcessDefinitionService extends BaseDAGService { Map<String, Object> result = new HashMap<>(); ProcessDefinition processDefinition = processDefineMapper.selectById(processId); if (processDefinition == null) { if (null == processDefinition) { logger.info("process define not exists"); throw new RuntimeException("process define not exists"); putMsg(result,Status.PROCESS_DEFINE_NOT_EXIST, processDefinition); return result; } DAG<String, TaskNode, TaskNodeRelation> dag = genDagGraph(processDefinition); /** Loading Loading @@ -1121,10 +1127,10 @@ public class ProcessDefinitionService extends BaseDAGService { pTreeViewDto.getChildren().add(treeViewDto); } postNodeList = dag.getSubsequentNodes(nodeName); if (postNodeList != null && postNodeList.size() > 0) { if (CollectionUtils.isNotEmpty(postNodeList)) { for (String nextNodeName : postNodeList) { List<TreeViewDto> treeViewDtoList = waitingRunningNodeMap.get(nextNodeName); if (treeViewDtoList != null && treeViewDtoList.size() > 0) { if (CollectionUtils.isNotEmpty(treeViewDtoList)) { treeViewDtoList.add(treeViewDto); waitingRunningNodeMap.put(nextNodeName, treeViewDtoList); } else { Loading @@ -1136,7 +1142,6 @@ public class ProcessDefinitionService extends BaseDAGService { } runningNodeMap.remove(nodeName); } if (waitingRunningNodeMap == null || waitingRunningNodeMap.size() == 0) { break; } else { Loading @@ -1161,75 +1166,29 @@ public class ProcessDefinitionService extends BaseDAGService { private DAG<String, TaskNode, TaskNodeRelation> genDagGraph(ProcessDefinition processDefinition) throws Exception { String processDefinitionJson = processDefinition.getProcessDefinitionJson(); ProcessData processData = JSONUtils.parseObject(processDefinitionJson, ProcessData.class); //check process data if (null != processData) { List<TaskNode> taskNodeList = processData.getTasks(); processDefinition.setGlobalParamList(processData.getGlobalParams()); List<TaskNodeRelation> taskNodeRelations = new ArrayList<>(); // Traverse node information and build relationships for (TaskNode taskNode : taskNodeList) { String preTasks = taskNode.getPreTasks(); List<String> preTasksList = JSONUtils.toList(preTasks, String.class); // If the dependency is not empty if (preTasksList != null) { for (String depNode : preTasksList) { taskNodeRelations.add(new TaskNodeRelation(depNode, taskNode.getName())); } } } ProcessDag processDag = new ProcessDag(); processDag.setEdges(taskNodeRelations); processDag.setNodes(taskNodeList); ProcessDag processDag = DagHelper.getProcessDag(taskNodeList); // Generate concrete Dag to be executed return genDagGraph(processDag); return DagHelper.buildDagGraph(processDag); } /** * Generate the DAG of process * * @return DAG */ private DAG<String, TaskNode, TaskNodeRelation> genDagGraph(ProcessDag processDag) { DAG<String, TaskNode, TaskNodeRelation> dag = new DAG<>(); /** * Add the ndoes */ if (CollectionUtils.isNotEmpty(processDag.getNodes())) { for (TaskNode node : processDag.getNodes()) { dag.addNode(node.getName(), node); } return new DAG<>(); } /** * Add the edges */ if (CollectionUtils.isNotEmpty(processDag.getEdges())) { for (TaskNodeRelation edge : processDag.getEdges()) { dag.addEdge(edge.getStartNode(), edge.getEndNode()); } } return dag; } /** * whether the graph has a ring * * @param taskNodeResponseList * @return * @param taskNodeResponseList task node response list * @return if graph has cycle flag */ private boolean graphHasCycle(List<TaskNode> taskNodeResponseList) { DAG<String, TaskNode, String> graph = new DAG<>(); Loading
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProcessDefinitionServiceTest.java +166 −11 Original line number Diff line number Diff line Loading @@ -16,10 +16,8 @@ */ package org.apache.dolphinscheduler.api.service; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.metadata.IPage; import org.apache.dolphinscheduler.api.ApiApplicationServer; import org.apache.dolphinscheduler.api.dto.ProcessMeta; import org.apache.dolphinscheduler.api.enums.Status; Loading Loading @@ -82,6 +80,12 @@ public class ProcessDefinitionServiceTest { @Mock private ProcessService processService; @Mock private ProcessInstanceMapper processInstanceMapper; @Mock private TaskInstanceMapper taskInstanceMapper; private String sqlDependentJson = "{\"globalParams\":[]," + "\"tasks\":[{\"type\":\"SQL\",\"id\":\"tasks-27297\",\"name\":\"sql\"," + "\"params\":{\"type\":\"MYSQL\",\"datasource\":1,\"sql\":\"select * from test\"," + Loading @@ -99,6 +103,12 @@ public class ProcessDefinitionServiceTest { "\"timeout\":{\"strategy\":\"\",\"enable\":false},\"taskInstancePriority\":\"MEDIUM\"," + "\"workerGroupId\":-1,\"preTasks\":[]}],\"tenantId\":1,\"timeout\":0}"; private String shellJson = "{\"globalParams\":[],\"tasks\":[{\"type\":\"SHELL\",\"id\":\"tasks-9527\",\"name\":\"shell-1\"," + "\"params\":{\"resourceList\":[],\"localParams\":[],\"rawScript\":\"#!/bin/bash\\necho \\\"shell-1\\\"\"}," + "\"description\":\"\",\"runFlag\":\"NORMAL\",\"dependence\":{},\"maxRetryTimes\":\"0\",\"retryInterval\":\"1\"," + "\"timeout\":{\"strategy\":\"\",\"interval\":1,\"enable\":false},\"taskInstancePriority\":\"MEDIUM\"," + "\"workerGroupId\":-1,\"preTasks\":[]}],\"tenantId\":1,\"timeout\":0}"; @Test public void testQueryProccessDefinitionList() { String projectName = "project_test1"; Loading Loading @@ -149,7 +159,7 @@ public class ProcessDefinitionServiceTest { } @Test public void testQueryProccessDefinitionById() { public void testQueryProcessDefinitionById() { String projectName = "project_test1"; Mockito.when(projectMapper.queryByName(projectName)).thenReturn(getProject(projectName)); Loading Loading @@ -255,7 +265,7 @@ public class ProcessDefinitionServiceTest { "project_test1", 46); Assert.assertEquals(Status.DELETE_PROCESS_DEFINE_BY_ID_ERROR, deleteFail.get(Constants.STATUS)); //delte success //delete success Mockito.when(processDefineMapper.deleteById(46)).thenReturn(1); Map<String, Object> deleteSuccess = processDefinitionService.deleteProcessDefinitionById(loginUser, "project_test1", 46); Loading Loading @@ -304,6 +314,155 @@ public class ProcessDefinitionServiceTest { Assert.assertEquals(Status.REQUEST_PARAMS_NOT_VALID_ERROR, failRes.get(Constants.STATUS)); } @Test public void testVerifyProcessDefinitionName() { String projectName = "project_test1"; Mockito.when(projectMapper.queryByName(projectName)).thenReturn(getProject(projectName)); Project project = getProject(projectName); User loginUser = new User(); loginUser.setId(-1); loginUser.setUserType(UserType.GENERAL_USER); //project check auth fail Map<String, Object> result = new HashMap<>(5); putMsg(result, Status.PROJECT_NOT_FOUNT, projectName); Mockito.when(projectService.checkProjectAndAuth(loginUser,project,projectName)).thenReturn(result); Map<String, Object> map = processDefinitionService.verifyProccessDefinitionName(loginUser, "project_test1", "test_pdf"); Assert.assertEquals(Status.PROJECT_NOT_FOUNT, map.get(Constants.STATUS)); //project check auth success, process not exist putMsg(result, Status.SUCCESS, projectName); Mockito.when(processDefineMapper.queryByDefineName(project.getId(),"test_pdf")).thenReturn(null); Map<String, Object> processNotExistRes = processDefinitionService.verifyProccessDefinitionName(loginUser, "project_test1", "test_pdf"); Assert.assertEquals(Status.SUCCESS, processNotExistRes.get(Constants.STATUS)); //process exist Mockito.when(processDefineMapper.queryByDefineName(project.getId(),"test_pdf")).thenReturn(getProcessDefinition()); Map<String, Object> processExistRes = processDefinitionService.verifyProccessDefinitionName(loginUser, "project_test1", "test_pdf"); Assert.assertEquals(Status.PROCESS_INSTANCE_EXIST, processExistRes.get(Constants.STATUS)); } @Test public void testCheckProcessNodeList() { Map<String, Object> dataNotValidRes = processDefinitionService.checkProcessNodeList(null, ""); Assert.assertEquals(Status.DATA_IS_NOT_VALID, dataNotValidRes.get(Constants.STATUS)); //task not empty String processDefinitionJson = shellJson; ProcessData processData = JSONUtils.parseObject(processDefinitionJson, ProcessData.class); assert processData != null; Map<String, Object> taskEmptyRes = processDefinitionService.checkProcessNodeList(processData, processDefinitionJson); Assert.assertEquals(Status.SUCCESS, taskEmptyRes.get(Constants.STATUS)); //task empty processData.setTasks(null); Map<String, Object> taskNotEmptyRes = processDefinitionService.checkProcessNodeList(processData, processDefinitionJson); Assert.assertEquals(Status.DATA_IS_NULL, taskNotEmptyRes.get(Constants.STATUS)); //json abnormal String abnormalJson = processDefinitionJson.replaceAll("SHELL",""); processData = JSONUtils.parseObject(abnormalJson, ProcessData.class); Map<String, Object> abnormalTaskRes = processDefinitionService.checkProcessNodeList(processData, abnormalJson); Assert.assertEquals(Status.PROCESS_NODE_S_PARAMETER_INVALID, abnormalTaskRes.get(Constants.STATUS)); } @Test public void testGetTaskNodeListByDefinitionId() throws Exception { //process definition not exist Mockito.when(processDefineMapper.selectById(46)).thenReturn(null); Map<String, Object> processDefinitionNullRes = processDefinitionService.getTaskNodeListByDefinitionId(46); Assert.assertEquals(Status.PROCESS_DEFINE_NOT_EXIST, processDefinitionNullRes.get(Constants.STATUS)); //process data null ProcessDefinition processDefinition = getProcessDefinition(); Mockito.when(processDefineMapper.selectById(46)).thenReturn(processDefinition); Map<String, Object> successRes = processDefinitionService.getTaskNodeListByDefinitionId(46); Assert.assertEquals(Status.DATA_IS_NOT_VALID, successRes.get(Constants.STATUS)); //success processDefinition.setProcessDefinitionJson(shellJson); Mockito.when(processDefineMapper.selectById(46)).thenReturn(processDefinition); Map<String, Object> dataNotValidRes = processDefinitionService.getTaskNodeListByDefinitionId(46); Assert.assertEquals(Status.SUCCESS, dataNotValidRes.get(Constants.STATUS)); } @Test public void testGetTaskNodeListByDefinitionIdList() throws Exception { //process definition not exist String defineIdList = "46"; Integer[] idArray = {46}; Mockito.when(processDefineMapper.queryDefinitionListByIdList(idArray)).thenReturn(null); Map<String, Object> processNotExistRes = processDefinitionService.getTaskNodeListByDefinitionIdList(defineIdList); Assert.assertEquals(Status.PROCESS_DEFINE_NOT_EXIST, processNotExistRes.get(Constants.STATUS)); //process definition exist ProcessDefinition processDefinition = getProcessDefinition(); processDefinition.setProcessDefinitionJson(shellJson); List<ProcessDefinition> processDefinitionList = new ArrayList<>(); processDefinitionList.add(processDefinition); Mockito.when(processDefineMapper.queryDefinitionListByIdList(idArray)).thenReturn(processDefinitionList); Map<String, Object> successRes = processDefinitionService.getTaskNodeListByDefinitionIdList(defineIdList); Assert.assertEquals(Status.SUCCESS, successRes.get(Constants.STATUS)); } @Test public void testQueryProccessDefinitionAllByProjectId() { int projectId = 1; ProcessDefinition processDefinition = getProcessDefinition(); processDefinition.setProcessDefinitionJson(shellJson); List<ProcessDefinition> processDefinitionList = new ArrayList<>(); processDefinitionList.add(processDefinition); Mockito.when(processDefineMapper.queryAllDefinitionList(projectId)).thenReturn(processDefinitionList); Map<String, Object> successRes = processDefinitionService.queryProccessDefinitionAllByProjectId(projectId); Assert.assertEquals(Status.SUCCESS, successRes.get(Constants.STATUS)); } @Test public void testViewTree() throws Exception { //process definition not exist ProcessDefinition processDefinition = getProcessDefinition(); processDefinition.setProcessDefinitionJson(shellJson); Mockito.when(processDefineMapper.selectById(46)).thenReturn(null); Map<String, Object> processDefinitionNullRes = processDefinitionService.viewTree(46, 10); Assert.assertEquals(Status.PROCESS_DEFINE_NOT_EXIST, processDefinitionNullRes.get(Constants.STATUS)); List<ProcessInstance> processInstanceList = new ArrayList<>(); ProcessInstance processInstance = new ProcessInstance(); processInstance.setId(1); processInstance.setName("test_instance"); processInstance.setState(ExecutionStatus.RUNNING_EXEUTION); processInstance.setHost("192.168.xx.xx"); processInstance.setStartTime(new Date()); processInstance.setEndTime(new Date()); processInstanceList.add(processInstance); TaskInstance taskInstance = new TaskInstance(); taskInstance.setStartTime(new Date()); taskInstance.setEndTime(new Date()); taskInstance.setTaskType("SHELL"); taskInstance.setId(1); taskInstance.setName("test_task_instance"); taskInstance.setState(ExecutionStatus.RUNNING_EXEUTION); taskInstance.setHost("192.168.xx.xx"); //task instance not exist Mockito.when(processDefineMapper.selectById(46)).thenReturn(processDefinition); Mockito.when(processInstanceMapper.queryByProcessDefineId(46, 10)).thenReturn(processInstanceList); Mockito.when(taskInstanceMapper.queryByInstanceIdAndName(processInstance.getId(), "shell-1")).thenReturn(null); Map<String, Object> taskNullRes = processDefinitionService.viewTree(46, 10); Assert.assertEquals(Status.SUCCESS, taskNullRes.get(Constants.STATUS)); //task instance exist Mockito.when(taskInstanceMapper.queryByInstanceIdAndName(processInstance.getId(), "shell-1")).thenReturn(taskInstance); Map<String, Object> taskNotNuLLRes = processDefinitionService.viewTree(46, 10); Assert.assertEquals(Status.SUCCESS, taskNotNuLLRes.get(Constants.STATUS)); } /** * add datasource param and dependent when export process * @throws JSONException Loading Loading @@ -334,13 +493,9 @@ public class ProcessDefinitionServiceTest { @Test public void testAddExportTaskNodeSpecialParam() throws JSONException { String shellJson = "{\"globalParams\":[],\"tasks\":[{\"id\":\"tasks-9527\",\"name\":\"shell-1\"," + "\"params\":{\"resourceList\":[],\"localParams\":[],\"rawScript\":\"#!/bin/bash\\necho \\\"shell-1\\\"\"}," + "\"description\":\"\",\"runFlag\":\"NORMAL\",\"dependence\":{},\"maxRetryTimes\":\"0\",\"retryInterval\":\"1\"," + "\"timeout\":{\"strategy\":\"\",\"interval\":1,\"enable\":false},\"taskInstancePriority\":\"MEDIUM\"," + "\"workerGroupId\":-1,\"preTasks\":[]}],\"tenantId\":1,\"timeout\":0}"; String shellData = shellJson; String resultStr = processDefinitionService.addExportTaskNodeSpecialParam(shellJson); String resultStr = processDefinitionService.addExportTaskNodeSpecialParam(shellData); JSONAssert.assertEquals(shellJson, resultStr, false); } Loading Loading @@ -610,7 +765,7 @@ public class ProcessDefinitionServiceTest { private ProcessDefinition getProcessDefinition(){ ProcessDefinition processDefinition = new ProcessDefinition(); processDefinition.setId(46); processDefinition.setName("testProject"); processDefinition.setName("test_pdf"); processDefinition.setProjectId(2); processDefinition.setTenantId(1); processDefinition.setDescription(""); Loading
dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/DagHelper.java +29 −6 Original line number Diff line number Diff line Loading @@ -319,18 +319,14 @@ public class DagHelper { DAG<String,TaskNode,TaskNodeRelation> dag = new DAG<>(); /** * add vertex */ //add vertex if (CollectionUtils.isNotEmpty(processDag.getNodes())){ for (TaskNode node : processDag.getNodes()){ dag.addNode(node.getName(),node); } } /** * add edge */ //add edge if (CollectionUtils.isNotEmpty(processDag.getEdges())){ for (TaskNodeRelation edge : processDag.getEdges()){ dag.addEdge(edge.getStartNode(),edge.getEndNode()); Loading @@ -338,4 +334,31 @@ public class DagHelper { } return dag; } /** * get process dag * @param taskNodeList task node list * @return Process dag */ public static ProcessDag getProcessDag(List<TaskNode> taskNodeList) { List<TaskNodeRelation> taskNodeRelations = new ArrayList<>(); // Traverse node information and build relationships for (TaskNode taskNode : taskNodeList) { String preTasks = taskNode.getPreTasks(); List<String> preTasksList = JSONUtils.toList(preTasks, String.class); // If the dependency is not empty if (preTasksList != null) { for (String depNode : preTasksList) { taskNodeRelations.add(new TaskNodeRelation(depNode, taskNode.getName())); } } } ProcessDag processDag = new ProcessDag(); processDag.setEdges(taskNodeRelations); processDag.setNodes(taskNodeList); return processDag; } }
dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/utils/DagHelperTest.java +18 −1 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import org.apache.dolphinscheduler.common.graph.DAG; import org.apache.dolphinscheduler.common.model.TaskNode; import org.apache.dolphinscheduler.common.model.TaskNodeRelation; import org.apache.dolphinscheduler.common.process.ProcessDag; import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.dao.entity.ProcessData; import org.apache.dolphinscheduler.dao.entity.TaskInstance; import org.junit.Assert; import org.junit.Test; Loading @@ -37,7 +39,6 @@ import java.util.Map; * dag helper test */ public class DagHelperTest { /** * test task node can submit * @throws JsonProcessingException if error throws JsonProcessingException Loading Loading @@ -131,4 +132,20 @@ public class DagHelperTest { return DagHelper.buildDagGraph(processDag); } @Test public void testBuildDagGraph() { String shellJson = "{\"globalParams\":[],\"tasks\":[{\"type\":\"SHELL\",\"id\":\"tasks-9527\",\"name\":\"shell-1\"," + "\"params\":{\"resourceList\":[],\"localParams\":[],\"rawScript\":\"#!/bin/bash\\necho \\\"shell-1\\\"\"}," + "\"description\":\"\",\"runFlag\":\"NORMAL\",\"dependence\":{},\"maxRetryTimes\":\"0\",\"retryInterval\":\"1\"," + "\"timeout\":{\"strategy\":\"\",\"interval\":1,\"enable\":false},\"taskInstancePriority\":\"MEDIUM\"," + "\"workerGroupId\":-1,\"preTasks\":[]}],\"tenantId\":1,\"timeout\":0}"; ProcessData processData = JSONUtils.parseObject(shellJson, ProcessData.class); assert processData != null; List<TaskNode> taskNodeList = processData.getTasks(); ProcessDag processDag = DagHelper.getProcessDag(taskNodeList); DAG<String, TaskNode, TaskNodeRelation> dag = DagHelper.buildDagGraph(processDag); Assert.assertNotNull(dag); } }