package app.mealsmadeeasy.api.image; import app.mealsmadeeasy.api.image.body.ImageUpdateBody; import app.mealsmadeeasy.api.image.converter.ImageToViewConverter; import app.mealsmadeeasy.api.image.spec.ImageCreateSpec; import app.mealsmadeeasy.api.image.spec.ImageUpdateSpec; import app.mealsmadeeasy.api.image.view.ImageView; import app.mealsmadeeasy.api.user.User; import app.mealsmadeeasy.api.user.UserService; import app.mealsmadeeasy.api.util.AccessDeniedView; import lombok.RequiredArgsConstructor; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authorization.AuthorizationDeniedException; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.io.InputStream; import java.util.Set; import java.util.stream.Collectors; @RestController @RequestMapping("/images") @RequiredArgsConstructor public class ImageController { private final ImageService imageService; private final UserService userService; private final ImageToViewConverter imageToViewConverter; private ImageUpdateSpec getImageUpdateSpec(ImageUpdateBody body) { final var builder = ImageUpdateSpec.builder() .alt(body.getAlt()) .caption(body.getCaption()) .isPublic(body.getIsPublic()) .clearAllViewers(body.getClearAllViewers()); if (body.getViewersToAdd() != null) { builder.viewersToAdd(body.getViewersToAdd().stream() .map(this.userService::getUser) .collect(Collectors.toSet()) ); } if (body.getViewersToRemove() != null) { builder.viewersToRemove(body.getViewersToRemove().stream() .map(this.userService::getUser) .collect(Collectors.toSet()) ); } return builder.build(); } @ExceptionHandler public ResponseEntity onAccessDenied(AccessDeniedException e) { if (e instanceof AuthorizationDeniedException) { return ResponseEntity.status(HttpStatus.FORBIDDEN) .contentType(MediaType.APPLICATION_JSON) .body(new AccessDeniedView(HttpStatus.FORBIDDEN.value(), e.getMessage())); } else { return ResponseEntity.status(HttpStatus.UNAUTHORIZED) .contentType(MediaType.APPLICATION_JSON) .body(new AccessDeniedView(HttpStatus.UNAUTHORIZED.value(), e.getMessage())); } } @GetMapping("/{username}/{filename}") public ResponseEntity getImage( @AuthenticationPrincipal User principal, @PathVariable String username, @PathVariable String filename ) throws IOException { final User owner = this.userService.getUser(username); final Image image = this.imageService.getByOwnerAndFilename(owner, filename, principal); final InputStream imageInputStream = this.imageService.getImageContent(image, principal); return ResponseEntity.status(200) .contentType(MediaType.parseMediaType(image.getMimeType())) .body(new InputStreamResource(imageInputStream)); } @PutMapping public ResponseEntity putImage( @RequestParam MultipartFile image, @RequestParam String filename, @RequestParam(required = false) String alt, @RequestParam(required = false) String caption, @RequestParam(required = false) Boolean isPublic, @RequestParam(required = false) Set viewers, @AuthenticationPrincipal User principal ) throws IOException, ImageException { final var specBuilder = ImageCreateSpec.builder() .alt(alt) .caption(caption) .isPublic(isPublic); if (viewers != null) { specBuilder.viewersToAdd(viewers.stream().map(this.userService::getUser).collect(Collectors.toSet())); } final Image saved = this.imageService.create( principal, filename, image.getInputStream(), image.getSize(), specBuilder.build() ); return ResponseEntity.status(201).body(this.imageToViewConverter.convert(saved, principal, true)); } @PostMapping("/{username}/{filename}") public ResponseEntity updateInfo( @AuthenticationPrincipal User principal, @PathVariable String username, @PathVariable String filename, @RequestBody ImageUpdateBody body ) { final User owner = this.userService.getUser(username); final Image image = this.imageService.getByOwnerAndFilename(owner, filename, principal); final Image updated = this.imageService.update(image, principal, this.getImageUpdateSpec(body)); return ResponseEntity.ok(this.imageToViewConverter.convert(updated, principal, true)); } @DeleteMapping("/{username}/{filename}") public ResponseEntity deleteImage( @AuthenticationPrincipal User principal, @PathVariable String username, @PathVariable String filename ) throws IOException { final User owner = this.userService.getUser(username); final Image image = this.imageService.getByOwnerAndFilename(owner, filename, principal); this.imageService.deleteImage(image, principal); return ResponseEntity.noContent().build(); } }