feat: pin running instances to immutable published definition versions
Replace register/replace with publish so new graphs can ship without rewriting old ones, and keep in-flight work on the version it started with. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+193
-109
@@ -15,6 +15,8 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -24,36 +26,45 @@ import java.util.Optional;
|
||||
|
||||
/** JDBC implementation of the definition storage port; steps and their candidates live in separate tables. */
|
||||
public final class JdbcProcessDefinitionRepository implements ProcessDefinitionRepository {
|
||||
private static final String INSERT_PROCESS =
|
||||
"INSERT INTO ordo_process (id, current_version, name) VALUES (?, ?, ?)";
|
||||
private static final String UPDATE_PROCESS =
|
||||
"UPDATE ordo_process SET current_version = ?, name = ? WHERE id = ?";
|
||||
private static final String LOCK_PROCESS =
|
||||
"SELECT current_version FROM ordo_process WHERE id = ? FOR UPDATE";
|
||||
private static final String INSERT_DEFINITION =
|
||||
"INSERT INTO ordo_process_definition (id, name) VALUES (?, ?)";
|
||||
"INSERT INTO ordo_process_definition (id, version, name, created_at) VALUES (?, ?, ?, ?)";
|
||||
private static final String INSERT_STEP =
|
||||
"INSERT INTO ordo_approval_step (definition_id, step_id, step_name, policy, step_order, kind, action_key,"
|
||||
+ " due_after, due_then, due_to, due_action) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
"INSERT INTO ordo_approval_step (definition_id, definition_version, step_id, step_name, policy, step_order,"
|
||||
+ " kind, action_key, due_after, due_then, due_to, due_action)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
private static final String INSERT_CANDIDATE =
|
||||
"INSERT INTO ordo_step_candidate (definition_id, step_id, candidate, candidate_order) VALUES (?, ?, ?, ?)";
|
||||
"INSERT INTO ordo_step_candidate (definition_id, definition_version, step_id, candidate, candidate_order)"
|
||||
+ " VALUES (?, ?, ?, ?, ?)";
|
||||
private static final String INSERT_TRANSITION =
|
||||
"INSERT INTO ordo_step_transition (definition_id, from_step_id, to_step_id, condition_key, priority) VALUES (?, ?, ?, ?, ?)";
|
||||
private static final String UPDATE_DEFINITION_NAME =
|
||||
"UPDATE ordo_process_definition SET name = ? WHERE id = ?";
|
||||
private static final String DELETE_TRANSITIONS =
|
||||
"DELETE FROM ordo_step_transition WHERE definition_id = ?";
|
||||
private static final String DELETE_CANDIDATES =
|
||||
"DELETE FROM ordo_step_candidate WHERE definition_id = ?";
|
||||
private static final String DELETE_STEPS =
|
||||
"DELETE FROM ordo_approval_step WHERE definition_id = ?";
|
||||
"INSERT INTO ordo_step_transition (definition_id, definition_version, from_step_id, to_step_id,"
|
||||
+ " condition_key, priority) VALUES (?, ?, ?, ?, ?, ?)";
|
||||
private static final String SELECT_DEFINITION =
|
||||
"SELECT id, name FROM ordo_process_definition WHERE id = ?";
|
||||
"SELECT name FROM ordo_process_definition WHERE id = ? AND version = ?";
|
||||
private static final String SELECT_STEPS =
|
||||
"SELECT step_id, step_name, policy, kind, action_key, due_after, due_then, due_to, due_action"
|
||||
+ " FROM ordo_approval_step"
|
||||
+ " WHERE definition_id = ? ORDER BY step_order";
|
||||
+ " WHERE definition_id = ? AND definition_version = ? ORDER BY step_order";
|
||||
private static final String SELECT_CANDIDATES =
|
||||
"SELECT step_id, candidate FROM ordo_step_candidate WHERE definition_id = ? ORDER BY step_id, candidate_order";
|
||||
"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 "
|
||||
+ "WHERE definition_id = ? ORDER BY from_step_id, priority";
|
||||
private static final String SELECT_DEFINITIONS_PAGE =
|
||||
"SELECT id, name FROM ordo_process_definition ORDER BY id LIMIT ? OFFSET ?";
|
||||
+ "WHERE definition_id = ? AND definition_version = ? ORDER BY from_step_id, priority";
|
||||
private static final String SELECT_LATEST_PAGE =
|
||||
"SELECT p.id, p.current_version, d.name FROM ordo_process p"
|
||||
+ " JOIN ordo_process_definition d ON d.id = p.id AND d.version = p.current_version"
|
||||
+ " ORDER BY p.id LIMIT ? OFFSET ?";
|
||||
private static final String COUNT_PROCESSES = "SELECT COUNT(*) FROM ordo_process";
|
||||
private static final String SELECT_VERSIONS_PAGE =
|
||||
"SELECT version, name FROM ordo_process_definition WHERE id = ? ORDER BY version DESC LIMIT ? OFFSET ?";
|
||||
private static final String COUNT_VERSIONS =
|
||||
"SELECT COUNT(*) FROM ordo_process_definition WHERE id = ?";
|
||||
|
||||
private final JdbcConnectionProvider connectionProvider;
|
||||
|
||||
@@ -62,62 +73,51 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean insertIfAbsent(ProcessDefinition definition) {
|
||||
public ProcessDefinition publish(ProcessDefinition definition) {
|
||||
Objects.requireNonNull(definition, "definition must not be null");
|
||||
Connection connection = connectionProvider.getConnection();
|
||||
try {
|
||||
if (!insertDefinitionRow(connection, definition)) {
|
||||
return false;
|
||||
Integer current = lockCurrentVersion(connection, definition.id());
|
||||
if (current == null) {
|
||||
if (insertProcessRow(connection, definition.id(), 1, definition.name())) {
|
||||
ProcessDefinition first = definition.withVersion(1);
|
||||
insertDefinitionVersion(connection, first);
|
||||
insertGraph(connection, first);
|
||||
return first;
|
||||
}
|
||||
current = lockCurrentVersion(connection, definition.id());
|
||||
if (current == null) {
|
||||
throw new JdbcStorageException(
|
||||
"failed to lock process after concurrent publish: " + definition.id(),
|
||||
new IllegalStateException("process row missing"));
|
||||
}
|
||||
}
|
||||
insertGraph(connection, definition);
|
||||
return true;
|
||||
ProcessDefinition latest = assemble(connection, definition.id(), current);
|
||||
if (latest.sameGraph(definition)) {
|
||||
return latest;
|
||||
}
|
||||
int next = current + 1;
|
||||
ProcessDefinition published = definition.withVersion(next);
|
||||
insertDefinitionVersion(connection, published);
|
||||
insertGraph(connection, published);
|
||||
updateProcess(connection, definition.id(), next, definition.name());
|
||||
return published;
|
||||
} catch (SQLException e) {
|
||||
throw new JdbcStorageException("failed to insert definition: " + definition.id(), e);
|
||||
throw new JdbcStorageException("failed to publish definition: " + definition.id(), e);
|
||||
} finally {
|
||||
connectionProvider.close(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upsert(ProcessDefinition definition) {
|
||||
Objects.requireNonNull(definition, "definition must not be null");
|
||||
public Optional<ProcessDefinition> findLatest(String definitionId) {
|
||||
Connection connection = connectionProvider.getConnection();
|
||||
try {
|
||||
// PostgreSQL aborts the current transaction on unique-constraint violations,
|
||||
// so upsert must not probe existence via a failing INSERT.
|
||||
if (definitionExists(connection, definition.id())) {
|
||||
updateDefinitionName(connection, definition);
|
||||
deleteGraph(connection, definition.id());
|
||||
} else {
|
||||
try (PreparedStatement insert = connection.prepareStatement(INSERT_DEFINITION)) {
|
||||
insert.setString(1, definition.id());
|
||||
insert.setString(2, definition.name());
|
||||
insert.executeUpdate();
|
||||
}
|
||||
Integer version = currentVersion(connection, definitionId);
|
||||
if (version == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
insertGraph(connection, definition);
|
||||
} catch (SQLException e) {
|
||||
throw new JdbcStorageException("failed to upsert definition: " + definition.id(), e);
|
||||
} finally {
|
||||
connectionProvider.close(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ProcessDefinition> findById(String definitionId) {
|
||||
Connection connection = connectionProvider.getConnection();
|
||||
try {
|
||||
String name;
|
||||
try (PreparedStatement selectDefinition = connection.prepareStatement(SELECT_DEFINITION)) {
|
||||
selectDefinition.setString(1, definitionId);
|
||||
try (ResultSet resultSet = selectDefinition.executeQuery()) {
|
||||
if (!resultSet.next()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
name = resultSet.getString("name");
|
||||
}
|
||||
}
|
||||
return Optional.of(assemble(connection, definitionId, name));
|
||||
return Optional.of(assemble(connection, definitionId, version));
|
||||
} catch (SQLException e) {
|
||||
throw new JdbcStorageException("failed to load definition: " + definitionId, e);
|
||||
} finally {
|
||||
@@ -125,24 +125,41 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ProcessDefinition> find(String definitionId, int version) {
|
||||
Connection connection = connectionProvider.getConnection();
|
||||
try {
|
||||
if (!definitionExists(connection, definitionId, version)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(assemble(connection, definitionId, version));
|
||||
} catch (SQLException e) {
|
||||
throw new JdbcStorageException("failed to load definition: " + definitionId + " version " + version, e);
|
||||
} finally {
|
||||
connectionProvider.close(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ProcessDefinition> findAll(PageRequest pageRequest) {
|
||||
Objects.requireNonNull(pageRequest, "pageRequest must not be null");
|
||||
Connection connection = connectionProvider.getConnection();
|
||||
try {
|
||||
long total = PageSupport.count(connection, "SELECT COUNT(*) FROM ordo_process_definition", List.of());
|
||||
long total = PageSupport.count(connection, COUNT_PROCESSES, List.of());
|
||||
List<ProcessDefinition> content = new ArrayList<>();
|
||||
try (PreparedStatement select = connection.prepareStatement(SELECT_DEFINITIONS_PAGE)) {
|
||||
try (PreparedStatement select = connection.prepareStatement(SELECT_LATEST_PAGE)) {
|
||||
select.setInt(1, pageRequest.size());
|
||||
select.setInt(2, pageRequest.offset());
|
||||
List<Map.Entry<String, String>> idsAndNames = new ArrayList<>();
|
||||
List<int[]> versions = new ArrayList<>();
|
||||
List<String> ids = new ArrayList<>();
|
||||
try (ResultSet resultSet = select.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
idsAndNames.add(Map.entry(resultSet.getString("id"), resultSet.getString("name")));
|
||||
ids.add(resultSet.getString("id"));
|
||||
versions.add(new int[] {resultSet.getInt("current_version")});
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, String> idAndName : idsAndNames) {
|
||||
content.add(assemble(connection, idAndName.getKey(), idAndName.getValue()));
|
||||
for (int i = 0; i < ids.size(); i++) {
|
||||
content.add(assemble(connection, ids.get(i), versions.get(i)[0]));
|
||||
}
|
||||
}
|
||||
return new Page<>(content, total, pageRequest.page(), pageRequest.size());
|
||||
@@ -153,10 +170,52 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
|
||||
}
|
||||
}
|
||||
|
||||
private static ProcessDefinition assemble(Connection connection, String definitionId, String name) throws SQLException {
|
||||
@Override
|
||||
public Page<ProcessDefinition> findVersions(String definitionId, PageRequest pageRequest) {
|
||||
Objects.requireNonNull(pageRequest, "pageRequest must not be null");
|
||||
Connection connection = connectionProvider.getConnection();
|
||||
try {
|
||||
long total = PageSupport.count(connection, COUNT_VERSIONS, List.of(definitionId));
|
||||
List<Integer> versionNumbers = new ArrayList<>();
|
||||
try (PreparedStatement select = connection.prepareStatement(SELECT_VERSIONS_PAGE)) {
|
||||
select.setString(1, definitionId);
|
||||
select.setInt(2, pageRequest.size());
|
||||
select.setInt(3, pageRequest.offset());
|
||||
try (ResultSet resultSet = select.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
versionNumbers.add(resultSet.getInt("version"));
|
||||
}
|
||||
}
|
||||
}
|
||||
List<ProcessDefinition> content = new ArrayList<>();
|
||||
for (int version : versionNumbers) {
|
||||
content.add(assemble(connection, definitionId, version));
|
||||
}
|
||||
return new Page<>(content, total, pageRequest.page(), pageRequest.size());
|
||||
} catch (SQLException e) {
|
||||
throw new JdbcStorageException("failed to list definition versions: " + definitionId, e);
|
||||
} finally {
|
||||
connectionProvider.close(connection);
|
||||
}
|
||||
}
|
||||
|
||||
private static ProcessDefinition assemble(Connection connection, String definitionId, int version)
|
||||
throws SQLException {
|
||||
String name;
|
||||
try (PreparedStatement selectDefinition = connection.prepareStatement(SELECT_DEFINITION)) {
|
||||
selectDefinition.setString(1, definitionId);
|
||||
selectDefinition.setInt(2, version);
|
||||
try (ResultSet resultSet = selectDefinition.executeQuery()) {
|
||||
if (!resultSet.next()) {
|
||||
throw new SQLException("definition row missing: " + definitionId + " version " + version);
|
||||
}
|
||||
name = resultSet.getString("name");
|
||||
}
|
||||
}
|
||||
List<StepRow> stepRows = new ArrayList<>();
|
||||
try (PreparedStatement selectSteps = connection.prepareStatement(SELECT_STEPS)) {
|
||||
selectSteps.setString(1, definitionId);
|
||||
selectSteps.setInt(2, version);
|
||||
try (ResultSet resultSet = selectSteps.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
stepRows.add(ApprovalStepMapper.readRow(resultSet));
|
||||
@@ -166,6 +225,7 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
|
||||
Map<String, List<String>> candidatesByStep = new LinkedHashMap<>();
|
||||
try (PreparedStatement selectCandidates = connection.prepareStatement(SELECT_CANDIDATES)) {
|
||||
selectCandidates.setString(1, definitionId);
|
||||
selectCandidates.setInt(2, version);
|
||||
try (ResultSet resultSet = selectCandidates.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
candidatesByStep.computeIfAbsent(resultSet.getString("step_id"), key -> new ArrayList<>())
|
||||
@@ -182,6 +242,7 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
|
||||
List<StepTransition> transitions = new ArrayList<>();
|
||||
try (PreparedStatement selectTransitions = connection.prepareStatement(SELECT_TRANSITIONS)) {
|
||||
selectTransitions.setString(1, definitionId);
|
||||
selectTransitions.setInt(2, version);
|
||||
try (ResultSet resultSet = selectTransitions.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
TransitionRow row = StepTransitionMapper.readRow(resultSet);
|
||||
@@ -190,23 +251,46 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ProcessDefinition(definitionId, name, steps, transitions);
|
||||
return new ProcessDefinition(definitionId, version, name, steps, transitions);
|
||||
}
|
||||
|
||||
private static boolean definitionExists(Connection connection, String definitionId) throws SQLException {
|
||||
private static Integer lockCurrentVersion(Connection connection, String definitionId) throws SQLException {
|
||||
try (PreparedStatement select = connection.prepareStatement(LOCK_PROCESS)) {
|
||||
select.setString(1, definitionId);
|
||||
try (ResultSet resultSet = select.executeQuery()) {
|
||||
return resultSet.next() ? resultSet.getInt("current_version") : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Integer currentVersion(Connection connection, String definitionId) throws SQLException {
|
||||
try (PreparedStatement select = connection.prepareStatement(
|
||||
"SELECT current_version FROM ordo_process WHERE id = ?")) {
|
||||
select.setString(1, definitionId);
|
||||
try (ResultSet resultSet = select.executeQuery()) {
|
||||
return resultSet.next() ? resultSet.getInt("current_version") : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean definitionExists(Connection connection, String definitionId, int version)
|
||||
throws SQLException {
|
||||
try (PreparedStatement select = connection.prepareStatement(SELECT_DEFINITION)) {
|
||||
select.setString(1, definitionId);
|
||||
select.setInt(2, version);
|
||||
try (ResultSet resultSet = select.executeQuery()) {
|
||||
return resultSet.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean insertDefinitionRow(Connection connection, ProcessDefinition definition) throws SQLException {
|
||||
try (PreparedStatement insertDefinition = connection.prepareStatement(INSERT_DEFINITION)) {
|
||||
insertDefinition.setString(1, definition.id());
|
||||
insertDefinition.setString(2, definition.name());
|
||||
insertDefinition.executeUpdate();
|
||||
private static boolean insertProcessRow(Connection connection, String id, int version, String name)
|
||||
throws SQLException {
|
||||
try (PreparedStatement insert = connection.prepareStatement(INSERT_PROCESS)) {
|
||||
insert.setString(1, id);
|
||||
insert.setInt(2, version);
|
||||
insert.setString(3, name);
|
||||
insert.executeUpdate();
|
||||
return true;
|
||||
} catch (SQLException e) {
|
||||
if (isDuplicateKey(e)) {
|
||||
@@ -216,26 +300,23 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateDefinitionName(Connection connection, ProcessDefinition definition) throws SQLException {
|
||||
try (PreparedStatement update = connection.prepareStatement(UPDATE_DEFINITION_NAME)) {
|
||||
update.setString(1, definition.name());
|
||||
update.setString(2, definition.id());
|
||||
private static void updateProcess(Connection connection, String id, int version, String name) throws SQLException {
|
||||
try (PreparedStatement update = connection.prepareStatement(UPDATE_PROCESS)) {
|
||||
update.setInt(1, version);
|
||||
update.setString(2, name);
|
||||
update.setString(3, id);
|
||||
update.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private static void deleteGraph(Connection connection, String definitionId) throws SQLException {
|
||||
try (PreparedStatement deleteTransitions = connection.prepareStatement(DELETE_TRANSITIONS)) {
|
||||
deleteTransitions.setString(1, definitionId);
|
||||
deleteTransitions.executeUpdate();
|
||||
}
|
||||
try (PreparedStatement deleteCandidates = connection.prepareStatement(DELETE_CANDIDATES)) {
|
||||
deleteCandidates.setString(1, definitionId);
|
||||
deleteCandidates.executeUpdate();
|
||||
}
|
||||
try (PreparedStatement deleteSteps = connection.prepareStatement(DELETE_STEPS)) {
|
||||
deleteSteps.setString(1, definitionId);
|
||||
deleteSteps.executeUpdate();
|
||||
private static void insertDefinitionVersion(Connection connection, ProcessDefinition definition)
|
||||
throws SQLException {
|
||||
try (PreparedStatement insert = connection.prepareStatement(INSERT_DEFINITION)) {
|
||||
insert.setString(1, definition.id());
|
||||
insert.setInt(2, definition.version());
|
||||
insert.setString(3, definition.name());
|
||||
insert.setTimestamp(4, Timestamp.from(Instant.now()));
|
||||
insert.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,22 +325,23 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
|
||||
for (ApprovalStep step : definition.steps()) {
|
||||
try (PreparedStatement insertStep = connection.prepareStatement(INSERT_STEP)) {
|
||||
insertStep.setString(1, definition.id());
|
||||
insertStep.setString(2, step.id());
|
||||
insertStep.setString(3, step.name());
|
||||
insertStep.setString(4, step.policy().name());
|
||||
insertStep.setInt(5, stepOrder++);
|
||||
insertStep.setString(6, step.kind().name());
|
||||
insertStep.setString(7, step.actionKey());
|
||||
insertStep.setInt(2, definition.version());
|
||||
insertStep.setString(3, step.id());
|
||||
insertStep.setString(4, step.name());
|
||||
insertStep.setString(5, step.policy().name());
|
||||
insertStep.setInt(6, stepOrder++);
|
||||
insertStep.setString(7, step.kind().name());
|
||||
insertStep.setString(8, step.actionKey());
|
||||
if (step.due() == null) {
|
||||
insertStep.setString(8, null);
|
||||
insertStep.setString(9, null);
|
||||
insertStep.setString(10, null);
|
||||
insertStep.setString(11, null);
|
||||
insertStep.setString(12, null);
|
||||
} else {
|
||||
insertStep.setString(8, step.due().after().toString());
|
||||
insertStep.setString(9, step.due().then().name());
|
||||
insertStep.setString(10, step.due().to());
|
||||
insertStep.setString(11, step.due().action());
|
||||
insertStep.setString(9, step.due().after().toString());
|
||||
insertStep.setString(10, step.due().then().name());
|
||||
insertStep.setString(11, step.due().to());
|
||||
insertStep.setString(12, step.due().action());
|
||||
}
|
||||
insertStep.executeUpdate();
|
||||
}
|
||||
@@ -267,9 +349,10 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
|
||||
for (String candidate : step.candidates()) {
|
||||
try (PreparedStatement insertCandidate = connection.prepareStatement(INSERT_CANDIDATE)) {
|
||||
insertCandidate.setString(1, definition.id());
|
||||
insertCandidate.setString(2, step.id());
|
||||
insertCandidate.setString(3, candidate);
|
||||
insertCandidate.setInt(4, candidateOrder++);
|
||||
insertCandidate.setInt(2, definition.version());
|
||||
insertCandidate.setString(3, step.id());
|
||||
insertCandidate.setString(4, candidate);
|
||||
insertCandidate.setInt(5, candidateOrder++);
|
||||
insertCandidate.executeUpdate();
|
||||
}
|
||||
}
|
||||
@@ -277,10 +360,11 @@ public final class JdbcProcessDefinitionRepository implements ProcessDefinitionR
|
||||
for (StepTransition transition : definition.transitions()) {
|
||||
try (PreparedStatement insertTransition = connection.prepareStatement(INSERT_TRANSITION)) {
|
||||
insertTransition.setString(1, definition.id());
|
||||
insertTransition.setString(2, transition.fromStepId());
|
||||
insertTransition.setString(3, transition.toStepId());
|
||||
insertTransition.setString(4, transition.conditionKey());
|
||||
insertTransition.setInt(5, transition.priority());
|
||||
insertTransition.setInt(2, definition.version());
|
||||
insertTransition.setString(3, transition.fromStepId());
|
||||
insertTransition.setString(4, transition.toStepId());
|
||||
insertTransition.setString(5, transition.conditionKey());
|
||||
insertTransition.setInt(6, transition.priority());
|
||||
insertTransition.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,14 +22,14 @@ import java.util.Optional;
|
||||
/** JDBC implementation of the instance storage port. */
|
||||
public final class JdbcProcessInstanceRepository implements ProcessInstanceRepository {
|
||||
private static final String INSERT_INSTANCE =
|
||||
"INSERT INTO ordo_process_instance (id, definition_id, initiator, status, context_json, started_at, finished_at)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||
"INSERT INTO ordo_process_instance (id, definition_id, definition_version, initiator, status, context_json,"
|
||||
+ " started_at, finished_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
private static final String UPDATE_INSTANCE =
|
||||
"UPDATE ordo_process_instance SET status = ?, finished_at = ? WHERE id = ?";
|
||||
private static final String COMPLETE_IF_RUNNING =
|
||||
"UPDATE ordo_process_instance SET status = ?, finished_at = ? WHERE id = ? AND status = 'RUNNING'";
|
||||
private static final String SELECT_INSTANCE =
|
||||
"SELECT id, definition_id, initiator, status, context_json, started_at, finished_at"
|
||||
"SELECT id, definition_id, definition_version, initiator, status, context_json, started_at, finished_at"
|
||||
+ " FROM ordo_process_instance WHERE id = ?";
|
||||
private static final String EXISTS_RUNNING =
|
||||
"SELECT 1 FROM ordo_process_instance WHERE definition_id = ? AND status = ? LIMIT 1";
|
||||
@@ -124,8 +124,8 @@ public final class JdbcProcessInstanceRepository implements ProcessInstanceRepos
|
||||
try {
|
||||
long total = PageSupport.count(connection,
|
||||
"SELECT COUNT(*) FROM ordo_process_instance" + where.sql(), where.params());
|
||||
String sql = "SELECT id, definition_id, initiator, status, context_json, started_at, finished_at"
|
||||
+ " FROM ordo_process_instance" + where.sql()
|
||||
String sql = "SELECT id, definition_id, definition_version, initiator, status, context_json, started_at,"
|
||||
+ " finished_at FROM ordo_process_instance" + where.sql()
|
||||
+ " ORDER BY started_at DESC, id DESC LIMIT ? OFFSET ?";
|
||||
List<ProcessInstance> content = new ArrayList<>();
|
||||
try (PreparedStatement select = connection.prepareStatement(sql)) {
|
||||
|
||||
+7
-5
@@ -16,11 +16,12 @@ public final class ProcessInstanceMapper {
|
||||
public static void bindInsert(PreparedStatement statement, ProcessInstance instance) throws SQLException {
|
||||
statement.setString(1, instance.id());
|
||||
statement.setString(2, instance.definitionId());
|
||||
statement.setString(3, instance.initiator());
|
||||
statement.setString(4, instance.status().name());
|
||||
statement.setString(5, ProcessContextCodec.encode(instance.context()));
|
||||
statement.setTimestamp(6, Timestamp.from(instance.startedAt()));
|
||||
statement.setTimestamp(7, instance.finishedAt() == null ? null : Timestamp.from(instance.finishedAt()));
|
||||
statement.setInt(3, instance.definitionVersion());
|
||||
statement.setString(4, instance.initiator());
|
||||
statement.setString(5, instance.status().name());
|
||||
statement.setString(6, ProcessContextCodec.encode(instance.context()));
|
||||
statement.setTimestamp(7, Timestamp.from(instance.startedAt()));
|
||||
statement.setTimestamp(8, instance.finishedAt() == null ? null : Timestamp.from(instance.finishedAt()));
|
||||
}
|
||||
|
||||
public static void bindUpdate(PreparedStatement statement, ProcessInstance instance) throws SQLException {
|
||||
@@ -35,6 +36,7 @@ public final class ProcessInstanceMapper {
|
||||
return new ProcessInstance(
|
||||
resultSet.getString("id"),
|
||||
resultSet.getString("definition_id"),
|
||||
resultSet.getInt("definition_version"),
|
||||
resultSet.getString("initiator"),
|
||||
ProcessStatus.valueOf(resultSet.getString("status")),
|
||||
startedAt.toInstant(),
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
CREATE TABLE ordo_process (
|
||||
id VARCHAR(64) PRIMARY KEY,
|
||||
current_version INTEGER NOT NULL,
|
||||
name VARCHAR(255) NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO ordo_process (id, current_version, name)
|
||||
SELECT id, 1, name FROM ordo_process_definition;
|
||||
|
||||
ALTER TABLE ordo_process_instance DROP CONSTRAINT fk_process_instance_definition;
|
||||
ALTER TABLE ordo_approval_step DROP CONSTRAINT fk_approval_step_definition;
|
||||
ALTER TABLE ordo_step_candidate DROP CONSTRAINT fk_step_candidate_step;
|
||||
ALTER TABLE ordo_step_transition DROP CONSTRAINT fk_transition_from;
|
||||
ALTER TABLE ordo_step_transition DROP CONSTRAINT fk_transition_to;
|
||||
|
||||
CREATE TABLE ordo_process_definition_v7 (
|
||||
id VARCHAR(64) NOT NULL,
|
||||
version INTEGER NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id, version)
|
||||
);
|
||||
|
||||
INSERT INTO ordo_process_definition_v7 (id, version, name)
|
||||
SELECT id, 1, name FROM ordo_process_definition;
|
||||
|
||||
DROP TABLE ordo_process_definition;
|
||||
ALTER TABLE ordo_process_definition_v7 RENAME TO ordo_process_definition;
|
||||
|
||||
CREATE TABLE ordo_approval_step_v7 (
|
||||
definition_id VARCHAR(64) NOT NULL,
|
||||
definition_version INTEGER NOT NULL,
|
||||
step_id VARCHAR(64) NOT NULL,
|
||||
step_name VARCHAR(255) NOT NULL,
|
||||
step_order INTEGER NOT NULL,
|
||||
policy VARCHAR(16) NOT NULL,
|
||||
kind VARCHAR(16) NOT NULL,
|
||||
action_key VARCHAR(255),
|
||||
due_after VARCHAR(32),
|
||||
due_then VARCHAR(16),
|
||||
due_to VARCHAR(255),
|
||||
due_action VARCHAR(255),
|
||||
PRIMARY KEY (definition_id, definition_version, step_id),
|
||||
CONSTRAINT fk_approval_step_definition FOREIGN KEY (definition_id, definition_version)
|
||||
REFERENCES ordo_process_definition (id, version)
|
||||
);
|
||||
|
||||
INSERT INTO ordo_approval_step_v7 (definition_id, definition_version, step_id, step_name, step_order, policy, kind,
|
||||
action_key, due_after, due_then, due_to, due_action)
|
||||
SELECT definition_id, 1, step_id, step_name, step_order, policy, kind, action_key, due_after, due_then, due_to,
|
||||
due_action
|
||||
FROM ordo_approval_step;
|
||||
|
||||
DROP TABLE ordo_approval_step;
|
||||
ALTER TABLE ordo_approval_step_v7 RENAME TO ordo_approval_step;
|
||||
|
||||
CREATE TABLE ordo_step_candidate_v7 (
|
||||
definition_id VARCHAR(64) NOT NULL,
|
||||
definition_version INTEGER NOT NULL,
|
||||
step_id VARCHAR(64) NOT NULL,
|
||||
candidate VARCHAR(255) NOT NULL,
|
||||
candidate_order INTEGER NOT NULL,
|
||||
PRIMARY KEY (definition_id, definition_version, step_id, candidate),
|
||||
CONSTRAINT fk_step_candidate_step FOREIGN KEY (definition_id, definition_version, step_id)
|
||||
REFERENCES ordo_approval_step (definition_id, definition_version, step_id)
|
||||
);
|
||||
|
||||
INSERT INTO ordo_step_candidate_v7 (definition_id, definition_version, step_id, candidate, candidate_order)
|
||||
SELECT definition_id, 1, step_id, candidate, candidate_order FROM ordo_step_candidate;
|
||||
|
||||
DROP TABLE ordo_step_candidate;
|
||||
ALTER TABLE ordo_step_candidate_v7 RENAME TO ordo_step_candidate;
|
||||
|
||||
CREATE TABLE ordo_step_transition_v7 (
|
||||
definition_id VARCHAR(64) NOT NULL,
|
||||
definition_version INTEGER NOT NULL,
|
||||
from_step_id VARCHAR(64) NOT NULL,
|
||||
to_step_id VARCHAR(64),
|
||||
condition_key VARCHAR(255),
|
||||
priority INTEGER NOT NULL,
|
||||
PRIMARY KEY (definition_id, definition_version, from_step_id, priority),
|
||||
CONSTRAINT fk_transition_from FOREIGN KEY (definition_id, definition_version, from_step_id)
|
||||
REFERENCES ordo_approval_step (definition_id, definition_version, step_id),
|
||||
CONSTRAINT fk_transition_to FOREIGN KEY (definition_id, definition_version, to_step_id)
|
||||
REFERENCES ordo_approval_step (definition_id, definition_version, step_id)
|
||||
);
|
||||
|
||||
INSERT INTO ordo_step_transition_v7 (definition_id, definition_version, from_step_id, to_step_id, condition_key, priority)
|
||||
SELECT definition_id, 1, from_step_id, to_step_id, condition_key, priority FROM ordo_step_transition;
|
||||
|
||||
DROP TABLE ordo_step_transition;
|
||||
ALTER TABLE ordo_step_transition_v7 RENAME TO ordo_step_transition;
|
||||
|
||||
ALTER TABLE ordo_process_instance ADD COLUMN definition_version INTEGER DEFAULT 1 NOT NULL;
|
||||
ALTER TABLE ordo_process_instance ADD CONSTRAINT fk_process_instance_definition
|
||||
FOREIGN KEY (definition_id, definition_version) REFERENCES ordo_process_definition (id, version);
|
||||
Reference in New Issue
Block a user