Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include photo on copy #95

Merged
merged 4 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/main/java/com/brennaswitzer/cookbook/domain/Photo.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ public class Photo {

@Embedded
private S3File file;
public void clearFile() {
file = null;
}
public boolean hasFile() {
return file != null;
}
Expand Down Expand Up @@ -50,6 +47,10 @@ public String getObjectKey() {
return file.getObjectKey();
}

public String getFilename() {
return file.getFilename();
}

public String getContentType() {
return file.getContentType();
}
Expand All @@ -58,4 +59,10 @@ public Long getSize() {
return file.getSize();
}

public void clear() {
file = null;
focusLeft = null;
focusTop = null;
}

}
5 changes: 2 additions & 3 deletions src/main/java/com/brennaswitzer/cookbook/domain/Recipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderBy;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import lombok.Getter;
import lombok.Setter;
Expand Down Expand Up @@ -82,7 +81,7 @@ public void setPhoto(S3File file) {
}
public void clearPhoto() {
if (hasPhoto()) {
photo.clearFile();
photo.clear();
}
}
public boolean hasPhoto() {
Expand Down Expand Up @@ -129,7 +128,7 @@ public void addRawIngredient(String raw) {
ingredients.add(new IngredientRef(raw));
}

@PrePersist
@Override
protected void onPrePersist() {
super.onPrePersist();
ensureRefOrder();
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/brennaswitzer/cookbook/domain/S3File.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ private static String lastSegment(String string, char delim) {

private Long size; // needs to be nullable for historical data

public String getFilename() {
return lastSegment(objectKey, '/');
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ public Recipe createRecipe(IngredientInfo info, Part photo, boolean cookThis) {
return recipe;
}

public Recipe createRecipeFrom(Long sourceRecipeId, IngredientInfo info, Part photo) {
Recipe recipe = info.asRecipe(entityManager);
recipe = recipeService.createNewRecipeFrom(sourceRecipeId, recipe, Upload.of(photo));
labelService.updateLabels(recipe, info.getLabels());
return recipe;
}

public Recipe updateRecipe(Long id, IngredientInfo info, Part photo) {
info.setId(id);
Recipe recipe = info.asRecipe(entityManager);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ public String store(Upload upload, String filename) {
return "images/pork_chops.jpg";
}

@Override
public String copy(String source, String dest) {
Assert.notNull(source, "source is required.");
Assert.notNull(dest, "dest is required.");
return "images/pork_chops.jpg";
}

@Override
public String load(String filename) {
Assert.notNull(filename, "Filename is required");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.brennaswitzer.cookbook.services;

import com.brennaswitzer.cookbook.domain.Photo;
import com.brennaswitzer.cookbook.domain.Recipe;
import com.brennaswitzer.cookbook.domain.S3File;
import com.brennaswitzer.cookbook.domain.Upload;
Expand Down Expand Up @@ -47,6 +48,18 @@ public Recipe createNewRecipe(Recipe recipe, Upload photo) {
return recipe;
}

public Recipe createNewRecipeFrom(Long sourceRecipeId, Recipe recipe, Upload photo) {
var source = recipeRepository.getReferenceById(sourceRecipeId);
recipe.setOwner(principalAccess.getUser());
recipe = recipeRepository.save(recipe);
if (photo != null) {
setPhotoInternal(recipe, photo);
} else if (source.hasPhoto()) {
copyPhoto(source, recipe);
}
return recipe;
}

public Recipe updateRecipe(Recipe recipe) {
return this.updateRecipe(recipe, null);
}
Expand Down Expand Up @@ -92,12 +105,31 @@ private void setPhotoInternal(Recipe recipe, Upload photo) {
} else {
name = S3File.sanitizeFilename(name);
}
String objectKey = "recipe/" + recipe.getId() + "/" + name;
recipe.setPhoto(new S3File(
storageService.store(photo, objectKey),
storageService.store(photo,
buildObjectKey(recipe, name)),
photo.getContentType(),
photo.getSize()
));
}

private void copyPhoto(Recipe source, Recipe dest) {
removePhotoInternal(dest);
if (!source.hasPhoto()) return; // silly caller
Photo photo = source.getPhoto();
dest.setPhoto(new S3File(
storageService.copy(photo.getObjectKey(),
buildObjectKey(dest, photo.getFilename())),
photo.getContentType(),
photo.getSize()
));
if (photo.hasFocus()) {
dest.getPhoto().setFocusArray(photo.getFocusArray());
}
}

private String buildObjectKey(Recipe recipe, String name) {
return "recipe/" + recipe.getId() + "/" + name;
}

private void removePhotoInternal(Recipe recipe) {
Expand All @@ -106,10 +138,6 @@ private void removePhotoInternal(Recipe recipe) {
recipe.clearPhoto();
}

public void sendToPlan(Long recipeId, Long planId) {
sendToPlan(recipeId, planId, 1d);
}

@SuppressWarnings("OptionalGetWithoutIsPresent")
public void sendToPlan(Long recipeId, Long planId, Double scale) {
planService.addRecipe(planId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ public String store(Upload upload, String objectKey) {
return objectKey;
}

@Override
public String copy(String source, String dest) {
Assert.notNull(source, "source is required.");
Assert.notNull(dest, "dest is required.");
client.copyObject(bucketName, source, bucketName, dest);
return dest;
}

@Override
public String load(String objectKey) {
Assert.notNull(objectKey, "objectKey is required");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,22 @@
public interface StorageService {

/**
* I take a Upload object and a string filename/key and write to storage
* I take an Upload object and a string filename/key and write to storage
* @param filename String filename for storage
* @return reference to the stored file
*/
String store(Upload upload, String filename);

/**
* I take two string filename/keys and copy the content of the first to the
* second. A complete copy is made, no backreferences are created.
*
* @param source String filename to copy from
* @param dest String filename to copy to
* @return reference to the stored (destination) file
*/
String copy(String source, String dest);

/**
* I return a fully-qualified URL for the passed file reference.
* @param ref A reference to a stored file.
Expand Down
13 changes: 13 additions & 0 deletions src/main/resources/graphqls/library.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,22 @@ extend type Mutation {
}

type LibraryMutation {
"""Create a new recipe in your library, from the passed info.
"""
createRecipe(info: IngredientInfo!, photo: Upload, cookThis: Boolean): Recipe!
"""Create a new recipe in your library, from the passed info, which is based
on the passed source recipe id.
"""
createRecipeFrom(sourceRecipeId: ID!, info: IngredientInfo!, photo: Upload): Recipe!
"""Update a recipe in your library, from the passed info.
"""
updateRecipe(id: ID!, info: IngredientInfo!, photo: Upload): Recipe!
"""Set the photo for a recipe in your library, without changing any other
info about the recipe. A photo may be set during create and/or update.
"""
setRecipePhoto(id: ID!, photo: Upload!): Recipe!
"""Delete a recipe from your library.
"""
deleteRecipe(id: ID!): Deletion!
history(recipeId: ID!): RecipeHistoryMutation
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,24 @@ void createRecipe_cookThis() {
verify(labelService).updateLabels(same(recipe), same(labels));
}

@Test
void createRecipeFrom() {
var recipe = mock(Recipe.class);
List<String> labels = new ArrayList<>();
var info = mock(IngredientInfo.class);
when(info.getLabels()).thenReturn(labels);
when(info.asRecipe(any())).thenReturn(recipe);
when(recipeService.createNewRecipeFrom(any(), any(), any()))
.thenAnswer(iom -> iom.getArgument(1));

var result = mutation.createRecipeFrom(123L, info, null);

assertSame(recipe, result);
verify(info).asRecipe(entityManager);
verify(recipeService).createNewRecipeFrom(eq(123L), same(recipe), isNull());
verify(labelService).updateLabels(same(recipe), same(labels));
}

@Test
void updateRecipe() {
var recipe = mock(Recipe.class);
Expand Down
Loading