Added ImageService.getImageContentById and ImageService.*viewer* methods.

This commit is contained in:
Jesse Brault 2024-07-22 18:21:07 -05:00
parent bb65ec7d24
commit 24db93111f
5 changed files with 140 additions and 13 deletions

View File

@ -41,6 +41,10 @@ public class S3ImageServiceTests {
registry.add("app.mealsmadeeasy.api.minio.secretKey", container::getPassword);
}
private static InputStream getHal9000() {
return S3ImageServiceTests.class.getResourceAsStream("HAL9000.svg");
}
@Autowired
private UserService userService;
@ -61,7 +65,7 @@ public class S3ImageServiceTests {
@Test
@DirtiesContext
public void simpleCreate() {
try (final InputStream hal9000 = S3ImageServiceTests.class.getResourceAsStream("HAL9000.svg")) {
try (final InputStream hal9000 = getHal9000()) {
final User owner = this.createTestUser("imageOwner");
final Image image = this.imageService.create(
owner,
@ -85,4 +89,69 @@ public class S3ImageServiceTests {
}
}
@Test
@DirtiesContext
public void loadImageWithOwner() {
try (final InputStream hal9000 = getHal9000()) {
final User owner = this.createTestUser("imageOwner");
final Image image = this.imageService.create(
owner,
"HAL9000.svg",
hal9000,
"image/svg+xml",
27881L
);
try (final InputStream stored = this.imageService.getImageContentById(image.getId(), owner)) {
final byte[] storedBytes = stored.readAllBytes();
assertThat(storedBytes.length, is(27881));
}
} catch (IOException | ImageException e) {
throw new RuntimeException(e);
}
}
@Test
public void loadPublicImage() {
try (final InputStream hal9000 = getHal9000()) {
final User owner = this.createTestUser("imageOwner");
Image image = this.imageService.create(
owner,
"HAL9000.svg",
hal9000,
"image/svg+xml",
27881L
);
image = this.imageService.setPublic(image, owner, true);
try (final InputStream stored = this.imageService.getImageContentById(image.getId())) {
final byte[] storedBytes = stored.readAllBytes();
assertThat(storedBytes.length, is(27881));
}
} catch (IOException | ImageException e) {
throw new RuntimeException(e);
}
}
@Test
@DirtiesContext
public void loadImageWithViewer() {
try (final InputStream hal9000 = getHal9000()) {
final User owner = this.createTestUser("imageOwner");
final User viewer = this.createTestUser("imageViewer");
Image image = this.imageService.create(
owner,
"HAL9000.svg",
hal9000,
"image/svg+xml",
27881L
);
image = this.imageService.addViewer(image, owner, viewer);
try (final InputStream stored = this.imageService.getImageContentById(image.getId(), viewer)) {
final byte[] storedBytes = stored.readAllBytes();
assertThat(storedBytes.length, is(27881));
}
} catch (IOException | ImageException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -7,3 +7,4 @@ app.mealsmadeeasy.api.security.refresh-token-lifetime=120
app.mealsmadeeasy.api.minio.endpoint=http://localhost:9000
app.mealsmadeeasy.api.minio.accessKey=minio-root
app.mealsmadeeasy.api.minio.secretKey=test0123
app.mealsmadeeasy.api.images.bucketName=images

View File

@ -11,8 +11,11 @@ public interface ImageService {
Image create(User owner, String userFilename, InputStream inputStream, String mimeType, long objectSize)
throws IOException, ImageException;
Image getById(long id);
Image getById(long id, User viewer);
Image getById(long id) throws ImageException;
Image getById(long id, User viewer) throws ImageException;
InputStream getImageContentById(long id) throws IOException, ImageException;
InputStream getImageContentById(long id, User viewer) throws IOException, ImageException;
List<Image> getImagesOwnedBy(User user);
@ -22,6 +25,10 @@ public interface ImageService {
Image setCaption(Image image, User owner, String caption);
Image setPublic(Image image, User owner, boolean isPublic);
Image addViewer(Image image, User owner, User viewer);
Image removeViewer(Image image, User owner, User viewer);
Image clearViewers(Image image, User owner);
void deleteImage(Image image, User owner) throws IOException;
void deleteById(long id, User owner) throws IOException;

View File

@ -3,6 +3,7 @@ package app.mealsmadeeasy.api.image;
import app.mealsmadeeasy.api.s3.S3Manager;
import app.mealsmadeeasy.api.user.User;
import app.mealsmadeeasy.api.user.UserEntity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
@ -17,10 +18,16 @@ public class S3ImageService implements ImageService {
private final S3Manager s3Manager;
private final ImageRepository imageRepository;
private final String imageBucketName;
public S3ImageService(S3Manager s3Manager, ImageRepository imageRepository) {
public S3ImageService(
S3Manager s3Manager,
ImageRepository imageRepository,
@Value("${app.mealsmadeeasy.api.images.bucketName}") String imageBucketName
) {
this.s3Manager = s3Manager;
this.imageRepository = imageRepository;
this.imageBucketName = imageBucketName;
}
private String getExtension(String mimeType) throws ImageException {
@ -39,27 +46,45 @@ public class S3ImageService implements ImageService {
final String uuid = UUID.randomUUID().toString();
final String extension = this.getExtension(mimeType);
final String filename = uuid + "." + extension;
final String objectName = this.s3Manager.store("images", filename, mimeType, inputStream, objectSize);
final String objectName = this.s3Manager.store(
this.imageBucketName, filename, mimeType, inputStream, objectSize
);
final ImageEntity draft = new ImageEntity();
draft.setOwner((UserEntity) owner);
draft.setUserFilename(userFilename);
draft.setMimeType(mimeType);
draft.setObjectName(objectName);
draft.setInternalUrl(this.s3Manager.getUrl("images", objectName));
draft.setInternalUrl(this.s3Manager.getUrl(this.imageBucketName, objectName));
return this.imageRepository.save(draft);
}
@Override
@PostAuthorize("returnObject.isPublic")
public Image getById(long id) {
return this.imageRepository.getReferenceById(id);
public Image getById(long id) throws ImageException {
return this.imageRepository.findById(id).orElseThrow(() -> new ImageException(
ImageException.Type.INVALID_ID, "No such image with id " + id
));
}
@Override
@PostAuthorize("@imageSecurity.isViewableBy(returnObject, #viewer)")
public Image getById(long id, User viewer) {
return this.imageRepository.getReferenceById(id);
public Image getById(long id, User viewer) throws ImageException {
return this.imageRepository.findById(id).orElseThrow(() -> new ImageException(
ImageException.Type.INVALID_ID, "No such image with id " + id
));
}
@Override
public InputStream getImageContentById(long id) throws IOException, ImageException {
final Image image = this.getById(id);
return this.s3Manager.load(this.imageBucketName, image.getObjectName());
}
@Override
public InputStream getImageContentById(long id, User viewer) throws IOException, ImageException {
final Image image = this.getById(id, viewer);
return this.s3Manager.load(this.imageBucketName, image.getObjectName());
}
@Override
@ -83,12 +108,36 @@ public class S3ImageService implements ImageService {
}
@Override
@PreAuthorize("@imageSecurity.isOwner(#image, #owner)")
public Image setPublic(Image image, User owner, boolean isPublic) {
return null;
final ImageEntity imageEntity = (ImageEntity) image;
imageEntity.setPublic(isPublic);
return this.imageRepository.save(imageEntity);
}
@Override
@PreAuthorize("@imageSecurity.isOwner(image, owner)")
public Image addViewer(Image image, User owner, User viewer) {
final ImageEntity withViewers = this.imageRepository.getByIdWithViewers(image.getId());
withViewers.getViewers().add((UserEntity) viewer);
return this.imageRepository.save(withViewers);
}
@Override
public Image removeViewer(Image image, User owner, User viewer) {
final ImageEntity withViewers = this.imageRepository.getByIdWithViewers(image.getId());
withViewers.getViewers().remove((UserEntity) viewer);
return this.imageRepository.save(withViewers);
}
@Override
public Image clearViewers(Image image, User owner) {
final ImageEntity withViewers = this.imageRepository.getByIdWithViewers(image.getId());
withViewers.getViewers().clear();
return this.imageRepository.save(withViewers);
}
@Override
@PreAuthorize("@imageSecurity.isOwner(#image, #owner)")
public void deleteImage(Image image, User owner) throws IOException {
this.imageRepository.delete((ImageEntity) image);
this.s3Manager.delete("images", image.getObjectName()); // TODO

View File

@ -9,3 +9,4 @@ app.mealsmadeeasy.api.security.refresh-token-lifetime=120
app.mealsmadeeasy.api.minio.endpoint=http://localhost:9000
app.mealsmadeeasy.api.minio.accessKey=minio-root
app.mealsmadeeasy.api.minio.secretKey=test0123
app.mealsmadeeasy.api.images.bucketName=images