Skip to content

Commit

Permalink
Merge pull request #23 from nramc/22-feat-implement-get-all-journeys-…
Browse files Browse the repository at this point in the history
…with-basic-query-params

22 feat implement get all journeys with basic query params
  • Loading branch information
nramc authored Mar 27, 2024
2 parents 55f33d5 + 3fe2915 commit dad6d6c
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ public class Resources {
public static final String HOME = "";
public static final String CREATE_JOURNEY = "/rest/journey";
public static final String FIND_JOURNEY = "/rest/journey/{id}";
public static final String FIND_JOURNEYS = "/rest/journeys";
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.constraints.UUID;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Optional;

import static com.github.nramc.dev.journey.api.web.resources.Resources.FIND_JOURNEY;
import static com.github.nramc.dev.journey.api.web.resources.Resources.FIND_JOURNEYS;

@RestController
@Slf4j
Expand All @@ -33,4 +38,21 @@ public ResponseEntity<FindJourneyResponse> findAndReturnJson(@Valid @NotBlank @P
return ResponseEntity.of(findJourneyResponse);
}

@GetMapping(value = FIND_JOURNEYS, produces = MediaType.APPLICATION_JSON_VALUE)
public Page<FindJourneyResponse> findAllAndReturnJson(
@RequestParam(name = "pageIndex", defaultValue = "0") int pageIndex,
@RequestParam(name = "pageSize", defaultValue = "10") int pageSize,
@RequestParam(name = "sort", defaultValue = "createdDate") String sortColumn,
@RequestParam(name = "order", defaultValue = "DESC") Sort.Direction sortOrder) {

Pageable pageable = PageRequest.of(pageIndex, pageSize, Sort.by(sortOrder, sortColumn));

Page<JourneyEntity> entityPage = journeyRepository.findAll(pageable);
Page<FindJourneyResponse> responsePage = entityPage.map(FindJourneyConverter::convert);

log.info("Journey exists:[{}] pages:[{}] total:[{}]",
responsePage.hasContent(), responsePage.getTotalPages(), responsePage.getTotalElements());
return responsePage;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,33 @@
import com.github.nramc.dev.journey.api.repository.journey.JourneyRepository;
import com.github.nramc.dev.journey.api.web.resources.Resources;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.testcontainers.containers.MongoDBContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;
import java.util.stream.IntStream;

import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(controllers = {FindJourneyResource.class})
@ActiveProfiles({"prod", "test"})
@Testcontainers
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles({"test"})
@AutoConfigureMockMvc
class FindJourneyResourceTest {
private static final String VALID_UUID = "ecc76991-0137-4152-b3b2-efce70a37ed0";
private static final String VALID_JSON_RESPONSE = """
Expand Down Expand Up @@ -62,12 +70,17 @@ class FindJourneyResourceTest {
.build();
@Autowired
private MockMvc mockMvc;
@MockBean
@Container
@ServiceConnection
static MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:latest"))
.withExposedPorts(27017);

@Autowired
private JourneyRepository journeyRepository;

@Test
void findAndReturnJson_whenJourneyExists_ShouldReturnValidJson() throws Exception {
Mockito.when(journeyRepository.findById(VALID_UUID)).thenReturn(Optional.of(VALID_JOURNEY));
journeyRepository.save(VALID_JOURNEY);

mockMvc.perform(MockMvcRequestBuilders.get(Resources.FIND_JOURNEY, VALID_UUID)
.accept(MediaType.APPLICATION_JSON)
Expand All @@ -79,7 +92,7 @@ void findAndReturnJson_whenJourneyExists_ShouldReturnValidJson() throws Exceptio

@Test
void findAndReturnJson_whenJourneyNotExists_shouldReturnError() throws Exception {
Mockito.when(journeyRepository.findById(VALID_UUID)).thenReturn(Optional.empty());
//Mockito.when(journeyRepository.findById(VALID_UUID)).thenReturn(Optional.empty());

mockMvc.perform(MockMvcRequestBuilders.get(Resources.FIND_JOURNEY, VALID_UUID)
.accept(MediaType.APPLICATION_JSON)
Expand All @@ -96,4 +109,104 @@ void findAndReturnJson_whenIdNotValid_thenShouldThrowError() throws Exception {
.andExpect(status().isBadRequest());

}

@Test
void findAllAndReturnJson_whenPagingAndSortingFieldGiven_shouldReturnCorrespondingPageWithRequestedSorting_withSecondPageAndAscendingSort() throws Exception {
// setup data
IntStream.range(0, 10).forEach(index -> {
journeyRepository.save(VALID_JOURNEY.toBuilder().id("ID_" + index).createdDate(LocalDate.now().plusDays(index)).build());
});

// Request result with page number 1 (second page) and order by id ascending
mockMvc.perform(MockMvcRequestBuilders.get(Resources.FIND_JOURNEYS)
.accept(MediaType.APPLICATION_JSON)
.param("sort", "id")
.param("order", "ASC")
.param("pageIndex", "1")
.param("pageSize", "5")
).andDo(print())
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
// assert paging
.andExpect(jsonPath("$.pageable.pageNumber").value("1"))
.andExpect(jsonPath("$.pageable.pageSize").value("5"))
.andExpect(jsonPath("$.totalPages").value("2"))
.andExpect(jsonPath("$.totalElements").value("10"))
// assert sorting order
.andExpect(jsonPath("$.sort.sorted").value("true"))
.andExpect(jsonPath("$.content").isArray())
.andExpect(jsonPath("$.content[0].id").value("ID_5"))
.andExpect(jsonPath("$.content[1].id").value("ID_6"))
.andExpect(jsonPath("$.content[2].id").value("ID_7"))
.andExpect(jsonPath("$.content[3].id").value("ID_8"))
.andExpect(jsonPath("$.content[4].id").value("ID_9"))
.andExpect(jsonPath("$.content[5]").doesNotExist());
}

@Test
void findAllAndReturnJson_whenPagingAndSortingFieldGiven_shouldReturnCorrespondingPageWithRequestedSorting_withSecondPageAndDescendingSort() throws Exception {
// setup data
IntStream.range(0, 10).forEach(index -> {
journeyRepository.save(VALID_JOURNEY.toBuilder().id("ID_" + index).createdDate(LocalDate.now().plusDays(index)).build());
});

// Request result with page number 1 (second page) and order by id ascending
mockMvc.perform(MockMvcRequestBuilders.get(Resources.FIND_JOURNEYS)
.accept(MediaType.APPLICATION_JSON)
.param("sort", "id")
.param("order", "DESC")
.param("pageIndex", "1")
.param("pageSize", "5")
).andDo(print())
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
// assert paging
.andExpect(jsonPath("$.pageable.pageNumber").value("1"))
.andExpect(jsonPath("$.pageable.pageSize").value("5"))
.andExpect(jsonPath("$.totalPages").value("2"))
.andExpect(jsonPath("$.totalElements").value("10"))
// assert sorting order
.andExpect(jsonPath("$.sort.sorted").value("true"))
.andExpect(jsonPath("$.content").isArray())
.andExpect(jsonPath("$.content[0].id").value("ID_4"))
.andExpect(jsonPath("$.content[1].id").value("ID_3"))
.andExpect(jsonPath("$.content[2].id").value("ID_2"))
.andExpect(jsonPath("$.content[3].id").value("ID_1"))
.andExpect(jsonPath("$.content[4].id").value("ID_0"))
.andExpect(jsonPath("$.content[5]").doesNotExist());
}

@Test
void findAllAndReturnJson_whenPagingAndSortingParamsNotGiven_thenShouldConsiderDefaultValues() throws Exception {
// setup data
IntStream.range(0, 10).forEach(index -> {
journeyRepository.save(VALID_JOURNEY.toBuilder().id("ID_" + index).createdDate(LocalDate.now().plusDays(index)).build());
});

// Request result with page number 1 (second page) and order by id ascending
mockMvc.perform(MockMvcRequestBuilders.get(Resources.FIND_JOURNEYS)
.accept(MediaType.APPLICATION_JSON)
).andDo(print())
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
// assert paging
.andExpect(jsonPath("$.pageable.pageNumber").value("0"))
.andExpect(jsonPath("$.pageable.pageSize").value("10"))
.andExpect(jsonPath("$.totalPages").value("1"))
.andExpect(jsonPath("$.totalElements").value("10"))
// assert sorting order
.andExpect(jsonPath("$.sort.sorted").value("true"))
.andExpect(jsonPath("$.content").isArray())
.andExpect(jsonPath("$.content[0].createdDate").value(LocalDate.now().plusDays(9).format(DateTimeFormatter.ISO_LOCAL_DATE)))
.andExpect(jsonPath("$.content[1].createdDate").value(LocalDate.now().plusDays(8).format(DateTimeFormatter.ISO_LOCAL_DATE)))
.andExpect(jsonPath("$.content[2].createdDate").value(LocalDate.now().plusDays(7).format(DateTimeFormatter.ISO_LOCAL_DATE)))
.andExpect(jsonPath("$.content[3].createdDate").value(LocalDate.now().plusDays(6).format(DateTimeFormatter.ISO_LOCAL_DATE)))
.andExpect(jsonPath("$.content[4].createdDate").value(LocalDate.now().plusDays(5).format(DateTimeFormatter.ISO_LOCAL_DATE)))
.andExpect(jsonPath("$.content[5].createdDate").value(LocalDate.now().plusDays(4).format(DateTimeFormatter.ISO_LOCAL_DATE)))
.andExpect(jsonPath("$.content[6].createdDate").value(LocalDate.now().plusDays(3).format(DateTimeFormatter.ISO_LOCAL_DATE)))
.andExpect(jsonPath("$.content[7].createdDate").value(LocalDate.now().plusDays(2).format(DateTimeFormatter.ISO_LOCAL_DATE)))
.andExpect(jsonPath("$.content[8].createdDate").value(LocalDate.now().plusDays(1).format(DateTimeFormatter.ISO_LOCAL_DATE)))
.andExpect(jsonPath("$.content[9].createdDate").value(LocalDate.now().plusDays(0).format(DateTimeFormatter.ISO_LOCAL_DATE)))
.andExpect(jsonPath("$.content[10]").doesNotExist());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"content": [
{
"id": "ID_5", "name": "First Flight Experience", "title": "One of the most beautiful experience ever in my life",
"description": "Travelled first time for work deputation to Germany, Munich city", "category": "Travel",
"city": "Munich", "country": "Germany", "tags": ["Travel", "Germany", "Munich"], "thumbnail": "valid image id",
"location": {"type": "Point", "type": "Point", "coordinates": [48.183160038296585, 11.53090747669896]},
"journeyDate": "2024-03-27", "createdDate": "2024-04-01"
}, {
"id": "ID_6", "name": "First Flight Experience", "title": "One of the most beautiful experience ever in my life",
"description": "Travelled first time for work deputation to Germany, Munich city", "category": "Travel",
"city": "Munich", "country": "Germany", "tags": ["Travel", "Germany", "Munich"], "thumbnail": "valid image id",
"location": {"type": "Point", "type": "Point", "coordinates": [48.183160038296585, 11.53090747669896]},
"journeyDate": "2024-03-27", "createdDate": "2024-04-02"
}, {
"id": "ID_7", "name": "First Flight Experience", "title": "One of the most beautiful experience ever in my life",
"description": "Travelled first time for work deputation to Germany, Munich city", "category": "Travel",
"city": "Munich", "country": "Germany", "tags": ["Travel", "Germany", "Munich"], "thumbnail": "valid image id",
"location": {"type": "Point", "type": "Point", "coordinates": [48.183160038296585, 11.53090747669896]},
"journeyDate": "2024-03-27", "createdDate": "2024-04-03"
}, {
"id": "ID_8", "name": "First Flight Experience", "title": "One of the most beautiful experience ever in my life",
"description": "Travelled first time for work deputation to Germany, Munich city", "category": "Travel",
"city": "Munich", "country": "Germany", "tags": ["Travel", "Germany", "Munich"], "thumbnail": "valid image id",
"location": {"type": "Point", "type": "Point", "coordinates": [48.183160038296585, 11.53090747669896]},
"journeyDate": "2024-03-27", "createdDate": "2024-04-04"
}, {
"id": "ID_9", "name": "First Flight Experience", "title": "One of the most beautiful experience ever in my life",
"description": "Travelled first time for work deputation to Germany, Munich city", "category": "Travel",
"city": "Munich", "country": "Germany", "tags": ["Travel", "Germany", "Munich"], "thumbnail": "valid image id",
"location": {"type": "Point", "type": "Point", "coordinates": [48.183160038296585, 11.53090747669896]},
"journeyDate": "2024-03-27", "createdDate": "2024-04-05"
}
], "pageable": {
"pageNumber": 1, "pageSize": 5, "sort": {"unsorted": false, "sorted": true, "empty": false}, "offset": 5,
"paged": true, "unpaged": false
}, "totalPages": 2, "totalElements": 10, "last": true, "numberOfElements": 5,
"sort": {"unsorted": false, "sorted": true, "empty": false}, "size": 5, "first": false, "number": 1, "empty": false
}

0 comments on commit dad6d6c

Please sign in to comment.