From 45e9f2c215900775febaa01d65640ba6374aea53 Mon Sep 17 00:00:00 2001 From: Nick Reuter Date: Sat, 14 Mar 2026 11:42:42 -0400 Subject: [PATCH 1/5] Rename ActionType to EventType for upcoming ephemeral event support The enum and field are renamed across BaseEvent, all event subclasses, services, and tests to better describe non-CRUD event types that will be added later (TIMER_START, FOCUS, etc.). The serialized JSON field changes from "actionType" to "eventType". --- .../{ActionType.java => EventType.java} | 2 +- .../notification/event/ActionItemEvent.java | 6 +++--- .../retroapi/notification/event/BaseEvent.java | 8 ++++---- .../notification/event/RetroFinishedEvent.java | 6 +++--- .../retroapi/notification/event/ThoughtEvent.java | 6 +++--- .../io/nickreuter/retroapi/retro/RetroService.java | 4 ++-- .../retroapi/retro/thought/ThoughtService.java | 14 +++++++------- .../team/actionitem/ActionItemService.java | 12 ++++++------ .../WebsocketNotificationServiceTest.java | 4 ++-- .../retroapi/retro/RetroServiceTest.java | 4 ++-- .../retroapi/retro/thought/ThoughtServiceTest.java | 14 +++++++------- .../team/actionitem/ActionItemServiceTest.java | 12 ++++++------ 12 files changed, 46 insertions(+), 46 deletions(-) rename src/main/java/io/nickreuter/retroapi/notification/{ActionType.java => EventType.java} (76%) diff --git a/src/main/java/io/nickreuter/retroapi/notification/ActionType.java b/src/main/java/io/nickreuter/retroapi/notification/EventType.java similarity index 76% rename from src/main/java/io/nickreuter/retroapi/notification/ActionType.java rename to src/main/java/io/nickreuter/retroapi/notification/EventType.java index 9cacde2..9fee3f3 100644 --- a/src/main/java/io/nickreuter/retroapi/notification/ActionType.java +++ b/src/main/java/io/nickreuter/retroapi/notification/EventType.java @@ -1,6 +1,6 @@ package io.nickreuter.retroapi.notification; -public enum ActionType { +public enum EventType { CREATE, UPDATE, DELETE diff --git a/src/main/java/io/nickreuter/retroapi/notification/event/ActionItemEvent.java b/src/main/java/io/nickreuter/retroapi/notification/event/ActionItemEvent.java index 5bc333d..f31ff39 100644 --- a/src/main/java/io/nickreuter/retroapi/notification/event/ActionItemEvent.java +++ b/src/main/java/io/nickreuter/retroapi/notification/event/ActionItemEvent.java @@ -1,6 +1,6 @@ package io.nickreuter.retroapi.notification.event; -import io.nickreuter.retroapi.notification.ActionType; +import io.nickreuter.retroapi.notification.EventType; import io.nickreuter.retroapi.team.actionitem.ActionItemEntity; import java.util.UUID; @@ -9,8 +9,8 @@ public class ActionItemEvent extends BaseEvent{ private static final String ROUTE_STRING = "/topic/%s.action-items"; private final UUID teamId; - public ActionItemEvent(Object source, ActionType actionType, ActionItemEntity payload, UUID teamId) { - super(source, actionType, payload); + public ActionItemEvent(Object source, EventType eventType, ActionItemEntity payload, UUID teamId) { + super(source, eventType, payload); this.teamId = teamId; } diff --git a/src/main/java/io/nickreuter/retroapi/notification/event/BaseEvent.java b/src/main/java/io/nickreuter/retroapi/notification/event/BaseEvent.java index e36bdb3..8bc1a7b 100644 --- a/src/main/java/io/nickreuter/retroapi/notification/event/BaseEvent.java +++ b/src/main/java/io/nickreuter/retroapi/notification/event/BaseEvent.java @@ -1,17 +1,17 @@ package io.nickreuter.retroapi.notification.event; -import io.nickreuter.retroapi.notification.ActionType; +import io.nickreuter.retroapi.notification.EventType; import lombok.Getter; import org.springframework.context.ApplicationEvent; @Getter public abstract class BaseEvent extends ApplicationEvent { - private final ActionType actionType; + private final EventType eventType; private final Object payload; - public BaseEvent(Object source, ActionType actionType, Object payload) { + public BaseEvent(Object source, EventType eventType, Object payload) { super(source); - this.actionType = actionType; + this.eventType = eventType; this.payload = payload; } diff --git a/src/main/java/io/nickreuter/retroapi/notification/event/RetroFinishedEvent.java b/src/main/java/io/nickreuter/retroapi/notification/event/RetroFinishedEvent.java index 2b05e88..9040366 100644 --- a/src/main/java/io/nickreuter/retroapi/notification/event/RetroFinishedEvent.java +++ b/src/main/java/io/nickreuter/retroapi/notification/event/RetroFinishedEvent.java @@ -1,6 +1,6 @@ package io.nickreuter.retroapi.notification.event; -import io.nickreuter.retroapi.notification.ActionType; +import io.nickreuter.retroapi.notification.EventType; import java.util.UUID; @@ -8,8 +8,8 @@ public class RetroFinishedEvent extends BaseEvent { private static final String ROUTE_STRING = "/topic/%s.finished"; private final UUID retroId; - public RetroFinishedEvent(Object source, ActionType actionType, boolean isFinished, UUID retroId) { - super(source, actionType, isFinished); + public RetroFinishedEvent(Object source, EventType eventType, boolean isFinished, UUID retroId) { + super(source, eventType, isFinished); this.retroId = retroId; } diff --git a/src/main/java/io/nickreuter/retroapi/notification/event/ThoughtEvent.java b/src/main/java/io/nickreuter/retroapi/notification/event/ThoughtEvent.java index ccf3c73..96d1788 100644 --- a/src/main/java/io/nickreuter/retroapi/notification/event/ThoughtEvent.java +++ b/src/main/java/io/nickreuter/retroapi/notification/event/ThoughtEvent.java @@ -1,6 +1,6 @@ package io.nickreuter.retroapi.notification.event; -import io.nickreuter.retroapi.notification.ActionType; +import io.nickreuter.retroapi.notification.EventType; import io.nickreuter.retroapi.retro.thought.ThoughtEntity; import java.util.UUID; @@ -9,8 +9,8 @@ public class ThoughtEvent extends BaseEvent { private static final String ROUTE_STRING = "/topic/%s.thoughts"; private final UUID retroId; - public ThoughtEvent(Object source, ActionType actionType, ThoughtEntity payload, UUID retroId) { - super(source, actionType, payload); + public ThoughtEvent(Object source, EventType eventType, ThoughtEntity payload, UUID retroId) { + super(source, eventType, payload); this.retroId = retroId; } diff --git a/src/main/java/io/nickreuter/retroapi/retro/RetroService.java b/src/main/java/io/nickreuter/retroapi/retro/RetroService.java index c7e7d8a..561b888 100644 --- a/src/main/java/io/nickreuter/retroapi/retro/RetroService.java +++ b/src/main/java/io/nickreuter/retroapi/retro/RetroService.java @@ -1,6 +1,6 @@ package io.nickreuter.retroapi.retro; -import io.nickreuter.retroapi.notification.ActionType; +import io.nickreuter.retroapi.notification.EventType; import io.nickreuter.retroapi.notification.event.RetroFinishedEvent; import io.nickreuter.retroapi.retro.template.Template; import org.springframework.context.ApplicationEventPublisher; @@ -53,6 +53,6 @@ public void setFinished(UUID retroId, boolean finished) throws RetroNotFoundExce var retro = retroRepository.findById(retroId).orElseThrow(RetroNotFoundException::new); retro.setFinished(finished); retroRepository.save(retro); - applicationEventPublisher.publishEvent(new RetroFinishedEvent(this, ActionType.UPDATE, finished, retroId)); + applicationEventPublisher.publishEvent(new RetroFinishedEvent(this, EventType.UPDATE, finished, retroId)); } } diff --git a/src/main/java/io/nickreuter/retroapi/retro/thought/ThoughtService.java b/src/main/java/io/nickreuter/retroapi/retro/thought/ThoughtService.java index 30e52b8..36c8e3f 100644 --- a/src/main/java/io/nickreuter/retroapi/retro/thought/ThoughtService.java +++ b/src/main/java/io/nickreuter/retroapi/retro/thought/ThoughtService.java @@ -1,6 +1,6 @@ package io.nickreuter.retroapi.retro.thought; -import io.nickreuter.retroapi.notification.ActionType; +import io.nickreuter.retroapi.notification.EventType; import io.nickreuter.retroapi.notification.event.ThoughtEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; @@ -21,7 +21,7 @@ public ThoughtService(ThoughtRepository thoughtRepository, ApplicationEventPubli public ThoughtEntity createThought(UUID retroId, String message, String category) { var savedThought = thoughtRepository.save(ThoughtEntity.from(message, category, retroId)); - applicationEventPublisher.publishEvent(new ThoughtEvent(this, ActionType.CREATE, savedThought, retroId)); + applicationEventPublisher.publishEvent(new ThoughtEvent(this, EventType.CREATE, savedThought, retroId)); return savedThought; } @@ -32,34 +32,34 @@ public Optional getThought(UUID thoughtId) { public void addVote(UUID thoughtId) { thoughtRepository.incrementVotes(thoughtId); var thought = thoughtRepository.findById(thoughtId).orElseThrow(); - applicationEventPublisher.publishEvent(new ThoughtEvent(this, ActionType.UPDATE, thought, thought.getRetroId())); + applicationEventPublisher.publishEvent(new ThoughtEvent(this, EventType.UPDATE, thought, thought.getRetroId())); } public void setCompleted(UUID thoughtId, boolean completed) { var thought = thoughtRepository.findById(thoughtId).orElseThrow(); thought.setCompleted(completed); var updatedThought = thoughtRepository.save(thought); - applicationEventPublisher.publishEvent(new ThoughtEvent(this, ActionType.UPDATE, updatedThought, updatedThought.getRetroId())); + applicationEventPublisher.publishEvent(new ThoughtEvent(this, EventType.UPDATE, updatedThought, updatedThought.getRetroId())); } public void setCategory(UUID thoughtId, String category) { var thought = thoughtRepository.findById(thoughtId).orElseThrow(); thought.setCategory(category); var updatedThought = thoughtRepository.save(thought); - applicationEventPublisher.publishEvent(new ThoughtEvent(this, ActionType.UPDATE, updatedThought, updatedThought.getRetroId())); + applicationEventPublisher.publishEvent(new ThoughtEvent(this, EventType.UPDATE, updatedThought, updatedThought.getRetroId())); } public void setMessage(UUID thoughtId, String message) { var thought = thoughtRepository.findById(thoughtId).orElseThrow(); thought.setMessage(message); var updatedThought = thoughtRepository.save(thought); - applicationEventPublisher.publishEvent(new ThoughtEvent(this, ActionType.UPDATE, updatedThought, updatedThought.getRetroId())); + applicationEventPublisher.publishEvent(new ThoughtEvent(this, EventType.UPDATE, updatedThought, updatedThought.getRetroId())); } public void deleteThought(UUID thoughtId) { var thought = thoughtRepository.findById(thoughtId).orElseThrow(); thoughtRepository.deleteById(thoughtId); - applicationEventPublisher.publishEvent(new ThoughtEvent(this, ActionType.DELETE, thought, thought.getRetroId())); + applicationEventPublisher.publishEvent(new ThoughtEvent(this, EventType.DELETE, thought, thought.getRetroId())); } public List getThoughtsForRetro(UUID retroId) { diff --git a/src/main/java/io/nickreuter/retroapi/team/actionitem/ActionItemService.java b/src/main/java/io/nickreuter/retroapi/team/actionitem/ActionItemService.java index d08c053..b8aad55 100644 --- a/src/main/java/io/nickreuter/retroapi/team/actionitem/ActionItemService.java +++ b/src/main/java/io/nickreuter/retroapi/team/actionitem/ActionItemService.java @@ -1,6 +1,6 @@ package io.nickreuter.retroapi.team.actionitem; -import io.nickreuter.retroapi.notification.ActionType; +import io.nickreuter.retroapi.notification.EventType; import io.nickreuter.retroapi.notification.event.ActionItemEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; @@ -25,7 +25,7 @@ public List getActionItemsForTeam(UUID teamId) { public ActionItemEntity createActionItem(String action, String assignee, UUID teamId) { var actionItem = actionItemRepository.save(ActionItemEntity.from(action, assignee, teamId)); - applicationEventPublisher.publishEvent(new ActionItemEvent(this, ActionType.CREATE, actionItem, teamId)); + applicationEventPublisher.publishEvent(new ActionItemEvent(this, EventType.CREATE, actionItem, teamId)); return actionItem; } @@ -33,27 +33,27 @@ public void setAction(UUID actionItemId, String action) { var actionItem = actionItemRepository.findById(actionItemId).orElseThrow(); actionItem.setAction(action); var updatedActionItem = actionItemRepository.save(actionItem); - applicationEventPublisher.publishEvent(new ActionItemEvent(this, ActionType.UPDATE, updatedActionItem, updatedActionItem.getTeamId())); + applicationEventPublisher.publishEvent(new ActionItemEvent(this, EventType.UPDATE, updatedActionItem, updatedActionItem.getTeamId())); } public void setAssignee(UUID actionItemId, String assignee) { var actionItem = actionItemRepository.findById(actionItemId).orElseThrow(); actionItem.setAssignee(assignee); var updatedActionItem = actionItemRepository.save(actionItem); - applicationEventPublisher.publishEvent(new ActionItemEvent(this, ActionType.UPDATE, updatedActionItem, updatedActionItem.getTeamId())); + applicationEventPublisher.publishEvent(new ActionItemEvent(this, EventType.UPDATE, updatedActionItem, updatedActionItem.getTeamId())); } public void setCompleted(UUID actionItemId, boolean completed) { var actionItem = actionItemRepository.findById(actionItemId).orElseThrow(); actionItem.setCompleted(completed); var updatedActionItem = actionItemRepository.save(actionItem); - applicationEventPublisher.publishEvent(new ActionItemEvent(this, ActionType.UPDATE, updatedActionItem, updatedActionItem.getTeamId())); + applicationEventPublisher.publishEvent(new ActionItemEvent(this, EventType.UPDATE, updatedActionItem, updatedActionItem.getTeamId())); } public void deleteActionItem(UUID actionItemId) { var actionItem = actionItemRepository.findById(actionItemId).orElseThrow(); actionItemRepository.delete(actionItem); - applicationEventPublisher.publishEvent(new ActionItemEvent(this, ActionType.DELETE, actionItem, actionItem.getTeamId())); + applicationEventPublisher.publishEvent(new ActionItemEvent(this, EventType.DELETE, actionItem, actionItem.getTeamId())); } public Optional getActionItem(UUID actionItemId) { diff --git a/src/test/java/io/nickreuter/retroapi/notification/WebsocketNotificationServiceTest.java b/src/test/java/io/nickreuter/retroapi/notification/WebsocketNotificationServiceTest.java index ed9bb6b..3f9f114 100644 --- a/src/test/java/io/nickreuter/retroapi/notification/WebsocketNotificationServiceTest.java +++ b/src/test/java/io/nickreuter/retroapi/notification/WebsocketNotificationServiceTest.java @@ -18,7 +18,7 @@ class WebsocketNotificationServiceTest { @Test void onApplicationEvent_WhenEventEmitted_SendMessageToWebSocket() throws Exception { var retroId = UUID.randomUUID(); - var event = new ThoughtEvent(this, ActionType.CREATE, null, retroId); + var event = new ThoughtEvent(this, EventType.CREATE, null, retroId); when(objectMapper.writeValueAsString(any())).thenReturn("value"); subject.onApplicationEvent(event); @@ -29,7 +29,7 @@ void onApplicationEvent_WhenEventEmitted_SendMessageToWebSocket() throws Excepti @Test void onApplicationEvent_WhenEventSerializationFails_DoesNotSendMessage() throws Exception { var retroId = UUID.randomUUID(); - var event = new ThoughtEvent(this, ActionType.CREATE, null, retroId); + var event = new ThoughtEvent(this, EventType.CREATE, null, retroId); when(objectMapper.writeValueAsString(any())).thenThrow(JsonProcessingException.class); subject.onApplicationEvent(event); diff --git a/src/test/java/io/nickreuter/retroapi/retro/RetroServiceTest.java b/src/test/java/io/nickreuter/retroapi/retro/RetroServiceTest.java index 0527f7b..14c2e12 100644 --- a/src/test/java/io/nickreuter/retroapi/retro/RetroServiceTest.java +++ b/src/test/java/io/nickreuter/retroapi/retro/RetroServiceTest.java @@ -1,6 +1,6 @@ package io.nickreuter.retroapi.retro; -import io.nickreuter.retroapi.notification.ActionType; +import io.nickreuter.retroapi.notification.EventType; import io.nickreuter.retroapi.notification.event.RetroFinishedEvent; import io.nickreuter.retroapi.retro.template.Category; import io.nickreuter.retroapi.retro.template.Template; @@ -94,7 +94,7 @@ void setFinished_PublishesEvent() throws RetroNotFoundException { var argCaptor = ArgumentCaptor.forClass(RetroFinishedEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.finished".formatted(retroId)); - assertThat(argCaptor.getValue().getActionType()).isEqualTo(ActionType.UPDATE); + assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(true); } diff --git a/src/test/java/io/nickreuter/retroapi/retro/thought/ThoughtServiceTest.java b/src/test/java/io/nickreuter/retroapi/retro/thought/ThoughtServiceTest.java index 95e40d6..3177951 100644 --- a/src/test/java/io/nickreuter/retroapi/retro/thought/ThoughtServiceTest.java +++ b/src/test/java/io/nickreuter/retroapi/retro/thought/ThoughtServiceTest.java @@ -1,6 +1,6 @@ package io.nickreuter.retroapi.retro.thought; -import io.nickreuter.retroapi.notification.ActionType; +import io.nickreuter.retroapi.notification.EventType; import io.nickreuter.retroapi.notification.event.ThoughtEvent; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -62,7 +62,7 @@ void createThought_SendsEventToApplicationEventPublisher() { var argCaptor = ArgumentCaptor.forClass(ThoughtEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.thoughts".formatted(retroId)); - assertThat(argCaptor.getValue().getActionType()).isEqualTo(ActionType.CREATE); + assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.CREATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -105,7 +105,7 @@ void addVote_WhenCountUpdated_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ThoughtEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.thoughts".formatted(expected.getRetroId())); - assertThat(argCaptor.getValue().getActionType()).isEqualTo(ActionType.UPDATE); + assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -136,7 +136,7 @@ void setCompleted_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ThoughtEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.thoughts".formatted(expected.getRetroId())); - assertThat(argCaptor.getValue().getActionType()).isEqualTo(ActionType.UPDATE); + assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -168,7 +168,7 @@ void setCategory_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ThoughtEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.thoughts".formatted(expected.getRetroId())); - assertThat(argCaptor.getValue().getActionType()).isEqualTo(ActionType.UPDATE); + assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -200,7 +200,7 @@ void setMessage_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ThoughtEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.thoughts".formatted(expected.getRetroId())); - assertThat(argCaptor.getValue().getActionType()).isEqualTo(ActionType.UPDATE); + assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -226,7 +226,7 @@ void deleteThought_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ThoughtEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.thoughts".formatted(savedThought.getRetroId())); - assertThat(argCaptor.getValue().getActionType()).isEqualTo(ActionType.DELETE); + assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.DELETE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(savedThought); } } \ No newline at end of file diff --git a/src/test/java/io/nickreuter/retroapi/team/actionitem/ActionItemServiceTest.java b/src/test/java/io/nickreuter/retroapi/team/actionitem/ActionItemServiceTest.java index e4fcf30..4cc07c6 100644 --- a/src/test/java/io/nickreuter/retroapi/team/actionitem/ActionItemServiceTest.java +++ b/src/test/java/io/nickreuter/retroapi/team/actionitem/ActionItemServiceTest.java @@ -1,6 +1,6 @@ package io.nickreuter.retroapi.team.actionitem; -import io.nickreuter.retroapi.notification.ActionType; +import io.nickreuter.retroapi.notification.EventType; import io.nickreuter.retroapi.notification.event.ActionItemEvent; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -63,7 +63,7 @@ void createActionItem_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ActionItemEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.action-items".formatted(teamId)); - assertThat(argCaptor.getValue().getActionType()).isEqualTo(ActionType.CREATE); + assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.CREATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -103,7 +103,7 @@ void setAction_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ActionItemEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.action-items".formatted(teamId)); - assertThat(argCaptor.getValue().getActionType()).isEqualTo(ActionType.UPDATE); + assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -143,7 +143,7 @@ void setAssignee_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ActionItemEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.action-items".formatted(teamId)); - assertThat(argCaptor.getValue().getActionType()).isEqualTo(ActionType.UPDATE); + assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -183,7 +183,7 @@ void setCompleted_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ActionItemEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.action-items".formatted(teamId)); - assertThat(argCaptor.getValue().getActionType()).isEqualTo(ActionType.UPDATE); + assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -210,7 +210,7 @@ void deleteActionItem_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ActionItemEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.action-items".formatted(teamId)); - assertThat(argCaptor.getValue().getActionType()).isEqualTo(ActionType.DELETE); + assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.DELETE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(savedActionItem); } From ac26320d15ec353b86a59c8786812f87f41022ee Mon Sep 17 00:00:00 2001 From: Nick Reuter Date: Sat, 14 Mar 2026 11:51:06 -0400 Subject: [PATCH 2/5] Restructure STOMP destination routes to include resource scope Routes now use explicit prefixes: /topic/retros.{id}.thoughts, /topic/teams.{id}.action-items, /topic/retros.{id}.events. Updated authorization matchers and regex patterns to match the new structure. --- .../retroapi/notification/WebsocketConfig.java | 10 +++++----- .../retroapi/notification/event/ActionItemEvent.java | 2 +- .../notification/event/RetroFinishedEvent.java | 2 +- .../retroapi/notification/event/ThoughtEvent.java | 2 +- .../WebsocketNotificationServiceTest.java | 2 +- .../nickreuter/retroapi/retro/RetroServiceTest.java | 2 +- .../retroapi/retro/thought/ThoughtServiceTest.java | 12 ++++++------ .../team/actionitem/ActionItemServiceTest.java | 10 +++++----- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/nickreuter/retroapi/notification/WebsocketConfig.java b/src/main/java/io/nickreuter/retroapi/notification/WebsocketConfig.java index b5054ed..adc2bce 100644 --- a/src/main/java/io/nickreuter/retroapi/notification/WebsocketConfig.java +++ b/src/main/java/io/nickreuter/retroapi/notification/WebsocketConfig.java @@ -89,9 +89,9 @@ public AuthorizationManager> messageAuthorizationManager(MessageMatch messages .simpTypeMatchers(UNSUBSCRIBE, DISCONNECT).permitAll() .nullDestMatcher().authenticated() - .simpSubscribeDestMatchers("/topic/*.thoughts").access(this::isAuthorizedRetroSubscription) - .simpSubscribeDestMatchers("/topic/*.finished").access(this::isAuthorizedRetroSubscription) - .simpSubscribeDestMatchers("/topic/*.action-items").access((this::isAuthorizedTeamSubscription)) + .simpSubscribeDestMatchers("/topic/retros.*.thoughts").access(this::isAuthorizedRetroSubscription) + .simpSubscribeDestMatchers("/topic/retros.*.events").access(this::isAuthorizedRetroSubscription) + .simpSubscribeDestMatchers("/topic/teams.*.action-items").access(this::isAuthorizedTeamSubscription) .simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll() .anyMessage().denyAll(); return messages.build(); @@ -158,7 +158,7 @@ ChannelInterceptor csrfChannelInterceptor() { } private AuthorizationDecision isAuthorizedRetroSubscription(Supplier authentication, MessageAuthorizationContext object) { - var ids = getIdFromTopic(object, "^/topic/(?.*)\\..*"); + var ids = getIdFromTopic(object, "^/topic/retros\\.(?[^.]+)\\..*"); if (!ids.find()) { return new AuthorizationDecision(false); } @@ -181,7 +181,7 @@ private AuthorizationDecision isAuthorizedRetroSubscription(Supplier authentication, MessageAuthorizationContext object) { - var ids = getIdFromTopic(object, "^/topic/(?.*)\\..*$"); + var ids = getIdFromTopic(object, "^/topic/teams\\.(?[^.]+)\\..*$"); AuthorizationDecision isAuthorized = ids.find() // TODO: Add error handling for when this fails because the UUID is too big ? new AuthorizationDecision(userMappingAuthorizationService.isUserMemberOfTeam(authentication.get(), UUID.fromString(ids.group("teamId")))) diff --git a/src/main/java/io/nickreuter/retroapi/notification/event/ActionItemEvent.java b/src/main/java/io/nickreuter/retroapi/notification/event/ActionItemEvent.java index f31ff39..f2d3c03 100644 --- a/src/main/java/io/nickreuter/retroapi/notification/event/ActionItemEvent.java +++ b/src/main/java/io/nickreuter/retroapi/notification/event/ActionItemEvent.java @@ -6,7 +6,7 @@ import java.util.UUID; public class ActionItemEvent extends BaseEvent{ - private static final String ROUTE_STRING = "/topic/%s.action-items"; + private static final String ROUTE_STRING = "/topic/teams.%s.action-items"; private final UUID teamId; public ActionItemEvent(Object source, EventType eventType, ActionItemEntity payload, UUID teamId) { diff --git a/src/main/java/io/nickreuter/retroapi/notification/event/RetroFinishedEvent.java b/src/main/java/io/nickreuter/retroapi/notification/event/RetroFinishedEvent.java index 9040366..7609559 100644 --- a/src/main/java/io/nickreuter/retroapi/notification/event/RetroFinishedEvent.java +++ b/src/main/java/io/nickreuter/retroapi/notification/event/RetroFinishedEvent.java @@ -5,7 +5,7 @@ import java.util.UUID; public class RetroFinishedEvent extends BaseEvent { - private static final String ROUTE_STRING = "/topic/%s.finished"; + private static final String ROUTE_STRING = "/topic/retros.%s.events"; private final UUID retroId; public RetroFinishedEvent(Object source, EventType eventType, boolean isFinished, UUID retroId) { diff --git a/src/main/java/io/nickreuter/retroapi/notification/event/ThoughtEvent.java b/src/main/java/io/nickreuter/retroapi/notification/event/ThoughtEvent.java index 96d1788..3e67f86 100644 --- a/src/main/java/io/nickreuter/retroapi/notification/event/ThoughtEvent.java +++ b/src/main/java/io/nickreuter/retroapi/notification/event/ThoughtEvent.java @@ -6,7 +6,7 @@ import java.util.UUID; public class ThoughtEvent extends BaseEvent { - private static final String ROUTE_STRING = "/topic/%s.thoughts"; + private static final String ROUTE_STRING = "/topic/retros.%s.thoughts"; private final UUID retroId; public ThoughtEvent(Object source, EventType eventType, ThoughtEntity payload, UUID retroId) { diff --git a/src/test/java/io/nickreuter/retroapi/notification/WebsocketNotificationServiceTest.java b/src/test/java/io/nickreuter/retroapi/notification/WebsocketNotificationServiceTest.java index 3f9f114..79b8162 100644 --- a/src/test/java/io/nickreuter/retroapi/notification/WebsocketNotificationServiceTest.java +++ b/src/test/java/io/nickreuter/retroapi/notification/WebsocketNotificationServiceTest.java @@ -23,7 +23,7 @@ void onApplicationEvent_WhenEventEmitted_SendMessageToWebSocket() throws Excepti subject.onApplicationEvent(event); - verify(simpMessagingTemplate).convertAndSend("/topic/%s.thoughts".formatted(retroId), "value"); + verify(simpMessagingTemplate).convertAndSend("/topic/retros.%s.thoughts".formatted(retroId), "value"); } @Test diff --git a/src/test/java/io/nickreuter/retroapi/retro/RetroServiceTest.java b/src/test/java/io/nickreuter/retroapi/retro/RetroServiceTest.java index 14c2e12..962f0c4 100644 --- a/src/test/java/io/nickreuter/retroapi/retro/RetroServiceTest.java +++ b/src/test/java/io/nickreuter/retroapi/retro/RetroServiceTest.java @@ -93,7 +93,7 @@ void setFinished_PublishesEvent() throws RetroNotFoundException { var argCaptor = ArgumentCaptor.forClass(RetroFinishedEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); - assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.finished".formatted(retroId)); + assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/retros.%s.events".formatted(retroId)); assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(true); } diff --git a/src/test/java/io/nickreuter/retroapi/retro/thought/ThoughtServiceTest.java b/src/test/java/io/nickreuter/retroapi/retro/thought/ThoughtServiceTest.java index 3177951..c3fe98f 100644 --- a/src/test/java/io/nickreuter/retroapi/retro/thought/ThoughtServiceTest.java +++ b/src/test/java/io/nickreuter/retroapi/retro/thought/ThoughtServiceTest.java @@ -61,7 +61,7 @@ void createThought_SendsEventToApplicationEventPublisher() { var argCaptor = ArgumentCaptor.forClass(ThoughtEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); - assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.thoughts".formatted(retroId)); + assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/retros.%s.thoughts".formatted(retroId)); assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.CREATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -104,7 +104,7 @@ void addVote_WhenCountUpdated_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ThoughtEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); - assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.thoughts".formatted(expected.getRetroId())); + assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/retros.%s.thoughts".formatted(expected.getRetroId())); assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -135,7 +135,7 @@ void setCompleted_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ThoughtEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); - assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.thoughts".formatted(expected.getRetroId())); + assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/retros.%s.thoughts".formatted(expected.getRetroId())); assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -167,7 +167,7 @@ void setCategory_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ThoughtEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); - assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.thoughts".formatted(expected.getRetroId())); + assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/retros.%s.thoughts".formatted(expected.getRetroId())); assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -199,7 +199,7 @@ void setMessage_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ThoughtEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); - assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.thoughts".formatted(expected.getRetroId())); + assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/retros.%s.thoughts".formatted(expected.getRetroId())); assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -225,7 +225,7 @@ void deleteThought_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ThoughtEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); - assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.thoughts".formatted(savedThought.getRetroId())); + assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/retros.%s.thoughts".formatted(savedThought.getRetroId())); assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.DELETE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(savedThought); } diff --git a/src/test/java/io/nickreuter/retroapi/team/actionitem/ActionItemServiceTest.java b/src/test/java/io/nickreuter/retroapi/team/actionitem/ActionItemServiceTest.java index 4cc07c6..727acd3 100644 --- a/src/test/java/io/nickreuter/retroapi/team/actionitem/ActionItemServiceTest.java +++ b/src/test/java/io/nickreuter/retroapi/team/actionitem/ActionItemServiceTest.java @@ -62,7 +62,7 @@ void createActionItem_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ActionItemEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); - assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.action-items".formatted(teamId)); + assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/teams.%s.action-items".formatted(teamId)); assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.CREATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -102,7 +102,7 @@ void setAction_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ActionItemEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); - assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.action-items".formatted(teamId)); + assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/teams.%s.action-items".formatted(teamId)); assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -142,7 +142,7 @@ void setAssignee_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ActionItemEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); - assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.action-items".formatted(teamId)); + assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/teams.%s.action-items".formatted(teamId)); assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -182,7 +182,7 @@ void setCompleted_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ActionItemEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); - assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.action-items".formatted(teamId)); + assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/teams.%s.action-items".formatted(teamId)); assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(expected); } @@ -209,7 +209,7 @@ void deleteActionItem_PublishesEvent() { var argCaptor = ArgumentCaptor.forClass(ActionItemEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); - assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/%s.action-items".formatted(teamId)); + assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/teams.%s.action-items".formatted(teamId)); assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.DELETE); assertThat(argCaptor.getValue().getPayload()).isEqualTo(savedActionItem); } From 374df47e5eaee7ae7a81f7bebea395f5cc15686b Mon Sep 17 00:00:00 2001 From: Nick Reuter Date: Sat, 14 Mar 2026 11:58:55 -0400 Subject: [PATCH 3/5] feat: add ephemeral event types to EventType enum --- .../io/nickreuter/retroapi/notification/EventType.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/nickreuter/retroapi/notification/EventType.java b/src/main/java/io/nickreuter/retroapi/notification/EventType.java index 9fee3f3..9eceb51 100644 --- a/src/main/java/io/nickreuter/retroapi/notification/EventType.java +++ b/src/main/java/io/nickreuter/retroapi/notification/EventType.java @@ -3,5 +3,12 @@ public enum EventType { CREATE, UPDATE, - DELETE + DELETE, + TIMER_START, + TIMER_STOP, + FOCUS, + FOCUS_CLEAR, + SORT, + PHASE, + RETRO_FINISHED } From b3d97ed08c8838bb05bb6d64dac98936217eac07 Mon Sep 17 00:00:00 2001 From: Nick Reuter Date: Sat, 14 Mar 2026 15:16:11 -0400 Subject: [PATCH 4/5] feat: add RetroEventController, service, and event classes for ephemeral retro events Introduces the full ephemeral event system in the retro.event package with REST endpoints for timer, focus, sort, and phase events that broadcast via WebSocket to /topic/retros.{retroId}.events. --- .../retroapi/retro/event/FocusRequest.java | 5 + .../retroapi/retro/event/PhaseRequest.java | 3 + .../retro/event/RetroEventController.java | 59 +++++ .../retro/event/RetroEventService.java | 45 ++++ .../retro/event/RetroFocusClearEvent.java | 21 ++ .../retroapi/retro/event/RetroFocusEvent.java | 21 ++ .../retroapi/retro/event/RetroPhaseEvent.java | 21 ++ .../retroapi/retro/event/RetroSortEvent.java | 21 ++ .../retro/event/RetroTimerStartEvent.java | 21 ++ .../retro/event/RetroTimerStopEvent.java | 21 ++ .../retroapi/retro/event/SortRequest.java | 3 + .../retro/event/TimerStartRequest.java | 3 + .../retro/event/RetroEventControllerTest.java | 249 ++++++++++++++++++ .../retro/event/RetroEventServiceTest.java | 83 ++++++ 14 files changed, 576 insertions(+) create mode 100644 src/main/java/io/nickreuter/retroapi/retro/event/FocusRequest.java create mode 100644 src/main/java/io/nickreuter/retroapi/retro/event/PhaseRequest.java create mode 100644 src/main/java/io/nickreuter/retroapi/retro/event/RetroEventController.java create mode 100644 src/main/java/io/nickreuter/retroapi/retro/event/RetroEventService.java create mode 100644 src/main/java/io/nickreuter/retroapi/retro/event/RetroFocusClearEvent.java create mode 100644 src/main/java/io/nickreuter/retroapi/retro/event/RetroFocusEvent.java create mode 100644 src/main/java/io/nickreuter/retroapi/retro/event/RetroPhaseEvent.java create mode 100644 src/main/java/io/nickreuter/retroapi/retro/event/RetroSortEvent.java create mode 100644 src/main/java/io/nickreuter/retroapi/retro/event/RetroTimerStartEvent.java create mode 100644 src/main/java/io/nickreuter/retroapi/retro/event/RetroTimerStopEvent.java create mode 100644 src/main/java/io/nickreuter/retroapi/retro/event/SortRequest.java create mode 100644 src/main/java/io/nickreuter/retroapi/retro/event/TimerStartRequest.java create mode 100644 src/test/java/io/nickreuter/retroapi/retro/event/RetroEventControllerTest.java create mode 100644 src/test/java/io/nickreuter/retroapi/retro/event/RetroEventServiceTest.java diff --git a/src/main/java/io/nickreuter/retroapi/retro/event/FocusRequest.java b/src/main/java/io/nickreuter/retroapi/retro/event/FocusRequest.java new file mode 100644 index 0000000..ba8cff1 --- /dev/null +++ b/src/main/java/io/nickreuter/retroapi/retro/event/FocusRequest.java @@ -0,0 +1,5 @@ +package io.nickreuter.retroapi.retro.event; + +import java.util.UUID; + +public record FocusRequest(UUID thoughtId) {} diff --git a/src/main/java/io/nickreuter/retroapi/retro/event/PhaseRequest.java b/src/main/java/io/nickreuter/retroapi/retro/event/PhaseRequest.java new file mode 100644 index 0000000..9285c34 --- /dev/null +++ b/src/main/java/io/nickreuter/retroapi/retro/event/PhaseRequest.java @@ -0,0 +1,3 @@ +package io.nickreuter.retroapi.retro.event; + +public record PhaseRequest(String phase) {} diff --git a/src/main/java/io/nickreuter/retroapi/retro/event/RetroEventController.java b/src/main/java/io/nickreuter/retroapi/retro/event/RetroEventController.java new file mode 100644 index 0000000..84f7fa8 --- /dev/null +++ b/src/main/java/io/nickreuter/retroapi/retro/event/RetroEventController.java @@ -0,0 +1,59 @@ +package io.nickreuter.retroapi.retro.event; + +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.UUID; + +@RestController +@RequestMapping("/api/teams/{teamId}/retros/{retroId}/events") +public class RetroEventController { + private final RetroEventService retroEventService; + + public RetroEventController(RetroEventService retroEventService) { + this.retroEventService = retroEventService; + } + + @PostMapping("/timer-start") + @PreAuthorize("@userMappingAuthorizationService.isUserMemberOfTeam(authentication, #teamId)") + public ResponseEntity startTimer(@PathVariable UUID teamId, @PathVariable UUID retroId, @RequestBody TimerStartRequest request) { + retroEventService.publishTimerStart(retroId, request.durationSeconds()); + return ResponseEntity.ok().build(); + } + + @PostMapping("/timer-stop") + @PreAuthorize("@userMappingAuthorizationService.isUserMemberOfTeam(authentication, #teamId)") + public ResponseEntity stopTimer(@PathVariable UUID teamId, @PathVariable UUID retroId) { + retroEventService.publishTimerStop(retroId); + return ResponseEntity.ok().build(); + } + + @PostMapping("/focus") + @PreAuthorize("@userMappingAuthorizationService.isUserMemberOfTeam(authentication, #teamId)") + public ResponseEntity focusThought(@PathVariable UUID teamId, @PathVariable UUID retroId, @RequestBody FocusRequest request) { + retroEventService.publishFocus(retroId, request.thoughtId()); + return ResponseEntity.ok().build(); + } + + @PostMapping("/focus-clear") + @PreAuthorize("@userMappingAuthorizationService.isUserMemberOfTeam(authentication, #teamId)") + public ResponseEntity clearFocus(@PathVariable UUID teamId, @PathVariable UUID retroId) { + retroEventService.publishFocusClear(retroId); + return ResponseEntity.ok().build(); + } + + @PostMapping("/sort") + @PreAuthorize("@userMappingAuthorizationService.isUserMemberOfTeam(authentication, #teamId)") + public ResponseEntity sortColumn(@PathVariable UUID teamId, @PathVariable UUID retroId, @RequestBody SortRequest request) { + retroEventService.publishSort(retroId, request.column(), request.direction()); + return ResponseEntity.ok().build(); + } + + @PostMapping("/phase") + @PreAuthorize("@userMappingAuthorizationService.isUserMemberOfTeam(authentication, #teamId)") + public ResponseEntity changePhase(@PathVariable UUID teamId, @PathVariable UUID retroId, @RequestBody PhaseRequest request) { + retroEventService.publishPhase(retroId, request.phase()); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/io/nickreuter/retroapi/retro/event/RetroEventService.java b/src/main/java/io/nickreuter/retroapi/retro/event/RetroEventService.java new file mode 100644 index 0000000..8fd9ea6 --- /dev/null +++ b/src/main/java/io/nickreuter/retroapi/retro/event/RetroEventService.java @@ -0,0 +1,45 @@ +package io.nickreuter.retroapi.retro.event; + +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.util.Map; +import java.util.UUID; + +@Service +public class RetroEventService { + private final ApplicationEventPublisher applicationEventPublisher; + + public RetroEventService(ApplicationEventPublisher applicationEventPublisher) { + this.applicationEventPublisher = applicationEventPublisher; + } + + public void publishTimerStart(UUID retroId, int durationSeconds) { + var payload = Map.of("durationSeconds", durationSeconds, "startedAt", Instant.now().toString()); + applicationEventPublisher.publishEvent(new RetroTimerStartEvent(this, payload, retroId)); + } + + public void publishTimerStop(UUID retroId) { + applicationEventPublisher.publishEvent(new RetroTimerStopEvent(this, null, retroId)); + } + + public void publishFocus(UUID retroId, UUID thoughtId) { + var payload = Map.of("thoughtId", thoughtId.toString()); + applicationEventPublisher.publishEvent(new RetroFocusEvent(this, payload, retroId)); + } + + public void publishFocusClear(UUID retroId) { + applicationEventPublisher.publishEvent(new RetroFocusClearEvent(this, null, retroId)); + } + + public void publishSort(UUID retroId, String column, String direction) { + var payload = Map.of("column", column, "direction", direction); + applicationEventPublisher.publishEvent(new RetroSortEvent(this, payload, retroId)); + } + + public void publishPhase(UUID retroId, String phase) { + var payload = Map.of("phase", phase); + applicationEventPublisher.publishEvent(new RetroPhaseEvent(this, payload, retroId)); + } +} diff --git a/src/main/java/io/nickreuter/retroapi/retro/event/RetroFocusClearEvent.java b/src/main/java/io/nickreuter/retroapi/retro/event/RetroFocusClearEvent.java new file mode 100644 index 0000000..c572aba --- /dev/null +++ b/src/main/java/io/nickreuter/retroapi/retro/event/RetroFocusClearEvent.java @@ -0,0 +1,21 @@ +package io.nickreuter.retroapi.retro.event; + +import io.nickreuter.retroapi.notification.EventType; +import io.nickreuter.retroapi.notification.event.BaseEvent; + +import java.util.UUID; + +public class RetroFocusClearEvent extends BaseEvent { + private static final String ROUTE_STRING = "/topic/retros.%s.events"; + private final UUID retroId; + + public RetroFocusClearEvent(Object source, Object payload, UUID retroId) { + super(source, EventType.FOCUS_CLEAR, payload); + this.retroId = retroId; + } + + @Override + public String getRoute() { + return String.format(ROUTE_STRING, retroId); + } +} diff --git a/src/main/java/io/nickreuter/retroapi/retro/event/RetroFocusEvent.java b/src/main/java/io/nickreuter/retroapi/retro/event/RetroFocusEvent.java new file mode 100644 index 0000000..938c877 --- /dev/null +++ b/src/main/java/io/nickreuter/retroapi/retro/event/RetroFocusEvent.java @@ -0,0 +1,21 @@ +package io.nickreuter.retroapi.retro.event; + +import io.nickreuter.retroapi.notification.EventType; +import io.nickreuter.retroapi.notification.event.BaseEvent; + +import java.util.UUID; + +public class RetroFocusEvent extends BaseEvent { + private static final String ROUTE_STRING = "/topic/retros.%s.events"; + private final UUID retroId; + + public RetroFocusEvent(Object source, Object payload, UUID retroId) { + super(source, EventType.FOCUS, payload); + this.retroId = retroId; + } + + @Override + public String getRoute() { + return String.format(ROUTE_STRING, retroId); + } +} diff --git a/src/main/java/io/nickreuter/retroapi/retro/event/RetroPhaseEvent.java b/src/main/java/io/nickreuter/retroapi/retro/event/RetroPhaseEvent.java new file mode 100644 index 0000000..c3301a1 --- /dev/null +++ b/src/main/java/io/nickreuter/retroapi/retro/event/RetroPhaseEvent.java @@ -0,0 +1,21 @@ +package io.nickreuter.retroapi.retro.event; + +import io.nickreuter.retroapi.notification.EventType; +import io.nickreuter.retroapi.notification.event.BaseEvent; + +import java.util.UUID; + +public class RetroPhaseEvent extends BaseEvent { + private static final String ROUTE_STRING = "/topic/retros.%s.events"; + private final UUID retroId; + + public RetroPhaseEvent(Object source, Object payload, UUID retroId) { + super(source, EventType.PHASE, payload); + this.retroId = retroId; + } + + @Override + public String getRoute() { + return String.format(ROUTE_STRING, retroId); + } +} diff --git a/src/main/java/io/nickreuter/retroapi/retro/event/RetroSortEvent.java b/src/main/java/io/nickreuter/retroapi/retro/event/RetroSortEvent.java new file mode 100644 index 0000000..1ab87e1 --- /dev/null +++ b/src/main/java/io/nickreuter/retroapi/retro/event/RetroSortEvent.java @@ -0,0 +1,21 @@ +package io.nickreuter.retroapi.retro.event; + +import io.nickreuter.retroapi.notification.EventType; +import io.nickreuter.retroapi.notification.event.BaseEvent; + +import java.util.UUID; + +public class RetroSortEvent extends BaseEvent { + private static final String ROUTE_STRING = "/topic/retros.%s.events"; + private final UUID retroId; + + public RetroSortEvent(Object source, Object payload, UUID retroId) { + super(source, EventType.SORT, payload); + this.retroId = retroId; + } + + @Override + public String getRoute() { + return String.format(ROUTE_STRING, retroId); + } +} diff --git a/src/main/java/io/nickreuter/retroapi/retro/event/RetroTimerStartEvent.java b/src/main/java/io/nickreuter/retroapi/retro/event/RetroTimerStartEvent.java new file mode 100644 index 0000000..1d35b1f --- /dev/null +++ b/src/main/java/io/nickreuter/retroapi/retro/event/RetroTimerStartEvent.java @@ -0,0 +1,21 @@ +package io.nickreuter.retroapi.retro.event; + +import io.nickreuter.retroapi.notification.EventType; +import io.nickreuter.retroapi.notification.event.BaseEvent; + +import java.util.UUID; + +public class RetroTimerStartEvent extends BaseEvent { + private static final String ROUTE_STRING = "/topic/retros.%s.events"; + private final UUID retroId; + + public RetroTimerStartEvent(Object source, Object payload, UUID retroId) { + super(source, EventType.TIMER_START, payload); + this.retroId = retroId; + } + + @Override + public String getRoute() { + return String.format(ROUTE_STRING, retroId); + } +} diff --git a/src/main/java/io/nickreuter/retroapi/retro/event/RetroTimerStopEvent.java b/src/main/java/io/nickreuter/retroapi/retro/event/RetroTimerStopEvent.java new file mode 100644 index 0000000..60b151f --- /dev/null +++ b/src/main/java/io/nickreuter/retroapi/retro/event/RetroTimerStopEvent.java @@ -0,0 +1,21 @@ +package io.nickreuter.retroapi.retro.event; + +import io.nickreuter.retroapi.notification.EventType; +import io.nickreuter.retroapi.notification.event.BaseEvent; + +import java.util.UUID; + +public class RetroTimerStopEvent extends BaseEvent { + private static final String ROUTE_STRING = "/topic/retros.%s.events"; + private final UUID retroId; + + public RetroTimerStopEvent(Object source, Object payload, UUID retroId) { + super(source, EventType.TIMER_STOP, payload); + this.retroId = retroId; + } + + @Override + public String getRoute() { + return String.format(ROUTE_STRING, retroId); + } +} diff --git a/src/main/java/io/nickreuter/retroapi/retro/event/SortRequest.java b/src/main/java/io/nickreuter/retroapi/retro/event/SortRequest.java new file mode 100644 index 0000000..2e2d7a3 --- /dev/null +++ b/src/main/java/io/nickreuter/retroapi/retro/event/SortRequest.java @@ -0,0 +1,3 @@ +package io.nickreuter.retroapi.retro.event; + +public record SortRequest(String column, String direction) {} diff --git a/src/main/java/io/nickreuter/retroapi/retro/event/TimerStartRequest.java b/src/main/java/io/nickreuter/retroapi/retro/event/TimerStartRequest.java new file mode 100644 index 0000000..842e8c9 --- /dev/null +++ b/src/main/java/io/nickreuter/retroapi/retro/event/TimerStartRequest.java @@ -0,0 +1,3 @@ +package io.nickreuter.retroapi.retro.event; + +public record TimerStartRequest(int durationSeconds) {} diff --git a/src/test/java/io/nickreuter/retroapi/retro/event/RetroEventControllerTest.java b/src/test/java/io/nickreuter/retroapi/retro/event/RetroEventControllerTest.java new file mode 100644 index 0000000..ff27ed0 --- /dev/null +++ b/src/test/java/io/nickreuter/retroapi/retro/event/RetroEventControllerTest.java @@ -0,0 +1,249 @@ +package io.nickreuter.retroapi.retro.event; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.nickreuter.retroapi.team.usermapping.UserMappingAuthorizationService; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.UUID; + +import static io.nickreuter.retroapi.team.TestAuthenticationCreationService.createAuthentication; +import static org.mockito.Mockito.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@AutoConfigureMockMvc +@SpringBootTest +class RetroEventControllerTest { + @MockitoBean + private JwtDecoder jwtDecoder; + @MockitoBean + private RetroEventService retroEventService; + @MockitoBean + private UserMappingAuthorizationService userMappingAuthorizationService; + + @Autowired + private MockMvc mockMvc; + @Autowired + private ObjectMapper objectMapper; + + private static final String BASE_URL = "/api/teams/%s/retros/%s/events"; + + // Timer Start + @Test + void startTimer_Returns200() throws Exception { + var teamId = UUID.randomUUID(); + var retroId = UUID.randomUUID(); + when(userMappingAuthorizationService.isUserMemberOfTeam(createAuthentication(), teamId)).thenReturn(true); + mockMvc.perform(post((BASE_URL + "/timer-start").formatted(teamId, retroId)) + .with(jwt()) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new TimerStartRequest(300)))) + .andExpect(status().isOk()); + verify(retroEventService).publishTimerStart(retroId, 300); + } + + @Test + void startTimer_WhenAnonymous_Returns401() throws Exception { + mockMvc.perform(post((BASE_URL + "/timer-start").formatted(UUID.randomUUID(), UUID.randomUUID())) + .with(anonymous()) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new TimerStartRequest(300)))) + .andExpect(status().isUnauthorized()); + } + + @Test + void startTimer_WhenNotMember_Returns403() throws Exception { + var teamId = UUID.randomUUID(); + when(userMappingAuthorizationService.isUserMemberOfTeam(createAuthentication(), teamId)).thenReturn(false); + mockMvc.perform(post((BASE_URL + "/timer-start").formatted(teamId, UUID.randomUUID())) + .with(jwt()) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new TimerStartRequest(300)))) + .andExpect(status().isForbidden()); + } + + // Timer Stop + @Test + void stopTimer_Returns200() throws Exception { + var teamId = UUID.randomUUID(); + var retroId = UUID.randomUUID(); + when(userMappingAuthorizationService.isUserMemberOfTeam(createAuthentication(), teamId)).thenReturn(true); + mockMvc.perform(post((BASE_URL + "/timer-stop").formatted(teamId, retroId)) + .with(jwt()) + .with(csrf())) + .andExpect(status().isOk()); + verify(retroEventService).publishTimerStop(retroId); + } + + @Test + void stopTimer_WhenAnonymous_Returns401() throws Exception { + mockMvc.perform(post((BASE_URL + "/timer-stop").formatted(UUID.randomUUID(), UUID.randomUUID())) + .with(anonymous()) + .with(csrf())) + .andExpect(status().isUnauthorized()); + } + + @Test + void stopTimer_WhenNotMember_Returns403() throws Exception { + var teamId = UUID.randomUUID(); + when(userMappingAuthorizationService.isUserMemberOfTeam(createAuthentication(), teamId)).thenReturn(false); + mockMvc.perform(post((BASE_URL + "/timer-stop").formatted(teamId, UUID.randomUUID())) + .with(jwt()) + .with(csrf())) + .andExpect(status().isForbidden()); + } + + // Focus + @Test + void focusThought_Returns200() throws Exception { + var teamId = UUID.randomUUID(); + var retroId = UUID.randomUUID(); + var thoughtId = UUID.randomUUID(); + when(userMappingAuthorizationService.isUserMemberOfTeam(createAuthentication(), teamId)).thenReturn(true); + mockMvc.perform(post((BASE_URL + "/focus").formatted(teamId, retroId)) + .with(jwt()) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new FocusRequest(thoughtId)))) + .andExpect(status().isOk()); + verify(retroEventService).publishFocus(retroId, thoughtId); + } + + @Test + void focusThought_WhenAnonymous_Returns401() throws Exception { + mockMvc.perform(post((BASE_URL + "/focus").formatted(UUID.randomUUID(), UUID.randomUUID())) + .with(anonymous()) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new FocusRequest(UUID.randomUUID())))) + .andExpect(status().isUnauthorized()); + } + + @Test + void focusThought_WhenNotMember_Returns403() throws Exception { + var teamId = UUID.randomUUID(); + when(userMappingAuthorizationService.isUserMemberOfTeam(createAuthentication(), teamId)).thenReturn(false); + mockMvc.perform(post((BASE_URL + "/focus").formatted(teamId, UUID.randomUUID())) + .with(jwt()) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new FocusRequest(UUID.randomUUID())))) + .andExpect(status().isForbidden()); + } + + // Focus Clear + @Test + void clearFocus_Returns200() throws Exception { + var teamId = UUID.randomUUID(); + var retroId = UUID.randomUUID(); + when(userMappingAuthorizationService.isUserMemberOfTeam(createAuthentication(), teamId)).thenReturn(true); + mockMvc.perform(post((BASE_URL + "/focus-clear").formatted(teamId, retroId)) + .with(jwt()) + .with(csrf())) + .andExpect(status().isOk()); + verify(retroEventService).publishFocusClear(retroId); + } + + @Test + void clearFocus_WhenAnonymous_Returns401() throws Exception { + mockMvc.perform(post((BASE_URL + "/focus-clear").formatted(UUID.randomUUID(), UUID.randomUUID())) + .with(anonymous()) + .with(csrf())) + .andExpect(status().isUnauthorized()); + } + + @Test + void clearFocus_WhenNotMember_Returns403() throws Exception { + var teamId = UUID.randomUUID(); + when(userMappingAuthorizationService.isUserMemberOfTeam(createAuthentication(), teamId)).thenReturn(false); + mockMvc.perform(post((BASE_URL + "/focus-clear").formatted(teamId, UUID.randomUUID())) + .with(jwt()) + .with(csrf())) + .andExpect(status().isForbidden()); + } + + // Sort + @Test + void sortColumn_Returns200() throws Exception { + var teamId = UUID.randomUUID(); + var retroId = UUID.randomUUID(); + when(userMappingAuthorizationService.isUserMemberOfTeam(createAuthentication(), teamId)).thenReturn(true); + mockMvc.perform(post((BASE_URL + "/sort").formatted(teamId, retroId)) + .with(jwt()) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new SortRequest("votes", "desc")))) + .andExpect(status().isOk()); + verify(retroEventService).publishSort(retroId, "votes", "desc"); + } + + @Test + void sortColumn_WhenAnonymous_Returns401() throws Exception { + mockMvc.perform(post((BASE_URL + "/sort").formatted(UUID.randomUUID(), UUID.randomUUID())) + .with(anonymous()) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new SortRequest("votes", "desc")))) + .andExpect(status().isUnauthorized()); + } + + @Test + void sortColumn_WhenNotMember_Returns403() throws Exception { + var teamId = UUID.randomUUID(); + when(userMappingAuthorizationService.isUserMemberOfTeam(createAuthentication(), teamId)).thenReturn(false); + mockMvc.perform(post((BASE_URL + "/sort").formatted(teamId, UUID.randomUUID())) + .with(jwt()) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new SortRequest("votes", "desc")))) + .andExpect(status().isForbidden()); + } + + // Phase + @Test + void changePhase_Returns200() throws Exception { + var teamId = UUID.randomUUID(); + var retroId = UUID.randomUUID(); + when(userMappingAuthorizationService.isUserMemberOfTeam(createAuthentication(), teamId)).thenReturn(true); + mockMvc.perform(post((BASE_URL + "/phase").formatted(teamId, retroId)) + .with(jwt()) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new PhaseRequest("voting")))) + .andExpect(status().isOk()); + verify(retroEventService).publishPhase(retroId, "voting"); + } + + @Test + void changePhase_WhenAnonymous_Returns401() throws Exception { + mockMvc.perform(post((BASE_URL + "/phase").formatted(UUID.randomUUID(), UUID.randomUUID())) + .with(anonymous()) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new PhaseRequest("voting")))) + .andExpect(status().isUnauthorized()); + } + + @Test + void changePhase_WhenNotMember_Returns403() throws Exception { + var teamId = UUID.randomUUID(); + when(userMappingAuthorizationService.isUserMemberOfTeam(createAuthentication(), teamId)).thenReturn(false); + mockMvc.perform(post((BASE_URL + "/phase").formatted(teamId, UUID.randomUUID())) + .with(jwt()) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new PhaseRequest("voting")))) + .andExpect(status().isForbidden()); + } +} diff --git a/src/test/java/io/nickreuter/retroapi/retro/event/RetroEventServiceTest.java b/src/test/java/io/nickreuter/retroapi/retro/event/RetroEventServiceTest.java new file mode 100644 index 0000000..7557875 --- /dev/null +++ b/src/test/java/io/nickreuter/retroapi/retro/event/RetroEventServiceTest.java @@ -0,0 +1,83 @@ +package io.nickreuter.retroapi.retro.event; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.context.ApplicationEventPublisher; + +import java.util.UUID; + +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class RetroEventServiceTest { + @Mock + private ApplicationEventPublisher applicationEventPublisher; + + @InjectMocks + private RetroEventService retroEventService; + + @Test + void publishTimerStart_PublishesRetroTimerStartEvent() { + var retroId = UUID.randomUUID(); + retroEventService.publishTimerStart(retroId, 300); + verify(applicationEventPublisher).publishEvent(argThat(event -> + event instanceof RetroTimerStartEvent e && + e.getRoute().equals("/topic/retros.%s.events".formatted(retroId)) + )); + } + + @Test + void publishTimerStop_PublishesRetroTimerStopEvent() { + var retroId = UUID.randomUUID(); + retroEventService.publishTimerStop(retroId); + verify(applicationEventPublisher).publishEvent(argThat(event -> + event instanceof RetroTimerStopEvent e && + e.getRoute().equals("/topic/retros.%s.events".formatted(retroId)) + )); + } + + @Test + void publishFocus_PublishesRetroFocusEvent() { + var retroId = UUID.randomUUID(); + var thoughtId = UUID.randomUUID(); + retroEventService.publishFocus(retroId, thoughtId); + verify(applicationEventPublisher).publishEvent(argThat(event -> + event instanceof RetroFocusEvent e && + e.getRoute().equals("/topic/retros.%s.events".formatted(retroId)) + )); + } + + @Test + void publishFocusClear_PublishesRetroFocusClearEvent() { + var retroId = UUID.randomUUID(); + retroEventService.publishFocusClear(retroId); + verify(applicationEventPublisher).publishEvent(argThat(event -> + event instanceof RetroFocusClearEvent e && + e.getRoute().equals("/topic/retros.%s.events".formatted(retroId)) + )); + } + + @Test + void publishSort_PublishesRetroSortEvent() { + var retroId = UUID.randomUUID(); + retroEventService.publishSort(retroId, "votes", "desc"); + verify(applicationEventPublisher).publishEvent(argThat(event -> + event instanceof RetroSortEvent e && + e.getRoute().equals("/topic/retros.%s.events".formatted(retroId)) + )); + } + + @Test + void publishPhase_PublishesRetroPhaseEvent() { + var retroId = UUID.randomUUID(); + retroEventService.publishPhase(retroId, "voting"); + verify(applicationEventPublisher).publishEvent(argThat(event -> + event instanceof RetroPhaseEvent e && + e.getRoute().equals("/topic/retros.%s.events".formatted(retroId)) + )); + } +} From 09c4f9c150322d2597ef260b5cab085c20d54e7b Mon Sep 17 00:00:00 2001 From: Nick Reuter Date: Sat, 14 Mar 2026 15:24:14 -0400 Subject: [PATCH 5/5] refactor: migrate RetroFinishedEvent to use RETRO_FINISHED event type --- .../retroapi/notification/event/RetroFinishedEvent.java | 4 ++-- src/main/java/io/nickreuter/retroapi/retro/RetroService.java | 3 +-- .../java/io/nickreuter/retroapi/retro/RetroServiceTest.java | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/nickreuter/retroapi/notification/event/RetroFinishedEvent.java b/src/main/java/io/nickreuter/retroapi/notification/event/RetroFinishedEvent.java index 7609559..25dff14 100644 --- a/src/main/java/io/nickreuter/retroapi/notification/event/RetroFinishedEvent.java +++ b/src/main/java/io/nickreuter/retroapi/notification/event/RetroFinishedEvent.java @@ -8,8 +8,8 @@ public class RetroFinishedEvent extends BaseEvent { private static final String ROUTE_STRING = "/topic/retros.%s.events"; private final UUID retroId; - public RetroFinishedEvent(Object source, EventType eventType, boolean isFinished, UUID retroId) { - super(source, eventType, isFinished); + public RetroFinishedEvent(Object source, boolean isFinished, UUID retroId) { + super(source, EventType.RETRO_FINISHED, isFinished); this.retroId = retroId; } diff --git a/src/main/java/io/nickreuter/retroapi/retro/RetroService.java b/src/main/java/io/nickreuter/retroapi/retro/RetroService.java index 561b888..8597350 100644 --- a/src/main/java/io/nickreuter/retroapi/retro/RetroService.java +++ b/src/main/java/io/nickreuter/retroapi/retro/RetroService.java @@ -1,6 +1,5 @@ package io.nickreuter.retroapi.retro; -import io.nickreuter.retroapi.notification.EventType; import io.nickreuter.retroapi.notification.event.RetroFinishedEvent; import io.nickreuter.retroapi.retro.template.Template; import org.springframework.context.ApplicationEventPublisher; @@ -53,6 +52,6 @@ public void setFinished(UUID retroId, boolean finished) throws RetroNotFoundExce var retro = retroRepository.findById(retroId).orElseThrow(RetroNotFoundException::new); retro.setFinished(finished); retroRepository.save(retro); - applicationEventPublisher.publishEvent(new RetroFinishedEvent(this, EventType.UPDATE, finished, retroId)); + applicationEventPublisher.publishEvent(new RetroFinishedEvent(this, finished, retroId)); } } diff --git a/src/test/java/io/nickreuter/retroapi/retro/RetroServiceTest.java b/src/test/java/io/nickreuter/retroapi/retro/RetroServiceTest.java index 962f0c4..375cc5d 100644 --- a/src/test/java/io/nickreuter/retroapi/retro/RetroServiceTest.java +++ b/src/test/java/io/nickreuter/retroapi/retro/RetroServiceTest.java @@ -94,7 +94,7 @@ void setFinished_PublishesEvent() throws RetroNotFoundException { var argCaptor = ArgumentCaptor.forClass(RetroFinishedEvent.class); verify(applicationEventPublisher).publishEvent(argCaptor.capture()); assertThat(argCaptor.getValue().getRoute()).isEqualTo("/topic/retros.%s.events".formatted(retroId)); - assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.UPDATE); + assertThat(argCaptor.getValue().getEventType()).isEqualTo(EventType.RETRO_FINISHED); assertThat(argCaptor.getValue().getPayload()).isEqualTo(true); }