Skip to content
Draft
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
4 changes: 4 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@
<artifactId>ical4j</artifactId>
<version>3.2.12</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-envers</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.BatchSize;
import org.hibernate.envers.Audited;

import java.util.Comparator;
import java.util.HashSet;
Expand Down Expand Up @@ -42,6 +43,7 @@ public abstract class Ingredient extends BaseEntity implements Named, Labeled {

@Getter
@Setter
@Audited
private String name;

@ElementCollection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import lombok.Setter;
import org.hibernate.annotations.BatchSize;
import org.hibernate.collection.spi.PersistentSet;
import org.hibernate.envers.Audited;
import org.hibernate.envers.NotAudited;
import org.springframework.util.Assert;

import java.util.Collections;
Expand All @@ -24,6 +26,7 @@
@Entity
@DiscriminatorValue("PantryItem")
@JsonTypeName("PantryItem")
@Audited
public class PantryItem extends Ingredient {

public static final Comparator<PantryItem> BY_STORE_ORDER = (a, b) -> {
Expand All @@ -37,12 +40,14 @@ public class PantryItem extends Ingredient {
};

// todo: make this user specific
@NotAudited
private int storeOrder = 0;

@ElementCollection
@BatchSize(size = 50)
@Column(name = "synonym")
@Setter(AccessLevel.PRIVATE)
@NotAudited
private Set<String> synonyms;

/**
Expand Down
15 changes: 5 additions & 10 deletions src/main/java/com/brennaswitzer/cookbook/domain/PlanBucket.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.envers.Audited;

import java.time.LocalDate;
import java.util.Collection;
Expand All @@ -19,29 +20,23 @@
@Table(name = "plan_bucket", uniqueConstraints = {
@UniqueConstraint(columnNames = { "plan_id", "name" })
})
@Getter
@Setter
public class PlanBucket extends BaseEntity {

@NotNull
@Getter
@Setter
@ManyToOne(fetch = FetchType.LAZY)
private Plan plan;

@Getter
@Setter
@Audited
private String name;

@Getter
@Setter
@Audited
private LocalDate date;

@Getter
@Setter
@OneToMany(mappedBy = "bucket")
private Collection<PlanItem> items;

@Getter
@Setter
@Column(name = "mod_count")
private int modCount;

Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/brennaswitzer/cookbook/domain/PlanItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import lombok.val;
import org.hibernate.Hibernate;
import org.hibernate.annotations.BatchSize;
import org.hibernate.envers.Audited;

import java.util.ArrayList;
import java.util.Collection;
Expand Down Expand Up @@ -73,6 +74,7 @@ public class PlanItem extends BaseEntity implements Named, MutableItem {
@NotNull
@Getter
@Setter
@Audited
private String name;

@Getter
Expand All @@ -82,6 +84,7 @@ public class PlanItem extends BaseEntity implements Named, MutableItem {
@Column(name = "status_id")
@Getter
@Setter
@Audited
private PlanItemStatus status = PlanItemStatus.NEEDED;

@Embedded
Expand Down Expand Up @@ -125,11 +128,13 @@ public class PlanItem extends BaseEntity implements Named, MutableItem {
fetch = FetchType.LAZY)
@Getter
@Setter
@Audited
private Ingredient ingredient;

@Getter
@Setter
@ManyToOne(fetch = FetchType.LAZY)
@Audited
private PlanBucket bucket;

@Getter
Expand Down
24 changes: 14 additions & 10 deletions src/main/java/com/brennaswitzer/cookbook/domain/Recipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,45 @@
import jakarta.persistence.PreUpdate;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.envers.Audited;
import org.hibernate.envers.NotAudited;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

@Setter
@Entity
@DiscriminatorValue("Recipe")
@JsonTypeName("Recipe")
@Audited
public class Recipe extends Ingredient implements AggregateIngredient, Owned {

// this will gracefully store the same way as an @Embedded Acl will
@ManyToOne(fetch = FetchType.LAZY)
@NotAudited
private User owner;

// these will gracefully emulate AccessControlled's owner property
@JsonIgnore // but hide it from the client :)
public User getOwner() { return owner; }
public void setOwner(User owner) { this.owner = owner; }

// end access control emulation

@Getter
@Setter
@NotAudited
private String externalUrl;

@Getter
@Setter
@NotAudited
private String directions;

@Getter
@Setter
@NotAudited
private Integer yield;

@Getter
@Setter
@NotAudited
private Integer calories;

@Embedded
Expand All @@ -62,6 +66,7 @@ public class Recipe extends Ingredient implements AggregateIngredient, Owned {
@AttributeOverride(name = "focusTop", column = @Column(name = "photo_focus_top")),
@AttributeOverride(name = "focusLeft", column = @Column(name = "photo_focus_left"))
})
@NotAudited
private Photo photo;
public Photo getPhoto() {
return getPhoto(false);
Expand All @@ -72,9 +77,7 @@ public Photo getPhoto(boolean create) {
}
return this.photo;
}
public void setPhoto(Photo photo) {
this.photo = photo;
}

public void setPhoto(S3File file) {
getPhoto(true).setFile(file);
}
Expand All @@ -91,17 +94,18 @@ public boolean hasPhoto() {
* Time is stored in milliseconds
*/
@Getter
@Setter
@NotAudited
private Integer totalTime;

@Setter
@Getter
@ElementCollection
@OrderBy("_idx, raw")
@NotAudited
private List<IngredientRef> ingredients;

@Getter
@OneToMany(mappedBy = "recipe")
@NotAudited
private Collection<PlannedRecipeHistory> planHistory;

public Recipe() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.brennaswitzer.cookbook.domain.envers;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import lombok.Data;
import org.hibernate.envers.RevisionEntity;
import org.hibernate.envers.RevisionNumber;
import org.hibernate.envers.RevisionTimestamp;

import java.time.Instant;

@Entity
@Table(name = "AUD__REVINFO") // two underscores so it sorts first
@RevisionEntity(BfsRevisionListener.class)
@SequenceGenerator(
name = "aud_seq",
sequenceName = "aud_seq"
)
@Data
public class BfsRevisionEntity {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "aud_seq")
@Column(name = "REV")
@RevisionNumber
private long id;

@Column(name = "REV_TSTMP")
@RevisionTimestamp
private long timestamp;

public Instant getInstant() {
return Instant.ofEpochMilli(timestamp);
}

@Column(name = "REV_USERNAME")
private String username;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.brennaswitzer.cookbook.domain.envers;

import com.brennaswitzer.cookbook.util.UserPrincipalAccess;
import org.hibernate.envers.RevisionListener;
import org.springframework.context.ApplicationContext;

/**
* Envers uses this to populate the custom {@link BfsRevisionEntity}. Due to
* partial Spring magic, this type is instantiated by the bean factory, but
* isn't quite a "real" bean with full wiring support. So just capture the
* {@link ApplicationContext} during construction, and then obtain the
* {@link UserPrincipalAccess} from it upon first need. This also happens to
* avoid a cyclic dependency, as this listener is needed for Hibernate to start,
* but Hibernate is needed by our implementation.
*/
public class BfsRevisionListener implements RevisionListener {

private final ApplicationContext appCtx;
private UserPrincipalAccess principalAccess;

public BfsRevisionListener(ApplicationContext appCtx) {
this.appCtx = appCtx;
}

private UserPrincipalAccess getPrincipalAccess() {
if (principalAccess == null) {
principalAccess = appCtx.getBean(UserPrincipalAccess.class);
}
return principalAccess;
}

@Override
public void newRevision(Object revisionEntity) {
if (revisionEntity instanceof BfsRevisionEntity rev) {
rev.setUsername(getPrincipalAccess().getUsername());
}
}

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

import com.brennaswitzer.cookbook.domain.Ingredient;
import org.springframework.data.repository.history.RevisionRepository;

public interface IngredientRepository extends BaseEntityRepository<Ingredient> {
public interface IngredientRepository extends BaseEntityRepository<Ingredient>, RevisionRepository<Ingredient, Long, Long> {

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.brennaswitzer.cookbook.repositories;

import com.brennaswitzer.cookbook.domain.PlanBucket;
import org.springframework.data.repository.history.RevisionRepository;

import java.util.stream.Stream;

public interface PlanBucketRepository extends BaseEntityRepository<PlanBucket> {
public interface PlanBucketRepository extends BaseEntityRepository<PlanBucket>, RevisionRepository<PlanBucket, Long, Long> {

Stream<PlanBucket> streamAllByPlanIdAndDateIsNotNull(Long id);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
import com.brennaswitzer.cookbook.domain.PlanItemStatus;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.history.RevisionRepository;

import java.time.Instant;

public interface PlanItemRepository extends BaseEntityRepository<PlanItem> {
public interface PlanItemRepository extends BaseEntityRepository<PlanItem>, RevisionRepository<PlanItem, Long, Long> {

int countByStatusNot(PlanItemStatus status);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ default Long getId() {
return getUserPrincipal().getId();
}

default String getUsername() {
return getUserPrincipal().getUsername();
}

UserPrincipal getUserPrincipal();

default User getUser() {
Expand Down
9 changes: 9 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ spring:
jdbc:
lob:
non_contextual_creation: true
org:
hibernate:
envers:
audit_table_prefix: "AUD_"
audit_table_suffix: ""
audit_strategy: org.hibernate.envers.strategy.ValidityAuditStrategy
audit_strategy_validity_store_revend_timestamp: true
audit_strategy_validity_revend_timestamp_numeric: true
cascade_delete_revision: true
open-in-view: true # the default; just suppress the warning
security:
oauth2:
Expand Down
Loading