Fix golden audit issues and tighten analysis/export pipeline.
Resolve property placeholders before call-chain linking, skip phantom routing states and lifecycle triggers from transition matching, dedupe exported states, and add golden JSON audit regression plus HTML lifecycle endpoint display. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -46,6 +46,7 @@ HTML-specific options:
|
||||
| `--no-diamonds` | Render choice pseudostates as normal nodes |
|
||||
| `--debug` | Same diagnostic mode as the core exporter |
|
||||
| `--include-generated-sources` | Same generated-source scanning as the core exporter |
|
||||
| `--no-inline-accessors` | Disable accessor indexing/inlining (enabled by default) |
|
||||
| `-p, --profiles` | Spring profiles for property resolution |
|
||||
|
||||
## Business Flows
|
||||
|
||||
168
state_machine_exporter/scripts/audit_golden_json.py
Normal file
168
state_machine_exporter/scripts/audit_golden_json.py
Normal file
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Deep audit of golden JSON analysis exports."""
|
||||
import json
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
GOLDEN = Path(__file__).resolve().parent.parent / "src/test/resources/golden"
|
||||
PH = re.compile(r"\$\{")
|
||||
|
||||
|
||||
def fn_form(identifier):
|
||||
if not identifier:
|
||||
return identifier
|
||||
last = identifier.rfind(".")
|
||||
if last <= 0:
|
||||
return identifier
|
||||
prev = identifier.rfind(".", 0, last)
|
||||
return identifier[prev + 1 :] if prev > 0 else identifier
|
||||
|
||||
|
||||
def sid(st):
|
||||
if isinstance(st, dict):
|
||||
return st.get("fullIdentifier") or st.get("rawName")
|
||||
return st
|
||||
|
||||
|
||||
def eid(ev):
|
||||
if isinstance(ev, dict):
|
||||
return ev.get("fullIdentifier") or ev.get("rawName")
|
||||
return ev
|
||||
|
||||
|
||||
def audit_file(path, data):
|
||||
name = data.get("name", path.stem)
|
||||
issues = []
|
||||
st_fqn = data.get("stateTypeFqn")
|
||||
ev_fqn = data.get("eventTypeFqn")
|
||||
transitions = data.get("transitions") or []
|
||||
starts = list(data.get("startStates") or [])
|
||||
ends = list(data.get("endStates") or [])
|
||||
states = data.get("states") or []
|
||||
meta = data.get("metadata") or {}
|
||||
|
||||
is_pkg_enum = st_fqn and "." in st_fqn and st_fqn not in ("String", "java.lang.String")
|
||||
is_string = st_fqn in (None, "String", "java.lang.String") or (
|
||||
not st_fqn and not ev_fqn
|
||||
)
|
||||
|
||||
if PH.search(json.dumps(data)):
|
||||
issues.append("BUG: unresolved ${placeholder} in export")
|
||||
|
||||
# Duplicate fullIdentifiers in states[]
|
||||
by_full = defaultdict(list)
|
||||
for s in states:
|
||||
by_full[sid(s)].append(s.get("rawName"))
|
||||
for fid, raws in by_full.items():
|
||||
if fid and len(raws) > 1 and len(set(raws)) > 1:
|
||||
issues.append(f"NOISE: duplicate state entry {fid!r} with rawNames {raws}")
|
||||
|
||||
# Collect transition state/event ids
|
||||
t_events, t_sources, t_targets = set(), set(), set()
|
||||
for t in transitions:
|
||||
if eid(t.get("event")):
|
||||
t_events.add(eid(t.get("event")))
|
||||
for s in t.get("sourceStates") or []:
|
||||
t_sources.add(sid(s))
|
||||
for s in t.get("targetStates") or []:
|
||||
t_targets.add(sid(s))
|
||||
|
||||
graph = t_sources | t_targets
|
||||
|
||||
# startStates canonical consistency
|
||||
for ss in starts:
|
||||
if is_string and ss and not ss.startswith("String."):
|
||||
if any(x.startswith("String.") for x in graph):
|
||||
issues.append(f"BUG: startStates {ss!r} bare but transitions use String.* form")
|
||||
if ss not in graph and fn_form(ss) not in {fn_form(x) for x in graph}:
|
||||
issues.append(f"WARN: startStates {ss!r} not in transition graph")
|
||||
|
||||
# endStates
|
||||
for es in ends:
|
||||
if es not in graph and fn_form(es) not in {fn_form(x) for x in graph}:
|
||||
issues.append(f"WARN: endStates {es!r} not in transition graph")
|
||||
|
||||
# Enum canonical fullIdentifiers
|
||||
if is_pkg_enum:
|
||||
for i, t in enumerate(transitions):
|
||||
for side in ("sourceStates", "targetStates"):
|
||||
for s in t.get(side) or []:
|
||||
fid = sid(s)
|
||||
if fid and not fid.startswith(st_fqn + ".") and not fid.startswith('"'):
|
||||
if "." in fid and not fid.startswith("<"):
|
||||
issues.append(f"BUG: transitions[{i}].{side} {fid!r} not under {st_fqn}")
|
||||
ev = eid(t.get("event"))
|
||||
if ev and ev_fqn and "." in ev_fqn and not ev.startswith("<") and not ev.startswith(ev_fqn + "."):
|
||||
if ev not in ("event",) and "." in ev and not ev.startswith(ev_fqn):
|
||||
issues.append(f"BUG: transitions[{i}].event {ev!r} not under {ev_fqn}")
|
||||
for i, tr in enumerate(meta.get("triggers") or []):
|
||||
for field in ("event", "sourceState"):
|
||||
val = tr.get(field)
|
||||
if not val or val in ("event", "payload", "customMessage", "eventProvider"):
|
||||
continue
|
||||
type_fqn = ev_fqn if field == "event" else st_fqn
|
||||
if type_fqn and "." in val and not val.startswith("<") and not val.startswith(type_fqn + "."):
|
||||
if field == "sourceState" and val not in graph and fn_form(val) not in {fn_form(x) for x in graph}:
|
||||
issues.append(f"BUG: triggers[{i}].sourceState {val!r} not a machine state")
|
||||
elif field == "event" and type_fqn and val.isupper() is False and "." not in val and not val.startswith("<"):
|
||||
if val not in ("event", "payload", "customMessage", "eventProvider", "true", "false"):
|
||||
issues.append(f"INFO: triggers[{i}].event dynamic {val!r}")
|
||||
|
||||
# matchedTransitions
|
||||
for i, chain in enumerate(meta.get("callChains") or []):
|
||||
for j, mt in enumerate(chain.get("matchedTransitions") or []):
|
||||
me, ms, mtgt = mt.get("event"), mt.get("sourceState"), mt.get("targetState")
|
||||
ok = False
|
||||
for t in transitions:
|
||||
if fn_form(eid(t.get("event"))) != fn_form(me):
|
||||
continue
|
||||
srcs = [sid(s) for s in t.get("sourceStates") or []]
|
||||
tgts = [sid(s) for s in t.get("targetStates") or []]
|
||||
if (not ms or any(fn_form(x) == fn_form(ms) for x in srcs)) and (
|
||||
not mtgt or any(fn_form(x) == fn_form(mtgt) for x in tgts)
|
||||
):
|
||||
ok = True
|
||||
break
|
||||
if not ok:
|
||||
issues.append(
|
||||
f"BUG: callChains[{i}].matchedTransitions[{j}] ({ms!r},{me!r},{mtgt!r}) no transition"
|
||||
)
|
||||
|
||||
# String machine: mixed NEW vs String.NEW
|
||||
if is_string or st_fqn == "String":
|
||||
all_ids = set(graph) | set(starts) | set(ends) | {sid(s) for s in states}
|
||||
for x in list(all_ids):
|
||||
if not x:
|
||||
continue
|
||||
bare = x.strip('"')
|
||||
if bare and "." not in bare and f"String.{bare}" in all_ids:
|
||||
issues.append(f"BUG: mixed bare {x!r} and String.{bare}")
|
||||
|
||||
# stateTypeFqn missing on enum machine
|
||||
if is_pkg_enum and not st_fqn:
|
||||
issues.append("BUG: enum machine missing stateTypeFqn")
|
||||
|
||||
return name, issues
|
||||
|
||||
|
||||
def main():
|
||||
total = 0
|
||||
for path in sorted(GOLDEN.rglob("*.json")):
|
||||
data = json.loads(path.read_text())
|
||||
machine, issues = audit_file(path, data)
|
||||
rel = path.relative_to(GOLDEN)
|
||||
bugs = [i for i in issues if i.startswith("BUG")]
|
||||
warns = [i for i in issues if i.startswith("WARN")]
|
||||
noise = [i for i in issues if i.startswith("NOISE")]
|
||||
info = [i for i in issues if i.startswith("INFO")]
|
||||
if bugs or warns:
|
||||
print(f"\n{'='*70}\n{rel}\n machine: {machine}")
|
||||
for i in bugs + warns + noise + info:
|
||||
print(f" [{i.split(':')[0]}] {i.split(': ', 1)[-1]}")
|
||||
total += len(bugs) + len(warns)
|
||||
print(f"\nTotal BUG+WARN: {total}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -4,6 +4,7 @@ import click.kamil.springstatemachineexporter.analysis.model.AnalysisResult;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.CodebaseMetadata;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.LifecycleTriggerMarkers;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.MatchedTransition;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||
import click.kamil.springstatemachineexporter.analysis.service.CodebaseIntelligenceProvider;
|
||||
@@ -42,6 +43,11 @@ public class TransitionLinkerEnricher implements AnalysisEnricher {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (LifecycleTriggerMarkers.isLifecycle(tp.getEvent())) {
|
||||
updatedChains.add(chain);
|
||||
continue;
|
||||
}
|
||||
|
||||
String triggerEvent = simplify(tp.getEvent());
|
||||
String triggerSource = tp.getSourceState() != null ? simplify(tp.getSourceState()) : null;
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@ public class StrictFqnMatchingEngine implements EventMatchingEngine {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (click.kamil.springstatemachineexporter.analysis.model.LifecycleTriggerMarkers
|
||||
.isLifecycle(triggerPoint.getEvent())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String smEventRaw = stateMachineEvent.fullIdentifier() != null ? stateMachineEvent.fullIdentifier() : stateMachineEvent.rawName();
|
||||
String rawTriggerEvent = triggerPoint.getEvent();
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.model;
|
||||
|
||||
/**
|
||||
* Synthetic trigger markers for persistence/lifecycle operations that do not fire
|
||||
* {@code sendEvent} transitions (e.g. {@code StateMachinePersister.restore(...)}).
|
||||
*/
|
||||
public final class LifecycleTriggerMarkers {
|
||||
|
||||
public static final String PREFIX = "[LIFECYCLE:";
|
||||
|
||||
private LifecycleTriggerMarkers() {
|
||||
}
|
||||
|
||||
public static boolean isLifecycle(String event) {
|
||||
return event != null && event.startsWith(PREFIX) && event.endsWith("]");
|
||||
}
|
||||
|
||||
public static String lifecycleType(String event) {
|
||||
if (!isLifecycle(event)) {
|
||||
return null;
|
||||
}
|
||||
return event.substring(PREFIX.length(), event.length() - 1);
|
||||
}
|
||||
|
||||
public static String displayLabel(String event) {
|
||||
String type = lifecycleType(event);
|
||||
if (type == null) {
|
||||
return event;
|
||||
}
|
||||
return switch (type.toUpperCase()) {
|
||||
case "RESTORE" -> "Restore persisted state";
|
||||
case "RESET" -> "Reset state machine";
|
||||
default -> "Lifecycle: " + type.toLowerCase();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,77 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.service;
|
||||
|
||||
import click.kamil.springstatemachineexporter.analysis.enricher.AnalysisEnricher;
|
||||
import click.kamil.springstatemachineexporter.analysis.enricher.CallChainEnricher;
|
||||
import click.kamil.springstatemachineexporter.analysis.enricher.EntryPointEnricher;
|
||||
import click.kamil.springstatemachineexporter.analysis.enricher.PropertyEnricher;
|
||||
import click.kamil.springstatemachineexporter.analysis.enricher.TriggerCanonicalizationEnricher;
|
||||
import click.kamil.springstatemachineexporter.analysis.enricher.TriggerEnricher;
|
||||
import click.kamil.springstatemachineexporter.analysis.enricher.TransitionLinkerEnricher;
|
||||
import click.kamil.springstatemachineexporter.analysis.enricher.path.ForwardPathEstimationEnricher;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.AnalysisResult;
|
||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class EnrichmentService {
|
||||
private final List<AnalysisEnricher> enrichers;
|
||||
private final List<AnalysisEnricher> prePropertyEnrichers;
|
||||
private final List<AnalysisEnricher> postPropertyEnrichers;
|
||||
|
||||
/** Single-phase pipeline (all enrichers run together; used by focused tests). */
|
||||
public EnrichmentService(List<AnalysisEnricher> enrichers) {
|
||||
this(enrichers, List.of());
|
||||
}
|
||||
|
||||
public EnrichmentService(
|
||||
List<AnalysisEnricher> prePropertyEnrichers,
|
||||
List<AnalysisEnricher> postPropertyEnrichers) {
|
||||
this.prePropertyEnrichers = prePropertyEnrichers;
|
||||
this.postPropertyEnrichers = postPropertyEnrichers;
|
||||
}
|
||||
|
||||
public static EnrichmentService createDefault() {
|
||||
return new EnrichmentService(
|
||||
List.of(
|
||||
new TriggerEnricher(),
|
||||
new EntryPointEnricher(),
|
||||
new PropertyEnricher()),
|
||||
List.of(
|
||||
new CallChainEnricher(),
|
||||
new ForwardPathEstimationEnricher(),
|
||||
new TriggerCanonicalizationEnricher(),
|
||||
new TransitionLinkerEnricher()));
|
||||
}
|
||||
|
||||
public void enrich(AnalysisResult result, CodebaseContext context, CodebaseIntelligenceProvider intelligence) {
|
||||
enrichPreProperty(result, context, intelligence);
|
||||
enrichPostProperty(result, context, intelligence);
|
||||
}
|
||||
|
||||
public void enrichPreProperty(
|
||||
AnalysisResult result, CodebaseContext context, CodebaseIntelligenceProvider intelligence) {
|
||||
runEnrichers(prePropertyEnrichers, result, context, intelligence);
|
||||
}
|
||||
|
||||
public void enrichPostProperty(
|
||||
AnalysisResult result, CodebaseContext context, CodebaseIntelligenceProvider intelligence) {
|
||||
runEnrichers(postPropertyEnrichers, result, context, intelligence);
|
||||
}
|
||||
|
||||
/** Re-runs transition linking after property resolution without rebuilding call chains. */
|
||||
public void relinkAfterPropertyResolution(
|
||||
AnalysisResult result, CodebaseContext context, CodebaseIntelligenceProvider intelligence) {
|
||||
runEnrichers(
|
||||
List.of(new TriggerCanonicalizationEnricher(), new TransitionLinkerEnricher()),
|
||||
result,
|
||||
context,
|
||||
intelligence);
|
||||
}
|
||||
|
||||
private static void runEnrichers(
|
||||
List<AnalysisEnricher> enrichers,
|
||||
AnalysisResult result,
|
||||
CodebaseContext context,
|
||||
CodebaseIntelligenceProvider intelligence) {
|
||||
for (AnalysisEnricher enricher : enrichers) {
|
||||
enricher.enrich(result, context, intelligence);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.eclipse.jdt.core.dom.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@@ -19,6 +20,17 @@ public class GenericEventDetector {
|
||||
private final ConstantResolver constantResolver;
|
||||
private final List<LibraryHint> hints;
|
||||
|
||||
/** Parameter names that route to a machine, not SM state enum constants. */
|
||||
private static final Set<String> ROUTING_PARAMETER_NAMES = Set.of(
|
||||
"machineType",
|
||||
"machineId",
|
||||
"machineName",
|
||||
"domain",
|
||||
"type",
|
||||
"action",
|
||||
"eventString",
|
||||
"version");
|
||||
|
||||
public List<TriggerPoint> detect(CompilationUnit cu) {
|
||||
List<TriggerPoint> triggers = new ArrayList<>();
|
||||
String fileName = "unknown";
|
||||
@@ -296,8 +308,10 @@ public class GenericEventDetector {
|
||||
String state = extractStateFromSiblings(current, block.statements());
|
||||
if (state != null) return state;
|
||||
} else if (parent instanceof SwitchStatement switchStmt) {
|
||||
String state = extractStateFromSiblings(current, switchStmt.statements());
|
||||
if (state != null) return state;
|
||||
if (!isRoutingParameter(switchStmt.getExpression())) {
|
||||
String state = extractStateFromSiblings(current, switchStmt.statements());
|
||||
if (state != null) return state;
|
||||
}
|
||||
}
|
||||
|
||||
current = parent;
|
||||
@@ -354,6 +368,10 @@ public class GenericEventDetector {
|
||||
if (left instanceof NullLiteral || right instanceof NullLiteral) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isRoutingParameter(left) || isRoutingParameter(right)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Usually one is a method call like getState() or a variable like `state`
|
||||
// and the other is the constant enum like `OrderState.PENDING` or `"PENDING"`
|
||||
@@ -374,6 +392,10 @@ public class GenericEventDetector {
|
||||
if (("equals".equals(methodName) || "equalsIgnoreCase".equals(methodName)) && !mi.arguments().isEmpty()) {
|
||||
Expression receiver = mi.getExpression();
|
||||
Expression arg = (Expression) mi.arguments().get(0);
|
||||
|
||||
if (isRoutingParameter(receiver) || isRoutingParameter(arg)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If receiver is null (e.g., implicit this), fall back to arg
|
||||
if (receiver == null) {
|
||||
@@ -395,6 +417,13 @@ public class GenericEventDetector {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isRoutingParameter(Expression expr) {
|
||||
if (expr instanceof SimpleName sn) {
|
||||
return ROUTING_PARAMETER_NAMES.contains(sn.getIdentifier());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getSimpleNameString(Expression expr) {
|
||||
if (expr instanceof QualifiedName qn) {
|
||||
return qn.getName().getIdentifier();
|
||||
|
||||
@@ -45,20 +45,44 @@ public class TransitionStateUtils {
|
||||
}
|
||||
|
||||
public static Set<State> findAllStates(Collection<Transition> transitions, Set<String> initialStates, Set<String> endStates) {
|
||||
Set<State> allStates = new java.util.HashSet<>();
|
||||
java.util.Map<String, State> byFullIdentifier = new java.util.LinkedHashMap<>();
|
||||
if (transitions != null) {
|
||||
transitions.forEach(t -> {
|
||||
if (t.getSourceStates() != null) allStates.addAll(t.getSourceStates());
|
||||
if (t.getTargetStates() != null) allStates.addAll(t.getTargetStates());
|
||||
mergeState(byFullIdentifier, t.getSourceStates());
|
||||
mergeState(byFullIdentifier, t.getTargetStates());
|
||||
});
|
||||
}
|
||||
if (initialStates != null) {
|
||||
initialStates.forEach(s -> allStates.add(State.of(s)));
|
||||
initialStates.forEach(label -> mergeState(byFullIdentifier, State.of(label)));
|
||||
}
|
||||
if (endStates != null) {
|
||||
endStates.forEach(s -> allStates.add(State.of(s)));
|
||||
endStates.forEach(label -> mergeState(byFullIdentifier, State.of(label)));
|
||||
}
|
||||
return allStates;
|
||||
return new java.util.LinkedHashSet<>(byFullIdentifier.values());
|
||||
}
|
||||
|
||||
private static void mergeState(java.util.Map<String, State> map, State state) {
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
String key = stateKey(state);
|
||||
map.putIfAbsent(key, state);
|
||||
}
|
||||
|
||||
private static void mergeState(java.util.Map<String, State> map, java.util.Collection<State> states) {
|
||||
if (states == null) {
|
||||
return;
|
||||
}
|
||||
for (State state : states) {
|
||||
mergeState(map, state);
|
||||
}
|
||||
}
|
||||
|
||||
private static String stateKey(State state) {
|
||||
if (state.fullIdentifier() != null && !state.fullIdentifier().isBlank()) {
|
||||
return state.fullIdentifier();
|
||||
}
|
||||
return state.rawName();
|
||||
}
|
||||
|
||||
private static Stream<String> normalizeStates(Collection<State> states) {
|
||||
|
||||
@@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@Slf4j
|
||||
public class CodebaseContext {
|
||||
private final Map<String, CompilationUnit> classes = new HashMap<>();
|
||||
private final Map<String, org.eclipse.jdt.core.dom.RecordDeclaration> recordDeclarations = new HashMap<>();
|
||||
private final Map<String, Path> classPaths = new HashMap<>();
|
||||
private final Map<String, String> simpleNameToFqn = new HashMap<>();
|
||||
private final Set<String> ambiguousSimpleNames = new HashSet<>();
|
||||
@@ -317,6 +318,7 @@ public class CodebaseContext {
|
||||
// Treat as a class since it is a type
|
||||
classes.put(fqn, (CompilationUnit) rd.getRoot());
|
||||
classPaths.put(fqn, javaFile);
|
||||
recordDeclarations.put(fqn, rd);
|
||||
|
||||
if (simpleNameToFqn.containsKey(simpleName)) {
|
||||
String existingFqn = simpleNameToFqn.get(simpleName);
|
||||
@@ -645,6 +647,21 @@ public class CodebaseContext {
|
||||
return null;
|
||||
}
|
||||
|
||||
public org.eclipse.jdt.core.dom.RecordDeclaration getRecordDeclaration(String fqn) {
|
||||
if (fqn == null || fqn.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
org.eclipse.jdt.core.dom.RecordDeclaration indexed = recordDeclarations.get(fqn);
|
||||
if (indexed != null) {
|
||||
return indexed;
|
||||
}
|
||||
AbstractTypeDeclaration abstractType = getAbstractTypeDeclaration(fqn);
|
||||
if (abstractType instanceof org.eclipse.jdt.core.dom.RecordDeclaration recordDeclaration) {
|
||||
return recordDeclaration;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public AbstractTypeDeclaration getAbstractTypeDeclaration(String name) {
|
||||
// Try exact FQN match first
|
||||
CompilationUnit cu = classes.get(name);
|
||||
|
||||
@@ -745,11 +745,7 @@ public class JdtDataFlowModel implements DataFlowModel {
|
||||
}
|
||||
|
||||
private org.eclipse.jdt.core.dom.RecordDeclaration findRecordDeclaration(String fqn) {
|
||||
org.eclipse.jdt.core.dom.AbstractTypeDeclaration abstractType = context.getAbstractTypeDeclaration(fqn);
|
||||
if (abstractType instanceof org.eclipse.jdt.core.dom.RecordDeclaration recordDeclaration) {
|
||||
return recordDeclaration;
|
||||
}
|
||||
return null;
|
||||
return context.getRecordDeclaration(fqn);
|
||||
}
|
||||
|
||||
private void inlineAccessorSetter(
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package click.kamil.springstatemachineexporter.service;
|
||||
|
||||
import click.kamil.springstatemachineexporter.analysis.enricher.CallChainEnricher;
|
||||
import click.kamil.springstatemachineexporter.analysis.resolver.MachineEnumCanonicalizer;
|
||||
import click.kamil.springstatemachineexporter.analysis.resolver.StateMachineTypeResolver;
|
||||
import click.kamil.springstatemachineexporter.analysis.enricher.EntryPointEnricher;
|
||||
import click.kamil.springstatemachineexporter.analysis.enricher.PropertyEnricher;
|
||||
import click.kamil.springstatemachineexporter.analysis.enricher.TriggerEnricher;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.AnalysisResult;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.BusinessFlow;
|
||||
import click.kamil.springstatemachineexporter.analysis.service.AnalysisResultFinalizer;
|
||||
@@ -44,15 +40,7 @@ public class ExportService {
|
||||
}
|
||||
|
||||
public ExportService(List<StateMachineExporter> exporters) {
|
||||
this(exporters, new EnrichmentService(List.of(
|
||||
new TriggerEnricher(),
|
||||
new EntryPointEnricher(),
|
||||
new PropertyEnricher(),
|
||||
new CallChainEnricher(),
|
||||
new click.kamil.springstatemachineexporter.analysis.enricher.path.ForwardPathEstimationEnricher(),
|
||||
new click.kamil.springstatemachineexporter.analysis.enricher.TriggerCanonicalizationEnricher(),
|
||||
new click.kamil.springstatemachineexporter.analysis.enricher.TransitionLinkerEnricher()
|
||||
)));
|
||||
this(exporters, EnrichmentService.createDefault());
|
||||
}
|
||||
|
||||
private static final List<String> TARGET_ANNOTATIONS = List.of("EnableStateMachineFactory", "EnableStateMachine");
|
||||
@@ -195,6 +183,8 @@ public class ExportService {
|
||||
log.info("JSON re-export: no source project found; using embedded machine types if present");
|
||||
}
|
||||
|
||||
enrichmentService.relinkAfterPropertyResolution(result, context, null);
|
||||
|
||||
StateMachineTypeResolver.MachineTypes machineTypes =
|
||||
click.kamil.springstatemachineexporter.analysis.service.JsonExportContextFactory.resolveMachineTypes(
|
||||
result.getName(),
|
||||
@@ -295,9 +285,9 @@ public class ExportService {
|
||||
.flows(flows)
|
||||
.build();
|
||||
|
||||
enrichmentService.enrich(result, context, intelligence);
|
||||
|
||||
enrichmentService.enrichPreProperty(result, context, intelligence);
|
||||
resolveProperties(result, activeProfiles);
|
||||
enrichmentService.enrichPostProperty(result, context, intelligence);
|
||||
AnalysisResultFinalizer.finalizeResult(result, context);
|
||||
|
||||
generateOutputs(outputDir, result, selectedFormats, eventFormat, stateFormat);
|
||||
@@ -337,9 +327,9 @@ public class ExportService {
|
||||
.flows(flows)
|
||||
.build();
|
||||
|
||||
enrichmentService.enrich(result, context, intelligence);
|
||||
|
||||
enrichmentService.enrichPreProperty(result, context, intelligence);
|
||||
resolveProperties(result, activeProfiles);
|
||||
enrichmentService.enrichPostProperty(result, context, intelligence);
|
||||
AnalysisResultFinalizer.finalizeResult(result, context);
|
||||
|
||||
generateOutputs(outputDir, result, selectedFormats, eventFormat, stateFormat);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package click.kamil.springstatemachineexporter;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Runs the golden JSON consistency audit script as a regression gate.
|
||||
*/
|
||||
class GoldenJsonAuditTest {
|
||||
|
||||
private static Path findProjectRoot() {
|
||||
Path current = Path.of(".").toAbsolutePath();
|
||||
while (current != null && !current.resolve("settings.gradle").toFile().exists()) {
|
||||
current = current.getParent();
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
@Test
|
||||
void goldenJsonExportsShouldPassConsistencyAudit() throws Exception {
|
||||
Path moduleRoot = findProjectRoot().resolve("state_machine_exporter");
|
||||
Path script = moduleRoot.resolve("scripts/audit_golden_json.py");
|
||||
assertThat(script).exists();
|
||||
|
||||
Process process = new ProcessBuilder("python3", script.toString())
|
||||
.directory(moduleRoot.toFile())
|
||||
.redirectErrorStream(true)
|
||||
.start();
|
||||
|
||||
String output;
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
||||
output = reader.lines().collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
assertThat(process.waitFor(60, TimeUnit.SECONDS))
|
||||
.as("audit script timed out\n%s", output)
|
||||
.isTrue();
|
||||
assertThat(process.exitValue())
|
||||
.as("audit script failed\n%s", output)
|
||||
.isZero();
|
||||
assertThat(output).contains("Total BUG+WARN: 0");
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package click.kamil.springstatemachineexporter;
|
||||
|
||||
import click.kamil.springstatemachineexporter.analysis.service.EnrichmentService;
|
||||
import click.kamil.springstatemachineexporter.exporter.Dot;
|
||||
import click.kamil.springstatemachineexporter.exporter.JsonExporter;
|
||||
import click.kamil.springstatemachineexporter.exporter.PlantUml;
|
||||
@@ -17,7 +16,6 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class GoldenUpdater {
|
||||
@@ -33,7 +31,16 @@ public class GoldenUpdater {
|
||||
System.out.println("Updating golden files for: " + scenario.name());
|
||||
Path tempDir = Files.createTempDirectory("golden-update-");
|
||||
try {
|
||||
exportService.runExporter(scenario.inputPath(), tempDir, List.of("puml", "dot", "scxml", "json"), true);
|
||||
exportService.runExporter(
|
||||
scenario.inputPath(),
|
||||
tempDir,
|
||||
List.of("puml", "dot", "scxml", "json"),
|
||||
true,
|
||||
scenario.activeProfiles(),
|
||||
null,
|
||||
null,
|
||||
click.kamil.springstatemachineexporter.exporter.EnumFormat.fn,
|
||||
click.kamil.springstatemachineexporter.exporter.EnumFormat.fn);
|
||||
|
||||
List<Path> generatedDirs;
|
||||
try (var stream = Files.list(tempDir)) {
|
||||
@@ -159,12 +166,73 @@ public class GoldenUpdater {
|
||||
Path.of("src/test/resources/golden/G2StateMachineConfiguration"),
|
||||
"G2StateMachineConfiguration"
|
||||
),
|
||||
new TestScenario(
|
||||
"Extended Analysis Sample",
|
||||
Path.of("state_machines/extended_analysis_sample"),
|
||||
Path.of("src/test/resources/golden/ExtendedStateMachineConfig"),
|
||||
"ExtendedStateMachineConfig"
|
||||
),
|
||||
new TestScenario(
|
||||
"Extended Analysis Sample (PROD)",
|
||||
Path.of("state_machines/extended_analysis_sample"),
|
||||
Path.of("src/test/resources/golden/ExtendedStateMachineConfig_PROD"),
|
||||
"ExtendedStateMachineConfig",
|
||||
List.of("prod")
|
||||
),
|
||||
new TestScenario(
|
||||
"Inheritance Sample",
|
||||
Path.of("state_machines/inheritance_sample"),
|
||||
Path.of("src/test/resources/golden/InheritanceStateMachineConfig"),
|
||||
"InheritanceStateMachineConfig"
|
||||
),
|
||||
new TestScenario(
|
||||
"Enterprise Order System",
|
||||
Path.of("state_machines/enterprise_order_system"),
|
||||
Path.of("src/test/resources/golden/EnterpriseStateMachineConfig"),
|
||||
"EnterpriseStateMachineConfig"
|
||||
),
|
||||
new TestScenario(
|
||||
"Enterprise Order State Machine",
|
||||
Path.of("state_machines/state_machine_enterprise"),
|
||||
Path.of("src/test/resources/golden/OrderStateMachineConfiguration"),
|
||||
"OrderStateMachineConfiguration"
|
||||
),
|
||||
new TestScenario(
|
||||
"Multi-Module Sample",
|
||||
Path.of("state_machines/multi_module_sample/core-module"),
|
||||
Path.of("src/test/resources/golden/OrderStateMachineConfig"),
|
||||
"OrderStateMachineConfig"
|
||||
),
|
||||
new TestScenario(
|
||||
"Maven Multi-Module Sample",
|
||||
Path.of("state_machines/maven_multi_module/core-module"),
|
||||
Path.of("src/test/resources/golden/MavenOrderStateMachine"),
|
||||
"MavenOrderStateMachine"
|
||||
),
|
||||
new TestScenario(
|
||||
"Complex Multi-Module Sample",
|
||||
Path.of("state_machines/complex_multi_module_sm"),
|
||||
Path.of("src/test/resources/golden/StateMachineConfig"),
|
||||
"StateMachineConfig"
|
||||
),
|
||||
new TestScenario(
|
||||
"Polymorphic Events Sample",
|
||||
Path.of("state_machines/polymorphic_events_sample"),
|
||||
Path.of("src/test/resources/golden/PolymorphicStateMachineConfiguration"),
|
||||
"PolymorphicStateMachineConfiguration"
|
||||
),
|
||||
new TestScenario(
|
||||
"Layered Dispatcher (Order)",
|
||||
Path.of("state_machines/layered_dispatcher_sample"),
|
||||
Path.of("src/test/resources/golden/StandardOrderStateMachineConfiguration"),
|
||||
"StandardOrderStateMachineConfiguration"
|
||||
),
|
||||
new TestScenario(
|
||||
"Layered Dispatcher (Document)",
|
||||
Path.of("state_machines/layered_dispatcher_sample"),
|
||||
Path.of("src/test/resources/golden/StandardDocumentStateMachineConfiguration"),
|
||||
"StandardDocumentStateMachineConfiguration"
|
||||
),
|
||||
new TestScenario(
|
||||
"Enterprise Document State Machine",
|
||||
Path.of("state_machines/state_machine_enterprise"),
|
||||
|
||||
@@ -173,6 +173,18 @@ public class RegressionTest {
|
||||
root.resolve("state_machines/layered_dispatcher_sample"),
|
||||
Path.of("src/test/resources/golden/StandardDocumentStateMachineConfiguration"),
|
||||
"StandardDocumentStateMachineConfiguration"
|
||||
),
|
||||
new TestScenario(
|
||||
"Enterprise Document State Machine",
|
||||
root.resolve("state_machines/state_machine_enterprise"),
|
||||
Path.of("src/test/resources/golden/DocumentStateMachineConfiguration"),
|
||||
"DocumentStateMachineConfiguration"
|
||||
),
|
||||
new TestScenario(
|
||||
"Enterprise User State Machine",
|
||||
root.resolve("state_machines/state_machine_enterprise"),
|
||||
Path.of("src/test/resources/golden/UserStateMachineConfiguration"),
|
||||
"UserStateMachineConfiguration"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.model;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class LifecycleTriggerMarkersTest {
|
||||
|
||||
@Test
|
||||
void shouldRecognizeLifecycleMarkers() {
|
||||
assertThat(LifecycleTriggerMarkers.isLifecycle("[LIFECYCLE:RESTORE]")).isTrue();
|
||||
assertThat(LifecycleTriggerMarkers.isLifecycle("[LIFECYCLE:RESET]")).isTrue();
|
||||
assertThat(LifecycleTriggerMarkers.isLifecycle("OrderEvent.PAY")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFormatDisplayLabels() {
|
||||
assertThat(LifecycleTriggerMarkers.displayLabel("[LIFECYCLE:RESTORE]"))
|
||||
.isEqualTo("Restore persisted state");
|
||||
assertThat(LifecycleTriggerMarkers.displayLabel("[LIFECYCLE:RESET]"))
|
||||
.isEqualTo("Reset state machine");
|
||||
}
|
||||
}
|
||||
@@ -129,4 +129,33 @@ class GenericEventDetectorControlFlowTest {
|
||||
TriggerPoint shipTrigger = triggers.stream().filter(t -> t.getEvent().equals("OrderEvent.SHIP")).findFirst().get();
|
||||
assertThat(shipTrigger.getSourceState()).isEqualTo("PAID");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotTreatMachineTypeDiscriminatorAsSourceState(@TempDir Path tempDir) throws IOException {
|
||||
String source = """
|
||||
package com.example;
|
||||
public class Dispatcher {
|
||||
private StateMachine sm;
|
||||
public void dispatch(String machineType, String eventString) {
|
||||
if ("ORDER".equalsIgnoreCase(machineType)) {
|
||||
sm.sendEvent("event");
|
||||
}
|
||||
}
|
||||
}
|
||||
class StateMachine {
|
||||
public void sendEvent(String e) {}
|
||||
}
|
||||
""";
|
||||
Files.writeString(tempDir.resolve("Dispatcher.java"), source);
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.scan(tempDir);
|
||||
|
||||
GenericEventDetector detector = new GenericEventDetector(context, new ConstantResolver(), Collections.emptyList());
|
||||
List<TriggerPoint> triggers = detector.detect(
|
||||
(org.eclipse.jdt.core.dom.CompilationUnit) context.getTypeDeclaration("com.example.Dispatcher").getRoot());
|
||||
|
||||
assertThat(triggers).hasSize(1);
|
||||
assertThat(triggers.get(0).getSourceState()).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.service;
|
||||
|
||||
import click.kamil.springstatemachineexporter.analysis.model.LifecycleTriggerMarkers;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class LifecycleDetectorTest {
|
||||
|
||||
@Test
|
||||
void shouldDetectPersisterRestoreCalls(@TempDir Path tempDir) throws Exception {
|
||||
String source = """
|
||||
package com.example;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.persist.StateMachinePersister;
|
||||
public class OrderService {
|
||||
private final StateMachine<String, String> stateMachine;
|
||||
private final StateMachinePersister<String, String, String> persister;
|
||||
OrderService(StateMachine<String, String> sm, StateMachinePersister<String, String, String> p) {
|
||||
this.stateMachine = sm;
|
||||
this.persister = p;
|
||||
}
|
||||
public void resume(String id) throws Exception {
|
||||
persister.restore(stateMachine, id);
|
||||
}
|
||||
}
|
||||
""";
|
||||
Files.writeString(tempDir.resolve("OrderService.java"), source);
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.setResolveBindings(true);
|
||||
context.scan(tempDir);
|
||||
|
||||
LifecycleDetector detector = new LifecycleDetector(context);
|
||||
org.eclipse.jdt.core.dom.TypeDeclaration td = context.getTypeDeclaration("com.example.OrderService");
|
||||
org.eclipse.jdt.core.dom.CompilationUnit cu =
|
||||
(org.eclipse.jdt.core.dom.CompilationUnit) td.getRoot();
|
||||
|
||||
List<TriggerPoint> triggers = detector.detect(cu);
|
||||
assertThat(triggers).hasSize(1);
|
||||
assertThat(triggers.get(0).getEvent()).isEqualTo("[LIFECYCLE:RESTORE]");
|
||||
assertThat(triggers.get(0).getMethodName()).isEqualTo("resume");
|
||||
assertThat(LifecycleTriggerMarkers.lifecycleType(triggers.get(0).getEvent())).isEqualTo("RESTORE");
|
||||
}
|
||||
}
|
||||
@@ -217,6 +217,24 @@ class TransitionStateUtilsTest {
|
||||
assertThat(endStates).containsExactlyInAnyOrder("DELIVERED", "REFUNDED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDeduplicateStatesByFullIdentifier() {
|
||||
Transition transition = new Transition();
|
||||
transition.getSourceStates().add(State.of("\"NEW\"", "String.NEW"));
|
||||
transition.getTargetStates().add(State.of("\"PAID\"", "String.PAID"));
|
||||
|
||||
Set<State> allStates = TransitionStateUtils.findAllStates(
|
||||
List.of(transition),
|
||||
Set.of("String.NEW"),
|
||||
Set.of("String.PAID"));
|
||||
|
||||
assertThat(allStates).hasSize(2);
|
||||
assertThat(allStates).extracting(State::fullIdentifier)
|
||||
.containsExactlyInAnyOrder("String.NEW", "String.PAID");
|
||||
assertThat(allStates).extracting(State::rawName)
|
||||
.containsExactlyInAnyOrder("\"NEW\"", "\"PAID\"");
|
||||
}
|
||||
|
||||
private Transition createTransition(String source, String target) {
|
||||
Transition transition = new Transition();
|
||||
if (source != null) {
|
||||
|
||||
@@ -30,9 +30,6 @@
|
||||
}, {
|
||||
"rawName" : "States.STATE20",
|
||||
"fullIdentifier" : "ComplexStateMachineConfig.States.STATE20"
|
||||
}, {
|
||||
"rawName" : "ComplexStateMachineConfig.States.STATE1",
|
||||
"fullIdentifier" : "ComplexStateMachineConfig.States.STATE1"
|
||||
}, {
|
||||
"rawName" : "States.STATE1",
|
||||
"fullIdentifier" : "ComplexStateMachineConfig.States.STATE1"
|
||||
|
||||
@@ -1,33 +1,76 @@
|
||||
{
|
||||
"name" : "click.kamil.enterprise.machines.document.DocumentStateMachineConfiguration",
|
||||
"states" : [ {
|
||||
"rawName" : "DocumentState.IN_REVIEW",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.document.DocumentState.IN_REVIEW"
|
||||
}, {
|
||||
"rawName" : "DocumentState.DRAFT",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.document.DocumentState.DRAFT"
|
||||
}, {
|
||||
"rawName" : "DocumentState.APPROVED",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.document.DocumentState.APPROVED"
|
||||
} ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "DocumentState.DRAFT",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.document.DocumentState.DRAFT"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "DocumentState.IN_REVIEW",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.document.DocumentState.IN_REVIEW"
|
||||
} ],
|
||||
"event" : {
|
||||
"rawName" : "DocumentEvent.SUBMIT",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.document.DocumentEvent.SUBMIT"
|
||||
},
|
||||
"guards" : [ ],
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
}, {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "DocumentState.IN_REVIEW",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.document.DocumentState.IN_REVIEW"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "DocumentState.APPROVED",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.document.DocumentState.APPROVED"
|
||||
} ],
|
||||
"event" : {
|
||||
"rawName" : "DocumentEvent.APPROVE",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.document.DocumentEvent.APPROVE"
|
||||
},
|
||||
"guards" : [ ],
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
}, {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "DocumentState.IN_REVIEW",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.document.DocumentState.IN_REVIEW"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "DocumentState.DRAFT",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.document.DocumentState.DRAFT"
|
||||
} ],
|
||||
"event" : {
|
||||
"rawName" : "DocumentEvent.REJECT",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.document.DocumentEvent.REJECT"
|
||||
},
|
||||
"guards" : [ ],
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
} ],
|
||||
"startStates" : [ "click.kamil.enterprise.machines.document.DocumentState.DRAFT" ],
|
||||
"endStates" : [ "click.kamil.enterprise.machines.document.DocumentState.APPROVED" ],
|
||||
"renderChoicesAsDiamonds" : true,
|
||||
"flows" : [ ],
|
||||
"stateTypeFqn" : "click.kamil.enterprise.machines.document.DocumentState",
|
||||
"eventTypeFqn" : "click.kamil.enterprise.machines.document.DocumentEvent",
|
||||
"metadata" : {
|
||||
"triggers" : [ {
|
||||
"event" : "OrderEvent.PAY",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "payOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 30,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "OrderEvent.SHIP",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "shipOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 37,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "DocumentEvent.SUBMIT",
|
||||
"event" : "click.kamil.enterprise.machines.document.DocumentEvent.SUBMIT",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "submitDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
@@ -40,7 +83,7 @@
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "DocumentEvent.APPROVE",
|
||||
"event" : "click.kamil.enterprise.machines.document.DocumentEvent.APPROVE",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "approveDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
@@ -53,7 +96,7 @@
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "DocumentEvent.REJECT",
|
||||
"event" : "click.kamil.enterprise.machines.document.DocumentEvent.REJECT",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "rejectDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
@@ -65,32 +108,6 @@
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "UserEvent.VERIFY",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "verifyUser",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 65,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "UserEvent.SUSPEND",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "suspendUser",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 72,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "event",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
@@ -98,145 +115,14 @@
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : "ORDER",
|
||||
"lineNumber" : 81,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
"constraint" : "\"ORDER\".equalsIgnoreCase(machineType)",
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "event",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "dispatch",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : "DOCUMENT",
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 87,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
"constraint" : "!(\"ORDER\".equalsIgnoreCase(machineType)) && \"DOCUMENT\".equalsIgnoreCase(machineType)",
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "event",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "dispatch",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : "USER",
|
||||
"lineNumber" : 93,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
"constraint" : "!(\"ORDER\".equalsIgnoreCase(machineType)) && !(\"DOCUMENT\".equalsIgnoreCase(machineType)) && \"USER\".equalsIgnoreCase(machineType)",
|
||||
"ambiguous" : false
|
||||
} ],
|
||||
"entryPoints" : [ {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/order/pay",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "payOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/order/pay",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/order/ship",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "shipOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/order/ship",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/document/submit",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "submitDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/document/submit",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/document/approve",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "approveDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/document/approve",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/document/reject",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "rejectDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/document/reject",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/user/verify",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "verifyUser",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/user/verify",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/user/suspend",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "suspendUser",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/user/suspend",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/{machineType}/transition/{event}",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
@@ -261,74 +147,6 @@
|
||||
} ]
|
||||
} ],
|
||||
"callChains" : [ {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/order/pay",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "payOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/order/pay",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.payOrder", "click.kamil.enterprise.web.StateMachineDispatcher.payOrder" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "OrderEvent.PAY",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "payOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 30,
|
||||
"polymorphicEvents" : [ "OrderEvent.PAY" ],
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/order/ship",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "shipOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/order/ship",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.shipOrder", "click.kamil.enterprise.web.StateMachineDispatcher.shipOrder" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "OrderEvent.SHIP",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "shipOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 37,
|
||||
"polymorphicEvents" : [ "OrderEvent.SHIP" ],
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/document/submit",
|
||||
@@ -347,7 +165,7 @@
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.submitDocument", "click.kamil.enterprise.web.StateMachineDispatcher.submitDocument" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "DocumentEvent.SUBMIT",
|
||||
"event" : "click.kamil.enterprise.machines.document.DocumentEvent.SUBMIT",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "submitDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
@@ -355,16 +173,16 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 44,
|
||||
"polymorphicEvents" : [ "DocumentEvent.SUBMIT" ],
|
||||
"polymorphicEvents" : [ "click.kamil.enterprise.machines.document.DocumentEvent.SUBMIT" ],
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "DocumentState.DRAFT",
|
||||
"targetState" : "DocumentState.IN_REVIEW",
|
||||
"event" : "DocumentEvent.SUBMIT"
|
||||
"sourceState" : "click.kamil.enterprise.machines.document.DocumentState.DRAFT",
|
||||
"targetState" : "click.kamil.enterprise.machines.document.DocumentState.IN_REVIEW",
|
||||
"event" : "click.kamil.enterprise.machines.document.DocumentEvent.SUBMIT"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
@@ -385,7 +203,7 @@
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.approveDocument", "click.kamil.enterprise.web.StateMachineDispatcher.approveDocument" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "DocumentEvent.APPROVE",
|
||||
"event" : "click.kamil.enterprise.machines.document.DocumentEvent.APPROVE",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "approveDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
@@ -393,16 +211,16 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 51,
|
||||
"polymorphicEvents" : [ "DocumentEvent.APPROVE" ],
|
||||
"polymorphicEvents" : [ "click.kamil.enterprise.machines.document.DocumentEvent.APPROVE" ],
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "DocumentState.IN_REVIEW",
|
||||
"targetState" : "DocumentState.APPROVED",
|
||||
"event" : "DocumentEvent.APPROVE"
|
||||
"sourceState" : "click.kamil.enterprise.machines.document.DocumentState.IN_REVIEW",
|
||||
"targetState" : "click.kamil.enterprise.machines.document.DocumentState.APPROVED",
|
||||
"event" : "click.kamil.enterprise.machines.document.DocumentEvent.APPROVE"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
@@ -423,7 +241,7 @@
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.rejectDocument", "click.kamil.enterprise.web.StateMachineDispatcher.rejectDocument" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "DocumentEvent.REJECT",
|
||||
"event" : "click.kamil.enterprise.machines.document.DocumentEvent.REJECT",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "rejectDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
@@ -431,85 +249,17 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 58,
|
||||
"polymorphicEvents" : [ "DocumentEvent.REJECT" ],
|
||||
"polymorphicEvents" : [ "click.kamil.enterprise.machines.document.DocumentEvent.REJECT" ],
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "DocumentState.IN_REVIEW",
|
||||
"targetState" : "DocumentState.DRAFT",
|
||||
"event" : "DocumentEvent.REJECT"
|
||||
"sourceState" : "click.kamil.enterprise.machines.document.DocumentState.IN_REVIEW",
|
||||
"targetState" : "click.kamil.enterprise.machines.document.DocumentState.DRAFT",
|
||||
"event" : "click.kamil.enterprise.machines.document.DocumentEvent.REJECT"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/user/verify",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "verifyUser",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/user/verify",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.verifyUser", "click.kamil.enterprise.web.StateMachineDispatcher.verifyUser" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "UserEvent.VERIFY",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "verifyUser",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 65,
|
||||
"polymorphicEvents" : [ "UserEvent.VERIFY" ],
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/user/suspend",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "suspendUser",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/user/suspend",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.suspendUser", "click.kamil.enterprise.web.StateMachineDispatcher.suspendUser" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "UserEvent.SUSPEND",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "suspendUser",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 72,
|
||||
"polymorphicEvents" : [ "UserEvent.SUSPEND" ],
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
@@ -543,49 +293,7 @@
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : "ORDER",
|
||||
"lineNumber" : 81,
|
||||
"polymorphicEvents" : [ "<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: DocumentEvent.*>", "<SYMBOLIC: UserEvent.*>" ],
|
||||
"external" : true,
|
||||
"constraint" : "\"ORDER\".equalsIgnoreCase(machineType)",
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/{machineType}/transition/{event}",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "transition",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/{machineType}/transition/{event}",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "machineType",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
}, {
|
||||
"name" : "event",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
}, {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.transition", "click.kamil.enterprise.web.StateMachineDispatcher.dispatch" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "true ? OrderEvent.valueOf(eventString.toUpperCase()) : true ? DocumentEvent.valueOf(eventString.toUpperCase()) : UserEvent.valueOf(eventString.toUpperCase())",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "dispatch",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : "DOCUMENT",
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 87,
|
||||
"polymorphicEvents" : [ "<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: DocumentEvent.*>", "<SYMBOLIC: UserEvent.*>" ],
|
||||
"external" : true,
|
||||
@@ -594,107 +302,9 @@
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/{machineType}/transition/{event}",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "transition",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/{machineType}/transition/{event}",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "machineType",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
}, {
|
||||
"name" : "event",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
}, {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.transition", "click.kamil.enterprise.web.StateMachineDispatcher.dispatch" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "true ? OrderEvent.valueOf(eventString.toUpperCase()) : true ? DocumentEvent.valueOf(eventString.toUpperCase()) : UserEvent.valueOf(eventString.toUpperCase())",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "dispatch",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : "USER",
|
||||
"lineNumber" : 93,
|
||||
"polymorphicEvents" : [ "<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: DocumentEvent.*>", "<SYMBOLIC: UserEvent.*>" ],
|
||||
"external" : true,
|
||||
"constraint" : "!(\"ORDER\".equalsIgnoreCase(machineType)) && !(\"DOCUMENT\".equalsIgnoreCase(machineType)) && \"USER\".equalsIgnoreCase(machineType)",
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
} ],
|
||||
"properties" : {
|
||||
"default" : { }
|
||||
}
|
||||
},
|
||||
"name" : "click.kamil.enterprise.machines.document.DocumentStateMachineConfiguration",
|
||||
"renderChoicesAsDiamonds" : true,
|
||||
"startStates" : [ "DocumentState.DRAFT" ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "DocumentState.DRAFT",
|
||||
"fullIdentifier" : "DocumentState.DRAFT"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "DocumentState.IN_REVIEW",
|
||||
"fullIdentifier" : "DocumentState.IN_REVIEW"
|
||||
} ],
|
||||
"event" : {
|
||||
"rawName" : "DocumentEvent.SUBMIT",
|
||||
"fullIdentifier" : "DocumentEvent.SUBMIT"
|
||||
},
|
||||
"guards" : [ ],
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
}, {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "DocumentState.IN_REVIEW",
|
||||
"fullIdentifier" : "DocumentState.IN_REVIEW"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "DocumentState.APPROVED",
|
||||
"fullIdentifier" : "DocumentState.APPROVED"
|
||||
} ],
|
||||
"event" : {
|
||||
"rawName" : "DocumentEvent.APPROVE",
|
||||
"fullIdentifier" : "DocumentEvent.APPROVE"
|
||||
},
|
||||
"guards" : [ ],
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
}, {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "DocumentState.IN_REVIEW",
|
||||
"fullIdentifier" : "DocumentState.IN_REVIEW"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "DocumentState.DRAFT",
|
||||
"fullIdentifier" : "DocumentState.DRAFT"
|
||||
} ],
|
||||
"event" : {
|
||||
"rawName" : "DocumentEvent.REJECT",
|
||||
"fullIdentifier" : "DocumentEvent.REJECT"
|
||||
},
|
||||
"guards" : [ ],
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
} ],
|
||||
"endStates" : [ "DocumentState.APPROVED" ]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,13 @@ skinparam ArrowThickness 1
|
||||
skinparam dpi 110
|
||||
skinparam svgLinkTarget _self
|
||||
|
||||
[*] --> DocumentState.DRAFT
|
||||
[*] --> click.kamil.enterprise.machines.document.DocumentState.DRAFT
|
||||
|
||||
|
||||
DocumentState.DRAFT -[#1E90FF,bold]-> DocumentState.IN_REVIEW <<external>> : DocumentEvent.SUBMIT
|
||||
DocumentState.IN_REVIEW -[#1E90FF,bold]-> DocumentState.APPROVED <<external>> : DocumentEvent.APPROVE
|
||||
DocumentState.IN_REVIEW -[#1E90FF,bold]-> DocumentState.DRAFT <<external>> : DocumentEvent.REJECT
|
||||
|
||||
DocumentState.APPROVED --> [*]
|
||||
click.kamil.enterprise.machines.document.DocumentState.APPROVED --> [*]
|
||||
@enduml
|
||||
|
||||
|
||||
@@ -9,5 +9,9 @@
|
||||
</state>
|
||||
<state id="APPROVED">
|
||||
</state>
|
||||
<state id="DRAFT">
|
||||
</state>
|
||||
<state id="APPROVED">
|
||||
</state>
|
||||
</scxml>
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"name" : "click.kamil.examples.enterprise.config.EnterpriseStateMachineConfig",
|
||||
"states" : [ {
|
||||
"rawName" : "String.CANCELLED",
|
||||
"fullIdentifier" : "String.CANCELLED"
|
||||
}, {
|
||||
"rawName" : "\"NEW\"",
|
||||
"fullIdentifier" : "String.NEW"
|
||||
}, {
|
||||
@@ -18,24 +15,15 @@
|
||||
}, {
|
||||
"rawName" : "\"PAID\"",
|
||||
"fullIdentifier" : "String.PAID"
|
||||
}, {
|
||||
"rawName" : "String.DELIVERED",
|
||||
"fullIdentifier" : "String.DELIVERED"
|
||||
}, {
|
||||
"rawName" : "\"CHECK_AVAILABILITY\"",
|
||||
"fullIdentifier" : "String.CHECK_AVAILABILITY"
|
||||
}, {
|
||||
"rawName" : "\"DELIVERED\"",
|
||||
"fullIdentifier" : "String.DELIVERED"
|
||||
}, {
|
||||
"rawName" : "String.NEW",
|
||||
"fullIdentifier" : "String.NEW"
|
||||
}, {
|
||||
"rawName" : "\"RETURNED\"",
|
||||
"fullIdentifier" : "String.RETURNED"
|
||||
}, {
|
||||
"rawName" : "String.RETURNED",
|
||||
"fullIdentifier" : "String.RETURNED"
|
||||
} ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
}, {
|
||||
"rawName" : "\"START\"",
|
||||
"fullIdentifier" : "String.START"
|
||||
}, {
|
||||
"rawName" : "String.START",
|
||||
"fullIdentifier" : "String.START"
|
||||
} ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
}, {
|
||||
"rawName" : "\"START\"",
|
||||
"fullIdentifier" : "String.START"
|
||||
}, {
|
||||
"rawName" : "String.START",
|
||||
"fullIdentifier" : "String.START"
|
||||
} ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
}, {
|
||||
"rawName" : "States.STATE_EXTRA_2",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE_EXTRA_2"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE1",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE1"
|
||||
}, {
|
||||
"rawName" : "States.STATE11",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE11"
|
||||
@@ -66,9 +63,6 @@
|
||||
}, {
|
||||
"rawName" : "States.STATE_EXTRA_3",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE_EXTRA_3"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATEZ",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATEZ"
|
||||
}, {
|
||||
"rawName" : "States.CANCEL",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.CANCEL"
|
||||
|
||||
@@ -24,9 +24,6 @@
|
||||
}, {
|
||||
"rawName" : "States.STATEY",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATEY"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE1",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE1"
|
||||
}, {
|
||||
"rawName" : "States.STATE11",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE11"
|
||||
@@ -69,9 +66,6 @@
|
||||
}, {
|
||||
"rawName" : "States.STATE_EXTRA_1",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE_EXTRA_1"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATEZ",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATEZ"
|
||||
}, {
|
||||
"rawName" : "States.CANCEL",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.CANCEL"
|
||||
|
||||
@@ -21,15 +21,9 @@
|
||||
}, {
|
||||
"rawName" : "States.END",
|
||||
"fullIdentifier" : "ForkJoinStateMachineConfig.States.END"
|
||||
}, {
|
||||
"rawName" : "ForkJoinStateMachineConfig.States.END",
|
||||
"fullIdentifier" : "ForkJoinStateMachineConfig.States.END"
|
||||
}, {
|
||||
"rawName" : "States.FORK",
|
||||
"fullIdentifier" : "ForkJoinStateMachineConfig.States.FORK"
|
||||
}, {
|
||||
"rawName" : "ForkJoinStateMachineConfig.States.START",
|
||||
"fullIdentifier" : "ForkJoinStateMachineConfig.States.START"
|
||||
} ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
}, {
|
||||
"rawName" : "States.STATE_EXTRA_2",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE_EXTRA_2"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE1",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE1"
|
||||
}, {
|
||||
"rawName" : "States.STATE2",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE2"
|
||||
@@ -45,9 +42,6 @@
|
||||
}, {
|
||||
"rawName" : "States.STATE_EXTRA_3",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE_EXTRA_3"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATEZ",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATEZ"
|
||||
}, {
|
||||
"rawName" : "States.CANCEL",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.CANCEL"
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
}, {
|
||||
"rawName" : "States.STATE_EXTRA_2",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE_EXTRA_2"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE1",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE1"
|
||||
}, {
|
||||
"rawName" : "States.STATE2",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE2"
|
||||
@@ -45,9 +42,6 @@
|
||||
}, {
|
||||
"rawName" : "States.STATE_EXTRA_1",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATE_EXTRA_1"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATEZ",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.STATEZ"
|
||||
}, {
|
||||
"rawName" : "States.CANCEL",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate3.States.CANCEL"
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
}, {
|
||||
"rawName" : "\"START\"",
|
||||
"fullIdentifier" : "String.START"
|
||||
}, {
|
||||
"rawName" : "String.START",
|
||||
"fullIdentifier" : "String.START"
|
||||
} ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
}, {
|
||||
"rawName" : "OrderStates.FULFILLED",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.enumstate.OrderStates.FULFILLED"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.enumstate.OrderStates.CANCELED",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.enumstate.OrderStates.CANCELED"
|
||||
}, {
|
||||
"rawName" : "OrderStates.PAID1",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.enumstate.OrderStates.PAID1"
|
||||
@@ -24,12 +21,6 @@
|
||||
}, {
|
||||
"rawName" : "OrderStates.PAID",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.enumstate.OrderStates.PAID"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.enumstate.OrderStates.SUBMITTED",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.enumstate.OrderStates.SUBMITTED"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.enumstate.OrderStates.FULFILLED",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.enumstate.OrderStates.FULFILLED"
|
||||
}, {
|
||||
"rawName" : "OrderStates.HAPPEN",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.enumstate.OrderStates.HAPPEN"
|
||||
|
||||
@@ -3,18 +3,12 @@
|
||||
"states" : [ {
|
||||
"rawName" : "\"DONE\"",
|
||||
"fullIdentifier" : "String.DONE"
|
||||
}, {
|
||||
"rawName" : "String.INIT",
|
||||
"fullIdentifier" : "String.INIT"
|
||||
}, {
|
||||
"rawName" : "\"INIT\"",
|
||||
"fullIdentifier" : "String.INIT"
|
||||
}, {
|
||||
"rawName" : "\"BUSY\"",
|
||||
"fullIdentifier" : "String.BUSY"
|
||||
}, {
|
||||
"rawName" : "String.DONE",
|
||||
"fullIdentifier" : "String.DONE"
|
||||
} ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
|
||||
@@ -1,679 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>State Machine Explorer - click.kamil.maven.core.MavenOrderStateMachine</title>
|
||||
<!-- Popper and Tippy for tooltips -->
|
||||
<script src="https://unpkg.com/@popperjs/core@2"></script>
|
||||
<script src="https://unpkg.com/tippy.js@6"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/tippy.js@6/animations/scale.css"/>
|
||||
<!-- SVG Pan & Zoom -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/svg-pan-zoom@3.6.1/dist/svg-pan-zoom.min.js"></script>
|
||||
<!-- Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;900&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--sidebar-bg: #ffffff;
|
||||
--main-bg: #f8fafc;
|
||||
--accent: #ef4444;
|
||||
--primary: #3b82f6;
|
||||
--text: #0f172a;
|
||||
--text-muted: #64748b;
|
||||
--border: #e2e8f0;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
font-family: 'Inter', -apple-system, sans-serif;
|
||||
background: var(--main-bg);
|
||||
color: var(--text);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
width: 480px;
|
||||
min-width: 350px;
|
||||
max-width: 900px;
|
||||
background: var(--sidebar-bg);
|
||||
border-right: 2px solid var(--border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 10px 0 15px rgba(0,0,0,0.02);
|
||||
z-index: 10;
|
||||
overflow-x: hidden;
|
||||
resize: horizontal;
|
||||
position: relative;
|
||||
transition: width 0.3s ease, min-width 0.3s ease, padding 0.3s ease;
|
||||
}
|
||||
|
||||
#sidebar.collapsed {
|
||||
width: 0 !important;
|
||||
min-width: 0 !important;
|
||||
border-right: none;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
#toggle-sidebar {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
z-index: 50;
|
||||
background: #fff;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
#toggle-sidebar:hover {
|
||||
background: #f8fafc;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
#toggle-sidebar svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
fill: none;
|
||||
stroke: var(--text-muted);
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
#sidebar::after {
|
||||
content: '⋮';
|
||||
position: absolute;
|
||||
right: 2px; top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--border);
|
||||
font-size: 1.2rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 30px 30px 10px;
|
||||
}
|
||||
|
||||
header h1 { font-weight: 900; font-size: 1.6rem; color: var(--text); margin: 0 0 5px 0; letter-spacing: -0.5px; }
|
||||
header p { font-family: 'JetBrains Mono', monospace; font-size: 0.7rem; color: var(--text-muted); margin: 0 0 15px 0; opacity: 0.7; }
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
padding: 0 30px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 10px 0;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.tab:hover { color: var(--text); }
|
||||
.tab.active { color: var(--primary); }
|
||||
.tab.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -1px; left: 0; right: 0;
|
||||
height: 2px;
|
||||
background: var(--primary);
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
flex-grow: 1;
|
||||
padding: 25px 30px;
|
||||
overflow-y: auto;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-content.active { display: block; }
|
||||
|
||||
.ep-card, .flow-card {
|
||||
padding: 18px;
|
||||
background: #fff;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
margin-bottom: 15px;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
||||
position: relative;
|
||||
word-break: break-all;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.ep-card:hover, .flow-card:hover {
|
||||
border-color: var(--primary);
|
||||
transform: translateX(5px);
|
||||
box-shadow: 0 15px 30px -10px rgba(59, 130, 246, 0.15);
|
||||
}
|
||||
|
||||
.ep-type {
|
||||
font-size: 0.6rem;
|
||||
font-weight: 900;
|
||||
padding: 3px 8px;
|
||||
border-radius: 6px;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.8px;
|
||||
}
|
||||
.type-rest { background: #eff6ff; color: #1d4ed8; }
|
||||
.type-custom { background: #fef2f2; color: #b91c1c; }
|
||||
.type-jms { background: #f5f3ff; color: #7c3aed; }
|
||||
.type-sqs { background: #fff7ed; color: #ea580c; }
|
||||
.type-sns { background: #fff1f2; color: #e11d48; }
|
||||
.type-kafka { background: #fefce8; color: #854d0e; }
|
||||
.type-rabbit { background: #f0fdf4; color: #166534; }
|
||||
|
||||
.ep-name, .flow-name { font-weight: 700; font-size: 0.95rem; margin-bottom: 6px; color: var(--text); }
|
||||
.ep-fqn, .flow-desc { font-family: 'Inter', sans-serif; font-size: 0.75rem; color: var(--text-muted); opacity: 0.9; line-height: 1.4; }
|
||||
|
||||
#main { flex-grow: 1; position: relative; background: var(--main-bg); overflow: hidden; }
|
||||
#svg-container {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* SVG Interactivity Styles */
|
||||
svg a { text-decoration: none !important; }
|
||||
|
||||
.active-path {
|
||||
stroke: var(--accent) !important;
|
||||
stroke-width: 2.5px !important;
|
||||
filter: drop-shadow(0 0 8px rgba(239, 68, 68, 0.3));
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.active-path text, a.active-path text {
|
||||
fill: #000 !important;
|
||||
font-weight: 900 !important;
|
||||
paint-order: stroke;
|
||||
stroke: #fff;
|
||||
stroke-width: 3.5px;
|
||||
}
|
||||
|
||||
.dimmed {
|
||||
opacity: 0.2 !important;
|
||||
filter: grayscale(1);
|
||||
transition: opacity 0.4s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Tippy Styling */
|
||||
.tippy-box[data-theme~='modern'] {
|
||||
background-color: #fff;
|
||||
color: var(--text);
|
||||
box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 16px;
|
||||
padding: 0;
|
||||
max-width: 550px !important;
|
||||
}
|
||||
|
||||
.tooltip-content {
|
||||
padding: 20px;
|
||||
max-width: 500px;
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.payload-box {
|
||||
background: #0f172a;
|
||||
color: #e2e8f0;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.5;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.payload-param {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.payload-param:last-child { margin-bottom: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<header>
|
||||
<h1>Explorer</h1>
|
||||
<p>click.kamil.maven.core.MavenOrderStateMachine</p>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<div class="tabs">
|
||||
<div class="tab active" data-tab="explorer">Explorer</div>
|
||||
<div class="tab" data-tab="flows">Business Flows</div>
|
||||
</div>
|
||||
|
||||
<div id="tab-explorer" class="tab-content active">
|
||||
<div id="ep-list"></div>
|
||||
</div>
|
||||
|
||||
<div id="tab-flows" class="tab-content">
|
||||
<div id="flow-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="main">
|
||||
<button id="toggle-sidebar" aria-label="Toggle Sidebar" title="Toggle Sidebar">
|
||||
<svg viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h16"></path></svg>
|
||||
</button>
|
||||
<div id="svg-container">
|
||||
<?xml version="1.0" encoding="us-ascii" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="100%" preserveAspectRatio="xMidYMid meet" version="1.1" viewBox="0 0 187 255" width="100%" zoomAndPan="magnify"><defs/><g><ellipse cx="35.5208" cy="34.375" fill="#222222" rx="11.4583" ry="11.4583" style="stroke:#222222;stroke-width:1.1458333333333333;"/><rect fill="#FFFFFF" height="45.8333" rx="14.3229" ry="14.3229" style="stroke:#94A3B8;stroke-width:1.1458333333333333;" width="57.2917" x="6.875" y="101.9792"/><text fill="#000000" font-family="Inter" font-size="10.3125" font-weight="bold" lengthAdjust="spacing" textLength="21.7708" x="24.6354" y="128.7932">INIT</text><rect fill="#FFFFFF" height="45.8333" rx="14.3229" ry="14.3229" style="stroke:#94A3B8;stroke-width:1.1458333333333333;" width="57.2917" x="6.875" y="202.8125"/><text fill="#000000" font-family="Inter" font-size="10.3125" font-weight="bold" lengthAdjust="spacing" textLength="27.5" x="21.7708" y="229.6266">BUSY</text><rect fill="#FFFFFF" height="45.8333" rx="14.3229" ry="14.3229" style="stroke:#94A3B8;stroke-width:1.1458333333333333;" width="57.2917" x="68.75" y="13.75"/><text fill="#000000" font-family="Inter" font-size="10.3125" font-weight="bold" lengthAdjust="spacing" textLength="29.7917" x="82.5" y="40.5641">DONE</text><ellipse cx="97.3958" cy="123.75" fill="none" rx="12.6042" ry="12.6042" style="stroke:#222222;stroke-width:1.1458333333333333;"/><ellipse cx="97.3958" cy="123.75" fill="#222222" rx="6.875" ry="6.875" style="stroke:#222222;stroke-width:1.1458333333333333;"/><polygon fill="#CBD5E1" points="35.5208,101.6748,40.1042,91.3623,35.5208,95.9456,30.9375,91.3623,35.5208,101.6748" style="stroke:#CBD5E1;stroke-width:1.1458333333333333;"/><path d="M35.5208,50.4929 C35.5208,63.9394 35.5208,78.5626 35.5208,94.7998 " fill="none" style="stroke:#CBD5E1;stroke-width:1.1458333333333333;"/><polygon fill="#1E90FF" points="35.5208,202.7749,40.1042,192.4624,35.5208,197.0457,30.9375,192.4624,35.5208,202.7749" style="stroke:#1E90FF;stroke-width:1.1458333333333333;"/><path d="M35.5208,147.9113 C35.5208,164.2413 35.5208,179.5846 35.5208,195.8999 " fill="none" style="stroke:#1E90FF;stroke-width:2.2916666666666665;"/><a href="#link_ORDER_EVENT" target="_self" title="#link_ORDER_EVENT" xlink:actuate="onRequest" xlink:href="#link_ORDER_EVENT" xlink:show="new" xlink:title="#link_ORDER_EVENT" xlink:type="simple"><text fill="#0000FF" font-family="JetBrains Mono" font-size="9.1667" lengthAdjust="spacing" text-decoration="underline" textLength="142.0833" x="36.6667" y="178.4456">ORDER_EVENT / loggingAction()</text></a><polygon fill="#CBD5E1" points="97.3958,110.7767,101.9792,100.4642,97.3958,105.0475,92.8125,100.4642,97.3958,110.7767" style="stroke:#CBD5E1;stroke-width:1.1458333333333333;"/><path d="M97.3958,59.9435 C97.3958,76.0566 97.3958,90.453 97.3958,103.9017 " fill="none" style="stroke:#CBD5E1;stroke-width:1.1458333333333333;"/><!--SRC=[RP5lJy8m4CRVzrESuOqQYSm_2HX20Z8IZ881D364a6CzjeQkNTeYJkDtjmF4XVYgrz-rzpntTv8PZ5C4YRbUEx0fELJ8BFcOCZJej06b5R54S09ACvS39niPaJcXrGvRHuQqopDYTYLKyI_r41t15mFeOBIAZLuhVg-bhxT9XAE2QyF9x5YbSOFNY_g1JX8HhHHP2u5dFQtS05E2ll9IUp0MdmIDtulB9S52I-x1QATb51cugddmZ9mB5VjQtsM72NAzAVWIfIrxRnkZDmVH1t8TWq9PUD9A__TiQwL-dDbt5YtuBGN7oNA3VocU2GY2MjdaU_mer6g29lPBcLkIIyQcvpEeLblG7_GdZB7YWEgq4eIDMgztKKnXvhETb_4RD9lquMUcKBPQnMK-77N3qJny3GSJJ-vWEgr8Br3cK8ulGUeuzbDgHyN6JyzcCyQwmq6uTU2T_000]--></g></svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script id="metadata-json" type="application/json">
|
||||
{
|
||||
"name" : "click.kamil.maven.core.MavenOrderStateMachine",
|
||||
"states" : [ {
|
||||
"rawName" : "DONE",
|
||||
"fullIdentifier" : "DONE"
|
||||
}, {
|
||||
"rawName" : "\"INIT\"",
|
||||
"fullIdentifier" : "INIT"
|
||||
}, {
|
||||
"rawName" : "\"BUSY\"",
|
||||
"fullIdentifier" : "BUSY"
|
||||
}, {
|
||||
"rawName" : "INIT",
|
||||
"fullIdentifier" : "INIT"
|
||||
} ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "\"INIT\"",
|
||||
"fullIdentifier" : "INIT"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "\"BUSY\"",
|
||||
"fullIdentifier" : "BUSY"
|
||||
} ],
|
||||
"event" : "ORDER_EVENT",
|
||||
"guard" : null,
|
||||
"actions" : [ {
|
||||
"expression" : "loggingAction()",
|
||||
"isLambda" : false,
|
||||
"internalLogic" : "context -> {\n System.out.println(\"LOG: State transition in progress...\");\n}\n",
|
||||
"lineNumber" : 23
|
||||
} ],
|
||||
"order" : null
|
||||
} ],
|
||||
"startStates" : [ "INIT" ],
|
||||
"endStates" : [ "DONE" ],
|
||||
"renderChoicesAsDiamonds" : true,
|
||||
"flows" : [ ],
|
||||
"metadata" : {
|
||||
"triggers" : [ {
|
||||
"event" : "ORDER_EVENT",
|
||||
"className" : "click.kamil.maven.core.OrderService",
|
||||
"methodName" : "onMessage",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 34
|
||||
} ],
|
||||
"entryPoints" : [ {
|
||||
"type" : "REST",
|
||||
"name" : "POST /reactive/order",
|
||||
"className" : "click.kamil.maven.api.ReactiveOrderApi",
|
||||
"methodName" : "orderRoutes",
|
||||
"metadata" : {
|
||||
"path" : "/reactive/order",
|
||||
"verb" : "POST",
|
||||
"style" : "WebFlux Functional"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "JMS",
|
||||
"name" : "JMS: order.queue",
|
||||
"className" : "click.kamil.maven.api.JmsOrderListener",
|
||||
"methodName" : "onMessage",
|
||||
"metadata" : {
|
||||
"protocol" : "JMS",
|
||||
"destination" : "order.queue"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "message",
|
||||
"type" : "String",
|
||||
"annotations" : [ ]
|
||||
} ]
|
||||
} ],
|
||||
"callChains" : [ ],
|
||||
"properties" : {
|
||||
"default" : { }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
const metadata = JSON.parse(document.getElementById('metadata-json').textContent);
|
||||
let panZoomInstance;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const svg = document.querySelector('#svg-container svg');
|
||||
panZoomInstance = svgPanZoom(svg, { zoomEnabled: true, controlIconsEnabled: true, fit: true, center: true });
|
||||
|
||||
document.getElementById('toggle-sidebar').addEventListener('click', () => {
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
sidebar.classList.toggle('collapsed');
|
||||
|
||||
// Re-center SVG pan-zoom after transition
|
||||
setTimeout(() => {
|
||||
if (panZoomInstance) {
|
||||
panZoomInstance.resize();
|
||||
panZoomInstance.center();
|
||||
}
|
||||
}, 300);
|
||||
});
|
||||
|
||||
setupTabs();
|
||||
populateSidebar();
|
||||
populateFlows();
|
||||
attachInteractivity();
|
||||
});
|
||||
|
||||
function setupTabs() {
|
||||
const flows = metadata.flows || [];
|
||||
if (flows.length === 0) {
|
||||
document.querySelector('.tabs').style.display = 'none';
|
||||
document.querySelector('.tab-content[data-tab="flows"]')?.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
document.querySelectorAll('.tab').forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
document.querySelectorAll('.tab, .tab-content').forEach(el => el.classList.remove('active'));
|
||||
tab.classList.add('active');
|
||||
document.getElementById('tab-' + tab.dataset.tab).classList.add('active');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function populateSidebar() {
|
||||
const list = document.getElementById('ep-list');
|
||||
const entryPoints = metadata.metadata.entryPoints || [];
|
||||
const machineEvents = new Set(metadata.transitions.map(t => t.event).filter(Boolean));
|
||||
|
||||
entryPoints.forEach(ep => {
|
||||
// Quality Filter: Hide if it doesn't trigger a machine event
|
||||
const chains = (metadata.metadata.callChains || []).filter(c =>
|
||||
c.entryPoint.className === ep.className &&
|
||||
c.entryPoint.methodName === ep.methodName &&
|
||||
machineEvents.has(c.triggerPoint.event)
|
||||
);
|
||||
|
||||
if (chains.length === 0) return;
|
||||
|
||||
const card = document.createElement('div');
|
||||
card.className = 'ep-card';
|
||||
card.innerHTML = `
|
||||
<div class="ep-type type-${ep.type.toLowerCase()}">${ep.type}</div>
|
||||
<div class="ep-name">${ep.name}</div>
|
||||
<div class="ep-fqn">${ep.className}.${ep.methodName}</div>
|
||||
`;
|
||||
card.onmouseenter = () => highlightEntryPoint(ep);
|
||||
card.onmouseleave = clearHighlights;
|
||||
list.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
function populateFlows() {
|
||||
const list = document.getElementById('flow-list');
|
||||
const flows = metadata.flows || [];
|
||||
|
||||
if (flows.length === 0) {
|
||||
list.innerHTML = '<p style="color:var(--text-muted); font-size:0.8rem; font-style:italic;">No business flows defined.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
flows.forEach(flow => {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'flow-card';
|
||||
card.innerHTML = `
|
||||
<div class="flow-name">${flow.name}</div>
|
||||
<div class="flow-desc">${flow.description}</div>
|
||||
`;
|
||||
card.onmouseenter = () => highlightFlow(flow);
|
||||
card.onmouseleave = clearHighlights;
|
||||
list.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
function highlightEntryPoint(ep) {
|
||||
clearHighlights();
|
||||
const machineEvents = new Set(metadata.transitions.map(t => t.event).filter(Boolean));
|
||||
const chains = metadata.metadata.callChains || [];
|
||||
const relevantEvents = chains
|
||||
.filter(c => c.entryPoint.className === ep.className && c.entryPoint.methodName === ep.methodName && machineEvents.has(c.triggerPoint.event))
|
||||
.map(c => c.triggerPoint.event);
|
||||
|
||||
if (relevantEvents.length === 0) return;
|
||||
highlightEvents(relevantEvents);
|
||||
}
|
||||
|
||||
function highlightFlow(flow) {
|
||||
clearHighlights();
|
||||
if (!flow.steps || flow.steps.length === 0) return;
|
||||
highlightEvents(flow.steps);
|
||||
}
|
||||
|
||||
function highlightEvents(steps) {
|
||||
const svg = document.querySelector('#svg-container svg');
|
||||
|
||||
// Dim everything first
|
||||
svg.querySelectorAll('path, rect, circle, ellipse, text, polygon').forEach(el => {
|
||||
el.classList.add('dimmed');
|
||||
});
|
||||
|
||||
steps.forEach(step => {
|
||||
let linkId = "";
|
||||
if (step.includes("->")) {
|
||||
// Named Choice Branch: "SRC->TGT"
|
||||
const parts = step.split("->");
|
||||
linkId = "#link_anon_" + normalize(parts[0]) + "_" + normalize(parts[1]);
|
||||
} else {
|
||||
// Standard Event
|
||||
linkId = "#link_" + normalize(step);
|
||||
}
|
||||
|
||||
svg.querySelectorAll('a').forEach(link => {
|
||||
const href = link.getAttribute('xlink:href') || link.getAttribute('href');
|
||||
if (href === linkId) {
|
||||
highlightLinkGroup(link);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Un-dim all state nodes for context
|
||||
svg.querySelectorAll('rect, circle, ellipse, polygon').forEach(el => {
|
||||
const isChoice = el.tagName === 'polygon' && el.points?.numberOfItems === 4;
|
||||
if (isChoice || ['rect', 'circle', 'ellipse'].includes(el.tagName)) {
|
||||
el.classList.remove('dimmed');
|
||||
let next = el.nextElementSibling;
|
||||
while(next && next.tagName === 'text') {
|
||||
next.classList.remove('dimmed');
|
||||
next = next.nextElementSibling;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function highlightLinkGroup(link) {
|
||||
link.classList.add('active-path');
|
||||
link.querySelectorAll('*').forEach(child => child.classList.remove('dimmed'));
|
||||
|
||||
// Find path and polygon immediately before the <a> tag (interleaved)
|
||||
let prev = link.previousElementSibling;
|
||||
let foundPath = false;
|
||||
let foundPolygon = false;
|
||||
while (prev) {
|
||||
if (prev.tagName === 'path' && !foundPath) {
|
||||
prev.classList.remove('dimmed');
|
||||
prev.classList.add('active-path');
|
||||
foundPath = true;
|
||||
} else if (prev.tagName === 'polygon' && !foundPolygon && prev.points?.numberOfItems !== 4) {
|
||||
prev.classList.remove('dimmed');
|
||||
prev.classList.add('active-path');
|
||||
foundPolygon = true;
|
||||
} else if (['rect', 'circle', 'ellipse'].includes(prev.tagName) ||
|
||||
(prev.tagName === 'polygon' && prev.points?.numberOfItems === 4)) {
|
||||
// Hit a state node, stop traversal
|
||||
break;
|
||||
}
|
||||
if (foundPath && foundPolygon) break;
|
||||
prev = prev.previousElementSibling;
|
||||
}
|
||||
}
|
||||
|
||||
function clearHighlights() {
|
||||
document.querySelectorAll('.dimmed, .active-path').forEach(el => {
|
||||
el.classList.remove('dimmed', 'active-path');
|
||||
});
|
||||
}
|
||||
|
||||
function attachInteractivity() {
|
||||
const svg = document.querySelector('#svg-container svg');
|
||||
const transitions = metadata.transitions || [];
|
||||
|
||||
svg.querySelectorAll('a').forEach(link => {
|
||||
const href = link.getAttribute('xlink:href') || link.getAttribute('href');
|
||||
if (!href || !href.startsWith('#link_')) return;
|
||||
|
||||
const linkId = href.replace('#link_', '');
|
||||
const isAnon = linkId.startsWith('anon_');
|
||||
|
||||
let metaTrans = null;
|
||||
if (isAnon) {
|
||||
metaTrans = transitions.find(t =>
|
||||
!t.event &&
|
||||
"anon_" + normalize(t.sourceStates[0].fullIdentifier) + "_" + normalize(t.targetStates[0].fullIdentifier) === linkId
|
||||
);
|
||||
} else {
|
||||
metaTrans = transitions.find(t => normalize(t.event) === linkId);
|
||||
}
|
||||
|
||||
if (metaTrans || isAnon) {
|
||||
link.style.cursor = 'pointer';
|
||||
const setupTippy = (el) => {
|
||||
tippy(el, {
|
||||
content: createTooltip(isAnon ? "Internal logic" : linkId, metaTrans),
|
||||
allowHTML: true,
|
||||
theme: 'modern',
|
||||
interactive: true,
|
||||
appendTo: document.body,
|
||||
placement: 'right',
|
||||
offset: [0, 20]
|
||||
});
|
||||
};
|
||||
setupTippy(link);
|
||||
let path = link.previousElementSibling;
|
||||
while(path && path.tagName !== 'path') path = path.previousElementSibling;
|
||||
if (path) {
|
||||
path.style.cursor = 'pointer';
|
||||
setupTippy(path);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function normalize(s) {
|
||||
if (!s) return "";
|
||||
return s.replace(/[^a-zA-Z0-9]/g, '_');
|
||||
}
|
||||
|
||||
function createTooltip(name, trans) {
|
||||
const chains = (metadata.metadata.callChains || []).filter(c => normalize(c.triggerPoint.event) === name);
|
||||
|
||||
let html = `<div class="tooltip-content">
|
||||
<div style="font-weight:900; font-size:1.1rem; margin-bottom:12px; border-bottom:1px solid #eee; padding-bottom:8px; line-height:1.3;">
|
||||
${name === 'Internal logic' ? name : 'Event: <span style="color:var(--accent)">' + name + '</span>'}
|
||||
</div>`;
|
||||
|
||||
if (trans && trans.guard) {
|
||||
const lineInfo = trans.guard.lineNumber ? ` <span style="color:#94a3b8; font-weight:400; font-size:0.6rem;">(Line: ${trans.guard.lineNumber})</span>` : '';
|
||||
html += `<div style="font-size:0.7rem; font-weight:800; color:#94a3b8; text-transform:uppercase; margin-bottom:4px">Guard${lineInfo}</div>
|
||||
<div style="background:#f1f5f9; padding:6px; border-radius:4px; font-family:monospace; font-size:0.8rem; margin-bottom:12px; border:1px solid #e2e8f0;">${trans.guard.expression}</div>`;
|
||||
if (trans.guard.internalLogic) {
|
||||
html += `<div style="font-size:0.65rem; font-weight:800; color:#cbd5e1; text-transform:uppercase; margin-bottom:4px">Internal Logic</div>
|
||||
<pre style="background:#1e293b; color:#f8fafc; padding:8px; border-radius:4px; font-family:'JetBrains Mono', monospace; font-size:0.7rem; overflow-x:auto; margin-bottom:12px;"><code>${trans.guard.internalLogic.replace(/</g, '<').replace(/>/g, '>')}</code></pre>`;
|
||||
}
|
||||
}
|
||||
|
||||
if (trans && trans.actions && trans.actions.length > 0) {
|
||||
html += `<div style="font-size:0.7rem; font-weight:800; color:#94a3b8; text-transform:uppercase; margin-bottom:4px">Actions</div>`;
|
||||
trans.actions.forEach(action => {
|
||||
const lineInfo = action.lineNumber ? ` <span style="color:#94a3b8; font-weight:400; font-size:0.6rem;">(Line: ${action.lineNumber})</span>` : '';
|
||||
html += `<div style="font-size:0.7rem; font-weight:800; color:#94a3b8; text-transform:uppercase; margin-bottom:4px">Action${lineInfo}</div>
|
||||
<div style="background:#f1f5f9; padding:6px; border-radius:4px; font-family:monospace; font-size:0.8rem; margin-bottom:12px; border:1px solid #e2e8f0;">${action.expression}</div>`;
|
||||
if (action.internalLogic) {
|
||||
html += `<div style="font-size:0.65rem; font-weight:800; color:#cbd5e1; text-transform:uppercase; margin-bottom:4px">Internal Logic</div>
|
||||
<pre style="background:#1e293b; color:#f8fafc; padding:8px; border-radius:4px; font-family:'JetBrains Mono', monospace; font-size:0.7rem; overflow-x:auto; margin-bottom:12px;"><code>${action.internalLogic.replace(/</g, '<').replace(/>/g, '>')}</code></pre>`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (chains && chains.length > 0) {
|
||||
html += `<div style="font-size:0.7rem; font-weight:800; color:#94a3b8; text-transform:uppercase; margin-bottom:10px; border-top:1px solid #eee; padding-top:10px">Triggered by</div>`;
|
||||
chains.forEach(c => {
|
||||
html += `<div style="margin-bottom:20px">
|
||||
<div style="font-weight:700; font-size:0.85rem; color:var(--text);">${c.entryPoint.name}</div>
|
||||
${renderParameters(c.entryPoint.parameters)}
|
||||
<div style="margin-left:10px; border-left:2px solid #e2e8f0; padding-left:12px; margin-top:10px">
|
||||
${c.methodChain.map((m, i) => `<div style="font-size:0.7rem; color:#475569; margin-top:4px; font-family:'JetBrains Mono', monospace;"><b>${i+1}</b> ${m}</div>`).join('')}
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
}
|
||||
html += `</div>`;
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderParameters(params) {
|
||||
if (!params || params.length === 0) return "";
|
||||
let html = `<div style="font-size:0.65rem; font-weight:700; color:#94a3b8; margin-top:8px; margin-bottom:4px; text-transform:uppercase;">Input Data</div>`;
|
||||
html += `<div class="payload-box">`;
|
||||
params.forEach(p => {
|
||||
const annotation = (p.annotations || []).find(a => a.endsWith("RequestBody") || a.endsWith("PathVariable") || a.endsWith("RequestParam"));
|
||||
const cleanAnn = annotation ? "@" + annotation.substring(annotation.lastIndexOf('.') + 1) : "";
|
||||
html += `<div class="payload-param">
|
||||
<span style="color:#f43f5e; font-size:0.65rem; font-weight:bold;">${cleanAnn}</span>
|
||||
<span style="color:#38bdf8;">${p.type}</span>
|
||||
<span style="color:#a3e635;">${p.name}</span>
|
||||
</div>`;
|
||||
});
|
||||
html += `</div>`;
|
||||
return html;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,66 +0,0 @@
|
||||
{
|
||||
"metadata" : {
|
||||
"triggers" : [ {
|
||||
"event" : "ORDER_EVENT",
|
||||
"className" : "click.kamil.maven.core.OrderService",
|
||||
"methodName" : "onMessage",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 34
|
||||
} ],
|
||||
"entryPoints" : [ {
|
||||
"type" : "REST",
|
||||
"name" : "POST /reactive/order",
|
||||
"className" : "click.kamil.maven.api.ReactiveOrderApi",
|
||||
"methodName" : "orderRoutes",
|
||||
"metadata" : {
|
||||
"path" : "/reactive/order",
|
||||
"verb" : "POST",
|
||||
"style" : "WebFlux Functional"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "JMS",
|
||||
"name" : "JMS: order.queue",
|
||||
"className" : "click.kamil.maven.api.JmsOrderListener",
|
||||
"methodName" : "onMessage",
|
||||
"metadata" : {
|
||||
"protocol" : "JMS",
|
||||
"destination" : "order.queue"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "message",
|
||||
"type" : "String",
|
||||
"annotations" : [ ]
|
||||
} ]
|
||||
} ],
|
||||
"callChains" : [ ],
|
||||
"properties" : {
|
||||
"default" : { }
|
||||
}
|
||||
},
|
||||
"name" : "click.kamil.maven.core.MavenOrderStateMachine",
|
||||
"renderChoicesAsDiamonds" : true,
|
||||
"startStates" : [ "INIT" ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "\"INIT\"",
|
||||
"fullIdentifier" : "INIT"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "\"BUSY\"",
|
||||
"fullIdentifier" : "BUSY"
|
||||
} ],
|
||||
"event" : "ORDER_EVENT",
|
||||
"guard" : null,
|
||||
"actions" : [ {
|
||||
"expression" : "loggingAction()",
|
||||
"isLambda" : false,
|
||||
"internalLogic" : "context -> {\n System.out.println(\"LOG: State transition in progress...\");\n}\n",
|
||||
"lineNumber" : 23
|
||||
} ],
|
||||
"order" : null
|
||||
} ],
|
||||
"endStates" : [ "DONE" ]
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
@startuml
|
||||
!pragma layout smetana
|
||||
set separator none
|
||||
hide empty description
|
||||
hide stereotype
|
||||
skinparam state {
|
||||
BackgroundColor white
|
||||
BorderColor #94a3b8
|
||||
BorderThickness 1
|
||||
FontName Inter
|
||||
FontSize 9
|
||||
FontStyle bold
|
||||
RoundCorner 20
|
||||
Padding 1
|
||||
}
|
||||
skinparam shadowing false
|
||||
skinparam ArrowFontName JetBrains Mono
|
||||
skinparam ArrowFontSize 8
|
||||
skinparam ArrowColor #cbd5e1
|
||||
skinparam ArrowThickness 1
|
||||
skinparam dpi 110
|
||||
skinparam svgLinkTarget _self
|
||||
|
||||
[*] --> INIT
|
||||
|
||||
|
||||
INIT -[#1E90FF,bold]-> BUSY <<external>> <<e_ORDER_EVENT>> : ORDER_EVENT / loggingAction()
|
||||
|
||||
DONE --> [*]
|
||||
@enduml
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
}, {
|
||||
"rawName" : "States.STATE20",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritancestate.States.STATE20"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.inheritancestate.States.STATEZ",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritancestate.States.STATEZ"
|
||||
}, {
|
||||
"rawName" : "States.STATE2",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritancestate.States.STATE2"
|
||||
@@ -54,9 +51,6 @@
|
||||
}, {
|
||||
"rawName" : "States.STATE18",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritancestate.States.STATE18"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.inheritancestate.States.STATE1",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritancestate.States.STATE1"
|
||||
}, {
|
||||
"rawName" : "States.STATE_EXTRA_1",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritancestate.States.STATE_EXTRA_1"
|
||||
|
||||
@@ -9,12 +9,6 @@
|
||||
}, {
|
||||
"rawName" : "\"PROCESSING\"",
|
||||
"fullIdentifier" : "String.PROCESSING"
|
||||
}, {
|
||||
"rawName" : "String.COMPLETED",
|
||||
"fullIdentifier" : "String.COMPLETED"
|
||||
}, {
|
||||
"rawName" : "String.NEW",
|
||||
"fullIdentifier" : "String.NEW"
|
||||
} ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
{
|
||||
"metadata" : {
|
||||
"triggers" : [ {
|
||||
"event" : "SUBMIT",
|
||||
"className" : "click.kamil.multi.core.OrderController",
|
||||
"methodName" : "submitOrder",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 18
|
||||
} ],
|
||||
"entryPoints" : [ {
|
||||
"type" : "REST",
|
||||
"name" : "POST /orders/submit",
|
||||
"className" : "click.kamil.multi.core.OrderController",
|
||||
"methodName" : "submitOrder",
|
||||
"metadata" : {
|
||||
"path" : "/orders/submit",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "orderId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestBody" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /orders/submit",
|
||||
"className" : "click.kamil.multi.api.OrderApi",
|
||||
"methodName" : "submitOrder",
|
||||
"metadata" : {
|
||||
"path" : "/orders/submit",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "orderId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestBody" ]
|
||||
} ]
|
||||
} ],
|
||||
"callChains" : [ {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /orders/submit",
|
||||
"className" : "click.kamil.multi.core.OrderController",
|
||||
"methodName" : "submitOrder",
|
||||
"metadata" : {
|
||||
"path" : "/orders/submit",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "orderId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestBody" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.multi.core.OrderController.submitOrder" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "SUBMIT",
|
||||
"className" : "click.kamil.multi.core.OrderController",
|
||||
"methodName" : "submitOrder",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 18
|
||||
}
|
||||
} ],
|
||||
"properties" : {
|
||||
"default" : { }
|
||||
}
|
||||
},
|
||||
"name" : "click.kamil.multi.core.OrderStateMachineConfig",
|
||||
"renderChoicesAsDiamonds" : true,
|
||||
"startStates" : [ "NEW" ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "\"NEW\"",
|
||||
"fullIdentifier" : "NEW"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "\"PROCESSING\"",
|
||||
"fullIdentifier" : "PROCESSING"
|
||||
} ],
|
||||
"event" : "SUBMIT",
|
||||
"guard" : null,
|
||||
"actions" : [ {
|
||||
"expression" : "processAction()",
|
||||
"isLambda" : false,
|
||||
"internalLogic" : "@Override default void execute(StateContext<String,String> context){\n System.out.println(\"Processing order: \" + context.getMessageHeader(\"orderId\"));\n}\n",
|
||||
"lineNumber" : 31
|
||||
} ],
|
||||
"order" : null
|
||||
}, {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "\"PROCESSING\"",
|
||||
"fullIdentifier" : "PROCESSING"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "\"COMPLETED\"",
|
||||
"fullIdentifier" : "COMPLETED"
|
||||
} ],
|
||||
"event" : "FINISH",
|
||||
"guard" : null,
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
} ],
|
||||
"endStates" : [ "COMPLETED" ]
|
||||
}
|
||||
@@ -9,12 +9,6 @@
|
||||
}, {
|
||||
"rawName" : "OrderState.SHIPPED",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.order.OrderState.SHIPPED"
|
||||
}, {
|
||||
"rawName" : "click.kamil.enterprise.machines.order.OrderState.SHIPPED",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.order.OrderState.SHIPPED"
|
||||
}, {
|
||||
"rawName" : "click.kamil.enterprise.machines.order.OrderState.NEW",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.order.OrderState.NEW"
|
||||
} ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
@@ -98,7 +92,7 @@
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : "click.kamil.enterprise.machines.order.OrderState.ORDER",
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 81,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
@@ -238,7 +232,7 @@
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : "click.kamil.enterprise.machines.order.OrderState.ORDER",
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 81,
|
||||
"polymorphicEvents" : [ "<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: DocumentEvent.*>", "<SYMBOLIC: UserEvent.*>" ],
|
||||
"external" : true,
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"name" : "click.kamil.examples.statemachine.polymorphic.PolymorphicStateMachineConfiguration",
|
||||
"states" : [ {
|
||||
"rawName" : "click.kamil.examples.statemachine.polymorphic.OrderStates.SUBMITTED",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.polymorphic.OrderStates.SUBMITTED"
|
||||
}, {
|
||||
"rawName" : "OrderStates.CANCELED",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.polymorphic.OrderStates.CANCELED"
|
||||
}, {
|
||||
|
||||
@@ -12,15 +12,6 @@
|
||||
}, {
|
||||
"rawName" : "OrderStates.PAID1",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.simple.OrderStates.PAID1"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.simple.OrderStates.SUBMITTED",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.simple.OrderStates.SUBMITTED"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.simple.OrderStates.FULFILLED",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.simple.OrderStates.FULFILLED"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.simple.OrderStates.CANCELED",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.simple.OrderStates.CANCELED"
|
||||
}, {
|
||||
"rawName" : "OrderStates.SUBMITTED",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.simple.OrderStates.SUBMITTED"
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
"states" : [ {
|
||||
"rawName" : "DocumentState.REJECTED",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.layered.document.DocumentState.REJECTED"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.layered.document.DocumentState.DRAFT",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.layered.document.DocumentState.DRAFT"
|
||||
}, {
|
||||
"rawName" : "DocumentState.DRAFT",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.layered.document.DocumentState.DRAFT"
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
}, {
|
||||
"rawName" : "OrderState.NEW",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.layered.order.OrderState.NEW"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.layered.order.OrderState.NEW",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.layered.order.OrderState.NEW"
|
||||
} ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
"states" : [ {
|
||||
"rawName" : "OrderState.NEW",
|
||||
"fullIdentifier" : "click.kamil.domain.OrderState.NEW"
|
||||
}, {
|
||||
"rawName" : "click.kamil.domain.OrderState.NEW",
|
||||
"fullIdentifier" : "click.kamil.domain.OrderState.NEW"
|
||||
}, {
|
||||
"rawName" : "OrderState.CANCELLED",
|
||||
"fullIdentifier" : "click.kamil.domain.OrderState.CANCELLED"
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
}, {
|
||||
"rawName" : "States.STATE_EXTRA_3",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate2.States.STATE_EXTRA_3"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate2.States.STATE1",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate2.States.STATE1"
|
||||
}, {
|
||||
"rawName" : "States.STATE2",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate2.States.STATE2"
|
||||
@@ -66,9 +63,6 @@
|
||||
}, {
|
||||
"rawName" : "States.STATE_EXTRA_2",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate2.States.STATE_EXTRA_2"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate2.States.STATEZ",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate2.States.STATEZ"
|
||||
}, {
|
||||
"rawName" : "States.STATE1",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate2.States.STATE1"
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"name" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate.TwoStateMachineConfiguration",
|
||||
"states" : [ {
|
||||
"rawName" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate.States.STATE1",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate.States.STATE1"
|
||||
}, {
|
||||
"rawName" : "States.STATE1",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate.States.STATE1"
|
||||
}, {
|
||||
@@ -39,9 +36,6 @@
|
||||
}, {
|
||||
"rawName" : "States.STATE20",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate.States.STATE20"
|
||||
}, {
|
||||
"rawName" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate.States.STATEZ",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate.States.STATEZ"
|
||||
}, {
|
||||
"rawName" : "States.STATE2",
|
||||
"fullIdentifier" : "click.kamil.examples.statemachine.inheritanceextrafunctionsstate.States.STATE2"
|
||||
|
||||
@@ -1,72 +1,59 @@
|
||||
{
|
||||
"name" : "click.kamil.enterprise.machines.user.UserStateMachineConfiguration",
|
||||
"states" : [ {
|
||||
"rawName" : "UserState.GUEST",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.user.UserState.GUEST"
|
||||
}, {
|
||||
"rawName" : "UserState.REGISTERED",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.user.UserState.REGISTERED"
|
||||
}, {
|
||||
"rawName" : "UserState.VERIFIED",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.user.UserState.VERIFIED"
|
||||
} ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "UserState.GUEST",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.user.UserState.GUEST"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "UserState.REGISTERED",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.user.UserState.REGISTERED"
|
||||
} ],
|
||||
"event" : {
|
||||
"rawName" : "UserEvent.REGISTER",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.user.UserEvent.REGISTER"
|
||||
},
|
||||
"guards" : [ ],
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
}, {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "UserState.REGISTERED",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.user.UserState.REGISTERED"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "UserState.VERIFIED",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.user.UserState.VERIFIED"
|
||||
} ],
|
||||
"event" : {
|
||||
"rawName" : "UserEvent.VERIFY",
|
||||
"fullIdentifier" : "click.kamil.enterprise.machines.user.UserEvent.VERIFY"
|
||||
},
|
||||
"guards" : [ ],
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
} ],
|
||||
"startStates" : [ "click.kamil.enterprise.machines.user.UserState.GUEST" ],
|
||||
"endStates" : [ "click.kamil.enterprise.machines.user.UserState.VERIFIED" ],
|
||||
"renderChoicesAsDiamonds" : true,
|
||||
"flows" : [ ],
|
||||
"stateTypeFqn" : "click.kamil.enterprise.machines.user.UserState",
|
||||
"eventTypeFqn" : "click.kamil.enterprise.machines.user.UserEvent",
|
||||
"metadata" : {
|
||||
"triggers" : [ {
|
||||
"event" : "OrderEvent.PAY",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "payOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 30,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "OrderEvent.SHIP",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "shipOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 37,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "DocumentEvent.SUBMIT",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "submitDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 44,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "DocumentEvent.APPROVE",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "approveDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 51,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "DocumentEvent.REJECT",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "rejectDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 58,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "UserEvent.VERIFY",
|
||||
"event" : "click.kamil.enterprise.machines.user.UserEvent.VERIFY",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "verifyUser",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
@@ -79,7 +66,7 @@
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "UserEvent.SUSPEND",
|
||||
"event" : "click.kamil.enterprise.machines.user.UserEvent.SUSPEND",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "suspendUser",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
@@ -98,33 +85,7 @@
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : "ORDER",
|
||||
"lineNumber" : 81,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
"constraint" : "\"ORDER\".equalsIgnoreCase(machineType)",
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "event",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "dispatch",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : "DOCUMENT",
|
||||
"lineNumber" : 87,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
"constraint" : "!(\"ORDER\".equalsIgnoreCase(machineType)) && \"DOCUMENT\".equalsIgnoreCase(machineType)",
|
||||
"ambiguous" : false
|
||||
}, {
|
||||
"event" : "event",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "dispatch",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : "USER",
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 93,
|
||||
"polymorphicEvents" : null,
|
||||
"external" : false,
|
||||
@@ -132,111 +93,6 @@
|
||||
"ambiguous" : false
|
||||
} ],
|
||||
"entryPoints" : [ {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/order/pay",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "payOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/order/pay",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/order/ship",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "shipOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/order/ship",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/document/submit",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "submitDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/document/submit",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/document/approve",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "approveDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/document/approve",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/document/reject",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "rejectDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/document/reject",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/user/verify",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "verifyUser",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/user/verify",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/user/suspend",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "suspendUser",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/user/suspend",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/{machineType}/transition/{event}",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
@@ -261,176 +117,6 @@
|
||||
} ]
|
||||
} ],
|
||||
"callChains" : [ {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/order/pay",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "payOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/order/pay",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.payOrder", "click.kamil.enterprise.web.StateMachineDispatcher.payOrder" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "OrderEvent.PAY",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "payOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 30,
|
||||
"polymorphicEvents" : [ "OrderEvent.PAY" ],
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/order/ship",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "shipOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/order/ship",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.shipOrder", "click.kamil.enterprise.web.StateMachineDispatcher.shipOrder" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "OrderEvent.SHIP",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "shipOrder",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 37,
|
||||
"polymorphicEvents" : [ "OrderEvent.SHIP" ],
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/document/submit",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "submitDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/document/submit",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.submitDocument", "click.kamil.enterprise.web.StateMachineDispatcher.submitDocument" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "DocumentEvent.SUBMIT",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "submitDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 44,
|
||||
"polymorphicEvents" : [ "DocumentEvent.SUBMIT" ],
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/document/approve",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "approveDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/document/approve",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.approveDocument", "click.kamil.enterprise.web.StateMachineDispatcher.approveDocument" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "DocumentEvent.APPROVE",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "approveDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 51,
|
||||
"polymorphicEvents" : [ "DocumentEvent.APPROVE" ],
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/document/reject",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "rejectDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/document/reject",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.rejectDocument", "click.kamil.enterprise.web.StateMachineDispatcher.rejectDocument" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "DocumentEvent.REJECT",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "rejectDocument",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 58,
|
||||
"polymorphicEvents" : [ "DocumentEvent.REJECT" ],
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/user/verify",
|
||||
@@ -449,7 +135,7 @@
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.verifyUser", "click.kamil.enterprise.web.StateMachineDispatcher.verifyUser" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "UserEvent.VERIFY",
|
||||
"event" : "click.kamil.enterprise.machines.user.UserEvent.VERIFY",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "verifyUser",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
@@ -457,16 +143,16 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 65,
|
||||
"polymorphicEvents" : [ "UserEvent.VERIFY" ],
|
||||
"polymorphicEvents" : [ "click.kamil.enterprise.machines.user.UserEvent.VERIFY" ],
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
"sourceState" : "UserState.REGISTERED",
|
||||
"targetState" : "UserState.VERIFIED",
|
||||
"event" : "UserEvent.VERIFY"
|
||||
"sourceState" : "click.kamil.enterprise.machines.user.UserState.REGISTERED",
|
||||
"targetState" : "click.kamil.enterprise.machines.user.UserState.VERIFIED",
|
||||
"event" : "click.kamil.enterprise.machines.user.UserEvent.VERIFY"
|
||||
} ]
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
@@ -487,7 +173,7 @@
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.suspendUser", "click.kamil.enterprise.web.StateMachineDispatcher.suspendUser" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "UserEvent.SUSPEND",
|
||||
"event" : "click.kamil.enterprise.machines.user.UserEvent.SUSPEND",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "suspendUser",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
@@ -495,7 +181,7 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 72,
|
||||
"polymorphicEvents" : [ "UserEvent.SUSPEND" ],
|
||||
"polymorphicEvents" : [ "click.kamil.enterprise.machines.user.UserEvent.SUSPEND" ],
|
||||
"external" : false,
|
||||
"constraint" : null,
|
||||
"ambiguous" : false
|
||||
@@ -535,91 +221,7 @@
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : "ORDER",
|
||||
"lineNumber" : 81,
|
||||
"polymorphicEvents" : [ "<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: DocumentEvent.*>", "<SYMBOLIC: UserEvent.*>" ],
|
||||
"external" : true,
|
||||
"constraint" : "\"ORDER\".equalsIgnoreCase(machineType)",
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/{machineType}/transition/{event}",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "transition",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/{machineType}/transition/{event}",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "machineType",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
}, {
|
||||
"name" : "event",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
}, {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.transition", "click.kamil.enterprise.web.StateMachineDispatcher.dispatch" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "true ? OrderEvent.valueOf(eventString.toUpperCase()) : true ? DocumentEvent.valueOf(eventString.toUpperCase()) : UserEvent.valueOf(eventString.toUpperCase())",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "dispatch",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : "DOCUMENT",
|
||||
"lineNumber" : 87,
|
||||
"polymorphicEvents" : [ "<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: DocumentEvent.*>", "<SYMBOLIC: UserEvent.*>" ],
|
||||
"external" : true,
|
||||
"constraint" : "!(\"ORDER\".equalsIgnoreCase(machineType)) && \"DOCUMENT\".equalsIgnoreCase(machineType)",
|
||||
"ambiguous" : false
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : null
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/machine/{machineType}/transition/{event}",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineController",
|
||||
"methodName" : "transition",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineController.java",
|
||||
"metadata" : {
|
||||
"path" : "/api/machine/{machineType}/transition/{event}",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "machineType",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
}, {
|
||||
"name" : "event",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
}, {
|
||||
"name" : "userId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestParam" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.enterprise.web.StateMachineController.transition", "click.kamil.enterprise.web.StateMachineDispatcher.dispatch" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "true ? OrderEvent.valueOf(eventString.toUpperCase()) : true ? DocumentEvent.valueOf(eventString.toUpperCase()) : UserEvent.valueOf(eventString.toUpperCase())",
|
||||
"className" : "click.kamil.enterprise.web.StateMachineDispatcher",
|
||||
"methodName" : "dispatch",
|
||||
"sourceFile" : "src/main/java/click/kamil/enterprise/web/StateMachineDispatcher.java",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : "USER",
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 93,
|
||||
"polymorphicEvents" : [ "<SYMBOLIC: OrderEvent.*>", "<SYMBOLIC: DocumentEvent.*>", "<SYMBOLIC: UserEvent.*>" ],
|
||||
"external" : true,
|
||||
@@ -632,44 +234,5 @@
|
||||
"properties" : {
|
||||
"default" : { }
|
||||
}
|
||||
},
|
||||
"name" : "click.kamil.enterprise.machines.user.UserStateMachineConfiguration",
|
||||
"renderChoicesAsDiamonds" : true,
|
||||
"startStates" : [ "UserState.GUEST" ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "UserState.GUEST",
|
||||
"fullIdentifier" : "UserState.GUEST"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "UserState.REGISTERED",
|
||||
"fullIdentifier" : "UserState.REGISTERED"
|
||||
} ],
|
||||
"event" : {
|
||||
"rawName" : "UserEvent.REGISTER",
|
||||
"fullIdentifier" : "UserEvent.REGISTER"
|
||||
},
|
||||
"guards" : [ ],
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
}, {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "UserState.REGISTERED",
|
||||
"fullIdentifier" : "UserState.REGISTERED"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "UserState.VERIFIED",
|
||||
"fullIdentifier" : "UserState.VERIFIED"
|
||||
} ],
|
||||
"event" : {
|
||||
"rawName" : "UserEvent.VERIFY",
|
||||
"fullIdentifier" : "UserEvent.VERIFY"
|
||||
},
|
||||
"guards" : [ ],
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
} ],
|
||||
"endStates" : [ "UserState.VERIFIED" ]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,12 @@ skinparam ArrowThickness 1
|
||||
skinparam dpi 110
|
||||
skinparam svgLinkTarget _self
|
||||
|
||||
[*] --> UserState.GUEST
|
||||
[*] --> click.kamil.enterprise.machines.user.UserState.GUEST
|
||||
|
||||
|
||||
UserState.GUEST -[#1E90FF,bold]-> UserState.REGISTERED <<external>> : UserEvent.REGISTER
|
||||
UserState.REGISTERED -[#1E90FF,bold]-> UserState.VERIFIED <<external>> : UserEvent.VERIFY
|
||||
|
||||
UserState.VERIFIED --> [*]
|
||||
click.kamil.enterprise.machines.user.UserState.VERIFIED --> [*]
|
||||
@enduml
|
||||
|
||||
|
||||
@@ -8,5 +8,9 @@
|
||||
</state>
|
||||
<state id="VERIFIED">
|
||||
</state>
|
||||
<state id="GUEST">
|
||||
</state>
|
||||
<state id="VERIFIED">
|
||||
</state>
|
||||
</scxml>
|
||||
|
||||
|
||||
@@ -65,6 +65,9 @@ public class HtmlExporterCommand implements Callable<Integer> {
|
||||
@Option(names = {"--include-generated-sources"}, description = "Scan existing Maven target/ and Gradle build/ trees for pre-generated .java sources. Does not run Maven or Gradle.", negatable = true, defaultValue = "true", fallbackValue = "true")
|
||||
private boolean includeGeneratedSources = true;
|
||||
|
||||
@Option(names = {"--inline-accessors"}, description = "Index trivial JavaBean/record accessors at scan time and inline them during call-chain and constant resolution.", negatable = true, defaultValue = "true", fallbackValue = "true")
|
||||
private boolean inlineAccessors = true;
|
||||
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
var out = spec.commandLine().getOut();
|
||||
@@ -100,7 +103,7 @@ public class HtmlExporterCommand implements Callable<Integer> {
|
||||
exportService.runJsonExporter(jsonFile, outputDir, formats, profiles, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, inputDir);
|
||||
} else {
|
||||
boolean renderChoicesAsDiamonds = !noDiamonds;
|
||||
exportService.runExporter(inputDir, outputDir, formats, renderChoicesAsDiamonds, profiles, flowsFile, machineFilter, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, includeGeneratedSources);
|
||||
exportService.runExporter(inputDir, outputDir, formats, renderChoicesAsDiamonds, profiles, flowsFile, machineFilter, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, click.kamil.springstatemachineexporter.exporter.EnumFormat.fn, includeGeneratedSources, inlineAccessors);
|
||||
}
|
||||
System.clearProperty("html.hideMetadataPane");
|
||||
|
||||
|
||||
@@ -356,19 +356,35 @@
|
||||
});
|
||||
}
|
||||
|
||||
function isLifecycleChain(c) {
|
||||
const ev = c.triggerPoint && c.triggerPoint.event;
|
||||
return ev && ev.startsWith('[LIFECYCLE:');
|
||||
}
|
||||
|
||||
function formatLifecycleLabel(event) {
|
||||
const m = event && event.match(/^\[LIFECYCLE:(\w+)\]$/);
|
||||
if (!m) return event;
|
||||
const type = m[1].toUpperCase();
|
||||
if (type === 'RESTORE') return 'Restore persisted state';
|
||||
if (type === 'RESET') return 'Reset state machine';
|
||||
return 'Lifecycle: ' + m[1].toLowerCase();
|
||||
}
|
||||
|
||||
function chainsForEntryPoint(ep) {
|
||||
return (metadata.metadata.callChains || []).filter(c =>
|
||||
c.entryPoint.className === ep.className &&
|
||||
c.entryPoint.methodName === ep.methodName &&
|
||||
((c.matchedTransitions && c.matchedTransitions.length > 0) || isLifecycleChain(c))
|
||||
);
|
||||
}
|
||||
|
||||
function populateSidebar() {
|
||||
const list = document.getElementById('ep-list');
|
||||
if (!list) return;
|
||||
const entryPoints = metadata.metadata.entryPoints || [];
|
||||
|
||||
entryPoints.forEach(ep => {
|
||||
// Quality Filter: Hide if it doesn't trigger a machine event
|
||||
const chains = (metadata.metadata.callChains || []).filter(c =>
|
||||
c.entryPoint.className === ep.className &&
|
||||
c.entryPoint.methodName === ep.methodName &&
|
||||
c.matchedTransitions && c.matchedTransitions.length > 0
|
||||
);
|
||||
|
||||
const chains = chainsForEntryPoint(ep);
|
||||
if (chains.length === 0) return;
|
||||
|
||||
const card = document.createElement('div');
|
||||
@@ -381,18 +397,24 @@
|
||||
${sourceHint}
|
||||
`;
|
||||
|
||||
// Add tags for matched events
|
||||
let triggersHtml = '<div class="ep-transitions" style="margin-top: 8px;">Triggers: ';
|
||||
const addedEvents = new Set();
|
||||
chains.forEach(c => {
|
||||
c.matchedTransitions.forEach(t => {
|
||||
const evName = t.event || "";
|
||||
if (!addedEvents.has(evName)) {
|
||||
addedEvents.add(evName);
|
||||
triggersHtml += `<span class="badge transition" style="font-size: 0.65rem; padding: 2px 6px;">${evName}</span> `;
|
||||
}
|
||||
let triggersHtml = '<div class="ep-transitions" style="margin-top: 8px;">';
|
||||
const lifecycleChain = chains.find(isLifecycleChain);
|
||||
if (lifecycleChain) {
|
||||
const label = formatLifecycleLabel(lifecycleChain.triggerPoint.event);
|
||||
triggersHtml += `<span class="badge lifecycle" style="font-size: 0.65rem; padding: 2px 6px; background:#ecfdf5; color:#047857; border:1px solid #a7f3d0;">${label}</span>`;
|
||||
} else {
|
||||
triggersHtml += 'Triggers: ';
|
||||
const addedEvents = new Set();
|
||||
chains.forEach(c => {
|
||||
c.matchedTransitions.forEach(t => {
|
||||
const evName = t.event || "";
|
||||
if (!addedEvents.has(evName)) {
|
||||
addedEvents.add(evName);
|
||||
triggersHtml += `<span class="badge transition" style="font-size: 0.65rem; padding: 2px 6px;">${evName}</span> `;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
triggersHtml += '</div>';
|
||||
card.innerHTML += triggersHtml;
|
||||
|
||||
@@ -427,20 +449,18 @@
|
||||
|
||||
function highlightEntryPoint(ep) {
|
||||
clearHighlights();
|
||||
const chains = metadata.metadata.callChains || [];
|
||||
const chains = chainsForEntryPoint(ep);
|
||||
|
||||
const seen = new Set();
|
||||
let steps = [];
|
||||
chains.forEach(c => {
|
||||
if (c.entryPoint.className === ep.className && c.entryPoint.methodName === ep.methodName) {
|
||||
if (c.matchedTransitions && c.matchedTransitions.length > 0) {
|
||||
c.matchedTransitions.forEach(t => {
|
||||
const key = buildLinkKey(t.sourceState, t.event);
|
||||
if (!key || seen.has(key)) return;
|
||||
seen.add(key);
|
||||
steps.push({ event: t.event, source: t.sourceState, ambiguous: c.triggerPoint && c.triggerPoint.ambiguous });
|
||||
});
|
||||
}
|
||||
if (c.matchedTransitions && c.matchedTransitions.length > 0) {
|
||||
c.matchedTransitions.forEach(t => {
|
||||
const key = buildLinkKey(t.sourceState, t.event);
|
||||
if (!key || seen.has(key)) return;
|
||||
seen.add(key);
|
||||
steps.push({ event: t.event, source: t.sourceState, ambiguous: c.triggerPoint && c.triggerPoint.ambiguous });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -667,11 +687,14 @@
|
||||
if (chains && chains.length > 0) {
|
||||
html += `<div style="font-size:0.7rem; font-weight:800; color:#94a3b8; text-transform:uppercase; margin-bottom:10px; border-top:1px solid #eee; padding-top:10px">Triggered by</div>`;
|
||||
chains.forEach(c => {
|
||||
const triggerEvent = c.triggerPoint && c.triggerPoint.event;
|
||||
const lifecycleLabel = triggerEvent && triggerEvent.startsWith('[LIFECYCLE:')
|
||||
? formatLifecycleLabel(triggerEvent) : null;
|
||||
const triggerInfo = c.triggerPoint.sourceFile ? ` <span style="color:#94a3b8; font-weight:400; font-size:0.6rem;">(${c.triggerPoint.sourceFile}:${c.triggerPoint.lineNumber})</span>` : '';
|
||||
const ambiguousLabel = c.triggerPoint.ambiguous ? `<div style="display:inline-block; margin-top:4px; padding:2px 6px; background:#fff7ed; color:#ea580c; border:1px solid #fed7aa; border-radius:4px; font-size:0.6rem; font-weight:700;">⚠️ Ambiguous Resolution (Over-approximated)</div>` : '';
|
||||
html += `<div style="margin-bottom:20px">
|
||||
<div style="font-weight:700; font-size:0.85rem; color:var(--text);">${c.entryPoint.name}</div>
|
||||
<div style="font-size:0.65rem; color:#64748b; font-family:monospace; margin-top:2px;">Trigger: ${c.triggerPoint.methodName}${triggerInfo}</div>
|
||||
<div style="font-size:0.65rem; color:#64748b; font-family:monospace; margin-top:2px;">${lifecycleLabel ? 'Lifecycle: ' + lifecycleLabel : 'Trigger: ' + c.triggerPoint.methodName}${triggerInfo}</div>
|
||||
${ambiguousLabel}
|
||||
${renderParameters(c.entryPoint.parameters)}
|
||||
<div style="margin-left:10px; border-left:2px solid #e2e8f0; padding-left:12px; margin-top:10px">
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package click.kamil.springstatemachineexporter.html;
|
||||
|
||||
import click.kamil.springstatemachineexporter.html.exporter.HtmlExporter;
|
||||
import click.kamil.springstatemachineexporter.service.ExportService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class ExtendedStateMachineLifecycleHtmlTest {
|
||||
|
||||
private static final Path EXTENDED_GOLDEN_JSON = Path.of(
|
||||
"../state_machine_exporter/src/test/resources/golden/ExtendedStateMachineConfig/ExtendedStateMachineConfig.json")
|
||||
.toAbsolutePath()
|
||||
.normalize();
|
||||
|
||||
@Test
|
||||
void shouldShowLifecycleRestoreEndpointsInSidebar(@TempDir Path tempDir) throws Exception {
|
||||
assertThat(EXTENDED_GOLDEN_JSON).exists();
|
||||
|
||||
ExportService exportService = new ExportService(List.of(new HtmlExporter()));
|
||||
exportService.runJsonExporter(EXTENDED_GOLDEN_JSON, tempDir, List.of("html"), List.of());
|
||||
|
||||
Path machineDir = tempDir.resolve("click.kamil.examples.statemachine.extended.config.ExtendedStateMachineConfig");
|
||||
Path htmlFile = machineDir.resolve("click.kamil.examples.statemachine.extended.config.ExtendedStateMachineConfig.html");
|
||||
assertThat(htmlFile).exists();
|
||||
|
||||
String html = Files.readString(htmlFile);
|
||||
assertThat(html).contains("function formatLifecycleLabel");
|
||||
assertThat(html).contains("POST /api/orders/resume");
|
||||
assertThat(html).contains("Restore persisted state");
|
||||
assertThat(html).contains("[LIFECYCLE:RESTORE]");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user