Make JSON a self-contained analysis interchange for downstream exporters.

Persist machine types, finalize on JSON import, resolve @Value placeholders, and canonicalize String states so HTML and other consumers can import JSON without source re-analysis.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-12 05:33:27 +02:00
parent 0aca0aade9
commit 4f13c9449c
45 changed files with 2349 additions and 849 deletions

View File

@@ -32,7 +32,7 @@ public class HtmlExporterCommand implements Callable<Integer> {
@CommandLine.Spec
CommandLine.Model.CommandSpec spec;
@Option(names = {"-i", "--input"}, description = "Input directory containing the Spring Boot project source code.")
@Option(names = {"-i", "--input"}, description = "Input directory containing the Spring Boot project source code. Optional with -j when JSON already contains machine types and canonical identifiers.")
private Path inputDir;
@Option(names = {"-j", "--json"}, description = "Input JSON file containing the state machine definition.")
@@ -97,7 +97,7 @@ public class HtmlExporterCommand implements Callable<Integer> {
}
if (jsonFile != null) {
exportService.runJsonExporter(jsonFile, outputDir, formats, profiles);
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);

View File

@@ -0,0 +1,46 @@
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;
/**
* Verifies that a finalized JSON export alone is sufficient for the HTML portal —
* no source directory required when machine types and canonical identifiers are present.
*/
class JsonToHtmlInterchangeTest {
private static final Path ORDER_GOLDEN_JSON = Path.of(
"../state_machine_exporter/src/test/resources/golden/OrderStateMachineConfiguration/OrderStateMachineConfiguration.json")
.toAbsolutePath()
.normalize();
@Test
void shouldGenerateInteractiveHtmlFromJsonOnly(@TempDir Path tempDir) throws Exception {
assertThat(ORDER_GOLDEN_JSON).exists();
ExportService exportService = new ExportService(List.of(new HtmlExporter()));
exportService.runJsonExporter(ORDER_GOLDEN_JSON, tempDir, List.of("html"), List.of());
Path machineDir = tempDir.resolve("click.kamil.enterprise.machines.order.OrderStateMachineConfiguration");
Path htmlFile = machineDir.resolve("click.kamil.enterprise.machines.order.OrderStateMachineConfiguration.html");
assertThat(htmlFile).exists();
String html = Files.readString(htmlFile);
assertThat(html).contains("<!DOCTYPE html>");
assertThat(html).contains("click.kamil.enterprise.machines.order.OrderStateMachineConfiguration");
// SVG link IDs derive from fn-formatted enum labels; matchedTransitions use package-canonical values.
assertThat(html).contains("OrderState_NEW__OrderEvent_PAY");
assertThat(html).contains("click.kamil.enterprise.machines.order.OrderEvent.PAY");
assertThat(html).contains("POST /api/machine/order/pay");
assertThat(html).contains("\"stateTypeFqn\" : \"click.kamil.enterprise.machines.order.OrderState\"");
}
}