US06: Add an owner create endpoint to the API

This commit is contained in:
Ivaylo Ivanov 2020-03-22 16:14:50 +01:00
parent 361a09b082
commit 3d9e918f39
10 changed files with 200 additions and 2 deletions

View File

@ -2,12 +2,16 @@ package at.ac.tuwien.sepm.assignment.individual.endpoint;
import at.ac.tuwien.sepm.assignment.individual.endpoint.dto.OwnerDto; import at.ac.tuwien.sepm.assignment.individual.endpoint.dto.OwnerDto;
import at.ac.tuwien.sepm.assignment.individual.endpoint.mapper.OwnerMapper; 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.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 at.ac.tuwien.sepm.assignment.individual.util.ValidationException;
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;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ResponseStatusException;
@ -36,4 +40,22 @@ public class OwnerEndpoint {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Error during reading owner", e); throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Error during reading owner", e);
} }
} }
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public OwnerDto addHorse(@RequestBody OwnerDto owner) {
LOGGER.info("POST " + BASE_URL);
try {
Owner ownerEntity = ownerMapper.dtoToEntity(owner);
return ownerMapper.entityToDto(ownerService.addOwner(ownerEntity));
} catch (ValidationException e) {
LOGGER.error(e.getMessage());
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"Error during adding new owner: " + e.getMessage());
} catch (DataAccessException e) {
LOGGER.error(e.getMessage());
throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY,
"Something went wrong during the communication with the database");
}
}
} }

View File

@ -11,5 +11,7 @@ public class OwnerMapper {
return new OwnerDto(owner.getId(), owner.getName(), owner.getCreatedAt(), owner.getUpdatedAt()); return new OwnerDto(owner.getId(), owner.getName(), owner.getCreatedAt(), owner.getUpdatedAt());
} }
public Owner dtoToEntity(OwnerDto owner) {
return new Owner(owner.getName());
}
} }

View File

@ -14,4 +14,11 @@ public interface OwnerDao {
*/ */
Owner findOneById(Long id); Owner findOneById(Long id);
/**
* @param owner that specifies the owner to add
* @return the newly created horse
* @throws DataAccessException will be thrown if something goes wrong during the database access.
*/
Owner addOwner(Owner owner);
} }

View File

@ -4,14 +4,20 @@ 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 at.ac.tuwien.sepm.assignment.individual.persistence.OwnerDao; import at.ac.tuwien.sepm.assignment.individual.persistence.OwnerDao;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
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;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
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.KeyHolder;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@Repository @Repository
@ -39,6 +45,49 @@ public class OwnerJdbcDao implements OwnerDao {
return owners.get(0); return owners.get(0);
} }
@Override
public Owner addOwner(Owner owner) {
LOGGER.trace("Add owner {}", owner);
final String sql = "INSERT INTO " + TABLE_NAME + "(name, created_at, updated_at) VALUES(?,?,?)";
try {
// Check if the constraints are violated
this.validateOwner(owner);
LocalDateTime currentTime = LocalDateTime.now();
owner.setCreatedAt(currentTime);
owner.setUpdatedAt(currentTime);
// Create a key holder to get the key of the new record
KeyHolder keyHolder = new GeneratedKeyHolder();
int changes = jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
ps.setString(1, owner.getName());
ps.setObject(2, owner.getCreatedAt());
ps.setObject(3, owner.getUpdatedAt());
return ps;
}, keyHolder);
if (changes == 0)
throw new DataAccessException("Creating owner failed, no rows affected") {};
owner.setId(((Number)keyHolder.getKeys().get("id")).longValue());
return owner;
} catch (DataAccessException e) {
// We are doing this in order to not change the exception type
throw new DataAccessException("Adding new 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 Owner mapRow(ResultSet resultSet, int i) throws SQLException { private Owner mapRow(ResultSet resultSet, int i) throws SQLException {
final Owner owner = new Owner(); final Owner owner = new Owner();

View File

@ -2,6 +2,8 @@ package at.ac.tuwien.sepm.assignment.individual.service;
import at.ac.tuwien.sepm.assignment.individual.entity.Owner; 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 at.ac.tuwien.sepm.assignment.individual.util.ValidationException;
import org.springframework.dao.DataAccessException;
public interface OwnerService { public interface OwnerService {
@ -14,4 +16,12 @@ public interface OwnerService {
*/ */
Owner findOneById(Long id); Owner findOneById(Long id);
/**
* @param owner to create
* @return the new owner
* @throws ValidationException will be thrown if something goes wrong during verification.
* @throws DataAccessException will be thrown if the horse could not be saved in the database.
*/
Owner addOwner(Owner owner) throws ValidationException, DataAccessException;
} }

View File

@ -3,11 +3,13 @@ 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.entity.Owner;
import at.ac.tuwien.sepm.assignment.individual.persistence.OwnerDao; 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.service.OwnerService;
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 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;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
@ -29,4 +31,10 @@ public class SimpleOwnerService implements OwnerService {
return ownerDao.findOneById(id); return ownerDao.findOneById(id);
} }
@Override
public Owner addOwner(Owner owner) throws ValidationException, DataAccessException {
LOGGER.trace("addOwner({})", owner);
this.validator.validateNewOwner(owner);
return ownerDao.addOwner(owner);
}
} }

View File

@ -15,6 +15,9 @@ public class Validator {
public void validateNewOwner(Owner owner) throws ValidationException { public void validateNewOwner(Owner owner) throws ValidationException {
if(owner.getName() == null || owner.getName().isEmpty()) {
throw new ValidationException("Required value name missing");
}
} }
public void validateUpdateOwner(Owner owner) throws ValidationException { public void validateUpdateOwner(Owner owner) throws ValidationException {

View File

@ -0,0 +1,42 @@
package at.ac.tuwien.sepm.assignment.individual.integration;
import static org.junit.jupiter.api.Assertions.*;
import at.ac.tuwien.sepm.assignment.individual.endpoint.dto.OwnerDto;
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.http.*;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.web.client.RestTemplate;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
public class OwnerEndpointTest {
@Value("${spring.upload.path}")
private String FILE_BASE_PATH;
private static final RestTemplate REST_TEMPLATE = new RestTemplate();
private static final String BASE_URL = "http://localhost:";
private static final String OWNER_URL = "/owners";
@LocalServerPort
private int port;
@Test
@DisplayName("Adding a new owner with the correct parameters will return HTTP 201 and the new OwnerDto")
public void addingNewOwner_correctParameters_shouldReturnStatus201AndOwner() {
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);
// Compare everything except ids and timestamps
assertEquals(response.getStatusCode(), HttpStatus.CREATED);
assertEquals(newOwner.getName(), response.getBody().getName());
}
}

View File

@ -2,11 +2,13 @@ package at.ac.tuwien.sepm.assignment.individual.unit.persistence;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
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 at.ac.tuwien.sepm.assignment.individual.persistence.OwnerDao; import at.ac.tuwien.sepm.assignment.individual.persistence.OwnerDao;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
public abstract class OwnerDaoTestBase { public abstract class OwnerDaoTestBase {
@ -17,7 +19,21 @@ public abstract class OwnerDaoTestBase {
@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() {
assertThrows(NotFoundException.class, assertThrows(NotFoundException.class,
() -> ownerDao.findOneById(1L)); () -> ownerDao.findOneById(0L));
} }
@Test
@DisplayName("Adding a new owner with the correct parameters should return the owner")
public void addingNewOwner_correctParameters_shouldReturnOwner() {
Owner newOwner = new Owner("Chad");
Owner savedOwner = ownerDao.addOwner(newOwner);
assertEquals(newOwner, savedOwner);
}
@Test
@DisplayName("Adding a new horse with the incorrect parameters should throw DataAccessException")
public void addingNewOwner_incorrectParameters_shouldThrowDataAccess() {
Owner newOwner = new Owner("");
assertThrows(DataAccessException.class, () -> ownerDao.addOwner(newOwner));
}
} }

View File

@ -0,0 +1,39 @@
package at.ac.tuwien.sepm.assignment.individual.unit.service;
import static org.junit.jupiter.api.Assertions.*;
import at.ac.tuwien.sepm.assignment.individual.entity.Owner;
import at.ac.tuwien.sepm.assignment.individual.service.OwnerService;
import at.ac.tuwien.sepm.assignment.individual.util.ValidationException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ExtendWith(SpringExtension.class)
@SpringBootTest
@ActiveProfiles("test")
public class OwnerServiceTest {
@Autowired
OwnerService ownerService;
@Test
@DisplayName("Adding a new owner with the correct parameters will return the new owner")
public void addingNewOwner_correctParameters_shouldReturnOwner() {
Owner newOwner = new Owner("Chad");
Owner savedOwner = ownerService.addOwner(newOwner);
assertEquals(newOwner, savedOwner);
}
@Test
@DisplayName("Adding a new owner with the incorrect parameters will throw a ValidationException")
public void addingNewOwner_incorrectParameters_shouldThrowValidation() {
Owner newOwner = new Owner("");
assertThrows(ValidationException.class, () -> ownerService.addOwner(newOwner));
}
}