v2.4 extended analysis render update
This commit is contained in:
@@ -15,7 +15,7 @@ public class PropertyEnricher implements AnalysisEnricher {
|
||||
public void enrich(AnalysisResult result, CodebaseContext context, CodebaseIntelligenceProvider intelligence) {
|
||||
log.info("Enriching {} with properties", result.getName());
|
||||
|
||||
Map<String, String> properties = intelligence.resolveProperties();
|
||||
Map<String, Map<String, String>> properties = intelligence.resolveProperties();
|
||||
|
||||
result.addMetadata(CodebaseMetadata.builder()
|
||||
.properties(properties)
|
||||
|
||||
@@ -6,31 +6,96 @@ import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
|
||||
import click.kamil.springstatemachineexporter.model.Transition;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
@Setter
|
||||
@Builder(toBuilder = true)
|
||||
@Jacksonized
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class AnalysisResult {
|
||||
private final String name;
|
||||
private String name;
|
||||
@Builder.Default
|
||||
private final Set<click.kamil.springstatemachineexporter.model.State> states = java.util.Collections.emptySet();
|
||||
private final List<Transition> transitions;
|
||||
private final Set<String> startStates;
|
||||
private final Set<String> endStates;
|
||||
private final boolean renderChoicesAsDiamonds;
|
||||
private Set<click.kamil.springstatemachineexporter.model.State> states = java.util.Collections.emptySet();
|
||||
private List<Transition> transitions;
|
||||
private Set<String> startStates;
|
||||
private Set<String> endStates;
|
||||
private boolean renderChoicesAsDiamonds;
|
||||
|
||||
@Builder.Default
|
||||
private final List<BusinessFlow> flows = java.util.Collections.emptyList();
|
||||
private List<BusinessFlow> flows = java.util.Collections.emptyList();
|
||||
|
||||
@Builder.Default
|
||||
private CodebaseMetadata metadata = CodebaseMetadata.empty();
|
||||
|
||||
public void applyResolution(Map<String, String> properties) {
|
||||
var resolver = new click.kamil.springstatemachineexporter.analysis.resolver.PropertyResolver();
|
||||
|
||||
// 1. Resolve start/end states strings
|
||||
if (startStates != null) {
|
||||
this.startStates = startStates.stream()
|
||||
.map(s -> resolver.resolveValue(s, properties))
|
||||
.collect(java.util.stream.Collectors.toSet());
|
||||
}
|
||||
if (endStates != null) {
|
||||
this.endStates = endStates.stream()
|
||||
.map(s -> resolver.resolveValue(s, properties))
|
||||
.collect(java.util.stream.Collectors.toSet());
|
||||
}
|
||||
|
||||
// 2. Resolve states (State record is immutable, so recreate)
|
||||
if (states != null) {
|
||||
this.states = states.stream()
|
||||
.map(s -> click.kamil.springstatemachineexporter.model.State.of(s.rawName(), resolver.resolveValue(s.fullIdentifier(), properties)))
|
||||
.collect(java.util.stream.Collectors.toSet());
|
||||
}
|
||||
|
||||
// 3. Resolve transitions
|
||||
if (transitions != null) {
|
||||
for (var t : transitions) {
|
||||
if (t.getEvent() != null) {
|
||||
t.setEvent(resolver.resolveValue(t.getEvent(), properties));
|
||||
}
|
||||
// Resolve states within transitions
|
||||
t.setSourceStates(t.getSourceStates().stream()
|
||||
.map(s -> click.kamil.springstatemachineexporter.model.State.of(s.rawName(), resolver.resolveValue(s.fullIdentifier(), properties)))
|
||||
.collect(java.util.stream.Collectors.toList()));
|
||||
t.setTargetStates(t.getTargetStates().stream()
|
||||
.map(s -> click.kamil.springstatemachineexporter.model.State.of(s.rawName(), resolver.resolveValue(s.fullIdentifier(), properties)))
|
||||
.collect(java.util.stream.Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Resolve metadata (triggers, entry points)
|
||||
if (metadata != null) {
|
||||
if (metadata.getTriggers() != null) {
|
||||
for (var trigger : metadata.getTriggers()) {
|
||||
if (trigger.getEvent() != null) {
|
||||
trigger.setEvent(resolver.resolveValue(trigger.getEvent(), properties));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (metadata.getEntryPoints() != null) {
|
||||
for (var ep : metadata.getEntryPoints()) {
|
||||
if (ep.getName() != null) {
|
||||
ep.setName(resolver.resolveValue(ep.getName(), properties));
|
||||
}
|
||||
if (ep.getMetadata() != null) {
|
||||
for (var entry : ep.getMetadata().entrySet()) {
|
||||
entry.setValue(resolver.resolveValue(entry.getValue(), properties));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addMetadata(CodebaseMetadata newMetadata) {
|
||||
if (newMetadata == null) return;
|
||||
|
||||
@@ -43,7 +108,7 @@ public class AnalysisResult {
|
||||
List<CallChain> combinedCallChains = new java.util.ArrayList<>(this.metadata.getCallChains());
|
||||
if (newMetadata.getCallChains() != null) combinedCallChains.addAll(newMetadata.getCallChains());
|
||||
|
||||
java.util.Map<String, String> combinedProperties = new java.util.HashMap<>(this.metadata.getProperties());
|
||||
java.util.Map<String, java.util.Map<String, String>> combinedProperties = new java.util.HashMap<>(this.metadata.getProperties());
|
||||
if (newMetadata.getProperties() != null) combinedProperties.putAll(newMetadata.getProperties());
|
||||
|
||||
this.metadata = CodebaseMetadata.builder()
|
||||
|
||||
@@ -22,7 +22,7 @@ public class CodebaseMetadata {
|
||||
@Builder.Default
|
||||
private final List<CallChain> callChains = Collections.emptyList();
|
||||
@Builder.Default
|
||||
private final Map<String, String> properties = Collections.emptyMap();
|
||||
private final Map<String, Map<String, String>> properties = Collections.emptyMap();
|
||||
|
||||
public static CodebaseMetadata empty() {
|
||||
return CodebaseMetadata.builder().build();
|
||||
|
||||
@@ -26,10 +26,10 @@ public class EntryPoint {
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
private final String name; // e.g., "POST /orders" or "Kafka Topic: orders"
|
||||
private String name; // e.g., "POST /orders" or "Kafka Topic: orders"
|
||||
private final String className;
|
||||
private final String methodName;
|
||||
private final Map<String, String> metadata; // e.g., path, topic, exchange
|
||||
private Map<String, String> metadata; // e.g., path, topic, exchange
|
||||
@Builder.Default
|
||||
private final List<Parameter> parameters = java.util.Collections.emptyList();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.util.Map;
|
||||
@Jacksonized
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class TriggerPoint {
|
||||
private final String event;
|
||||
private String event;
|
||||
private final String className;
|
||||
private final String methodName;
|
||||
private final String sourceModule;
|
||||
|
||||
@@ -42,6 +42,37 @@ public class PropertyResolver {
|
||||
return allProperties;
|
||||
}
|
||||
|
||||
public Map<String, Map<String, String>> resolveAllProperties(Path rootDir) {
|
||||
Map<String, Map<String, String>> profiles = new HashMap<>();
|
||||
|
||||
// 1. Load default profile
|
||||
Map<String, String> defaultProps = new HashMap<>();
|
||||
loadMatching(rootDir, "application.properties", defaultProps);
|
||||
loadMatching(rootDir, "application.yml", defaultProps);
|
||||
loadMatching(rootDir, "application.yaml", defaultProps);
|
||||
profiles.put("default", defaultProps);
|
||||
|
||||
// 2. Discover and load all other profiles
|
||||
try (Stream<Path> paths = Files.walk(rootDir)) {
|
||||
paths.filter(Files::isRegularFile)
|
||||
.filter(p -> {
|
||||
String name = p.getFileName().toString();
|
||||
return name.startsWith("application-") &&
|
||||
(name.endsWith(".properties") || name.endsWith(".yml") || name.endsWith(".yaml"));
|
||||
})
|
||||
.forEach(p -> {
|
||||
String name = p.getFileName().toString();
|
||||
String profile = name.substring("application-".length(), name.lastIndexOf('.'));
|
||||
Map<String, String> loaded = p.toString().endsWith(".properties") ? loadProperties(p) : loadYaml(p);
|
||||
profiles.computeIfAbsent(profile, k -> new HashMap<>()).putAll(loaded);
|
||||
});
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to scan for profiles in {}", rootDir, e);
|
||||
}
|
||||
|
||||
return profiles;
|
||||
}
|
||||
|
||||
private void loadMatching(Path rootDir, String fileName, Map<String, String> target) {
|
||||
try (Stream<Path> paths = Files.walk(rootDir)) {
|
||||
paths.filter(p -> Files.isRegularFile(p))
|
||||
|
||||
@@ -24,7 +24,7 @@ public interface CodebaseIntelligenceProvider {
|
||||
List<CallChain> findCallChains(List<EntryPoint> entryPoints, List<TriggerPoint> triggers);
|
||||
|
||||
/**
|
||||
* Resolves configuration properties.
|
||||
* Resolves configuration properties per profile.
|
||||
*/
|
||||
Map<String, String> resolveProperties();
|
||||
Map<String, Map<String, String>> resolveProperties();
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public class JdtIntelligenceProvider implements CodebaseIntelligenceProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> resolveProperties() {
|
||||
public Map<String, Map<String, String>> resolveProperties() {
|
||||
return context.getProperties();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public class CodebaseContext {
|
||||
private final Set<String> ambiguousSimpleNames = new HashSet<>();
|
||||
private final ConstantResolver constantResolver = new ConstantResolver();
|
||||
private final PropertyResolver propertyResolver = new PropertyResolver();
|
||||
private Map<String, String> properties = new HashMap<>();
|
||||
private Map<String, Map<String, String>> allProperties = new HashMap<>();
|
||||
private List<LibraryHint> libraryHints = new ArrayList<>();
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@@ -48,8 +48,8 @@ public class CodebaseContext {
|
||||
private String[] sourcepath = new String[0];
|
||||
private boolean resolveBindings = false;
|
||||
|
||||
public Map<String, String> getProperties() {
|
||||
return Collections.unmodifiableMap(properties);
|
||||
public Map<String, Map<String, String>> getProperties() {
|
||||
return Collections.unmodifiableMap(allProperties);
|
||||
}
|
||||
|
||||
public void setClasspath(List<String> classpath) {
|
||||
@@ -77,7 +77,7 @@ public class CodebaseContext {
|
||||
private final Map<String, List<String>> interfaceToImpls = new HashMap<>();
|
||||
|
||||
public void scan(Path rootDir, Set<String> customIgnorePatterns) throws IOException {
|
||||
this.properties = propertyResolver.resolveProperties(rootDir);
|
||||
this.allProperties = propertyResolver.resolveAllProperties(rootDir);
|
||||
|
||||
Set<String> activeIgnore = new HashSet<>(ignorePatterns);
|
||||
activeIgnore.addAll(customIgnorePatterns);
|
||||
@@ -179,8 +179,7 @@ public class CodebaseContext {
|
||||
if (mod instanceof Annotation ann) {
|
||||
String name = ann.getTypeName().getFullyQualifiedName();
|
||||
if (name.endsWith("Value")) {
|
||||
String placeholder = extractAnnotationValue(ann);
|
||||
return propertyResolver.resolveValue(placeholder, properties);
|
||||
return extractAnnotationValue(ann);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,9 @@ public class ExporterCommand implements Callable<Integer> {
|
||||
@Option(names = {"--no-diamonds"}, description = "Render choice/junction states as regular states instead of diamonds.", negatable = true, defaultValue = "true")
|
||||
private boolean renderChoicesAsDiamonds;
|
||||
|
||||
@Option(names = {"-p", "--profiles"}, description = "Spring profiles to activate for property resolution.", split = ",")
|
||||
private List<String> activeProfiles;
|
||||
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
var out = spec.commandLine().getOut();
|
||||
@@ -66,10 +69,11 @@ public class ExporterCommand implements Callable<Integer> {
|
||||
}
|
||||
|
||||
try {
|
||||
List<String> profiles = activeProfiles != null ? activeProfiles : java.util.Collections.emptyList();
|
||||
if (jsonFile != null) {
|
||||
exportService.runJsonExporter(jsonFile, outputDir, selectedFormats);
|
||||
exportService.runJsonExporter(jsonFile, outputDir, selectedFormats, profiles);
|
||||
} else {
|
||||
exportService.runExporter(inputDir, outputDir, selectedFormats, renderChoicesAsDiamonds);
|
||||
exportService.runExporter(inputDir, outputDir, selectedFormats, renderChoicesAsDiamonds, profiles);
|
||||
}
|
||||
out.println(CommandLine.Help.Ansi.AUTO.string("%n@|bold,green SUCCESS:|@ All state machines have been exported to " + outputDir.toAbsolutePath()));
|
||||
return 0;
|
||||
|
||||
@@ -97,12 +97,40 @@ public class ExportService {
|
||||
}
|
||||
|
||||
public void runJsonExporter(Path jsonFile, Path outputDir, List<String> selectedFormats) throws IOException {
|
||||
runJsonExporter(jsonFile, outputDir, selectedFormats, Collections.emptyList());
|
||||
}
|
||||
|
||||
public void runJsonExporter(Path jsonFile, Path outputDir, List<String> selectedFormats, List<String> activeProfiles) throws IOException {
|
||||
JsonImportService jsonImportService = new JsonImportService();
|
||||
AnalysisResult result = jsonImportService.importAnalysisResult(jsonFile);
|
||||
|
||||
// Apply property resolution pass if placeholders exist
|
||||
resolveProperties(result, activeProfiles);
|
||||
|
||||
generateOutputs(outputDir, result, selectedFormats);
|
||||
}
|
||||
|
||||
private void resolveProperties(AnalysisResult result, List<String> activeProfiles) {
|
||||
Map<String, Map<String, String>> allProps = result.getMetadata().getProperties();
|
||||
if (allProps == null || allProps.isEmpty()) return;
|
||||
|
||||
// 1. Merge properties based on active profiles
|
||||
Map<String, String> merged = new HashMap<>();
|
||||
// Start with default
|
||||
if (allProps.containsKey("default")) {
|
||||
merged.putAll(allProps.get("default"));
|
||||
}
|
||||
// Overlay active profiles
|
||||
for (String profile : activeProfiles) {
|
||||
if (allProps.containsKey(profile)) {
|
||||
merged.putAll(allProps.get(profile));
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Delegate to result for resolution
|
||||
result.applyResolution(merged);
|
||||
}
|
||||
|
||||
private void exportAll(CodebaseContext context, CodebaseIntelligenceProvider intelligence, Path outputDir, List<String> selectedFormats, boolean renderChoicesAsDiamonds, List<BusinessFlow> flows, String machineFilter) throws IOException {
|
||||
Set<String> processedLocations = new HashSet<>();
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package click.kamil.springstatemachineexporter.service;
|
||||
|
||||
import click.kamil.springstatemachineexporter.analysis.model.AnalysisResult;
|
||||
import click.kamil.springstatemachineexporter.model.State;
|
||||
import click.kamil.springstatemachineexporter.model.Transition;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class DeferredProfileResolutionTest {
|
||||
|
||||
@Test
|
||||
void shouldResolvePlaceholdersWithDefaultProfile() {
|
||||
// Given
|
||||
AnalysisResult result = createMockResult();
|
||||
Map<String, String> properties = Map.of("app.state.initial", "START_RESOLVED");
|
||||
|
||||
// When
|
||||
result.applyResolution(properties);
|
||||
|
||||
// Then
|
||||
assertThat(result.getStartStates()).containsExactly("START_RESOLVED");
|
||||
|
||||
Transition t = result.getTransitions().get(0);
|
||||
assertThat(t.getEvent()).isEqualTo("RESOLVED_EVENT");
|
||||
assertThat(t.getSourceStates().get(0).fullIdentifier()).isEqualTo("START_RESOLVED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveWithFallback() {
|
||||
// Given
|
||||
AnalysisResult result = AnalysisResult.builder()
|
||||
.name("TestSM")
|
||||
.startStates(Set.of("${missing.key:FALLBACK}"))
|
||||
.transitions(Collections.emptyList())
|
||||
.build();
|
||||
|
||||
// When
|
||||
result.applyResolution(Collections.emptyMap());
|
||||
|
||||
// Then
|
||||
assertThat(result.getStartStates()).containsExactly("FALLBACK");
|
||||
}
|
||||
|
||||
private AnalysisResult createMockResult() {
|
||||
String placeholder = "${app.state.initial}";
|
||||
State s1 = State.of("START", placeholder);
|
||||
State s2 = State.of("END", "END");
|
||||
|
||||
Transition t1 = new Transition();
|
||||
t1.setEvent("${app.event.name:RESOLVED_EVENT}");
|
||||
t1.setSourceStates(List.of(s1));
|
||||
t1.setTargetStates(List.of(s2));
|
||||
|
||||
return AnalysisResult.builder()
|
||||
.name("MockSM")
|
||||
.startStates(Set.of(placeholder))
|
||||
.endStates(Set.of("END"))
|
||||
.states(Set.of(s1, s2))
|
||||
.transitions(List.of(t1))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,387 @@
|
||||
{
|
||||
"metadata" : {
|
||||
"triggers" : [ {
|
||||
"event" : "AUDIT_EVENT",
|
||||
"className" : "click.kamil.examples.enterprise.api.SecurityInterceptor",
|
||||
"methodName" : "preHandle",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 17
|
||||
}, {
|
||||
"event" : "PLACE_ORDER",
|
||||
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
|
||||
"methodName" : "place",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 16
|
||||
}, {
|
||||
"event" : "CANCEL_ORDER",
|
||||
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
|
||||
"methodName" : "cancel",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 21
|
||||
}, {
|
||||
"event" : "PAY_ORDER",
|
||||
"className" : "click.kamil.examples.enterprise.service.ReactivePaymentService",
|
||||
"methodName" : "processPayment",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 18
|
||||
}, {
|
||||
"event" : "SHIP_ORDER",
|
||||
"className" : "click.kamil.examples.enterprise.messaging.ShippingJmsListener",
|
||||
"methodName" : "onShippingReady",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 17
|
||||
}, {
|
||||
"event" : "RETURN_ORDER",
|
||||
"className" : "click.kamil.examples.enterprise.messaging.ReturnsRabbitListener",
|
||||
"methodName" : "onReturn",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 17
|
||||
} ],
|
||||
"entryPoints" : [ {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/enterprise/orders/place",
|
||||
"className" : "click.kamil.examples.enterprise.api.OrderController",
|
||||
"methodName" : "placeOrder",
|
||||
"metadata" : {
|
||||
"path" : "/api/enterprise/orders/place",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/enterprise/orders/{id}/cancel",
|
||||
"className" : "click.kamil.examples.enterprise.api.OrderController",
|
||||
"methodName" : "cancelOrder",
|
||||
"metadata" : {
|
||||
"path" : "/api/enterprise/orders/{id}/cancel",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "id",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "CUSTOM",
|
||||
"name" : "INTERCEPTOR: SecurityInterceptor.preHandle",
|
||||
"className" : "click.kamil.examples.enterprise.api.SecurityInterceptor",
|
||||
"methodName" : "preHandle",
|
||||
"metadata" : {
|
||||
"interceptorType" : "Spring MVC Interceptor"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
}, {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/enterprise/payments",
|
||||
"className" : "click.kamil.examples.enterprise.api.ReactivePaymentController",
|
||||
"methodName" : "pay",
|
||||
"metadata" : {
|
||||
"path" : "/api/enterprise/payments",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "orderId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestBody" ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "JMS",
|
||||
"name" : "JMS: shipping.queue",
|
||||
"className" : "click.kamil.examples.enterprise.messaging.ShippingJmsListener",
|
||||
"methodName" : "onShippingReady",
|
||||
"metadata" : {
|
||||
"protocol" : "JMS",
|
||||
"destination" : "shipping.queue"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "orderId",
|
||||
"type" : "String",
|
||||
"annotations" : [ ]
|
||||
} ]
|
||||
}, {
|
||||
"type" : "RABBIT",
|
||||
"name" : "RABBIT: returns.queue",
|
||||
"className" : "click.kamil.examples.enterprise.messaging.ReturnsRabbitListener",
|
||||
"methodName" : "onReturn",
|
||||
"metadata" : {
|
||||
"protocol" : "RABBIT",
|
||||
"destination" : "returns.queue"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "orderId",
|
||||
"type" : "String",
|
||||
"annotations" : [ ]
|
||||
} ]
|
||||
} ],
|
||||
"callChains" : [ {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/enterprise/orders/place",
|
||||
"className" : "click.kamil.examples.enterprise.api.OrderController",
|
||||
"methodName" : "placeOrder",
|
||||
"metadata" : {
|
||||
"path" : "/api/enterprise/orders/place",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.enterprise.api.OrderController.placeOrder", "click.kamil.examples.enterprise.service.OrderServiceImpl.place" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "PLACE_ORDER",
|
||||
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
|
||||
"methodName" : "place",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 16
|
||||
}
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/enterprise/orders/{id}/cancel",
|
||||
"className" : "click.kamil.examples.enterprise.api.OrderController",
|
||||
"methodName" : "cancelOrder",
|
||||
"metadata" : {
|
||||
"path" : "/api/enterprise/orders/{id}/cancel",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "id",
|
||||
"type" : "String",
|
||||
"annotations" : [ "PathVariable" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.enterprise.api.OrderController.cancelOrder", "click.kamil.examples.enterprise.service.OrderServiceImpl.cancel" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "CANCEL_ORDER",
|
||||
"className" : "click.kamil.examples.enterprise.service.OrderServiceImpl",
|
||||
"methodName" : "cancel",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 21
|
||||
}
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "CUSTOM",
|
||||
"name" : "INTERCEPTOR: SecurityInterceptor.preHandle",
|
||||
"className" : "click.kamil.examples.enterprise.api.SecurityInterceptor",
|
||||
"methodName" : "preHandle",
|
||||
"metadata" : {
|
||||
"interceptorType" : "Spring MVC Interceptor"
|
||||
},
|
||||
"parameters" : [ ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.enterprise.api.SecurityInterceptor.preHandle" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "AUDIT_EVENT",
|
||||
"className" : "click.kamil.examples.enterprise.api.SecurityInterceptor",
|
||||
"methodName" : "preHandle",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 17
|
||||
}
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "REST",
|
||||
"name" : "POST /api/enterprise/payments",
|
||||
"className" : "click.kamil.examples.enterprise.api.ReactivePaymentController",
|
||||
"methodName" : "pay",
|
||||
"metadata" : {
|
||||
"path" : "/api/enterprise/payments",
|
||||
"verb" : "POST"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "orderId",
|
||||
"type" : "String",
|
||||
"annotations" : [ "RequestBody" ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.enterprise.api.ReactivePaymentController.pay", "click.kamil.examples.enterprise.service.ReactivePaymentService.processPayment" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "PAY_ORDER",
|
||||
"className" : "click.kamil.examples.enterprise.service.ReactivePaymentService",
|
||||
"methodName" : "processPayment",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 18
|
||||
}
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "JMS",
|
||||
"name" : "JMS: shipping.queue",
|
||||
"className" : "click.kamil.examples.enterprise.messaging.ShippingJmsListener",
|
||||
"methodName" : "onShippingReady",
|
||||
"metadata" : {
|
||||
"protocol" : "JMS",
|
||||
"destination" : "shipping.queue"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "orderId",
|
||||
"type" : "String",
|
||||
"annotations" : [ ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.enterprise.messaging.ShippingJmsListener.onShippingReady" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "SHIP_ORDER",
|
||||
"className" : "click.kamil.examples.enterprise.messaging.ShippingJmsListener",
|
||||
"methodName" : "onShippingReady",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 17
|
||||
}
|
||||
}, {
|
||||
"entryPoint" : {
|
||||
"type" : "RABBIT",
|
||||
"name" : "RABBIT: returns.queue",
|
||||
"className" : "click.kamil.examples.enterprise.messaging.ReturnsRabbitListener",
|
||||
"methodName" : "onReturn",
|
||||
"metadata" : {
|
||||
"protocol" : "RABBIT",
|
||||
"destination" : "returns.queue"
|
||||
},
|
||||
"parameters" : [ {
|
||||
"name" : "orderId",
|
||||
"type" : "String",
|
||||
"annotations" : [ ]
|
||||
} ]
|
||||
},
|
||||
"methodChain" : [ "click.kamil.examples.enterprise.messaging.ReturnsRabbitListener.onReturn" ],
|
||||
"triggerPoint" : {
|
||||
"event" : "RETURN_ORDER",
|
||||
"className" : "click.kamil.examples.enterprise.messaging.ReturnsRabbitListener",
|
||||
"methodName" : "onReturn",
|
||||
"sourceModule" : null,
|
||||
"stateMachineId" : null,
|
||||
"lineNumber" : 17
|
||||
}
|
||||
} ],
|
||||
"properties" : { }
|
||||
},
|
||||
"name" : "click.kamil.examples.enterprise.config.EnterpriseStateMachineConfig",
|
||||
"renderChoicesAsDiamonds" : false,
|
||||
"startStates" : [ "NEW" ],
|
||||
"transitions" : [ {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "\"NEW\"",
|
||||
"fullIdentifier" : "NEW"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "\"CHECK_AVAILABILITY\"",
|
||||
"fullIdentifier" : "CHECK_AVAILABILITY"
|
||||
} ],
|
||||
"event" : "PLACE_ORDER",
|
||||
"guard" : null,
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
}, {
|
||||
"type" : "CHOICE",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "\"CHECK_AVAILABILITY\"",
|
||||
"fullIdentifier" : "CHECK_AVAILABILITY"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "\"PENDING_PAYMENT\"",
|
||||
"fullIdentifier" : "PENDING_PAYMENT"
|
||||
} ],
|
||||
"event" : null,
|
||||
"guard" : {
|
||||
"expression" : "(c) -> true",
|
||||
"isLambda" : true,
|
||||
"internalLogic" : null
|
||||
},
|
||||
"actions" : [ ],
|
||||
"order" : 0
|
||||
}, {
|
||||
"type" : "CHOICE",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "\"CHECK_AVAILABILITY\"",
|
||||
"fullIdentifier" : "CHECK_AVAILABILITY"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "\"CANCELLED\"",
|
||||
"fullIdentifier" : "CANCELLED"
|
||||
} ],
|
||||
"event" : null,
|
||||
"guard" : null,
|
||||
"actions" : [ ],
|
||||
"order" : 1
|
||||
}, {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "\"PENDING_PAYMENT\"",
|
||||
"fullIdentifier" : "PENDING_PAYMENT"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "\"PAID\"",
|
||||
"fullIdentifier" : "PAID"
|
||||
} ],
|
||||
"event" : "PAY_ORDER",
|
||||
"guard" : null,
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
}, {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "\"PAID\"",
|
||||
"fullIdentifier" : "PAID"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "\"SHIPPED\"",
|
||||
"fullIdentifier" : "SHIPPED"
|
||||
} ],
|
||||
"event" : "SHIP_ORDER",
|
||||
"guard" : null,
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
}, {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "\"SHIPPED\"",
|
||||
"fullIdentifier" : "SHIPPED"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "\"DELIVERED\"",
|
||||
"fullIdentifier" : "DELIVERED"
|
||||
} ],
|
||||
"event" : "FINALIZE",
|
||||
"guard" : null,
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
}, {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "\"PAID\"",
|
||||
"fullIdentifier" : "PAID"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "\"CANCELLED\"",
|
||||
"fullIdentifier" : "CANCELLED"
|
||||
} ],
|
||||
"event" : "CANCEL_ORDER",
|
||||
"guard" : null,
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
}, {
|
||||
"type" : "EXTERNAL",
|
||||
"sourceStates" : [ {
|
||||
"rawName" : "\"DELIVERED\"",
|
||||
"fullIdentifier" : "DELIVERED"
|
||||
} ],
|
||||
"targetStates" : [ {
|
||||
"rawName" : "\"RETURNED\"",
|
||||
"fullIdentifier" : "RETURNED"
|
||||
} ],
|
||||
"event" : "RETURN_ORDER",
|
||||
"guard" : null,
|
||||
"actions" : [ ],
|
||||
"order" : null
|
||||
} ],
|
||||
"endStates" : [ "CANCELLED", "DELIVERED", "RETURNED" ]
|
||||
}
|
||||
@@ -35,6 +35,9 @@ public class HtmlExporterCommand implements Callable<Integer> {
|
||||
@Option(names = {"-i", "--input"}, description = "Input directory containing the Spring Boot project source code.")
|
||||
private Path inputDir;
|
||||
|
||||
@Option(names = {"-j", "--json"}, description = "Input JSON file containing the state machine definition.")
|
||||
private Path jsonFile;
|
||||
|
||||
@Option(names = {"-o", "--output"}, description = "Output directory for the generated portal.", defaultValue = "./out_html")
|
||||
private Path outputDir;
|
||||
|
||||
@@ -58,29 +61,29 @@ public class HtmlExporterCommand implements Callable<Integer> {
|
||||
var out = spec.commandLine().getOut();
|
||||
var err = spec.commandLine().getErr();
|
||||
|
||||
if (inputDir == null) {
|
||||
if (inputDir == null && jsonFile == null) {
|
||||
inputDir = Path.of(".");
|
||||
}
|
||||
|
||||
out.println(CommandLine.Help.Ansi.AUTO.string("@|bold,red Initializing Interactive Portal Generation...|@"));
|
||||
out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Input Directory:|@ " + inputDir.toAbsolutePath()));
|
||||
if (jsonFile != null) {
|
||||
out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Input JSON File:|@ " + jsonFile.toAbsolutePath()));
|
||||
} else {
|
||||
out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Input Directory:|@ " + inputDir.toAbsolutePath()));
|
||||
}
|
||||
out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Output Directory:|@ " + outputDir.toAbsolutePath()));
|
||||
if (flowsFile != null) {
|
||||
out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Custom Flows:|@ " + flowsFile.toAbsolutePath()));
|
||||
}
|
||||
if (machineFilter != null) {
|
||||
out.println(CommandLine.Help.Ansi.AUTO.string("@|yellow Machine Filter:|@ " + machineFilter));
|
||||
}
|
||||
|
||||
try {
|
||||
// Force HTML and JSON formats for the interactive portal
|
||||
List<String> formats = List.of("html", "json");
|
||||
List<String> profiles = activeProfiles != null ? activeProfiles : java.util.Collections.emptyList();
|
||||
|
||||
boolean renderChoicesAsDiamonds = !noDiamonds;
|
||||
|
||||
exportService.runExporter(inputDir, outputDir, formats, renderChoicesAsDiamonds,
|
||||
activeProfiles != null ? activeProfiles : java.util.Collections.emptyList(),
|
||||
flowsFile, machineFilter);
|
||||
if (jsonFile != null) {
|
||||
exportService.runJsonExporter(jsonFile, outputDir, formats, profiles);
|
||||
} else {
|
||||
boolean renderChoicesAsDiamonds = !noDiamonds;
|
||||
exportService.runExporter(inputDir, outputDir, formats, renderChoicesAsDiamonds, profiles, flowsFile, machineFilter);
|
||||
}
|
||||
|
||||
out.println(CommandLine.Help.Ansi.AUTO.string("%n@|bold,green SUCCESS:|@ Interactive documentation generated successfully."));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user