diff --git a/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/ExclusiveMergeBlockConfiguration.java b/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/BranchRejoinBlockConfiguration.java similarity index 73% rename from src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/ExclusiveMergeBlockConfiguration.java rename to src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/BranchRejoinBlockConfiguration.java index cba3d33..eda6981 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/ExclusiveMergeBlockConfiguration.java +++ b/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/BranchRejoinBlockConfiguration.java @@ -5,7 +5,7 @@ import java.util.List; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonIgnore; -import it.cnr.isti.workflow.manager.blocks.types.ExclusiveMergeBlockType; +import it.cnr.isti.workflow.manager.blocks.types.BranchRejoinBlockType; import it.cnr.isti.workflow.manager.configurations.annotations.Structural; import it.cnr.isti.workflow.manager.configurations.annotations.UiOrder; import it.cnr.isti.workflow.manager.configurations.annotations.UiUniqueItemsBy; @@ -22,7 +22,7 @@ import lombok.NonNull; @Getter @EqualsAndHashCode(callSuper = true) @NoArgsConstructor(access = lombok.AccessLevel.PROTECTED) -public class ExclusiveMergeBlockConfiguration extends BlockConfiguration { +public class BranchRejoinBlockConfiguration extends BlockConfiguration { @UiOrder(30) @Structural @@ -30,7 +30,7 @@ public class ExclusiveMergeBlockConfiguration extends BlockConfiguration inputs = List.of(); + private List inputs = List.of(); @UiOrder(40) @Structural @@ -40,7 +40,7 @@ public class ExclusiveMergeBlockConfiguration extends BlockConfiguration inputs, + public BranchRejoinBlockConfiguration(@NonNull String name, List inputs, String outputName) { super(name); this.inputs = inputs == null ? List.of() : List.copyOf(inputs); @@ -48,14 +48,14 @@ public class ExclusiveMergeBlockConfiguration extends BlockConfiguration getBlockType() { - return ExclusiveMergeBlockType.class; + public Class getBlockType() { + return BranchRejoinBlockType.class; } - public static ExclusiveMergeBlockConfiguration empty() { - return ExclusiveMergeBlockConfiguration.builder() - .name(ExclusiveMergeBlockType.TYPE) - .inputs(List.of(new ExclusiveMergeInput("branchA"), new ExclusiveMergeInput("branchB"))) + public static BranchRejoinBlockConfiguration empty() { + return BranchRejoinBlockConfiguration.builder() + .name(BranchRejoinBlockType.TYPE) + .inputs(List.of(new BranchRejoinInput("branchA"), new BranchRejoinInput("branchB"))) .outputName("output") .build(); } @@ -71,7 +71,7 @@ public class ExclusiveMergeBlockConfiguration extends BlockConfiguration input != null && input.name() != null && !input.name().isBlank()) - .map(ExclusiveMergeInput::name) + .map(BranchRejoinInput::name) .distinct() .count(); return valid == inputs.size() && distinct == valid; diff --git a/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/ExclusiveMergeInput.java b/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/BranchRejoinInput.java similarity index 83% rename from src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/ExclusiveMergeInput.java rename to src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/BranchRejoinInput.java index 1205a04..e0e673c 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/ExclusiveMergeInput.java +++ b/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/BranchRejoinInput.java @@ -6,16 +6,16 @@ import it.cnr.isti.workflow.manager.ios.IOType; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; -public record ExclusiveMergeInput( +public record BranchRejoinInput( @NotBlank @Size(max = 64) String name, @Size(max = 255) String label, @JsonProperty(required = false) IOType type) { - public ExclusiveMergeInput(String name) { + public BranchRejoinInput(String name) { this(name, name, IOType.ANY); } - public ExclusiveMergeInput { + public BranchRejoinInput { label = label == null || label.isBlank() ? name : label; type = type == null ? IOType.ANY : type; } diff --git a/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/DynamicBlockConfigurationTypeResolver.java b/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/DynamicBlockConfigurationTypeResolver.java index aeb7172..cf4c450 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/DynamicBlockConfigurationTypeResolver.java +++ b/src/main/java/it/cnr/isti/workflow/manager/blocks/configurations/DynamicBlockConfigurationTypeResolver.java @@ -17,6 +17,8 @@ public class DynamicBlockConfigurationTypeResolver extends TypeIdResolverBase { private static final String CHAT_INTERACTION_LEGACY_CONFIGURATION_ID = "ChatHumanInteractionBlockConfiguration"; private static final String CHAT_INTERACTION_CONFIGURATION_ID = "ChatInteractionBlockConfiguration"; + private static final String BRANCH_REJOIN_LEGACY_CONFIGURATION_ID = "ExclusiveMergeBlockConfiguration"; + private static final String BRANCH_REJOIN_CONFIGURATION_ID = "BranchRejoinBlockConfiguration"; private Map> idToClass = new HashMap<>(); private Map, String> classToId = new HashMap<>(); @@ -51,6 +53,9 @@ public class DynamicBlockConfigurationTypeResolver extends TypeIdResolverBase { if (CHAT_INTERACTION_LEGACY_CONFIGURATION_ID.equals(id)) { id = CHAT_INTERACTION_CONFIGURATION_ID; } + if (BRANCH_REJOIN_LEGACY_CONFIGURATION_ID.equals(id)) { + id = BRANCH_REJOIN_CONFIGURATION_ID; + } Class clazz = idToClass.get(id); if (clazz == null && id != null) { String normalized = id; diff --git a/src/main/java/it/cnr/isti/workflow/manager/blocks/factories/ExclusiveMergeBlockFactory.java b/src/main/java/it/cnr/isti/workflow/manager/blocks/factories/BranchRejoinBlockFactory.java similarity index 72% rename from src/main/java/it/cnr/isti/workflow/manager/blocks/factories/ExclusiveMergeBlockFactory.java rename to src/main/java/it/cnr/isti/workflow/manager/blocks/factories/BranchRejoinBlockFactory.java index 6e0978f..c439a21 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/blocks/factories/ExclusiveMergeBlockFactory.java +++ b/src/main/java/it/cnr/isti/workflow/manager/blocks/factories/BranchRejoinBlockFactory.java @@ -8,24 +8,24 @@ import org.springframework.stereotype.Component; import it.cnr.isti.workflow.manager.blocks.Block; import it.cnr.isti.workflow.manager.blocks.IOCapability; import it.cnr.isti.workflow.manager.blocks.IOCapabilityType; -import it.cnr.isti.workflow.manager.blocks.configurations.ExclusiveMergeBlockConfiguration; -import it.cnr.isti.workflow.manager.blocks.types.ExclusiveMergeBlockType; +import it.cnr.isti.workflow.manager.blocks.configurations.BranchRejoinBlockConfiguration; +import it.cnr.isti.workflow.manager.blocks.types.BranchRejoinBlockType; import it.cnr.isti.workflow.manager.ios.IODescriptor; import it.cnr.isti.workflow.manager.ios.IOType; @Component -public class ExclusiveMergeBlockFactory - implements BlockFactory { +public class BranchRejoinBlockFactory + implements BlockFactory { public static final String DEFAULT_OUTPUT = "output"; private static final List ANY_CAPABILITY = List.of(new IOCapability(IOCapabilityType.ANY, false)); @Autowired - ExclusiveMergeBlockType blockType; + BranchRejoinBlockType blockType; @Override - public Block create(ExclusiveMergeBlockConfiguration configuration) { - Block.BlockBuilder builder = Block.builder() + public Block create(BranchRejoinBlockConfiguration configuration) { + Block.BlockBuilder builder = Block.builder() .specificConfiguration(configuration) .type(blockType); configuration.getInputs().forEach(input -> builder.input(IODescriptor.input( @@ -35,13 +35,13 @@ public class ExclusiveMergeBlockFactory } @Override - public Block createEmpty() { - return create(ExclusiveMergeBlockConfiguration.empty()); + public Block createEmpty() { + return create(BranchRejoinBlockConfiguration.empty()); } @Override - public Class getBlockType() { - return ExclusiveMergeBlockType.class; + public Class getBlockType() { + return BranchRejoinBlockType.class; } @Override diff --git a/src/main/java/it/cnr/isti/workflow/manager/blocks/types/BlockTypes.java b/src/main/java/it/cnr/isti/workflow/manager/blocks/types/BlockTypes.java index 0e9f0c7..76fab67 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/blocks/types/BlockTypes.java +++ b/src/main/java/it/cnr/isti/workflow/manager/blocks/types/BlockTypes.java @@ -11,6 +11,7 @@ public class BlockTypes { private static Map blockTypes = new HashMap<>(); private static final String CHAT_INTERACTION_LEGACY_TYPE = "ChatHumanInteraction"; + private static final String BRANCH_REJOIN_LEGACY_TYPE = "ExclusiveMergeBlock"; public static BlockType get(String name) { @@ -21,6 +22,9 @@ public class BlockTypes { if (CHAT_INTERACTION_LEGACY_TYPE.equals(name)) { return blockTypes.get("ChatInteraction"); } + if (BRANCH_REJOIN_LEGACY_TYPE.equals(name)) { + return blockTypes.get("BranchRejoinBlock"); + } return null; } diff --git a/src/main/java/it/cnr/isti/workflow/manager/blocks/types/ExclusiveMergeBlockType.java b/src/main/java/it/cnr/isti/workflow/manager/blocks/types/BranchRejoinBlockType.java similarity index 69% rename from src/main/java/it/cnr/isti/workflow/manager/blocks/types/ExclusiveMergeBlockType.java rename to src/main/java/it/cnr/isti/workflow/manager/blocks/types/BranchRejoinBlockType.java index 0f1cd2a..55cc538 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/blocks/types/ExclusiveMergeBlockType.java +++ b/src/main/java/it/cnr/isti/workflow/manager/blocks/types/BranchRejoinBlockType.java @@ -3,13 +3,13 @@ package it.cnr.isti.workflow.manager.blocks.types; import org.springframework.stereotype.Component; import it.cnr.isti.workflow.manager.blocks.configurations.BlockConfiguration; -import it.cnr.isti.workflow.manager.blocks.configurations.ExclusiveMergeBlockConfiguration; +import it.cnr.isti.workflow.manager.blocks.configurations.BranchRejoinBlockConfiguration; import it.cnr.isti.workflow.manager.flows.model.capabilities.NodeTypeCapabilities; -@Component(ExclusiveMergeBlockType.TYPE) -public class ExclusiveMergeBlockType implements BlockType { +@Component(BranchRejoinBlockType.TYPE) +public class BranchRejoinBlockType implements BlockType { - public static final String TYPE = "ExclusiveMergeBlock"; + public static final String TYPE = "BranchRejoinBlock"; @Override public String getName() { @@ -33,11 +33,11 @@ public class ExclusiveMergeBlockType implements BlockType { @Override public NodeTypeCapabilities getCapabilities() { - return NodeTypeCapabilities.merge(); + return NodeTypeCapabilities.branchRejoin(); } @Override public Class> getBlockConfigurationClass() { - return ExclusiveMergeBlockConfiguration.class; + return BranchRejoinBlockConfiguration.class; } } diff --git a/src/main/java/it/cnr/isti/workflow/manager/executions/executors/blocks/ExclusiveMergeExecutor.java b/src/main/java/it/cnr/isti/workflow/manager/executions/executors/blocks/BranchRejoinExecutor.java similarity index 65% rename from src/main/java/it/cnr/isti/workflow/manager/executions/executors/blocks/ExclusiveMergeExecutor.java rename to src/main/java/it/cnr/isti/workflow/manager/executions/executors/blocks/BranchRejoinExecutor.java index 62c3035..675c9cd 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/executions/executors/blocks/ExclusiveMergeExecutor.java +++ b/src/main/java/it/cnr/isti/workflow/manager/executions/executors/blocks/BranchRejoinExecutor.java @@ -6,8 +6,8 @@ import java.util.Map; import org.springframework.stereotype.Component; import it.cnr.isti.workflow.manager.blocks.Block; -import it.cnr.isti.workflow.manager.blocks.configurations.ExclusiveMergeBlockConfiguration; -import it.cnr.isti.workflow.manager.blocks.types.ExclusiveMergeBlockType; +import it.cnr.isti.workflow.manager.blocks.configurations.BranchRejoinBlockConfiguration; +import it.cnr.isti.workflow.manager.blocks.types.BranchRejoinBlockType; import it.cnr.isti.workflow.manager.executions.ExecutionEventLogger; import it.cnr.isti.workflow.manager.executions.ExecutionVariableDescriptor; import it.cnr.isti.workflow.manager.executions.NodeExecutionException; @@ -15,30 +15,30 @@ import it.cnr.isti.workflow.manager.executions.steps.Input; import it.cnr.isti.workflow.manager.executions.steps.InputResolutionState; @Component -public class ExclusiveMergeExecutor implements BlockExecutor { +public class BranchRejoinExecutor implements BlockExecutor { @Override - public Map execute(Block block, List inputs, + public Map execute(Block block, List inputs, Map authorizations, Map executionVariables, Map executionVariableDescriptors, ExecutionEventLogger eventLogger) { List selected = inputs.stream() .filter(input -> input.getResolutionState() == InputResolutionState.VALUE) .toList(); if (inputs.stream().anyMatch(input -> input.getResolutionState() == InputResolutionState.UNAVAILABLE)) { - throw new NodeExecutionException("EXCLUSIVE_MERGE_INPUT_UNAVAILABLE", + throw new NodeExecutionException("BRANCH_REJOIN_INPUT_UNAVAILABLE", "Exclusive merge received an unavailable input"); } if (selected.size() != 1) { - throw new NodeExecutionException("EXCLUSIVE_MERGE_MULTIPLE_VALUES", + throw new NodeExecutionException("BRANCH_REJOIN_MULTIPLE_VALUES", "Exclusive merge expected exactly one selected input but received " + selected.size()); } - ExclusiveMergeBlockConfiguration configuration = - (ExclusiveMergeBlockConfiguration) block.getSpecificConfiguration(); + BranchRejoinBlockConfiguration configuration = + (BranchRejoinBlockConfiguration) block.getSpecificConfiguration(); return Map.of(configuration.getOutputName(), selected.getFirst().getValue()); } @Override - public Class getBlockType() { - return ExclusiveMergeBlockType.class; + public Class getBlockType() { + return BranchRejoinBlockType.class; } } diff --git a/src/main/java/it/cnr/isti/workflow/manager/executions/steps/activation/ExclusiveMergeActivationPolicy.java b/src/main/java/it/cnr/isti/workflow/manager/executions/steps/activation/BranchRejoinActivationPolicy.java similarity index 94% rename from src/main/java/it/cnr/isti/workflow/manager/executions/steps/activation/ExclusiveMergeActivationPolicy.java rename to src/main/java/it/cnr/isti/workflow/manager/executions/steps/activation/BranchRejoinActivationPolicy.java index 07ff4fc..4c49f74 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/executions/steps/activation/ExclusiveMergeActivationPolicy.java +++ b/src/main/java/it/cnr/isti/workflow/manager/executions/steps/activation/BranchRejoinActivationPolicy.java @@ -6,7 +6,7 @@ import it.cnr.isti.workflow.manager.executions.steps.Input; import it.cnr.isti.workflow.manager.executions.steps.InputResolutionState; import it.cnr.isti.workflow.manager.executions.steps.StepSkipReason; -public final class ExclusiveMergeActivationPolicy implements StepActivationPolicy { +public final class BranchRejoinActivationPolicy implements StepActivationPolicy { @Override public StepActivationDecision evaluate(List inputs, boolean dependenciesSatisfied, diff --git a/src/main/java/it/cnr/isti/workflow/manager/executions/steps/activation/StepActivationPolicyRegistry.java b/src/main/java/it/cnr/isti/workflow/manager/executions/steps/activation/StepActivationPolicyRegistry.java index d57fa3b..9d413d5 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/executions/steps/activation/StepActivationPolicyRegistry.java +++ b/src/main/java/it/cnr/isti/workflow/manager/executions/steps/activation/StepActivationPolicyRegistry.java @@ -1,13 +1,13 @@ package it.cnr.isti.workflow.manager.executions.steps.activation; import it.cnr.isti.workflow.manager.blocks.Block; -import it.cnr.isti.workflow.manager.blocks.types.ExclusiveMergeBlockType; +import it.cnr.isti.workflow.manager.blocks.types.BranchRejoinBlockType; import it.cnr.isti.workflow.manager.flows.model.FlowNode; public final class StepActivationPolicyRegistry { private static final StepActivationPolicy DEFAULT_POLICY = new AllRequiredActivationPolicy(); - private static final StepActivationPolicy EXCLUSIVE_MERGE_POLICY = new ExclusiveMergeActivationPolicy(); + private static final StepActivationPolicy BRANCH_REJOIN_POLICY = new BranchRejoinActivationPolicy(); private StepActivationPolicyRegistry() { } @@ -16,8 +16,8 @@ public final class StepActivationPolicyRegistry { if (node == null) { throw new IllegalArgumentException("Flow node is required to resolve its activation policy"); } - if (node instanceof Block block && block.getType() instanceof ExclusiveMergeBlockType) { - return EXCLUSIVE_MERGE_POLICY; + if (node instanceof Block block && block.getType() instanceof BranchRejoinBlockType) { + return BRANCH_REJOIN_POLICY; } return DEFAULT_POLICY; } diff --git a/src/main/java/it/cnr/isti/workflow/manager/flows/model/capabilities/NodeTypeCapabilities.java b/src/main/java/it/cnr/isti/workflow/manager/flows/model/capabilities/NodeTypeCapabilities.java index 2714159..d84dfbe 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/flows/model/capabilities/NodeTypeCapabilities.java +++ b/src/main/java/it/cnr/isti/workflow/manager/flows/model/capabilities/NodeTypeCapabilities.java @@ -21,8 +21,8 @@ public record NodeTypeCapabilities( return new NodeTypeCapabilities(NodeVisualRole.DECISION, false, true, true, true, true, true); } - public static NodeTypeCapabilities merge() { - return new NodeTypeCapabilities(NodeVisualRole.MERGE, false, true, true, true, true, true); + public static NodeTypeCapabilities branchRejoin() { + return new NodeTypeCapabilities(NodeVisualRole.BRANCH_REJOIN, false, true, true, true, true, true); } public static NodeTypeCapabilities end() { diff --git a/src/main/java/it/cnr/isti/workflow/manager/flows/model/capabilities/NodeVisualRole.java b/src/main/java/it/cnr/isti/workflow/manager/flows/model/capabilities/NodeVisualRole.java index 016f01f..1ab018d 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/flows/model/capabilities/NodeVisualRole.java +++ b/src/main/java/it/cnr/isti/workflow/manager/flows/model/capabilities/NodeVisualRole.java @@ -3,7 +3,7 @@ package it.cnr.isti.workflow.manager.flows.model.capabilities; public enum NodeVisualRole { ACTIVITY, DECISION, - MERGE, + BRANCH_REJOIN, END, CONTAINER } diff --git a/src/main/java/it/cnr/isti/workflow/manager/flows/validation/FlowDataValidator.java b/src/main/java/it/cnr/isti/workflow/manager/flows/validation/FlowDataValidator.java index e42da47..bb26b9a 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/flows/validation/FlowDataValidator.java +++ b/src/main/java/it/cnr/isti/workflow/manager/flows/validation/FlowDataValidator.java @@ -14,12 +14,12 @@ import org.springframework.stereotype.Component; import it.cnr.isti.workflow.manager.blocks.Block; import it.cnr.isti.workflow.manager.blocks.configurations.BlockConfiguration; import it.cnr.isti.workflow.manager.blocks.configurations.EndBlockConfiguration; -import it.cnr.isti.workflow.manager.blocks.configurations.ExclusiveMergeBlockConfiguration; +import it.cnr.isti.workflow.manager.blocks.configurations.BranchRejoinBlockConfiguration; import it.cnr.isti.workflow.manager.blocks.factories.BlockFactory; import it.cnr.isti.workflow.manager.blocks.factories.ConditionalBlockFactory; import it.cnr.isti.workflow.manager.blocks.factories.SwitchBlockFactory; import it.cnr.isti.workflow.manager.blocks.types.ConditionalBlockType; -import it.cnr.isti.workflow.manager.blocks.types.ExclusiveMergeBlockType; +import it.cnr.isti.workflow.manager.blocks.types.BranchRejoinBlockType; import it.cnr.isti.workflow.manager.blocks.types.HumanDecisionBlockType; import it.cnr.isti.workflow.manager.blocks.types.SwitchBlockType; import it.cnr.isti.workflow.manager.containers.Container; @@ -99,7 +99,7 @@ public class FlowDataValidator implements ConstraintValidator mergedNodes = new LinkedHashSet<>(reachableByOutput.get(leftOutput)); mergedNodes.retainAll(reachableByOutput.get(rightOutput)); Set explicitlyMergedNodes = mergedNodes.stream() - .filter(nodeId -> isExclusiveMerge(nodesById.get(nodeId))) + .filter(nodeId -> isBranchRejoin(nodesById.get(nodeId))) .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); Set allowedSharedNodes = new LinkedHashSet<>(); for (String mergeNodeId : explicitlyMergedNodes) { @@ -585,21 +585,21 @@ public class FlowDataValidator implements ConstraintValidator block && ExclusiveMergeBlockType.TYPE.equals(block.getType().getName()); + private boolean isBranchRejoin(FlowNode node) { + return node instanceof Block block && BranchRejoinBlockType.TYPE.equals(block.getType().getName()); } - private void validateExclusiveMergeNodes(List nodes, List connections) { + private void validateBranchRejoinNodes(List nodes, List connections) { for (FlowNode node : nodes) { if (!(node instanceof Block block) - || !(block.getSpecificConfiguration() instanceof ExclusiveMergeBlockConfiguration configuration)) { + || !(block.getSpecificConfiguration() instanceof BranchRejoinBlockConfiguration configuration)) { continue; } List incoming = connections.stream() .filter(connection -> block.getId().equals(connection.getTargetId())) .toList(); if (configuration.getInputs().size() < 2 || incoming.size() < 2) { - throw validationError(error(ValidationErrorCode.EXCLUSIVE_MERGE_MIN_INPUTS, "block", block.getId(), + throw validationError(error(ValidationErrorCode.BRANCH_REJOIN_MIN_INPUTS, "block", block.getId(), "inputs", "Exclusive merge requires at least two connected inputs")); } for (IODescriptor input : block.getInputs()) { @@ -607,11 +607,11 @@ public class FlowDataValidator implements ConstraintValidator input.getName().equals(connection.getTargetName())) .count(); if (connectionCount == 0) { - throw validationError(error(ValidationErrorCode.EXCLUSIVE_MERGE_INPUT_NOT_CONNECTED, + throw validationError(error(ValidationErrorCode.BRANCH_REJOIN_INPUT_NOT_CONNECTED, "block", block.getId(), "inputs", "Exclusive merge input is not connected: " + input.getName())); } if (connectionCount > 1) { - throw validationError(error(ValidationErrorCode.EXCLUSIVE_MERGE_INPUT_MULTIPLE_CONNECTIONS, + throw validationError(error(ValidationErrorCode.BRANCH_REJOIN_INPUT_MULTIPLE_CONNECTIONS, "block", block.getId(), "inputs", "Exclusive merge input has multiple connections: " + input.getName())); } diff --git a/src/main/java/it/cnr/isti/workflow/manager/flows/validation/ValidationErrorCode.java b/src/main/java/it/cnr/isti/workflow/manager/flows/validation/ValidationErrorCode.java index edb5d85..cec47cc 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/flows/validation/ValidationErrorCode.java +++ b/src/main/java/it/cnr/isti/workflow/manager/flows/validation/ValidationErrorCode.java @@ -66,12 +66,12 @@ public enum ValidationErrorCode { NODE_CANNOT_DEPEND_ON_OTHER_NODES, NODE_CANNOT_HAVE_DEPENDENT_NODES, EXCLUSIVE_BRANCH_MERGE, - EXCLUSIVE_MERGE_MIN_INPUTS, - EXCLUSIVE_MERGE_INPUT_NOT_CONNECTED, - EXCLUSIVE_MERGE_INPUT_MULTIPLE_CONNECTIONS, - EXCLUSIVE_MERGE_MULTIPLE_VALUES, - EXCLUSIVE_MERGE_INPUT_UNAVAILABLE, - UNSTRUCTURED_EXCLUSIVE_MERGE, + BRANCH_REJOIN_MIN_INPUTS, + BRANCH_REJOIN_INPUT_NOT_CONNECTED, + BRANCH_REJOIN_INPUT_MULTIPLE_CONNECTIONS, + BRANCH_REJOIN_MULTIPLE_VALUES, + BRANCH_REJOIN_INPUT_UNAVAILABLE, + UNSTRUCTURED_BRANCH_REJOIN, HUMAN_DECISION_INVALID_CHOICE, HUMAN_DECISION_RATIONALE_REQUIRED, END_OUTCOME_REQUIRED, diff --git a/src/main/resources/workflow-editor-init/flows.json b/src/main/resources/workflow-editor-init/flows.json index dbb5ad4..3375452 100644 --- a/src/main/resources/workflow-editor-init/flows.json +++ b/src/main/resources/workflow-editor-init/flows.json @@ -671,7 +671,7 @@ }, { "name": "Jensen Recruitment Process - Full Revised (Structured)", - "description": "Node-per-node structured port of the Full Revised Jensen recruitment UML, built with explicit HumanDecisionBlock gateways, ExclusiveMergeBlock rejoins, EndBlock outcomes and FlowLane swimlanes. Does not replace the existing macro-flow seed.", + "description": "Node-per-node structured port of the Full Revised Jensen recruitment UML, built with explicit HumanDecisionBlock gateways, BranchRejoinBlock rejoins, EndBlock outcomes and FlowLane swimlanes. Does not replace the existing macro-flow seed.", "owner": "testuser", "createdAt": "2026-07-22T10:00:00", "lastUpdateAt": "2026-07-22T10:00:00", @@ -1056,7 +1056,7 @@ ], "laneId": "2bc166dc-fe39-550d-a1cf-f4d7a6e7686c", "specificConfiguration": { - "type": "ExclusiveMergeBlockConfiguration", + "type": "BranchRejoinBlockConfiguration", "name": "merge-job-description", "inputs": [ { @@ -1072,7 +1072,7 @@ ], "outputName": "output" }, - "typeName": "ExclusiveMergeBlock" + "typeName": "BranchRejoinBlock" }, { "id": "287ca7f6-074d-5430-87fb-131a76acc91e", @@ -1224,7 +1224,7 @@ ], "laneId": "18a8d278-ebcd-53f2-b3e8-3a4fb4c4a83e", "specificConfiguration": { - "type": "ExclusiveMergeBlockConfiguration", + "type": "BranchRejoinBlockConfiguration", "name": "merge-job-advertisement", "inputs": [ { @@ -1240,7 +1240,7 @@ ], "outputName": "output" }, - "typeName": "ExclusiveMergeBlock" + "typeName": "BranchRejoinBlock" }, { "id": "fb6beef9-8de2-5b40-a169-33e8d0b5777f", @@ -1433,7 +1433,7 @@ ], "laneId": "8036d9f4-67bd-5198-942f-53bb41afa29c", "specificConfiguration": { - "type": "ExclusiveMergeBlockConfiguration", + "type": "BranchRejoinBlockConfiguration", "name": "merge-external-recruitment-start", "inputs": [ { @@ -1449,7 +1449,7 @@ ], "outputName": "output" }, - "typeName": "ExclusiveMergeBlock" + "typeName": "BranchRejoinBlock" }, { "id": "e67b2d57-359c-5710-b63a-6df27e3b6bd0", @@ -1682,7 +1682,7 @@ ], "laneId": "91bbb8f9-8882-5cc3-ba02-f41fc60e75b7", "specificConfiguration": { - "type": "ExclusiveMergeBlockConfiguration", + "type": "BranchRejoinBlockConfiguration", "name": "merge-application-submission-entry", "inputs": [ { @@ -1698,7 +1698,7 @@ ], "outputName": "output" }, - "typeName": "ExclusiveMergeBlock" + "typeName": "BranchRejoinBlock" }, { "id": "298edbd7-7bda-511c-b72e-93cf65468939", @@ -2006,7 +2006,7 @@ ], "laneId": "47b1f830-b4d1-5788-85a6-538193521f23", "specificConfiguration": { - "type": "ExclusiveMergeBlockConfiguration", + "type": "BranchRejoinBlockConfiguration", "name": "merge-post-assessment", "inputs": [ { @@ -2027,7 +2027,7 @@ ], "outputName": "output" }, - "typeName": "ExclusiveMergeBlock" + "typeName": "BranchRejoinBlock" }, { "id": "851978f2-057f-59bf-ad7f-e75e0f412005", @@ -3009,7 +3009,7 @@ ], "laneId": "df5fcc5f-c0cd-5592-bb50-7815a7f96d70", "specificConfiguration": { - "type": "ExclusiveMergeBlockConfiguration", + "type": "BranchRejoinBlockConfiguration", "name": "merge-job-description", "inputs": [ { @@ -3025,7 +3025,7 @@ ], "outputName": "output" }, - "typeName": "ExclusiveMergeBlock" + "typeName": "BranchRejoinBlock" }, { "id": "0159e7e4-9dba-5bde-b86a-e5a8cd97c005", @@ -3322,7 +3322,7 @@ ], "laneId": "906bae7d-fbe8-5dc1-a33a-91094e302d78", "specificConfiguration": { - "type": "ExclusiveMergeBlockConfiguration", + "type": "BranchRejoinBlockConfiguration", "name": "merge-advertisement-review-method", "inputs": [ { @@ -3338,7 +3338,7 @@ ], "outputName": "output" }, - "typeName": "ExclusiveMergeBlock" + "typeName": "BranchRejoinBlock" }, { "id": "1163d218-5c38-5ed2-96f4-3ba9901f8daa", @@ -3397,7 +3397,7 @@ ], "laneId": "906bae7d-fbe8-5dc1-a33a-91094e302d78", "specificConfiguration": { - "type": "ExclusiveMergeBlockConfiguration", + "type": "BranchRejoinBlockConfiguration", "name": "merge-job-advertisement", "inputs": [ { @@ -3413,7 +3413,7 @@ ], "outputName": "output" }, - "typeName": "ExclusiveMergeBlock" + "typeName": "BranchRejoinBlock" }, { "id": "d6aa9f6d-ae17-5b8b-b42b-f274768c227a", @@ -3686,7 +3686,7 @@ ], "laneId": "e3844222-ee0a-5bd8-8448-187364789eca", "specificConfiguration": { - "type": "ExclusiveMergeBlockConfiguration", + "type": "BranchRejoinBlockConfiguration", "name": "merge-external-recruitment-start", "inputs": [ { @@ -3702,7 +3702,7 @@ ], "outputName": "output" }, - "typeName": "ExclusiveMergeBlock" + "typeName": "BranchRejoinBlock" }, { "id": "188536f9-3f93-5b9e-9fa5-93223be338e4", @@ -3992,7 +3992,7 @@ ], "laneId": "efaffb7a-47b3-521c-b11f-0f9074212cdf", "specificConfiguration": { - "type": "ExclusiveMergeBlockConfiguration", + "type": "BranchRejoinBlockConfiguration", "name": "merge-application-submission-entry", "inputs": [ { @@ -4008,7 +4008,7 @@ ], "outputName": "output" }, - "typeName": "ExclusiveMergeBlock" + "typeName": "BranchRejoinBlock" }, { "id": "a3903787-c097-5602-acf0-b7d0012a62e4", @@ -4387,7 +4387,7 @@ ], "laneId": "595b4e4b-0c5d-504f-aa1c-b35e9f2a6a15", "specificConfiguration": { - "type": "ExclusiveMergeBlockConfiguration", + "type": "BranchRejoinBlockConfiguration", "name": "merge-post-assessment", "inputs": [ { @@ -4408,7 +4408,7 @@ ], "outputName": "output" }, - "typeName": "ExclusiveMergeBlock" + "typeName": "BranchRejoinBlock" }, { "id": "2ba31b21-8d5e-5a9e-8f75-140274fb6389", diff --git a/src/test/java/it/cnr/isti/workflow/manager/blocks/configurations/BlockConfigurationValidationTest.java b/src/test/java/it/cnr/isti/workflow/manager/blocks/configurations/BlockConfigurationValidationTest.java index 6354f34..2748798 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/blocks/configurations/BlockConfigurationValidationTest.java +++ b/src/test/java/it/cnr/isti/workflow/manager/blocks/configurations/BlockConfigurationValidationTest.java @@ -17,7 +17,7 @@ import tools.jackson.databind.ObjectMapper; /** * Bean-validation coverage for the dynamic configurations introduced alongside - * ExclusiveMergeBlock, HumanDecisionBlock and EndBlock: cardinality bounds, uniqueness + * BranchRejoinBlock, HumanDecisionBlock and EndBlock: cardinality bounds, uniqueness * checks and required fields. */ class BlockConfigurationValidationTest { @@ -25,10 +25,10 @@ class BlockConfigurationValidationTest { private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); @Test - void exclusiveMergeRejectsFewerThanTwoInputs() { - ExclusiveMergeBlockConfiguration configuration = ExclusiveMergeBlockConfiguration.builder() + void branchRejoinRejectsFewerThanTwoInputs() { + BranchRejoinBlockConfiguration configuration = BranchRejoinBlockConfiguration.builder() .name("merge") - .inputs(List.of(new ExclusiveMergeInput("only"))) + .inputs(List.of(new BranchRejoinInput("only"))) .outputName("output") .build(); @@ -36,11 +36,11 @@ class BlockConfigurationValidationTest { } @Test - void exclusiveMergeRejectsMoreThanTenInputs() { - List inputs = java.util.stream.IntStream.range(0, 11) - .mapToObj(index -> new ExclusiveMergeInput("branch" + index)) + void branchRejoinRejectsMoreThanTenInputs() { + List inputs = java.util.stream.IntStream.range(0, 11) + .mapToObj(index -> new BranchRejoinInput("branch" + index)) .toList(); - ExclusiveMergeBlockConfiguration configuration = ExclusiveMergeBlockConfiguration.builder() + BranchRejoinBlockConfiguration configuration = BranchRejoinBlockConfiguration.builder() .name("merge") .inputs(inputs) .outputName("output") @@ -50,23 +50,23 @@ class BlockConfigurationValidationTest { } @Test - void exclusiveMergeRejectsDuplicateInputNames() { - ExclusiveMergeBlockConfiguration configuration = ExclusiveMergeBlockConfiguration.builder() + void branchRejoinRejectsDuplicateInputNames() { + BranchRejoinBlockConfiguration configuration = BranchRejoinBlockConfiguration.builder() .name("merge") - .inputs(List.of(new ExclusiveMergeInput("branch", "A", IOType.ANY), - new ExclusiveMergeInput("branch", "B", IOType.ANY))) + .inputs(List.of(new BranchRejoinInput("branch", "A", IOType.ANY), + new BranchRejoinInput("branch", "B", IOType.ANY))) .outputName("output") .build(); - Set> violations = validator.validate(configuration); + Set> violations = validator.validate(configuration); assertTrue(violations.stream().anyMatch(v -> v.getMessage().contains("unique"))); } @Test - void exclusiveMergeAcceptsAWellFormedConfiguration() { - ExclusiveMergeBlockConfiguration configuration = ExclusiveMergeBlockConfiguration.builder() + void branchRejoinAcceptsAWellFormedConfiguration() { + BranchRejoinBlockConfiguration configuration = BranchRejoinBlockConfiguration.builder() .name("merge") - .inputs(List.of(new ExclusiveMergeInput("existing"), new ExclusiveMergeInput("new"))) + .inputs(List.of(new BranchRejoinInput("existing"), new BranchRejoinInput("new"))) .outputName("output") .build(); diff --git a/src/test/java/it/cnr/isti/workflow/manager/blocks/configurations/LegacyBlockTypeIdTest.java b/src/test/java/it/cnr/isti/workflow/manager/blocks/configurations/LegacyBlockTypeIdTest.java new file mode 100644 index 0000000..753e99e --- /dev/null +++ b/src/test/java/it/cnr/isti/workflow/manager/blocks/configurations/LegacyBlockTypeIdTest.java @@ -0,0 +1,65 @@ +package it.cnr.isti.workflow.manager.blocks.configurations; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; + +import it.cnr.isti.workflow.manager.app.ObjectMapperHolder; +import it.cnr.isti.workflow.manager.blocks.Block; +import it.cnr.isti.workflow.manager.blocks.types.BranchRejoinBlockType; + +/** + * A renamed block type (e.g. ExclusiveMergeBlock -> BranchRejoinBlock) must + * still deserialize flows that were persisted under the old type id before + * the rename - flow_data in the database isn't migrated automatically. + * DynamicBlockConfigurationTypeResolver/BlockTypes carry a legacy-id + * fallback for exactly this; this test locks in the current one (found the + * hard way: renaming without it breaks application startup on any + * already-persisted flow using the old id). + */ +@SpringBootTest +@TestPropertySource(locations = "classpath:test.properties") +class LegacyBlockTypeIdTest { + + private static final String LEGACY_BLOCK_JSON = """ + { + "id": "legacy-merge-block", + "name": "merge", + "inputs": [ + {"name": "a", "type": "ANY", "multiple": false}, + {"name": "b", "type": "ANY", "multiple": false} + ], + "outputs": [{"name": "output", "type": "ANY", "multiple": false}], + "specificConfiguration": { + "type": "ExclusiveMergeBlockConfiguration", + "name": "merge", + "inputs": [ + {"name": "a", "label": "A", "type": "ANY"}, + {"name": "b", "label": "B", "type": "ANY"} + ], + "outputName": "output" + }, + "typeName": "ExclusiveMergeBlock" + } + """; + + @Test + void legacyExclusiveMergeConfigurationIdResolvesToBranchRejoin() throws Exception { + Block block = ObjectMapperHolder.mapper.readValue(LEGACY_BLOCK_JSON, Block.class); + + assertNotNull(block.getSpecificConfiguration()); + assertInstanceOf(BranchRejoinBlockConfiguration.class, block.getSpecificConfiguration()); + } + + @Test + void legacyExclusiveMergeTypeNameResolvesToBranchRejoinBlockType() throws Exception { + Block block = ObjectMapperHolder.mapper.readValue(LEGACY_BLOCK_JSON, Block.class); + + assertNotNull(block.getType()); + assertEquals(BranchRejoinBlockType.TYPE, block.getType().getName()); + } +} diff --git a/src/test/java/it/cnr/isti/workflow/manager/executions/ExclusiveMergeConcurrencyTest.java b/src/test/java/it/cnr/isti/workflow/manager/executions/BranchRejoinConcurrencyTest.java similarity index 92% rename from src/test/java/it/cnr/isti/workflow/manager/executions/ExclusiveMergeConcurrencyTest.java rename to src/test/java/it/cnr/isti/workflow/manager/executions/BranchRejoinConcurrencyTest.java index 8b3be69..744c1a2 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/executions/ExclusiveMergeConcurrencyTest.java +++ b/src/test/java/it/cnr/isti/workflow/manager/executions/BranchRejoinConcurrencyTest.java @@ -22,28 +22,28 @@ import org.springframework.test.context.TestPropertySource; import it.cnr.isti.workflow.manager.blocks.Block; import it.cnr.isti.workflow.manager.blocks.configurations.ConditionalBlockConfiguration; import it.cnr.isti.workflow.manager.blocks.configurations.EndBlockConfiguration; -import it.cnr.isti.workflow.manager.blocks.configurations.ExclusiveMergeBlockConfiguration; -import it.cnr.isti.workflow.manager.blocks.configurations.ExclusiveMergeInput; +import it.cnr.isti.workflow.manager.blocks.configurations.BranchRejoinBlockConfiguration; +import it.cnr.isti.workflow.manager.blocks.configurations.BranchRejoinInput; import it.cnr.isti.workflow.manager.blocks.factories.ConditionalBlockFactory; import it.cnr.isti.workflow.manager.blocks.factories.EndBlockFactory; -import it.cnr.isti.workflow.manager.blocks.factories.ExclusiveMergeBlockFactory; +import it.cnr.isti.workflow.manager.blocks.factories.BranchRejoinBlockFactory; import it.cnr.isti.workflow.manager.blocks.types.ConditionalBlockType; import it.cnr.isti.workflow.manager.blocks.types.EndBlockType; -import it.cnr.isti.workflow.manager.blocks.types.ExclusiveMergeBlockType; +import it.cnr.isti.workflow.manager.blocks.types.BranchRejoinBlockType; import it.cnr.isti.workflow.manager.executions.steps.StepStatus; import it.cnr.isti.workflow.manager.flows.model.Connection; import it.cnr.isti.workflow.manager.flows.model.FlowData; import it.cnr.isti.workflow.manager.ios.IOType; /** - * Concurrency coverage for ExclusiveMergeBlock scheduling: repeated split+merge executions + * Concurrency coverage for BranchRejoinBlock scheduling: repeated split+merge executions * under the engine's own asynchronous executors must never double-schedule the merge, must * only complete it once every input is resolved, and must stay consistent under cancel and * under concurrent snapshot reads. */ @SpringBootTest @TestPropertySource(locations = "classpath:test.properties") -class ExclusiveMergeConcurrencyTest { +class BranchRejoinConcurrencyTest { @Autowired ExecutionsService executionsService; @@ -52,7 +52,7 @@ class ExclusiveMergeConcurrencyTest { ConditionalBlockFactory conditionalBlockFactory; @Autowired - ExclusiveMergeBlockFactory exclusiveMergeBlockFactory; + BranchRejoinBlockFactory branchRejoinBlockFactory; @Autowired EndBlockFactory endBlockFactory; @@ -98,7 +98,7 @@ class ExclusiveMergeConcurrencyTest { .filter(block -> block.getType() instanceof ConditionalBlockType) .findFirst().orElseThrow(); Block merge = flow.getBlocks().stream() - .filter(block -> block.getType() instanceof ExclusiveMergeBlockType) + .filter(block -> block.getType() instanceof BranchRejoinBlockType) .findFirst().orElseThrow(); ExecutionObject execution = executionsService.createExecution("Concurrent merge - " + decision, flow); @@ -212,12 +212,12 @@ class ExclusiveMergeConcurrencyTest { .condition("${{decision}} == 'yes'") .outputTemplate("${{payload}}") .build()); - Block merge = exclusiveMergeBlockFactory.create( - ExclusiveMergeBlockConfiguration.builder() + Block merge = branchRejoinBlockFactory.create( + BranchRejoinBlockConfiguration.builder() .name("Eligibility merge") .inputs(List.of( - new ExclusiveMergeInput("accepted", "Accepted", IOType.TEXT), - new ExclusiveMergeInput("rejected", "Rejected", IOType.TEXT))) + new BranchRejoinInput("accepted", "Accepted", IOType.TEXT), + new BranchRejoinInput("rejected", "Rejected", IOType.TEXT))) .outputName("result") .build()); Block end = endBlockFactory.create(EndBlockConfiguration.builder() diff --git a/src/test/java/it/cnr/isti/workflow/manager/executions/ControlFlowGatewayIntegrationTest.java b/src/test/java/it/cnr/isti/workflow/manager/executions/ControlFlowGatewayIntegrationTest.java index 975bbec..0f58864 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/executions/ControlFlowGatewayIntegrationTest.java +++ b/src/test/java/it/cnr/isti/workflow/manager/executions/ControlFlowGatewayIntegrationTest.java @@ -16,17 +16,17 @@ import org.springframework.test.context.TestPropertySource; import it.cnr.isti.workflow.manager.blocks.Block; import it.cnr.isti.workflow.manager.blocks.configurations.ConditionalBlockConfiguration; import it.cnr.isti.workflow.manager.blocks.configurations.EndBlockConfiguration; -import it.cnr.isti.workflow.manager.blocks.configurations.ExclusiveMergeBlockConfiguration; -import it.cnr.isti.workflow.manager.blocks.configurations.ExclusiveMergeInput; +import it.cnr.isti.workflow.manager.blocks.configurations.BranchRejoinBlockConfiguration; +import it.cnr.isti.workflow.manager.blocks.configurations.BranchRejoinInput; import it.cnr.isti.workflow.manager.blocks.configurations.HumanDecisionBlockConfiguration; import it.cnr.isti.workflow.manager.blocks.configurations.HumanDecisionOption; import it.cnr.isti.workflow.manager.blocks.factories.ConditionalBlockFactory; import it.cnr.isti.workflow.manager.blocks.factories.EndBlockFactory; -import it.cnr.isti.workflow.manager.blocks.factories.ExclusiveMergeBlockFactory; +import it.cnr.isti.workflow.manager.blocks.factories.BranchRejoinBlockFactory; import it.cnr.isti.workflow.manager.blocks.factories.HumanDecisionBlockFactory; import it.cnr.isti.workflow.manager.blocks.types.ConditionalBlockType; import it.cnr.isti.workflow.manager.blocks.types.EndBlockType; -import it.cnr.isti.workflow.manager.blocks.types.ExclusiveMergeBlockType; +import it.cnr.isti.workflow.manager.blocks.types.BranchRejoinBlockType; import it.cnr.isti.workflow.manager.blocks.types.HumanDecisionBlockType; import it.cnr.isti.workflow.manager.executions.bias.BiasExecutionContext; import it.cnr.isti.workflow.manager.executions.bias.BiasExecutionMode; @@ -54,7 +54,7 @@ class ControlFlowGatewayIntegrationTest { ConditionalBlockFactory conditionalBlockFactory; @Autowired - ExclusiveMergeBlockFactory exclusiveMergeBlockFactory; + BranchRejoinBlockFactory branchRejoinBlockFactory; @Autowired HumanDecisionBlockFactory humanDecisionBlockFactory; @@ -69,12 +69,12 @@ class ControlFlowGatewayIntegrationTest { .condition("${{decision}} == 'yes'") .outputTemplate("${{payload}}") .build()); - Block merge = exclusiveMergeBlockFactory.create( - ExclusiveMergeBlockConfiguration.builder() + Block merge = branchRejoinBlockFactory.create( + BranchRejoinBlockConfiguration.builder() .name("Eligibility merge") .inputs(List.of( - new ExclusiveMergeInput("accepted", "Accepted", IOType.TEXT), - new ExclusiveMergeInput("rejected", "Rejected", IOType.TEXT))) + new BranchRejoinInput("accepted", "Accepted", IOType.TEXT), + new BranchRejoinInput("rejected", "Rejected", IOType.TEXT))) .outputName("result") .build()); Block end = endBlockFactory.create(end("DONE", "Completed")); @@ -88,7 +88,7 @@ class ControlFlowGatewayIntegrationTest { .build(); for (String decision : List.of("yes", "no")) { - ExecutionObject execution = executionsService.createExecution("Explicit exclusive merge", flow); + ExecutionObject execution = executionsService.createExecution("Explicit branch rejoin", flow); executionsService.prepareInput(execution.getId(), router.getId(), "decision", decision); executionsService.prepareInput(execution.getId(), router.getId(), "payload", "case-42"); executionsService.startExecution(execution.getId()); diff --git a/src/test/java/it/cnr/isti/workflow/manager/executions/JsonDossierExecutionIntegrationTest.java b/src/test/java/it/cnr/isti/workflow/manager/executions/JsonDossierExecutionIntegrationTest.java index f0dfe1c..4440a09 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/executions/JsonDossierExecutionIntegrationTest.java +++ b/src/test/java/it/cnr/isti/workflow/manager/executions/JsonDossierExecutionIntegrationTest.java @@ -17,15 +17,15 @@ import tools.jackson.databind.JsonNode; import it.cnr.isti.workflow.manager.blocks.Block; import it.cnr.isti.workflow.manager.blocks.configurations.EndBlockConfiguration; -import it.cnr.isti.workflow.manager.blocks.configurations.ExclusiveMergeBlockConfiguration; -import it.cnr.isti.workflow.manager.blocks.configurations.ExclusiveMergeInput; +import it.cnr.isti.workflow.manager.blocks.configurations.BranchRejoinBlockConfiguration; +import it.cnr.isti.workflow.manager.blocks.configurations.BranchRejoinInput; import it.cnr.isti.workflow.manager.blocks.configurations.HumanDecisionBlockConfiguration; import it.cnr.isti.workflow.manager.blocks.configurations.HumanDecisionOption; import it.cnr.isti.workflow.manager.blocks.factories.EndBlockFactory; -import it.cnr.isti.workflow.manager.blocks.factories.ExclusiveMergeBlockFactory; +import it.cnr.isti.workflow.manager.blocks.factories.BranchRejoinBlockFactory; import it.cnr.isti.workflow.manager.blocks.factories.HumanDecisionBlockFactory; import it.cnr.isti.workflow.manager.blocks.types.EndBlockType; -import it.cnr.isti.workflow.manager.blocks.types.ExclusiveMergeBlockType; +import it.cnr.isti.workflow.manager.blocks.types.BranchRejoinBlockType; import it.cnr.isti.workflow.manager.blocks.types.HumanDecisionBlockType; import it.cnr.isti.workflow.manager.executions.steps.InputResolutionState; import it.cnr.isti.workflow.manager.flows.model.Connection; @@ -48,7 +48,7 @@ class JsonDossierExecutionIntegrationTest { HumanDecisionBlockFactory humanDecisionBlockFactory; @Autowired - ExclusiveMergeBlockFactory exclusiveMergeBlockFactory; + BranchRejoinBlockFactory branchRejoinBlockFactory; @Autowired EndBlockFactory endBlockFactory; @@ -62,12 +62,12 @@ class JsonDossierExecutionIntegrationTest { new HumanDecisionOption("existing", "Existing position"), new HumanDecisionOption("new", "New position"))) .build()); - Block merge = exclusiveMergeBlockFactory.create( - ExclusiveMergeBlockConfiguration.builder() + Block merge = branchRejoinBlockFactory.create( + BranchRejoinBlockConfiguration.builder() .name("merge-existing-position") .inputs(List.of( - new ExclusiveMergeInput("existing", "Existing position", IOType.JSON), - new ExclusiveMergeInput("new", "New position", IOType.JSON))) + new BranchRejoinInput("existing", "Existing position", IOType.JSON), + new BranchRejoinInput("new", "New position", IOType.JSON))) .outputName("output") .build()); Block end = endBlockFactory.create(EndBlockConfiguration.builder() diff --git a/src/test/java/it/cnr/isti/workflow/manager/executions/bias/ControlFlowBlockBiasCapabilitiesTest.java b/src/test/java/it/cnr/isti/workflow/manager/executions/bias/ControlFlowBlockBiasCapabilitiesTest.java index cea246c..bce8dfe 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/executions/bias/ControlFlowBlockBiasCapabilitiesTest.java +++ b/src/test/java/it/cnr/isti/workflow/manager/executions/bias/ControlFlowBlockBiasCapabilitiesTest.java @@ -9,7 +9,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.TestPropertySource; import it.cnr.isti.workflow.manager.blocks.types.EndBlockType; -import it.cnr.isti.workflow.manager.blocks.types.ExclusiveMergeBlockType; +import it.cnr.isti.workflow.manager.blocks.types.BranchRejoinBlockType; import it.cnr.isti.workflow.manager.blocks.types.HumanDecisionBlockType; import it.cnr.isti.workflow.manager.controllers.BiasCapabilitiesController; import it.cnr.isti.workflow.manager.flows.model.bias.BiasActivationMode; @@ -34,8 +34,8 @@ class ControlFlowBlockBiasCapabilitiesTest { } @Test - void exclusiveMergeBlockDoesNotSupportRoutingOverride() { - var capabilities = capabilitiesController.getCapabilities(ExclusiveMergeBlockType.TYPE); + void branchRejoinBlockDoesNotSupportRoutingOverride() { + var capabilities = capabilitiesController.getCapabilities(BranchRejoinBlockType.TYPE); assertFalse(capabilities.activationModes().contains(BiasActivationMode.ROUTING_OVERRIDE)); } diff --git a/src/test/java/it/cnr/isti/workflow/manager/executions/executors/blocks/ExclusiveMergeExecutorTest.java b/src/test/java/it/cnr/isti/workflow/manager/executions/executors/blocks/BranchRejoinExecutorTest.java similarity index 72% rename from src/test/java/it/cnr/isti/workflow/manager/executions/executors/blocks/ExclusiveMergeExecutorTest.java rename to src/test/java/it/cnr/isti/workflow/manager/executions/executors/blocks/BranchRejoinExecutorTest.java index d74e429..797c858 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/executions/executors/blocks/ExclusiveMergeExecutorTest.java +++ b/src/test/java/it/cnr/isti/workflow/manager/executions/executors/blocks/BranchRejoinExecutorTest.java @@ -9,17 +9,17 @@ import java.util.Map; import org.junit.jupiter.api.Test; import it.cnr.isti.workflow.manager.blocks.Block; -import it.cnr.isti.workflow.manager.blocks.configurations.ExclusiveMergeBlockConfiguration; -import it.cnr.isti.workflow.manager.blocks.configurations.ExclusiveMergeInput; -import it.cnr.isti.workflow.manager.blocks.types.ExclusiveMergeBlockType; +import it.cnr.isti.workflow.manager.blocks.configurations.BranchRejoinBlockConfiguration; +import it.cnr.isti.workflow.manager.blocks.configurations.BranchRejoinInput; +import it.cnr.isti.workflow.manager.blocks.types.BranchRejoinBlockType; import it.cnr.isti.workflow.manager.executions.NodeExecutionException; import it.cnr.isti.workflow.manager.executions.steps.Input; import it.cnr.isti.workflow.manager.ios.IODescriptor; import it.cnr.isti.workflow.manager.ios.IOType; -class ExclusiveMergeExecutorTest { +class BranchRejoinExecutorTest { - private final ExclusiveMergeExecutor executor = new ExclusiveMergeExecutor(); + private final BranchRejoinExecutor executor = new BranchRejoinExecutor(); @Test void propagatesTheSingleSelectedValueUnderTheConfiguredOutputName() { @@ -41,7 +41,7 @@ class ExclusiveMergeExecutorTest { NodeExecutionException exception = assertThrows(NodeExecutionException.class, () -> executor.execute(block(), List.of(existing, newValue), Map.of(), Map.of(), Map.of(), null)); - assertEquals("EXCLUSIVE_MERGE_MULTIPLE_VALUES", exception.getErrorCode()); + assertEquals("BRANCH_REJOIN_MULTIPLE_VALUES", exception.getErrorCode()); } @Test @@ -53,18 +53,18 @@ class ExclusiveMergeExecutorTest { NodeExecutionException exception = assertThrows(NodeExecutionException.class, () -> executor.execute(block(), List.of(existing, unavailable), Map.of(), Map.of(), Map.of(), null)); - assertEquals("EXCLUSIVE_MERGE_INPUT_UNAVAILABLE", exception.getErrorCode()); + assertEquals("BRANCH_REJOIN_INPUT_UNAVAILABLE", exception.getErrorCode()); } - private Block block() { - ExclusiveMergeBlockConfiguration configuration = ExclusiveMergeBlockConfiguration.builder() + private Block block() { + BranchRejoinBlockConfiguration configuration = BranchRejoinBlockConfiguration.builder() .name("merge") - .inputs(List.of(new ExclusiveMergeInput("existing"), new ExclusiveMergeInput("new"))) + .inputs(List.of(new BranchRejoinInput("existing"), new BranchRejoinInput("new"))) .outputName("output") .build(); - return Block.builder() + return Block.builder() .specificConfiguration(configuration) - .type(new ExclusiveMergeBlockType()) + .type(new BranchRejoinBlockType()) .build(); } } diff --git a/src/test/java/it/cnr/isti/workflow/manager/executions/steps/activation/ExclusiveMergeActivationPolicyTest.java b/src/test/java/it/cnr/isti/workflow/manager/executions/steps/activation/BranchRejoinActivationPolicyTest.java similarity index 95% rename from src/test/java/it/cnr/isti/workflow/manager/executions/steps/activation/ExclusiveMergeActivationPolicyTest.java rename to src/test/java/it/cnr/isti/workflow/manager/executions/steps/activation/BranchRejoinActivationPolicyTest.java index faed6e9..b7ed072 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/executions/steps/activation/ExclusiveMergeActivationPolicyTest.java +++ b/src/test/java/it/cnr/isti/workflow/manager/executions/steps/activation/BranchRejoinActivationPolicyTest.java @@ -11,9 +11,9 @@ import it.cnr.isti.workflow.manager.executions.steps.StepSkipReason; import it.cnr.isti.workflow.manager.ios.IODescriptor; import it.cnr.isti.workflow.manager.ios.IOType; -class ExclusiveMergeActivationPolicyTest { +class BranchRejoinActivationPolicyTest { - private final ExclusiveMergeActivationPolicy policy = new ExclusiveMergeActivationPolicy(); + private final BranchRejoinActivationPolicy policy = new BranchRejoinActivationPolicy(); private final IODescriptor existingInput = IODescriptor.of("existing", IOType.TEXT); private final IODescriptor newInput = IODescriptor.of("new", IOType.TEXT); diff --git a/src/test/java/it/cnr/isti/workflow/manager/flows/JensenStructuredFlowsTest.java b/src/test/java/it/cnr/isti/workflow/manager/flows/JensenStructuredFlowsTest.java index 11c59c6..942855a 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/flows/JensenStructuredFlowsTest.java +++ b/src/test/java/it/cnr/isti/workflow/manager/flows/JensenStructuredFlowsTest.java @@ -17,7 +17,7 @@ import org.springframework.test.context.TestPropertySource; import it.cnr.isti.workflow.manager.app.ObjectMapperHolder; import it.cnr.isti.workflow.manager.blocks.Block; import it.cnr.isti.workflow.manager.blocks.types.EndBlockType; -import it.cnr.isti.workflow.manager.blocks.types.ExclusiveMergeBlockType; +import it.cnr.isti.workflow.manager.blocks.types.BranchRejoinBlockType; import it.cnr.isti.workflow.manager.blocks.types.HumanDecisionBlockType; import it.cnr.isti.workflow.manager.flows.model.FlowNode; import it.cnr.isti.workflow.manager.flows.model.bias.BlockBiasAnnotation; @@ -96,13 +96,13 @@ class JensenStructuredFlowsTest { .filter(node -> node instanceof Block block && block.getType() instanceof HumanDecisionBlockType) .count(); long merges = nodes.stream() - .filter(node -> node instanceof Block block && block.getType() instanceof ExclusiveMergeBlockType) + .filter(node -> node instanceof Block block && block.getType() instanceof BranchRejoinBlockType) .count(); long ends = nodes.stream() .filter(node -> node instanceof Block block && block.getType() instanceof EndBlockType) .count(); assertEquals(expectedDecisions, decisions, () -> flow.name() + ": HumanDecisionBlock count"); - assertEquals(expectedMerges, merges, () -> flow.name() + ": ExclusiveMergeBlock count"); + assertEquals(expectedMerges, merges, () -> flow.name() + ": BranchRejoinBlock count"); assertEquals(expectedEnds, ends, () -> flow.name() + ": EndBlock count"); assertEquals(expectedLanes, flow.flow().getLanes().size(), () -> flow.name() + ": lane count"); } diff --git a/src/test/java/it/cnr/isti/workflow/manager/flows/validation/ExclusiveMergeStructureValidationTest.java b/src/test/java/it/cnr/isti/workflow/manager/flows/validation/BranchRejoinStructureValidationTest.java similarity index 83% rename from src/test/java/it/cnr/isti/workflow/manager/flows/validation/ExclusiveMergeStructureValidationTest.java rename to src/test/java/it/cnr/isti/workflow/manager/flows/validation/BranchRejoinStructureValidationTest.java index ebf7492..6552b57 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/flows/validation/ExclusiveMergeStructureValidationTest.java +++ b/src/test/java/it/cnr/isti/workflow/manager/flows/validation/BranchRejoinStructureValidationTest.java @@ -13,14 +13,14 @@ import org.springframework.test.context.TestPropertySource; import it.cnr.isti.workflow.manager.blocks.Block; import it.cnr.isti.workflow.manager.blocks.configurations.ConditionalBlockConfiguration; -import it.cnr.isti.workflow.manager.blocks.configurations.ExclusiveMergeBlockConfiguration; -import it.cnr.isti.workflow.manager.blocks.configurations.ExclusiveMergeInput; +import it.cnr.isti.workflow.manager.blocks.configurations.BranchRejoinBlockConfiguration; +import it.cnr.isti.workflow.manager.blocks.configurations.BranchRejoinInput; import it.cnr.isti.workflow.manager.blocks.configurations.HumanInteractiveBlockConfiguration; import it.cnr.isti.workflow.manager.blocks.factories.ConditionalBlockFactory; -import it.cnr.isti.workflow.manager.blocks.factories.ExclusiveMergeBlockFactory; +import it.cnr.isti.workflow.manager.blocks.factories.BranchRejoinBlockFactory; import it.cnr.isti.workflow.manager.blocks.factories.HumanInteractiveBlockFactory; import it.cnr.isti.workflow.manager.blocks.types.ConditionalBlockType; -import it.cnr.isti.workflow.manager.blocks.types.ExclusiveMergeBlockType; +import it.cnr.isti.workflow.manager.blocks.types.BranchRejoinBlockType; import it.cnr.isti.workflow.manager.blocks.types.HumanInteractionBlockType; import it.cnr.isti.workflow.manager.flows.model.Connection; import it.cnr.isti.workflow.manager.flows.model.FlowData; @@ -30,12 +30,12 @@ import jakarta.validation.Validator; /** * Confirms the "merge incrociato rifiutato" requirement: two exclusive branches from the same * Conditional cannot reconverge on a shared downstream node unless that node is an explicit - * ExclusiveMergeBlock. Complements the Phase 0 characterization tests, which only assert this + * BranchRejoinBlock. Complements the Phase 0 characterization tests, which only assert this * behavior indirectly. */ @SpringBootTest @TestPropertySource(locations = "classpath:test.properties") -class ExclusiveMergeStructureValidationTest { +class BranchRejoinStructureValidationTest { @Autowired Validator validator; @@ -47,10 +47,10 @@ class ExclusiveMergeStructureValidationTest { HumanInteractiveBlockFactory humanInteractiveBlockFactory; @Autowired - ExclusiveMergeBlockFactory exclusiveMergeBlockFactory; + BranchRejoinBlockFactory branchRejoinBlockFactory; @Test - void implicitReconvergenceWithoutAnExclusiveMergeBlockIsRejected() { + void implicitReconvergenceWithoutAnBranchRejoinBlockIsRejected() { Block conditional = conditional(); Block shared = humanInteraction("shared-downstream"); @@ -67,12 +67,12 @@ class ExclusiveMergeStructureValidationTest { } @Test - void reconvergenceThroughAnExplicitExclusiveMergeBlockIsAccepted() { + void reconvergenceThroughAnExplicitBranchRejoinBlockIsAccepted() { Block conditional = conditional(); - Block merge = exclusiveMergeBlockFactory.create( - ExclusiveMergeBlockConfiguration.builder() + Block merge = branchRejoinBlockFactory.create( + BranchRejoinBlockConfiguration.builder() .name("merge") - .inputs(List.of(new ExclusiveMergeInput("onTrue"), new ExclusiveMergeInput("onFalse"))) + .inputs(List.of(new BranchRejoinInput("onTrue"), new BranchRejoinInput("onFalse"))) .outputName("output") .build()); Block shared = humanInteraction("shared-downstream"); diff --git a/src/test/java/it/cnr/isti/workflow/manager/flows/validation/NodeTypeCapabilitiesIntegrationTest.java b/src/test/java/it/cnr/isti/workflow/manager/flows/validation/NodeTypeCapabilitiesIntegrationTest.java index a689388..8df2956 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/flows/validation/NodeTypeCapabilitiesIntegrationTest.java +++ b/src/test/java/it/cnr/isti/workflow/manager/flows/validation/NodeTypeCapabilitiesIntegrationTest.java @@ -18,7 +18,7 @@ import it.cnr.isti.workflow.manager.blocks.configurations.HumanInteractiveBlockC import it.cnr.isti.workflow.manager.blocks.factories.EndBlockFactory; import it.cnr.isti.workflow.manager.blocks.factories.HumanInteractiveBlockFactory; import it.cnr.isti.workflow.manager.blocks.types.EndBlockType; -import it.cnr.isti.workflow.manager.blocks.types.ExclusiveMergeBlockType; +import it.cnr.isti.workflow.manager.blocks.types.BranchRejoinBlockType; import it.cnr.isti.workflow.manager.blocks.types.HumanDecisionBlockType; import it.cnr.isti.workflow.manager.blocks.types.HumanInteractionBlockType; import it.cnr.isti.workflow.manager.containers.types.GenericContainerType; @@ -68,8 +68,8 @@ class NodeTypeCapabilitiesIntegrationTest { assertEquals(NodeVisualRole.DECISION, decision.visualRole()); assertTrue(decision.biasAnnotationsAllowed()); - var merge = blocksController.getConfigurationDescriptorForType(ExclusiveMergeBlockType.TYPE).capabilities(); - assertEquals(NodeVisualRole.MERGE, merge.visualRole()); + var branchRejoin = blocksController.getConfigurationDescriptorForType(BranchRejoinBlockType.TYPE).capabilities(); + assertEquals(NodeVisualRole.BRANCH_REJOIN, branchRejoin.visualRole()); var container = containersController.getTypes().stream() .filter(descriptor -> GenericContainerType.TYPE.equals(descriptor.type()))