US02: Make it possible to upload a picture through the API
This commit is contained in:
parent
f9a8d5016a
commit
0f5f0d2267
@ -12,8 +12,10 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.dao.DataAccessException;
|
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.multipart.MultipartFile;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@ -50,7 +52,7 @@ public class HorseEndpoint {
|
|||||||
return horseMapper.entityToDto(horseService.addHorse(horseEntity));
|
return horseMapper.entityToDto(horseService.addHorse(horseEntity));
|
||||||
} catch (ValidationException e) {
|
} catch (ValidationException e) {
|
||||||
LOGGER.error(e.getMessage());
|
LOGGER.error(e.getMessage());
|
||||||
throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY,
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
|
||||||
"Error during adding new horse: " + e.getMessage(), e);
|
"Error during adding new horse: " + e.getMessage(), e);
|
||||||
} catch (DataAccessException e) {
|
} catch (DataAccessException e) {
|
||||||
LOGGER.error(e.getMessage());
|
LOGGER.error(e.getMessage());
|
||||||
@ -58,4 +60,21 @@ public class HorseEndpoint {
|
|||||||
"Something went wrong during the communication with the database", e);
|
"Something went wrong during the communication with the database", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/upload")
|
||||||
|
@ResponseStatus(HttpStatus.CREATED)
|
||||||
|
public void addImage(@RequestParam("file") MultipartFile image) {
|
||||||
|
LOGGER.info("POST " + BASE_URL + "/upload");
|
||||||
|
try {
|
||||||
|
horseService.saveImage(image);
|
||||||
|
} catch(ValidationException e) {
|
||||||
|
LOGGER.error(e.getMessage());
|
||||||
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
|
||||||
|
"Unsupported file type provided. Supported file types are jpg and png.");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.error(e.getMessage());
|
||||||
|
throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY,
|
||||||
|
"Something went wrong while uploading the file");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,26 +14,29 @@ public class HorseDto extends BaseDto {
|
|||||||
private short score;
|
private short score;
|
||||||
private ERace race;
|
private ERace race;
|
||||||
private Date birthday;
|
private Date birthday;
|
||||||
|
private String imagePath;
|
||||||
private Long owner;
|
private Long owner;
|
||||||
|
|
||||||
public HorseDto() {}
|
public HorseDto() {}
|
||||||
|
|
||||||
public HorseDto(String name, String description, short score, Date birthday, ERace race, Long owner) {
|
public HorseDto(String name, String description, short score, Date birthday, ERace race, String imagePath, Long owner) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.score = score;
|
this.score = score;
|
||||||
this.birthday = birthday;
|
this.birthday = birthday;
|
||||||
this.race = race;
|
this.race = race;
|
||||||
|
this.imagePath = imagePath;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HorseDto(Long id, String name, String description, short score, Date birthday, ERace race, LocalDateTime created, LocalDateTime updated, Long owner) {
|
public HorseDto(Long id, String name, String description, short score, Date birthday, ERace race, String imagePath, LocalDateTime created, LocalDateTime updated, Long owner) {
|
||||||
super(id, created, updated);
|
super(id, created, updated);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.score = score;
|
this.score = score;
|
||||||
this.birthday = birthday;
|
this.birthday = birthday;
|
||||||
this.race = race;
|
this.race = race;
|
||||||
|
this.imagePath = imagePath;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,6 +80,14 @@ public class HorseDto extends BaseDto {
|
|||||||
this.race = race;
|
this.race = race;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getImagePath() {
|
||||||
|
return imagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImagePath(String imagePath) {
|
||||||
|
this.imagePath = imagePath;
|
||||||
|
}
|
||||||
|
|
||||||
public Long getOwner() {
|
public Long getOwner() {
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
@ -96,6 +107,7 @@ public class HorseDto extends BaseDto {
|
|||||||
Objects.equals(score, h.score) &&
|
Objects.equals(score, h.score) &&
|
||||||
Objects.equals(birthday, h.birthday) &&
|
Objects.equals(birthday, h.birthday) &&
|
||||||
Objects.equals(race, h.race) &&
|
Objects.equals(race, h.race) &&
|
||||||
|
Objects.equals(imagePath, h.imagePath) &&
|
||||||
Objects.equals(owner, h.owner);
|
Objects.equals(owner, h.owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +125,7 @@ public class HorseDto extends BaseDto {
|
|||||||
", score='" + score + '\'' +
|
", score='" + score + '\'' +
|
||||||
", birthday='" + df.format(birthday) + '\'' +
|
", birthday='" + df.format(birthday) + '\'' +
|
||||||
", race='" + race + '\'' +
|
", race='" + race + '\'' +
|
||||||
|
", imagePath='" + imagePath + '\'' +
|
||||||
", owner_id='" + owner + '\'';
|
", owner_id='" + owner + '\'';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,10 +8,10 @@ import org.springframework.stereotype.Component;
|
|||||||
@Component
|
@Component
|
||||||
public class HorseMapper {
|
public class HorseMapper {
|
||||||
public HorseDto entityToDto(Horse horse) {
|
public HorseDto entityToDto(Horse horse) {
|
||||||
return new HorseDto(horse.getId(), horse.getName(), horse.getDescription(), horse.getScore(), horse.getBirthday(), horse.getRace(), horse.getCreatedAt(), horse.getUpdatedAt(), horse.getOwner());
|
return new HorseDto(horse.getId(), horse.getName(), horse.getDescription(), horse.getScore(), horse.getBirthday(), horse.getRace(), horse.getImagePath(), horse.getCreatedAt(), horse.getUpdatedAt(), horse.getOwner());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Horse dtoToEntity(HorseDto horse) {
|
public Horse dtoToEntity(HorseDto horse) {
|
||||||
return new Horse(horse.getId(), horse.getName(), horse.getDescription(), horse.getScore(), horse.getBirthday(), horse.getRace(), horse.getOwner());
|
return new Horse(horse.getId(), horse.getName(), horse.getDescription(), horse.getScore(), horse.getBirthday(), horse.getRace(), horse.getImagePath(), horse.getOwner());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,36 +13,40 @@ public class Horse extends BaseEntity {
|
|||||||
private short score;
|
private short score;
|
||||||
private ERace race;
|
private ERace race;
|
||||||
private Date birthday;
|
private Date birthday;
|
||||||
|
private String imagePath;
|
||||||
private Long owner;
|
private Long owner;
|
||||||
|
|
||||||
public Horse() {}
|
public Horse() {}
|
||||||
|
|
||||||
public Horse(String name, String description, short score, Date birthday, ERace race,Long owner) {
|
public Horse(String name, String description, short score, Date birthday, ERace race, String imagePath, Long owner) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.score = score;
|
this.score = score;
|
||||||
this.birthday = birthday;
|
this.birthday = birthday;
|
||||||
this.race = race;
|
this.race = race;
|
||||||
|
this.imagePath = imagePath;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Horse(Long id, String name, String description, short score, Date birthday, ERace race, Long owner) {
|
public Horse(Long id, String name, String description, short score, Date birthday, ERace race, String imagePath, Long owner) {
|
||||||
super(id);
|
super(id);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.score = score;
|
this.score = score;
|
||||||
this.birthday = birthday;
|
this.birthday = birthday;
|
||||||
this.race = race;
|
this.race = race;
|
||||||
|
this.imagePath = imagePath;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Horse(Long id, String name, String description, short score, Date birthday, ERace race, Long owner, LocalDateTime created, LocalDateTime updated) {
|
public Horse(Long id, String name, String description, short score, Date birthday, ERace race, String imagePath, Long owner, LocalDateTime created, LocalDateTime updated) {
|
||||||
super(id, created, updated);
|
super(id, created, updated);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.score = score;
|
this.score = score;
|
||||||
this.birthday = birthday;
|
this.birthday = birthday;
|
||||||
this.race = race;
|
this.race = race;
|
||||||
|
this.imagePath = imagePath;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +90,14 @@ public class Horse extends BaseEntity {
|
|||||||
this.race = race;
|
this.race = race;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getImagePath() {
|
||||||
|
return imagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImagePath(String imagePath) {
|
||||||
|
this.imagePath = imagePath;
|
||||||
|
}
|
||||||
|
|
||||||
public Long getOwner() {
|
public Long getOwner() {
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
@ -105,6 +117,7 @@ public class Horse extends BaseEntity {
|
|||||||
Objects.equals(score, h.score) &&
|
Objects.equals(score, h.score) &&
|
||||||
Objects.equals(birthday, h.birthday) &&
|
Objects.equals(birthday, h.birthday) &&
|
||||||
Objects.equals(race, h.race) &&
|
Objects.equals(race, h.race) &&
|
||||||
|
Objects.equals(imagePath, h.imagePath) &&
|
||||||
Objects.equals(owner, h.owner);
|
Objects.equals(owner, h.owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,6 +135,7 @@ public class Horse extends BaseEntity {
|
|||||||
", score='" + score + '\'' +
|
", score='" + score + '\'' +
|
||||||
", birthday='" + df.format(birthday) + '\'' +
|
", birthday='" + df.format(birthday) + '\'' +
|
||||||
", race='" + race + '\'' +
|
", race='" + race + '\'' +
|
||||||
|
", imagePath='" + imagePath + '\'' +
|
||||||
", owner='" + owner + '\'';
|
", owner='" + owner + '\'';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
package at.ac.tuwien.sepm.assignment.individual.persistence;
|
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface FileDao {
|
||||||
|
/**
|
||||||
|
* Used for saving files on the local file system
|
||||||
|
* @param file file to save
|
||||||
|
*/
|
||||||
|
void save(MultipartFile file) throws IOException;
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package at.ac.tuwien.sepm.assignment.individual.persistence.impl;
|
||||||
|
|
||||||
|
import at.ac.tuwien.sepm.assignment.individual.persistence.FileDao;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.nio.file.FileSystemException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public class HorseFileDao implements FileDao {
|
||||||
|
@Value("${spring.upload.path}")
|
||||||
|
private String FILE_BASE_PATH;
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||||
|
public HorseFileDao() {};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(MultipartFile file) throws IOException {
|
||||||
|
if(file == null || file.getOriginalFilename() == null || file.isEmpty())
|
||||||
|
throw new IOException("Cannot save an empty file");
|
||||||
|
|
||||||
|
if(!file.getContentType().equals("image/jpeg") && !file.getContentType().equals("image/png"))
|
||||||
|
throw new IOException("Unsupported file type");
|
||||||
|
|
||||||
|
String fileName = file.getOriginalFilename();
|
||||||
|
LOGGER.trace("Writing file to " + FILE_BASE_PATH + fileName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create directory if it doesn't exist
|
||||||
|
File uploadDir = new File(FILE_BASE_PATH);
|
||||||
|
if(!uploadDir.exists())
|
||||||
|
uploadDir.mkdir();
|
||||||
|
|
||||||
|
// Save the file
|
||||||
|
if(uploadDir.exists()) {
|
||||||
|
File target = new File(FILE_BASE_PATH + fileName);
|
||||||
|
Files.write(target.toPath(), file.getBytes());
|
||||||
|
} else {
|
||||||
|
throw new FileSystemException("Base upload directory " + FILE_BASE_PATH + " does not exist");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error(e.getMessage());
|
||||||
|
throw new IOException("Saving the file failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -50,11 +50,11 @@ public class HorseJdbcDao implements HorseDao {
|
|||||||
@Override
|
@Override
|
||||||
public Horse addHorse(Horse horse) throws DataAccessException {
|
public Horse addHorse(Horse horse) throws DataAccessException {
|
||||||
LOGGER.trace("Add horse {}", horse.toString());
|
LOGGER.trace("Add horse {}", horse.toString());
|
||||||
String sql = "INSERT INTO " + TABLE_NAME + "(name, description, score, birthday, race, owner_id, created_at, updated_at) VALUES(?, ?, ?, ?, ?, ?, ?, ?)";
|
String sql = "INSERT INTO " + TABLE_NAME + "(name, description, score, birthday, race, image_path, owner_id, created_at, updated_at) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check if the constraints are violated
|
// Check if the constraints are violated
|
||||||
if(horse.getName() == null || horse.getScore() == 0 || horse.getBirthday() == null || horse.getRace() == null)
|
if(horse.getName() == null || horse.getScore() == 0 || horse.getBirthday() == null || horse.getRace() == null || horse.getImagePath() == null)
|
||||||
throw new DataIntegrityViolationException("Required parameters for horse missing");
|
throw new DataIntegrityViolationException("Required parameters for horse missing");
|
||||||
|
|
||||||
LocalDateTime currentTime = LocalDateTime.now();
|
LocalDateTime currentTime = LocalDateTime.now();
|
||||||
@ -74,13 +74,15 @@ public class HorseJdbcDao implements HorseDao {
|
|||||||
ps.setDate(4, horse.getBirthday());
|
ps.setDate(4, horse.getBirthday());
|
||||||
ps.setString(5, horse.getRace().toString()); // Convert to string to be able to save in DB
|
ps.setString(5, horse.getRace().toString()); // Convert to string to be able to save in DB
|
||||||
|
|
||||||
if(horse.getOwner() == null || horse.getOwner() == 0)
|
ps.setString(6, horse.getImagePath());
|
||||||
ps.setNull(6, Types.NULL);
|
|
||||||
else
|
|
||||||
ps.setObject(6, horse.getOwner());
|
|
||||||
|
|
||||||
ps.setObject(7, horse.getCreatedAt());
|
if(horse.getOwner() == null || horse.getOwner() == 0)
|
||||||
ps.setObject(8, horse.getUpdatedAt());
|
ps.setNull(7, Types.NULL);
|
||||||
|
else
|
||||||
|
ps.setObject(7, horse.getOwner());
|
||||||
|
|
||||||
|
ps.setObject(8, horse.getCreatedAt());
|
||||||
|
ps.setObject(9, horse.getUpdatedAt());
|
||||||
return ps;
|
return ps;
|
||||||
}, keyHolder);
|
}, keyHolder);
|
||||||
|
|
||||||
@ -98,6 +100,7 @@ public class HorseJdbcDao implements HorseDao {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Horse mapRow(ResultSet resultSet, int i) throws SQLException {
|
private Horse mapRow(ResultSet resultSet, int i) throws SQLException {
|
||||||
final Horse horse = new Horse();
|
final Horse horse = new Horse();
|
||||||
horse.setId(resultSet.getLong("id"));
|
horse.setId(resultSet.getLong("id"));
|
||||||
@ -107,6 +110,7 @@ public class HorseJdbcDao implements HorseDao {
|
|||||||
horse.setBirthday(resultSet.getDate("birthday"));
|
horse.setBirthday(resultSet.getDate("birthday"));
|
||||||
horse.setRace(ERace.valueOf(resultSet.getString("race"))); // Convert to Enum for usage in objects
|
horse.setRace(ERace.valueOf(resultSet.getString("race"))); // Convert to Enum for usage in objects
|
||||||
horse.setOwner(resultSet.getLong("owner_id"));
|
horse.setOwner(resultSet.getLong("owner_id"));
|
||||||
|
horse.setImagePath(resultSet.getString("image_path"));
|
||||||
horse.setCreatedAt(resultSet.getTimestamp("created_at").toLocalDateTime());
|
horse.setCreatedAt(resultSet.getTimestamp("created_at").toLocalDateTime());
|
||||||
horse.setUpdatedAt(resultSet.getTimestamp("updated_at").toLocalDateTime());
|
horse.setUpdatedAt(resultSet.getTimestamp("updated_at").toLocalDateTime());
|
||||||
return horse;
|
return horse;
|
||||||
|
@ -2,6 +2,9 @@ package at.ac.tuwien.sepm.assignment.individual.service;
|
|||||||
|
|
||||||
import at.ac.tuwien.sepm.assignment.individual.entity.Horse;
|
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.exception.NotFoundException;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
public interface HorseService {
|
public interface HorseService {
|
||||||
/**
|
/**
|
||||||
@ -19,4 +22,10 @@ public interface HorseService {
|
|||||||
* @throws NotFoundException will be thrown if the horse could not be found in the system.
|
* @throws NotFoundException will be thrown if the horse could not be found in the system.
|
||||||
*/
|
*/
|
||||||
Horse addHorse(Horse horse);
|
Horse addHorse(Horse horse);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param img image to upload
|
||||||
|
* @throws IOException will be thrown if something goes wrong with saving the file
|
||||||
|
*/
|
||||||
|
void saveImage(MultipartFile img) throws IOException;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package at.ac.tuwien.sepm.assignment.individual.service.impl;
|
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.entity.Horse;
|
||||||
|
import at.ac.tuwien.sepm.assignment.individual.persistence.FileDao;
|
||||||
import at.ac.tuwien.sepm.assignment.individual.persistence.HorseDao;
|
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.service.HorseService;
|
||||||
import at.ac.tuwien.sepm.assignment.individual.util.ValidationException;
|
import at.ac.tuwien.sepm.assignment.individual.util.ValidationException;
|
||||||
@ -10,32 +11,42 @@ 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.DataAccessException;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class SimpleHorseService implements HorseService {
|
public class SimpleHorseService implements HorseService {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||||
private final HorseDao horseDao;
|
private final HorseDao horseJdbcDao;
|
||||||
|
private final FileDao horseFileDao;
|
||||||
private final Validator validator;
|
private final Validator validator;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public SimpleHorseService(HorseDao horseDao, Validator validator) {
|
public SimpleHorseService(HorseDao horseJdbcDao, FileDao horseFileDao, Validator validator) {
|
||||||
this.horseDao = horseDao;
|
this.horseJdbcDao = horseJdbcDao;
|
||||||
|
this.horseFileDao = horseFileDao;
|
||||||
this.validator = validator;
|
this.validator = validator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Horse findOneById(Long id) {
|
public Horse findOneById(Long id) {
|
||||||
LOGGER.trace("findOneById({})", id);
|
LOGGER.trace("findOneById({})", id);
|
||||||
return horseDao.findOneById(id);
|
return horseJdbcDao.findOneById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Horse addHorse(Horse horse) throws ValidationException, DataAccessException {
|
public Horse addHorse(Horse horse) throws ValidationException, DataAccessException {
|
||||||
this.validator.validateNewHorse(horse);
|
this.validator.validateNewHorse(horse);
|
||||||
LOGGER.trace("addHorse({})", horse.toString());
|
LOGGER.trace("addHorse({})", horse.toString());
|
||||||
return horseDao.addHorse(horse);
|
return horseJdbcDao.addHorse(horse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveImage(MultipartFile img) throws ValidationException, IOException {
|
||||||
|
this.validator.validateHorseImage(img);
|
||||||
|
LOGGER.trace("saveImage({})", img.getName());
|
||||||
|
horseFileDao.save(img);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ 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.Horse;
|
||||||
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 org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class Validator {
|
public class Validator {
|
||||||
@ -22,5 +23,18 @@ public class Validator {
|
|||||||
if(horse.getScore() > 5 || horse.getScore() < 1) {
|
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");
|
throw new ValidationException("Score value " + horse.getScore() + " not allowed. The score must be an integer between 1 and 5");
|
||||||
}
|
}
|
||||||
|
if(!horse.getImagePath().endsWith(".png") && !horse.getImagePath().endsWith(".jpg") && !horse.getImagePath().endsWith(".jpeg")) {
|
||||||
|
throw new ValidationException("Unsupported file type supplied. Supported file types are jpg and png");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void validateHorseImage(MultipartFile image) throws ValidationException {
|
||||||
|
if(image == null || image.getContentType() == null || image.isEmpty()) {
|
||||||
|
throw new ValidationException("No image supplied");
|
||||||
|
}
|
||||||
|
if(!image.getContentType().equals("image/png") && !image.getContentType().equals("image/jpeg")) {
|
||||||
|
throw new ValidationException("Unsupported file type supplied: " + image.getContentType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -15,5 +15,12 @@ spring:
|
|||||||
h2:
|
h2:
|
||||||
console:
|
console:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
upload:
|
||||||
|
path: /tmp/uploads/
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
enabled: true
|
||||||
|
max-file-size: 10MB
|
||||||
|
max-request-size: 15MB
|
||||||
server:
|
server:
|
||||||
port: 8080
|
port: 8080
|
||||||
|
@ -17,6 +17,7 @@ CREATE TABLE IF NOT EXISTS horse
|
|||||||
score TINYINT NOT NULL CHECK(score >= 1 AND score <= 5),
|
score TINYINT NOT NULL CHECK(score >= 1 AND score <= 5),
|
||||||
birthday DATE NOT NULL,
|
birthday DATE NOT NULL,
|
||||||
race RACE NOT NULL,
|
race RACE NOT NULL,
|
||||||
|
image_path VARCHAR(255) NOT NULL,
|
||||||
created_at DATETIME NOT NULL,
|
created_at DATETIME NOT NULL,
|
||||||
updated_at DATETIME NOT NULL
|
updated_at DATETIME NOT NULL
|
||||||
);
|
);
|
||||||
|
@ -6,20 +6,25 @@ import at.ac.tuwien.sepm.assignment.individual.endpoint.dto.HorseDto;
|
|||||||
import at.ac.tuwien.sepm.assignment.individual.enums.ERace;
|
import at.ac.tuwien.sepm.assignment.individual.enums.ERace;
|
||||||
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.Value;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.boot.web.server.LocalServerPort;
|
import org.springframework.boot.web.server.LocalServerPort;
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.*;
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.test.context.ActiveProfiles;
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
import org.springframework.test.context.event.annotation.AfterTestMethod;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.sql.Date;
|
import java.sql.Date;
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
@ActiveProfiles("test")
|
@ActiveProfiles("test")
|
||||||
public class HorseEndpointTest {
|
public class HorseEndpointTest {
|
||||||
|
@Value("${spring.upload.path}")
|
||||||
|
private String FILE_BASE_PATH;
|
||||||
private static final RestTemplate REST_TEMPLATE = new RestTemplate();
|
private static final RestTemplate REST_TEMPLATE = new RestTemplate();
|
||||||
private static final String BASE_URL = "http://localhost:";
|
private static final String BASE_URL = "http://localhost:";
|
||||||
private static final String HORSE_URL = "/horses";
|
private static final String HORSE_URL = "/horses";
|
||||||
@ -31,7 +36,7 @@ public class HorseEndpointTest {
|
|||||||
@DisplayName("Adding a new horse with the correct parameters will return HTTP 201 and the new HorseDto")
|
@DisplayName("Adding a new horse with the correct parameters will return HTTP 201 and the new HorseDto")
|
||||||
public void addingNewHorse_correctParameters_shouldReturnStatus201AndHorse() {
|
public void addingNewHorse_correctParameters_shouldReturnStatus201AndHorse() {
|
||||||
String birthday = "2020-01-01";
|
String birthday = "2020-01-01";
|
||||||
HorseDto newHorse = new HorseDto("Zephyr", "Nice horse", (short) 4, Date.valueOf(birthday), ERace.APPALOOSA, null);
|
HorseDto newHorse = new HorseDto("Zephyr", "Nice horse", (short) 4, Date.valueOf(birthday), ERace.APPALOOSA, "files/test.png", null);
|
||||||
|
|
||||||
HttpEntity<HorseDto> request = new HttpEntity<>(newHorse);
|
HttpEntity<HorseDto> request = new HttpEntity<>(newHorse);
|
||||||
ResponseEntity<HorseDto> response = REST_TEMPLATE
|
ResponseEntity<HorseDto> response = REST_TEMPLATE
|
||||||
@ -43,6 +48,32 @@ public class HorseEndpointTest {
|
|||||||
assertEquals(newHorse.getDescription(), response.getBody().getDescription());
|
assertEquals(newHorse.getDescription(), response.getBody().getDescription());
|
||||||
assertEquals(newHorse.getScore(), response.getBody().getScore());
|
assertEquals(newHorse.getScore(), response.getBody().getScore());
|
||||||
assertEquals(newHorse.getBirthday().toString(), response.getBody().getBirthday().toString());
|
assertEquals(newHorse.getBirthday().toString(), response.getBody().getBirthday().toString());
|
||||||
|
assertEquals(newHorse.getImagePath(), response.getBody().getImagePath());
|
||||||
assertEquals(newHorse.getOwner(), response.getBody().getOwner());
|
assertEquals(newHorse.getOwner(), response.getBody().getOwner());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Uploading an image in the correct format will return HTTP 201")
|
||||||
|
public void addingNewImage_correctFormat_shouldReturnStatus201() {
|
||||||
|
try {
|
||||||
|
// More information:
|
||||||
|
// https://www.baeldung.com/spring-rest-template-multipart-upload
|
||||||
|
// https://github.com/spring-guides/gs-uploading-files/blob/master/complete/src/test/java/com/example/uploadingfiles/FileUploadIntegrationTests.java
|
||||||
|
|
||||||
|
// Get the file
|
||||||
|
ClassPathResource image = new ClassPathResource("horse.jpg", getClass());
|
||||||
|
|
||||||
|
// Set the body
|
||||||
|
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
||||||
|
body.add("file", image);
|
||||||
|
|
||||||
|
// Create the HTTP Entity
|
||||||
|
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(body);
|
||||||
|
|
||||||
|
ResponseEntity<String> response = REST_TEMPLATE.postForEntity(BASE_URL + port + HORSE_URL + "/upload", request, String.class);
|
||||||
|
assertEquals(HttpStatus.CREATED, response.getStatusCode());
|
||||||
|
} finally {
|
||||||
|
new File(FILE_BASE_PATH + "horse.jpg").delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ public abstract class HorseDaoTestBase {
|
|||||||
@DisplayName("Adding a new horse with the correct parameters should return the horse")
|
@DisplayName("Adding a new horse with the correct parameters should return the horse")
|
||||||
public void addingNewHorse_correctParameters_shouldReturnHorse() {
|
public void addingNewHorse_correctParameters_shouldReturnHorse() {
|
||||||
String birthday = "2020-01-01";
|
String birthday = "2020-01-01";
|
||||||
Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 4, Date.valueOf(birthday), ERace.APPALOOSA, null);
|
Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 4, Date.valueOf(birthday), ERace.APPALOOSA, "files/test.png", null);
|
||||||
Horse savedHorse = horseDao.addHorse(newHorse);
|
Horse savedHorse = horseDao.addHorse(newHorse);
|
||||||
assertEquals(newHorse, savedHorse);
|
assertEquals(newHorse, savedHorse);
|
||||||
}
|
}
|
||||||
@ -30,7 +30,7 @@ public abstract class HorseDaoTestBase {
|
|||||||
@DisplayName("Adding a new horse with the incorrect parameters should throw DataAccessException")
|
@DisplayName("Adding a new horse with the incorrect parameters should throw DataAccessException")
|
||||||
public void addingNewHorse_incorrectParameters_shouldThrowDataAccess() {
|
public void addingNewHorse_incorrectParameters_shouldThrowDataAccess() {
|
||||||
String birthday = "2020-01-01";
|
String birthday = "2020-01-01";
|
||||||
Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 80, Date.valueOf(birthday), null, null);
|
Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 80, Date.valueOf(birthday), null, null, null);
|
||||||
assertThrows(DataAccessException.class, () -> horseDao.addHorse(newHorse));
|
assertThrows(DataAccessException.class, () -> horseDao.addHorse(newHorse));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
package at.ac.tuwien.sepm.assignment.individual.unit.persistence;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import at.ac.tuwien.sepm.assignment.individual.persistence.impl.HorseFileDao;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
@ActiveProfiles("test")
|
||||||
|
public class HorseFileDaoTest {
|
||||||
|
@Value("${spring.upload.path}")
|
||||||
|
private String FILE_BASE_PATH;
|
||||||
|
@Autowired
|
||||||
|
HorseFileDao horseFileDao;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Creating a file with the correct type should result in a saved file")
|
||||||
|
public void savingFile_correctType_shouldWriteFile() throws FileNotFoundException, IOException {
|
||||||
|
File image = new File("src/test/resources/at/ac/tuwien/sepm/assignment/individual/integration/horse.jpg");
|
||||||
|
horseFileDao.save(new MockMultipartFile("file", image.getName(), MediaType.IMAGE_JPEG_VALUE, new FileInputStream(image)));
|
||||||
|
File savedImage = new File(FILE_BASE_PATH + "horse.jpg");
|
||||||
|
assertTrue(savedImage.exists());
|
||||||
|
assertTrue(savedImage.isFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Creating a file with the correct type should result in a saved file")
|
||||||
|
public void savingFile_incorrectType_shouldThrowIOException() throws FileNotFoundException, IOException {
|
||||||
|
File image = new File("src/test/resources/at/ac/tuwien/sepm/assignment/individual/integration/horse.jpg");
|
||||||
|
assertThrows(IOException.class, () -> horseFileDao.save(new MockMultipartFile("file", image.getName(), MediaType.TEXT_HTML_VALUE, new FileInputStream(image))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void cleanup() {
|
||||||
|
new File(FILE_BASE_PATH + "horse.jpg").delete();
|
||||||
|
}
|
||||||
|
}
|
@ -29,7 +29,7 @@ public class HorseServiceTest {
|
|||||||
@DisplayName("Adding a new horse with the correct parameters will return the new horse")
|
@DisplayName("Adding a new horse with the correct parameters will return the new horse")
|
||||||
public void addingNewHorse_correctParameters_shouldReturnHorse() {
|
public void addingNewHorse_correctParameters_shouldReturnHorse() {
|
||||||
String birthday = "2020-01-01";
|
String birthday = "2020-01-01";
|
||||||
Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 4, Date.valueOf(birthday), ERace.APPALOOSA, null);
|
Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 4, Date.valueOf(birthday), ERace.APPALOOSA, "files/test.png", null);
|
||||||
Horse savedHorse = horseService.addHorse(newHorse);
|
Horse savedHorse = horseService.addHorse(newHorse);
|
||||||
assertEquals(newHorse, savedHorse);
|
assertEquals(newHorse, savedHorse);
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@ public class HorseServiceTest {
|
|||||||
@DisplayName("Adding a new horse with the incorrect parameters will throw a ValidationException")
|
@DisplayName("Adding a new horse with the incorrect parameters will throw a ValidationException")
|
||||||
public void addingNewHorse_incorrectParameters_shouldThrowValidation() {
|
public void addingNewHorse_incorrectParameters_shouldThrowValidation() {
|
||||||
String birthday = "2020-01-01";
|
String birthday = "2020-01-01";
|
||||||
Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 80, Date.valueOf(birthday), null, null);
|
Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 80, Date.valueOf(birthday), null, null, null);
|
||||||
assertThrows(ValidationException.class, () -> horseService.addHorse(newHorse));
|
assertThrows(ValidationException.class, () -> horseService.addHorse(newHorse));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
Reference in New Issue
Block a user