MME-13 Add delete recipe endpoint and tests.
This commit is contained in:
parent
2fbb9ee51e
commit
d2beb2af0f
@ -362,6 +362,33 @@ public class RecipeServiceTests {
|
||||
assertThrows(AccessDeniedException.class, () -> this.recipeService.deleteRecipe(toDelete.getId(), notOwner));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteRecipeByUsernameAndSlug() {
|
||||
final User owner = this.seedUser();
|
||||
final Recipe toDelete = this.createTestRecipe(owner);
|
||||
this.recipeService.deleteRecipe(owner.getUsername(), toDelete.getSlug(), owner);
|
||||
assertThrows(NoSuchEntityWithIdException.class, () -> this.recipeService.getById(toDelete.getId(), owner));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteRecipeByUsernameAndSlug_throwsIfNullDeleter() {
|
||||
final User owner = this.seedUser();
|
||||
final Recipe recipe = this.createTestRecipe(owner);
|
||||
assertThrows(AccessDeniedException.class, () -> this.recipeService.deleteRecipe(
|
||||
owner.getUsername(), recipe.getSlug(), null
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteRecipeByUsernameAndSlug_throwsIfNotOwner() {
|
||||
final User owner = this.seedUser();
|
||||
final User wrong = this.seedUser();
|
||||
final Recipe recipe = this.createTestRecipe(owner);
|
||||
assertThrows(AccessDeniedException.class, () -> this.recipeService.deleteRecipe(
|
||||
owner.getUsername(), recipe.getSlug(), wrong
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDraftReturnsDefaultRecipeDraft() {
|
||||
final User owner = this.seedUser();
|
||||
|
||||
@ -328,4 +328,59 @@ public class RecipesControllerTests {
|
||||
.andExpect(status().isNoContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteRecipeByOwner_returnsNoContent() throws Exception {
|
||||
final User owner = this.seedUser();
|
||||
final Recipe recipe = this.createTestRecipe(owner, false);
|
||||
this.mockMvc.perform(
|
||||
delete("/recipes/{username}/{slug}", recipe.getOwner().getUsername(), recipe.getSlug())
|
||||
.header("Authorization", "Bearer " + this.getAccessToken(owner))
|
||||
)
|
||||
.andExpect(status().isNoContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deletePrivateRecipe_noAuth_returnsUnauthorized() throws Exception {
|
||||
final User owner = this.seedUser();
|
||||
final Recipe recipe = this.createTestRecipe(owner, false);
|
||||
this.mockMvc.perform(
|
||||
delete("/recipes/{username}/{slug}", recipe.getOwner().getUsername(), recipe.getSlug())
|
||||
)
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deletePublicRecipe_noAuth_returnsUnauthorized() throws Exception {
|
||||
final User owner = this.seedUser();
|
||||
final Recipe recipe = this.createTestRecipe(owner, true);
|
||||
this.mockMvc.perform(
|
||||
delete("/recipes/{username}/{slug}", recipe.getOwner().getUsername(), recipe.getSlug())
|
||||
)
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deletePublicRecipe_wrongAuth_returnsForbidden() throws Exception {
|
||||
final User owner = this.seedUser();
|
||||
final User wrong = this.seedUser();
|
||||
final Recipe recipe = this.createTestRecipe(owner, true);
|
||||
this.mockMvc.perform(
|
||||
delete("/recipes/{username}/{slug}", recipe.getOwner().getUsername(), recipe.getSlug())
|
||||
.header("Authorization", "Bearer " + this.getAccessToken(wrong))
|
||||
)
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deletePrivateRecipe_wrongAuth_returnsForbidden() throws Exception {
|
||||
final User owner = this.seedUser();
|
||||
final User wrong = this.seedUser();
|
||||
final Recipe recipe = this.createTestRecipe(owner, false);
|
||||
this.mockMvc.perform(
|
||||
delete("/recipes/{username}/{slug}", recipe.getOwner().getUsername(), recipe.getSlug())
|
||||
.header("Authorization", "Bearer " + this.getAccessToken(wrong))
|
||||
)
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -74,4 +74,6 @@ public interface RecipeRepository extends JpaRepository<Recipe, Integer> {
|
||||
@Query("SELECT count(r) FROM Recipe r WHERE ?1 MEMBER OF r.viewers OR r.isPublic IS TRUE")
|
||||
int countViewableBy(User viewer);
|
||||
|
||||
void deleteRecipeByOwnerUsernameAndSlug(String username, String slug);
|
||||
|
||||
}
|
||||
|
||||
@ -17,11 +17,12 @@ public class RecipeSecurity {
|
||||
this.recipeRepository = recipeRepository;
|
||||
}
|
||||
|
||||
public boolean isOwner(Recipe recipe, User user) {
|
||||
public boolean isOwner(Recipe recipe, @Nullable User user) {
|
||||
if (user == null) return false;
|
||||
return recipe.getOwner() != null && recipe.getOwner().getId().equals(user.getId());
|
||||
}
|
||||
|
||||
public boolean isOwner(Integer recipeId, User user) {
|
||||
public boolean isOwner(Integer recipeId, @Nullable User user) {
|
||||
final Recipe recipe = this.recipeRepository.findById(recipeId).orElseThrow(
|
||||
() -> new NoSuchEntityWithIdException(Recipe.class, recipeId)
|
||||
);
|
||||
|
||||
@ -219,6 +219,12 @@ public class RecipeService {
|
||||
this.recipeRepository.deleteById(id);
|
||||
}
|
||||
|
||||
@PreAuthorize("@recipeSecurity.isOwner(#username, #slug, #deleter)")
|
||||
@Transactional
|
||||
public void deleteRecipe(String username, String slug, User deleter) {
|
||||
this.recipeRepository.deleteRecipeByOwnerUsernameAndSlug(username, slug);
|
||||
}
|
||||
|
||||
@PreAuthorize("@recipeSecurity.isViewableBy(#username, #slug, #viewer)")
|
||||
@Contract("_, _, null -> null")
|
||||
public @Nullable Boolean isStarer(String username, String slug, @Nullable User viewer) {
|
||||
|
||||
@ -118,6 +118,16 @@ public class RecipesController {
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{username}/{slug}")
|
||||
public ResponseEntity<Void> deleteRecipe(
|
||||
@AuthenticationPrincipal User principal,
|
||||
@PathVariable String username,
|
||||
@PathVariable String slug
|
||||
) {
|
||||
this.recipeService.deleteRecipe(username, slug, principal);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@PostMapping("/{username}/{slug}/star")
|
||||
public ResponseEntity<RecipeStar> addStar(
|
||||
@PathVariable String username,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user