Skip to content

Commit

Permalink
Merge pull request #108 from folded-ear/plan-color
Browse files Browse the repository at this point in the history
support a String-typed color property on Plan
  • Loading branch information
barneyb authored Oct 5, 2024
2 parents 1e5a9d5 + 0078f49 commit d319a78
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 71 deletions.
33 changes: 33 additions & 0 deletions src/main/java/com/brennaswitzer/cookbook/domain/Plan.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.BatchSize;
import org.springframework.util.StringUtils;

import java.util.HashSet;
import java.util.Set;
Expand All @@ -17,6 +18,25 @@
@DiscriminatorValue("plan")
public class Plan extends PlanItem implements AccessControlled {

// generated at http://medialab.github.io/iwanthue/
private static final String[] COLORS = {
"#cb4771",
"#caa29e",
"#783b32",
"#d14f32",
"#c9954c",
"#cbd152",
"#56713c",
"#6dce55",
"#8dd4aa",
"#77adc2",
"#6a7dc8",
"#3b3a41",
"#7145ca",
"#552b6b",
"#c583bd",
"#cc4ac0" };

@Embedded
@NotNull
@Getter
Expand All @@ -31,6 +51,8 @@ public class Plan extends PlanItem implements AccessControlled {
@BatchSize(size = 50)
private Set<PlanItem> trashBinItems;

private String color;

public Plan() {
}

Expand Down Expand Up @@ -83,4 +105,15 @@ public User getOwner() {
return getAcl().getOwner();
}

public String getColor() {
if (color == null) {
color = COLORS[(int) (get_eqkey() % COLORS.length)];
}
return color;
}

public void setColor(String color) {
this.color = StringUtils.hasText(color) ? color : null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ public PlanBucket updateBucket(Long planId, Long bucketId, String name, LocalDat
return planService.updateBucket(planId, bucketId, name, date);
}

public Plan setColor(Long planId, String color) {
return planService.setColor(planId, color);
}

public Plan setGrant(Long planId, Long userId, AccessLevel accessLevel) {
return planService.setGrantOnPlan(planId, userId, accessLevel);
}
Expand Down
78 changes: 20 additions & 58 deletions src/main/java/com/brennaswitzer/cookbook/payload/PlanItemInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.Hibernate;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import static com.brennaswitzer.cookbook.util.IdUtils.toIdList;

@Setter
@Getter
@SuppressWarnings("WeakerAccess")
public class PlanItemInfo {

public static PlanItemInfo fromPlanItem(PlanItem item) {
public static PlanItemInfo from(PlanItem item) {
item = (PlanItem) Hibernate.unproxy(item);
PlanItemInfo info = new PlanItemInfo();
info.id = item.getId();
info.name = item.getName();
Expand Down Expand Up @@ -50,102 +54,60 @@ public static PlanItemInfo fromPlanItem(PlanItem item) {
if (item.hasBucket()) {
info.bucketId = item.getBucket().getId();
}
return info;
}

public static PlanItemInfo fromPlan(Plan plan) {
PlanItemInfo info = fromPlanItem(plan);
info.acl = AclInfo.fromAcl(plan.getAcl());
if (plan.hasBuckets()) {
info.buckets = plan.getBuckets().stream()
.map(PlanBucketInfo::from)
.collect(Collectors.toList());
if (item instanceof Plan plan) {
info.acl = AclInfo.fromAcl(plan.getAcl());
info.color = plan.getColor();
if (plan.hasBuckets()) {
info.buckets = plan.getBuckets().stream()
.map(PlanBucketInfo::from)
.collect(Collectors.toList());
}
}
return info;
}

public static List<PlanItemInfo> fromPlanItems(Iterable<PlanItem> items) {
return StreamSupport.stream(items.spliterator(), false)
.map(PlanItemInfo::fromPlanItem)
.collect(Collectors.toList());
public static List<PlanItemInfo> from(Iterable<? extends PlanItem> items) {
List<PlanItemInfo> result = new ArrayList<>();
for (var it : items) result.add(from(it));
return result;
}

public static List<PlanItemInfo> fromPlans(Iterable<Plan> plans) {
return StreamSupport.stream(plans.spliterator(), false)
.map(PlanItemInfo::fromPlan)
.collect(Collectors.toList());
}

@Getter
@Setter
private Long id;

@Getter
@Setter
private String name;

@Getter
@Setter
private String notes;

@Getter
@Setter
private PlanItemStatus status;

@Getter
@Setter
private Long parentId;

@Getter
@Setter
private Long aggregateId;

@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private AclInfo acl;

@Getter
@Setter
private String color;

@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<PlanBucketInfo> buckets;

@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private long[] subtaskIds;

@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private long[] componentIds;

@Getter
@Setter
private Double quantity;

@Getter
@Setter
private String units;

@Getter
@Setter
private Long uomId;

@Getter
@Setter
private Long ingredientId;

@Getter
@Setter
private Long bucketId;

@Getter
@Setter
private String preparation;

public boolean hasSubtasks() {
return subtaskIds != null && subtaskIds.length > 0;
}

}
27 changes: 21 additions & 6 deletions src/main/java/com/brennaswitzer/cookbook/services/PlanService.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.Instant;
import java.time.LocalDate;
Expand All @@ -39,12 +40,15 @@
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.regex.Pattern;

@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
@Service
@Transactional
public class PlanService {

private static final Pattern RE_COLOR = Pattern.compile("#[0-9a-fA-F]{6}");

@Autowired
protected PlanItemRepository itemRepo;

Expand Down Expand Up @@ -78,7 +82,7 @@ public Iterable<Plan> getPlans() {
}

public Iterable<Plan> getPlans(Long userId) {
User user = userRepo.getById(userId);
User user = userRepo.getReferenceById(userId);
List<Plan> result = new LinkedList<>();
planRepo.findAccessibleLists(userId)
.forEach(l -> {
Expand Down Expand Up @@ -240,7 +244,7 @@ private PlanMessage buildCreationMessage(PlanItem item) {
m.setId(parent.getId());
m.setType("create");
List<PlanItem> tree = getTreeById(parent);
m.setInfo(PlanItemInfo.fromPlanItems(tree));
m.setInfo(PlanItemInfo.from(tree));
return m;
}

Expand Down Expand Up @@ -290,7 +294,7 @@ public Plan createPlan(String name) {
}

public Plan createPlan(String name, Long ownerId) {
User user = userRepo.getById(ownerId);
User user = userRepo.getReferenceById(ownerId);
Plan plan = new Plan(name);
plan.setOwner(user);
plan.setPosition(1 + planRepo.getMaxPosition(user));
Expand Down Expand Up @@ -414,7 +418,7 @@ private PlanMessage buildUpdateMessage(PlanItem item) {
PlanMessage m = new PlanMessage();
m.setId(item.getId());
m.setType("update");
m.setInfo(PlanItemInfo.fromPlanItem(item));
m.setInfo(PlanItemInfo.from(item));
return m;
}

Expand Down Expand Up @@ -504,13 +508,24 @@ public void severLibraryLinks(Recipe r) {

public Plan setGrantOnPlan(Long planId, Long userId, AccessLevel level) {
Plan plan = getPlanById(planId, AccessLevel.ADMINISTER);
plan.getAcl().setGrant(userRepo.getById(userId), level);
plan.getAcl().setGrant(userRepo.getReferenceById(userId), level);
return plan;
}

public Plan revokeGrantFromPlan(Long planId, Long userId) {
Plan plan = getPlanById(planId, AccessLevel.ADMINISTER);
plan.getAcl().revokeGrant(userRepo.getById(userId));
plan.getAcl().revokeGrant(userRepo.getReferenceById(userId));
return plan;
}

public Plan setColor(Long planId, String color) {
if (StringUtils.hasText(color) && !RE_COLOR.matcher(color).matches()) {
throw new IllegalArgumentException(String.format(
"Color '%s' is invalid. Use six hash-prefix digits (e.g., '#f57f17').",
color));
}
Plan plan = getPlanById(planId, AccessLevel.ADMINISTER);
plan.setColor(color);
return plan;
}

Expand Down
12 changes: 6 additions & 6 deletions src/main/java/com/brennaswitzer/cookbook/web/PlanController.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public class PlanController {
@GetMapping("")
@ResponseStatus(HttpStatus.OK)
public List<PlanItemInfo> getPlans() {
return PlanItemInfo.fromPlans(planService.getPlans());
return PlanItemInfo.from(planService.getPlans());
}

@PostMapping("")
Expand All @@ -70,15 +70,15 @@ public PlanItemInfo createPlan(@RequestBody PlanItemCreate info) {
? planService.duplicatePlan(info.getName(),
info.getFromId())
: planService.createPlan(info.getName());
return PlanItemInfo.fromPlan(plan);
return PlanItemInfo.from(plan);
}

@GetMapping("/{id}")
@ResponseStatus(HttpStatus.OK)
public PlanItemInfo getPlanItem(
@PathVariable("id") Long id
) {
return PlanItemInfo.fromPlanItem(planService.getPlanItemById(id));
return PlanItemInfo.from(planService.getPlanItemById(id));
}

@GetMapping("/{id}/acl")
Expand All @@ -103,7 +103,7 @@ public ShareInfo getShareInfoById(
public List<PlanItemInfo> getDescendants(
@PathVariable("id") Long id
) {
return PlanItemInfo.fromPlanItems(
return PlanItemInfo.from(
planService.getTreeById(id));
}

Expand All @@ -112,7 +112,7 @@ public List<PlanItemInfo> getUpdatedSince(
@PathVariable("id") Long id,
@RequestParam Long cutoff
) {
return PlanItemInfo.fromPlanItems(
return PlanItemInfo.from(
planService.getTreeDeltasById(
id,
Instant.ofEpochMilli(cutoff)));
Expand Down Expand Up @@ -245,7 +245,7 @@ public GrantInfo addGrant(
) {
Plan plan = planService.setGrantOnPlan(id, grant.getUserId(), grant.getAccessLevel());
Acl acl = plan.getAcl();
User user = userRepo.getById(grant.getUserId());
User user = userRepo.getReferenceById(grant.getUserId());
return GrantInfo.fromGrant(user, acl.getGrant(user));
}

Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/db/changelog/gobrennas-2024.sql
Original file line number Diff line number Diff line change
Expand Up @@ -461,3 +461,7 @@ where exists (select *
--changeset barneyb:index-cook-history-owner-status
CREATE INDEX idx_planned_recipe_history_owner_status
ON planned_recipe_history (owner_id, status_id);

--changeset barneyb:plan-color
ALTER TABLE plan_item
ADD color VARCHAR;
8 changes: 7 additions & 1 deletion src/main/resources/graphqls/planner.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ type Plan implements Node & Owned & AccessControlled & CorePlanItem {
id: ID!
owner: User!
name: String!
"""The color associated with the plan, expressed as a number sign and six
hex digits (e.g., '#F57F17').
"""
color: String!
"""A plan's plan is always itself."""
plan: Plan!
share: ShareInfo
Expand Down Expand Up @@ -110,8 +114,10 @@ type PlannerMutation {
an item under a different parent is included in the list, it will be moved
under this item."""
reorderSubitems(parentId:ID!, itemIds: [ID!]!): PlanItem
"""Set the plan's color (e.g., '#F57F17'), or reset it with a null or empty string."""
setColor(planId: ID!, color: String): Plan!
"""Set the access level granted to a user w/in a plan."""
setGrant(planId: ID!, userId: ID!, accessLevel:AccessLevel): Plan!
setGrant(planId: ID!, userId: ID!, accessLevel: AccessLevel): Plan!
"""Sets the status of the given item. This will always return the updated
item, though it may immediately moved to the trash (in the background)."""
setStatus(id: ID!, status: PlanItemStatus!, doneAt: DateTime): PlanItem!
Expand Down
Loading

0 comments on commit d319a78

Please sign in to comment.