Skip to content

Commit

Permalink
Merge pull request #59 from folded-ear/graphql-delete
Browse files Browse the repository at this point in the history
new Deletion type for graphql deletions, so apollo can get the ID
  • Loading branch information
barneyb authored May 12, 2024
2 parents 8c958d9 + 75f6ca9 commit aaf86c6
Show file tree
Hide file tree
Showing 22 changed files with 114 additions and 87 deletions.
8 changes: 3 additions & 5 deletions src/main/java/com/brennaswitzer/cookbook/domain/Acl.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MapKeyJoinColumn;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;

import java.util.Collections;
import java.util.HashMap;
Expand All @@ -17,6 +18,7 @@
@Embeddable
public class Acl {

@Getter
@NotNull
@ManyToOne
private User owner;
Expand All @@ -26,10 +28,6 @@ public class Acl {
@Column(name = "level_id")
private Map<User, AccessLevel> grants;

public User getOwner() {
return owner;
}

public void setOwner(User owner) {
this.owner = owner;
// clear any explicit grant the new owner previously had
Expand Down Expand Up @@ -63,7 +61,7 @@ public AccessLevel setGrant(User user, AccessLevel level) {
return grants.put(user, level);
}

public AccessLevel deleteGrant(User user) {
public AccessLevel revokeGrant(User user) {
if (user == null) throw new IllegalArgumentException("You can't revoke access from the null user.");
if (user.equals(owner)) throw new UnsupportedOperationException();
if (grants == null) return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
@Table(name = "plan_bucket", uniqueConstraints = {
@UniqueConstraint(columnNames = { "plan_id", "name" })
})
public class PlanBucket extends BaseEntity {
public class PlanBucket extends BaseEntity implements Named {

@NotNull
@Getter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.brennaswitzer.cookbook.domain.Recipe;
import com.brennaswitzer.cookbook.domain.Upload;
import com.brennaswitzer.cookbook.graphql.model.Deletion;
import com.brennaswitzer.cookbook.payload.IngredientInfo;
import com.brennaswitzer.cookbook.services.ItemService;
import com.brennaswitzer.cookbook.services.LabelService;
Expand Down Expand Up @@ -45,9 +46,8 @@ public Recipe setRecipePhoto(Long id, Part photo) {
return recipeService.setRecipePhoto(id, Upload.of(photo));
}

public boolean deleteRecipe(Long id) {
recipeService.deleteRecipeById(id);
return true;
public Deletion deleteRecipe(Long id) {
return Deletion.of(recipeService.deleteRecipeById(id));
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.brennaswitzer.cookbook.graphql;

import com.brennaswitzer.cookbook.domain.PantryItem;
import com.brennaswitzer.cookbook.graphql.model.Deletion;
import com.brennaswitzer.cookbook.services.PantryItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
Expand Down Expand Up @@ -54,8 +55,8 @@ public PantryItem combineItems(List<Long> ids) {
return pantryItemService.combineItems(ids);
}

public boolean deleteItem(Long id) {
return pantryItemService.deleteItem(id);
public Deletion deleteItem(Long id) {
return Deletion.of(pantryItemService.deleteItem(id));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.brennaswitzer.cookbook.domain.PlanBucket;
import com.brennaswitzer.cookbook.domain.PlanItem;
import com.brennaswitzer.cookbook.domain.PlanItemStatus;
import com.brennaswitzer.cookbook.graphql.model.Deletion;
import com.brennaswitzer.cookbook.services.PlanService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
Expand Down Expand Up @@ -34,17 +35,16 @@ public Plan createPlan(String name) {
return planService.createPlan(name);
}

public Plan deleteBucket(Long planId, Long bucketId) {
return planService.deleteBucket(planId, bucketId).getPlan();
public Deletion deleteBucket(Long planId, Long bucketId) {
return Deletion.of(planService.deleteBucket(planId, bucketId));
}

public PlanItem deleteItem(Long id) {
return planService.deleteItemForParent(id);
public Deletion deleteItem(Long id) {
return Deletion.of(planService.deleteItem(id));
}

public boolean deletePlan(Long id) {
planService.deletePlan(id);
return true;
public Deletion deletePlan(Long id) {
return Deletion.of(planService.deletePlan(id));
}

public Plan duplicatePlan(String name, Long sourcePlanId) {
Expand Down Expand Up @@ -75,8 +75,8 @@ public PlanItem setStatus(Long id, PlanItemStatus status) {
return planService.setItemStatus(id, status);
}

public Plan deleteGrant(Long planId, Long userId) {
return planService.deleteGrantFromPlan(planId, userId);
public Plan revokeGrant(Long planId, Long userId) {
return planService.revokeGrantFromPlan(planId, userId);
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.brennaswitzer.cookbook.graphql;

import com.brennaswitzer.cookbook.domain.Timer;
import com.brennaswitzer.cookbook.graphql.model.Deletion;
import com.brennaswitzer.cookbook.services.timers.UpdateTimers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
Expand Down Expand Up @@ -28,8 +29,9 @@ public Timer addTime(Long id, int duration) {
return update.addTime(id, duration);
}

public boolean delete(Long id) {
return update.deleteTimer(id);
public Deletion delete(Long id) {
return new Deletion(update.deleteTimer(id).getId(),
"Unnamed Timer");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.brennaswitzer.cookbook.graphql.model;

import com.brennaswitzer.cookbook.domain.Identified;
import com.brennaswitzer.cookbook.domain.Named;
import lombok.Value;

@Value
public class Deletion {

Long id;
String name;

public static <T extends Identified & Named> Deletion of(T it) {
return new Deletion(it.getId(), it.getName());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@

import com.brennaswitzer.cookbook.domain.PantryItem;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;

import java.time.Instant;
import java.util.List;

public interface PantryItemRepository extends CrudRepository<PantryItem, Long>, PantryItemSearchRepository {
public interface PantryItemRepository extends BaseEntityRepository<PantryItem>, PantryItemSearchRepository {

@Query("""
select item
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,10 @@ public PantryItem combineItems(List<Long> ids) {
}

@PreAuthorize("hasRole('DEVELOPER')")
public boolean deleteItem(Long id) {
public PantryItem deleteItem(Long id) {
var it = pantryItemRepository.getReferenceById(id);
pantryItemRepository.deleteById(id);
return true;
return it;
}

private void needsDupesFound(PantryItem item) {
Expand Down
19 changes: 6 additions & 13 deletions src/main/java/com/brennaswitzer/cookbook/services/PlanService.java
Original file line number Diff line number Diff line change
Expand Up @@ -426,20 +426,14 @@ public PlanMessage setItemStatusForMessage(Long id, PlanItemStatus status) {
return buildUpdateMessage(item);
}

public PlanItem deleteItemForParent(Long id) {
val item = setItemStatus(id, PlanItemStatus.DELETED);
if (item.hasParent()) {
return item.getParent();
} else {
throw new IllegalArgumentException(String.format(
"ID '%s' is a plan",
id));
}
public PlanItem deleteItem(Long id) {
return setItemStatus(id, PlanItemStatus.DELETED);
}

public void deletePlan(Long id) {
public Plan deletePlan(Long id) {
val plan = getPlanById(id, AccessLevel.ADMINISTER);
planRepo.delete(plan);
return plan;
}

public void severLibraryLinks(Recipe r) {
Expand All @@ -455,10 +449,9 @@ public Plan setGrantOnPlan(Long planId, Long userId, AccessLevel level) {
return plan;
}

@SuppressWarnings("UnusedReturnValue")
public Plan deleteGrantFromPlan(Long planId, Long userId) {
public Plan revokeGrantFromPlan(Long planId, Long userId) {
Plan plan = getPlanById(planId, AccessLevel.ADMINISTER);
plan.getAcl().deleteGrant(userRepo.getById(userId));
plan.getAcl().revokeGrant(userRepo.getById(userId));
return plan;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,12 @@ public Optional<Recipe> findRecipeById(Long id) {
return recipeRepository.findById(id);
}

public void deleteRecipeById(Long id) {
public Recipe deleteRecipeById(Long id) {
Recipe recipe = getMyRecipe(id);
removePhotoInternal(recipe);
planService.severLibraryLinks(recipe);
recipeRepository.delete(recipe);
return recipe;
}

private void setPhotoInternal(Recipe recipe, Upload photo) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,11 @@ public Timer addTime(Long id, int duration) {
return t;
}

public boolean deleteTimer(Long id) {
val optionalTimer = repo.findById(id);
if (optionalTimer.isEmpty()) {
return false;
}
val t = optionalTimer.get();
public Timer deleteTimer(Long id) {
val t = repo.getReferenceById(id);
t.ensurePermitted(principalAccess.getUser(), AccessLevel.CHANGE);
repo.delete(t);
return true;
return t;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ public void deleteItem(
if (!planId.equals(item.getPlan().getId())) {
throw new IllegalArgumentException("Item belongs to a different plan");
}
planService.deleteItemForParent(id);
planService.deleteItem(id);
}

@PostMapping("/{id}/buckets")
Expand Down Expand Up @@ -249,11 +249,11 @@ public GrantInfo addGrant(

@DeleteMapping("/{id}/acl/grants/{userId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteGrant(
public void revokeGrant(
@PathVariable("id") Long id,
@PathVariable("userId") Long userId
) {
planService.deleteGrantFromPlan(id, userId);
planService.revokeGrantFromPlan(id, userId);
}

}
5 changes: 5 additions & 0 deletions src/main/resources/graphqls/__master.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,8 @@ type ShareInfo {
slug: String!
secret: String!
}

type Deletion {
id: ID!
name: String
}
2 changes: 1 addition & 1 deletion src/main/resources/graphqls/library.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ type LibraryMutation {
createRecipe(info: IngredientInfo!, photo: Upload, cookThis: Boolean): Recipe!
updateRecipe(id: ID!, info: IngredientInfo!, photo: Upload): Recipe!
setRecipePhoto(id: ID!, photo: Upload!): Recipe!
deleteRecipe(id: ID!): Boolean!
deleteRecipe(id: ID!): Deletion!
}

input IngredientInfo {
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/graphqls/pantry.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,5 @@ type PantryMutation {
combineItems(ids: [ID!]!): PantryItem
"""Delete a pantry item, which MUST be unreferenced.
"""
deleteItem(id: ID!): Boolean!
deleteItem(id: ID!): Deletion!
}
13 changes: 6 additions & 7 deletions src/main/resources/graphqls/planner.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,13 @@ type PlannerMutation {
"""Create a new plan by duplicating the specified source plan."""
duplicatePlan(name: String!, sourcePlanId: ID!): Plan!
"""Delete a bucket from a plan."""
deleteBucket(planId: ID!, bucketId: ID!): Plan!
"""Deletes the grant for a user w/in a plan, if one exists."""
deleteGrant(planId: ID!, userId: ID!): Plan!
"""Deletes an item from a plan, and return its former parent (plan or item).
This operation cascades."""
deleteItem(id: ID!): PlanItem
deleteBucket(planId: ID!, bucketId: ID!): Deletion!
"""Revokes the grant for a user w/in a plan, if one exists."""
revokeGrant(planId: ID!, userId: ID!): Plan!
"""Deletes an item from a plan. This operation cascades."""
deleteItem(id: ID!): Deletion!
"""Deletes the given plan, and all its related data."""
deletePlan(id: ID!): Boolean!
deletePlan(id: ID!): Deletion!
"""Move the given items under the given parent, in order, optionally after a
specific item already under that parent. The parent's info is returned."""
mutateTree(itemIds: [ID!]!, parentId: ID!, afterId: ID): PlanItem!
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/graphqls/timers.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type TimerMutation {
"""Ensure the specified timer has been deleted, regardless of its status or
existence, returning whether any action was taken.
"""
delete(id: ID!): Boolean!
delete(id: ID!): Deletion!
}

"""Represents a pause-able timer of user-specified length.
Expand Down
9 changes: 6 additions & 3 deletions src/test/java/com/brennaswitzer/cookbook/domain/AclTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class AclTest {

Expand All @@ -28,7 +31,7 @@ public void grants() {
assertEquals(AccessLevel.VIEW, acl.getGrant(bob));
assertNull(acl.getGrant(eve));

acl.deleteGrant(bob);
assertEquals(AccessLevel.VIEW, acl.revokeGrant(bob));
assertNull(acl.getGrant(bob));
}

Expand Down Expand Up @@ -65,7 +68,7 @@ public void cantRemoveOwnerGrants() {
acl.setOwner(alice);

assertThrows(UnsupportedOperationException.class, () ->
acl.deleteGrant(alice));
acl.revokeGrant(alice));
}

@Test
Expand Down
Loading

0 comments on commit aaf86c6

Please sign in to comment.