US01: Make it possible for the user to add a horse through the API

This commit is contained in:
2020-03-16 20:46:18 +01:00
commit 707487c236
80 changed files with 16073 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
package at.ac.tuwien.sepm.assignment.individual;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WendysRennpferdeApplication {
public static void main(String[] args) {
SpringApplication.run(WendysRennpferdeApplication.class, args);
}
}

View File

@@ -0,0 +1,14 @@
package at.ac.tuwien.sepm.assignment.individual.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("GET", "POST", "OPTION", "HEAD", "DELETE", "PUT", "PATCH");
}
}

View File

@@ -0,0 +1,61 @@
package at.ac.tuwien.sepm.assignment.individual.endpoint;
import at.ac.tuwien.sepm.assignment.individual.endpoint.dto.HorseDto;
import at.ac.tuwien.sepm.assignment.individual.endpoint.mapper.HorseMapper;
import at.ac.tuwien.sepm.assignment.individual.entity.Horse;
import at.ac.tuwien.sepm.assignment.individual.exception.NotFoundException;
import at.ac.tuwien.sepm.assignment.individual.service.HorseService;
import at.ac.tuwien.sepm.assignment.individual.util.ValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import java.lang.invoke.MethodHandles;
@RestController
@RequestMapping(at.ac.tuwien.sepm.assignment.individual.endpoint.HorseEndpoint.BASE_URL)
public class HorseEndpoint {
static final String BASE_URL = "/horses";
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final HorseService horseService;
private final HorseMapper horseMapper;
@Autowired
public HorseEndpoint(HorseService horseService, HorseMapper horseMapper) {
this.horseService = horseService;
this.horseMapper = horseMapper;
}
@GetMapping(value = "/{id}")
public HorseDto getOneById(@PathVariable("id") Long id) {
LOGGER.info("GET " + BASE_URL + "/{}", id);
try {
return horseMapper.entityToDto(horseService.findOneById(id));
} catch (NotFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Error during reading horse", e);
}
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public HorseDto addHorse(@RequestBody HorseDto horse) {
LOGGER.info("POST " + BASE_URL);
try {
Horse horseEntity = horseMapper.dtoToEntity(horse);
return horseMapper.entityToDto(horseService.addHorse(horseEntity));
} catch (ValidationException e) {
LOGGER.error(e.getMessage());
throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY,
"Error during adding new horse: " + e.getMessage(), e);
} catch (DataAccessException e) {
LOGGER.error(e.getMessage());
throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY,
"Something went wrong during the communication with the database", e);
}
}
}

View File

@@ -0,0 +1,39 @@
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.mapper.OwnerMapper;
import at.ac.tuwien.sepm.assignment.individual.exception.NotFoundException;
import at.ac.tuwien.sepm.assignment.individual.service.OwnerService;
import java.lang.invoke.MethodHandles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
@RestController
@RequestMapping(OwnerEndpoint.BASE_URL)
public class OwnerEndpoint {
static final String BASE_URL = "/owners";
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final OwnerService ownerService;
private final OwnerMapper ownerMapper;
@Autowired
public OwnerEndpoint(OwnerService ownerService, OwnerMapper ownerMapper) {
this.ownerService = ownerService;
this.ownerMapper = ownerMapper;
}
@GetMapping(value = "/{id}")
public OwnerDto getOneById(@PathVariable("id") Long id) {
LOGGER.info("GET " + BASE_URL + "/{}", id);
try {
return ownerMapper.entityToDto(ownerService.findOneById(id));
} catch (NotFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Error during reading owner", e);
}
}
}

View File

@@ -0,0 +1,70 @@
package at.ac.tuwien.sepm.assignment.individual.endpoint.dto;
import java.time.LocalDateTime;
import java.util.Objects;
abstract class BaseDto {
private Long id;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
protected BaseDto() {
}
public BaseDto(Long id, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.id = id;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BaseDto)) return false;
BaseDto baseDto = (BaseDto) o;
return Objects.equals(id, baseDto.id) &&
Objects.equals(createdAt, baseDto.createdAt) &&
Objects.equals(updatedAt, baseDto.updatedAt);
}
@Override
public int hashCode() {
return Objects.hash(id, createdAt, updatedAt);
}
protected String fieldsString() {
return "id=" + id +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt;
}
@Override
public String toString() {
return "BaseDto{ " + fieldsString() + " }";
}
}

View File

@@ -0,0 +1,104 @@
package at.ac.tuwien.sepm.assignment.individual.endpoint.dto;
import java.time.LocalDateTime;
import java.util.Objects;
import java.sql.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class HorseDto extends BaseDto {
private String name;
private String description;
private short score;
private Date birthday;
private long owner;
public HorseDto() {}
public HorseDto(String name, String description, short score, Date birthday, Long owner) {
this.name = name;
this.description = description;
this.score = score;
this.birthday = birthday;
this.owner = owner;
}
public HorseDto(Long id, String name, String description, short score, Date birthday, LocalDateTime created, LocalDateTime updated, Long owner) {
super(id, created, updated);
this.name = name;
this.description = description;
this.score = score;
this.birthday = birthday;
this.owner = owner;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public short getScore() {
return score;
}
public void setScore(short score) {
this.score = score;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Long getOwner() {
return owner;
}
public void setOwner(Long owner) {
this.owner = owner;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof HorseDto)) return false;
if (!super.equals(o)) return false;
HorseDto horseDto = (HorseDto) o;
return Objects.equals(name, horseDto.name);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), name);
}
@Override
protected String fieldsString() {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
return super.fieldsString() + ", name='" + name + '\'' +
"description='" + description + '\'' +
"score='" + score + '\'' +
"birthday='" + df.format(birthday) + '\'' +
"owner_id='" + owner + '\'';
}
@Override
public String toString() {
return "HorseDto{ " + fieldsString() + " }";
}
}

View File

@@ -0,0 +1,54 @@
package at.ac.tuwien.sepm.assignment.individual.endpoint.dto;
import java.time.LocalDateTime;
import java.util.Objects;
public class OwnerDto extends BaseDto {
private String name;
public OwnerDto() {
}
public OwnerDto(String name) {
this.name = name;
}
public OwnerDto(Long id, String name, LocalDateTime created, LocalDateTime updated) {
super(id, created, updated);
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof OwnerDto)) return false;
if (!super.equals(o)) return false;
OwnerDto ownerDto = (OwnerDto) o;
return Objects.equals(name, ownerDto.name);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), name);
}
@Override
protected String fieldsString() {
return super.fieldsString() + ", name='" + name + '\'';
}
@Override
public String toString() {
return "OwnerDto{ " + fieldsString() + " }";
}
}

View File

@@ -0,0 +1,17 @@
package at.ac.tuwien.sepm.assignment.individual.endpoint.mapper;
import at.ac.tuwien.sepm.assignment.individual.endpoint.dto.HorseDto;
import at.ac.tuwien.sepm.assignment.individual.entity.Horse;
import at.ac.tuwien.sepm.assignment.individual.entity.Owner;
import org.springframework.stereotype.Component;
@Component
public class HorseMapper {
public HorseDto entityToDto(Horse horse) {
return new HorseDto(horse.getId(), horse.getName(), horse.getDescription(), horse.getScore(), horse.getBirthday(), horse.getCreatedAt(), horse.getUpdatedAt(), horse.getOwner());
}
public Horse dtoToEntity(HorseDto horse) {
return new Horse(horse.getId(), horse.getName(), horse.getDescription(), horse.getScore(), horse.getBirthday(), horse.getOwner());
}
}

View File

@@ -0,0 +1,15 @@
package at.ac.tuwien.sepm.assignment.individual.endpoint.mapper;
import at.ac.tuwien.sepm.assignment.individual.endpoint.dto.OwnerDto;
import at.ac.tuwien.sepm.assignment.individual.entity.Owner;
import org.springframework.stereotype.Component;
@Component
public class OwnerMapper {
public OwnerDto entityToDto(Owner owner) {
return new OwnerDto(owner.getId(), owner.getName(), owner.getCreatedAt(), owner.getUpdatedAt());
}
}

View File

@@ -0,0 +1,74 @@
package at.ac.tuwien.sepm.assignment.individual.entity;
import java.time.LocalDateTime;
import java.util.Objects;
abstract class BaseEntity {
private Long id;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
protected BaseEntity() {
}
protected BaseEntity(Long id) {
this.id = id;
}
protected BaseEntity(Long id, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.id = id;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BaseEntity)) return false;
BaseEntity that = (BaseEntity) o;
return Objects.equals(id, that.id) &&
Objects.equals(createdAt, that.createdAt) &&
Objects.equals(updatedAt, that.updatedAt);
}
@Override
public int hashCode() {
return Objects.hash(id, createdAt, updatedAt);
}
protected String fieldsString() {
return "id=" + id +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt;
}
@Override
public String toString() {
return "BaseEntity{ " + fieldsString() + " }";
}
}

View File

@@ -0,0 +1,113 @@
package at.ac.tuwien.sepm.assignment.individual.entity;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.sql.Date;
import java.util.Objects;
public class Horse extends BaseEntity {
private String name;
private String description;
private short score;
private Date birthday;
private Long owner;
public Horse() {}
public Horse(String name, String description, short score, Date birthday, Long owner) {
this.name = name;
this.description = description;
this.score = score;
this.birthday = birthday;
this.owner = owner;
}
public Horse(Long id, String name, String description, short score, Date birthday, Long owner) {
super(id);
this.name = name;
this.description = description;
this.score = score;
this.birthday = birthday;
this.owner = owner;
}
public Horse(Long id, String name, String description, short score, Date birthday, Long owner, LocalDateTime created, LocalDateTime updated) {
super(id, created, updated);
this.name = name;
this.description = description;
this.score = score;
this.birthday = birthday;
this.owner = owner;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public short getScore() {
return score;
}
public void setScore(short score) {
this.score = score;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Long getOwner() {
return owner;
}
public void setOwner(Long owner) {
this.owner = owner;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Horse)) return false;
if (!super.equals(o)) return false;
Horse Horse = (Horse) o;
return Objects.equals(name, Horse.name);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), name);
}
@Override
protected String fieldsString() {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
return super.fieldsString() + ", name='" + name + '\'' +
"description='" + description + '\'' +
"score='" + score + '\'' +
"birthday='" + df.format(birthday) + '\'' +
"owner='" + owner + '\'';
}
@Override
public String toString() {
return "Horse{ " + fieldsString() + " }";
}
}

View File

@@ -0,0 +1,58 @@
package at.ac.tuwien.sepm.assignment.individual.entity;
import java.time.LocalDateTime;
import java.util.Objects;
public class Owner extends BaseEntity {
private String name;
public Owner() {
}
public Owner(String name) {
this.name = name;
}
public Owner(Long id, String name) {
super(id);
this.name = name;
}
public Owner(Long id, String name, LocalDateTime created, LocalDateTime updated) {
super(id, created, updated);
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Owner)) return false;
if (!super.equals(o)) return false;
Owner owner = (Owner) o;
return Objects.equals(name, owner.name);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), name);
}
@Override
protected String fieldsString() {
return super.fieldsString() + ", name='" + name + '\'';
}
@Override
public String toString() {
return "Owner{ " + fieldsString() +" }";
}
}

View File

@@ -0,0 +1,8 @@
package at.ac.tuwien.sepm.assignment.individual.exception;
public class NotFoundException extends RuntimeException {
public NotFoundException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,39 @@
package at.ac.tuwien.sepm.assignment.individual.persistence;
import java.lang.invoke.MethodHandles;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.init.ScriptUtils;
/**
* This component is only created, if the profile {@code datagen} is active
* You can activate this profile by adding {@code -Dspring.profiles.active=datagen} to your maven command line
*/
@Configuration
@Profile("datagen")
public class DataGeneratorBean {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private DataSource source;
public DataGeneratorBean(DataSource source) {
this.source = source;
}
/**
* Executed once when the component is instantiated. Inserts some dummy data.
*/
@PostConstruct
void insertDummyData() {
try {
ScriptUtils.executeSqlScript(source.getConnection(), new ClassPathResource("sql/insertData.sql"));
} catch (Exception e) {
LOGGER.error("Error inserting test data", e);
}
}
}

View File

@@ -0,0 +1,23 @@
package at.ac.tuwien.sepm.assignment.individual.persistence;
import at.ac.tuwien.sepm.assignment.individual.entity.Horse;
import at.ac.tuwien.sepm.assignment.individual.exception.NotFoundException;
import org.springframework.dao.DataAccessException;
public interface HorseDao {
/**
* @param id of the horse to find.
* @return the horse with the specified id.
* @throws DataAccessException will be thrown if something goes wrong during the database access.
* @throws NotFoundException will be thrown if the horse could not be found in the database.
*/
Horse findOneById(Long id);
/**
* @param horse that specifies the horse to add
* @return the newly created horse
* @throws DataAccessException will be thrown if something goes wrong during the database access.
*/
Horse addHorse(Horse horse);
}

View File

@@ -0,0 +1,17 @@
package at.ac.tuwien.sepm.assignment.individual.persistence;
import at.ac.tuwien.sepm.assignment.individual.entity.Owner;
import at.ac.tuwien.sepm.assignment.individual.exception.NotFoundException;
import org.springframework.dao.DataAccessException;
public interface OwnerDao {
/**
* @param id of the owner to find.
* @return the owner with the specified id.
* @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.
*/
Owner findOneById(Long id);
}

View File

@@ -0,0 +1,104 @@
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.exception.NotFoundException;
import at.ac.tuwien.sepm.assignment.individual.persistence.HorseDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.jdbc.core.JdbcTemplate;
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 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.List;
@Repository
public class HorseJdbcDao implements HorseDao {
private static final String TABLE_NAME = "horse";
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final JdbcTemplate jdbcTemplate;
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public HorseJdbcDao(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
}
@Override
public Horse findOneById(Long id) {
LOGGER.trace("Get horse with id {}", id);
final String sql = "SELECT * FROM " + TABLE_NAME + " WHERE id=?";
List<Horse> horses = jdbcTemplate.query(sql, new Object[] { id }, this::mapRow);
if (horses.isEmpty()) throw new NotFoundException("Could not find horse with id " + id);
return horses.get(0);
}
@Override
public Horse addHorse(Horse horse) throws DataAccessException {
LOGGER.trace("Add horse {}", horse.toString());
String sql = "INSERT INTO " + TABLE_NAME + "(name, description, score, birthday, owner_id, created_at, updated_at) VALUES(?, ?, ?, ?, ?, ?, ?)";
try {
LocalDateTime currentTime = LocalDateTime.now();
horse.setCreatedAt(currentTime);
horse.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, horse.getName());
ps.setString(2, horse.getDescription());
ps.setInt(3, horse.getScore());
ps.setDate(4, horse.getBirthday());
if(horse.getOwner() == 0)
ps.setNull(5, Types.NULL);
else
ps.setObject(5, horse.getOwner());
ps.setObject(6, horse.getCreatedAt());
ps.setObject(7, horse.getUpdatedAt());
return ps;
}, keyHolder);
if (changes == 0)
throw new NotFoundException("Creating horse failed, no rows affected");
horse.setId(((Number)keyHolder.getKeys().get("id")).longValue());
return horse;
} catch (DataAccessException e) {
// We are doing this in order to not change the exception type
throw new DataAccessException("Adding new records failed", e) {};
} catch(NotFoundException e){
throw new DataRetrievalFailureException("No new records added", e);
}
}
private Horse mapRow(ResultSet resultSet, int i) throws SQLException {
final Horse horse = new Horse();
horse.setId(resultSet.getLong("id"));
horse.setName(resultSet.getString("name"));
horse.setDescription(resultSet.getString("description"));
horse.setScore(resultSet.getShort("score"));
horse.setBirthday(resultSet.getDate("birthday"));
horse.setOwner(resultSet.getLong("owner_id"));
return horse;
}
}

View File

@@ -0,0 +1,52 @@
package at.ac.tuwien.sepm.assignment.individual.persistence.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 java.lang.invoke.MethodHandles;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class OwnerJdbcDao implements OwnerDao {
private static final String TABLE_NAME = "Owner";
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final JdbcTemplate jdbcTemplate;
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@Autowired
public OwnerJdbcDao(JdbcTemplate jdbcTemplate, NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
}
@Override
public Owner findOneById(Long id) {
LOGGER.trace("Get owner with id {}", id);
final String sql = "SELECT * FROM " + TABLE_NAME + " WHERE id=?";
List<Owner> owners = jdbcTemplate.query(sql, new Object[] { id }, this::mapRow);
if (owners.isEmpty()) throw new NotFoundException("Could not find owner with id " + id);
return owners.get(0);
}
private Owner mapRow(ResultSet resultSet, int i) throws SQLException {
final Owner owner = new Owner();
owner.setId(resultSet.getLong("id"));
owner.setName(resultSet.getString("name"));
owner.setCreatedAt(resultSet.getTimestamp("created_at").toLocalDateTime());
owner.setUpdatedAt(resultSet.getTimestamp("updated_at").toLocalDateTime());
return owner;
}
}

View File

@@ -0,0 +1,22 @@
package at.ac.tuwien.sepm.assignment.individual.service;
import at.ac.tuwien.sepm.assignment.individual.entity.Horse;
import at.ac.tuwien.sepm.assignment.individual.exception.NotFoundException;
public interface HorseService {
/**
* @param id of the horse to find.
* @return the horse with the specified id.
* @throws RuntimeException will be thrown if something goes wrong during data processing.
* @throws NotFoundException will be thrown if the horse could not be found in the system.
*/
Horse findOneById(Long id);
/**
* @param horse to add.
* @return the horse with the specified id.
* @throws RuntimeException will be thrown if something goes wrong during data processing.
* @throws NotFoundException will be thrown if the horse could not be found in the system.
*/
Horse addHorse(Horse horse);
}

View File

@@ -0,0 +1,17 @@
package at.ac.tuwien.sepm.assignment.individual.service;
import at.ac.tuwien.sepm.assignment.individual.entity.Owner;
import at.ac.tuwien.sepm.assignment.individual.exception.NotFoundException;
public interface OwnerService {
/**
* @param id of the owner to find.
* @return the owner with the specified id.
* @throws RuntimeException will be thrown if something goes wrong during data processing.
* @throws NotFoundException will be thrown if the owner could not be found in the system.
*/
Owner findOneById(Long id);
}

View File

@@ -0,0 +1,40 @@
package at.ac.tuwien.sepm.assignment.individual.service.impl;
import at.ac.tuwien.sepm.assignment.individual.entity.Horse;
import at.ac.tuwien.sepm.assignment.individual.persistence.HorseDao;
import at.ac.tuwien.sepm.assignment.individual.service.HorseService;
import at.ac.tuwien.sepm.assignment.individual.util.ValidationException;
import at.ac.tuwien.sepm.assignment.individual.util.Validator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import java.lang.invoke.MethodHandles;
@Service
public class SimpleHorseService implements HorseService {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final HorseDao horseDao;
private final Validator validator;
@Autowired
public SimpleHorseService(HorseDao horseDao, Validator validator) {
this.horseDao = horseDao;
this.validator = validator;
}
@Override
public Horse findOneById(Long id) {
LOGGER.trace("findOneById({})", id);
return horseDao.findOneById(id);
}
@Override
public Horse addHorse(Horse horse) throws ValidationException, DataAccessException {
LOGGER.trace("addHorse({})", horse.toString());
this.validator.validateNewHorse(horse);
return horseDao.addHorse(horse);
}
}

View File

@@ -0,0 +1,32 @@
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.persistence.OwnerDao;
import at.ac.tuwien.sepm.assignment.individual.service.OwnerService;
import at.ac.tuwien.sepm.assignment.individual.util.Validator;
import java.lang.invoke.MethodHandles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class SimpleOwnerService implements OwnerService {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final OwnerDao ownerDao;
private final Validator validator;
@Autowired
public SimpleOwnerService(OwnerDao ownerDao, Validator validator) {
this.ownerDao = ownerDao;
this.validator = validator;
}
@Override
public Owner findOneById(Long id) {
LOGGER.trace("findOneById({})", id);
return ownerDao.findOneById(id);
}
}

View File

@@ -0,0 +1,9 @@
package at.ac.tuwien.sepm.assignment.individual.util;
public class ValidationException extends RuntimeException {
public ValidationException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,23 @@
package at.ac.tuwien.sepm.assignment.individual.util;
import at.ac.tuwien.sepm.assignment.individual.entity.Horse;
import at.ac.tuwien.sepm.assignment.individual.entity.Owner;
import org.springframework.stereotype.Component;
@Component
public class Validator {
public void validateNewOwner(Owner owner) throws ValidationException {
}
public void validateUpdateOwner(Owner owner) throws ValidationException {
}
public void validateNewHorse(Horse horse) throws ValidationException {
if(horse.getScore() > 5 || horse.getScore() < 1) {
throw new ValidationException("Score value " + horse.getScore() + " not allowed. The score must be an integer between 1 and 5");
}
}
}

View File

@@ -0,0 +1,19 @@
logging:
level:
root: INFO
file:
name: ./log/wendys-friends.log
spring:
application:
name: wendys-friends
datasource:
url: "jdbc:h2:./wendydb;INIT=RUNSCRIPT FROM 'classpath:sql/createSchema.sql'"
username: "sa"
password: ""
driver-class-name: "org.h2.Driver"
h2:
console:
enabled: true
server:
port: 8080

View File

@@ -0,0 +1,21 @@
CREATE TABLE IF NOT EXISTS owner
(
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL
);
CREATE TABLE IF NOT EXISTS horse
(
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
score TINYINT NOT NULL CHECK(score >= 1 AND score <= 5),
birthday DATE NOT NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL
);
ALTER TABLE horse ADD IF NOT EXISTS owner_id BIGINT NULL;
ALTER TABLE horse ADD CONSTRAINT IF NOT EXISTS FL_OWNER FOREIGN KEY (owner_id) REFERENCES owner(id);

View File

@@ -0,0 +1,7 @@
-- insert initial test data
-- the id is hardcode to enable references between further test data
INSERT INTO owner (ID, NAME, CREATED_AT, UPDATED_AT)
VALUES (1, 'Fred', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()),
(2, 'Julia', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP()),
(3, 'Kim', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP());

View File

@@ -0,0 +1,30 @@
package at.ac.tuwien.sepm.assignment.individual.base;
import at.ac.tuwien.sepm.assignment.individual.entity.Owner;
public interface TestData {
/**
* URI Data
*/
String BASE_URL = "http://localhost:";
String HORSE_URL = "/horses";
String OWNER_URL = "/owners";
/**
* Owner Data
*/
static Owner getNewOwner() {
return new Owner("Owner");
}
static Owner getNewOwner(String name) {
return new Owner(name);
}
static Owner getNewOwnerWithId() {
return new Owner(1L, "Owner");
}
}

View File

@@ -0,0 +1,40 @@
package at.ac.tuwien.sepm.assignment.individual.unit.persistence;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
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 org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
@ExtendWith(SpringExtension.class)
@SpringBootTest
@ActiveProfiles("test")
public class OwnerDaoJdbcTest extends OwnerDaoTestBase {
@Autowired
PlatformTransactionManager txm;
TransactionStatus txstatus;
@BeforeEach
public void setupDBTransaction() {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
txstatus = txm.getTransaction(def);
assumeTrue(txstatus.isNewTransaction());
txstatus.setRollbackOnly();
}
@AfterEach
public void tearDownDBData() {
txm.rollback(txstatus);
}
}

View File

@@ -0,0 +1,23 @@
package at.ac.tuwien.sepm.assignment.individual.unit.persistence;
import static org.junit.jupiter.api.Assertions.*;
import at.ac.tuwien.sepm.assignment.individual.exception.NotFoundException;
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;
public abstract class OwnerDaoTestBase {
@Autowired
OwnerDao ownerDao;
@Test
@DisplayName("Finding owner by non-existing ID should throw NotFoundException")
public void findingOwnerById_nonExisting_shouldThrowNotFoundException() {
assertThrows(NotFoundException.class,
() -> ownerDao.findOneById(1L));
}
}