From bf1f89fde84de1b6485717a219e6416eaad1d1c7 Mon Sep 17 00:00:00 2001 From: "humoyun.norboboev" Date: Mon, 2 Feb 2026 17:06:09 +0500 Subject: [PATCH] feat(EDGRTAC-126): Add support for sorting in RTAC cache search APIs --- .../edge/rtac/client/RtacCacheClient.java | 7 +++-- .../rtac/controller/RtacCacheController.java | 10 ++++--- .../edge/rtac/service/RtacCacheService.java | 8 +++--- src/main/resources/swagger.api/edge-rtac.yaml | 11 ++++++++ .../folio/edge/rtac/BaseIntegrationTests.java | 7 +++++ .../controller/RtacCacheControllerIT.java | 10 +++++++ .../rtac/service/RtacCacheServiceTest.java | 27 ++++++++++++------- .../resources/mappings/rtac-cache-search.json | 27 ++++++++++++++++--- 8 files changed, 85 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/folio/edge/rtac/client/RtacCacheClient.java b/src/main/java/org/folio/edge/rtac/client/RtacCacheClient.java index 7dbb2c3..3eda351 100644 --- a/src/main/java/org/folio/edge/rtac/client/RtacCacheClient.java +++ b/src/main/java/org/folio/edge/rtac/client/RtacCacheClient.java @@ -18,10 +18,13 @@ String searchRtacCacheHoldings(@PathVariable("instanceId") String instanceId, @RequestParam String query, @RequestParam(required = false) Boolean available, @RequestParam Integer limit, - @RequestParam Integer offset); + @RequestParam Integer offset, + @RequestParam(required = false) String sort); @GetMapping(value = "/rtac-cache/{instanceId}", consumes = MediaType.APPLICATION_JSON_VALUE) - String rtacCacheById(@PathVariable String instanceId, @RequestParam Integer limit, @RequestParam Integer offset); + String rtacCacheById(@PathVariable String instanceId, + @RequestParam Integer limit, @RequestParam Integer offset, + @RequestParam(required = false) String sort); @PostMapping(value = "/rtac-cache/batch", consumes = MediaType.APPLICATION_JSON_VALUE) String rtacCacheBatch(@RequestBody RtacRequest rtacRequest); diff --git a/src/main/java/org/folio/edge/rtac/controller/RtacCacheController.java b/src/main/java/org/folio/edge/rtac/controller/RtacCacheController.java index 834d200..4961b07 100644 --- a/src/main/java/org/folio/edge/rtac/controller/RtacCacheController.java +++ b/src/main/java/org/folio/edge/rtac/controller/RtacCacheController.java @@ -21,19 +21,23 @@ public ResponseEntity searchRtacCacheHoldings(String instanceId, Boolean available, Integer limit, Integer offset, + String sort, String authorization, String apiKey) { - var holdings = rtacCacheService.searchRtacCacheHoldings(instanceId, query, available, limit, offset); + var holdings = rtacCacheService.searchRtacCacheHoldings(instanceId, query, available, limit, offset, sort); return ResponseEntity.ok(holdings); } + @Override public ResponseEntity getRtacCacheHoldingsById(String instanceId, Integer limit, Integer offset, + String sort, String authorization, - String apiKey) { - var holdings = rtacCacheService.getRtacCacheHoldingsById(instanceId, limit, offset); + String apiKey + ) { + var holdings = rtacCacheService.getRtacCacheHoldingsById(instanceId, limit, offset, sort); return ResponseEntity.ok(holdings); } diff --git a/src/main/java/org/folio/edge/rtac/service/RtacCacheService.java b/src/main/java/org/folio/edge/rtac/service/RtacCacheService.java index 314c78c..e25322a 100644 --- a/src/main/java/org/folio/edge/rtac/service/RtacCacheService.java +++ b/src/main/java/org/folio/edge/rtac/service/RtacCacheService.java @@ -14,12 +14,12 @@ public class RtacCacheService { private final RtacCacheClient rtacCacheClient; public String searchRtacCacheHoldings(String instanceId, String query, Boolean available, - Integer limit, Integer offset) { - return rtacCacheClient.searchRtacCacheHoldings(instanceId, query, available, limit, offset); + Integer limit, Integer offset, String sort) { + return rtacCacheClient.searchRtacCacheHoldings(instanceId, query, available, limit, offset, sort); } - public String getRtacCacheHoldingsById(String instanceId, Integer limit, Integer offset) { - return rtacCacheClient.rtacCacheById(instanceId, limit, offset); + public String getRtacCacheHoldingsById(String instanceId, Integer limit, Integer offset, String sort) { + return rtacCacheClient.rtacCacheById(instanceId, limit, offset, sort); } public String getRtacCacheBatchHoldings(RtacRequest rtacRequest) { diff --git a/src/main/resources/swagger.api/edge-rtac.yaml b/src/main/resources/swagger.api/edge-rtac.yaml index 0befead..1f7380e 100644 --- a/src/main/resources/swagger.api/edge-rtac.yaml +++ b/src/main/resources/swagger.api/edge-rtac.yaml @@ -97,6 +97,7 @@ paths: - $ref: '#/components/parameters/available-parameter' - $ref: '#/components/parameters/limit' - $ref: '#/components/parameters/offset' + - $ref: '#/components/parameters/sort' - $ref: '#/components/parameters/authorization' - $ref: '#/components/parameters/apiKey' responses: @@ -126,6 +127,7 @@ paths: - $ref: '#/components/parameters/instance-id-path' - $ref: '#/components/parameters/limit' - $ref: '#/components/parameters/offset' + - $ref: '#/components/parameters/sort' - $ref: '#/components/parameters/authorization' - $ref: '#/components/parameters/apiKey' responses: @@ -321,6 +323,15 @@ components: required: false schema: type: boolean + sort: + name: sort + in: query + description: | + Sort criteria in the format: `property,direction` (e.g., `locationName,asc`). + Supported properties are: effectiveShelvingOrder, libraryName, locationName, status. + The default sort order is effectiveShelvingOrder, libraryName, locationName, status (ascending). + schema: + type: string responses: healthCheckResponse: diff --git a/src/test/java/org/folio/edge/rtac/BaseIntegrationTests.java b/src/test/java/org/folio/edge/rtac/BaseIntegrationTests.java index af5e122..a66d7a7 100644 --- a/src/test/java/org/folio/edge/rtac/BaseIntegrationTests.java +++ b/src/test/java/org/folio/edge/rtac/BaseIntegrationTests.java @@ -8,6 +8,7 @@ import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer; import java.util.List; +import java.util.Map; import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; import org.folio.edge.rtac.config.RtacClientRequestInterceptor; @@ -92,6 +93,12 @@ protected static ResultActions doGetWithParam(MockMvc mockMvc, String url, Strin .accept(acceptType)); } + protected static ResultActions doGetWithParams(MockMvc mockMvc, String url, Map params) throws Exception { + var request = get(url).headers(defaultHeaders()); + params.forEach(request::param); + return mockMvc.perform(request); + } + private static HttpHeaders defaultHeaders() { final HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(APPLICATION_JSON); diff --git a/src/test/java/org/folio/edge/rtac/controller/RtacCacheControllerIT.java b/src/test/java/org/folio/edge/rtac/controller/RtacCacheControllerIT.java index 222cb6a..ccb3b08 100644 --- a/src/test/java/org/folio/edge/rtac/controller/RtacCacheControllerIT.java +++ b/src/test/java/org/folio/edge/rtac/controller/RtacCacheControllerIT.java @@ -12,6 +12,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import java.util.Map; import org.folio.edge.rtac.BaseIntegrationTests; import org.folio.edge.rtac.TestConstants; import org.folio.edge.rtac.TestUtil; @@ -34,6 +35,15 @@ void searchInstanceRtacCache_shouldReturnCachedHoldings() throws Exception { .andExpect(jsonPath("$.totalRecords", equalTo(2))); } + @Test + void searchInstanceRtacCache_shouldReturnCachedHoldings_withSortParameter() throws Exception { + doGetWithParams(mockMvc, RTAC_CACHE_SEARCH_URL + INSTANCE_UUID, Map.of(QUERY_PARAM, QUERY_PARAM_VALUE, "sort", "locationName,asc")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.instanceId", equalTo(INSTANCE_UUID))) + .andExpect(jsonPath("$.holdings", notNullValue())) + .andExpect(jsonPath("$.totalRecords", equalTo(2))); + } + @Test void searchInstanceRtacCache_shouldReturnNotFound_whenInstanceDoesNotExist() throws Exception { doGetWithParam(mockMvc, RTAC_CACHE_SEARCH_URL + NON_EXISTENT_INSTANCE_UUID, QUERY_PARAM, QUERY_PARAM_VALUE) diff --git a/src/test/java/org/folio/edge/rtac/service/RtacCacheServiceTest.java b/src/test/java/org/folio/edge/rtac/service/RtacCacheServiceTest.java index b160063..6e9ba48 100644 --- a/src/test/java/org/folio/edge/rtac/service/RtacCacheServiceTest.java +++ b/src/test/java/org/folio/edge/rtac/service/RtacCacheServiceTest.java @@ -24,6 +24,8 @@ class RtacCacheServiceTest { private static final Integer OFFSET_0 = 0; private static final Integer LIMIT_5 = 5; private static final Integer OFFSET_2 = 2; + private static final String SORT_BY_LOCATION = "locationName,asc"; + private static final String SORT_BY_STATUS = "status,desc"; @InjectMocks private RtacCacheService rtacCacheService; @@ -35,28 +37,28 @@ class RtacCacheServiceTest { @Test void searchRtacCacheHoldings_shouldDelegateToClient_andReturnResult() { // given - when(rtacCacheClient.searchRtacCacheHoldings(INSTANCE_ID_1, QUERY_TITLE_FOO, AVAILABLE_TRUE, LIMIT_10, OFFSET_0)) + when(rtacCacheClient.searchRtacCacheHoldings(INSTANCE_ID_1, QUERY_TITLE_FOO, AVAILABLE_TRUE, LIMIT_10, OFFSET_0, SORT_BY_LOCATION)) .thenReturn("search-response"); // when - String result = rtacCacheService.searchRtacCacheHoldings(INSTANCE_ID_1, QUERY_TITLE_FOO, AVAILABLE_TRUE, LIMIT_10, OFFSET_0); + String result = rtacCacheService.searchRtacCacheHoldings(INSTANCE_ID_1, QUERY_TITLE_FOO, AVAILABLE_TRUE, LIMIT_10, OFFSET_0, SORT_BY_LOCATION); // then assertThat(result).isNotNull(); - assertSearchDelegation(INSTANCE_ID_1, QUERY_TITLE_FOO, AVAILABLE_TRUE, LIMIT_10, OFFSET_0); + assertSearchDelegation(INSTANCE_ID_1, QUERY_TITLE_FOO, AVAILABLE_TRUE, LIMIT_10, OFFSET_0, SORT_BY_LOCATION); } @Test void getRtacCacheHoldingsById_shouldDelegateToClient_andReturnResult() { // given - when(rtacCacheClient.rtacCacheById(INSTANCE_ID_2, LIMIT_5, OFFSET_2)).thenReturn("single-response"); + when(rtacCacheClient.rtacCacheById(INSTANCE_ID_2, LIMIT_5, OFFSET_2, SORT_BY_STATUS)).thenReturn("single-response"); // when - String result = rtacCacheService.getRtacCacheHoldingsById(INSTANCE_ID_2, LIMIT_5, OFFSET_2); + String result = rtacCacheService.getRtacCacheHoldingsById(INSTANCE_ID_2, LIMIT_5, OFFSET_2, SORT_BY_STATUS); // then assertThat(result).isNotNull(); - assertByIdDelegation(INSTANCE_ID_2, LIMIT_5, OFFSET_2); + assertByIdDelegation(INSTANCE_ID_2, LIMIT_5, OFFSET_2, SORT_BY_STATUS); } @Test @@ -73,19 +75,21 @@ void getRtacCacheBatchHoldings_shouldDelegateToClient_andReturnResult() { assertBatchDelegation(request); } - private void assertSearchDelegation(String instanceId, String query, Boolean available, Integer limit, Integer offset) { + private void assertSearchDelegation(String instanceId, String query, Boolean available, Integer limit, Integer offset, String sort) { ArgumentCaptor instanceCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor availableCaptor = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor limitCaptor = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor offsetCaptor = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor sortCaptor = ArgumentCaptor.forClass(String.class); verify(rtacCacheClient).searchRtacCacheHoldings( instanceCaptor.capture(), queryCaptor.capture(), availableCaptor.capture(), limitCaptor.capture(), - offsetCaptor.capture() + offsetCaptor.capture(), + sortCaptor.capture() ); assertThat(instanceCaptor.getValue()).isEqualTo(instanceId); @@ -93,18 +97,21 @@ private void assertSearchDelegation(String instanceId, String query, Boolean ava assertThat(availableCaptor.getValue()).isEqualTo(available); assertThat(limitCaptor.getValue()).isEqualTo(limit); assertThat(offsetCaptor.getValue()).isEqualTo(offset); + assertThat(sortCaptor.getValue()).isEqualTo(sort); } - private void assertByIdDelegation(String instanceId, Integer limit, Integer offset) { + private void assertByIdDelegation(String instanceId, Integer limit, Integer offset, String sort) { ArgumentCaptor instanceCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor limitCaptor = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor offsetCaptor = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor sortCaptor = ArgumentCaptor.forClass(String.class); - verify(rtacCacheClient).rtacCacheById(instanceCaptor.capture(), limitCaptor.capture(), offsetCaptor.capture()); + verify(rtacCacheClient).rtacCacheById(instanceCaptor.capture(), limitCaptor.capture(), offsetCaptor.capture(), sortCaptor.capture()); assertThat(instanceCaptor.getValue()).isEqualTo(instanceId); assertThat(limitCaptor.getValue()).isEqualTo(limit); assertThat(offsetCaptor.getValue()).isEqualTo(offset); + assertThat(sortCaptor.getValue()).isEqualTo(sort); } private void assertBatchDelegation(RtacRequest request) { diff --git a/src/test/resources/mappings/rtac-cache-search.json b/src/test/resources/mappings/rtac-cache-search.json index 73b6b39..4be75ae 100644 --- a/src/test/resources/mappings/rtac-cache-search.json +++ b/src/test/resources/mappings/rtac-cache-search.json @@ -1,10 +1,32 @@ { "mappings": [ { - "priority": 1, "request": { "method": "GET", - "urlPath": "/rtac-cache/search/83034b0a-bf71-4495-b642-2e998f721e5d" + "urlPath": "/rtac-cache/search/83034b0a-bf71-4495-b642-2e998f721e5d", + "queryParameters": { + "sort": { + "equalTo": "locationName,asc" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "rtac-cache/rtac-cache-response.json" + } + }, + { + "request": { + "method": "GET", + "urlPath": "/rtac-cache/search/83034b0a-bf71-4495-b642-2e998f721e5d", + "queryParameters": { + "sort": { + "absent": true + } + } }, "response": { "status": 200, @@ -15,7 +37,6 @@ } }, { - "priority": 2, "request": { "method": "GET", "urlPath": "/rtac-cache/search/a826719e-4ed2-4681-95ca-d3de6bbdec66"