Skip to content

Commit

Permalink
[API] - Add search mocks api based on specified column
Browse files Browse the repository at this point in the history
- search mocks based on the search query string and the search column
  such as name, description or route
- #179
  • Loading branch information
aravinve authored and neel1996 committed Nov 10, 2022
1 parent 71fc6a1 commit fb71937
Show file tree
Hide file tree
Showing 8 changed files with 305 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ private UrlConfig() {
// region MOCKS
public static final String MOCKS = "/mocks";
public static final String MOCKS_PAGEABLE = "/filter";
public static final String MOCKS_SEARCH = "/search";
public static final String MOCKS_CSV_EXPORT = "/csv/export";
public static final String MOCKS_CSV_TEMPLATE_EXPORT = "/csv/template/export";
// endregion
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.arbindo.mimock.manage.mimocks.controller;

import com.arbindo.mimock.common.constants.UrlConfig;
import com.arbindo.mimock.entities.Mock;
import com.arbindo.mimock.manage.mimocks.mapper.ResponseModelMapper;
import com.arbindo.mimock.manage.mimocks.models.response.ListMocksResponse;
import com.arbindo.mimock.manage.mimocks.service.SearchMocksService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.SortDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Log4j2
@RequestMapping(UrlConfig.MOCKS_PATH)
@SecurityRequirement(name = UrlConfig.SWAGGER_BEARER_AUTH_KEY)
@Tag(name = "Mock Management", description = "Handles operations related to mock resource.")
public class SearchMocksController {

@Autowired
private SearchMocksService searchMocksService;

@Operation(summary = "Search and List Mocks As Pageable", description = "Searches for mocks and lists them in " +
"pageable format based on the search query provided", tags = {"Mock Management"})
@GetMapping(UrlConfig.MOCKS_SEARCH)
public ResponseEntity<Page<ListMocksResponse>> searchMocks(@SortDefault(sort = "createdAt",
direction = Sort.Direction.DESC) Pageable pageable, @RequestParam(required = true) String searchColumnString,
@RequestParam(required = true) String searchQuery) {
Page<Mock> mockPageable = searchMocksService.searchMocks(pageable, searchColumnString, searchQuery);
if(mockPageable == null){
return ResponseEntity.badRequest().build();
}
Page<ListMocksResponse> responsePage = mockPageable.map(ResponseModelMapper::map);
return ResponseEntity.ok(responsePage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.arbindo.mimock.manage.mimocks.enums;

public enum SearchColumn {
NAME("NAME"),
DESCRIPTION("DESCRIPTION"),
ROUTE("ROUTE");

public final String value;

private SearchColumn(String value) {
this.value = value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.arbindo.mimock.manage.mimocks.service;

import com.arbindo.mimock.entities.Mock;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

public interface SearchMocksService {

Page<Mock> searchMocks(Pageable pageable, String searchColumnString, String searchQuery);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.arbindo.mimock.manage.mimocks.service;

import com.arbindo.mimock.entities.Mock;
import com.arbindo.mimock.manage.mimocks.enums.SearchColumn;
import com.arbindo.mimock.repository.MocksRepository;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.extern.log4j.Log4j2;
import org.apache.logging.log4j.Level;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

@Service
@Log4j2
@Builder
@AllArgsConstructor
public class SearchMocksServiceImpl implements SearchMocksService{

@Autowired
private MocksRepository mocksRepository;

@Override
public Page<Mock> searchMocks(Pageable pageable, String searchColumnString, String searchQuery) {
try {
SearchColumn searchColumn = SearchColumn.valueOf(searchColumnString);
String sanitizedSearchQuery = searchQuery
.replaceAll("\n", "")
.replaceAll("\r", "")
.trim();
switch (searchColumn){
case NAME:
return mocksRepository.findAllByMockNameIgnoreCaseContaining(sanitizedSearchQuery, pageable);
case DESCRIPTION:
return mocksRepository.findAllByDescriptionIgnoreCaseContaining(sanitizedSearchQuery, pageable);
case ROUTE:
return mocksRepository.findAllByRouteIgnoreCaseContaining(sanitizedSearchQuery, pageable);
default:
return mocksRepository.findAll(pageable);
}
} catch(Exception e) {
log.log(Level.DEBUG, e.getMessage());
}
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ Page<Mock> findAllByEntityStatusAndHttpMethodAndTextualResponseIsNullAndBinaryRe

List<Mock> findAllByEntityStatusAndDeletedAt(EntityStatus entityStatus, ZonedDateTime deletedAt);

Page<Mock> findAllByMockNameIgnoreCaseContaining(String searchQuery, Pageable pageable);
Page<Mock> findAllByDescriptionIgnoreCaseContaining(String searchQuery, Pageable pageable);
Page<Mock> findAllByRouteIgnoreCaseContaining(String searchQuery, Pageable pageable);

Optional<Mock> findOneByMockName(String mockName);

Optional<Mock> findOneByRouteAndHttpMethodAndQueryParamValuesAndRequestBodiesForMockAndRequestHeadersAndDeletedAtIsNull
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package com.arbindo.mimock.manage.mimocks.controller;

import com.arbindo.mimock.common.constants.UrlConfig;
import com.arbindo.mimock.entities.Mock;
import com.arbindo.mimock.interceptor.DefaultHttpInterceptor;
import com.arbindo.mimock.manage.mimocks.models.response.ListMocksResponse;
import com.arbindo.mimock.manage.mimocks.service.SearchMocksService;
import com.arbindo.mimock.security.JwtRequestFilter;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.jdbc.support.DatabaseStartupValidator;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;

import javax.sql.DataSource;

import static com.arbindo.mimock.helpers.entities.MocksGenerator.generateMocksPageable;
import static com.arbindo.mimock.helpers.entities.MocksGenerator.getListMocksResponseInPageableFormat;
import static com.arbindo.mimock.helpers.general.JsonMapper.convertObjectToJsonString;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@WebMvcTest(value = SearchMocksController.class, excludeAutoConfiguration = {
SecurityAutoConfiguration.class,
UserDetailsServiceAutoConfiguration.class,
})
@AutoConfigureMockMvc(addFilters = false)
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, JpaRepositoriesAutoConfiguration.class})
public class SearchMocksControllerTest {
@Autowired
MockMvc mockMvc;

@MockBean
SearchMocksService mockSearchService;

@MockBean
DataSource mockDataSource;

@MockBean
DatabaseStartupValidator mockDataStartupValidator;

@MockBean
DefaultHttpInterceptor defaultHttpInterceptor;

@MockBean
UserDetailsService userDetailsService;

@MockBean
JwtRequestFilter jwtRequestFilter;

@Test
void shouldReturnHttpOk_SearchMocks_ReturnMocksAsPageableForSearchQuery() throws Exception {
// Arrange
String route = UrlConfig.MOCKS_PATH + UrlConfig.MOCKS_SEARCH;
String expectedContentType = "application/json";
Page<Mock> expectedMocksFromDB = generateMocksPageable();
Page<ListMocksResponse> expectedListMocksResponse = getListMocksResponseInPageableFormat(expectedMocksFromDB);
String expectedResponseBody = convertObjectToJsonString(expectedListMocksResponse);

lenient().when(mockSearchService.searchMocks(any(Pageable.class), anyString(), anyString()))
.thenReturn(expectedMocksFromDB);

// Act
MvcResult result = mockMvc.perform(get(route)
.param("searchColumnString", "NAME")
.param("searchQuery", "randomQueryString"))
.andExpect(status().isOk())
.andExpect(content().contentType(expectedContentType))
.andReturn();

// Assert
assertEquals(expectedResponseBody, result.getResponse().getContentAsString());
}

@Test
void shouldReturnHttpBadRequest_SearchMocks_UnableToGetMocksDueToInvalidSearchColumn() throws Exception {
// Arrange
String route = UrlConfig.MOCKS_PATH + UrlConfig.MOCKS_SEARCH;

lenient().when(mockSearchService.searchMocks(any(Pageable.class), anyString(), anyString()))
.thenReturn(null);

// Act & Assert
MvcResult result = mockMvc.perform(get(route)
.param("searchColumnString", "NAME")
.param("searchQuery", "randomQueryString"))
.andExpect(status().isBadRequest())
.andReturn();

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.arbindo.mimock.manage.mimocks.service;

import com.arbindo.mimock.entities.Mock;
import com.arbindo.mimock.repository.MocksRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EmptySource;
import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import static com.arbindo.mimock.helpers.entities.MocksGenerator.generateMocksPageable;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.lenient;

@ExtendWith(MockitoExtension.class)
@SpringBootTest
public class SearchMocksServiceTest {

@org.mockito.Mock
MocksRepository mockRepository;

SearchMocksService searchMocksService;

@BeforeEach
void setupMock(){
this.searchMocksService = SearchMocksServiceImpl.builder()
.mocksRepository(mockRepository)
.build();
}

@ParameterizedTest
@EmptySource
@NullSource
void shouldReturnNull_WhenSearchColumnStringIsNullOrEmpty(String searchColumnString) {
// Act
Page<Mock> result = searchMocksService.searchMocks(Pageable.unpaged(), searchColumnString, "randomQuery");

// Assert
assertNull(result);
}

@ParameterizedTest
@ValueSource(strings = {"Test", "UUID", "RandomString"})
void shouldReturnNull_WhenSearchColumnIsInvalid(String searchColumnString) {
// Act
Page<Mock> result = searchMocksService.searchMocks(Pageable.unpaged(), searchColumnString, "randomQuery");

// Assert
assertNull(result);
}

@ParameterizedTest
@ValueSource(strings = {"NAME", "DESCRIPTION", "ROUTE"})
void shouldReturnNull_WhenSearchColumnIsValid_ReturnMocksAsPageable(String searchColumnString) {
// Arrange
Page<Mock> expectedMocksFromDB = generateMocksPageable();
lenient().when(mockRepository.findAllByMockNameIgnoreCaseContaining(anyString(), any(Pageable.class)))
.thenReturn(expectedMocksFromDB);
lenient().when(mockRepository.findAllByDescriptionIgnoreCaseContaining(anyString(), any(Pageable.class)))
.thenReturn(expectedMocksFromDB);
lenient().when(mockRepository.findAllByRouteIgnoreCaseContaining(anyString(), any(Pageable.class)))
.thenReturn(expectedMocksFromDB);

// Act
Page<Mock> result = searchMocksService.searchMocks(Pageable.unpaged(), searchColumnString, "randomQuery");

// Assert
assertNotNull(result);
assertEquals(expectedMocksFromDB, result);
}
}

0 comments on commit fb71937

Please sign in to comment.