diff --git a/state_machine_exporter_html/src/main/resources/html/template.html b/state_machine_exporter_html/src/main/resources/html/template.html index b6e1c11..8c9b617 100644 --- a/state_machine_exporter_html/src/main/resources/html/template.html +++ b/state_machine_exporter_html/src/main/resources/html/template.html @@ -394,10 +394,20 @@ return isLifecycleChain(c) || isHighlightableChain(c) || isUnresolvedExternalChain(c); } + function entryPointsMatch(chainEntryPoint, sidebarEntryPoint) { + if (!chainEntryPoint || !sidebarEntryPoint) { + return false; + } + if (chainEntryPoint.name && sidebarEntryPoint.name) { + return chainEntryPoint.name === sidebarEntryPoint.name; + } + return chainEntryPoint.className === sidebarEntryPoint.className + && chainEntryPoint.methodName === sidebarEntryPoint.methodName; + } + function chainsForEntryPoint(ep) { return (metadata.metadata.callChains || []).filter(c => - c.entryPoint.className === ep.className && - c.entryPoint.methodName === ep.methodName && + entryPointsMatch(c.entryPoint, ep) && isSidebarChain(c) ); } diff --git a/state_machine_exporter_html/src/test/java/click/kamil/springstatemachineexporter/html/EnterpriseDedicatedEndpointHtmlTest.java b/state_machine_exporter_html/src/test/java/click/kamil/springstatemachineexporter/html/EnterpriseDedicatedEndpointHtmlTest.java index 7b5514a..2509007 100644 --- a/state_machine_exporter_html/src/test/java/click/kamil/springstatemachineexporter/html/EnterpriseDedicatedEndpointHtmlTest.java +++ b/state_machine_exporter_html/src/test/java/click/kamil/springstatemachineexporter/html/EnterpriseDedicatedEndpointHtmlTest.java @@ -13,16 +13,16 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; /** - * Enterprise dedicated REST endpoints must produce HTML explorer link keys end-to-end; - * generic {@code {event}} dispatchers remain unresolved external. + * Enterprise dedicated and expanded generic REST endpoints must produce HTML explorer link keys end-to-end. */ class EnterpriseDedicatedEndpointHtmlTest { private static final String PAY_ENDPOINT = "POST /api/machine/order/pay"; - private static final String GENERIC_ENDPOINT = "POST /api/machine/{machineType}/transition/{event}"; + private static final String ORDER_PAY = "POST /api/machine/ORDER/transition/PAY"; + private static final String ORDER_SHIP = "POST /api/machine/ORDER/transition/SHIP"; @Test - void htmlShouldExposeLinkKeysForDedicatedPayEndpoint(@TempDir Path tempDir) throws Exception { + void htmlShouldExposeLinkKeysForDedicatedAndExpandedOrderEndpoints(@TempDir Path tempDir) throws Exception { Path enterprise = findProjectRoot().resolve("state_machines/state_machine_enterprise"); ExportService exportService = new ExportService(List.of(new HtmlExporter(), new JsonExporter())); exportService.runExporter(enterprise, tempDir, List.of("html"), true, List.of(), null, null, @@ -33,11 +33,14 @@ class EnterpriseDedicatedEndpointHtmlTest { String html = Files.readString(htmlFile); assertThat(html).contains(PAY_ENDPOINT); + assertThat(html).contains(ORDER_PAY); + assertThat(html).contains(ORDER_SHIP); + assertThat(html).contains("function entryPointsMatch(chainEntryPoint, sidebarEntryPoint)"); assertThat(html).contains("\"linkResolution\" : \"RESOLVED\""); - assertThat(html).contains("#link_"); - assertThat(html).contains("\"linkKey\""); - assertThat(html).contains(GENERIC_ENDPOINT); - assertThat(html).contains("linkResolution === 'UNRESOLVED_EXTERNAL'"); + assertThat(html).contains("#link_OrderState_NEW__OrderEvent_PAY"); + assertThat(html).contains("#link_OrderState_PENDING__OrderEvent_SHIP"); + assertThat(html).contains("\"linkKey\" : \"OrderState_NEW__OrderEvent_PAY\""); + assertThat(html).contains("\"linkKey\" : \"OrderState_PENDING__OrderEvent_SHIP\""); } private static Path findOrderMachineHtml(Path outputDir) throws Exception { diff --git a/state_machine_exporter_html/src/test/java/click/kamil/springstatemachineexporter/html/HtmlTransitionLinkConsistencyTest.java b/state_machine_exporter_html/src/test/java/click/kamil/springstatemachineexporter/html/HtmlTransitionLinkConsistencyTest.java index d1d04e0..4586882 100644 --- a/state_machine_exporter_html/src/test/java/click/kamil/springstatemachineexporter/html/HtmlTransitionLinkConsistencyTest.java +++ b/state_machine_exporter_html/src/test/java/click/kamil/springstatemachineexporter/html/HtmlTransitionLinkConsistencyTest.java @@ -410,6 +410,78 @@ class HtmlTransitionLinkConsistencyTest { assertThat(html).contains("#link_OrderState_NEW__OrderEvent_PAY"); } + @Test + void htmlShouldMatchCallChainsByEntryPointNameWhenControllerMethodIsShared() { + EntryPoint payTransition = EntryPoint.builder() + .type(EntryPoint.Type.REST) + .name("POST /api/machine/ORDER/transition/PAY") + .className("com.example.web.StateMachineController") + .methodName("transition") + .build(); + EntryPoint shipTransition = EntryPoint.builder() + .type(EntryPoint.Type.REST) + .name("POST /api/machine/ORDER/transition/SHIP") + .className("com.example.web.StateMachineController") + .methodName("transition") + .build(); + + CallChain payChain = CallChain.builder() + .entryPoint(payTransition) + .linkResolution(LinkResolution.RESOLVED) + .triggerPoint(TriggerPoint.builder() + .event("com.example.order.OrderEvent.PAY") + .build()) + .matchedTransitions(List.of(MatchedTransition.builder() + .sourceState("com.example.order.OrderState.NEW") + .targetState("com.example.order.OrderState.PAID") + .event("com.example.order.OrderEvent.PAY") + .linkKey("OrderState_NEW__OrderEvent_PAY") + .build())) + .build(); + CallChain shipChain = CallChain.builder() + .entryPoint(shipTransition) + .linkResolution(LinkResolution.RESOLVED) + .triggerPoint(TriggerPoint.builder() + .event("com.example.order.OrderEvent.SHIP") + .build()) + .matchedTransitions(List.of(MatchedTransition.builder() + .sourceState("com.example.order.OrderState.PAID") + .targetState("com.example.order.OrderState.SHIPPED") + .event("com.example.order.OrderEvent.SHIP") + .linkKey("OrderState_PAID__OrderEvent_SHIP") + .build())) + .build(); + + AnalysisResult result = AnalysisResult.builder() + .name("com.example.config.OrderStateMachineConfiguration") + .stateTypeFqn("com.example.order.OrderState") + .eventTypeFqn("com.example.order.OrderEvent") + .transitions(List.of( + shortTransition( + "com.example.order.OrderEvent.PAY", + "com.example.order.OrderState.NEW", + "com.example.order.OrderState.PAID"), + shortTransition( + "com.example.order.OrderEvent.SHIP", + "com.example.order.OrderState.PAID", + "com.example.order.OrderState.SHIPPED"))) + .startStates(Set.of("com.example.order.OrderState.NEW")) + .endStates(Set.of("com.example.order.OrderState.SHIPPED")) + .metadata(CodebaseMetadata.builder() + .entryPoints(List.of(payTransition, shipTransition)) + .callChains(List.of(payChain, shipChain)) + .build()) + .build(); + + String html = new HtmlExporter().export(result, ExportOptions.builder().includeMetadataPane(true).build()); + + assertThat(html).contains("function entryPointsMatch(chainEntryPoint, sidebarEntryPoint)"); + assertThat(html).contains("POST /api/machine/ORDER/transition/PAY"); + assertThat(html).contains("POST /api/machine/ORDER/transition/SHIP"); + assertThat(html).contains("\"linkKey\" : \"OrderState_NEW__OrderEvent_PAY\""); + assertThat(html).contains("\"linkKey\" : \"OrderState_PAID__OrderEvent_SHIP\""); + } + @Test void ambiguousWidenHtmlShouldExposeMatchedTransitionLinkKeys() { EntryPoint ambiguousEp = EntryPoint.builder()