refactor: extract shared Texts, Jsons, and SqlBindings helpers
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -8,18 +8,12 @@ public record ActionExecution(String id, String instanceId, String stepId, Strin
|
||||
ActionExecutionStatus status, String errorMessage, Instant startedAt,
|
||||
Instant finishedAt) {
|
||||
public ActionExecution {
|
||||
requireText(id, "id");
|
||||
requireText(instanceId, "instanceId");
|
||||
requireText(stepId, "stepId");
|
||||
requireText(actionKey, "actionKey");
|
||||
Texts.requireText(id, "id");
|
||||
Texts.requireText(instanceId, "instanceId");
|
||||
Texts.requireText(stepId, "stepId");
|
||||
Texts.requireText(actionKey, "actionKey");
|
||||
Objects.requireNonNull(status, "status must not be null");
|
||||
Objects.requireNonNull(startedAt, "startedAt must not be null");
|
||||
errorMessage = errorMessage == null || errorMessage.isBlank() ? null : errorMessage.strip();
|
||||
}
|
||||
|
||||
private static void requireText(String value, String name) {
|
||||
if (value == null || value.isBlank()) {
|
||||
throw new IllegalArgumentException(name + " must not be blank");
|
||||
}
|
||||
errorMessage = Texts.blankToNull(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import java.util.Set;
|
||||
public record ApprovalStep(String id, String name, List<String> candidates, ApprovalPolicy policy, StepKind kind,
|
||||
String actionKey, StepDue due) {
|
||||
public ApprovalStep {
|
||||
requireText(id, "step id");
|
||||
requireText(name, "step name");
|
||||
Texts.requireText(id, "step id");
|
||||
Texts.requireText(name, "step name");
|
||||
kind = kind == null ? StepKind.APPROVAL : kind;
|
||||
Objects.requireNonNull(policy, "policy must not be null");
|
||||
Objects.requireNonNull(candidates, "candidates must not be null");
|
||||
@@ -22,7 +22,7 @@ public record ApprovalStep(String id, String name, List<String> candidates, Appr
|
||||
if (!candidates.isEmpty()) {
|
||||
throw new IllegalArgumentException("an action step must not have candidates");
|
||||
}
|
||||
requireText(actionKey, "action key");
|
||||
Texts.requireText(actionKey, "action key");
|
||||
actionKey = actionKey.strip();
|
||||
if (due != null) {
|
||||
throw new IllegalArgumentException("an action step must not have due");
|
||||
@@ -37,7 +37,7 @@ public record ApprovalStep(String id, String name, List<String> candidates, Appr
|
||||
}
|
||||
Set<String> distinct = new HashSet<>();
|
||||
for (String candidate : candidates) {
|
||||
requireText(candidate, "candidate");
|
||||
Texts.requireText(candidate, "candidate");
|
||||
if (!distinct.add(candidate)) {
|
||||
throw new IllegalArgumentException("duplicate candidate in step " + id + ": " + candidate);
|
||||
}
|
||||
@@ -62,10 +62,4 @@ public record ApprovalStep(String id, String name, List<String> candidates, Appr
|
||||
public static ApprovalStep action(String id, String name, String actionKey) {
|
||||
return new ApprovalStep(id, name, List.of(), ApprovalPolicy.ANY, StepKind.ACTION, actionKey);
|
||||
}
|
||||
|
||||
static void requireText(String value, String field) {
|
||||
if (value == null || value.isBlank()) {
|
||||
throw new IllegalArgumentException(field + " must not be blank");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.jetlumen.ordo.api;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/** Shared Jackson mappers. */
|
||||
public final class Jsons {
|
||||
public static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
public static final ObjectMapper NON_NULL = new ObjectMapper()
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
|
||||
private Jsons() {
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,9 @@ public interface OrdoCatalog {
|
||||
|
||||
record Param(String name, String type, String label) {
|
||||
public Param {
|
||||
ApprovalStep.requireText(name, "param name");
|
||||
ApprovalStep.requireText(type, "param type");
|
||||
ApprovalStep.requireText(label, "param label");
|
||||
Texts.requireText(name, "param name");
|
||||
Texts.requireText(type, "param type");
|
||||
Texts.requireText(label, "param label");
|
||||
type = type.strip().toLowerCase(Locale.ROOT);
|
||||
if (!PARAM_TYPES.contains(type)) {
|
||||
throw new IllegalArgumentException("param type must be number, string or boolean");
|
||||
|
||||
@@ -11,11 +11,11 @@ import java.util.Set;
|
||||
public record ProcessDefinition(String id, int version, String name, List<ApprovalStep> steps,
|
||||
List<StepTransition> transitions) {
|
||||
public ProcessDefinition {
|
||||
ApprovalStep.requireText(id, "definition id");
|
||||
Texts.requireText(id, "definition id");
|
||||
if (version < 0) {
|
||||
throw new IllegalArgumentException("definition version must not be negative");
|
||||
}
|
||||
ApprovalStep.requireText(name, "definition name");
|
||||
Texts.requireText(name, "definition name");
|
||||
steps = List.copyOf(steps);
|
||||
if (steps.isEmpty()) {
|
||||
throw new IllegalArgumentException("a definition must contain at least one step");
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.jetlumen.ordo.api;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
@@ -17,8 +16,7 @@ import java.util.Objects;
|
||||
|
||||
/** Parses a structural JSON process graph into a {@link ProcessDefinition}. */
|
||||
public final class ProcessDefinitionParser {
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper()
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
private static final ObjectMapper MAPPER = Jsons.NON_NULL;
|
||||
|
||||
private ProcessDefinitionParser() {
|
||||
}
|
||||
|
||||
@@ -7,23 +7,13 @@ import java.util.Objects;
|
||||
public record ProcessEvent(String id, String instanceId, String taskId, String stepId, ProcessEventType type,
|
||||
String actor, String detail, Instant occurredAt) {
|
||||
public ProcessEvent {
|
||||
requireText(id, "id");
|
||||
requireText(instanceId, "instanceId");
|
||||
Texts.requireText(id, "id");
|
||||
Texts.requireText(instanceId, "instanceId");
|
||||
Objects.requireNonNull(type, "type must not be null");
|
||||
Objects.requireNonNull(occurredAt, "occurredAt must not be null");
|
||||
taskId = blankToNull(taskId);
|
||||
stepId = blankToNull(stepId);
|
||||
actor = blankToNull(actor);
|
||||
detail = blankToNull(detail);
|
||||
}
|
||||
|
||||
private static String blankToNull(String value) {
|
||||
return value == null || value.isBlank() ? null : value.strip();
|
||||
}
|
||||
|
||||
private static void requireText(String value, String name) {
|
||||
if (value == null || value.isBlank()) {
|
||||
throw new IllegalArgumentException(name + " must not be blank");
|
||||
}
|
||||
taskId = Texts.blankToNull(taskId);
|
||||
stepId = Texts.blankToNull(stepId);
|
||||
actor = Texts.blankToNull(actor);
|
||||
detail = Texts.blankToNull(detail);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,14 +26,14 @@ public sealed interface RoutingPredicate permits RoutingPredicate.Compare, Routi
|
||||
record Compare(Op op, String var, Object value) implements RoutingPredicate {
|
||||
public Compare {
|
||||
Objects.requireNonNull(op, "op must not be null");
|
||||
ApprovalStep.requireText(var, "var");
|
||||
Texts.requireText(var, "var");
|
||||
value = requireLiteral(value, "value");
|
||||
}
|
||||
}
|
||||
|
||||
record In(String var, List<Object> values) implements RoutingPredicate {
|
||||
public In {
|
||||
ApprovalStep.requireText(var, "var");
|
||||
Texts.requireText(var, "var");
|
||||
Objects.requireNonNull(values, "values must not be null");
|
||||
if (values.isEmpty()) {
|
||||
throw new IllegalArgumentException("in values must not be empty");
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.util.Objects;
|
||||
* {@link RoutingCondition} reference with arguments.
|
||||
*/
|
||||
public sealed interface RoutingWhen permits RoutingWhen.Predicate, RoutingWhen.Ref {
|
||||
ObjectMapper MAPPER = new ObjectMapper();
|
||||
ObjectMapper MAPPER = Jsons.MAPPER;
|
||||
|
||||
record Predicate(RoutingPredicate tree) implements RoutingWhen {
|
||||
public Predicate {
|
||||
@@ -25,7 +25,7 @@ public sealed interface RoutingWhen permits RoutingWhen.Predicate, RoutingWhen.R
|
||||
|
||||
record Ref(String key, Map<String, Object> args) implements RoutingWhen {
|
||||
public Ref {
|
||||
ApprovalStep.requireText(key, "ref");
|
||||
Texts.requireText(key, "ref");
|
||||
args = Map.copyOf(args == null ? Map.of() : args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ public record StepDue(Duration after, DueThen then, String to, String action) {
|
||||
throw new IllegalArgumentException("due after must be positive");
|
||||
}
|
||||
Objects.requireNonNull(then, "due then must not be null");
|
||||
to = blankToNull(to);
|
||||
action = blankToNull(action);
|
||||
to = Texts.blankToNull(to);
|
||||
action = Texts.blankToNull(action);
|
||||
switch (then) {
|
||||
case REASSIGN -> {
|
||||
if (to == null) {
|
||||
@@ -50,7 +50,4 @@ public record StepDue(Duration after, DueThen then, String to, String action) {
|
||||
return new StepDue(after, DueThen.GOTO, stepId, null);
|
||||
}
|
||||
|
||||
private static String blankToNull(String value) {
|
||||
return value == null || value.isBlank() ? null : value.strip();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ package com.jetlumen.ordo.api;
|
||||
*/
|
||||
public record StepTransition(String fromStepId, String toStepId, RoutingWhen when, int priority) {
|
||||
public StepTransition {
|
||||
ApprovalStep.requireText(fromStepId, "fromStepId");
|
||||
Texts.requireText(fromStepId, "fromStepId");
|
||||
if (toStepId != null && toStepId.isBlank()) {
|
||||
throw new IllegalArgumentException("toStepId must not be blank when present");
|
||||
}
|
||||
|
||||
@@ -6,10 +6,8 @@ import java.util.Objects;
|
||||
/** Immutable audit record created when an approval task is completed. */
|
||||
public record TaskAction(String actor, String comment, Instant operatedAt) {
|
||||
public TaskAction {
|
||||
if (actor == null || actor.isBlank()) {
|
||||
throw new IllegalArgumentException("actor must not be blank");
|
||||
}
|
||||
comment = comment == null || comment.isBlank() ? null : comment.strip();
|
||||
Texts.requireText(actor, "actor");
|
||||
comment = Texts.blankToNull(comment);
|
||||
Objects.requireNonNull(operatedAt, "operatedAt must not be null");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.jetlumen.ordo.api;
|
||||
|
||||
/** Shared blank-string checks used by domain records and the engine. */
|
||||
public final class Texts {
|
||||
private Texts() {
|
||||
}
|
||||
|
||||
public static void requireText(String value, String name) {
|
||||
if (value == null || value.isBlank()) {
|
||||
throw new IllegalArgumentException(name + " must not be blank");
|
||||
}
|
||||
}
|
||||
|
||||
public static String blankToNull(String value) {
|
||||
return value == null || value.isBlank() ? null : value.strip();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user