Implement server state handling in ChatController and ChatService, add ServerStateMapper utility, and create/update server state unit tests.

This commit is contained in:
akastijn 2026-08-09 00:41:36 +02:00
parent b61b117f41
commit 65d0118e3d
6 changed files with 105 additions and 10 deletions

View File

@ -28,7 +28,7 @@ public class ChatController implements ChatApi {
@Override @Override
public ResponseEntity<Void> updateServerStates(ServerStateDto serverStateDto) { public ResponseEntity<Void> updateServerStates(ServerStateDto serverStateDto) {
//TODO [Stijn] [2026-07-18]: Implement handling server state updates (push to listeners) chatService.updateServerState(serverStateDto);
throw new UnsupportedOperationException("Not implemented"); return new ResponseEntity<>(HttpStatus.ACCEPTED);
} }
} }

View File

@ -0,0 +1,24 @@
package com.alttd.altitudeweb.controllers.chat;
import com.alttd.altitudeweb.model.ServerStateDto;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.experimental.UtilityClass;
@UtilityClass
public class ServerStateMapper {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
.registerModule(new JavaTimeModule())
.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS);
public static String toJson(ServerStateDto serverStateDto) {
try {
return OBJECT_MAPPER.writeValueAsString(serverStateDto);
} catch (JsonProcessingException e) {
throw new IllegalStateException("Failed to serialize ServerStateDto to JSON", e);
}
}
}

View File

@ -2,11 +2,14 @@ package com.alttd.altitudeweb.services.chat;
import com.alttd.altitudeweb.controllers.chat.ChatMessage; import com.alttd.altitudeweb.controllers.chat.ChatMessage;
import com.alttd.altitudeweb.controllers.chat.ChatMessageMapper; import com.alttd.altitudeweb.controllers.chat.ChatMessageMapper;
import com.alttd.altitudeweb.controllers.chat.ServerStateMapper;
import com.alttd.altitudeweb.database.Databases; import com.alttd.altitudeweb.database.Databases;
import com.alttd.altitudeweb.database.chat.ChatLogMapper; import com.alttd.altitudeweb.database.chat.ChatLogMapper;
import com.alttd.altitudeweb.database.web_db.chat_session.ChatSession; import com.alttd.altitudeweb.database.web_db.chat_session.ChatSession;
import com.alttd.altitudeweb.database.web_db.chat_session.ChatSessionMapper; import com.alttd.altitudeweb.database.web_db.chat_session.ChatSessionMapper;
import com.alttd.altitudeweb.model.PermissionClaimDto; import com.alttd.altitudeweb.model.PermissionClaimDto;
import com.alttd.altitudeweb.model.ServerDto;
import com.alttd.altitudeweb.model.ServerStateDto;
import com.alttd.altitudeweb.services.chat.event_publisher.EventPublisher; import com.alttd.altitudeweb.services.chat.event_publisher.EventPublisher;
import com.alttd.altitudeweb.services.chat.event_publisher.EventUser; import com.alttd.altitudeweb.services.chat.event_publisher.EventUser;
import com.alttd.altitudeweb.services.chat.event_publisher.MessageForUser; import com.alttd.altitudeweb.services.chat.event_publisher.MessageForUser;
@ -121,6 +124,20 @@ public class ChatService {
sendMessagesToListeners(chatMessageList); sendMessagesToListeners(chatMessageList);
} }
public void updateServerState(ServerStateDto serverStateDto) {
sendToUsers("server-state", (eventUser) -> {
if (eventUser.hasPermission(PermissionClaimDto.HEAD_MOD)) {
return ServerStateMapper.toJson(serverStateDto);
}
List<ServerDto> filteredServers = serverStateDto.getServers().stream()
.filter(serverDto -> Arrays.asList(allowedServers).contains(serverDto.getName()))
.toList();
return ServerStateMapper.toJson(new ServerStateDto().servers(filteredServers));
});
}
private void sendMessagesToListeners(List<ChatMessage> chatMessageList) { private void sendMessagesToListeners(List<ChatMessage> chatMessageList) {
Map<ChatMessage, String> jsonCache = new HashMap<>(); Map<ChatMessage, String> jsonCache = new HashMap<>();
for (ChatMessage message : chatMessageList) { for (ChatMessage message : chatMessageList) {

View File

@ -0,0 +1,34 @@
package com.alttd.altitudeweb.controllers.chat;
import com.alttd.altitudeweb.model.ServerStateDto;
import com.alttd.altitudeweb.services.chat.ChatService;
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.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class ChatControllerTest {
@Mock
private ChatService chatService;
@InjectMocks
private ChatController chatController;
@Test
void testUpdateServerStatesDelegatesToService() {
ServerStateDto serverStateDto = new ServerStateDto();
ResponseEntity<Void> response = chatController.updateServerStates(serverStateDto);
verify(chatService).updateServerState(serverStateDto);
assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
}
}

View File

@ -3,6 +3,8 @@ package com.alttd.altitudeweb.services.chat;
import com.alttd.altitudeweb.controllers.chat.ChatMessage; import com.alttd.altitudeweb.controllers.chat.ChatMessage;
import com.alttd.altitudeweb.controllers.chat.ChatMessageType; import com.alttd.altitudeweb.controllers.chat.ChatMessageType;
import com.alttd.altitudeweb.model.PermissionClaimDto; import com.alttd.altitudeweb.model.PermissionClaimDto;
import com.alttd.altitudeweb.model.ServerDto;
import com.alttd.altitudeweb.model.ServerStateDto;
import com.alttd.altitudeweb.services.chat.event_publisher.EventPublisher; import com.alttd.altitudeweb.services.chat.event_publisher.EventPublisher;
import com.alttd.altitudeweb.services.chat.event_publisher.EventUser; import com.alttd.altitudeweb.services.chat.event_publisher.EventUser;
import com.alttd.altitudeweb.services.chat.event_publisher.MessageForUser; import com.alttd.altitudeweb.services.chat.event_publisher.MessageForUser;
@ -18,6 +20,7 @@ import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
class ChatServiceTest { class ChatServiceTest {
@ -152,4 +155,28 @@ class ChatServiceTest {
assertFalse(messageForUser.get(regularUser).contains("blocked message")); assertFalse(messageForUser.get(regularUser).contains("blocked message"));
assertTrue(messageForUser.get(headModUser).contains("blocked message")); assertTrue(messageForUser.get(headModUser).contains("blocked message"));
} }
@Test
void testUpdateServerStateFiltering() {
ServerStateDto serverStateDto = new ServerStateDto()
.addServersItem(new ServerDto().name("server1"))
.addServersItem(new ServerDto().name("server2"));
chatService.updateServerState(serverStateDto);
ArgumentCaptor<MessageForUser> captor = ArgumentCaptor.forClass(MessageForUser.class);
verify(chatService).sendToUsers(eq("server-state"), captor.capture());
MessageForUser messageForUser = captor.getValue();
EventUser regularUser = new EventUser(UUID.randomUUID(), List.of(), null);
String regularResult = messageForUser.get(regularUser);
assertTrue(regularResult.contains("server1"), "Regular user should see allowed server");
assertFalse(regularResult.contains("server2"), "Regular user should NOT see non-allowed server");
EventUser headModUser = new EventUser(UUID.randomUUID(), List.of(PermissionClaimDto.HEAD_MOD.getValue()), null);
String headModResult = messageForUser.get(headModUser);
assertTrue(headModResult.contains("server1"), "HEAD_MOD should see allowed server");
assertTrue(headModResult.contains("server2"), "HEAD_MOD should see non-allowed server");
}
} }

View File

@ -53,6 +53,7 @@ components:
- uuid - uuid
- name - name
- styledName - styledName
# TODO [Stijn] [2026-07-19]: Add who they ignore and who they are ignored by
properties: properties:
uuid: uuid:
@ -140,11 +141,3 @@ components:
type: array type: array
items: items:
$ref: "#/components/schemas/Server" $ref: "#/components/schemas/Server"
parameters:
From:
name: from
in: path
required: true
schema:
type: string
format: date-time