Basic Recipe entity and models.
This commit is contained in:
parent
e205db813a
commit
b10451203b
@ -31,6 +31,14 @@ dependencies {
|
||||
implementation 'io.jsonwebtoken:jjwt-api:0.12.5'
|
||||
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.5'
|
||||
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5'
|
||||
|
||||
implementation 'org.commonmark:commonmark:0.22.0'
|
||||
implementation 'org.jsoup:jsoup:1.17.2'
|
||||
|
||||
compileOnly 'org.jetbrains:annotations:24.1.0'
|
||||
|
||||
// Custom testing
|
||||
testRuntimeOnly 'com.h2database:h2'
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
|
21
src/main/java/app/mealsmadeeasy/api/recipe/Recipe.java
Normal file
21
src/main/java/app/mealsmadeeasy/api/recipe/Recipe.java
Normal file
@ -0,0 +1,21 @@
|
||||
package app.mealsmadeeasy.api.recipe;
|
||||
|
||||
import app.mealsmadeeasy.api.recipe.comment.RecipeComment;
|
||||
import app.mealsmadeeasy.api.user.User;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Set;
|
||||
|
||||
public interface Recipe {
|
||||
Long getId();
|
||||
LocalDateTime getCreated();
|
||||
@Nullable LocalDateTime getModified();
|
||||
String getTitle();
|
||||
String getRawText();
|
||||
User getOwner();
|
||||
Set<User> getStarGazers();
|
||||
boolean isPublic();
|
||||
Set<User> getViewers();
|
||||
Set<RecipeComment> getComments();
|
||||
}
|
170
src/main/java/app/mealsmadeeasy/api/recipe/RecipeEntity.java
Normal file
170
src/main/java/app/mealsmadeeasy/api/recipe/RecipeEntity.java
Normal file
@ -0,0 +1,170 @@
|
||||
package app.mealsmadeeasy.api.recipe;
|
||||
|
||||
import app.mealsmadeeasy.api.recipe.comment.RecipeComment;
|
||||
import app.mealsmadeeasy.api.recipe.comment.RecipeCommentEntity;
|
||||
import app.mealsmadeeasy.api.user.User;
|
||||
import app.mealsmadeeasy.api.user.UserEntity;
|
||||
import jakarta.persistence.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Entity(name = "Recipe")
|
||||
public final class RecipeEntity implements Recipe {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@Column(nullable = false, updatable = false)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime created = LocalDateTime.now();
|
||||
|
||||
private LocalDateTime modified;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String title;
|
||||
|
||||
@Column(nullable = false)
|
||||
@Lob
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private String rawText;
|
||||
|
||||
@Lob
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private String cachedRenderedText;
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "owner_id", nullable = false)
|
||||
private UserEntity owner;
|
||||
|
||||
@OneToMany
|
||||
private Set<UserEntity> starGazers = new HashSet<>();
|
||||
|
||||
@OneToMany(mappedBy = "recipe")
|
||||
private Set<RecipeCommentEntity> comments = new HashSet<>();
|
||||
|
||||
@Column(nullable = false)
|
||||
private Boolean isPublic = false;
|
||||
|
||||
@ManyToMany
|
||||
private Set<UserEntity> viewers = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime getCreated() {
|
||||
return this.created;
|
||||
}
|
||||
|
||||
public void setCreated(LocalDateTime created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable LocalDateTime getModified() {
|
||||
return this.modified;
|
||||
}
|
||||
|
||||
public void setModified(@Nullable LocalDateTime modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRawText() {
|
||||
return this.rawText;
|
||||
}
|
||||
|
||||
public void setRawText(String rawText) {
|
||||
this.rawText = rawText;
|
||||
}
|
||||
|
||||
public String getCachedRenderedText() {
|
||||
return this.cachedRenderedText;
|
||||
}
|
||||
|
||||
public void setCachedRenderedText(String cachedRenderedText) {
|
||||
this.cachedRenderedText = cachedRenderedText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserEntity getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
public void setOwner(UserEntity owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPublic() {
|
||||
return this.isPublic;
|
||||
}
|
||||
|
||||
public void setPublic(Boolean isPublic) {
|
||||
this.isPublic = isPublic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<User> getViewers() {
|
||||
return Set.copyOf(this.viewers);
|
||||
}
|
||||
|
||||
public Set<UserEntity> getViewerEntities() {
|
||||
return this.viewers;
|
||||
}
|
||||
|
||||
public void setViewers(Set<UserEntity> viewers) {
|
||||
this.viewers = viewers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<User> getStarGazers() {
|
||||
return Set.copyOf(this.starGazers);
|
||||
}
|
||||
|
||||
public Set<UserEntity> getStarGazerEntities() {
|
||||
return this.starGazers;
|
||||
}
|
||||
|
||||
public void setStarGazers(Set<UserEntity> starGazers) {
|
||||
this.starGazers = starGazers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RecipeComment> getComments() {
|
||||
return Set.copyOf(this.comments);
|
||||
}
|
||||
|
||||
public Set<RecipeCommentEntity> getCommentEntities() {
|
||||
return this.comments;
|
||||
}
|
||||
|
||||
public void setComments(Set<RecipeCommentEntity> comments) {
|
||||
this.comments = comments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RecipeEntity(" + this.id + ", " + this.title + ")";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package app.mealsmadeeasy.api.recipe;
|
||||
|
||||
public class RecipeException extends Exception {
|
||||
|
||||
public enum Type {
|
||||
INVALID_OWNER_USERNAME, INVALID_STAR, INVALID_ID
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
|
||||
public RecipeException(Type type, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public RecipeException(Type type, String message) {
|
||||
super(message);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package app.mealsmadeeasy.api.recipe;
|
||||
|
||||
import app.mealsmadeeasy.api.user.UserEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface RecipeRepository extends JpaRepository<RecipeEntity, Long> {
|
||||
List<RecipeEntity> findAllByIsPublicIsTrue();
|
||||
List<RecipeEntity> findAllByViewersContaining(UserEntity viewer);
|
||||
List<RecipeEntity> findAllByOwner(UserEntity owner);
|
||||
|
||||
@Query("SELECT r FROM Recipe r WHERE size(r.starGazers) > ?1")
|
||||
List<RecipeEntity> findAllByStarGazersGreaterThanEqual(long stars);
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package app.mealsmadeeasy.api.recipe;
|
||||
|
||||
import app.mealsmadeeasy.api.recipe.comment.RecipeComment;
|
||||
import app.mealsmadeeasy.api.recipe.star.RecipeStar;
|
||||
import app.mealsmadeeasy.api.user.User;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface RecipeService {
|
||||
|
||||
Recipe create(String ownerUsername, String title, String rawText) throws RecipeException;
|
||||
|
||||
Recipe getById(long id) throws RecipeException;
|
||||
List<Recipe> getByMinimumStars(long minimumStars);
|
||||
List<Recipe> getPublicRecipes();
|
||||
List<Recipe> getRecipesViewableBy(User user);
|
||||
List<Recipe> getRecipesOwnedBy(User user);
|
||||
|
||||
String getRenderedMarkdown(Recipe recipe);
|
||||
|
||||
Recipe updateRawText(Recipe recipe, String newRawText);
|
||||
|
||||
Recipe updateOwner(Recipe recipe, String newOwnerUsername) throws RecipeException;
|
||||
|
||||
RecipeStar addStar(Recipe recipe, User giver);
|
||||
void deleteStarByUser(Recipe recipe, User giver) throws RecipeException;
|
||||
void deleteStar(RecipeStar recipeStar);
|
||||
|
||||
Recipe setPublic(Recipe recipe, boolean isPublic);
|
||||
|
||||
Recipe addViewer(Recipe recipe, User user);
|
||||
Recipe removeViewer(Recipe recipe, User user);
|
||||
Recipe clearViewers(Recipe recipe);
|
||||
|
||||
RecipeComment getCommentById(long id) throws RecipeException;
|
||||
RecipeComment addComment(Recipe recipe, String rawCommentText, User commenter);
|
||||
RecipeComment updateComment(RecipeComment comment, String newRawCommentText);
|
||||
String getRenderedMarkdown(RecipeComment recipeComment);
|
||||
void deleteComment(RecipeComment comment);
|
||||
Recipe clearComments(Recipe recipe);
|
||||
|
||||
void deleteRecipe(Recipe recipe);
|
||||
void deleteById(long id);
|
||||
|
||||
}
|
@ -0,0 +1,236 @@
|
||||
package app.mealsmadeeasy.api.recipe;
|
||||
|
||||
import app.mealsmadeeasy.api.recipe.comment.RecipeComment;
|
||||
import app.mealsmadeeasy.api.recipe.comment.RecipeCommentEntity;
|
||||
import app.mealsmadeeasy.api.recipe.comment.RecipeCommentRepository;
|
||||
import app.mealsmadeeasy.api.recipe.star.RecipeStar;
|
||||
import app.mealsmadeeasy.api.recipe.star.RecipeStarEntity;
|
||||
import app.mealsmadeeasy.api.recipe.star.RecipeStarRepository;
|
||||
import app.mealsmadeeasy.api.user.User;
|
||||
import app.mealsmadeeasy.api.user.UserEntity;
|
||||
import app.mealsmadeeasy.api.user.UserRepository;
|
||||
import org.commonmark.parser.Parser;
|
||||
import org.commonmark.renderer.html.HtmlRenderer;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.safety.Safelist;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
public final class RecipeServiceImpl implements RecipeService {
|
||||
|
||||
private static String renderAndCleanMarkdown(String rawText) {
|
||||
final var parser = Parser.builder().build();
|
||||
final var node = parser.parse(rawText);
|
||||
final var htmlRenderer = HtmlRenderer.builder().build();
|
||||
final String unsafeHtml = htmlRenderer.render(node);
|
||||
return Jsoup.clean(unsafeHtml, Safelist.relaxed());
|
||||
}
|
||||
|
||||
private final RecipeRepository recipeRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final RecipeStarRepository recipeStarRepository;
|
||||
private final RecipeCommentRepository recipeCommentRepository;
|
||||
|
||||
public RecipeServiceImpl(
|
||||
RecipeRepository recipeRepository,
|
||||
UserRepository userRepository,
|
||||
RecipeStarRepository recipeStarRepository,
|
||||
RecipeCommentRepository recipeCommentRepository
|
||||
) {
|
||||
this.recipeRepository = recipeRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.recipeStarRepository = recipeStarRepository;
|
||||
this.recipeCommentRepository = recipeCommentRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Recipe create(String ownerUsername, String title, String rawText) throws RecipeException {
|
||||
final RecipeEntity draft = new RecipeEntity();
|
||||
final UserEntity userEntity = this.userRepository.findByUsername(ownerUsername)
|
||||
.orElseThrow(() -> new RecipeException(
|
||||
RecipeException.Type.INVALID_OWNER_USERNAME,
|
||||
"No such ownerUsername " + ownerUsername
|
||||
));
|
||||
draft.setOwner(userEntity);
|
||||
draft.setTitle(title);
|
||||
draft.setRawText(rawText);
|
||||
return this.recipeRepository.save(draft);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Recipe getById(long id) throws RecipeException {
|
||||
return this.recipeRepository.findById(id).orElseThrow(() -> new RecipeException(
|
||||
RecipeException.Type.INVALID_ID,
|
||||
"No such recipe for id " + id
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Recipe> getByMinimumStars(long minimumStars) {
|
||||
return List.copyOf(this.recipeRepository.findAllByStarGazersGreaterThanEqual(minimumStars));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Recipe> getPublicRecipes() {
|
||||
return List.copyOf(this.recipeRepository.findAllByIsPublicIsTrue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Recipe> getRecipesViewableBy(User user) {
|
||||
return List.copyOf(this.recipeRepository.findAllByViewersContaining((UserEntity) user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Recipe> getRecipesOwnedBy(User user) {
|
||||
return List.copyOf(this.recipeRepository.findAllByOwner((UserEntity) user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRenderedMarkdown(Recipe recipe) {
|
||||
RecipeEntity entity = (RecipeEntity) recipe;
|
||||
if (entity.getCachedRenderedText() == null) {
|
||||
entity.setCachedRenderedText(renderAndCleanMarkdown(entity.getRawText()));
|
||||
entity = this.recipeRepository.save(entity);
|
||||
}
|
||||
return entity.getCachedRenderedText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Recipe updateRawText(Recipe recipe, String newRawText) {
|
||||
final RecipeEntity entity = (RecipeEntity) recipe;
|
||||
entity.setCachedRenderedText(null);
|
||||
entity.setRawText(newRawText);
|
||||
return this.recipeRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Recipe updateOwner(Recipe recipe, String newOwnerUsername) throws RecipeException {
|
||||
final RecipeEntity entity = (RecipeEntity) recipe;
|
||||
final UserEntity newOwner = this.userRepository.findByUsername(newOwnerUsername)
|
||||
.orElseThrow(() -> new RecipeException(
|
||||
RecipeException.Type.INVALID_OWNER_USERNAME,
|
||||
"No such username: " + newOwnerUsername
|
||||
));
|
||||
entity.setOwner(newOwner);
|
||||
return this.recipeRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeStar addStar(Recipe recipe, User giver) {
|
||||
final RecipeEntity entity = (RecipeEntity) recipe;
|
||||
final RecipeStarEntity star = new RecipeStarEntity();
|
||||
star.setOwner((UserEntity) giver);
|
||||
star.setRecipe(entity);
|
||||
return this.recipeStarRepository.save(star);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteStar(RecipeStar recipeStar) {
|
||||
this.recipeStarRepository.delete((RecipeStarEntity) recipeStar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteStarByUser(Recipe recipe, User giver) throws RecipeException {
|
||||
final RecipeStarEntity star = this.recipeStarRepository.findByOwnerAndRecipe(
|
||||
(UserEntity) giver,
|
||||
(RecipeEntity) recipe
|
||||
).orElseThrow(() -> new RecipeException(
|
||||
RecipeException.Type.INVALID_STAR,
|
||||
"No such star for user " + giver.getUsername() + " and recipe " + recipe.getId()
|
||||
));
|
||||
this.recipeStarRepository.delete(star);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Recipe setPublic(Recipe recipe, boolean isPublic) {
|
||||
final RecipeEntity entity = (RecipeEntity) recipe;
|
||||
entity.setPublic(isPublic);
|
||||
return this.recipeRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Recipe addViewer(Recipe recipe, User user) {
|
||||
final RecipeEntity entity = (RecipeEntity) recipe;
|
||||
final Set<UserEntity> viewers = new HashSet<>(entity.getViewerEntities());
|
||||
viewers.add((UserEntity) user);
|
||||
entity.setViewers(viewers);
|
||||
return this.recipeRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Recipe removeViewer(Recipe recipe, User user) {
|
||||
final RecipeEntity entity = (RecipeEntity) recipe;
|
||||
final Set<UserEntity> viewers = new HashSet<>(entity.getViewerEntities());
|
||||
viewers.remove((UserEntity) user);
|
||||
entity.setViewers(viewers);
|
||||
return this.recipeRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Recipe clearViewers(Recipe recipe) {
|
||||
final RecipeEntity entity = (RecipeEntity) recipe;
|
||||
entity.setViewers(new HashSet<>());
|
||||
return this.recipeRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeComment getCommentById(long id) throws RecipeException {
|
||||
return this.recipeCommentRepository.findById(id)
|
||||
.orElseThrow(() -> new RecipeException(
|
||||
RecipeException.Type.INVALID_ID,
|
||||
"No such RecipeComment for id " + id
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeComment addComment(Recipe recipe, String rawCommentText, User commenter) {
|
||||
final RecipeCommentEntity draft = new RecipeCommentEntity();
|
||||
draft.setRawText(rawCommentText);
|
||||
draft.setOwner((UserEntity) commenter);
|
||||
return this.recipeCommentRepository.save(draft);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeComment updateComment(RecipeComment comment, String newRawCommentText) {
|
||||
final RecipeCommentEntity entity = (RecipeCommentEntity) comment;
|
||||
entity.setCachedRenderedText(null);
|
||||
entity.setRawText(newRawCommentText);
|
||||
return this.recipeCommentRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRenderedMarkdown(RecipeComment recipeComment) {
|
||||
RecipeCommentEntity entity = (RecipeCommentEntity) recipeComment;
|
||||
if (entity.getCachedRenderedText() == null) {
|
||||
entity.setCachedRenderedText(renderAndCleanMarkdown(entity.getRawText()));
|
||||
entity = this.recipeCommentRepository.save(entity);
|
||||
}
|
||||
return entity.getCachedRenderedText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteComment(RecipeComment comment) {
|
||||
this.recipeCommentRepository.delete((RecipeCommentEntity) comment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Recipe clearComments(Recipe recipe) {
|
||||
this.recipeCommentRepository.deleteAllByRecipe((RecipeEntity) recipe);
|
||||
return this.recipeRepository.getReferenceById(recipe.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteRecipe(Recipe recipe) {
|
||||
this.recipeRepository.delete((RecipeEntity) recipe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(long id) {
|
||||
this.recipeRepository.deleteById(id);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package app.mealsmadeeasy.api.recipe.comment;
|
||||
|
||||
import app.mealsmadeeasy.api.recipe.Recipe;
|
||||
import app.mealsmadeeasy.api.user.User;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public interface RecipeComment {
|
||||
LocalDateTime getCreated();
|
||||
LocalDateTime getModified();
|
||||
String getRawText();
|
||||
User getOwner();
|
||||
Recipe getRecipe();
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package app.mealsmadeeasy.api.recipe.comment;
|
||||
|
||||
import app.mealsmadeeasy.api.recipe.RecipeEntity;
|
||||
import app.mealsmadeeasy.api.user.UserEntity;
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity(name = "RecipeComment")
|
||||
public final class RecipeCommentEntity implements RecipeComment {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@Column(nullable = false)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime created = LocalDateTime.now();
|
||||
|
||||
private LocalDateTime modified;
|
||||
|
||||
@Lob
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private String rawText;
|
||||
|
||||
@Lob
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private String cachedRenderedText;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "owner_id", nullable = false, updatable = false)
|
||||
private UserEntity owner;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "recipe_id", nullable = false, updatable = false)
|
||||
private RecipeEntity recipe;
|
||||
|
||||
public Long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime getCreated() {
|
||||
return this.created;
|
||||
}
|
||||
|
||||
public void setCreated(LocalDateTime created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime getModified() {
|
||||
return this.modified;
|
||||
}
|
||||
|
||||
public void setModified(LocalDateTime modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRawText() {
|
||||
return this.rawText;
|
||||
}
|
||||
|
||||
public void setRawText(String rawText) {
|
||||
this.rawText = rawText;
|
||||
}
|
||||
|
||||
public String getCachedRenderedText() {
|
||||
return this.cachedRenderedText;
|
||||
}
|
||||
|
||||
public void setCachedRenderedText(String cachedRenderedText) {
|
||||
this.cachedRenderedText = cachedRenderedText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserEntity getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
public void setOwner(UserEntity owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeEntity getRecipe() {
|
||||
return this.recipe;
|
||||
}
|
||||
|
||||
public void setRecipe(RecipeEntity recipe) {
|
||||
this.recipe = recipe;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package app.mealsmadeeasy.api.recipe.comment;
|
||||
|
||||
import app.mealsmadeeasy.api.recipe.RecipeEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface RecipeCommentRepository extends JpaRepository<RecipeCommentEntity, Long> {
|
||||
void deleteAllByRecipe(RecipeEntity recipe);
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package app.mealsmadeeasy.api.recipe.star;
|
||||
|
||||
import app.mealsmadeeasy.api.recipe.Recipe;
|
||||
import app.mealsmadeeasy.api.user.User;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public interface RecipeStar {
|
||||
User getOwner();
|
||||
LocalDateTime getDate();
|
||||
Recipe getRecipe();
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package app.mealsmadeeasy.api.recipe.star;
|
||||
|
||||
import app.mealsmadeeasy.api.recipe.RecipeEntity;
|
||||
import app.mealsmadeeasy.api.user.UserEntity;
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity(name = "RecipeStar")
|
||||
public final class RecipeStarEntity implements RecipeStar {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@Column(nullable = false)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "owner_id", nullable = false, updatable = false)
|
||||
private UserEntity owner;
|
||||
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime date = LocalDateTime.now();
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "recipe_id", nullable = false, updatable = false)
|
||||
private RecipeEntity recipe;
|
||||
|
||||
public long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserEntity getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
public void setOwner(UserEntity owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime getDate() {
|
||||
return this.date;
|
||||
}
|
||||
|
||||
public void setDate(LocalDateTime date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeEntity getRecipe() {
|
||||
return this.recipe;
|
||||
}
|
||||
|
||||
public void setRecipe(RecipeEntity recipe) {
|
||||
this.recipe = recipe;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package app.mealsmadeeasy.api.recipe.star;
|
||||
|
||||
import app.mealsmadeeasy.api.recipe.RecipeEntity;
|
||||
import app.mealsmadeeasy.api.user.UserEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface RecipeStarRepository extends JpaRepository<RecipeStarEntity, Long> {
|
||||
List<RecipeStarEntity> findAllByOwner(UserEntity user);
|
||||
long countAllByOwner(UserEntity user);
|
||||
Optional<RecipeStarEntity> findByOwnerAndRecipe(UserEntity user, RecipeEntity recipe);
|
||||
}
|
@ -5,9 +5,11 @@ import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
@Entity(name = "User")
|
||||
@Table(name = "\"user\"")
|
||||
public final class UserEntity implements User {
|
||||
|
||||
public static UserEntity getDefaultDraft() {
|
||||
@ -141,4 +143,41 @@ public final class UserEntity implements User {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
this.id,
|
||||
this.username,
|
||||
this.email,
|
||||
this.password,
|
||||
this.authorities,
|
||||
this.enabled,
|
||||
this.expired,
|
||||
this.locked,
|
||||
this.credentialsExpired
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj instanceof User o) {
|
||||
return Objects.equals(this.id, o.getId())
|
||||
&& Objects.equals(this.username, o.getUsername())
|
||||
&& Objects.equals(this.password, o.getPassword())
|
||||
&& Objects.equals(this.authorities, o.getAuthorities())
|
||||
&& Objects.equals(this.enabled, o.isEnabled())
|
||||
&& Objects.equals(this.expired, !o.isAccountNonExpired())
|
||||
&& Objects.equals(this.locked, !o.isAccountNonLocked())
|
||||
&& Objects.equals(this.credentialsExpired, !o.isCredentialsNonExpired());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User(" + this.id + ", " + this.username + ", " + this.email + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,102 @@
|
||||
package app.mealsmadeeasy.api.recipe;
|
||||
|
||||
import app.mealsmadeeasy.api.user.UserEntity;
|
||||
import app.mealsmadeeasy.api.user.UserRepository;
|
||||
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 java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest
|
||||
public class RecipeRepositoryTests {
|
||||
|
||||
@Autowired
|
||||
private RecipeRepository recipeRepository;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
private UserEntity getOwnerUser() {
|
||||
final UserEntity recipeUser = UserEntity.getDefaultDraft();
|
||||
recipeUser.setUsername("recipeUser");
|
||||
recipeUser.setEmail("recipe@user.com");
|
||||
recipeUser.setPassword("test");
|
||||
return this.userRepository.save(recipeUser);
|
||||
}
|
||||
|
||||
private UserEntity getViewerUser() {
|
||||
final UserEntity viewerUser = UserEntity.getDefaultDraft();
|
||||
viewerUser.setUsername("recipeViewerUser");
|
||||
viewerUser.setEmail("recipe-viewer@user.com");
|
||||
viewerUser.setPassword("test");
|
||||
return this.userRepository.save(viewerUser);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void findsAllPublicRecipes() {
|
||||
final RecipeEntity publicRecipe = new RecipeEntity();
|
||||
publicRecipe.setPublic(true);
|
||||
publicRecipe.setOwner(this.getOwnerUser());
|
||||
publicRecipe.setTitle("Public Recipe");
|
||||
publicRecipe.setRawText("Hello, World!");
|
||||
this.recipeRepository.save(publicRecipe);
|
||||
|
||||
final List<RecipeEntity> publicRecipes = this.recipeRepository.findAllByIsPublicIsTrue();
|
||||
assertThat(publicRecipes.size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void doesNotFindNonPublicRecipe() {
|
||||
final RecipeEntity nonPublicRecipe = new RecipeEntity();
|
||||
nonPublicRecipe.setOwner(this.getOwnerUser());
|
||||
nonPublicRecipe.setTitle("Non-Public Recipe");
|
||||
nonPublicRecipe.setRawText("Hello, World!");
|
||||
this.recipeRepository.save(nonPublicRecipe);
|
||||
|
||||
final List<RecipeEntity> publicRecipes = this.recipeRepository.findAllByIsPublicIsTrue();
|
||||
assertThat(publicRecipes.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void findsAllForViewer() {
|
||||
final RecipeEntity recipe = new RecipeEntity();
|
||||
recipe.setOwner(this.getOwnerUser());
|
||||
recipe.setTitle("Test Recipe");
|
||||
recipe.setRawText("Hello, World!");
|
||||
final RecipeEntity saved = this.recipeRepository.save(recipe);
|
||||
|
||||
final UserEntity viewer = this.getViewerUser();
|
||||
final Set<UserEntity> viewers = new HashSet<>(recipe.getViewerEntities());
|
||||
viewers.add(viewer);
|
||||
saved.setViewers(viewers);
|
||||
|
||||
this.recipeRepository.save(saved);
|
||||
|
||||
final List<RecipeEntity> viewable = this.recipeRepository.findAllByViewersContaining(viewer);
|
||||
assertThat(viewable.size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void doesNotIncludeNonViewable() {
|
||||
final RecipeEntity recipe = new RecipeEntity();
|
||||
recipe.setOwner(this.getOwnerUser());
|
||||
recipe.setTitle("Test Recipe");
|
||||
recipe.setRawText("Hello, World!");
|
||||
this.recipeRepository.save(recipe);
|
||||
|
||||
final UserEntity viewer = this.getViewerUser();
|
||||
final List<RecipeEntity> viewable = this.recipeRepository.findAllByViewersContaining(viewer);
|
||||
assertThat(viewable.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package app.mealsmadeeasy.api.recipe;
|
||||
|
||||
import app.mealsmadeeasy.api.user.User;
|
||||
import app.mealsmadeeasy.api.user.UserEntity;
|
||||
import app.mealsmadeeasy.api.user.UserRepository;
|
||||
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.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
@SpringBootTest
|
||||
public class RecipeServiceTests {
|
||||
|
||||
@Autowired
|
||||
private RecipeService recipeService;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
private UserEntity createTestUser(String username) {
|
||||
final UserEntity draft = UserEntity.getDefaultDraft();
|
||||
draft.setUsername(username);
|
||||
draft.setEmail(username + "@test.com");
|
||||
draft.setPassword("test");
|
||||
return this.userRepository.save(draft);
|
||||
}
|
||||
|
||||
private Recipe createTestRecipe(User owner) throws RecipeException {
|
||||
return this.recipeService.create(owner.getUsername(), "My Recipe" , "Hello!");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void simpleCreate() throws RecipeException {
|
||||
final User user = this.createTestUser("recipeOwner");
|
||||
final Recipe recipe = this.recipeService.create(user.getUsername(), "My Recipe" , "Hello!");
|
||||
assertThat(recipe.getOwner(), is(user));
|
||||
assertThat(recipe.getTitle(), is("My Recipe"));
|
||||
assertThat(recipe.getRawText(), is("Hello!"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void simpleGetById() throws RecipeException {
|
||||
final Recipe testRecipe = this.createTestRecipe(this.createTestUser("recipeOwner"));
|
||||
final Recipe byId = this.recipeService.getById(testRecipe.getId());
|
||||
assertThat(byId.getId(), is(testRecipe.getId()));
|
||||
assertThat(byId.getTitle(), is("My Recipe"));
|
||||
assertThat(byId.getRawText(), is("Hello!"));
|
||||
}
|
||||
|
||||
}
|
6
src/test/resources/application.properties
Normal file
6
src/test/resources/application.properties
Normal file
@ -0,0 +1,6 @@
|
||||
spring.datasource.driver-class-name=org.h2.Driver
|
||||
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=sa
|
||||
app.mealsmadeeasy.api.security.access-token-lifetime=60
|
||||
app.mealsmadeeasy.api.security.refresh-token-lifetime=120
|
Loading…
Reference in New Issue
Block a user