US08: Add an owner delete endpoint to the API
This commit is contained in:
parent
9c36a93dd5
commit
5c53ad4f01
@ -5,6 +5,7 @@ import at.ac.tuwien.sepm.assignment.individual.endpoint.mapper.OwnerMapper;
|
||||
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.service.OwnerService;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import at.ac.tuwien.sepm.assignment.individual.util.ValidationException;
|
||||
@ -12,6 +13,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
@ -81,4 +83,25 @@ public class OwnerEndpoint {
|
||||
"The requested owner could not be found");
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping(value = "/{id}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void deleteOwner(@PathVariable("id") Long id) {
|
||||
LOGGER.info("DELETE " + BASE_URL + "/{}", id);
|
||||
try {
|
||||
ownerService.deleteOwner(id);
|
||||
} catch (DataIntegrityViolationException e) {
|
||||
LOGGER.error(e.getMessage());
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN,
|
||||
"The requested owner cannot be deleted because there are horses that are assigned to him/her");
|
||||
}catch (DataAccessException e) {
|
||||
LOGGER.error(e.getMessage());
|
||||
throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
"Something went wrong during the communication with the database");
|
||||
} catch (NotFoundException e) {
|
||||
LOGGER.error(e.getMessage());
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND,
|
||||
"The requested owner has not been found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,4 +29,11 @@ public interface OwnerDao {
|
||||
* @throws DataAccessException will be thrown if something goes wrong during the database access.
|
||||
*/
|
||||
Owner updateOwner(Owner owner) throws DataAccessException;
|
||||
|
||||
/**
|
||||
* @param id of the owner to delete
|
||||
* @throws DataAccessException will be thrown if something goes wrong during the database access.
|
||||
* @throws NotFoundException will be thrown if the owner could not be found in the database.
|
||||
*/
|
||||
void deleteOwner(Long id) throws DataAccessException, NotFoundException;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import at.ac.tuwien.sepm.assignment.individual.persistence.FileDao;
|
||||
import at.ac.tuwien.sepm.assignment.individual.persistence.HorseDao;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@ -35,6 +36,7 @@ public class HorseJdbcDao implements HorseDao {
|
||||
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
|
||||
private final FileDao fileDao = new HorseFileDao();
|
||||
|
||||
@Autowired
|
||||
public HorseJdbcDao(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
|
||||
|
@ -1,20 +1,27 @@
|
||||
package at.ac.tuwien.sepm.assignment.individual.persistence.impl;
|
||||
|
||||
import at.ac.tuwien.sepm.assignment.individual.entity.Horse;
|
||||
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.persistence.OwnerDao;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.jdbc.core.BeanPropertyRowMapper;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.jdbc.support.GeneratedKeyHolder;
|
||||
@ -25,6 +32,7 @@ import org.springframework.stereotype.Repository;
|
||||
public class OwnerJdbcDao implements OwnerDao {
|
||||
|
||||
private static final String TABLE_NAME = "Owner";
|
||||
private static final String HORSE_TABLE_NAME = "Horse";
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
|
||||
@ -92,7 +100,7 @@ public class OwnerJdbcDao implements OwnerDao {
|
||||
|
||||
try {
|
||||
if(owner.getId() == null || owner.getId() == 0)
|
||||
throw new DataIntegrityViolationException("Horse Id missing or 0");
|
||||
throw new DataIntegrityViolationException("Owner Id missing or 0");
|
||||
|
||||
this.validateOwner(owner);
|
||||
|
||||
@ -123,11 +131,52 @@ public class OwnerJdbcDao implements OwnerDao {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteOwner(Long id) throws DataAccessException, NotFoundException {
|
||||
Owner ownerToDelete = this.findOneById(id);
|
||||
LOGGER.trace("Delete owner with id {}", id);
|
||||
final String sql = "DELETE FROM " + TABLE_NAME + " WHERE id=?";
|
||||
|
||||
if (ownerOwnsHorses(id))
|
||||
throw new DataIntegrityViolationException("Deleting owner failed, owner has horses assigned");
|
||||
|
||||
try {
|
||||
int changes = jdbcTemplate.update(connection -> {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ps.setLong(1, id);
|
||||
return ps;
|
||||
});
|
||||
|
||||
if (changes == 0)
|
||||
throw new DataAccessException("Deleting owner failed, no rows affected") {};
|
||||
|
||||
} catch(DataAccessException e){
|
||||
// We are doing this in order to not change the exception type
|
||||
throw new DataAccessException("Deleting records failed", e) {};
|
||||
}
|
||||
}
|
||||
|
||||
private void validateOwner(Owner owner) throws DataIntegrityViolationException {
|
||||
if(owner.getName() == null || owner.getName().isEmpty())
|
||||
throw new DataIntegrityViolationException("Required parameters for owner missing");
|
||||
}
|
||||
|
||||
private boolean ownerOwnsHorses(Long ownerId) {
|
||||
final String sql = "SELECT * FROM " + HORSE_TABLE_NAME + " WHERE owner_id=?";
|
||||
|
||||
try {
|
||||
jdbcTemplate.queryForObject(sql, new Object[] {ownerId}, BeanPropertyRowMapper.newInstance(Horse.class));
|
||||
} catch(EmptyResultDataAccessException e) {
|
||||
// If empty, return false
|
||||
return false;
|
||||
} catch (IncorrectResultSizeDataAccessException e) {
|
||||
// If incorrect size above 0, return true
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Owner mapRow(ResultSet resultSet, int i) throws SQLException {
|
||||
final Owner owner = new Owner();
|
||||
owner.setId(resultSet.getLong("id"));
|
||||
|
@ -5,8 +5,6 @@ import at.ac.tuwien.sepm.assignment.individual.exception.NotFoundException;
|
||||
import at.ac.tuwien.sepm.assignment.individual.util.ValidationException;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface OwnerService {
|
||||
|
||||
|
||||
@ -33,4 +31,11 @@ public interface OwnerService {
|
||||
* @throws DataAccessException will be thrown if the owner could not be saved in the database.
|
||||
*/
|
||||
Owner updateOwner(Owner owner) throws ValidationException, DataAccessException;
|
||||
|
||||
/**
|
||||
* @param id of the owner to delete
|
||||
* @throws NotFoundException will be thrown if the owner could not be found in the system
|
||||
* @throws DataAccessException will be thrown if the owner could not be deleted from the database
|
||||
*/
|
||||
void deleteOwner(Long id) throws NotFoundException, DataAccessException;
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
package at.ac.tuwien.sepm.assignment.individual.service.impl;
|
||||
|
||||
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.persistence.OwnerDao;
|
||||
import at.ac.tuwien.sepm.assignment.individual.service.OwnerService;
|
||||
import at.ac.tuwien.sepm.assignment.individual.util.ValidationException;
|
||||
import at.ac.tuwien.sepm.assignment.individual.util.Validator;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -44,4 +46,10 @@ public class SimpleOwnerService implements OwnerService {
|
||||
this.validator.validateUpdateOwner(owner);
|
||||
return ownerDao.updateOwner(owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteOwner(Long id) throws NotFoundException, DataAccessException {
|
||||
LOGGER.trace("deleteOwner({})", id);
|
||||
ownerDao.deleteOwner(id);
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,6 @@ import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class Validator {
|
||||
|
||||
|
||||
|
||||
public void validateNewOwner(Owner owner) throws ValidationException {
|
||||
if(owner.getName() == null || owner.getName().isEmpty()) {
|
||||
throw new ValidationException("Required value name missing");
|
||||
|
@ -1,17 +1,24 @@
|
||||
package at.ac.tuwien.sepm.assignment.individual.integration;
|
||||
|
||||
import static at.ac.tuwien.sepm.assignment.individual.base.TestData.HORSE_URL;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import at.ac.tuwien.sepm.assignment.individual.endpoint.dto.HorseDto;
|
||||
import at.ac.tuwien.sepm.assignment.individual.endpoint.dto.OwnerDto;
|
||||
import at.ac.tuwien.sepm.assignment.individual.enums.ERace;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.sql.Date;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles("test")
|
||||
public class OwnerEndpointTest {
|
||||
@ -62,5 +69,67 @@ public class OwnerEndpointTest {
|
||||
assertEquals(newOwner.getId(), response.getBody().getId());
|
||||
assertEquals(newOwner.getName(), response.getBody().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Deleting an existing owner without owners will return HTTP 204")
|
||||
public void deletingOwner_existingNoOwnersOwned_shouldReturnStatus204() {
|
||||
// Create the owner
|
||||
OwnerDto newOwner = new OwnerDto("Chad");
|
||||
|
||||
HttpEntity<OwnerDto> request = new HttpEntity<>(newOwner);
|
||||
ResponseEntity<OwnerDto> response = REST_TEMPLATE
|
||||
.exchange(BASE_URL + port + OWNER_URL, HttpMethod.POST, request, OwnerDto.class);
|
||||
|
||||
// Delete and test if deleted
|
||||
ResponseEntity res = REST_TEMPLATE
|
||||
.exchange(BASE_URL + port + OWNER_URL + '/' + response.getBody().getId(), HttpMethod.DELETE, null, new ParameterizedTypeReference<OwnerDto>() {});
|
||||
|
||||
assertEquals(res.getStatusCode(), HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Deleting an existing owner without horses will return HTTP 204")
|
||||
public void deletingOwner_existingNoHorsesOwned_shouldReturnStatus204() {
|
||||
// Create the owner
|
||||
OwnerDto newOwner = new OwnerDto("Chad");
|
||||
|
||||
HttpEntity<OwnerDto> request = new HttpEntity<>(newOwner);
|
||||
ResponseEntity<OwnerDto> response = REST_TEMPLATE
|
||||
.exchange(BASE_URL + port + OWNER_URL, HttpMethod.POST, request, OwnerDto.class);
|
||||
|
||||
// Delete and test if deleted
|
||||
ResponseEntity res = REST_TEMPLATE
|
||||
.exchange(BASE_URL + port + OWNER_URL + '/' + response.getBody().getId(), HttpMethod.DELETE, null, new ParameterizedTypeReference<OwnerDto>() {});
|
||||
|
||||
assertEquals(res.getStatusCode(), HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Deleting an nonexistent owner will return HTTP 404")
|
||||
public void deletingOwner_nonexistent_shouldReturnStatus404() {
|
||||
assertThrows(HttpClientErrorException.NotFound.class, () ->
|
||||
REST_TEMPLATE
|
||||
.exchange(BASE_URL + port + OWNER_URL + "/0", HttpMethod.DELETE, null, new ParameterizedTypeReference<OwnerDto>() {}));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Deleting an exiting owner with horses will return HTTP 403")
|
||||
public void deletingOwner_existingHorsesOwned_shouldReturnStatus403() {
|
||||
// Create the owner
|
||||
OwnerDto newOwner = new OwnerDto("Chad");
|
||||
|
||||
HttpEntity<OwnerDto> request = new HttpEntity<>(newOwner);
|
||||
OwnerDto savedOwner = REST_TEMPLATE
|
||||
.exchange(BASE_URL + port + OWNER_URL, HttpMethod.POST, request, OwnerDto.class).getBody();
|
||||
|
||||
// Create the horse
|
||||
HorseDto newHorse = new HorseDto("Zephyr", "Nice horse", (short) 4, Date.valueOf("2020-01-01"), ERace.APPALOOSA, "files/test.png", savedOwner.getId());
|
||||
|
||||
request = new HttpEntity(newHorse);
|
||||
REST_TEMPLATE.exchange(BASE_URL + port + HORSE_URL, HttpMethod.POST, request, HorseDto.class);
|
||||
|
||||
assertThrows(HttpClientErrorException.Forbidden.class, () ->
|
||||
REST_TEMPLATE
|
||||
.exchange(BASE_URL + port + OWNER_URL + "/" + savedOwner.getId(), HttpMethod.DELETE, null, new ParameterizedTypeReference<OwnerDto>() {}));
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,17 @@ package at.ac.tuwien.sepm.assignment.individual.unit.persistence;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import at.ac.tuwien.sepm.assignment.individual.entity.Horse;
|
||||
import at.ac.tuwien.sepm.assignment.individual.entity.Owner;
|
||||
import at.ac.tuwien.sepm.assignment.individual.enums.ERace;
|
||||
import at.ac.tuwien.sepm.assignment.individual.exception.NotFoundException;
|
||||
import at.ac.tuwien.sepm.assignment.individual.persistence.HorseDao;
|
||||
import at.ac.tuwien.sepm.assignment.individual.persistence.OwnerDao;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Date;
|
||||
@ -18,6 +22,9 @@ public abstract class OwnerDaoTestBase {
|
||||
@Autowired
|
||||
OwnerDao ownerDao;
|
||||
|
||||
@Autowired
|
||||
HorseDao horseDao;
|
||||
|
||||
@Test
|
||||
@DisplayName("Finding owner by non-existing ID should throw NotFoundException")
|
||||
public void findingOwnerById_nonExisting_shouldThrowNotFoundException() {
|
||||
@ -70,4 +77,39 @@ public abstract class OwnerDaoTestBase {
|
||||
newOwner.setName("");
|
||||
assertThrows(DataAccessException.class, () -> ownerDao.updateOwner(newOwner));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Deleting an existing owner without horses should delete the owner")
|
||||
public void deletingOwner_existingOwnerNoHorsesOwned_shouldDeleteOwner() throws IOException {
|
||||
// Create the owner
|
||||
Owner newOwner = new Owner("Chad");
|
||||
Owner savedOwner = ownerDao.addOwner(newOwner);
|
||||
|
||||
// Delete the owner
|
||||
ownerDao.deleteOwner(savedOwner.getId());
|
||||
|
||||
// Check if deleted
|
||||
assertThrows(NotFoundException.class, () -> ownerDao.findOneById(savedOwner.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Deleting an nonexistent owner should throw NotFoundException")
|
||||
public void deletingOwner_nonexistent_shouldThrowNotFound() throws IOException {
|
||||
assertThrows(NotFoundException.class, () -> ownerDao.deleteOwner(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Deleting an existing owner with horses should throw DataIntegrityViolationException")
|
||||
public void deletingHorse_existing_shouldDeleteHorse() {
|
||||
// Create the owner
|
||||
Owner newOwner = new Owner("Chad");
|
||||
Owner savedOwner = ownerDao.addOwner(newOwner);
|
||||
|
||||
// Create the horse
|
||||
Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 4, Date.valueOf("2020-01-01"), ERace.APPALOOSA, "files/test.png", savedOwner.getId());
|
||||
Horse savedHorse = horseDao.addHorse(newHorse);
|
||||
|
||||
// Delete the owner
|
||||
assertThrows(DataIntegrityViolationException.class, () -> ownerDao.deleteOwner(savedOwner.getId()));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user