Skip to content

Commit

Permalink
fixed paging for newer Spring
Browse files Browse the repository at this point in the history
  • Loading branch information
martin-kuba committed Oct 4, 2024
1 parent 32e2c45 commit 6a32c3e
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 113 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ This project demonstrates the following features:

## Installation

Prerequisites: git, [Apache Maven](https://maven.apache.org/) and JDK 17+
Prerequisites: git, [Apache Maven](https://maven.apache.org/) and JDK 21+

Download and compile:
```bash
Expand Down
17 changes: 9 additions & 8 deletions chat-client-java/src/main/java/cz/muni/chat/client/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import cz.muni.chat.client.model.BackgroundColorEnum;
import cz.muni.chat.client.model.ChatMessage;
import cz.muni.chat.client.model.NewChatMessageRequest;
import cz.muni.chat.client.model.PageChatMessage;
import cz.muni.chat.client.model.PageMetadata;
import cz.muni.chat.client.model.PagedModelChatMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
Expand Down Expand Up @@ -59,13 +60,13 @@ public void run(String... args) throws Exception {
//get paged messages
int pageIndex = 2;
int pageSize = 3;
PageChatMessage paged = chat.paged(pageIndex, pageSize, null);
log.info("paged messages: page={}/{} offset={} items={}/{} total={}",
paged.getNumber() + 1, paged.getTotalPages(),
paged.getPageable().getOffset(),
paged.getNumberOfElements(), paged.getSize(),
paged.getTotalElements());
for (ChatMessage chatMessage : paged.getContent()) {
PagedModelChatMessage pagedModel = chat.paged(pageIndex, pageSize, null);
PageMetadata page = pagedModel.getPage();
log.info("paged messages: page={}/{} page.size={} totalElements={}",
page.getNumber() + 1, page.getTotalPages(),
page.getSize(),
page.getTotalElements());
for (ChatMessage chatMessage : pagedModel.getContent()) {
log.info("msg: {}", chatMessage);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
import cz.muni.chat.generated.server.model.ErrorMessage;
import cz.muni.chat.generated.server.model.NewChatMessageRequest;
import cz.muni.chat.generated.server.model.NewChatMessageRequest.TextColorEnum;
import cz.muni.chat.generated.server.model.PageChatMessage;
import cz.muni.chat.generated.server.model.PageableObject;
import cz.muni.chat.generated.server.model.SortObject;
import cz.muni.chat.generated.server.model.PageMetadata;
import cz.muni.chat.generated.server.model.PagedModelChatMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
Expand Down Expand Up @@ -37,7 +36,7 @@ public ResponseEntity<List<ChatMessage>> getAllMessages() {
return new ResponseEntity<>(messages, HttpStatus.OK);
}

@SuppressWarnings({"rawtypes","unchecked"})
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public ResponseEntity<ChatMessage> getMessage(String id) {
log.debug("getMessage({})", id);
Expand All @@ -48,7 +47,7 @@ public ResponseEntity<ChatMessage> getMessage(String id) {
ErrorMessage errorMessage = new ErrorMessage()
.error(HttpStatus.NOT_FOUND.getReasonPhrase())
.status(HttpStatus.NOT_FOUND.value())
.path("/api/message/"+id)
.path("/api/message/" + id)
.timestamp(OffsetDateTime.now())
.message("message with id=" + id + " not found");
return new ResponseEntity(errorMessage, HttpStatus.NOT_FOUND);
Expand All @@ -70,40 +69,22 @@ public ResponseEntity<ChatMessage> createMessage(NewChatMessageRequest r, String
}

@Override
public ResponseEntity<PageChatMessage> paged(Integer page, Integer size, List<String> sort) {
public ResponseEntity<PagedModelChatMessage> paged(Integer page, Integer size, List<String> sort) {
log.debug("paged(page={}, size={})", page, size);
PageRequest p = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "timestamp"));
List<ChatMessage> chatMessages = messages.stream().skip(p.getOffset()).limit(p.getPageSize()).toList();
Page<ChatMessage> m = new PageImpl<>(chatMessages, p, messages.size());

// copy to generated models :-(
List<SortObject> s = p.getSort().get().map(order -> new SortObject()
.property(order.getProperty())
.ascending(order.isAscending())
.direction(order.getDirection().name())
.ignoreCase(order.isIgnoreCase())
.nullHandling(order.getNullHandling().name())
).toList();
PageableObject pageableObject = new PageableObject()
.paged(p.isPaged())
.unpaged(p.isUnpaged())
.pageNumber(p.getPageNumber())
.pageSize(p.getPageSize())
.sort(s)
.offset(p.getOffset());
PageChatMessage pageChatMessage = new PageChatMessage()
.content(chatMessages)
.pageable(pageableObject)
.last(m.isLast())
.first(m.isFirst())
.empty(m.isEmpty())
.totalPages(m.getTotalPages())
PageMetadata pageMetadata = new PageMetadata()
.size((long) m.getSize())
.number((long) m.getNumber())
.totalElements(m.getTotalElements())
.number(m.getNumber())
.numberOfElements(m.getNumberOfElements())
.size(m.getSize())
.sort(s);
return new ResponseEntity<>(pageChatMessage, HttpStatus.OK);
.totalPages((long) m.getTotalPages());
PagedModelChatMessage pagedModelChatMessage = new PagedModelChatMessage()
.content(chatMessages)
.page(pageMetadata);
return new ResponseEntity<>(pagedModelChatMessage, HttpStatus.OK);
}

private final List<ChatMessage> messages = new CopyOnWriteArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.data.web.config.EnableSpringDataWebSupport;

import static org.springframework.data.web.config.EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO;

@Configuration
// fix for paging
// see https://docs.spring.io/spring-data/commons/reference/repositories/core-extensions.html#core.web.page
@EnableSpringDataWebSupport(pageSerializationMode = VIA_DTO)
public class ApiConfig {

private static final Logger log = LoggerFactory.getLogger(ApiConfig.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,10 @@ void testPaging() throws Exception {
int pageSize = 3;
mockMvc.perform(get("/api/paged?page={page}&size={size}", pageIndex, pageSize).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.pageable.paged").value(true))
.andExpect(jsonPath("$.pageable.pageNumber").value(pageIndex))
.andExpect(jsonPath("$.pageable.pageSize").value(pageSize))
.andExpect(jsonPath("$.totalElements").value(totalMessages))
.andExpect(jsonPath("$.numberOfElements").value(pageSize))
.andExpect(jsonPath("$.page.size").value(pageSize))
.andExpect(jsonPath("$.page.number").value(pageIndex))
.andExpect(jsonPath("$.page.totalElements").value(totalMessages))
.andExpect(jsonPath("$.page.totalPages").value(4))
.andExpect(jsonPath("$.content.length()").value(pageSize))
// .andDo(print())
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

Expand All @@ -29,6 +30,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.springframework.data.web.config.EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
Expand All @@ -45,6 +47,7 @@
* and with mock implementation of Spring MVC that calls the RestController.
*/
@WebMvcTest(ChatRestController.class)
@EnableSpringDataWebSupport(pageSerializationMode = VIA_DTO)
class ChatRestControllerUnitTests {

private static final Logger log = LoggerFactory.getLogger(ChatRestControllerUnitTests.class);
Expand Down
91 changes: 24 additions & 67 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ paths:
schema:
type: array
items:
$ref: '#/components/schemas/ChatMessage'
$ref: "#/components/schemas/ChatMessage"
post:
tags:
- Chat
Expand All @@ -83,17 +83,17 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/NewChatMessageRequest'
$ref: "#/components/schemas/NewChatMessageRequest"
required: true
responses:
"201":
$ref: '#/components/responses/SingleMessageResponse'
$ref: "#/components/responses/SingleMessageResponse"
"400":
description: input data were not correct
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
$ref: "#/components/schemas/ErrorMessage"
/api/paged:
get:
tags:
Expand Down Expand Up @@ -137,7 +137,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/PageChatMessage'
$ref: "#/components/schemas/PagedModelChatMessage"
/api/message/{id}:
get:
tags:
Expand All @@ -153,13 +153,13 @@ paths:
type: string
responses:
"200":
$ref: '#/components/responses/SingleMessageResponse'
$ref: "#/components/responses/SingleMessageResponse"
"404":
description: message not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
$ref: "#/components/schemas/ErrorMessage"
components:
schemas:
ErrorMessage:
Expand Down Expand Up @@ -199,7 +199,7 @@ components:
- aquamarine
- lightyellow
- lightblue
- '#ffe4c4'
- "#ffe4c4"
NewChatMessageRequest:
required:
- text
Expand All @@ -218,7 +218,7 @@ components:
- blue
- darkgrey
backgroundColor:
$ref: '#/components/schemas/BackgroundColorEnum'
$ref: "#/components/schemas/BackgroundColorEnum"
description: |
Object for requesting new message.
**Text** of the message must be located in the request body because URLs are limited in size.
Expand Down Expand Up @@ -253,82 +253,39 @@ components:
description: HTML color name or RGB hex code
example: black
backgroundColor:
$ref: '#/components/schemas/BackgroundColorEnum'
$ref: "#/components/schemas/BackgroundColorEnum"
description: represents a message in a chat
PageChatMessage:
PageMetadata:
type: object
properties:
totalPages:
type: integer
format: int32
totalElements:
type: integer
format: int64
pageable:
$ref: '#/components/schemas/PageableObject'
first:
type: boolean
last:
type: boolean
size:
type: integer
format: int32
content:
type: array
items:
$ref: '#/components/schemas/ChatMessage'
format: int64
number:
type: integer
format: int32
sort:
type: array
items:
$ref: '#/components/schemas/SortObject'
numberOfElements:
type: integer
format: int32
empty:
type: boolean
PageableObject:
type: object
properties:
pageNumber:
type: integer
format: int32
pageSize:
format: int64
totalElements:
type: integer
format: int32
offset:
format: int64
totalPages:
type: integer
format: int64
sort:
type: array
items:
$ref: '#/components/schemas/SortObject'
paged:
type: boolean
unpaged:
type: boolean
SortObject:
PagedModelChatMessage:
type: object
properties:
direction:
type: string
nullHandling:
type: string
ascending:
type: boolean
property:
type: string
ignoreCase:
type: boolean
content:
type: array
items:
$ref: "#/components/schemas/ChatMessage"
page:
$ref: "#/components/schemas/PageMetadata"
responses:
SingleMessageResponse:
description: response containing a single message
content:
application/json:
schema:
$ref: '#/components/schemas/ChatMessage'
$ref: "#/components/schemas/ChatMessage"
links:
link_to_getMessage:
operationId: getMessage
Expand Down

0 comments on commit 6a32c3e

Please sign in to comment.