US10: Make it possible for horse information to be displayed on the view page, add a get image method to the API
This commit is contained in:
parent
3cc36466df
commit
4be964b537
@ -11,6 +11,7 @@ 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.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
@ -146,4 +147,24 @@ public class HorseEndpoint {
|
|||||||
"Something went wrong while uploading the file");
|
"Something went wrong while uploading the file");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/image/{path}", produces = {MediaType.IMAGE_JPEG_VALUE,MediaType.IMAGE_PNG_VALUE}, method=RequestMethod.GET)
|
||||||
|
@ResponseStatus(HttpStatus.OK)
|
||||||
|
public @ResponseBody byte[] getImage(@PathVariable("path") String path) {
|
||||||
|
// Get the image as byte array and return it with a media type assigned
|
||||||
|
// https://www.baeldung.com/spring-controller-return-image-file
|
||||||
|
// https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/MediaType.html
|
||||||
|
LOGGER.info("GET " + BASE_URL + "/image/{}", path);
|
||||||
|
try {
|
||||||
|
return horseService.getImage(path);
|
||||||
|
} 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.NOT_FOUND,
|
||||||
|
"Something went wrong while reading the file");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,4 +18,12 @@ public interface FileDao {
|
|||||||
* @throws IOException if something goes wrong with deleting the file
|
* @throws IOException if something goes wrong with deleting the file
|
||||||
*/
|
*/
|
||||||
void delete(String fileName) throws IOException;
|
void delete(String fileName) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used fot getting a file from the local filesystem
|
||||||
|
* @param fileName of the file to open
|
||||||
|
* @return the file as bytes
|
||||||
|
* @throws IOException if something goes wrong during file access
|
||||||
|
*/
|
||||||
|
byte[] get(String fileName) throws IOException;
|
||||||
}
|
}
|
||||||
|
@ -63,4 +63,21 @@ public class HorseFileDao implements FileDao {
|
|||||||
throw new IOException("Deleting the file specified failed");
|
throw new IOException("Deleting the file specified failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] get(String fileName) throws IOException {
|
||||||
|
if(fileName == null || fileName.equals(""))
|
||||||
|
throw new IOException("Cannot open an unexisting file");
|
||||||
|
|
||||||
|
LOGGER.trace("Getting file from " + FILE_BASE_PATH + fileName);
|
||||||
|
try {
|
||||||
|
// Read the file and return a byte array
|
||||||
|
// https://stackoverflow.com/a/5083666
|
||||||
|
File imageFile = new File(FILE_BASE_PATH + fileName);
|
||||||
|
return Files.readAllBytes(imageFile.toPath());
|
||||||
|
} catch(Exception e) {
|
||||||
|
LOGGER.error(e.getMessage());
|
||||||
|
throw new IOException("Opening the file specified failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,4 +64,12 @@ public interface HorseService {
|
|||||||
* @throws ValidationException will be thrown if the file is in the incorrect format
|
* @throws ValidationException will be thrown if the file is in the incorrect format
|
||||||
*/
|
*/
|
||||||
void saveImage(MultipartFile img) throws IOException, ValidationException;
|
void saveImage(MultipartFile img) throws IOException, ValidationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param path of the image to get
|
||||||
|
* @return the image
|
||||||
|
* @throws IOException will be thrown if the file could not be accessed
|
||||||
|
* @throws ValidationException will be thrown if the path does not end with .jpg, .jpeg or .png
|
||||||
|
*/
|
||||||
|
byte[] getImage(String path) throws IOException, ValidationException;
|
||||||
}
|
}
|
||||||
|
@ -78,4 +78,11 @@ public class SimpleHorseService implements HorseService {
|
|||||||
LOGGER.trace("saveImage({})", img.getName());
|
LOGGER.trace("saveImage({})", img.getName());
|
||||||
horseFileDao.save(img);
|
horseFileDao.save(img);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getImage(String path) throws IOException, ValidationException {
|
||||||
|
this.validator.validateImageRequest(path);
|
||||||
|
LOGGER.trace("getImage({})", path);
|
||||||
|
return horseFileDao.get(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,15 @@ public class Validator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void validateImageRequest(String path) throws ValidationException {
|
||||||
|
if(path == null || path.isEmpty()) {
|
||||||
|
throw new ValidationException("No image path supplied");
|
||||||
|
}
|
||||||
|
if(!path.endsWith(".png") && !path.endsWith(".jpg") && !path.endsWith(".jpeg")) {
|
||||||
|
throw new ValidationException("Unsupported file extension supplied.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void validateHorseFilter(Map<String, String> filters) throws ValidationException {
|
public void validateHorseFilter(Map<String, String> filters) throws ValidationException {
|
||||||
if(filters.get("score") != null) {
|
if(filters.get("score") != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -6,12 +6,40 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container mt-3" *ngIf="horse">
|
<div class="container mt-3" *ngIf="horse">
|
||||||
<div class="alert alert-success" role="alert">
|
<h1>Horse Info for {{ horse.name }}</h1>
|
||||||
<h4 class="alert-heading">Well done!</h4>
|
<hr>
|
||||||
<p>Your application is up and running</p>
|
<div class="row">
|
||||||
<hr>
|
<img class="img-thumbnail float-left col-md-6" src="http://localhost:8080/horses/image/{{ horse.imagePath }}" *ngIf="horse.imagePath">
|
||||||
<p>This is the name of the horse with id 1 from your backend system:
|
<table class="table table-striped float-right col-md-6">
|
||||||
<span class="font-weight-bold">{{horse.name}}</span>
|
<tbody>
|
||||||
</p>
|
<tr>
|
||||||
|
<th scope="row">Name</th>
|
||||||
|
<td>{{ horse.name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Owner</th>
|
||||||
|
<td>
|
||||||
|
<a href="owner/{{ horse.owner }}" *ngIf="horse.owner; else noOwner">{{ ownerName }}</a>
|
||||||
|
<ng-template #noOwner> {{ ownerName }} </ng-template>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Birthday</th>
|
||||||
|
<td>{{ horse.birthday }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Race</th>
|
||||||
|
<td>{{ horse.race }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Score</th>
|
||||||
|
<td>{{ horse.score }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Description</th>
|
||||||
|
<td>{{ horse.description }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,6 +2,8 @@ import { Component, OnInit } from '@angular/core';
|
|||||||
import { HorseService } from '../../service/horse.service';
|
import { HorseService } from '../../service/horse.service';
|
||||||
import { Horse } from '../../dto/horse';
|
import { Horse } from '../../dto/horse';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { OwnerService } from 'src/app/service/owner.service';
|
||||||
|
import { Owner } from 'src/app/dto/owner';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-horse',
|
selector: 'app-horse',
|
||||||
@ -13,8 +15,9 @@ export class HorseComponent implements OnInit {
|
|||||||
error = false;
|
error = false;
|
||||||
errorMessage = '';
|
errorMessage = '';
|
||||||
horse: Horse;
|
horse: Horse;
|
||||||
|
ownerName: string;
|
||||||
|
|
||||||
constructor(private horseService: HorseService, private route: ActivatedRoute) { }
|
constructor(private horseService: HorseService, private route: ActivatedRoute, private ownerService: OwnerService) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// Extract id from url
|
// Extract id from url
|
||||||
@ -37,6 +40,7 @@ export class HorseComponent implements OnInit {
|
|||||||
this.horseService.getHorseById(id).subscribe(
|
this.horseService.getHorseById(id).subscribe(
|
||||||
(horse: Horse) => {
|
(horse: Horse) => {
|
||||||
this.horse = horse;
|
this.horse = horse;
|
||||||
|
this.loadOwnerNameForHorse(horse.owner);
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
this.defaultServiceErrorHandling(error);
|
this.defaultServiceErrorHandling(error);
|
||||||
@ -44,6 +48,23 @@ export class HorseComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the name of the owner of a horse
|
||||||
|
* @param id of the owner to get
|
||||||
|
*/
|
||||||
|
private loadOwnerNameForHorse(id: number) {
|
||||||
|
this.ownerService.getOwnerById(id).subscribe(
|
||||||
|
(owner: Owner) => {
|
||||||
|
console.log(owner.name)
|
||||||
|
this.ownerName = owner.name;
|
||||||
|
console.log(this.ownerName);
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
this.ownerName = "N/A";
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private defaultServiceErrorHandling(error: any) {
|
private defaultServiceErrorHandling(error: any) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
this.error = true;
|
this.error = true;
|
||||||
|
Reference in New Issue
Block a user