US09: Make it possible to search for owners using the API

This commit is contained in:
Ivaylo Ivanov 2020-03-25 07:53:57 +01:00
parent 5c53ad4f01
commit 5ffbe6c225
10 changed files with 274 additions and 1 deletions

View File

@ -7,6 +7,8 @@ import at.ac.tuwien.sepm.assignment.individual.exception.NotFoundException;
import at.ac.tuwien.sepm.assignment.individual.service.OwnerService; import at.ac.tuwien.sepm.assignment.individual.service.OwnerService;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.util.List;
import java.util.Map;
import at.ac.tuwien.sepm.assignment.individual.util.ValidationException; import at.ac.tuwien.sepm.assignment.individual.util.ValidationException;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -43,6 +45,26 @@ public class OwnerEndpoint {
} }
} }
@GetMapping
@ResponseStatus(HttpStatus.OK)
public List<OwnerDto> getAll(@RequestParam Map<String, String> filters) {
try {
if(filters.isEmpty()) {
LOGGER.info("GET " + BASE_URL);
return ownerMapper.entityListToDtoList(ownerService.getAll());
} else {
LOGGER.info("GET " + BASE_URL + "/ with filters" + filters.entrySet());
return ownerMapper.entityListToDtoList(ownerService.getFiltered(filters));
}
} catch (NotFoundException e) {
LOGGER.error(e.getMessage());
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "No owners found");
} catch (ValidationException e) {
LOGGER.error(e.getMessage());
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "The request contains filters with bad values: " + e.getMessage());
}
}
@PostMapping @PostMapping
@ResponseStatus(HttpStatus.CREATED) @ResponseStatus(HttpStatus.CREATED)
public OwnerDto addOwner(@RequestBody OwnerDto owner) { public OwnerDto addOwner(@RequestBody OwnerDto owner) {

View File

@ -4,6 +4,9 @@ import at.ac.tuwien.sepm.assignment.individual.endpoint.dto.OwnerDto;
import at.ac.tuwien.sepm.assignment.individual.entity.Owner; import at.ac.tuwien.sepm.assignment.individual.entity.Owner;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component @Component
public class OwnerMapper { public class OwnerMapper {
@ -14,4 +17,13 @@ public class OwnerMapper {
public Owner dtoToEntity(OwnerDto owner) { public Owner dtoToEntity(OwnerDto owner) {
return new Owner(owner.getName()); return new Owner(owner.getName());
} }
public List<OwnerDto> entityListToDtoList(List<Owner> ownerEntities) {
List<OwnerDto> ownerDtos = new ArrayList<>();
for(Owner owner: ownerEntities) {
ownerDtos.add(entityToDto(owner));
}
return ownerDtos;
}
} }

View File

@ -4,7 +4,8 @@ import at.ac.tuwien.sepm.assignment.individual.entity.Owner;
import at.ac.tuwien.sepm.assignment.individual.exception.NotFoundException; import at.ac.tuwien.sepm.assignment.individual.exception.NotFoundException;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import java.io.IOException; import java.util.List;
import java.util.Map;
public interface OwnerDao { public interface OwnerDao {
@ -16,6 +17,21 @@ public interface OwnerDao {
*/ */
Owner findOneById(Long id); Owner findOneById(Long id);
/**
* @return a list of all owners in the system
* @throws NotFoundException will be thrown if no owners are present in the database
* @throws DataAccessException will be thrown if something goes wrong during the database access
*/
List<Owner> getAll() throws NotFoundException;
/**
* @param filters to use for filtering the owners
* @return a list of all owners that fill the criteria
* @throws NotFoundException wil be thrown if no owners fill the criteria
* @throws DataAccessException will be thrown if something goes wrong during the database access
*/
List<Owner> getFiltered(Map<String, String> filters) throws NotFoundException;
/** /**
* @param owner that specifies the owner to add * @param owner that specifies the owner to add
* @return the newly created owner * @return the newly created owner

View File

@ -11,6 +11,7 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -23,6 +24,7 @@ import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder; import org.springframework.jdbc.support.KeyHolder;
@ -54,6 +56,44 @@ public class OwnerJdbcDao implements OwnerDao {
return owners.get(0); return owners.get(0);
} }
@Override
public List<Owner> getAll() throws NotFoundException {
LOGGER.trace("Get all owners");
final String sql = "SELECT * FROM " + TABLE_NAME;
List<Owner> owners = jdbcTemplate.query(sql, new Object[] { }, this::mapRow);
if(owners.isEmpty()) throw new NotFoundException("No owners found in the database");
return owners;
}
@Override
public List<Owner> getFiltered(Map<String, String> filters) throws NotFoundException {
LOGGER.trace("Get all owners with filters " + filters.entrySet());
final String sql = "SELECT * FROM " + TABLE_NAME + " WHERE UPPER(name) LIKE :name";
// Create a list to hold the results
List<Owner> owners = new ArrayList<>();
// Create a map to hold the sql filters with all values set as wildcards
Map<String, String> queryFilters = new HashMap<>();
queryFilters.put("name", "%_%");
// Go through the supplied filters and find set values
if(filters.get("name") != null)
queryFilters.replace("name", '%' + filters.get("name").toUpperCase() + '%');
// Create an map sql parameter source for use in the query
MapSqlParameterSource sqlMap = new MapSqlParameterSource();
sqlMap.addValues(queryFilters);
owners = namedParameterJdbcTemplate.query(sql, sqlMap, this::mapRow);
if(owners.isEmpty()) throw new NotFoundException("No owners found in the database");
return owners;
}
@Override @Override
public Owner addOwner(Owner owner) { public Owner addOwner(Owner owner) {
LOGGER.trace("Add owner {}", owner); LOGGER.trace("Add owner {}", owner);

View File

@ -5,6 +5,9 @@ import at.ac.tuwien.sepm.assignment.individual.exception.NotFoundException;
import at.ac.tuwien.sepm.assignment.individual.util.ValidationException; import at.ac.tuwien.sepm.assignment.individual.util.ValidationException;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import java.util.List;
import java.util.Map;
public interface OwnerService { public interface OwnerService {
@ -16,6 +19,20 @@ public interface OwnerService {
*/ */
Owner findOneById(Long id); Owner findOneById(Long id);
/**
* @return a list of all owners in the database
* @throws NotFoundException will be thrown if there are no owners in the database
*/
List<Owner> getAll() throws NotFoundException;
/**
* @param filters to use for filtering the owners
* @return a list of all owners that fill the criteria
* @throws NotFoundException will be thrown if no owners fill the criteria
* @throws ValidationException will be thrown if the filter contains bad values
*/
List<Owner> getFiltered(Map<String, String> filters) throws NotFoundException, ValidationException;
/** /**
* @param owner to create * @param owner to create
* @return the new owner * @return the new owner

View File

@ -8,6 +8,9 @@ import at.ac.tuwien.sepm.assignment.individual.util.ValidationException;
import at.ac.tuwien.sepm.assignment.individual.util.Validator; import at.ac.tuwien.sepm.assignment.individual.util.Validator;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -33,6 +36,19 @@ public class SimpleOwnerService implements OwnerService {
return ownerDao.findOneById(id); return ownerDao.findOneById(id);
} }
@Override
public List<Owner> getAll() throws NotFoundException {
LOGGER.trace("getAll()");
return ownerDao.getAll();
}
@Override
public List<Owner> getFiltered(Map<String, String> filters) throws NotFoundException, ValidationException {
LOGGER.trace("getFiltered({})", filters.entrySet());
this.validator.validateOwnerFilter(filters);
return ownerDao.getFiltered(filters);
}
@Override @Override
public Owner addOwner(Owner owner) throws ValidationException, DataAccessException { public Owner addOwner(Owner owner) throws ValidationException, DataAccessException {
LOGGER.trace("addOwner({})", owner); LOGGER.trace("addOwner({})", owner);

View File

@ -26,6 +26,12 @@ public class Validator {
} }
} }
public void validateOwnerFilter(Map<String, String> filters) {
if(filters.get("name") == null || filters.get("name").isEmpty()) {
throw new ValidationException("Owner name cannot be empty");
}
}
public void validateNewHorse(Horse horse) throws ValidationException { public void validateNewHorse(Horse horse) throws ValidationException {
if(horse.getName() == null || horse.getScore() == 0 || horse.getBirthday() == null || horse.getRace() == null || horse.getImagePath() == null){ if(horse.getName() == null || horse.getScore() == 0 || horse.getBirthday() == null || horse.getRace() == null || horse.getImagePath() == null){
throw new ValidationException("All or some required values missing: name, score, birthday, race, imagePath"); throw new ValidationException("All or some required values missing: name, score, birthday, race, imagePath");

View File

@ -16,8 +16,10 @@ import org.springframework.http.*;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.sql.Date; import java.sql.Date;
import java.util.List;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test") @ActiveProfiles("test")
@ -32,6 +34,90 @@ public class OwnerEndpointTest {
private int port; private int port;
@Test
@DisplayName("Searching for all owners with no filters, given two owners should return HTTP 200 and both ownerss")
public void searchingOwners_noFiltersGivenTwoOwners_shouldReturnStatus200AndOwners() {
// Create the owners
OwnerDto newOwner = new OwnerDto("Chad");
HttpEntity<OwnerDto> request = new HttpEntity<>(newOwner);
ResponseEntity<OwnerDto> firstOwner = REST_TEMPLATE
.exchange(BASE_URL + port + OWNER_URL, HttpMethod.POST, request, OwnerDto.class);
newOwner = new OwnerDto("Gigachad");
request = new HttpEntity<>(newOwner);
ResponseEntity<OwnerDto> secondOwner = REST_TEMPLATE
.exchange(BASE_URL + port + OWNER_URL, HttpMethod.POST, request, OwnerDto.class);
// Get all owners and save in list
// https://stackoverflow.com/a/31947188
ResponseEntity<List<OwnerDto>> allOwners = REST_TEMPLATE
.exchange(BASE_URL + port + OWNER_URL, HttpMethod.GET, null, new ParameterizedTypeReference<List<OwnerDto>>() {});
assertEquals(allOwners.getStatusCode(), HttpStatus.OK);
assertTrue(allOwners.getBody().contains(firstOwner.getBody()));
assertTrue(allOwners.getBody().contains(secondOwner.getBody()));
}
@Test
@DisplayName("Searching for all owners with correct filters, given two owners should return HTTP 200 and one owner")
public void searchingOwners_withCorrectFiltersGivenTwoOwners_shouldReturnStatus200AndOwner() {
// Create the owners
OwnerDto newOwner = new OwnerDto("Chad");
HttpEntity<OwnerDto> request = new HttpEntity<>(newOwner);
ResponseEntity<OwnerDto> firstOwner = REST_TEMPLATE
.exchange(BASE_URL + port + OWNER_URL, HttpMethod.POST, request, OwnerDto.class);
newOwner = new OwnerDto("Lad");
request = new HttpEntity<>(newOwner);
ResponseEntity<OwnerDto> secondOwner = REST_TEMPLATE
.exchange(BASE_URL + port + OWNER_URL, HttpMethod.POST, request, OwnerDto.class);
// Get all owners and save in list
// https://stackoverflow.com/a/25434451
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(BASE_URL + port + OWNER_URL)
.queryParam("name", "chad");
ResponseEntity<List<OwnerDto>> allOwners = REST_TEMPLATE.exchange(
builder.toUriString(),
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<OwnerDto>>() {}
);
assertEquals(allOwners.getStatusCode(), HttpStatus.OK);
assertTrue(allOwners.getBody().contains(firstOwner.getBody()));
}
@Test
@DisplayName("Searching for all owners with incorrect filters, given two owners should return HTTP 400")
public void searchingOwners_withIncorrectFiltersGivenTwoOwners_shouldReturnStatus400() {
// Create the owners
OwnerDto newOwner = new OwnerDto("Chad");
HttpEntity<OwnerDto> request = new HttpEntity<>(newOwner);
ResponseEntity<OwnerDto> firstOwner = REST_TEMPLATE
.exchange(BASE_URL + port + OWNER_URL, HttpMethod.POST, request, OwnerDto.class);
newOwner = new OwnerDto("Lad");
request = new HttpEntity<>(newOwner);
ResponseEntity<OwnerDto> secondOwner = REST_TEMPLATE
.exchange(BASE_URL + port + OWNER_URL, HttpMethod.POST, request, OwnerDto.class);
// Get all owners and save in list
// https://stackoverflow.com/a/25434451
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(BASE_URL + port + OWNER_URL)
.queryParam("name", "");
assertThrows(HttpClientErrorException.BadRequest.class, () ->
REST_TEMPLATE
.exchange(builder.toUriString(), HttpMethod.GET, null, new ParameterizedTypeReference<OwnerDto>() {}));
}
@Test @Test
@DisplayName("Adding a new owner with the correct parameters will return HTTP 201 and the new OwnerDto") @DisplayName("Adding a new owner with the correct parameters will return HTTP 201 and the new OwnerDto")
public void addingNewOwner_correctParameters_shouldReturnStatus201AndOwner() { public void addingNewOwner_correctParameters_shouldReturnStatus201AndOwner() {

View File

@ -16,6 +16,9 @@ import org.springframework.dao.DataIntegrityViolationException;
import java.io.IOException; import java.io.IOException;
import java.sql.Date; import java.sql.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class OwnerDaoTestBase { public abstract class OwnerDaoTestBase {
@ -25,6 +28,48 @@ public abstract class OwnerDaoTestBase {
@Autowired @Autowired
HorseDao horseDao; HorseDao horseDao;
@Test
@DisplayName("Getting all owners given two owners should return a list with the owners")
public void gettingAllOwners_givenTwoOwners_shouldReturnOwners() {
// Create the owners
Owner firstOwner = ownerDao.addOwner(new Owner("Chad"));
Owner secondOwner = ownerDao.addOwner(new Owner("Lad"));
// Test if the owners are present
List<Owner> allOwners = ownerDao.getAll();
assertTrue(allOwners.contains(firstOwner));
assertTrue(allOwners.contains(secondOwner));
}
@Test
@DisplayName("Searching all owners with correct filters given two owners should return a list with the owner")
public void searchingOwners_withCorrectFiltersGivenTwoOwners_shouldReturnOwner() {
// Create the owners
Owner firstOwner = ownerDao.addOwner(new Owner("Brad"));
Owner secondOwner = ownerDao.addOwner(new Owner("Becky"));
// Test if the owners are present
Map<String, String> filters = new HashMap<String, String>();
filters.put("name", "brad");
List<Owner> allOwners = ownerDao.getFiltered(filters);
assertTrue(allOwners.contains(firstOwner));
}
@Test
@DisplayName("Searching all owners with incorrect filters given two owners should throw NotFoundException")
public void searchingOwners_withIncorrectFiltersGivenTwoOwners_shouldThrowNotFound() {
// Create the owners
Owner firstOwner = ownerDao.addOwner(new Owner("Brad"));
Owner secondOwner = ownerDao.addOwner(new Owner("Stacy"));
// Test if the owners are present
Map<String, String> filters = new HashMap<String, String>();
filters.put("name", "Tester Owner");
assertThrows(NotFoundException.class, () -> ownerDao.getFiltered(filters));
}
@Test @Test
@DisplayName("Finding owner by non-existing ID should throw NotFoundException") @DisplayName("Finding owner by non-existing ID should throw NotFoundException")
public void findingOwnerById_nonExisting_shouldThrowNotFoundException() { public void findingOwnerById_nonExisting_shouldThrowNotFoundException() {

View File

@ -13,6 +13,9 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@ExtendWith(SpringExtension.class) @ExtendWith(SpringExtension.class)
@ -67,4 +70,14 @@ public class OwnerServiceTest {
newOwner.setName(""); newOwner.setName("");
assertThrows(ValidationException.class, () -> ownerService.updateOwner(newOwner)); assertThrows(ValidationException.class, () -> ownerService.updateOwner(newOwner));
} }
@Test
@DisplayName("Searching all owners with incorrect filters given no owners should throw ValidationException")
public void searchingOwners_withIncorrectFiltersGivenNoOwners_shouldThrowValidation() {
// Test for exception
Map<String, String> filters = new HashMap<String, String>();
filters.put("name", "");
assertThrows(ValidationException.class, () -> ownerService.getFiltered(filters));
}
} }