Compare commits
No commits in common. "0ad45adac1645cc3b0d93f1a979e697b7134b4df" and "a38ac6b6f5a9b3c281028a2505f31fa84b562ff8" have entirely different histories.
0ad45adac1
...
a38ac6b6f5
@ -29,10 +29,6 @@ sourceSets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
compileOnly {
|
|
||||||
extendsFrom annotationProcessor
|
|
||||||
}
|
|
||||||
|
|
||||||
testFixturesImplementation {
|
testFixturesImplementation {
|
||||||
extendsFrom implementation
|
extendsFrom implementation
|
||||||
}
|
}
|
||||||
@ -64,8 +60,6 @@ dependencies {
|
|||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
testImplementation 'org.springframework.security:spring-security-test'
|
testImplementation 'org.springframework.security:spring-security-test'
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||||
compileOnly 'org.projectlombok:lombok'
|
|
||||||
annotationProcessor 'org.projectlombok:lombok'
|
|
||||||
|
|
||||||
// flyway
|
// flyway
|
||||||
implementation 'org.flywaydb:flyway-core'
|
implementation 'org.flywaydb:flyway-core'
|
||||||
|
|||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
13
gradlew
vendored
13
gradlew
vendored
@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright © 2015 the original authors.
|
# Copyright © 2015-2021 the original authors.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -15,8 +15,6 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
|
||||||
#
|
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
@ -86,7 +84,7 @@ done
|
|||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@ -114,6 +112,7 @@ case "$( uname )" in #(
|
|||||||
NONSTOP* ) nonstop=true ;;
|
NONSTOP* ) nonstop=true ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
@ -171,6 +170,7 @@ fi
|
|||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if "$cygwin" || "$msys" ; then
|
if "$cygwin" || "$msys" ; then
|
||||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
@ -203,14 +203,15 @@ fi
|
|||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Collect all arguments for the java command:
|
# Collect all arguments for the java command:
|
||||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
# and any embedded shellness will be escaped.
|
# and any embedded shellness will be escaped.
|
||||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
# treated as '${Hostname}' itself on the command line.
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
"$@"
|
"$@"
|
||||||
|
|
||||||
# Stop when "xargs" is not available.
|
# Stop when "xargs" is not available.
|
||||||
|
|||||||
5
gradlew.bat
vendored
5
gradlew.bat
vendored
@ -13,8 +13,6 @@
|
|||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
@rem SPDX-License-Identifier: Apache-2.0
|
|
||||||
@rem
|
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@ -70,10 +68,11 @@ goto fail
|
|||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package app.mealsmadeeasy.api.recipe;
|
package app.mealsmadeeasy.api.recipe;
|
||||||
|
|
||||||
import app.mealsmadeeasy.api.IntegrationTestsExtension;
|
import app.mealsmadeeasy.api.IntegrationTestsExtension;
|
||||||
import app.mealsmadeeasy.api.user.User;
|
import app.mealsmadeeasy.api.user.UserEntity;
|
||||||
import app.mealsmadeeasy.api.user.UserRepository;
|
import app.mealsmadeeasy.api.user.UserRepository;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
@ -26,9 +26,9 @@ public class RecipeRepositoryTests {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
|
|
||||||
private User seedUser() {
|
private UserEntity seedUser() {
|
||||||
final String uuid = UUID.randomUUID().toString();
|
final String uuid = UUID.randomUUID().toString();
|
||||||
final User draft = User.getDefaultDraft();
|
final UserEntity draft = UserEntity.getDefaultDraft();
|
||||||
draft.setUsername(uuid);
|
draft.setUsername(uuid);
|
||||||
draft.setEmail(uuid + "@test.com");
|
draft.setEmail(uuid + "@test.com");
|
||||||
draft.setPassword("test");
|
draft.setPassword("test");
|
||||||
@ -37,57 +37,57 @@ public class RecipeRepositoryTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void findsAllPublicRecipes() {
|
public void findsAllPublicRecipes() {
|
||||||
final Recipe publicRecipe = new Recipe();
|
final RecipeEntity publicRecipe = new RecipeEntity();
|
||||||
publicRecipe.setCreated(OffsetDateTime.now());
|
publicRecipe.setCreated(OffsetDateTime.now());
|
||||||
publicRecipe.setSlug(UUID.randomUUID().toString());
|
publicRecipe.setSlug(UUID.randomUUID().toString());
|
||||||
publicRecipe.setIsPublic(true);
|
publicRecipe.setPublic(true);
|
||||||
publicRecipe.setOwner(this.seedUser());
|
publicRecipe.setOwner(this.seedUser());
|
||||||
publicRecipe.setTitle("Public Recipe");
|
publicRecipe.setTitle("Public Recipe");
|
||||||
publicRecipe.setRawText("Hello, World!");
|
publicRecipe.setRawText("Hello, World!");
|
||||||
final Recipe savedRecipe = this.recipeRepository.save(publicRecipe);
|
final RecipeEntity savedRecipe = this.recipeRepository.save(publicRecipe);
|
||||||
|
|
||||||
final List<Recipe> publicRecipes = this.recipeRepository.findAllByIsPublicIsTrue();
|
final List<RecipeEntity> publicRecipes = this.recipeRepository.findAllByIsPublicIsTrue();
|
||||||
assertThat(publicRecipes).anyMatch(recipeEntity -> recipeEntity.getId().equals(savedRecipe.getId()));
|
assertThat(publicRecipes).anyMatch(recipeEntity -> recipeEntity.getId().equals(savedRecipe.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void doesNotFindNonPublicRecipe() {
|
public void doesNotFindNonPublicRecipe() {
|
||||||
final Recipe nonPublicRecipe = new Recipe();
|
final RecipeEntity nonPublicRecipe = new RecipeEntity();
|
||||||
nonPublicRecipe.setCreated(OffsetDateTime.now());
|
nonPublicRecipe.setCreated(OffsetDateTime.now());
|
||||||
nonPublicRecipe.setSlug(UUID.randomUUID().toString());
|
nonPublicRecipe.setSlug(UUID.randomUUID().toString());
|
||||||
nonPublicRecipe.setOwner(this.seedUser());
|
nonPublicRecipe.setOwner(this.seedUser());
|
||||||
nonPublicRecipe.setTitle("Non-Public Recipe");
|
nonPublicRecipe.setTitle("Non-Public Recipe");
|
||||||
nonPublicRecipe.setRawText("Hello, World!");
|
nonPublicRecipe.setRawText("Hello, World!");
|
||||||
final Recipe savedRecipe = this.recipeRepository.save(nonPublicRecipe);
|
final RecipeEntity savedRecipe = this.recipeRepository.save(nonPublicRecipe);
|
||||||
|
|
||||||
final List<Recipe> publicRecipes = this.recipeRepository.findAllByIsPublicIsTrue();
|
final List<RecipeEntity> publicRecipes = this.recipeRepository.findAllByIsPublicIsTrue();
|
||||||
assertThat(publicRecipes).noneMatch(recipeEntity -> recipeEntity.getId().equals(savedRecipe.getId()));
|
assertThat(publicRecipes).noneMatch(recipeEntity -> recipeEntity.getId().equals(savedRecipe.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void findsAllForViewer() {
|
public void findsAllForViewer() {
|
||||||
final Recipe recipe = new Recipe();
|
final RecipeEntity recipe = new RecipeEntity();
|
||||||
recipe.setCreated(OffsetDateTime.now());
|
recipe.setCreated(OffsetDateTime.now());
|
||||||
recipe.setSlug(UUID.randomUUID().toString());
|
recipe.setSlug(UUID.randomUUID().toString());
|
||||||
recipe.setOwner(this.seedUser());
|
recipe.setOwner(this.seedUser());
|
||||||
recipe.setTitle("Test Recipe");
|
recipe.setTitle("Test Recipe");
|
||||||
recipe.setRawText("Hello, World!");
|
recipe.setRawText("Hello, World!");
|
||||||
final Recipe saved = this.recipeRepository.save(recipe);
|
final RecipeEntity saved = this.recipeRepository.save(recipe);
|
||||||
|
|
||||||
final User viewer = this.seedUser();
|
final UserEntity viewer = this.seedUser();
|
||||||
final Set<User> viewers = new HashSet<>(recipe.getViewers());
|
final Set<UserEntity> viewers = new HashSet<>(recipe.getViewerEntities());
|
||||||
viewers.add(viewer);
|
viewers.add(viewer);
|
||||||
saved.setViewers(viewers);
|
saved.setViewers(viewers);
|
||||||
|
|
||||||
this.recipeRepository.save(saved);
|
this.recipeRepository.save(saved);
|
||||||
|
|
||||||
final List<Recipe> viewable = this.recipeRepository.findAllByViewersContaining(viewer);
|
final List<RecipeEntity> viewable = this.recipeRepository.findAllByViewersContaining(viewer);
|
||||||
assertThat(viewable.size()).isEqualTo(1);
|
assertThat(viewable.size()).isEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void doesNotIncludeNonViewable() {
|
public void doesNotIncludeNonViewable() {
|
||||||
final Recipe recipe = new Recipe();
|
final RecipeEntity recipe = new RecipeEntity();
|
||||||
recipe.setCreated(OffsetDateTime.now());
|
recipe.setCreated(OffsetDateTime.now());
|
||||||
recipe.setSlug(UUID.randomUUID().toString());
|
recipe.setSlug(UUID.randomUUID().toString());
|
||||||
recipe.setOwner(this.seedUser());
|
recipe.setOwner(this.seedUser());
|
||||||
@ -95,8 +95,8 @@ public class RecipeRepositoryTests {
|
|||||||
recipe.setRawText("Hello, World!");
|
recipe.setRawText("Hello, World!");
|
||||||
this.recipeRepository.save(recipe);
|
this.recipeRepository.save(recipe);
|
||||||
|
|
||||||
final User viewer = this.seedUser();
|
final UserEntity viewer = this.seedUser();
|
||||||
final List<Recipe> viewable = this.recipeRepository.findAllByViewersContaining(viewer);
|
final List<RecipeEntity> viewable = this.recipeRepository.findAllByViewersContaining(viewer);
|
||||||
assertThat(viewable.size()).isEqualTo(0);
|
assertThat(viewable.size()).isEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import app.mealsmadeeasy.api.recipe.star.RecipeStar;
|
|||||||
import app.mealsmadeeasy.api.recipe.star.RecipeStarService;
|
import app.mealsmadeeasy.api.recipe.star.RecipeStarService;
|
||||||
import app.mealsmadeeasy.api.recipe.view.RecipeInfoView;
|
import app.mealsmadeeasy.api.recipe.view.RecipeInfoView;
|
||||||
import app.mealsmadeeasy.api.user.User;
|
import app.mealsmadeeasy.api.user.User;
|
||||||
|
import app.mealsmadeeasy.api.user.UserEntity;
|
||||||
import app.mealsmadeeasy.api.user.UserRepository;
|
import app.mealsmadeeasy.api.user.UserRepository;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -44,9 +45,9 @@ public class RecipeServiceTests {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
|
|
||||||
private User seedUser() {
|
private UserEntity seedUser() {
|
||||||
final String uuid = UUID.randomUUID().toString();
|
final String uuid = UUID.randomUUID().toString();
|
||||||
final User draft = User.getDefaultDraft();
|
final UserEntity draft = UserEntity.getDefaultDraft();
|
||||||
draft.setUsername(uuid);
|
draft.setUsername(uuid);
|
||||||
draft.setEmail(uuid + "@test.com");
|
draft.setEmail(uuid + "@test.com");
|
||||||
draft.setPassword("test");
|
draft.setPassword("test");
|
||||||
@ -99,7 +100,7 @@ public class RecipeServiceTests {
|
|||||||
assertThat(byId.getSlug(), is(recipe.getSlug()));
|
assertThat(byId.getSlug(), is(recipe.getSlug()));
|
||||||
assertThat(byId.getTitle(), is("My Recipe"));
|
assertThat(byId.getTitle(), is("My Recipe"));
|
||||||
assertThat(byId.getRawText(), is("Hello!"));
|
assertThat(byId.getRawText(), is("Hello!"));
|
||||||
assertThat(byId.getIsPublic(), is(true));
|
assertThat(byId.isPublic(), is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
package app.mealsmadeeasy.api.recipe.star;
|
package app.mealsmadeeasy.api.recipe.star;
|
||||||
|
|
||||||
import app.mealsmadeeasy.api.IntegrationTestsExtension;
|
import app.mealsmadeeasy.api.IntegrationTestsExtension;
|
||||||
import app.mealsmadeeasy.api.recipe.Recipe;
|
import app.mealsmadeeasy.api.recipe.RecipeEntity;
|
||||||
import app.mealsmadeeasy.api.recipe.RecipeRepository;
|
import app.mealsmadeeasy.api.recipe.RecipeRepository;
|
||||||
import app.mealsmadeeasy.api.user.User;
|
import app.mealsmadeeasy.api.user.UserEntity;
|
||||||
import app.mealsmadeeasy.api.user.UserRepository;
|
import app.mealsmadeeasy.api.user.UserRepository;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
@ -29,8 +29,8 @@ public class RecipeStarRepositoryTests {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
|
|
||||||
private User seedUser() {
|
private UserEntity seedUser() {
|
||||||
final User draft = User.getDefaultDraft();
|
final UserEntity draft = UserEntity.getDefaultDraft();
|
||||||
final String uuid = UUID.randomUUID().toString();
|
final String uuid = UUID.randomUUID().toString();
|
||||||
draft.setUsername(uuid);
|
draft.setUsername(uuid);
|
||||||
draft.setEmail(uuid + "@test.com");
|
draft.setEmail(uuid + "@test.com");
|
||||||
@ -38,8 +38,8 @@ public class RecipeStarRepositoryTests {
|
|||||||
return this.userRepository.save(draft);
|
return this.userRepository.save(draft);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Recipe getTestRecipe(User owner) {
|
private RecipeEntity getTestRecipe(UserEntity owner) {
|
||||||
final Recipe recipeDraft = new Recipe();
|
final RecipeEntity recipeDraft = new RecipeEntity();
|
||||||
recipeDraft.setCreated(OffsetDateTime.now());
|
recipeDraft.setCreated(OffsetDateTime.now());
|
||||||
recipeDraft.setSlug(UUID.randomUUID().toString());
|
recipeDraft.setSlug(UUID.randomUUID().toString());
|
||||||
recipeDraft.setOwner(owner);
|
recipeDraft.setOwner(owner);
|
||||||
@ -50,10 +50,10 @@ public class RecipeStarRepositoryTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void returnsTrueIfStarer() {
|
public void returnsTrueIfStarer() {
|
||||||
final User owner = this.seedUser();
|
final UserEntity owner = this.seedUser();
|
||||||
final Recipe recipe = this.getTestRecipe(owner);
|
final RecipeEntity recipe = this.getTestRecipe(owner);
|
||||||
|
|
||||||
final RecipeStar starDraft = new RecipeStar();
|
final RecipeStarEntity starDraft = new RecipeStarEntity();
|
||||||
final RecipeStarId starId = new RecipeStarId();
|
final RecipeStarId starId = new RecipeStarId();
|
||||||
starId.setRecipeId(recipe.getId());
|
starId.setRecipeId(recipe.getId());
|
||||||
starId.getOwnerId(owner.getId());
|
starId.getOwnerId(owner.getId());
|
||||||
@ -72,8 +72,8 @@ public class RecipeStarRepositoryTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void returnsFalseIfNotStarer() {
|
public void returnsFalseIfNotStarer() {
|
||||||
final User owner = this.seedUser();
|
final UserEntity owner = this.seedUser();
|
||||||
final Recipe recipe = this.getTestRecipe(owner);
|
final RecipeEntity recipe = this.getTestRecipe(owner);
|
||||||
assertThat(
|
assertThat(
|
||||||
this.recipeStarRepository.isStarer(
|
this.recipeStarRepository.isStarer(
|
||||||
recipe.getOwner().getUsername(),
|
recipe.getOwner().getUsername(),
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package app.mealsmadeeasy.api;
|
package app.mealsmadeeasy.api;
|
||||||
|
|
||||||
import app.mealsmadeeasy.api.recipe.Recipe;
|
|
||||||
import app.mealsmadeeasy.api.recipe.RecipeEmbeddingEntity;
|
import app.mealsmadeeasy.api.recipe.RecipeEmbeddingEntity;
|
||||||
|
import app.mealsmadeeasy.api.recipe.RecipeEntity;
|
||||||
import app.mealsmadeeasy.api.recipe.RecipeRepository;
|
import app.mealsmadeeasy.api.recipe.RecipeRepository;
|
||||||
import app.mealsmadeeasy.api.recipe.RecipeService;
|
import app.mealsmadeeasy.api.recipe.RecipeService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -37,20 +37,20 @@ public class BackfillRecipeEmbeddings implements ApplicationRunner {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(ApplicationArguments args) {
|
public void run(ApplicationArguments args) {
|
||||||
final List<Recipe> recipeEntities = this.recipeRepository.findAllByEmbeddingIsNull();
|
final List<RecipeEntity> recipeEntities = this.recipeRepository.findAllByEmbeddingIsNull();
|
||||||
for (final Recipe recipe : recipeEntities) {
|
for (final RecipeEntity recipeEntity : recipeEntities) {
|
||||||
logger.info("Calculating embedding for {}", recipe);
|
logger.info("Calculating embedding for {}", recipeEntity);
|
||||||
final String renderedMarkdown = this.recipeService.getRenderedMarkdown(recipe);
|
final String renderedMarkdown = this.recipeService.getRenderedMarkdown(recipeEntity);
|
||||||
final String toEmbed = "<h1>" + recipe.getTitle() + "</h1>" + renderedMarkdown;
|
final String toEmbed = "<h1>" + recipeEntity.getTitle() + "</h1>" + renderedMarkdown;
|
||||||
final float[] embedding = this.embeddingModel.embed(toEmbed);
|
final float[] embedding = this.embeddingModel.embed(toEmbed);
|
||||||
|
|
||||||
final RecipeEmbeddingEntity recipeEmbedding = new RecipeEmbeddingEntity();
|
final RecipeEmbeddingEntity recipeEmbedding = new RecipeEmbeddingEntity();
|
||||||
recipeEmbedding.setRecipe(recipe);
|
recipeEmbedding.setRecipe(recipeEntity);
|
||||||
recipeEmbedding.setEmbedding(embedding);
|
recipeEmbedding.setEmbedding(embedding);
|
||||||
recipeEmbedding.setTimestamp(OffsetDateTime.now());
|
recipeEmbedding.setTimestamp(OffsetDateTime.now());
|
||||||
recipe.setEmbedding(recipeEmbedding);
|
recipeEntity.setEmbedding(recipeEmbedding);
|
||||||
|
|
||||||
this.recipeRepository.save(recipe);
|
this.recipeRepository.save(recipeEntity);
|
||||||
}
|
}
|
||||||
this.recipeRepository.flush();
|
this.recipeRepository.flush();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package app.mealsmadeeasy.api.auth;
|
package app.mealsmadeeasy.api.auth;
|
||||||
|
|
||||||
import app.mealsmadeeasy.api.jwt.JwtService;
|
import app.mealsmadeeasy.api.jwt.JwtService;
|
||||||
import app.mealsmadeeasy.api.user.User;
|
import app.mealsmadeeasy.api.user.UserEntity;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@ -36,7 +36,7 @@ public class AuthServiceImpl implements AuthService {
|
|||||||
this.refreshTokenLifetime = refreshTokenLifetime;
|
this.refreshTokenLifetime = refreshTokenLifetime;
|
||||||
}
|
}
|
||||||
|
|
||||||
private RefreshToken createRefreshToken(User principal) {
|
private RefreshToken createRefreshToken(UserEntity principal) {
|
||||||
final RefreshTokenEntity refreshTokenDraft = new RefreshTokenEntity();
|
final RefreshTokenEntity refreshTokenDraft = new RefreshTokenEntity();
|
||||||
refreshTokenDraft.setToken(UUID.randomUUID());
|
refreshTokenDraft.setToken(UUID.randomUUID());
|
||||||
refreshTokenDraft.setIssued(OffsetDateTime.now());
|
refreshTokenDraft.setIssued(OffsetDateTime.now());
|
||||||
@ -51,7 +51,7 @@ public class AuthServiceImpl implements AuthService {
|
|||||||
final Authentication authentication = this.authenticationManager.authenticate(
|
final Authentication authentication = this.authenticationManager.authenticate(
|
||||||
new UsernamePasswordAuthenticationToken(username, password)
|
new UsernamePasswordAuthenticationToken(username, password)
|
||||||
);
|
);
|
||||||
final User principal = (User) authentication.getPrincipal();
|
final UserEntity principal = (UserEntity) authentication.getPrincipal();
|
||||||
return new LoginDetails(
|
return new LoginDetails(
|
||||||
username,
|
username,
|
||||||
this.jwtService.generateAccessToken(username),
|
this.jwtService.generateAccessToken(username),
|
||||||
@ -87,7 +87,7 @@ public class AuthServiceImpl implements AuthService {
|
|||||||
throw new LoginException(LoginExceptionReason.EXPIRED_REFRESH_TOKEN, "Refresh token is expired.");
|
throw new LoginException(LoginExceptionReason.EXPIRED_REFRESH_TOKEN, "Refresh token is expired.");
|
||||||
}
|
}
|
||||||
|
|
||||||
final User principal = old.getOwner();
|
final UserEntity principal = old.getOwner();
|
||||||
old.setDeleted(true);
|
old.setDeleted(true);
|
||||||
this.refreshTokenRepository.save(old);
|
this.refreshTokenRepository.save(old);
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
package app.mealsmadeeasy.api.auth;
|
package app.mealsmadeeasy.api.auth;
|
||||||
|
|
||||||
import app.mealsmadeeasy.api.user.User;
|
import app.mealsmadeeasy.api.user.UserEntity;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
@ -23,7 +23,7 @@ public class RefreshTokenEntity implements RefreshToken {
|
|||||||
|
|
||||||
@ManyToOne(optional = false)
|
@ManyToOne(optional = false)
|
||||||
@JoinColumn(name = "owner_id", nullable = false)
|
@JoinColumn(name = "owner_id", nullable = false)
|
||||||
private User owner;
|
private UserEntity owner;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Boolean deleted = false;
|
private Boolean deleted = false;
|
||||||
@ -67,11 +67,11 @@ public class RefreshTokenEntity implements RefreshToken {
|
|||||||
this.revoked = revoked;
|
this.revoked = revoked;
|
||||||
}
|
}
|
||||||
|
|
||||||
public User getOwner() {
|
public UserEntity getOwner() {
|
||||||
return this.owner;
|
return this.owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOwner(User owner) {
|
public void setOwner(UserEntity owner) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package app.mealsmadeeasy.api.image;
|
package app.mealsmadeeasy.api.image;
|
||||||
|
|
||||||
import app.mealsmadeeasy.api.user.User;
|
import app.mealsmadeeasy.api.user.User;
|
||||||
|
import app.mealsmadeeasy.api.user.UserEntity;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ public class S3ImageEntity implements Image {
|
|||||||
|
|
||||||
@ManyToOne(optional = false)
|
@ManyToOne(optional = false)
|
||||||
@JoinColumn(name = "owner_id", nullable = false)
|
@JoinColumn(name = "owner_id", nullable = false)
|
||||||
private User owner;
|
private UserEntity owner;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Boolean isPublic = false;
|
private Boolean isPublic = false;
|
||||||
@ -52,7 +53,7 @@ public class S3ImageEntity implements Image {
|
|||||||
joinColumns = @JoinColumn(name = "image_id"),
|
joinColumns = @JoinColumn(name = "image_id"),
|
||||||
inverseJoinColumns = @JoinColumn(name = "viewer_id")
|
inverseJoinColumns = @JoinColumn(name = "viewer_id")
|
||||||
)
|
)
|
||||||
private Set<User> viewers = new HashSet<>();
|
private Set<UserEntity> viewers = new HashSet<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer getId() {
|
public Integer getId() {
|
||||||
@ -148,7 +149,7 @@ public class S3ImageEntity implements Image {
|
|||||||
return this.owner;
|
return this.owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOwner(User owner) {
|
public void setOwner(UserEntity owner) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,11 +167,11 @@ public class S3ImageEntity implements Image {
|
|||||||
return Set.copyOf(this.viewers);
|
return Set.copyOf(this.viewers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<User> getViewerEntities() {
|
public Set<UserEntity> getViewerEntities() {
|
||||||
return this.viewers;
|
return this.viewers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setViewers(Set<User> viewers) {
|
public void setViewers(Set<UserEntity> viewers) {
|
||||||
this.viewers = viewers;
|
this.viewers = viewers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
package app.mealsmadeeasy.api.image;
|
package app.mealsmadeeasy.api.image;
|
||||||
|
|
||||||
import app.mealsmadeeasy.api.user.User;
|
import app.mealsmadeeasy.api.user.UserEntity;
|
||||||
import org.springframework.data.jpa.repository.EntityGraph;
|
import org.springframework.data.jpa.repository.EntityGraph;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
@ -14,8 +14,8 @@ public interface S3ImageRepository extends JpaRepository<S3ImageEntity, Long> {
|
|||||||
@EntityGraph(attributePaths = { "viewers" })
|
@EntityGraph(attributePaths = { "viewers" })
|
||||||
S3ImageEntity getByIdWithViewers(long id);
|
S3ImageEntity getByIdWithViewers(long id);
|
||||||
|
|
||||||
List<S3ImageEntity> findAllByOwner(User owner);
|
List<S3ImageEntity> findAllByOwner(UserEntity owner);
|
||||||
Optional<S3ImageEntity> findByOwnerAndUserFilename(User owner, String filename);
|
Optional<S3ImageEntity> findByOwnerAndUserFilename(UserEntity owner, String filename);
|
||||||
|
|
||||||
@Query("SELECT image from Image image WHERE image.owner.username = ?1 AND image.userFilename = ?2")
|
@Query("SELECT image from Image image WHERE image.owner.username = ?1 AND image.userFilename = ?2")
|
||||||
Optional<S3ImageEntity> findByOwnerUsernameAndFilename(String username, String filename);
|
Optional<S3ImageEntity> findByOwnerUsernameAndFilename(String username, String filename);
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import app.mealsmadeeasy.api.image.spec.ImageUpdateInfoSpec;
|
|||||||
import app.mealsmadeeasy.api.image.view.ImageView;
|
import app.mealsmadeeasy.api.image.view.ImageView;
|
||||||
import app.mealsmadeeasy.api.s3.S3Manager;
|
import app.mealsmadeeasy.api.s3.S3Manager;
|
||||||
import app.mealsmadeeasy.api.user.User;
|
import app.mealsmadeeasy.api.user.User;
|
||||||
|
import app.mealsmadeeasy.api.user.UserEntity;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.security.access.prepost.PostAuthorize;
|
import org.springframework.security.access.prepost.PostAuthorize;
|
||||||
@ -94,9 +95,9 @@ public class S3ImageService implements ImageService {
|
|||||||
}
|
}
|
||||||
final @Nullable Set<User> viewersToAdd = spec.getViewersToAdd();
|
final @Nullable Set<User> viewersToAdd = spec.getViewersToAdd();
|
||||||
if (viewersToAdd != null) {
|
if (viewersToAdd != null) {
|
||||||
final Set<User> viewers = new HashSet<>(entity.getViewerEntities());
|
final Set<UserEntity> viewers = new HashSet<>(entity.getViewerEntities());
|
||||||
for (final User viewerToAdd : spec.getViewersToAdd()) {
|
for (final User viewerToAdd : spec.getViewersToAdd()) {
|
||||||
viewers.add((User) viewerToAdd);
|
viewers.add((UserEntity) viewerToAdd);
|
||||||
}
|
}
|
||||||
entity.setViewers(viewers);
|
entity.setViewers(viewers);
|
||||||
didTransfer = true;
|
didTransfer = true;
|
||||||
@ -153,7 +154,7 @@ public class S3ImageService implements ImageService {
|
|||||||
inputStream.close();
|
inputStream.close();
|
||||||
|
|
||||||
final S3ImageEntity draft = new S3ImageEntity();
|
final S3ImageEntity draft = new S3ImageEntity();
|
||||||
draft.setOwner((User) owner);
|
draft.setOwner((UserEntity) owner);
|
||||||
draft.setUserFilename(userFilename);
|
draft.setUserFilename(userFilename);
|
||||||
draft.setMimeType(mimeType);
|
draft.setMimeType(mimeType);
|
||||||
draft.setObjectName(objectName);
|
draft.setObjectName(objectName);
|
||||||
@ -174,7 +175,7 @@ public class S3ImageService implements ImageService {
|
|||||||
@Override
|
@Override
|
||||||
@PostAuthorize("@imageSecurity.isViewableBy(returnObject, #viewer)")
|
@PostAuthorize("@imageSecurity.isViewableBy(returnObject, #viewer)")
|
||||||
public Image getByOwnerAndFilename(User owner, String filename, User viewer) throws ImageException {
|
public Image getByOwnerAndFilename(User owner, String filename, User viewer) throws ImageException {
|
||||||
return this.imageRepository.findByOwnerAndUserFilename((User) owner, filename)
|
return this.imageRepository.findByOwnerAndUserFilename((UserEntity) owner, filename)
|
||||||
.orElseThrow(() -> new ImageException(
|
.orElseThrow(() -> new ImageException(
|
||||||
ImageException.Type.IMAGE_NOT_FOUND,
|
ImageException.Type.IMAGE_NOT_FOUND,
|
||||||
"No such image for owner " + owner + " with filename " + filename
|
"No such image for owner " + owner + " with filename " + filename
|
||||||
@ -200,7 +201,7 @@ public class S3ImageService implements ImageService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Image> getImagesOwnedBy(User user) {
|
public List<Image> getImagesOwnedBy(User user) {
|
||||||
return new ArrayList<>(this.imageRepository.findAllByOwner((User) user));
|
return new ArrayList<>(this.imageRepository.findAllByOwner((UserEntity) user));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -215,9 +216,9 @@ public class S3ImageService implements ImageService {
|
|||||||
} else {
|
} else {
|
||||||
final @Nullable Set<User> viewersToRemove = updateSpec.getViewersToRemove();
|
final @Nullable Set<User> viewersToRemove = updateSpec.getViewersToRemove();
|
||||||
if (viewersToRemove != null) {
|
if (viewersToRemove != null) {
|
||||||
final Set<User> currentViewers = new HashSet<>(entity.getViewerEntities());
|
final Set<UserEntity> currentViewers = new HashSet<>(entity.getViewerEntities());
|
||||||
for (final User toRemove : updateSpec.getViewersToRemove()) {
|
for (final User toRemove : updateSpec.getViewersToRemove()) {
|
||||||
currentViewers.remove((User) toRemove);
|
currentViewers.remove((UserEntity) toRemove);
|
||||||
}
|
}
|
||||||
entity.setViewers(currentViewers);
|
entity.setViewers(currentViewers);
|
||||||
didUpdate = true;
|
didUpdate = true;
|
||||||
|
|||||||
@ -1,83 +1,28 @@
|
|||||||
package app.mealsmadeeasy.api.recipe;
|
package app.mealsmadeeasy.api.recipe;
|
||||||
|
|
||||||
import app.mealsmadeeasy.api.image.S3ImageEntity;
|
import app.mealsmadeeasy.api.image.Image;
|
||||||
import app.mealsmadeeasy.api.recipe.comment.RecipeComment;
|
import app.mealsmadeeasy.api.recipe.comment.RecipeComment;
|
||||||
import app.mealsmadeeasy.api.recipe.star.RecipeStar;
|
import app.mealsmadeeasy.api.recipe.star.RecipeStar;
|
||||||
import app.mealsmadeeasy.api.user.User;
|
import app.mealsmadeeasy.api.user.User;
|
||||||
import jakarta.persistence.*;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@Entity(name = "Recipe")
|
public interface Recipe {
|
||||||
@Data
|
Integer getId();
|
||||||
public final class Recipe {
|
OffsetDateTime getCreated();
|
||||||
|
@Nullable OffsetDateTime getModified();
|
||||||
@Id
|
String getSlug();
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
String getTitle();
|
||||||
@Column(nullable = false, updatable = false)
|
@Nullable Integer getPreparationTime();
|
||||||
private Integer id;
|
@Nullable Integer getCookingTime();
|
||||||
|
@Nullable Integer getTotalTime();
|
||||||
@Column(nullable = false)
|
String getRawText();
|
||||||
private OffsetDateTime created;
|
User getOwner();
|
||||||
|
Set<RecipeStar> getStars();
|
||||||
private OffsetDateTime modified;
|
boolean isPublic();
|
||||||
|
Set<User> getViewers();
|
||||||
@Column(nullable = false, unique = true)
|
Set<RecipeComment> getComments();
|
||||||
private String slug;
|
@Nullable Image getMainImage();
|
||||||
|
|
||||||
@Column(nullable = false)
|
|
||||||
private String title;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Integer preparationTime;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Integer cookingTime;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Integer totalTime;
|
|
||||||
|
|
||||||
@Lob
|
|
||||||
@Column(name = "raw_text", columnDefinition = "TEXT", nullable = false)
|
|
||||||
@Basic(fetch = FetchType.LAZY)
|
|
||||||
private String rawText;
|
|
||||||
|
|
||||||
@Lob
|
|
||||||
@Column(name = "cached_rendered_text", columnDefinition = "TEXT")
|
|
||||||
@Basic(fetch = FetchType.LAZY)
|
|
||||||
private String cachedRenderedText;
|
|
||||||
|
|
||||||
@ManyToOne(optional = false)
|
|
||||||
@JoinColumn(name = "owner_id", nullable = false)
|
|
||||||
private User owner;
|
|
||||||
|
|
||||||
@OneToMany
|
|
||||||
@JoinColumn(name = "recipe_id")
|
|
||||||
private Set<RecipeStar> stars = new HashSet<>();
|
|
||||||
|
|
||||||
@OneToMany(mappedBy = "recipe")
|
|
||||||
private Set<RecipeComment> comments = new HashSet<>();
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
|
||||||
private Boolean isPublic = false;
|
|
||||||
|
|
||||||
@ManyToMany
|
|
||||||
@JoinTable(
|
|
||||||
name = "recipe_viewer",
|
|
||||||
joinColumns = @JoinColumn(name = "recipe_id"),
|
|
||||||
inverseJoinColumns = @JoinColumn(name = "viewer_id")
|
|
||||||
)
|
|
||||||
private Set<User> viewers = new HashSet<>();
|
|
||||||
|
|
||||||
@ManyToOne
|
|
||||||
@JoinColumn(name = "main_image_id")
|
|
||||||
private S3ImageEntity mainImage;
|
|
||||||
|
|
||||||
@OneToOne(mappedBy = "recipe", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
|
|
||||||
private RecipeEmbeddingEntity embedding;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ public class RecipeEmbeddingEntity {
|
|||||||
@OneToOne(fetch = FetchType.LAZY, optional = false)
|
@OneToOne(fetch = FetchType.LAZY, optional = false)
|
||||||
@MapsId
|
@MapsId
|
||||||
@JoinColumn(name = "recipe_id")
|
@JoinColumn(name = "recipe_id")
|
||||||
private Recipe recipe;
|
private RecipeEntity recipe;
|
||||||
|
|
||||||
@JdbcTypeCode(SqlTypes.VECTOR)
|
@JdbcTypeCode(SqlTypes.VECTOR)
|
||||||
@Array(length = 1024)
|
@Array(length = 1024)
|
||||||
@ -36,11 +36,11 @@ public class RecipeEmbeddingEntity {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Recipe getRecipe() {
|
public RecipeEntity getRecipe() {
|
||||||
return this.recipe;
|
return this.recipe;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRecipe(Recipe recipe) {
|
public void setRecipe(RecipeEntity recipe) {
|
||||||
this.recipe = recipe;
|
this.recipe = recipe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
252
src/main/java/app/mealsmadeeasy/api/recipe/RecipeEntity.java
Normal file
252
src/main/java/app/mealsmadeeasy/api/recipe/RecipeEntity.java
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
package app.mealsmadeeasy.api.recipe;
|
||||||
|
|
||||||
|
import app.mealsmadeeasy.api.image.S3ImageEntity;
|
||||||
|
import app.mealsmadeeasy.api.recipe.comment.RecipeComment;
|
||||||
|
import app.mealsmadeeasy.api.recipe.comment.RecipeCommentEntity;
|
||||||
|
import app.mealsmadeeasy.api.recipe.star.RecipeStar;
|
||||||
|
import app.mealsmadeeasy.api.recipe.star.RecipeStarEntity;
|
||||||
|
import app.mealsmadeeasy.api.user.User;
|
||||||
|
import app.mealsmadeeasy.api.user.UserEntity;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Entity(name = "Recipe")
|
||||||
|
public final class RecipeEntity implements Recipe {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(nullable = false, updatable = false)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private OffsetDateTime created;
|
||||||
|
|
||||||
|
private OffsetDateTime modified;
|
||||||
|
|
||||||
|
@Column(nullable = false, unique = true)
|
||||||
|
private String slug;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Integer preparationTime;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Integer cookingTime;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Integer totalTime;
|
||||||
|
|
||||||
|
@Lob
|
||||||
|
@Column(name = "raw_text", columnDefinition = "TEXT", nullable = false)
|
||||||
|
@Basic(fetch = FetchType.LAZY)
|
||||||
|
private String rawText;
|
||||||
|
|
||||||
|
@Lob
|
||||||
|
@Column(name = "cached_rendered_text", columnDefinition = "TEXT")
|
||||||
|
@Basic(fetch = FetchType.LAZY)
|
||||||
|
private String cachedRenderedText;
|
||||||
|
|
||||||
|
@ManyToOne(optional = false)
|
||||||
|
@JoinColumn(name = "owner_id", nullable = false)
|
||||||
|
private UserEntity owner;
|
||||||
|
|
||||||
|
@OneToMany
|
||||||
|
@JoinColumn(name = "recipe_id")
|
||||||
|
private Set<RecipeStarEntity> stars = new HashSet<>();
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "recipe")
|
||||||
|
private Set<RecipeCommentEntity> comments = new HashSet<>();
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Boolean isPublic = false;
|
||||||
|
|
||||||
|
@ManyToMany
|
||||||
|
@JoinTable(
|
||||||
|
name = "recipe_viewer",
|
||||||
|
joinColumns = @JoinColumn(name = "recipe_id"),
|
||||||
|
inverseJoinColumns = @JoinColumn(name = "viewer_id")
|
||||||
|
)
|
||||||
|
private Set<UserEntity> viewers = new HashSet<>();
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "main_image_id")
|
||||||
|
private S3ImageEntity mainImage;
|
||||||
|
|
||||||
|
@OneToOne(mappedBy = "recipe", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
|
||||||
|
private RecipeEmbeddingEntity embedding;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OffsetDateTime getCreated() {
|
||||||
|
return this.created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreated(OffsetDateTime created) {
|
||||||
|
this.created = created;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable OffsetDateTime getModified() {
|
||||||
|
return this.modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModified(@Nullable OffsetDateTime modified) {
|
||||||
|
this.modified = modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSlug() {
|
||||||
|
return this.slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSlug(String slug) {
|
||||||
|
this.slug = slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return this.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Integer getPreparationTime() {
|
||||||
|
return this.preparationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreparationTime(@Nullable Integer preparationTime) {
|
||||||
|
this.preparationTime = preparationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Integer getCookingTime() {
|
||||||
|
return this.cookingTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookingTime(@Nullable Integer cookingTime) {
|
||||||
|
this.cookingTime = cookingTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Integer getTotalTime() {
|
||||||
|
return this.totalTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalTime(@Nullable Integer totalTime) {
|
||||||
|
this.totalTime = totalTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRawText() {
|
||||||
|
return this.rawText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRawText(String rawText) {
|
||||||
|
this.rawText = rawText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getCachedRenderedText() {
|
||||||
|
return this.cachedRenderedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCachedRenderedText(@Nullable 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<RecipeStar> getStars() {
|
||||||
|
return Set.copyOf(this.stars);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<RecipeStarEntity> getStarEntities() {
|
||||||
|
return this.stars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStarEntities(Set<RecipeStarEntity> starGazers) {
|
||||||
|
this.stars = 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 + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable S3ImageEntity getMainImage() {
|
||||||
|
return this.mainImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMainImage(@Nullable S3ImageEntity image) {
|
||||||
|
this.mainImage = image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecipeEmbeddingEntity getEmbedding() {
|
||||||
|
return this.embedding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmbedding(RecipeEmbeddingEntity embedding) {
|
||||||
|
this.embedding = embedding;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
package app.mealsmadeeasy.api.recipe;
|
package app.mealsmadeeasy.api.recipe;
|
||||||
|
|
||||||
import app.mealsmadeeasy.api.user.User;
|
import app.mealsmadeeasy.api.user.UserEntity;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.domain.Slice;
|
import org.springframework.data.domain.Slice;
|
||||||
import org.springframework.data.jpa.repository.EntityGraph;
|
import org.springframework.data.jpa.repository.EntityGraph;
|
||||||
@ -10,27 +10,27 @@ import org.springframework.data.jpa.repository.Query;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface RecipeRepository extends JpaRepository<Recipe, Long> {
|
public interface RecipeRepository extends JpaRepository<RecipeEntity, Long> {
|
||||||
|
|
||||||
List<Recipe> findAllByIsPublicIsTrue();
|
List<RecipeEntity> findAllByIsPublicIsTrue();
|
||||||
|
|
||||||
List<Recipe> findAllByViewersContaining(User viewer);
|
List<RecipeEntity> findAllByViewersContaining(UserEntity viewer);
|
||||||
|
|
||||||
List<Recipe> findAllByOwner(User owner);
|
List<RecipeEntity> findAllByOwner(UserEntity owner);
|
||||||
|
|
||||||
@Query("SELECT r from Recipe r WHERE r.owner.username = ?1 AND r.slug = ?2")
|
@Query("SELECT r from Recipe r WHERE r.owner.username = ?1 AND r.slug = ?2")
|
||||||
Optional<Recipe> findByOwnerUsernameAndSlug(String ownerUsername, String slug);
|
Optional<RecipeEntity> findByOwnerUsernameAndSlug(String ownerUsername, String slug);
|
||||||
|
|
||||||
@Query("SELECT r FROM Recipe r WHERE size(r.stars) >= ?1 AND (r.isPublic OR ?2 MEMBER OF r.viewers)")
|
@Query("SELECT r FROM Recipe r WHERE size(r.stars) >= ?1 AND (r.isPublic OR ?2 MEMBER OF r.viewers)")
|
||||||
List<Recipe> findAllViewableByStarsGreaterThanEqual(long stars, User viewer);
|
List<RecipeEntity> findAllViewableByStarsGreaterThanEqual(long stars, UserEntity viewer);
|
||||||
|
|
||||||
@Query("SELECT r FROM Recipe r WHERE r.id = ?1")
|
@Query("SELECT r FROM Recipe r WHERE r.id = ?1")
|
||||||
@EntityGraph(attributePaths = { "viewers" })
|
@EntityGraph(attributePaths = { "viewers" })
|
||||||
Optional<Recipe> findByIdWithViewers(long id);
|
Optional<RecipeEntity> findByIdWithViewers(long id);
|
||||||
|
|
||||||
@Query("SELECT r FROM Recipe r WHERE r.id = ?1")
|
@Query("SELECT r FROM Recipe r WHERE r.id = ?1")
|
||||||
@EntityGraph(attributePaths = { "stars" })
|
@EntityGraph(attributePaths = { "stars" })
|
||||||
Optional<Recipe> findByIdWithStars(long id);
|
Optional<RecipeEntity> findByIdWithStars(long id);
|
||||||
|
|
||||||
@Query("SELECT size(r.stars) FROM Recipe r WHERE r.id = ?1")
|
@Query("SELECT size(r.stars) FROM Recipe r WHERE r.id = ?1")
|
||||||
int getStarCount(long recipeId);
|
int getStarCount(long recipeId);
|
||||||
@ -39,9 +39,9 @@ public interface RecipeRepository extends JpaRepository<Recipe, Long> {
|
|||||||
int getViewerCount(long recipeId);
|
int getViewerCount(long recipeId);
|
||||||
|
|
||||||
@Query("SELECT r FROM Recipe r WHERE r.isPublic OR r.owner = ?1 OR ?1 MEMBER OF r.viewers")
|
@Query("SELECT r FROM Recipe r WHERE r.isPublic OR r.owner = ?1 OR ?1 MEMBER OF r.viewers")
|
||||||
Slice<Recipe> findAllViewableBy(User viewer, Pageable pageable);
|
Slice<RecipeEntity> findAllViewableBy(UserEntity viewer, Pageable pageable);
|
||||||
|
|
||||||
List<Recipe> findAllByEmbeddingIsNull();
|
List<RecipeEntity> findAllByEmbeddingIsNull();
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
nativeQuery = true,
|
nativeQuery = true,
|
||||||
@ -57,7 +57,7 @@ public interface RecipeRepository extends JpaRepository<Recipe, Long> {
|
|||||||
ORDER BY d.distance;
|
ORDER BY d.distance;
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
List<Recipe> searchByEmbeddingAndViewableBy(float[] queryEmbedding, float similarity, Integer viewerId);
|
List<RecipeEntity> searchByEmbeddingAndViewableBy(float[] queryEmbedding, float similarity, Integer viewerId);
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
nativeQuery = true,
|
nativeQuery = true,
|
||||||
@ -69,6 +69,6 @@ public interface RecipeRepository extends JpaRepository<Recipe, Long> {
|
|||||||
ORDER BY d.distance;
|
ORDER BY d.distance;
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
List<Recipe> searchByEmbeddingAndIsPublic(float[] queryEmbedding, float similarity);
|
List<RecipeEntity> searchByEmbeddingAndIsPublic(float[] queryEmbedding, float similarity);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,7 +42,7 @@ public class RecipeSecurityImpl implements RecipeSecurity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isViewableBy(Recipe recipe, @Nullable User user) throws RecipeException {
|
public boolean isViewableBy(Recipe recipe, @Nullable User user) throws RecipeException {
|
||||||
if (recipe.getIsPublic()) {
|
if (recipe.isPublic()) {
|
||||||
// public recipe
|
// public recipe
|
||||||
return true;
|
return true;
|
||||||
} else if (user == null) {
|
} else if (user == null) {
|
||||||
@ -53,7 +53,7 @@ public class RecipeSecurityImpl implements RecipeSecurity {
|
|||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// check if viewer
|
// check if viewer
|
||||||
final Recipe withViewers = this.recipeRepository.findByIdWithViewers(recipe.getId())
|
final RecipeEntity withViewers = this.recipeRepository.findByIdWithViewers(recipe.getId())
|
||||||
.orElseThrow(() -> new RecipeException(
|
.orElseThrow(() -> new RecipeException(
|
||||||
RecipeException.Type.INVALID_ID, "No such Recipe with id: " + recipe.getId()
|
RecipeException.Type.INVALID_ID, "No such Recipe with id: " + recipe.getId()
|
||||||
));
|
));
|
||||||
|
|||||||
@ -58,6 +58,6 @@ public interface RecipeService {
|
|||||||
@Nullable Boolean isOwner(String username, String slug, @Nullable User viewer);
|
@Nullable Boolean isOwner(String username, String slug, @Nullable User viewer);
|
||||||
|
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
String getRenderedMarkdown(Recipe entity);
|
String getRenderedMarkdown(RecipeEntity entity);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import app.mealsmadeeasy.api.recipe.star.RecipeStarRepository;
|
|||||||
import app.mealsmadeeasy.api.recipe.view.FullRecipeView;
|
import app.mealsmadeeasy.api.recipe.view.FullRecipeView;
|
||||||
import app.mealsmadeeasy.api.recipe.view.RecipeInfoView;
|
import app.mealsmadeeasy.api.recipe.view.RecipeInfoView;
|
||||||
import app.mealsmadeeasy.api.user.User;
|
import app.mealsmadeeasy.api.user.User;
|
||||||
|
import app.mealsmadeeasy.api.user.UserEntity;
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@ -57,18 +58,18 @@ public class RecipeServiceImpl implements RecipeService {
|
|||||||
if (owner == null) {
|
if (owner == null) {
|
||||||
throw new AccessDeniedException("Must be logged in.");
|
throw new AccessDeniedException("Must be logged in.");
|
||||||
}
|
}
|
||||||
final Recipe draft = new Recipe();
|
final RecipeEntity draft = new RecipeEntity();
|
||||||
draft.setCreated(OffsetDateTime.now());
|
draft.setCreated(OffsetDateTime.now());
|
||||||
draft.setOwner((User) owner);
|
draft.setOwner((UserEntity) owner);
|
||||||
draft.setSlug(spec.getSlug());
|
draft.setSlug(spec.getSlug());
|
||||||
draft.setTitle(spec.getTitle());
|
draft.setTitle(spec.getTitle());
|
||||||
draft.setRawText(spec.getRawText());
|
draft.setRawText(spec.getRawText());
|
||||||
draft.setMainImage((S3ImageEntity) spec.getMainImage());
|
draft.setMainImage((S3ImageEntity) spec.getMainImage());
|
||||||
draft.setIsPublic(spec.isPublic());
|
draft.setPublic(spec.isPublic());
|
||||||
return this.recipeRepository.save(draft);
|
return this.recipeRepository.save(draft);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Recipe findRecipeEntity(long id) throws RecipeException {
|
private RecipeEntity findRecipeEntity(long id) throws RecipeException {
|
||||||
return this.recipeRepository.findById(id).orElseThrow(() -> new RecipeException(
|
return this.recipeRepository.findById(id).orElseThrow(() -> new RecipeException(
|
||||||
RecipeException.Type.INVALID_ID, "No such Recipe with id: " + id
|
RecipeException.Type.INVALID_ID, "No such Recipe with id: " + id
|
||||||
));
|
));
|
||||||
@ -100,7 +101,7 @@ public class RecipeServiceImpl implements RecipeService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
public String getRenderedMarkdown(Recipe entity) {
|
public String getRenderedMarkdown(RecipeEntity entity) {
|
||||||
if (entity.getCachedRenderedText() == null) {
|
if (entity.getCachedRenderedText() == null) {
|
||||||
entity.setCachedRenderedText(this.markdownService.renderAndCleanMarkdown(entity.getRawText()));
|
entity.setCachedRenderedText(this.markdownService.renderAndCleanMarkdown(entity.getRawText()));
|
||||||
entity = this.recipeRepository.save(entity);
|
entity = this.recipeRepository.save(entity);
|
||||||
@ -125,7 +126,7 @@ public class RecipeServiceImpl implements RecipeService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private FullRecipeView getFullView(Recipe recipe, boolean includeRawText, @Nullable User viewer) {
|
private FullRecipeView getFullView(RecipeEntity recipe, boolean includeRawText, @Nullable User viewer) {
|
||||||
return FullRecipeView.from(
|
return FullRecipeView.from(
|
||||||
recipe,
|
recipe,
|
||||||
this.getRenderedMarkdown(recipe),
|
this.getRenderedMarkdown(recipe),
|
||||||
@ -136,7 +137,7 @@ public class RecipeServiceImpl implements RecipeService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private RecipeInfoView getInfoView(Recipe recipe, @Nullable User viewer) {
|
private RecipeInfoView getInfoView(RecipeEntity recipe, @Nullable User viewer) {
|
||||||
return RecipeInfoView.from(
|
return RecipeInfoView.from(
|
||||||
recipe,
|
recipe,
|
||||||
this.getStarCount(recipe),
|
this.getStarCount(recipe),
|
||||||
@ -147,7 +148,7 @@ public class RecipeServiceImpl implements RecipeService {
|
|||||||
@Override
|
@Override
|
||||||
@PreAuthorize("@recipeSecurity.isViewableBy(#id, #viewer)")
|
@PreAuthorize("@recipeSecurity.isViewableBy(#id, #viewer)")
|
||||||
public FullRecipeView getFullViewById(long id, @Nullable User viewer) throws RecipeException {
|
public FullRecipeView getFullViewById(long id, @Nullable User viewer) throws RecipeException {
|
||||||
final Recipe recipe = this.recipeRepository.findById(id).orElseThrow(() -> new RecipeException(
|
final RecipeEntity recipe = this.recipeRepository.findById(id).orElseThrow(() -> new RecipeException(
|
||||||
RecipeException.Type.INVALID_ID, "No such Recipe for id: " + id
|
RecipeException.Type.INVALID_ID, "No such Recipe for id: " + id
|
||||||
));
|
));
|
||||||
return this.getFullView(recipe, false, viewer);
|
return this.getFullView(recipe, false, viewer);
|
||||||
@ -161,7 +162,7 @@ public class RecipeServiceImpl implements RecipeService {
|
|||||||
boolean includeRawText,
|
boolean includeRawText,
|
||||||
@Nullable User viewer
|
@Nullable User viewer
|
||||||
) throws RecipeException {
|
) throws RecipeException {
|
||||||
final Recipe recipe = this.recipeRepository.findByOwnerUsernameAndSlug(username, slug)
|
final RecipeEntity recipe = this.recipeRepository.findByOwnerUsernameAndSlug(username, slug)
|
||||||
.orElseThrow(() -> new RecipeException(
|
.orElseThrow(() -> new RecipeException(
|
||||||
RecipeException.Type.INVALID_USERNAME_OR_SLUG,
|
RecipeException.Type.INVALID_USERNAME_OR_SLUG,
|
||||||
"No such Recipe for username " + username + " and slug: " + slug
|
"No such Recipe for username " + username + " and slug: " + slug
|
||||||
@ -171,7 +172,7 @@ public class RecipeServiceImpl implements RecipeService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Slice<RecipeInfoView> getInfoViewsViewableBy(Pageable pageable, @Nullable User viewer) {
|
public Slice<RecipeInfoView> getInfoViewsViewableBy(Pageable pageable, @Nullable User viewer) {
|
||||||
return this.recipeRepository.findAllViewableBy((User) viewer, pageable).map(recipe ->
|
return this.recipeRepository.findAllViewableBy((UserEntity) viewer, pageable).map(recipe ->
|
||||||
this.getInfoView(recipe, viewer)
|
this.getInfoView(recipe, viewer)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -179,7 +180,7 @@ public class RecipeServiceImpl implements RecipeService {
|
|||||||
@Override
|
@Override
|
||||||
public List<Recipe> getByMinimumStars(long minimumStars, User viewer) {
|
public List<Recipe> getByMinimumStars(long minimumStars, User viewer) {
|
||||||
return List.copyOf(
|
return List.copyOf(
|
||||||
this.recipeRepository.findAllViewableByStarsGreaterThanEqual(minimumStars, (User) viewer)
|
this.recipeRepository.findAllViewableByStarsGreaterThanEqual(minimumStars, (UserEntity) viewer)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,18 +191,18 @@ public class RecipeServiceImpl implements RecipeService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Recipe> getRecipesViewableBy(User viewer) {
|
public List<Recipe> getRecipesViewableBy(User viewer) {
|
||||||
return List.copyOf(this.recipeRepository.findAllByViewersContaining((User) viewer));
|
return List.copyOf(this.recipeRepository.findAllByViewersContaining((UserEntity) viewer));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Recipe> getRecipesOwnedBy(User owner) {
|
public List<Recipe> getRecipesOwnedBy(User owner) {
|
||||||
return List.copyOf(this.recipeRepository.findAllByOwner((User) owner));
|
return List.copyOf(this.recipeRepository.findAllByOwner((UserEntity) owner));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<RecipeInfoView> aiSearch(RecipeAiSearchSpec searchSpec, @Nullable User viewer) {
|
public List<RecipeInfoView> aiSearch(RecipeAiSearchSpec searchSpec, @Nullable User viewer) {
|
||||||
final float[] queryEmbedding = this.embeddingModel.embed(searchSpec.getPrompt());
|
final float[] queryEmbedding = this.embeddingModel.embed(searchSpec.getPrompt());
|
||||||
final List<Recipe> results;
|
final List<RecipeEntity> results;
|
||||||
if (viewer == null) {
|
if (viewer == null) {
|
||||||
results = this.recipeRepository.searchByEmbeddingAndIsPublic(queryEmbedding, 0.5f);
|
results = this.recipeRepository.searchByEmbeddingAndIsPublic(queryEmbedding, 0.5f);
|
||||||
} else {
|
} else {
|
||||||
@ -216,7 +217,7 @@ public class RecipeServiceImpl implements RecipeService {
|
|||||||
@PreAuthorize("@recipeSecurity.isOwner(#username, #slug, #modifier)")
|
@PreAuthorize("@recipeSecurity.isOwner(#username, #slug, #modifier)")
|
||||||
public Recipe update(String username, String slug, RecipeUpdateSpec spec, User modifier)
|
public Recipe update(String username, String slug, RecipeUpdateSpec spec, User modifier)
|
||||||
throws RecipeException, ImageException {
|
throws RecipeException, ImageException {
|
||||||
final Recipe recipe = this.recipeRepository.findByOwnerUsernameAndSlug(username, slug).orElseThrow(() ->
|
final RecipeEntity recipe = this.recipeRepository.findByOwnerUsernameAndSlug(username, slug).orElseThrow(() ->
|
||||||
new RecipeException(
|
new RecipeException(
|
||||||
RecipeException.Type.INVALID_USERNAME_OR_SLUG,
|
RecipeException.Type.INVALID_USERNAME_OR_SLUG,
|
||||||
"No such Recipe for username " + username + " and slug: " + slug
|
"No such Recipe for username " + username + " and slug: " + slug
|
||||||
@ -229,7 +230,7 @@ public class RecipeServiceImpl implements RecipeService {
|
|||||||
recipe.setTotalTime(spec.getTotalTime());
|
recipe.setTotalTime(spec.getTotalTime());
|
||||||
recipe.setRawText(spec.getRawText());
|
recipe.setRawText(spec.getRawText());
|
||||||
recipe.setCachedRenderedText(null);
|
recipe.setCachedRenderedText(null);
|
||||||
recipe.setIsPublic(spec.getIsPublic());
|
recipe.setPublic(spec.getIsPublic());
|
||||||
|
|
||||||
final S3ImageEntity mainImage;
|
final S3ImageEntity mainImage;
|
||||||
if (spec.getMainImage() == null) {
|
if (spec.getMainImage() == null) {
|
||||||
@ -250,11 +251,11 @@ public class RecipeServiceImpl implements RecipeService {
|
|||||||
@Override
|
@Override
|
||||||
@PreAuthorize("@recipeSecurity.isOwner(#id, #modifier)")
|
@PreAuthorize("@recipeSecurity.isOwner(#id, #modifier)")
|
||||||
public Recipe addViewer(long id, User modifier, User viewer) throws RecipeException {
|
public Recipe addViewer(long id, User modifier, User viewer) throws RecipeException {
|
||||||
final Recipe entity = this.recipeRepository.findByIdWithViewers(id).orElseThrow(() -> new RecipeException(
|
final RecipeEntity entity = this.recipeRepository.findByIdWithViewers(id).orElseThrow(() -> new RecipeException(
|
||||||
RecipeException.Type.INVALID_ID, "No such Recipe with id: " + id
|
RecipeException.Type.INVALID_ID, "No such Recipe with id: " + id
|
||||||
));
|
));
|
||||||
final Set<User> viewers = new HashSet<>(entity.getViewers());
|
final Set<UserEntity> viewers = new HashSet<>(entity.getViewerEntities());
|
||||||
viewers.add((User) viewer);
|
viewers.add((UserEntity) viewer);
|
||||||
entity.setViewers(viewers);
|
entity.setViewers(viewers);
|
||||||
return this.recipeRepository.save(entity);
|
return this.recipeRepository.save(entity);
|
||||||
}
|
}
|
||||||
@ -262,9 +263,9 @@ public class RecipeServiceImpl implements RecipeService {
|
|||||||
@Override
|
@Override
|
||||||
@PreAuthorize("@recipeSecurity.isOwner(#id, #modifier)")
|
@PreAuthorize("@recipeSecurity.isOwner(#id, #modifier)")
|
||||||
public Recipe removeViewer(long id, User modifier, User viewer) throws RecipeException {
|
public Recipe removeViewer(long id, User modifier, User viewer) throws RecipeException {
|
||||||
final Recipe entity = this.findRecipeEntity(id);
|
final RecipeEntity entity = this.findRecipeEntity(id);
|
||||||
final Set<User> viewers = new HashSet<>(entity.getViewers());
|
final Set<UserEntity> viewers = new HashSet<>(entity.getViewerEntities());
|
||||||
viewers.remove((User) viewer);
|
viewers.remove((UserEntity) viewer);
|
||||||
entity.setViewers(viewers);
|
entity.setViewers(viewers);
|
||||||
return this.recipeRepository.save(entity);
|
return this.recipeRepository.save(entity);
|
||||||
}
|
}
|
||||||
@ -272,7 +273,7 @@ public class RecipeServiceImpl implements RecipeService {
|
|||||||
@Override
|
@Override
|
||||||
@PreAuthorize("@recipeSecurity.isOwner(#id, #modifier)")
|
@PreAuthorize("@recipeSecurity.isOwner(#id, #modifier)")
|
||||||
public Recipe clearAllViewers(long id, User modifier) throws RecipeException {
|
public Recipe clearAllViewers(long id, User modifier) throws RecipeException {
|
||||||
final Recipe entity = this.findRecipeEntity(id);
|
final RecipeEntity entity = this.findRecipeEntity(id);
|
||||||
entity.setViewers(new HashSet<>());
|
entity.setViewers(new HashSet<>());
|
||||||
return this.recipeRepository.save(entity);
|
return this.recipeRepository.save(entity);
|
||||||
}
|
}
|
||||||
@ -285,12 +286,12 @@ public class RecipeServiceImpl implements RecipeService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FullRecipeView toFullRecipeView(Recipe recipe, boolean includeRawText, @Nullable User viewer) {
|
public FullRecipeView toFullRecipeView(Recipe recipe, boolean includeRawText, @Nullable User viewer) {
|
||||||
return this.getFullView((Recipe) recipe, includeRawText, viewer);
|
return this.getFullView((RecipeEntity) recipe, includeRawText, viewer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RecipeInfoView toRecipeInfoView(Recipe recipe, @Nullable User viewer) {
|
public RecipeInfoView toRecipeInfoView(Recipe recipe, @Nullable User viewer) {
|
||||||
return this.getInfoView((Recipe) recipe, viewer);
|
return this.getInfoView((RecipeEntity) recipe, viewer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -2,39 +2,15 @@ package app.mealsmadeeasy.api.recipe.comment;
|
|||||||
|
|
||||||
import app.mealsmadeeasy.api.recipe.Recipe;
|
import app.mealsmadeeasy.api.recipe.Recipe;
|
||||||
import app.mealsmadeeasy.api.user.User;
|
import app.mealsmadeeasy.api.user.User;
|
||||||
import jakarta.persistence.*;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
@Entity
|
public interface RecipeComment {
|
||||||
@Data
|
Integer getId();
|
||||||
public final class RecipeComment {
|
OffsetDateTime getCreated();
|
||||||
|
@Nullable OffsetDateTime getModified();
|
||||||
@Id
|
String getRawText();
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
User getOwner();
|
||||||
@Column(nullable = false)
|
Recipe getRecipe();
|
||||||
private Integer id;
|
|
||||||
|
|
||||||
@Column(nullable = false, updatable = false)
|
|
||||||
private OffsetDateTime created = OffsetDateTime.now();
|
|
||||||
|
|
||||||
private OffsetDateTime 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 User owner;
|
|
||||||
|
|
||||||
@ManyToOne
|
|
||||||
@JoinColumn(name = "recipe_id", nullable = false, updatable = false)
|
|
||||||
private Recipe recipe;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,100 @@
|
|||||||
|
package app.mealsmadeeasy.api.recipe.comment;
|
||||||
|
|
||||||
|
import app.mealsmadeeasy.api.recipe.RecipeEntity;
|
||||||
|
import app.mealsmadeeasy.api.user.UserEntity;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
|
@Entity(name = "RecipeComment")
|
||||||
|
@Table(name = "recipe_comment")
|
||||||
|
public final class RecipeCommentEntity implements RecipeComment {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Column(nullable = false, updatable = false)
|
||||||
|
private OffsetDateTime created = OffsetDateTime.now();
|
||||||
|
|
||||||
|
private OffsetDateTime 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 Integer getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OffsetDateTime getCreated() {
|
||||||
|
return this.created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreated(OffsetDateTime created) {
|
||||||
|
this.created = created;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OffsetDateTime getModified() {
|
||||||
|
return this.modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModified(OffsetDateTime 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,11 +1,11 @@
|
|||||||
package app.mealsmadeeasy.api.recipe.comment;
|
package app.mealsmadeeasy.api.recipe.comment;
|
||||||
|
|
||||||
import app.mealsmadeeasy.api.recipe.Recipe;
|
import app.mealsmadeeasy.api.recipe.RecipeEntity;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.domain.Slice;
|
import org.springframework.data.domain.Slice;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
public interface RecipeCommentRepository extends JpaRepository<RecipeComment, Long> {
|
public interface RecipeCommentRepository extends JpaRepository<RecipeCommentEntity, Long> {
|
||||||
void deleteAllByRecipe(Recipe recipe);
|
void deleteAllByRecipe(RecipeEntity recipe);
|
||||||
Slice<RecipeComment> findAllByRecipe(Recipe recipe, Pageable pageable);
|
Slice<RecipeCommentEntity> findAllByRecipe(RecipeEntity recipe, Pageable pageable);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
package app.mealsmadeeasy.api.recipe.comment;
|
package app.mealsmadeeasy.api.recipe.comment;
|
||||||
|
|
||||||
import app.mealsmadeeasy.api.markdown.MarkdownService;
|
import app.mealsmadeeasy.api.markdown.MarkdownService;
|
||||||
import app.mealsmadeeasy.api.recipe.Recipe;
|
import app.mealsmadeeasy.api.recipe.RecipeEntity;
|
||||||
import app.mealsmadeeasy.api.recipe.RecipeException;
|
import app.mealsmadeeasy.api.recipe.RecipeException;
|
||||||
import app.mealsmadeeasy.api.recipe.RecipeRepository;
|
import app.mealsmadeeasy.api.recipe.RecipeRepository;
|
||||||
import app.mealsmadeeasy.api.user.User;
|
import app.mealsmadeeasy.api.user.User;
|
||||||
|
import app.mealsmadeeasy.api.user.UserEntity;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.domain.Slice;
|
import org.springframework.data.domain.Slice;
|
||||||
import org.springframework.security.access.prepost.PostAuthorize;
|
import org.springframework.security.access.prepost.PostAuthorize;
|
||||||
@ -41,12 +42,12 @@ public class RecipeCommentServiceImpl implements RecipeCommentService {
|
|||||||
RecipeCommentCreateBody body
|
RecipeCommentCreateBody body
|
||||||
) throws RecipeException {
|
) throws RecipeException {
|
||||||
requireNonNull(commenter);
|
requireNonNull(commenter);
|
||||||
final RecipeComment draft = new RecipeComment();
|
final RecipeCommentEntity draft = new RecipeCommentEntity();
|
||||||
draft.setCreated(OffsetDateTime.now());
|
draft.setCreated(OffsetDateTime.now());
|
||||||
draft.setRawText(body.getText());
|
draft.setRawText(body.getText());
|
||||||
draft.setCachedRenderedText(this.markdownService.renderAndCleanMarkdown(body.getText()));
|
draft.setCachedRenderedText(this.markdownService.renderAndCleanMarkdown(body.getText()));
|
||||||
draft.setOwner((User) commenter);
|
draft.setOwner((UserEntity) commenter);
|
||||||
final Recipe recipe = this.recipeRepository.findByOwnerUsernameAndSlug(recipeUsername, recipeSlug)
|
final RecipeEntity recipe = this.recipeRepository.findByOwnerUsernameAndSlug(recipeUsername, recipeSlug)
|
||||||
.orElseThrow(() -> new RecipeException(
|
.orElseThrow(() -> new RecipeException(
|
||||||
RecipeException.Type.INVALID_USERNAME_OR_SLUG,
|
RecipeException.Type.INVALID_USERNAME_OR_SLUG,
|
||||||
"Invalid username or slug: " + recipeUsername + "/" + recipeSlug
|
"Invalid username or slug: " + recipeUsername + "/" + recipeSlug
|
||||||
@ -56,7 +57,7 @@ public class RecipeCommentServiceImpl implements RecipeCommentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostAuthorize("@recipeSecurity.isViewableBy(returnObject.recipe, #viewer)")
|
@PostAuthorize("@recipeSecurity.isViewableBy(returnObject.recipe, #viewer)")
|
||||||
private RecipeComment loadCommentEntity(long commentId, User viewer) throws RecipeException {
|
private RecipeCommentEntity loadCommentEntity(long commentId, User viewer) throws RecipeException {
|
||||||
return this.recipeCommentRepository.findById(commentId).orElseThrow(() -> new RecipeException(
|
return this.recipeCommentRepository.findById(commentId).orElseThrow(() -> new RecipeException(
|
||||||
RecipeException.Type.INVALID_COMMENT_ID, "No such RecipeComment for id: " + commentId
|
RecipeException.Type.INVALID_COMMENT_ID, "No such RecipeComment for id: " + commentId
|
||||||
));
|
));
|
||||||
@ -70,13 +71,13 @@ public class RecipeCommentServiceImpl implements RecipeCommentService {
|
|||||||
@Override
|
@Override
|
||||||
@PreAuthorize("@recipeSecurity.isViewableBy(#recipeUsername, #recipeSlug, #viewer)")
|
@PreAuthorize("@recipeSecurity.isViewableBy(#recipeUsername, #recipeSlug, #viewer)")
|
||||||
public Slice<RecipeCommentView> getComments(String recipeUsername, String recipeSlug, Pageable pageable, User viewer) throws RecipeException {
|
public Slice<RecipeCommentView> getComments(String recipeUsername, String recipeSlug, Pageable pageable, User viewer) throws RecipeException {
|
||||||
final Recipe recipe = this.recipeRepository.findByOwnerUsernameAndSlug(recipeUsername, recipeSlug).orElseThrow(
|
final RecipeEntity recipe = this.recipeRepository.findByOwnerUsernameAndSlug(recipeUsername, recipeSlug).orElseThrow(
|
||||||
() -> new RecipeException(
|
() -> new RecipeException(
|
||||||
RecipeException.Type.INVALID_USERNAME_OR_SLUG,
|
RecipeException.Type.INVALID_USERNAME_OR_SLUG,
|
||||||
"No such Recipe for username/slug: " + recipeUsername + "/" + recipeSlug
|
"No such Recipe for username/slug: " + recipeUsername + "/" + recipeSlug
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
final Slice<RecipeComment> commentEntities = this.recipeCommentRepository.findAllByRecipe(recipe, pageable);
|
final Slice<RecipeCommentEntity> commentEntities = this.recipeCommentRepository.findAllByRecipe(recipe, pageable);
|
||||||
return commentEntities.map(commentEntity -> RecipeCommentView.from(
|
return commentEntities.map(commentEntity -> RecipeCommentView.from(
|
||||||
commentEntity,
|
commentEntity,
|
||||||
false
|
false
|
||||||
@ -85,13 +86,13 @@ public class RecipeCommentServiceImpl implements RecipeCommentService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RecipeComment update(long commentId, User viewer, RecipeCommentUpdateSpec spec) throws RecipeException {
|
public RecipeComment update(long commentId, User viewer, RecipeCommentUpdateSpec spec) throws RecipeException {
|
||||||
final RecipeComment entity = this.loadCommentEntity(commentId, viewer);
|
final RecipeCommentEntity entity = this.loadCommentEntity(commentId, viewer);
|
||||||
entity.setRawText(spec.getRawText());
|
entity.setRawText(spec.getRawText());
|
||||||
return this.recipeCommentRepository.save(entity);
|
return this.recipeCommentRepository.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostAuthorize("@recipeSecurity.isOwner(returnObject.recipe, #modifier)")
|
@PostAuthorize("@recipeSecurity.isOwner(returnObject.recipe, #modifier)")
|
||||||
private RecipeComment loadForDelete(long commentId, User modifier) throws RecipeException {
|
private RecipeCommentEntity loadForDelete(long commentId, User modifier) throws RecipeException {
|
||||||
return this.recipeCommentRepository.findById(commentId).orElseThrow(() -> new RecipeException(
|
return this.recipeCommentRepository.findById(commentId).orElseThrow(() -> new RecipeException(
|
||||||
RecipeException.Type.INVALID_COMMENT_ID, "No such RecipeComment for id: " + commentId
|
RecipeException.Type.INVALID_COMMENT_ID, "No such RecipeComment for id: " + commentId
|
||||||
));
|
));
|
||||||
@ -99,7 +100,7 @@ public class RecipeCommentServiceImpl implements RecipeCommentService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(long commentId, User modifier) throws RecipeException {
|
public void delete(long commentId, User modifier) throws RecipeException {
|
||||||
final RecipeComment entityToDelete = this.loadForDelete(commentId, modifier);
|
final RecipeCommentEntity entityToDelete = this.loadForDelete(commentId, modifier);
|
||||||
this.recipeCommentRepository.delete(entityToDelete);
|
this.recipeCommentRepository.delete(entityToDelete);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ public class RecipeCommentView {
|
|||||||
view.setId(comment.getId());
|
view.setId(comment.getId());
|
||||||
view.setCreated(comment.getCreated());
|
view.setCreated(comment.getCreated());
|
||||||
view.setModified(comment.getModified());
|
view.setModified(comment.getModified());
|
||||||
view.setText(((RecipeComment) comment).getCachedRenderedText());
|
view.setText(((RecipeCommentEntity) comment).getCachedRenderedText());
|
||||||
if (includeRawText) {
|
if (includeRawText) {
|
||||||
view.setRawText(comment.getRawText());
|
view.setRawText(comment.getRawText());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,7 +53,7 @@ public class RecipeUpdateSpec {
|
|||||||
this.cookingTime = recipe.getCookingTime();
|
this.cookingTime = recipe.getCookingTime();
|
||||||
this.totalTime = recipe.getTotalTime();
|
this.totalTime = recipe.getTotalTime();
|
||||||
this.rawText = recipe.getRawText();
|
this.rawText = recipe.getRawText();
|
||||||
this.isPublic = recipe.getIsPublic();
|
this.isPublic = recipe.isPublic();
|
||||||
final @Nullable Image mainImage = recipe.getMainImage();
|
final @Nullable Image mainImage = recipe.getMainImage();
|
||||||
if (mainImage != null) {
|
if (mainImage != null) {
|
||||||
this.mainImage = new MainImageUpdateSpec();
|
this.mainImage = new MainImageUpdateSpec();
|
||||||
|
|||||||
@ -1,22 +1,7 @@
|
|||||||
package app.mealsmadeeasy.api.recipe.star;
|
package app.mealsmadeeasy.api.recipe.star;
|
||||||
|
|
||||||
import jakarta.persistence.Column;
|
|
||||||
import jakarta.persistence.EmbeddedId;
|
|
||||||
import jakarta.persistence.Entity;
|
|
||||||
import jakarta.persistence.Table;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
@Entity(name = "RecipeStar")
|
public interface RecipeStar {
|
||||||
@Table(name = "recipe_star")
|
OffsetDateTime getTimestamp();
|
||||||
@Data
|
|
||||||
public final class RecipeStar {
|
|
||||||
|
|
||||||
@EmbeddedId
|
|
||||||
private RecipeStarId id;
|
|
||||||
|
|
||||||
@Column(nullable = false, updatable = false)
|
|
||||||
private OffsetDateTime timestamp = OffsetDateTime.now();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,41 @@
|
|||||||
|
package app.mealsmadeeasy.api.recipe.star;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.EmbeddedId;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
|
@Entity(name = "RecipeStar")
|
||||||
|
@Table(name = "recipe_star")
|
||||||
|
public final class RecipeStarEntity implements RecipeStar {
|
||||||
|
|
||||||
|
@EmbeddedId
|
||||||
|
private RecipeStarId id;
|
||||||
|
|
||||||
|
@Column(nullable = false, updatable = false)
|
||||||
|
private OffsetDateTime timestamp = OffsetDateTime.now();
|
||||||
|
|
||||||
|
public RecipeStarId getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(RecipeStarId id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OffsetDateTime getTimestamp() {
|
||||||
|
return this.timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimestamp(OffsetDateTime date) {
|
||||||
|
this.timestamp = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "RecipeStarEntity(" + this.id + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -7,10 +7,10 @@ import org.springframework.data.jpa.repository.Query;
|
|||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface RecipeStarRepository extends JpaRepository<RecipeStar, Long> {
|
public interface RecipeStarRepository extends JpaRepository<RecipeStarEntity, Long> {
|
||||||
|
|
||||||
@Query("SELECT star FROM RecipeStar star WHERE star.id.recipeId = ?1 AND star.id.ownerId = ?2")
|
@Query("SELECT star FROM RecipeStar star WHERE star.id.recipeId = ?1 AND star.id.ownerId = ?2")
|
||||||
Optional<RecipeStar> findByRecipeIdAndOwnerId(Integer recipeId, Integer ownerId);
|
Optional<RecipeStarEntity> findByRecipeIdAndOwnerId(Integer recipeId, Integer ownerId);
|
||||||
|
|
||||||
@Query("SELECT count(rs) > 0 FROM RecipeStar rs, Recipe r WHERE r.owner.username = ?1 AND r.slug = ?2 AND r.id = rs.id.recipeId AND rs.id.ownerId = ?3")
|
@Query("SELECT count(rs) > 0 FROM RecipeStar rs, Recipe r WHERE r.owner.username = ?1 AND r.slug = ?2 AND r.id = rs.id.recipeId AND rs.id.ownerId = ?3")
|
||||||
boolean isStarer(String ownerUsername, String slug, Integer viewerId);
|
boolean isStarer(String ownerUsername, String slug, Integer viewerId);
|
||||||
|
|||||||
@ -22,7 +22,7 @@ public class RecipeStarServiceImpl implements RecipeStarService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RecipeStar create(Integer recipeId, Integer ownerId) {
|
public RecipeStar create(Integer recipeId, Integer ownerId) {
|
||||||
final RecipeStar draft = new RecipeStar();
|
final RecipeStarEntity draft = new RecipeStarEntity();
|
||||||
final RecipeStarId id = new RecipeStarId();
|
final RecipeStarId id = new RecipeStarId();
|
||||||
id.setRecipeId(recipeId);
|
id.setRecipeId(recipeId);
|
||||||
id.getOwnerId(ownerId);
|
id.getOwnerId(ownerId);
|
||||||
@ -34,7 +34,7 @@ public class RecipeStarServiceImpl implements RecipeStarService {
|
|||||||
@Override
|
@Override
|
||||||
public RecipeStar create(String recipeOwnerUsername, String recipeSlug, User starer) throws RecipeException {
|
public RecipeStar create(String recipeOwnerUsername, String recipeSlug, User starer) throws RecipeException {
|
||||||
final Recipe recipe = this.recipeService.getByUsernameAndSlug(recipeOwnerUsername, recipeSlug, starer);
|
final Recipe recipe = this.recipeService.getByUsernameAndSlug(recipeOwnerUsername, recipeSlug, starer);
|
||||||
final Optional<RecipeStar> existing = this.recipeStarRepository.findByRecipeIdAndOwnerId(
|
final Optional<RecipeStarEntity> existing = this.recipeStarRepository.findByRecipeIdAndOwnerId(
|
||||||
recipe.getId(),
|
recipe.getId(),
|
||||||
starer.getId()
|
starer.getId()
|
||||||
);
|
);
|
||||||
@ -47,7 +47,8 @@ public class RecipeStarServiceImpl implements RecipeStarService {
|
|||||||
@Override
|
@Override
|
||||||
public Optional<RecipeStar> find(String recipeOwnerUsername, String recipeSlug, User starer) throws RecipeException {
|
public Optional<RecipeStar> find(String recipeOwnerUsername, String recipeSlug, User starer) throws RecipeException {
|
||||||
final Recipe recipe = this.recipeService.getByUsernameAndSlug(recipeOwnerUsername, recipeSlug, starer);
|
final Recipe recipe = this.recipeService.getByUsernameAndSlug(recipeOwnerUsername, recipeSlug, starer);
|
||||||
return this.recipeStarRepository.findByRecipeIdAndOwnerId(recipe.getId(), starer.getId());
|
return this.recipeStarRepository.findByRecipeIdAndOwnerId(recipe.getId(), starer.getId())
|
||||||
|
.map(RecipeStar.class::cast);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -36,7 +36,7 @@ public class FullRecipeView {
|
|||||||
view.setStarCount(starCount);
|
view.setStarCount(starCount);
|
||||||
view.setViewerCount(viewerCount);
|
view.setViewerCount(viewerCount);
|
||||||
view.setMainImage(mainImage);
|
view.setMainImage(mainImage);
|
||||||
view.setIsPublic(recipe.getIsPublic());
|
view.setIsPublic(recipe.isPublic());
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,13 +3,11 @@ package app.mealsmadeeasy.api.recipe.view;
|
|||||||
import app.mealsmadeeasy.api.image.view.ImageView;
|
import app.mealsmadeeasy.api.image.view.ImageView;
|
||||||
import app.mealsmadeeasy.api.recipe.Recipe;
|
import app.mealsmadeeasy.api.recipe.Recipe;
|
||||||
import app.mealsmadeeasy.api.user.view.UserInfoView;
|
import app.mealsmadeeasy.api.user.view.UserInfoView;
|
||||||
import lombok.Data;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
@Data
|
public final class RecipeInfoView {
|
||||||
public class RecipeInfoView {
|
|
||||||
|
|
||||||
public static RecipeInfoView from(Recipe recipe, int starCount, @Nullable ImageView mainImage) {
|
public static RecipeInfoView from(Recipe recipe, int starCount, @Nullable ImageView mainImage) {
|
||||||
final RecipeInfoView view = new RecipeInfoView();
|
final RecipeInfoView view = new RecipeInfoView();
|
||||||
@ -22,7 +20,7 @@ public class RecipeInfoView {
|
|||||||
view.setCookingTime(recipe.getCookingTime());
|
view.setCookingTime(recipe.getCookingTime());
|
||||||
view.setTotalTime(recipe.getTotalTime());
|
view.setTotalTime(recipe.getTotalTime());
|
||||||
view.setOwner(UserInfoView.from(recipe.getOwner()));
|
view.setOwner(UserInfoView.from(recipe.getOwner()));
|
||||||
view.setPublic(recipe.getIsPublic());
|
view.setIsPublic(recipe.isPublic());
|
||||||
view.setStarCount(starCount);
|
view.setStarCount(starCount);
|
||||||
view.setMainImage(mainImage);
|
view.setMainImage(mainImage);
|
||||||
return view;
|
return view;
|
||||||
@ -41,4 +39,100 @@ public class RecipeInfoView {
|
|||||||
private int starCount;
|
private int starCount;
|
||||||
private @Nullable ImageView mainImage;
|
private @Nullable ImageView mainImage;
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OffsetDateTime getCreated() {
|
||||||
|
return this.created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreated(OffsetDateTime created) {
|
||||||
|
this.created = created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OffsetDateTime getModified() {
|
||||||
|
return this.modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModified(OffsetDateTime modified) {
|
||||||
|
this.modified = modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSlug() {
|
||||||
|
return this.slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSlug(String slug) {
|
||||||
|
this.slug = slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return this.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Integer getPreparationTime() {
|
||||||
|
return this.preparationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreparationTime(@Nullable Integer preparationTime) {
|
||||||
|
this.preparationTime = preparationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Integer getCookingTime() {
|
||||||
|
return this.cookingTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCookingTime(@Nullable Integer cookingTime) {
|
||||||
|
this.cookingTime = cookingTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Integer getTotalTime() {
|
||||||
|
return this.totalTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalTime(@Nullable Integer totalTime) {
|
||||||
|
this.totalTime = totalTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserInfoView getOwner() {
|
||||||
|
return this.owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOwner(UserInfoView owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getIsPublic() {
|
||||||
|
return this.isPublic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsPublic(boolean isPublic) {
|
||||||
|
this.isPublic = isPublic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStarCount() {
|
||||||
|
return this.starCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStarCount(int starCount) {
|
||||||
|
this.starCount = starCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable ImageView getMainImage() {
|
||||||
|
return this.mainImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMainImage(@Nullable ImageView mainImage) {
|
||||||
|
this.mainImage = mainImage;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,92 +1,18 @@
|
|||||||
package app.mealsmadeeasy.api.user;
|
package app.mealsmadeeasy.api.user;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@Entity(name = "User")
|
public interface User extends UserDetails {
|
||||||
@Table(name = "\"user\"")
|
|
||||||
@Data
|
|
||||||
public final class User implements UserDetails {
|
|
||||||
|
|
||||||
public static User getDefaultDraft() {
|
Integer getId();
|
||||||
final var user = new User();
|
|
||||||
user.setEnabled(true);
|
|
||||||
user.setExpired(false);
|
|
||||||
user.setLocked(false);
|
|
||||||
user.setCredentialsExpired(false);
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Id
|
String getEmail();
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
void setEmail(String email);
|
||||||
@Column(nullable = false)
|
|
||||||
private Integer id;
|
|
||||||
|
|
||||||
@Column(unique = true, nullable = false)
|
void addAuthority(UserGrantedAuthority userGrantedAuthority);
|
||||||
private String username;
|
void addAuthorities(Set<? extends UserGrantedAuthority> userGrantedAuthorities);
|
||||||
|
void removeAuthority(UserGrantedAuthority userGrantedAuthority);
|
||||||
@Column(unique = true, nullable = false)
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
|
||||||
private String password;
|
|
||||||
|
|
||||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "user")
|
|
||||||
private final Set<UserGrantedAuthority> authorities = new HashSet<>();
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
|
||||||
private Boolean enabled;
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
|
||||||
private Boolean expired;
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
|
||||||
private Boolean locked;
|
|
||||||
|
|
||||||
@Column(nullable = false)
|
|
||||||
private Boolean credentialsExpired;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
|
||||||
return this.authorities;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addAuthority(UserGrantedAuthority userGrantedAuthority) {
|
|
||||||
this.authorities.add(userGrantedAuthority);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addAuthorities(Set<? extends UserGrantedAuthority> userGrantedAuthorities) {
|
|
||||||
userGrantedAuthorities.forEach(this::addAuthority);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeAuthority(UserGrantedAuthority userGrantedAuthority) {
|
|
||||||
this.authorities.remove(userGrantedAuthority);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAccountNonExpired() {
|
|
||||||
return !this.expired;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAccountNonLocked() {
|
|
||||||
return !this.locked;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCredentialsNonExpired() {
|
|
||||||
return !this.credentialsExpired;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return this.enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
183
src/main/java/app/mealsmadeeasy/api/user/UserEntity.java
Normal file
183
src/main/java/app/mealsmadeeasy/api/user/UserEntity.java
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
package app.mealsmadeeasy.api.user;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
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() {
|
||||||
|
final var user = new UserEntity();
|
||||||
|
user.setEnabled(true);
|
||||||
|
user.setExpired(false);
|
||||||
|
user.setLocked(false);
|
||||||
|
user.setCredentialsExpired(false);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Column(unique = true, nullable = false)
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@Column(unique = true, nullable = false)
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "userEntity")
|
||||||
|
private final Set<UserGrantedAuthorityEntity> authorities = new HashSet<>();
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Boolean enabled;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Boolean expired;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Boolean locked;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Boolean credentialsExpired;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return this.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEmail() {
|
||||||
|
return this.email;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPassword() {
|
||||||
|
return this.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
|
return this.authorities;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAuthority(UserGrantedAuthority userGrantedAuthority) {
|
||||||
|
this.authorities.add((UserGrantedAuthorityEntity) userGrantedAuthority);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAuthorities(Set<? extends UserGrantedAuthority> userGrantedAuthorities) {
|
||||||
|
userGrantedAuthorities.forEach(this::addAuthority);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAuthority(UserGrantedAuthority userGrantedAuthority) {
|
||||||
|
this.authorities.remove((UserGrantedAuthorityEntity) userGrantedAuthority);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonExpired() {
|
||||||
|
return !this.expired;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpired(Boolean expired) {
|
||||||
|
this.expired = expired;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonLocked() {
|
||||||
|
return !this.locked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocked(Boolean locked) {
|
||||||
|
this.locked = locked;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCredentialsNonExpired() {
|
||||||
|
return !this.credentialsExpired;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCredentialsExpired(Boolean credentialsExpired) {
|
||||||
|
this.credentialsExpired = credentialsExpired;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return this.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(Boolean enabled) {
|
||||||
|
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 "UserEntity(" + this.id + ", " + this.username + ", " + this.email + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,23 +1,5 @@
|
|||||||
package app.mealsmadeeasy.api.user;
|
package app.mealsmadeeasy.api.user;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
@Entity(name = "UserGrantedAuthority")
|
public interface UserGrantedAuthority extends GrantedAuthority {}
|
||||||
@Table(name = "user_granted_authority")
|
|
||||||
@Data
|
|
||||||
public final class UserGrantedAuthority implements GrantedAuthority {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
@Column(nullable = false)
|
|
||||||
private Integer id;
|
|
||||||
|
|
||||||
private String authority;
|
|
||||||
|
|
||||||
@ManyToOne
|
|
||||||
@JoinColumn(name = "user_id")
|
|
||||||
private User user;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
package app.mealsmadeeasy.api.user;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
|
@Entity(name = "UserGrantedAuthority")
|
||||||
|
@Table(name = "user_granted_authority")
|
||||||
|
public final class UserGrantedAuthorityEntity implements UserGrantedAuthority {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
private String authority;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "user_id")
|
||||||
|
private UserEntity userEntity;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAuthority() {
|
||||||
|
return this.authority;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -2,4 +2,4 @@ package app.mealsmadeeasy.api.user;
|
|||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
public interface UserGrantedAuthorityRepository extends JpaRepository<UserGrantedAuthority, Long> {}
|
public interface UserGrantedAuthorityRepository extends JpaRepository<UserGrantedAuthorityEntity, Long> {}
|
||||||
|
|||||||
@ -4,9 +4,9 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface UserRepository extends JpaRepository<User, Long> {
|
public interface UserRepository extends JpaRepository<UserEntity, Long> {
|
||||||
Optional<User> findByUsername(String username);
|
Optional<UserEntity> findByUsername(String username);
|
||||||
User getByUsername(String username);
|
UserEntity getByUsername(String username);
|
||||||
boolean existsByUsername(String username);
|
boolean existsByUsername(String username);
|
||||||
boolean existsByEmail(String email);
|
boolean existsByEmail(String email);
|
||||||
void deleteByUsername(String username);
|
void deleteByUsername(String username);
|
||||||
|
|||||||
@ -32,7 +32,7 @@ public final class UserServiceImpl implements UserService {
|
|||||||
if (this.userRepository.existsByEmail(email)) {
|
if (this.userRepository.existsByEmail(email)) {
|
||||||
throw new UserCreateException(UserCreateException.Type.EMAIL_TAKEN, "Email " + email + " is taken.");
|
throw new UserCreateException(UserCreateException.Type.EMAIL_TAKEN, "Email " + email + " is taken.");
|
||||||
}
|
}
|
||||||
final User draft = User.getDefaultDraft();
|
final UserEntity draft = UserEntity.getDefaultDraft();
|
||||||
draft.setUsername(username);
|
draft.setUsername(username);
|
||||||
draft.setEmail(email);
|
draft.setEmail(email);
|
||||||
draft.setPassword(this.passwordEncoder.encode(rawPassword));
|
draft.setPassword(this.passwordEncoder.encode(rawPassword));
|
||||||
@ -47,12 +47,12 @@ public final class UserServiceImpl implements UserService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public User updateUser(User user) {
|
public User updateUser(User user) {
|
||||||
return this.userRepository.save((User) user);
|
return this.userRepository.save((UserEntity) user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteUser(User user) {
|
public void deleteUser(User user) {
|
||||||
this.userRepository.delete((User) user);
|
this.userRepository.delete((UserEntity) user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package app.mealsmadeeasy.api.recipe;
|
|||||||
|
|
||||||
import app.mealsmadeeasy.api.matchers.ContainsItemsMatcher;
|
import app.mealsmadeeasy.api.matchers.ContainsItemsMatcher;
|
||||||
import app.mealsmadeeasy.api.recipe.star.RecipeStar;
|
import app.mealsmadeeasy.api.recipe.star.RecipeStar;
|
||||||
|
import app.mealsmadeeasy.api.recipe.star.RecipeStarEntity;
|
||||||
import app.mealsmadeeasy.api.recipe.star.RecipeStarId;
|
import app.mealsmadeeasy.api.recipe.star.RecipeStarId;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -17,8 +18,8 @@ public class ContainsRecipeStarsMatcher extends ContainsItemsMatcher<RecipeStar,
|
|||||||
super(
|
super(
|
||||||
List.of(allExpected),
|
List.of(allExpected),
|
||||||
o -> o instanceof RecipeStar,
|
o -> o instanceof RecipeStar,
|
||||||
RecipeStar::getId,
|
recipeStar -> ((RecipeStarEntity) recipeStar).getId(),
|
||||||
RecipeStar::getId,
|
recipeStar -> ((RecipeStarEntity) recipeStar).getId(),
|
||||||
(id0, id1) -> Objects.equals(id0.getRecipeId(), id1.getRecipeId())
|
(id0, id1) -> Objects.equals(id0.getRecipeId(), id1.getRecipeId())
|
||||||
&& Objects.equals(id0.getOwnerId(), id1.getOwnerId())
|
&& Objects.equals(id0.getOwnerId(), id1.getOwnerId())
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user