Skip to content

Commit

Permalink
Add retrieval of chat room backlogs
Browse files Browse the repository at this point in the history
Also add arguments to control the maximum number of lines and the maximum backlog date to retrieve for both private chats and backroom chats.
  • Loading branch information
zapek committed Dec 24, 2024
1 parent ee4ce4a commit a94ad71
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
import io.xeres.common.rest.chat.CreateChatRoomRequest;
import io.xeres.common.rest.chat.InviteToChatRoomRequest;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -45,6 +48,8 @@

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.EnumSet;
import java.util.List;
import java.util.stream.Collectors;
Expand All @@ -57,6 +62,11 @@
@RequestMapping(value = CHAT_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
public class ChatController
{
private static final int PRIVATE_CHAT_DEFAULT_MAX_LINES = 20;
private static final Duration PRIVATE_CHAT_DEFAULT_DURATION = Duration.ofDays(7);
private static final int ROOM_CHAT_DEFAULT_MAX_LINES = 50;
private static final Duration ROOM_CHAT_DEFAULT_DURATION = Duration.ofDays(7);

private final ChatRsService chatRsService;
private final ChatBacklogService chatBacklogService;
private final LocationService locationService;
Expand Down Expand Up @@ -118,17 +128,27 @@ public ChatRoomContextDTO getChatRoomContext()
@GetMapping("/rooms/{roomId}/messages")
@Operation(summary = "Get the chat room messages backlog")
@ApiResponse(responseCode = "200", description = "Request successful")
public List<ChatRoomBacklogDTO> getChatRoomMessages(@PathVariable long roomId)
public List<ChatRoomBacklogDTO> getChatRoomMessages(@PathVariable long roomId,
@RequestParam(value = "maxLines", required = false) @Min(1) @Max(500) Integer maxLines,
@RequestParam(value = "from", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime from)
{
return toChatRoomBacklogDTOs(chatBacklogService.getChatRoomMessages(roomId, Instant.now().minus(Duration.ofDays(7))));
return toChatRoomBacklogDTOs(chatBacklogService.getChatRoomMessages(
roomId,
from != null ? from.toInstant(ZoneOffset.UTC) : Instant.now().minus(ROOM_CHAT_DEFAULT_DURATION),
maxLines != null ? maxLines : ROOM_CHAT_DEFAULT_MAX_LINES));
}

@GetMapping("/chats/{locationId}/messages")
@Operation(summary = "Get the chat messages backlog")
@ApiResponse(responseCode = "200", description = "Request successful")
public List<ChatBacklogDTO> getChatMessages(@PathVariable long locationId)
public List<ChatBacklogDTO> getChatMessages(@PathVariable long locationId,
@RequestParam(value = "maxLines", required = false) @Min(1) @Max(500) Integer maxLines,
@RequestParam(value = "from", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime from)
{
var location = locationService.findLocationById(locationId).orElseThrow();
return toChatBacklogDTOs(chatBacklogService.getMessages(location, Instant.now().minus(Duration.ofDays(7))));
return toChatBacklogDTOs(chatBacklogService.getMessages(
location,
from != null ? from.toInstant(ZoneOffset.UTC) : Instant.now().minus(PRIVATE_CHAT_DEFAULT_DURATION),
maxLines != null ? maxLines : PRIVATE_CHAT_DEFAULT_MAX_LINES));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@
@Service
public class ChatBacklogService
{
private static final int LAST_LINES_CHAT = 20;
private static final int LAST_LINES_CHAT_ROOMS = 50;
private static final Duration MAXIMUM_DURATION = Duration.ofDays(31);

private final ChatBacklogRepository chatBacklogRepository;
Expand Down Expand Up @@ -71,10 +69,10 @@ public void storeOutgoingChatRoomMessage(long chatRoomId, String nickname, Strin
}

@Transactional(readOnly = true)
public List<ChatRoomBacklog> getChatRoomMessages(long chatRoomId, Instant from)
public List<ChatRoomBacklog> getChatRoomMessages(long chatRoomId, Instant from, int maxLines)
{
var chatRoom = chatRoomRepository.findByRoomId(chatRoomId).orElseThrow();
return chatRoomBacklogRepository.findAllByRoomAndCreatedAfterOrderByCreatedDesc(chatRoom, from, Limit.of(LAST_LINES_CHAT_ROOMS)).reversed();
return chatRoomBacklogRepository.findAllByRoomAndCreatedAfterOrderByCreatedDesc(chatRoom, from, Limit.of(maxLines)).reversed();
}

@Transactional
Expand All @@ -91,9 +89,9 @@ public void storeOutgoingMessage(LocationIdentifier to, String message)
chatBacklogRepository.save(new ChatBacklog(location, true, message));
}

public List<ChatBacklog> getMessages(Location with, Instant from)
public List<ChatBacklog> getMessages(Location with, Instant from, int maxLines)
{
return chatBacklogRepository.findAllByLocationAndCreatedAfterOrderByCreatedDesc(with, from, Limit.of(LAST_LINES_CHAT)).reversed();
return chatBacklogRepository.findAllByLocationAndCreatedAfterOrderByCreatedDesc(with, from, Limit.of(maxLines)).reversed();
}

public void storeIncomingDistantMessage(GxsId from, String message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
package io.xeres.app.api.controller.chat;

import io.xeres.app.api.controller.AbstractControllerTest;
import io.xeres.app.database.model.chat.ChatBacklog;
import io.xeres.app.database.model.chat.ChatRoomBacklog;
import io.xeres.app.database.model.chat.ChatRoomFakes;
import io.xeres.app.database.model.identity.IdentityFakes;
import io.xeres.app.database.model.location.LocationFakes;
import io.xeres.app.service.LocationService;
Expand All @@ -42,12 +45,18 @@
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static io.xeres.common.rest.PathConfig.CHAT_PATH;
import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
Expand Down Expand Up @@ -146,4 +155,86 @@ void GetChatRoomContext_Success() throws Exception

verify(chatRsService).getChatRoomContext();
}

@Test
void GetChatMessages_Default_Success() throws Exception
{
var creation = Instant.now();
var location = LocationFakes.createLocation();
var chatBacklog = new ChatBacklog(location, false, "hey");
chatBacklog.setCreated(creation);

when(locationService.findLocationById(location.getId())).thenReturn(Optional.of(location));
when(chatBacklogService.getMessages(eq(location), any(Instant.class), anyInt())).thenReturn(List.of(chatBacklog));

mvc.perform(getJson(BASE_URL + "/chats/" + location.getId() + "/messages"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].created", is(creation.toString())))
.andExpect(jsonPath("$[0].own", is(false)))
.andExpect(jsonPath("$[0].message", is("hey")));

verify(chatBacklogService).getMessages(eq(location), any(Instant.class), eq(20));
}

@Test
void GetChatMessages_WithParameters_Success() throws Exception
{
var creation = Instant.now();
var location = LocationFakes.createLocation();
var chatBacklog = new ChatBacklog(location, false, "hey");
chatBacklog.setCreated(creation);

when(locationService.findLocationById(location.getId())).thenReturn(Optional.of(location));
when(chatBacklogService.getMessages(eq(location), any(Instant.class), anyInt())).thenReturn(List.of(chatBacklog));

mvc.perform(getJson(BASE_URL + "/chats/" + location.getId() + "/messages?maxLines=30&from=2024-12-23T22:13"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].created", is(creation.toString())))
.andExpect(jsonPath("$[0].own", is(false)))
.andExpect(jsonPath("$[0].message", is("hey")));

verify(chatBacklogService).getMessages(location, LocalDateTime.parse("2024-12-23T22:13").toInstant(ZoneOffset.UTC), 30);
}

@Test
void GetChatRoomMessages_Default_Success() throws Exception
{
var creation = Instant.now();
var chatRoom = ChatRoomFakes.createChatRoomEntity();
var ownIdentity = IdentityFakes.createOwn();
var chatRoomBacklog = new ChatRoomBacklog(chatRoom, ownIdentity.getGxsId(), "Foobar", "blabla");
chatRoomBacklog.setCreated(creation);

when(chatBacklogService.getChatRoomMessages(eq(chatRoom.getRoomId()), any(Instant.class), anyInt())).thenReturn(List.of(chatRoomBacklog));

mvc.perform(getJson(BASE_URL + "/rooms/" + chatRoom.getRoomId() + "/messages"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].created", is(creation.toString())))
.andExpect(jsonPath("$[0].gxsId.bytes", is(Base64.toBase64String(ownIdentity.getGxsId().getBytes()))))
.andExpect(jsonPath("$[0].nickname", is("Foobar")))
.andExpect(jsonPath("$[0].message", is("blabla")));

verify(chatBacklogService).getChatRoomMessages(eq(chatRoom.getRoomId()), any(Instant.class), eq(50));
}

@Test
void GetChatRoomMessages_WithParameters_Success() throws Exception
{
var creation = Instant.now();
var chatRoom = ChatRoomFakes.createChatRoomEntity();
var ownIdentity = IdentityFakes.createOwn();
var chatRoomBacklog = new ChatRoomBacklog(chatRoom, ownIdentity.getGxsId(), "Foobar", "blabla");
chatRoomBacklog.setCreated(creation);

when(chatBacklogService.getChatRoomMessages(eq(chatRoom.getRoomId()), any(Instant.class), anyInt())).thenReturn(List.of(chatRoomBacklog));

mvc.perform(getJson(BASE_URL + "/rooms/" + chatRoom.getRoomId() + "/messages?maxLines=80&from=2024-12-24T01:27"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].created", is(creation.toString())))
.andExpect(jsonPath("$[0].gxsId.bytes", is(Base64.toBase64String(ownIdentity.getGxsId().getBytes()))))
.andExpect(jsonPath("$[0].nickname", is("Foobar")))
.andExpect(jsonPath("$[0].message", is("blabla")));

verify(chatBacklogService).getChatRoomMessages(chatRoom.getRoomId(), LocalDateTime.parse("2024-12-24T01:27").toInstant(ZoneOffset.UTC), 80);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -648,12 +648,32 @@ private ChatListView getChatListViewOrCreate(TreeItem<RoomHolder> roomInfoTreeIt
var chatListView = roomInfoTreeItem.getValue().getChatListView();
if (chatListView == null)
{
chatListView = new ChatListView(nickname, roomInfoTreeItem.getValue().getRoomInfo().getId(), markdownService, uriService, generalClient, imageCache);
var chatRoomId = roomInfoTreeItem.getValue().getRoomInfo().getId();
chatListView = new ChatListView(nickname, chatRoomId, markdownService, uriService, generalClient, imageCache);
var finalChatListView = chatListView;
chatClient.getChatRoomBacklog(chatRoomId).collectList()
.doOnSuccess(backlogs -> Platform.runLater(() -> fillBacklog(finalChatListView, backlogs)))
.subscribe();
roomInfoTreeItem.getValue().setChatListView(chatListView);
}
return chatListView;
}

private void fillBacklog(ChatListView chatListView, List<ChatRoomBacklog> messages)
{
messages.forEach(message -> {
if (message.gxsId() == null)
{
chatListView.addOwnMessage(message.create(), message.message());
}
else
{
chatListView.addUserMessage(message.nickname(), message.gxsId(), message.message());
}
});
chatListView.jumpToBottom(true);
}

private void handleInputKeys(KeyEvent event)
{
if (TAB_KEY.match(event))
Expand Down

0 comments on commit a94ad71

Please sign in to comment.