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()