Clean up image and auth classes.

This commit is contained in:
Jesse Brault 2026-01-15 16:17:08 -06:00
parent 14c911c283
commit 70c560f0cb
17 changed files with 231 additions and 393 deletions

View File

@ -3,9 +3,9 @@ package app.mealsmadeeasy.api.image;
import app.mealsmadeeasy.api.IntegrationTestsExtension;
import app.mealsmadeeasy.api.auth.AuthService;
import app.mealsmadeeasy.api.auth.LoginException;
import app.mealsmadeeasy.api.image.body.ImageUpdateInfoBody;
import app.mealsmadeeasy.api.image.spec.ImageCreateInfoSpec;
import app.mealsmadeeasy.api.image.spec.ImageUpdateInfoSpec;
import app.mealsmadeeasy.api.image.body.ImageUpdateBody;
import app.mealsmadeeasy.api.image.spec.ImageCreateSpec;
import app.mealsmadeeasy.api.image.spec.ImageUpdateSpec;
import app.mealsmadeeasy.api.user.User;
import app.mealsmadeeasy.api.user.UserCreateException;
import app.mealsmadeeasy.api.user.UserService;
@ -89,7 +89,7 @@ public class ImageControllerTests {
}
}
private Image seedImage(User owner, ImageCreateInfoSpec spec) {
private Image seedImage(User owner, ImageCreateSpec spec) {
try {
return this.imageService.create(
owner,
@ -104,7 +104,7 @@ public class ImageControllerTests {
}
private Image seedImage(User owner) {
return this.seedImage(owner, new ImageCreateInfoSpec());
return this.seedImage(owner, ImageCreateSpec.builder().build());
}
private static String getImageUrl(User owner, Image image) {
@ -122,8 +122,9 @@ public class ImageControllerTests {
@Test
public void getPublicImageNoPrincipal() throws Exception {
final User owner = this.seedUser();
final ImageCreateInfoSpec spec = new ImageCreateInfoSpec();
spec.setPublic(true);
final ImageCreateSpec spec = ImageCreateSpec.builder()
.isPublic(true)
.build();
final Image image = this.seedImage(owner, spec);
// Assert bytes the same and proper mime type
@ -161,8 +162,9 @@ public class ImageControllerTests {
final Image image = this.seedImage(owner);
// add viewer
final ImageUpdateInfoSpec spec = new ImageUpdateInfoSpec();
spec.setViewersToAdd(Set.of(viewer));
final ImageUpdateSpec spec = ImageUpdateSpec.builder()
.viewersToAdd(Set.of(viewer))
.build();
this.imageService.update(image, owner, spec);
this.doGetImageTestWithAccessToken(owner, image, this.getAccessToken(viewer));
@ -195,8 +197,9 @@ public class ImageControllerTests {
final User viewer = this.seedUser();
// add viewer
final ImageUpdateInfoSpec spec = new ImageUpdateInfoSpec();
spec.setViewersToAdd(Set.of(viewer));
final ImageUpdateSpec spec = ImageUpdateSpec.builder()
.viewersToAdd(Set.of(viewer))
.build();
this.imageService.update(image, owner, spec);
this.mockMvc.perform(get(getImageUrl(owner, image))).andExpect(status().isForbidden());
@ -231,7 +234,7 @@ public class ImageControllerTests {
.andExpect(jsonPath("$.mimeType").value("image/svg+xml"))
.andExpect(jsonPath("$.alt").value("HAL 9000"))
.andExpect(jsonPath("$.caption").value("HAL 9000, from 2001: A Space Odyssey"))
.andExpect(jsonPath("$.isPublic").value(true))
.andExpect(jsonPath("$.public").value(true))
.andExpect(jsonPath("$.owner.username").value(owner.getUsername()))
.andExpect(jsonPath("$.owner.id").value(owner.getId()))
.andExpect(jsonPath("$.viewers").value(empty()));
@ -243,7 +246,7 @@ public class ImageControllerTests {
final User owner = this.seedUser();
final Image image = this.seedImage(owner);
final String accessToken = this.getAccessToken(owner);
final ImageUpdateInfoBody body = new ImageUpdateInfoBody();
final ImageUpdateBody body = new ImageUpdateBody();
body.setAlt("HAL 9000");
this.mockMvc.perform(
post(getImageUrl(owner, image))
@ -261,7 +264,7 @@ public class ImageControllerTests {
final User owner = this.seedUser();
final Image image = this.seedImage(owner);
final String accessToken = this.getAccessToken(owner);
final ImageUpdateInfoBody body = new ImageUpdateInfoBody();
final ImageUpdateBody body = new ImageUpdateBody();
body.setCaption("HAL 9000 from 2001: A Space Odyssey");
this.mockMvc.perform(
post(getImageUrl(owner, image))
@ -279,8 +282,8 @@ public class ImageControllerTests {
final User owner = this.seedUser();
final Image image = this.seedImage(owner);
final String accessToken = this.getAccessToken(owner);
final ImageUpdateInfoBody body = new ImageUpdateInfoBody();
body.setPublic(true);
final ImageUpdateBody body = new ImageUpdateBody();
body.setIsPublic(true);
this.mockMvc.perform(
post(getImageUrl(owner, image))
.contentType(MediaType.APPLICATION_JSON)
@ -289,7 +292,7 @@ public class ImageControllerTests {
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.modified").value(notNullValue()))
.andExpect(jsonPath("$.isPublic").value(true));
.andExpect(jsonPath("$.public").value(true));
}
@Test
@ -299,7 +302,7 @@ public class ImageControllerTests {
final Image image = this.seedImage(owner);
final String accessToken = this.getAccessToken(owner);
final ImageUpdateInfoBody body = new ImageUpdateInfoBody();
final ImageUpdateBody body = new ImageUpdateBody();
final Set<String> viewerUsernames = Set.of(viewerToAdd.getUsername());
body.setViewersToAdd(viewerUsernames);
@ -321,8 +324,9 @@ public class ImageControllerTests {
final User owner = this.seedUser();
final User viewer = this.seedUser();
final Image image = this.seedImage(owner);
final ImageUpdateInfoSpec spec = new ImageUpdateInfoSpec();
spec.setViewersToAdd(Set.of(viewer));
final ImageUpdateSpec spec = ImageUpdateSpec.builder()
.viewersToAdd(Set.of(viewer))
.build();
this.imageService.update(image, owner, spec);
return new OwnerViewerImage(owner, viewer, image);
}
@ -332,7 +336,7 @@ public class ImageControllerTests {
final OwnerViewerImage ownerViewerImage = this.prepOwnerViewerImage();
final String accessToken = this.getAccessToken(ownerViewerImage.owner());
final ImageUpdateInfoBody body = new ImageUpdateInfoBody();
final ImageUpdateBody body = new ImageUpdateBody();
body.setViewersToRemove(Set.of(ownerViewerImage.viewer().getUsername()));
this.mockMvc.perform(
@ -350,7 +354,7 @@ public class ImageControllerTests {
public void clearAllViewers() throws Exception {
final OwnerViewerImage ownerViewerImage = this.prepOwnerViewerImage();
final String accessToken = this.getAccessToken(ownerViewerImage.owner());
final ImageUpdateInfoBody body = new ImageUpdateInfoBody();
final ImageUpdateBody body = new ImageUpdateBody();
body.setClearAllViewers(true);
this.mockMvc.perform(
post(getImageUrl(ownerViewerImage.owner(), ownerViewerImage.image()))
@ -367,7 +371,7 @@ public class ImageControllerTests {
public void updateInfoByViewerForbidden() throws Exception {
final OwnerViewerImage ownerViewerImage = this.prepOwnerViewerImage();
final String accessToken = this.getAccessToken(ownerViewerImage.viewer()); // viewer
final ImageUpdateInfoBody body = new ImageUpdateInfoBody();
final ImageUpdateBody body = new ImageUpdateBody();
this.mockMvc.perform(
post(getImageUrl(ownerViewerImage.owner(), ownerViewerImage.image()))
.contentType(MediaType.APPLICATION_JSON)

View File

@ -1,8 +1,8 @@
package app.mealsmadeeasy.api.image;
import app.mealsmadeeasy.api.IntegrationTestsExtension;
import app.mealsmadeeasy.api.image.spec.ImageCreateInfoSpec;
import app.mealsmadeeasy.api.image.spec.ImageUpdateInfoSpec;
import app.mealsmadeeasy.api.image.spec.ImageCreateSpec;
import app.mealsmadeeasy.api.image.spec.ImageUpdateSpec;
import app.mealsmadeeasy.api.user.User;
import app.mealsmadeeasy.api.user.UserCreateException;
import app.mealsmadeeasy.api.user.UserService;
@ -77,7 +77,7 @@ public class S3ImageServiceTests {
return UUID.randomUUID() + ".svg";
}
private Image seedImage(User owner, ImageCreateInfoSpec spec) {
private Image seedImage(User owner, ImageCreateSpec spec) {
try (final InputStream hal9000 = getHal9000InputStream()) {
return this.imageService.create(
owner,
@ -92,12 +92,13 @@ public class S3ImageServiceTests {
}
private Image seedImage(User owner) {
return this.seedImage(owner, new ImageCreateInfoSpec());
return this.seedImage(owner, ImageCreateSpec.builder().build());
}
private Image makePublic(Image image, User modifier) {
final ImageUpdateInfoSpec spec = new ImageUpdateInfoSpec();
spec.setPublic(true);
final ImageUpdateSpec spec = ImageUpdateSpec.builder()
.isPublic(true)
.build();
return this.imageService.update(image, modifier, spec);
}
@ -124,7 +125,6 @@ public class S3ImageServiceTests {
final User owner = this.seedUser();
final Image image = this.seedImage(owner);
final InputStream content = assertDoesNotThrow(() -> this.imageService.getImageContent(image, owner));
//noinspection DataFlowIssue
final byte[] contentBytes = content.readAllBytes();
assertThat(contentBytes.length, is((int) HAL_LENGTH));
content.close();
@ -135,7 +135,6 @@ public class S3ImageServiceTests {
final User owner = this.seedUser();
final Image image = this.seedImage(owner);
final InputStream content = assertDoesNotThrow(() -> this.imageService.getImageContent(image, owner));
//noinspection DataFlowIssue
content.close();
}
@ -145,7 +144,6 @@ public class S3ImageServiceTests {
final Image seedImage = this.seedImage(owner);
final Image publicImage = this.makePublic(seedImage, owner);
final InputStream content = assertDoesNotThrow(() -> this.imageService.getImageContent(publicImage, null));
//noinspection DataFlowIssue
content.close();
}
@ -154,11 +152,11 @@ public class S3ImageServiceTests {
final User owner = this.seedUser();
final User viewer = this.seedUser();
Image seedImage = this.seedImage(owner);
final ImageUpdateInfoSpec spec = new ImageUpdateInfoSpec();
spec.setViewersToAdd(Set.of(viewer));
final ImageUpdateSpec spec = ImageUpdateSpec.builder()
.viewersToAdd(Set.of(viewer))
.build();
final Image imageWithViewer = this.imageService.update(seedImage, owner, spec);
final InputStream content = assertDoesNotThrow(() -> this.imageService.getImageContent(imageWithViewer, viewer));
//noinspection DataFlowIssue
content.close();
}
@ -183,8 +181,9 @@ public class S3ImageServiceTests {
public void updateAlt() {
final User owner = this.seedUser();
Image image = this.seedImage(owner);
final ImageUpdateInfoSpec spec = new ImageUpdateInfoSpec();
spec.setAlt("HAL 9000");
final ImageUpdateSpec spec = ImageUpdateSpec.builder()
.alt("HAL 9000")
.build();
image = this.imageService.update(image, owner, spec);
assertThat(image.getAlt(), is("HAL 9000"));
}
@ -193,8 +192,9 @@ public class S3ImageServiceTests {
public void updateCaption() {
final User owner = this.seedUser();
Image image = this.seedImage(owner);
final ImageUpdateInfoSpec spec = new ImageUpdateInfoSpec();
spec.setCaption("HAL 9000 from 2001: A Space Odyssey");
final ImageUpdateSpec spec = ImageUpdateSpec.builder()
.caption("HAL 9000 from 2001: A Space Odyssey")
.build();
image = this.imageService.update(image, owner, spec);
assertThat(image.getCaption(), is("HAL 9000 from 2001: A Space Odyssey"));
}
@ -203,15 +203,17 @@ public class S3ImageServiceTests {
public void updateIsPublic() {
final User owner = this.seedUser();
Image image = this.seedImage(owner);
final ImageUpdateInfoSpec spec = new ImageUpdateInfoSpec();
spec.setPublic(true);
final ImageUpdateSpec spec = ImageUpdateSpec.builder()
.isPublic(true)
.build();
image = this.imageService.update(image, owner, spec);
assertThat(image.getIsPublic(), is(true));
}
private Image addViewer(Image image, User owner, User viewer) {
final ImageUpdateInfoSpec spec0 = new ImageUpdateInfoSpec();
spec0.setViewersToAdd(Set.of(viewer));
final ImageUpdateSpec spec0 = ImageUpdateSpec.builder()
.viewersToAdd(Set.of(viewer))
.build();
return this.imageService.update(image, owner, spec0);
}
@ -232,8 +234,9 @@ public class S3ImageServiceTests {
image = this.addViewer(image, owner, viewer);
assertThat(image.getViewers(), containsUsers(viewer));
final ImageUpdateInfoSpec spec1 = new ImageUpdateInfoSpec();
spec1.setViewersToRemove(Set.of(viewer));
final ImageUpdateSpec spec1 = ImageUpdateSpec.builder()
.viewersToRemove(Set.of(viewer))
.build();
image = this.imageService.update(image, owner, spec1);
assertThat(image.getViewers(), empty());
}
@ -246,8 +249,9 @@ public class S3ImageServiceTests {
image = this.addViewer(image, owner, viewer);
assertThat(image.getViewers(), containsUsers(viewer));
final ImageUpdateInfoSpec spec1 = new ImageUpdateInfoSpec();
spec1.setClearAllViewers(true);
final ImageUpdateSpec spec1 = ImageUpdateSpec.builder()
.clearAllViewers(true)
.build();
image = this.imageService.update(image, owner, spec1);
assertThat(image.getViewers(), empty());
}

View File

@ -7,7 +7,7 @@ import app.mealsmadeeasy.api.auth.LoginException;
import app.mealsmadeeasy.api.image.Image;
import app.mealsmadeeasy.api.image.ImageService;
import app.mealsmadeeasy.api.image.S3ImageServiceTests;
import app.mealsmadeeasy.api.image.spec.ImageCreateInfoSpec;
import app.mealsmadeeasy.api.image.spec.ImageCreateSpec;
import app.mealsmadeeasy.api.recipe.spec.RecipeCreateSpec;
import app.mealsmadeeasy.api.recipe.spec.RecipeUpdateSpec;
import app.mealsmadeeasy.api.recipe.star.RecipeStarService;
@ -117,7 +117,7 @@ public class RecipeControllerTests {
UUID.randomUUID() + ".svg",
hal9000,
27881L,
new ImageCreateInfoSpec()
ImageCreateSpec.builder().build()
);
} catch (Exception e) {
throw new RuntimeException(e);

View File

@ -2,7 +2,7 @@ package app.mealsmadeeasy.api;
import app.mealsmadeeasy.api.image.Image;
import app.mealsmadeeasy.api.image.ImageService;
import app.mealsmadeeasy.api.image.spec.ImageCreateInfoSpec;
import app.mealsmadeeasy.api.image.spec.ImageCreateSpec;
import app.mealsmadeeasy.api.recipe.Recipe;
import app.mealsmadeeasy.api.recipe.RecipeService;
import app.mealsmadeeasy.api.recipe.spec.RecipeCreateSpec;
@ -82,10 +82,11 @@ public class DevConfiguration {
rawFrontMatter, RecipeFrontMatter.class
);
final ImageCreateInfoSpec imageCreateSpec = new ImageCreateInfoSpec();
imageCreateSpec.setAlt(frontMatter.mainImage.alt);
imageCreateSpec.setCaption(frontMatter.mainImage.caption);
imageCreateSpec.setPublic(frontMatter.mainImage.isPublic);
final ImageCreateSpec imageCreateSpec = ImageCreateSpec.builder()
.alt(frontMatter.mainImage.alt)
.caption(frontMatter.mainImage.caption)
.isPublic(frontMatter.mainImage.isPublic)
.build();
final Path givenPath = Path.of(frontMatter.mainImage.src);
final Path resolvedPath = Path.of("dev-data", "images").resolve(givenPath);
final Image mainImage;

View File

@ -8,7 +8,7 @@ import org.springframework.data.jpa.repository.Query;
import java.util.Optional;
import java.util.UUID;
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, UUID> {
Optional<RefreshToken> findByToken(UUID token);

View File

@ -1,8 +1,8 @@
package app.mealsmadeeasy.api.image;
import app.mealsmadeeasy.api.image.body.ImageUpdateInfoBody;
import app.mealsmadeeasy.api.image.spec.ImageCreateInfoSpec;
import app.mealsmadeeasy.api.image.spec.ImageUpdateInfoSpec;
import app.mealsmadeeasy.api.image.body.ImageUpdateBody;
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;
@ -34,27 +34,25 @@ public class ImageController {
this.userService = userService;
}
private ImageUpdateInfoSpec getImageUpdateSpec(ImageUpdateInfoBody body) {
final ImageUpdateInfoSpec spec = new ImageUpdateInfoSpec();
spec.setAlt(body.getAlt());
spec.setCaption(body.getCaption());
spec.setPublic(body.getPublic());
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) {
spec.setViewersToAdd(
body.getViewersToAdd().stream()
builder.viewersToAdd(body.getViewersToAdd().stream()
.map(this.userService::getUser)
.collect(Collectors.toSet())
);
}
if (body.getViewersToRemove() != null) {
spec.setViewersToRemove(
body.getViewersToRemove().stream()
builder.viewersToRemove(body.getViewersToRemove().stream()
.map(this.userService::getUser)
.collect(Collectors.toSet())
);
}
spec.setClearAllViewers(body.getClearAllViewers());
return spec;
return builder.build();
}
@ExceptionHandler
@ -97,19 +95,20 @@ public class ImageController {
if (principal == null) {
throw new AccessDeniedException("Must be logged in.");
}
final ImageCreateInfoSpec createSpec = new ImageCreateInfoSpec();
createSpec.setAlt(alt);
createSpec.setCaption(caption);
createSpec.setPublic(isPublic);
final var specBuilder = ImageCreateSpec.builder()
.alt(alt)
.caption(caption)
.isPublic(isPublic);
if (viewers != null) {
createSpec.setViewersToAdd(viewers.stream().map(this.userService::getUser).collect(Collectors.toSet()));
specBuilder.viewersToAdd(viewers.stream().map(this.userService::getUser).collect(Collectors.toSet()));
}
final Image saved = this.imageService.create(
principal,
filename,
image.getInputStream(),
image.getSize(),
createSpec
specBuilder.build()
);
return ResponseEntity.status(201).body(this.imageService.toImageView(saved, principal));
}
@ -119,7 +118,7 @@ public class ImageController {
@AuthenticationPrincipal User principal,
@PathVariable String username,
@PathVariable String filename,
@RequestBody ImageUpdateInfoBody body
@RequestBody ImageUpdateBody body
) throws ImageException {
if (principal == null) {
throw new AccessDeniedException("Must be logged in.");

View File

@ -8,11 +8,11 @@ import org.springframework.data.jpa.repository.Query;
import java.util.List;
import java.util.Optional;
public interface ImageRepository extends JpaRepository<Image, Long> {
public interface ImageRepository extends JpaRepository<Image, Integer> {
@Query("SELECT image FROM Image image WHERE image.id = ?1")
@EntityGraph(attributePaths = { "viewers" })
Image getByIdWithViewers(long id);
Image getByIdWithViewers(Integer id);
List<Image> findAllByOwner(User owner);
Optional<Image> findByOwnerAndUserFilename(User owner, String filename);

View File

@ -1,7 +1,7 @@
package app.mealsmadeeasy.api.image;
import app.mealsmadeeasy.api.image.spec.ImageCreateInfoSpec;
import app.mealsmadeeasy.api.image.spec.ImageUpdateInfoSpec;
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 org.jetbrains.annotations.Nullable;
@ -12,17 +12,17 @@ import java.util.List;
public interface ImageService {
Image create(User owner, String userFilename, InputStream inputStream, long objectSize, ImageCreateInfoSpec infoSpec)
Image create(User owner, String userFilename, InputStream inputStream, long objectSize, ImageCreateSpec infoSpec)
throws IOException, ImageException;
Image getById(long id, @Nullable User viewer) throws ImageException;
Image getById(Integer id, @Nullable User viewer) throws ImageException;
Image getByOwnerAndFilename(User owner, String filename, User viewer) throws ImageException;
Image getByUsernameAndFilename(String username, String filename, User viewer) throws ImageException;
InputStream getImageContent(Image image, @Nullable User viewer) throws IOException;
List<Image> getImagesOwnedBy(User user);
Image update(Image image, User modifier, ImageUpdateInfoSpec spec);
Image update(Image image, User modifier, ImageUpdateSpec spec);
void deleteImage(Image image, User modifier) throws IOException;

View File

@ -1,7 +1,7 @@
package app.mealsmadeeasy.api.image;
import app.mealsmadeeasy.api.image.spec.ImageCreateInfoSpec;
import app.mealsmadeeasy.api.image.spec.ImageUpdateInfoSpec;
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.s3.S3Manager;
import app.mealsmadeeasy.api.user.User;
@ -78,7 +78,7 @@ public class S3ImageService implements ImageService {
};
}
private boolean transferFromSpec(Image entity, ImageCreateInfoSpec spec) {
private boolean transferFromCreateSpec(Image entity, ImageCreateSpec spec) {
boolean didTransfer = false;
if (spec.getAlt() != null) {
entity.setAlt(spec.getAlt());
@ -88,22 +88,55 @@ public class S3ImageService implements ImageService {
entity.setCaption(spec.getCaption());
didTransfer = true;
}
if (spec.getPublic() != null) {
entity.setIsPublic(spec.getPublic());
if (spec.getIsPublic() != null) {
entity.setIsPublic(spec.getIsPublic());
didTransfer = true;
}
final @Nullable Set<User> viewersToAdd = spec.getViewersToAdd();
if (viewersToAdd != null) {
final Set<User> viewers = new HashSet<>(entity.getViewers());
for (final User viewerToAdd : spec.getViewersToAdd()) {
viewers.add((User) viewerToAdd);
}
viewers.addAll(spec.getViewersToAdd());
entity.setViewers(viewers);
didTransfer = true;
}
return didTransfer;
}
private boolean transferFromUpdateSpec(Image entity, ImageUpdateSpec spec) {
boolean didTransfer = false;
if (spec.getAlt() != null) {
entity.setAlt(spec.getAlt());
didTransfer = true;
}
if (spec.getCaption() != null) {
entity.setCaption(spec.getCaption());
didTransfer = true;
}
if (spec.getIsPublic() != null) {
entity.setIsPublic(spec.getIsPublic());
didTransfer = true;
}
final @Nullable Set<User> viewersToAdd = spec.getViewersToAdd();
if (viewersToAdd != null) {
final Set<User> viewers = new HashSet<>(entity.getViewers());
viewers.addAll(spec.getViewersToAdd());
entity.setViewers(viewers);
didTransfer = true;
}
final @Nullable Set<User> viewersToRemove = spec.getViewersToRemove();
if (viewersToRemove != null) {
final Set<User> viewers = new HashSet<>(entity.getViewers());
viewers.removeAll(spec.getViewersToRemove());
entity.setViewers(viewers);
didTransfer = true;
}
if (spec.getClearAllViewers() != null && spec.getClearAllViewers()) {
entity.setViewers(new HashSet<>());
didTransfer = true;
}
return didTransfer;
}
/**
* @apiNote Consumes and closes the {@link java.io.InputStream}.
*
@ -122,7 +155,7 @@ public class S3ImageService implements ImageService {
String userFilename,
InputStream inputStream,
long objectSize,
ImageCreateInfoSpec createSpec
ImageCreateSpec createSpec
) throws IOException, ImageException {
final String mimeType = this.getMimeType(userFilename);
final String uuid = UUID.randomUUID().toString();
@ -153,19 +186,19 @@ public class S3ImageService implements ImageService {
inputStream.close();
final Image draft = new Image();
draft.setOwner((User) owner);
draft.setOwner(owner);
draft.setUserFilename(userFilename);
draft.setMimeType(mimeType);
draft.setObjectName(objectName);
draft.setHeight(height);
draft.setWidth(width);
this.transferFromSpec(draft, createSpec);
this.transferFromCreateSpec(draft, createSpec);
return this.imageRepository.save(draft);
}
@Override
@PostAuthorize("@imageSecurity.isViewableBy(returnObject, #viewer)")
public Image getById(long id, @Nullable User viewer) throws ImageException {
public Image getById(Integer id, @Nullable User viewer) throws ImageException {
return this.imageRepository.findById(id).orElseThrow(() -> new ImageException(
ImageException.Type.INVALID_ID, "No Image with id: " + id
));
@ -205,36 +238,19 @@ public class S3ImageService implements ImageService {
@Override
@PreAuthorize("@imageSecurity.isOwner(#image, #modifier)")
public Image update(final Image image, User modifier, ImageUpdateInfoSpec updateSpec) {
Image entity = (Image) image;
boolean didUpdate = this.transferFromSpec(entity, updateSpec);
final @Nullable Boolean clearAllViewers = updateSpec.getClearAllViewers();
if (clearAllViewers != null && clearAllViewers) {
entity.setViewers(new HashSet<>());
didUpdate = true;
} else {
final @Nullable Set<User> viewersToRemove = updateSpec.getViewersToRemove();
if (viewersToRemove != null) {
final Set<User> currentViewers = new HashSet<>(entity.getViewers());
for (final User toRemove : updateSpec.getViewersToRemove()) {
currentViewers.remove((User) toRemove);
}
entity.setViewers(currentViewers);
didUpdate = true;
}
}
public Image update(final Image image, User modifier, ImageUpdateSpec updateSpec) {
final boolean didUpdate = this.transferFromUpdateSpec(image, updateSpec);
if (didUpdate) {
entity.setModified(OffsetDateTime.now());
image.setModified(OffsetDateTime.now());
}
return this.imageRepository.save(entity);
return this.imageRepository.save(image);
}
@Override
@PreAuthorize("@imageSecurity.isOwner(#image, #modifier)")
public void deleteImage(Image image, User modifier) throws IOException {
final Image imageEntity = (Image) image;
this.imageRepository.delete(imageEntity);
this.s3Manager.delete("images", imageEntity.getObjectName());
this.imageRepository.delete(image);
this.s3Manager.delete("images", image.getObjectName());
}
private String getImageUrl(Image image) {

View File

@ -0,0 +1,16 @@
package app.mealsmadeeasy.api.image.body;
import lombok.Data;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
@Data
public class ImageUpdateBody {
private @Nullable String alt;
private @Nullable String caption;
private @Nullable Boolean isPublic;
private @Nullable Set<String> viewersToAdd;
private @Nullable Set<String> viewersToRemove;
private @Nullable Boolean clearAllViewers;
}

View File

@ -1,64 +0,0 @@
package app.mealsmadeeasy.api.image.body;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
public class ImageUpdateInfoBody {
private @Nullable String alt;
private @Nullable String caption;
private @Nullable Boolean isPublic;
private @Nullable Set<String> viewersToAdd;
private @Nullable Set<String> viewersToRemove;
private @Nullable Boolean clearAllViewers;
public @Nullable String getAlt() {
return this.alt;
}
public void setAlt(@Nullable String alt) {
this.alt = alt;
}
public @Nullable String getCaption() {
return this.caption;
}
public void setCaption(@Nullable String caption) {
this.caption = caption;
}
public @Nullable Boolean getPublic() {
return this.isPublic;
}
public void setPublic(@Nullable Boolean aPublic) {
isPublic = aPublic;
}
public @Nullable Set<String> getViewersToAdd() {
return this.viewersToAdd;
}
public void setViewersToAdd(@Nullable Set<String> viewersToAdd) {
this.viewersToAdd = viewersToAdd;
}
public @Nullable Set<String> getViewersToRemove() {
return this.viewersToRemove;
}
public void setViewersToRemove(@Nullable Set<String> viewersToRemove) {
this.viewersToRemove = viewersToRemove;
}
public @Nullable Boolean getClearAllViewers() {
return this.clearAllViewers;
}
public void setClearAllViewers(@Nullable Boolean clearAllViewers) {
this.clearAllViewers = clearAllViewers;
}
}

View File

@ -1,48 +0,0 @@
package app.mealsmadeeasy.api.image.spec;
import app.mealsmadeeasy.api.user.User;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.Set;
public class ImageCreateInfoSpec {
private @Nullable String alt;
private @Nullable String caption;
private @Nullable Boolean isPublic;
private @Nullable Set<User> viewersToAdd = new HashSet<>();
public @Nullable String getAlt() {
return this.alt;
}
public void setAlt(@Nullable String alt) {
this.alt = alt;
}
public @Nullable String getCaption() {
return this.caption;
}
public void setCaption(@Nullable String caption) {
this.caption = caption;
}
public @Nullable Boolean getPublic() {
return this.isPublic;
}
public void setPublic(@Nullable Boolean aPublic) {
isPublic = aPublic;
}
public @Nullable Set<User> getViewersToAdd() {
return this.viewersToAdd;
}
public void setViewersToAdd(@Nullable Set<User> viewersToAdd) {
this.viewersToAdd = viewersToAdd;
}
}

View File

@ -0,0 +1,17 @@
package app.mealsmadeeasy.api.image.spec;
import app.mealsmadeeasy.api.user.User;
import lombok.Builder;
import lombok.Value;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
@Value
@Builder
public class ImageCreateSpec {
@Nullable String alt;
@Nullable String caption;
@Nullable Boolean isPublic;
@Nullable Set<User> viewersToAdd;
}

View File

@ -1,29 +0,0 @@
package app.mealsmadeeasy.api.image.spec;
import app.mealsmadeeasy.api.user.User;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
public class ImageUpdateInfoSpec extends ImageCreateInfoSpec {
private @Nullable Set<User> viewersToRemove;
private @Nullable Boolean clearAllViewers;
public @Nullable Set<User> getViewersToRemove() {
return this.viewersToRemove;
}
public void setViewersToRemove(@Nullable Set<User> viewersToRemove) {
this.viewersToRemove = viewersToRemove;
}
public @Nullable Boolean getClearAllViewers() {
return this.clearAllViewers;
}
public void setClearAllViewers(@Nullable Boolean clearAllViewers) {
this.clearAllViewers = clearAllViewers;
}
}

View File

@ -0,0 +1,19 @@
package app.mealsmadeeasy.api.image.spec;
import app.mealsmadeeasy.api.user.User;
import lombok.Builder;
import lombok.Value;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
@Value
@Builder
public class ImageUpdateSpec {
@Nullable String alt;
@Nullable String caption;
@Nullable Boolean isPublic;
@Nullable Set<User> viewersToAdd;
@Nullable Set<User> viewersToRemove;
@Nullable Boolean clearAllViewers;
}

View File

@ -1,6 +0,0 @@
package app.mealsmadeeasy.api.image.view;
public class ImageExceptionView {
}

View File

@ -2,143 +2,52 @@ package app.mealsmadeeasy.api.image.view;
import app.mealsmadeeasy.api.image.Image;
import app.mealsmadeeasy.api.user.view.UserInfoView;
import lombok.Builder;
import lombok.Value;
import org.jetbrains.annotations.Nullable;
import java.time.OffsetDateTime;
import java.util.Set;
import java.util.stream.Collectors;
@Value
@Builder
public class ImageView {
public static ImageView from(Image image, String url, boolean includeViewers) {
final ImageView view = new ImageView();
view.setUrl(url);
view.setCreated(image.getCreated());
view.setModified(image.getModified());
view.setFilename(image.getUserFilename());
view.setMimeType(image.getMimeType());
view.setAlt(image.getAlt());
view.setCaption(image.getCaption());
view.setOwner(UserInfoView.from(image.getOwner()));
view.setIsPublic(image.getIsPublic());
view.setHeight(image.getHeight());
view.setWidth(image.getWidth());
final var builder = ImageView.builder()
.url(url)
.created(image.getCreated())
.modified(image.getModified())
.filename(image.getUserFilename())
.mimeType(image.getMimeType())
.alt(image.getAlt())
.caption(image.getCaption())
.owner(UserInfoView.from(image.getOwner()))
.isPublic(image.getIsPublic())
.height(image.getHeight())
.width(image.getWidth());
if (includeViewers) {
view.setViewers(image.getViewers().stream()
builder.viewers(
image.getViewers().stream()
.map(UserInfoView::from)
.collect(Collectors.toSet())
);
}
return view;
return builder.build();
}
private String url;
private OffsetDateTime created;
private @Nullable OffsetDateTime modified;
private String filename;
private String mimeType;
private @Nullable String alt;
private @Nullable String caption;
private UserInfoView owner;
private boolean isPublic;
private @Nullable Integer height;
private @Nullable Integer width;
private @Nullable Set<UserInfoView> viewers;
public String getUrl() {
return this.url;
}
public void setUrl(String url) {
this.url = url;
}
public OffsetDateTime getCreated() {
return this.created;
}
public void setCreated(OffsetDateTime created) {
this.created = created;
}
public @Nullable OffsetDateTime getModified() {
return this.modified;
}
public void setModified(@Nullable OffsetDateTime modified) {
this.modified = modified;
}
public String getFilename() {
return this.filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public String getMimeType() {
return this.mimeType;
}
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
public @Nullable String getAlt() {
return this.alt;
}
public void setAlt(@Nullable String alt) {
this.alt = alt;
}
public @Nullable String getCaption() {
return this.caption;
}
public void setCaption(@Nullable String caption) {
this.caption = caption;
}
public UserInfoView getOwner() {
return this.owner;
}
public void setOwner(UserInfoView owner) {
this.owner = owner;
}
public boolean getIsPublic() {
return this.isPublic;
}
public void setIsPublic(boolean isPublic) {
this.isPublic = isPublic;
}
public @Nullable Integer getHeight() {
return this.height;
}
public void setHeight(Integer height) {
this.height = height;
}
public @Nullable Integer getWidth() {
return this.width;
}
public void setWidth(Integer width) {
this.width = width;
}
public @Nullable Set<UserInfoView> getViewers() {
return this.viewers;
}
public void setViewers(@Nullable Set<UserInfoView> viewers) {
this.viewers = viewers;
}
String url;
OffsetDateTime created;
@Nullable OffsetDateTime modified;
String filename;
String mimeType;
@Nullable String alt;
@Nullable String caption;
UserInfoView owner;
boolean isPublic;
@Nullable Integer height;
@Nullable Integer width;
@Nullable Set<UserInfoView> viewers;
}