feat: evaluate built-in routing predicates and parameterized host conditions
Store transition when as JSON instead of a lookup key so designers can compose closed-set logic while hosts still supply complex refs with args. Match conditional edges before the unconditional default. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+4
-4
@@ -45,7 +45,7 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
|
||||
+ " VALUES (?, ?, ?, ?, ?)";
|
||||
private static final String INSERT_TRANSITION =
|
||||
"INSERT INTO ordo_step_transition (definition_id, definition_version, from_step_id, to_step_id,"
|
||||
+ " condition_key, priority) VALUES (?, ?, ?, ?, ?, ?)";
|
||||
+ " condition_json, priority) VALUES (?, ?, ?, ?, ?, ?)";
|
||||
private static final String SELECT_DEFINITION =
|
||||
"SELECT name FROM ordo_process_definition WHERE id = ? AND version = ?";
|
||||
private static final String SELECT_STEPS =
|
||||
@@ -56,7 +56,7 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
|
||||
"SELECT step_id, candidate FROM ordo_step_candidate"
|
||||
+ " WHERE definition_id = ? AND definition_version = ? ORDER BY step_id, candidate_order";
|
||||
private static final String SELECT_TRANSITIONS =
|
||||
"SELECT from_step_id, to_step_id, condition_key, priority FROM ordo_step_transition "
|
||||
"SELECT from_step_id, to_step_id, condition_json, priority FROM ordo_step_transition "
|
||||
+ "WHERE definition_id = ? AND definition_version = ? ORDER BY from_step_id, priority";
|
||||
private static final String SELECT_LATEST_PAGE_BASE =
|
||||
"SELECT p.id, p.current_version, d.name FROM ordo_process p"
|
||||
@@ -260,7 +260,7 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
|
||||
try (ResultSet resultSet = selectTransitions.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
TransitionRow row = StepTransitionMapper.readRow(resultSet);
|
||||
transitions.add(new StepTransition(row.fromStepId(), row.toStepId(), row.conditionKey(),
|
||||
transitions.add(new StepTransition(row.fromStepId(), row.toStepId(), row.when(),
|
||||
row.priority()));
|
||||
}
|
||||
}
|
||||
@@ -377,7 +377,7 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
|
||||
insertTransition.setInt(2, definition.version());
|
||||
insertTransition.setString(3, transition.fromStepId());
|
||||
insertTransition.setString(4, transition.toStepId());
|
||||
insertTransition.setString(5, transition.conditionKey());
|
||||
insertTransition.setString(5, transition.when() == null ? null : transition.when().toJson());
|
||||
insertTransition.setInt(6, transition.priority());
|
||||
insertTransition.executeUpdate();
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,5 +1,7 @@
|
||||
package com.jetlumen.ordo.storage.jdbc.mapper;
|
||||
|
||||
import com.jetlumen.ordo.api.RoutingWhen;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@@ -12,10 +14,10 @@ public final class StepTransitionMapper {
|
||||
return new TransitionRow(
|
||||
resultSet.getString("from_step_id"),
|
||||
resultSet.getString("to_step_id"),
|
||||
resultSet.getString("condition_key"),
|
||||
RoutingWhen.parseJson(resultSet.getString("condition_json")),
|
||||
resultSet.getInt("priority"));
|
||||
}
|
||||
|
||||
public record TransitionRow(String fromStepId, String toStepId, String conditionKey, int priority) {
|
||||
public record TransitionRow(String fromStepId, String toStepId, RoutingWhen when, int priority) {
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
-- Replace opaque condition_key with the full when JSON object.
|
||||
|
||||
ALTER TABLE ordo_step_transition ADD COLUMN condition_json TEXT;
|
||||
ALTER TABLE ordo_step_transition DROP COLUMN condition_key;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
-- Replace opaque condition_key with the full when JSON object.
|
||||
|
||||
ALTER TABLE ordo_step_transition ADD COLUMN condition_json TEXT;
|
||||
ALTER TABLE ordo_step_transition DROP COLUMN condition_key;
|
||||
+1
-1
@@ -76,7 +76,7 @@ class JdbcMysqlIntegrationTest {
|
||||
}
|
||||
|
||||
MysqlDataSource schemaDataSource = newDataSource(DATABASE);
|
||||
JdbcTestSupport.applySchema(schemaDataSource, JdbcTestSupport.MYSQL_BASELINE);
|
||||
JdbcTestSupport.applySchema(schemaDataSource, JdbcTestSupport.MYSQL_BASELINE, JdbcTestSupport.MYSQL_V2);
|
||||
dataSource = schemaDataSource;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -183,7 +183,7 @@ class JdbcOrdoEngineIntegrationTest {
|
||||
@Test
|
||||
void rollsBackTheWholeApprovalWhenNoRouteMatches() {
|
||||
OrdoEngine failingEngine = new DefaultOrdoEngine(Clock.fixed(NOW, ZoneOffset.UTC),
|
||||
AssigneeResolver.direct(), (key, context) -> false, ActionHandler.noop(),
|
||||
AssigneeResolver.direct(), (key, args, context) -> false, ActionHandler.noop(),
|
||||
new JdbcTransactionExecutor(connectionProvider),
|
||||
new JdbcProcessDefinitionRepository(connectionProvider),
|
||||
new JdbcProcessInstanceRepository(connectionProvider),
|
||||
@@ -193,7 +193,7 @@ class JdbcOrdoEngineIntegrationTest {
|
||||
List.of());
|
||||
failingEngine.publish(new ProcessDefinition("leave-noroute", "Leave request",
|
||||
List.of(ApprovalStep.single("manager", "Manager approval", "maria")),
|
||||
List.of(StepTransition.endWhen("manager", "never", 0))));
|
||||
List.of(StepTransition.endRef("manager", "never", java.util.Map.of(), 0))));
|
||||
|
||||
ProcessInstance instance = failingEngine.start("leave-noroute", "alice");
|
||||
ApprovalTask managerTask = failingEngine.findPendingTasksByInstanceId(instance.id()).get(0);
|
||||
|
||||
+2
-1
@@ -5,6 +5,7 @@ import com.jetlumen.ordo.api.ApprovalStep;
|
||||
import com.jetlumen.ordo.api.ProcessDefinition;
|
||||
import com.jetlumen.ordo.api.StepDue;
|
||||
import com.jetlumen.ordo.api.StepKind;
|
||||
import com.jetlumen.ordo.api.RoutingPredicate;
|
||||
import com.jetlumen.ordo.api.StepTransition;
|
||||
import com.jetlumen.ordo.api.query.Page;
|
||||
import com.jetlumen.ordo.api.query.PageRequest;
|
||||
@@ -73,7 +74,7 @@ class JdbcProcessDefinitionRepositoryTest {
|
||||
ApprovalStep.single("manager", "Manager approval", "maria"),
|
||||
ApprovalStep.single("director", "Director approval", "diana")),
|
||||
List.of(
|
||||
StepTransition.when("manager", "director", "amount-gt-1000", 0),
|
||||
StepTransition.when("manager", "director", RoutingPredicate.gt("amount", 1000), 0),
|
||||
new StepTransition("manager", null, null, 1),
|
||||
StepTransition.end("director")));
|
||||
|
||||
|
||||
+10
-6
@@ -14,7 +14,9 @@ import java.util.UUID;
|
||||
/** Creates isolated in-memory H2 databases with the Ordo schema applied. */
|
||||
final class JdbcTestSupport {
|
||||
static final String POSTGRES_BASELINE = "/db/postgresql/migration/V1__baseline.sql";
|
||||
static final String POSTGRES_V2 = "/db/postgresql/migration/V2__transition_condition_json.sql";
|
||||
static final String MYSQL_BASELINE = "/db/mysql/migration/V1__baseline.sql";
|
||||
static final String MYSQL_V2 = "/db/mysql/migration/V2__transition_condition_json.sql";
|
||||
|
||||
private JdbcTestSupport() {
|
||||
}
|
||||
@@ -29,15 +31,17 @@ final class JdbcTestSupport {
|
||||
}
|
||||
|
||||
static void applySchema(DataSource dataSource) {
|
||||
applySchema(dataSource, POSTGRES_BASELINE);
|
||||
applySchema(dataSource, POSTGRES_BASELINE, POSTGRES_V2);
|
||||
}
|
||||
|
||||
static void applySchema(DataSource dataSource, String resourcePath) {
|
||||
String migration = loadSchema(resourcePath);
|
||||
static void applySchema(DataSource dataSource, String... resourcePaths) {
|
||||
try (Connection connection = dataSource.getConnection(); Statement statement = connection.createStatement()) {
|
||||
for (String sql : stripComments(migration).split(";")) {
|
||||
if (!sql.isBlank()) {
|
||||
statement.execute(sql);
|
||||
for (String resourcePath : resourcePaths) {
|
||||
String migration = loadSchema(resourcePath);
|
||||
for (String sql : stripComments(migration).split(";")) {
|
||||
if (!sql.isBlank()) {
|
||||
statement.execute(sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
|
||||
Reference in New Issue
Block a user