Skip to content

Commit

Permalink
TE-607: Handle TaskEnd event in subprocess for find tasks on path
Browse files Browse the repository at this point in the history
  • Loading branch information
trungmaihova committed Jun 13, 2024
1 parent 028553a commit f897f82
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 44 deletions.
192 changes: 166 additions & 26 deletions process-analyzer-test/processes/FlowSubprocess.p.json
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@
"at" : { "x" : 96, "y" : 440 }
},
"connect" : [
{ "id" : "f18", "to" : "f32", "var" : "in1" }
{ "id" : "f18", "to" : "S70" }
]
}, {
"id" : "S40",
Expand Down Expand Up @@ -686,30 +686,6 @@
"visual" : {
"at" : { "x" : 808, "y" : 440 }
}
}, {
"id" : "f32",
"type" : "UserTask",
"name" : "TaskA4",
"config" : {
"dialog" : "com.axonivy.utils.process.analyzer.test.Dummy:start()",
"task" : {
"name" : "TaskA4",
"code" : [
"import com.axonivy.utils.process.analyzer.APAConfig;",
"import com.axonivy.utils.process.analyzer.test.UseCase;",
"import java.util.concurrent.TimeUnit;",
"",
"APAConfig.setEstimate(5,TimeUnit.HOURS,UseCase.BIGPROJECT);",
"APAConfig.setEstimate(3,TimeUnit.HOURS,UseCase.SMALLPROJECT);"
]
}
},
"visual" : {
"at" : { "x" : 224, "y" : 440 }
},
"connect" : [
{ "id" : "f33", "to" : "f17", "var" : "in1" }
]
}, {
"id" : "S60",
"type" : "EmbeddedProcess",
Expand Down Expand Up @@ -762,5 +738,169 @@
"connect" : [
{ "id" : "f25", "to" : "f27", "var" : "in2" }
]
} ]
}, {
"id" : "S70",
"type" : "EmbeddedProcess",
"name" : "Sub Process 0",
"elements" : [ {
"id" : "S70-g0",
"type" : "EmbeddedStart",
"visual" : {
"at" : { "x" : 64, "y" : 256 }
},
"parentConnector" : "f18",
"connect" : [
{ "id" : "S70-f0", "to" : "S70-f1" }
]
}, {
"id" : "S70-g1",
"type" : "EmbeddedEnd",
"visual" : {
"at" : { "x" : 592, "y" : 256 }
},
"parentConnector" : "f29"
}, {
"id" : "S70-f1",
"type" : "UserTask",
"name" : "Sub0-TaskA",
"config" : {
"dialog" : "com.axonivy.utils.process.analyzer.test.Dummy:start()",
"task" : {
"name" : "Sub0-TaskA",
"code" : [
"import com.axonivy.utils.process.analyzer.APAConfig;",
"import com.axonivy.utils.process.analyzer.test.UseCase;",
"import java.util.concurrent.TimeUnit;",
"",
"APAConfig.setEstimate(5,TimeUnit.HOURS,UseCase.BIGPROJECT);",
"APAConfig.setEstimate(3,TimeUnit.HOURS,UseCase.SMALLPROJECT);"
]
}
},
"visual" : {
"at" : { "x" : 184, "y" : 256 }
},
"connect" : [
{ "id" : "S70-f2", "to" : "S70-f3" }
]
}, {
"id" : "S70-f3",
"type" : "Alternative",
"config" : {
"conditions" : {
"S70-f4" : "true",
"S70-f7" : ""
}
},
"visual" : {
"at" : { "x" : 320, "y" : 256 }
},
"connect" : [
{ "id" : "S70-f7", "to" : "S70-f22" },
{ "id" : "S70-f4", "to" : "S70-S10", "label" : {
"name" : "{happy}"
} }
]
}, {
"id" : "S70-f22",
"type" : "TaskEnd",
"visual" : {
"at" : { "x" : 320, "y" : 352 },
"color" : "End"
}
}, {
"id" : "S70-S10",
"type" : "EmbeddedProcess",
"name" : "Sub Process 0-0",
"elements" : [ {
"id" : "S70-S10-g0",
"type" : "EmbeddedStart",
"visual" : {
"at" : { "x" : 64, "y" : 256 }
},
"parentConnector" : "S70-f4",
"connect" : [
{ "id" : "S70-S10-f0", "to" : "S70-S10-f1" }
]
}, {
"id" : "S70-S10-g1",
"type" : "EmbeddedEnd",
"visual" : {
"at" : { "x" : 560, "y" : 256 }
},
"parentConnector" : "S70-f8"
}, {
"id" : "S70-S10-f1",
"type" : "UserTask",
"name" : "Sub00-TaskA",
"config" : {
"dialog" : "com.axonivy.utils.process.analyzer.test.Dummy:start()",
"task" : {
"name" : "Sub00-TaskA"
}
},
"visual" : {
"at" : { "x" : 192, "y" : 256 }
},
"connect" : [
{ "id" : "S70-S10-f2", "to" : "S70-S10-f3" }
]
}, {
"id" : "S70-S10-f3",
"type" : "Alternative",
"config" : {
"conditions" : {
"S70-S10-f4" : "true"
}
},
"visual" : {
"at" : { "x" : 320, "y" : 256 }
},
"connect" : [
{ "id" : "S70-S10-f4", "to" : "S70-S10-f5" },
{ "id" : "S70-S10-f7", "to" : "S70-S10-f22" }
]
}, {
"id" : "S70-S10-f5",
"type" : "UserTask",
"name" : "Sub00-TaskB",
"config" : {
"dialog" : "com.axonivy.utils.process.analyzer.test.Dummy:start()",
"task" : {
"name" : "Sub00-TaskB"
}
},
"visual" : {
"at" : { "x" : 432, "y" : 256 }
},
"connect" : [
{ "id" : "S70-S10-f6", "to" : "S70-S10-g1" }
]
}, {
"id" : "S70-S10-f22",
"type" : "TaskEnd",
"visual" : {
"at" : { "x" : 320, "y" : 328 },
"color" : "End"
}
} ],
"visual" : {
"at" : { "x" : 448, "y" : 256 }
},
"connect" : [
{ "id" : "S70-f8", "to" : "S70-g1" }
]
} ],
"visual" : {
"at" : { "x" : 224, "y" : 440 }
},
"connect" : [
{ "id" : "f29", "to" : "f17", "var" : "in1" }
]
} ],
"layout" : {
"colors" : {
"End" : "#ef0606"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,23 @@ void shouldFindAllTasksAtStart4() throws Exception {
var start4 = ProcessGraphHelper.findByElementName(process, "start4");
var detectedTasks = processAnalyzer.findAllTasks(start4, null);

var expected = Arrays.array("TaskA4", "Sub1-TaskA", "Sub2-TaskE", "Sub2-TaskB", "Sub2-TaskC", "Sub3-TaskA", "Sub2-TaskA", "Sub2-TaskD");
var expected = Arrays.array("Sub0-TaskA", "Sub00-TaskA", "Sub00-TaskB", "Sub1-TaskA", "Sub2-TaskE", "Sub2-TaskB", "Sub2-TaskC", "Sub3-TaskA", "Sub2-TaskA", "Sub2-TaskD");
var taskNames = getTaskNames(detectedTasks);

assertArrayEquals(expected, taskNames);
}

@Test
void shouldFindAllTasksAtStart4OnEndPath() throws Exception {
var start4 = ProcessGraphHelper.findByElementName(process, "start4");
var detectedTasks = processAnalyzer.findTasksOnPath(start4, null, "happy");

var expected = Arrays.array("Sub0-TaskA", "Sub00-TaskA");
var taskNames = getTaskNames(detectedTasks);

assertArrayEquals(expected, taskNames);
}

@Test
void shouldFindTaskParentNames() throws Exception {
var start = ProcessGraphHelper.findByElementName(process, "start");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
import ch.ivyteam.ivy.process.model.diagram.value.Label;
import ch.ivyteam.ivy.process.model.element.EmbeddedProcessElement;
import ch.ivyteam.ivy.process.model.element.SingleTaskCreator;
import ch.ivyteam.ivy.process.model.element.TaskAndCaseModifier;
import ch.ivyteam.ivy.process.model.element.event.end.TaskEnd;
import ch.ivyteam.ivy.process.model.element.event.start.RequestStart;
import ch.ivyteam.ivy.process.model.element.gateway.Alternative;
import ch.ivyteam.ivy.process.model.element.gateway.TaskSwitchGateway;
Expand All @@ -56,7 +56,7 @@ private enum FindType {
private List<ProcessElement> froms;
private String flowName;
private Map<String, String> processFlowOverrides = emptyMap();

public PathFinder() {
this.processGraph = new ProcessGraph();
}
Expand Down Expand Up @@ -133,8 +133,7 @@ private ProcessElement getParentElement(ProcessElement startElement) {
return null;
}

private Map<ProcessElement, List<AnalysisPath>> findPath(List<ProcessElement> froms, String flowName,
FindType findType) throws Exception {
private Map<ProcessElement, List<AnalysisPath>> findPath(List<ProcessElement> froms, String flowName, FindType findType) throws Exception {
Map<ProcessElement, List<AnalysisPath>> result = new LinkedHashMap<>();
for (ProcessElement from : froms) {
List<AnalysisPath> path = findAnalysisPaths(from, flowName, findType, emptyList());
Expand All @@ -147,10 +146,10 @@ private Map<ProcessElement, List<AnalysisPath>> findPath(List<ProcessElement> fr
}

// Find again from intersection task
List<AnalysisPath> subPath = findAnalysisPaths(new CommonElement(intersectionTask.getElement()), flowName,
findType, emptyList());
ProcessElement startElement = new CommonElement(intersectionTask.getElement());
Map<ProcessElement, List<AnalysisPath>> subPaths = findPath(List.of(startElement), flowName, findType);

Map<ProcessElement, List<AnalysisPath>> fullPath = mergePath(result, intersectionTask, subPath);
Map<ProcessElement, List<AnalysisPath>> fullPath = mergePath(result, intersectionTask, subPaths.getOrDefault(startElement, emptyList()));

return fullPath;
}
Expand Down Expand Up @@ -270,7 +269,8 @@ private Map<SequenceFlow, List<AnalysisPath>> convertToInternalPathForTaskParall
Map<ProcessElement, List<AnalysisPath>> internalPath) {
Map<SequenceFlow, List<AnalysisPath>> result = new LinkedHashMap<>();
internalPath.entrySet().forEach(it -> {
result.put(((TaskAndCaseModifier) it.getKey().getElement()).getIncoming().get(0), it.getValue());
NodeElement element = (NodeElement) it.getKey().getElement();
result.put(element.getIncoming().get(0), it.getValue());
});
return result;
}
Expand Down Expand Up @@ -386,8 +386,8 @@ private List<AnalysisPath> findAnalysisPaths(ProcessElement startElement, String
if (from.getElement() instanceof NodeElement) {

if (from.getElement() instanceof EmbeddedProcessElement) {
SubProcessGroup subProcessGroup = findPathOfSubProcess(from, flowName, findType, currentPath);
path = AnalysisPathHelper.removeLastElementByClassType(path, EmbeddedProcessElement.class);
SubProcessGroup subProcessGroup = findPathOfSubProcess(from, flowName, findType, currentPath);
path = AnalysisPathHelper.removeLastElementByClassType(path, EmbeddedProcessElement.class);
path = addAllToPath(path, List.of(subProcessGroup));
}

Expand Down Expand Up @@ -421,14 +421,18 @@ private List<AnalysisPath> findAnalysisPaths(ProcessElement startElement, String
}

// Call recursion for next normal node
var pathOptions = findAnalysisPathForNextNode(from, flowName, findType, currentPath);
var newPath = addToPath(currentPath, path);
if (shouldStopFindTask(newPath, findType)) {
return path;
}
var pathOptions = findAnalysisPathForNextNode(from, flowName, findType, newPath);
path = addAllToPath(path, pathOptions);
}

return path;
}

private Map<SequenceFlow, List<AnalysisPath>> findAnalysisPathForNextNode(ProcessElement from, String flowName,
private Map<SequenceFlow, List<AnalysisPath>> findAnalysisPathForNextNode(ProcessElement from, String flowName,
FindType findType, List<AnalysisPath> currentPath) throws Exception {

List<SequenceFlow> outs = getSequenceFlows((NodeElement) from.getElement(), flowName, findType);
Expand All @@ -440,7 +444,7 @@ private Map<SequenceFlow, List<AnalysisPath>> findAnalysisPathForNextNode(Proces
Map<SequenceFlow, List<AnalysisPath>> pathOptions = new LinkedHashMap<>();
for (SequenceFlow out : outs) {
CommonElement outElement = new CommonElement(out);
List<AnalysisPath> newPath = addAllToPath(currentPath, Arrays.asList(from, outElement));
List<AnalysisPath> newPath = addAllToPath(currentPath, Arrays.asList(outElement));

ProcessElement nextStartElement = new CommonElement(out.getTarget());
List<AnalysisPath> nextOfPath = findAnalysisPaths(nextStartElement, flowName, findType, newPath);
Expand Down Expand Up @@ -500,7 +504,6 @@ private SubProcessGroup findPathOfSubProcess(ProcessElement subProcessElement, S
.map(ProcessElement::getElement)
.map(SequenceFlow.class::cast).orElse(null);

//TODO: Which subprocess with more than one EmbeddedStart, how to handle it?
BaseElement start = processGraph.findStartElementOfProcess((SequenceFlow)lastElement, processElement);
List<AnalysisPath> path = findAnalysisPaths(new CommonElement(start), flowName, findType, emptyList());

Expand Down Expand Up @@ -641,7 +644,6 @@ private boolean isJoinTaskSwitchGateway(List<AnalysisPath> paths, ProcessElement
}

result = hasStartBefore && !hasFullInComing;

}
return result;
}
Expand Down Expand Up @@ -691,8 +693,6 @@ private ProcessElement getJoinTaskSwithGateWay(TaskParallelGroup taskParallelGro
return size > 0 ? elements.get(size - 1) : null;
}



private List<SequenceFlow> getSequenceFlowOfTaskSwitchGateway(TaskSwitchGateway taskSwitchGateway, NodeElement startNode) {
List<SequenceFlow> sequenceFlows = taskSwitchGateway.getIncoming();

Expand All @@ -717,4 +717,22 @@ private boolean isStartedFromOf(NodeElement startNode, SequenceFlow sequenceFlow
}
return false;
}

private boolean shouldStopFindTask(SubProcessGroup element, FindType findType) {
List<AnalysisPath> paths = element.getInternalPaths();
return shouldStopFindTask(paths, findType);
}

private boolean shouldStopFindTask(List<AnalysisPath> paths, FindType findType) {

if (FindType.TASKS_ON_PATH.equals(findType) && isNotEmpty(paths)) {
ProcessElement lastElement = AnalysisPathHelper.getLastElement(paths.get(0));
if (lastElement.getElement() instanceof TaskEnd) {
return true;
} else if (lastElement instanceof SubProcessGroup) {
return shouldStopFindTask((SubProcessGroup) lastElement, findType);
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ public static <T> int getLastIndex(AnalysisPath path) {
return elements.size() == 0 ? 0 : elements.size() - 1;
}

public static ProcessElement getLastElement(AnalysisPath path) {
List<ProcessElement> elements = path.getElements();
int size = elements.size();
return size == 0 ? null : elements.get(size - 1);
}

public static List<AnalysisPath> removeLastElementByClassType(List<AnalysisPath> paths , Class<?> clazz) {

List<AnalysisPath> result = new ArrayList<>();
Expand Down

0 comments on commit f897f82

Please sign in to comment.