Scan source context before JSON property resolution

Property placeholders on JSON re-export now qualify through applyResolution with CodebaseContext so ambiguous enum names fail closed instead of guessing without bindings.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-14 06:26:42 +02:00
parent 8c4f15133e
commit be1ac91beb
2 changed files with 51 additions and 2 deletions

View File

@@ -167,8 +167,6 @@ public class ExportService {
JsonImportService jsonImportService = new JsonImportService(); JsonImportService jsonImportService = new JsonImportService();
AnalysisResult result = jsonImportService.importAnalysisResult(jsonFile); AnalysisResult result = jsonImportService.importAnalysisResult(jsonFile);
resolveProperties(result, activeProfiles, null);
CodebaseContext context = null; CodebaseContext context = null;
Path sourceRoot = optionalSourceDir; Path sourceRoot = optionalSourceDir;
if (sourceRoot == null) { if (sourceRoot == null) {
@@ -187,6 +185,8 @@ public class ExportService {
log.info("JSON re-export: no source project found; using embedded machine types if present"); log.info("JSON re-export: no source project found; using embedded machine types if present");
} }
resolveProperties(result, activeProfiles, context);
StateMachineTypeResolver.MachineTypes machineTypes = StateMachineTypeResolver.MachineTypes machineTypes =
click.kamil.springstatemachineexporter.analysis.service.JsonExportContextFactory.resolveMachineTypes( click.kamil.springstatemachineexporter.analysis.service.JsonExportContextFactory.resolveMachineTypes(
result.getName(), result.getName(),

View File

@@ -98,6 +98,55 @@ class JsonRoundTripCanonicalizationTest {
.isEqualTo("com.example.order.OrderEvent.PAY"); .isEqualTo("com.example.order.OrderEvent.PAY");
} }
@Test
void shouldResolvePropertyPlaceholdersOnJsonReExportWithSourceContext(@TempDir Path tempDir) throws Exception {
writeSampleProject(tempDir);
TriggerPoint trigger = TriggerPoint.builder()
.event("${app.event}")
.sourceState("${app.state}")
.className("com.example.web.OrderController")
.methodName("pay")
.sourceFile("OrderController.java")
.polymorphicEvents(List.of("${app.event}"))
.eventTypeFqn("com.example.order.OrderEvent")
.stateTypeFqn("com.example.order.OrderState")
.build();
AnalysisResult shortForm = AnalysisResult.builder()
.name("com.example.config.OrderStateMachineConfiguration")
.stateTypeFqn("com.example.order.OrderState")
.eventTypeFqn("com.example.order.OrderEvent")
.transitions(List.of(shortTransition(
"OrderEvent.PAY",
"OrderState.NEW",
"OrderState.PAID")))
.metadata(CodebaseMetadata.builder()
.triggers(List.of(trigger))
.properties(Map.of("default", Map.of(
"app.event", "OrderEvent.PAY",
"app.state", "OrderState.NEW")))
.build())
.build();
Path jsonFile = tempDir.resolve("machine.json");
Files.writeString(jsonFile, new JsonExporter().export(shortForm, ExportOptions.builder().build()));
Path outputDir = tempDir.resolve("out");
ExportService exportService = new ExportService(List.of(new JsonExporter()));
exportService.runJsonExporter(jsonFile, outputDir, List.of("json"));
AnalysisResult roundTripped = new JsonImportService().importAnalysisResult(
outputDir.resolve("com.example.config.OrderStateMachineConfiguration")
.resolve("com.example.config.OrderStateMachineConfiguration.json"));
TriggerPoint resolvedTrigger = roundTripped.getMetadata().getTriggers().get(0);
assertThat(resolvedTrigger.getEvent()).isEqualTo("com.example.order.OrderEvent.PAY");
assertThat(resolvedTrigger.getSourceState()).isEqualTo("com.example.order.OrderState.NEW");
assertThat(resolvedTrigger.getPolymorphicEvents())
.containsExactly("com.example.order.OrderEvent.PAY");
}
@Test @Test
void shouldRelinkMatchedTransitionsAfterPreCanonicalizingShortFormJson(@TempDir Path tempDir) throws Exception { void shouldRelinkMatchedTransitionsAfterPreCanonicalizingShortFormJson(@TempDir Path tempDir) throws Exception {
writeSampleProject(tempDir); writeSampleProject(tempDir);