Added addStar to RecipeController and related.

This commit is contained in:
Jesse Brault 2024-08-13 11:13:24 -05:00
parent ccae29b202
commit e23526dbcc
7 changed files with 140 additions and 2 deletions

View File

@ -15,6 +15,7 @@ import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -125,4 +126,21 @@ public class RecipeControllerTests {
.andExpect(jsonPath("$.content", hasSize(3))); .andExpect(jsonPath("$.content", hasSize(3)));
} }
@Test
@DirtiesContext
public void addStarToRecipe() throws Exception {
final User owner = this.createTestUser("recipe-owner");
final User starer = this.createTestUser("recipe-starer");
final Recipe recipe = this.createTestRecipe(owner, true);
final String accessToken = this.authService.login(starer.getUsername(), "test")
.getAccessToken()
.getToken();
this.mockMvc.perform(
post("/recipes/{username}/{slug}/stars", recipe.getOwner().getUsername(), recipe.getSlug())
.header("Authorization", "Bearer " + accessToken)
)
.andExpect(status().isCreated())
.andExpect(jsonPath("$.date").exists());
}
} }

View File

@ -0,0 +1,75 @@
package app.mealsmadeeasy.api.recipe.star;
import app.mealsmadeeasy.api.recipe.Recipe;
import app.mealsmadeeasy.api.recipe.RecipeService;
import app.mealsmadeeasy.api.recipe.spec.RecipeCreateSpec;
import app.mealsmadeeasy.api.user.User;
import app.mealsmadeeasy.api.user.UserCreateException;
import app.mealsmadeeasy.api.user.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
@SpringBootTest
public class RecipeStarServiceTests {
@Autowired
private RecipeStarService recipeStarService;
@Autowired
private UserService userService;
@Autowired
private RecipeService recipeService;
private User getTestUser(String username) {
try {
return this.userService.createUser(username, username + "@test.com", "test");
} catch (UserCreateException e) {
throw new RuntimeException(e);
}
}
private Recipe getTestRecipe(User owner, String slug, boolean isPublic) {
final RecipeCreateSpec spec = new RecipeCreateSpec();
spec.setSlug(slug);
spec.setTitle("Test Recipe");
spec.setRawText("My great recipe has five ingredients.");
spec.setPublic(isPublic);
return this.recipeService.create(owner, spec);
}
@Test
@DirtiesContext
public void createViaUsernameAndSlug() {
final User owner = this.getTestUser("recipe-owner");
final User starer = this.getTestUser("recipe-starer");
final Recipe recipe = this.getTestRecipe(owner, "test-recipe", true);
final RecipeStar star = assertDoesNotThrow(() -> this.recipeStarService.create(
recipe.getOwner().getUsername(),
recipe.getSlug(),
starer
));
assertThat(star.getDate(), is(notNullValue()));
}
@Test
@DirtiesContext
public void createViaId() {
final User owner = this.getTestUser("recipe-owner");
final User starer = this.getTestUser("recipe-starer");
final Recipe recipe = this.getTestRecipe(owner, "test-recipe", true);
final RecipeStar star = assertDoesNotThrow(() -> this.recipeStarService.create(
recipe.getId(),
starer.getUsername()
));
assertThat(star.getDate(), is(notNullValue()));
}
}

View File

@ -1,5 +1,7 @@
package app.mealsmadeeasy.api.recipe; package app.mealsmadeeasy.api.recipe;
import app.mealsmadeeasy.api.recipe.star.RecipeStar;
import app.mealsmadeeasy.api.recipe.star.RecipeStarService;
import app.mealsmadeeasy.api.recipe.view.FullRecipeView; import app.mealsmadeeasy.api.recipe.view.FullRecipeView;
import app.mealsmadeeasy.api.recipe.view.RecipeExceptionView; import app.mealsmadeeasy.api.recipe.view.RecipeExceptionView;
import app.mealsmadeeasy.api.recipe.view.RecipeInfoView; import app.mealsmadeeasy.api.recipe.view.RecipeInfoView;
@ -19,9 +21,11 @@ import java.util.Map;
public class RecipeController { public class RecipeController {
private final RecipeService recipeService; private final RecipeService recipeService;
private final RecipeStarService recipeStarService;
public RecipeController(RecipeService recipeService) { public RecipeController(RecipeService recipeService, RecipeStarService recipeStarService) {
this.recipeService = recipeService; this.recipeService = recipeService;
this.recipeStarService = recipeStarService;
} }
@ExceptionHandler(RecipeException.class) @ExceptionHandler(RecipeException.class)
@ -61,4 +65,13 @@ public class RecipeController {
return ResponseEntity.ok(view); return ResponseEntity.ok(view);
} }
@PostMapping("/{username}/{slug}/stars")
public ResponseEntity<RecipeStar> addStar(
@PathVariable String username,
@PathVariable String slug,
@AuthenticationPrincipal User principal
) throws RecipeException {
return ResponseEntity.status(HttpStatus.CREATED).body(this.recipeStarService.create(username, slug, principal));
}
} }

View File

@ -17,6 +17,8 @@ public interface RecipeService {
Recipe getById(long id, @Nullable User viewer) throws RecipeException; Recipe getById(long id, @Nullable User viewer) throws RecipeException;
Recipe getByIdWithStars(long id, @Nullable User viewer) throws RecipeException; Recipe getByIdWithStars(long id, @Nullable User viewer) throws RecipeException;
Recipe getByUsernameAndSlug(String username, String slug, @Nullable User viewer) throws RecipeException;
FullRecipeView getFullViewById(long id, @Nullable User viewer) throws RecipeException; FullRecipeView getFullViewById(long id, @Nullable User viewer) throws RecipeException;
FullRecipeView getFullViewByUsernameAndSlug(String username, String slug, @Nullable User viewer) throws RecipeException; FullRecipeView getFullViewByUsernameAndSlug(String username, String slug, @Nullable User viewer) throws RecipeException;

View File

@ -85,6 +85,15 @@ public class RecipeServiceImpl implements RecipeService {
)); ));
} }
@Override
@PostAuthorize("@recipeSecurity.isViewableBy(returnObject, #viewer)")
public Recipe getByUsernameAndSlug(String username, String slug, @Nullable User viewer) throws RecipeException {
return this.recipeRepository.findByOwnerUsernameAndSlug(username, slug).orElseThrow(() -> new RecipeException(
RecipeException.Type.INVALID_USERNAME_OR_SLUG,
"No such Recipe for username " + username + " and slug " + slug
));
}
private String getRenderedMarkdown(RecipeEntity entity) { private String getRenderedMarkdown(RecipeEntity entity) {
if (entity.getCachedRenderedText() == null) { if (entity.getCachedRenderedText() == null) {
entity.setCachedRenderedText(renderAndCleanMarkdown(entity.getRawText())); entity.setCachedRenderedText(renderAndCleanMarkdown(entity.getRawText()));

View File

@ -1,9 +1,11 @@
package app.mealsmadeeasy.api.recipe.star; package app.mealsmadeeasy.api.recipe.star;
import app.mealsmadeeasy.api.recipe.RecipeException; import app.mealsmadeeasy.api.recipe.RecipeException;
import app.mealsmadeeasy.api.user.User;
public interface RecipeStarService { public interface RecipeStarService {
RecipeStar create(long recipeId, String ownerUsername); RecipeStar create(long recipeId, String ownerUsername);
RecipeStar create(String recipeOwnerUsername, String recipeSlug, User starer) throws RecipeException;
RecipeStar get(long recipeId, String ownerUsername) throws RecipeException; RecipeStar get(long recipeId, String ownerUsername) throws RecipeException;
void delete(long recipeId, String ownerUsername); void delete(long recipeId, String ownerUsername);
} }

View File

@ -1,17 +1,23 @@
package app.mealsmadeeasy.api.recipe.star; package app.mealsmadeeasy.api.recipe.star;
import app.mealsmadeeasy.api.recipe.Recipe;
import app.mealsmadeeasy.api.recipe.RecipeException; import app.mealsmadeeasy.api.recipe.RecipeException;
import app.mealsmadeeasy.api.recipe.RecipeService;
import app.mealsmadeeasy.api.user.User;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Optional;
@Service @Service
public class RecipeStarServiceImpl implements RecipeStarService { public class RecipeStarServiceImpl implements RecipeStarService {
private final RecipeStarRepository recipeStarRepository; private final RecipeStarRepository recipeStarRepository;
private final RecipeService recipeService;
public RecipeStarServiceImpl(RecipeStarRepository recipeStarRepository) { public RecipeStarServiceImpl(RecipeStarRepository recipeStarRepository, RecipeService recipeService) {
this.recipeStarRepository = recipeStarRepository; this.recipeStarRepository = recipeStarRepository;
this.recipeService = recipeService;
} }
@Override @Override
@ -25,6 +31,19 @@ public class RecipeStarServiceImpl implements RecipeStarService {
return this.recipeStarRepository.save(draft); return this.recipeStarRepository.save(draft);
} }
@Override
public RecipeStar create(String recipeOwnerUsername, String recipeSlug, User starer) throws RecipeException {
final Recipe recipe = this.recipeService.getByUsernameAndSlug(recipeOwnerUsername, recipeSlug, starer);
final Optional<RecipeStarEntity> existing = this.recipeStarRepository.findByRecipeIdAndOwnerUsername(
recipe.getId(),
starer.getUsername()
);
if (existing.isPresent()) {
return existing.get();
}
return this.create(recipe.getId(), starer.getUsername());
}
@Override @Override
public RecipeStar get(long recipeId, String ownerUsername) throws RecipeException { public RecipeStar get(long recipeId, String ownerUsername) throws RecipeException {
return this.recipeStarRepository.findByRecipeIdAndOwnerUsername(recipeId, ownerUsername).orElseThrow( return this.recipeStarRepository.findByRecipeIdAndOwnerUsername(recipeId, ownerUsername).orElseThrow(