diff --git a/build.gradle b/build.gradle index 974598808..b63f26221 100644 --- a/build.gradle +++ b/build.gradle @@ -175,6 +175,7 @@ dependencies { implementation("com.yahoo.elide:elide-model-config:${elideVersion}") implementation("com.yahoo.elide:elide-spring-boot-autoconfigure:${elideVersion}") implementation("com.yahoo.elide:elide-swagger:${elideVersion}") + implementation("com.yahoo.elide:elide-datastore-noop:${elideVersion}") implementation("com.yahoo.elide:elide-datastore-jpa:${elideVersion}") implementation("com.yahoo.elide:elide-datastore-multiplex:${elideVersion}") implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:${springdocVersion}") diff --git a/src/main/java/com/faforever/api/config/EphemeralDatastoreConfig.java b/src/main/java/com/faforever/api/config/EphemeralDatastoreConfig.java new file mode 100644 index 000000000..339aa3cd6 --- /dev/null +++ b/src/main/java/com/faforever/api/config/EphemeralDatastoreConfig.java @@ -0,0 +1,35 @@ +package com.faforever.api.config; + +import com.faforever.api.data.annotation.Ephemeral; +import com.yahoo.elide.core.datastore.DataStore; +import com.yahoo.elide.datastores.noop.NoopDataStore; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.type.filter.AnnotationTypeFilter; + +import java.util.List; + +@Configuration +public class EphemeralDatastoreConfig { + + @Bean + DataStore ephemeralDataStore() { + ClassPathScanningCandidateComponentProvider ephemeralScanner = new ClassPathScanningCandidateComponentProvider( + false); + ephemeralScanner.addIncludeFilter(new AnnotationTypeFilter(Ephemeral.class)); + List ephemeralModels = ephemeralScanner.findCandidateComponents("com.faforever.api.data.domain") + .stream() + .map(beanDefinition -> { + try { + return Class.forName(beanDefinition.getBeanClassName()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + }) + .map(Class.class::cast) + .toList(); + + return new NoopDataStore(ephemeralModels); + } +} diff --git a/src/main/java/com/faforever/api/config/elide/ElideConfig.java b/src/main/java/com/faforever/api/config/elide/ElideConfig.java index 9e09d4426..e2eecece1 100644 --- a/src/main/java/com/faforever/api/config/elide/ElideConfig.java +++ b/src/main/java/com/faforever/api/config/elide/ElideConfig.java @@ -37,9 +37,10 @@ public class ElideConfig { @Bean MultiplexManager multiplexDataStore( DataStore fafDataStore, - DataStore leagueDataStore + DataStore leagueDataStore, + DataStore ephemeralDataStore ) { - return new MultiplexManager(fafDataStore, leagueDataStore); + return new MultiplexManager(fafDataStore, leagueDataStore, ephemeralDataStore); } @Bean @@ -70,7 +71,7 @@ public JsonApi jsonApi(Elide elide) { } /** - * See https://github.com/yahoo/elide/issues/428. + * See https://github.com/yahoo/elide/issues/428. */ private void registerAdditionalConverters() { CoerceUtil.coerce("", String.class); diff --git a/src/main/java/com/faforever/api/data/annotation/Ephemeral.java b/src/main/java/com/faforever/api/data/annotation/Ephemeral.java new file mode 100644 index 000000000..28ea21439 --- /dev/null +++ b/src/main/java/com/faforever/api/data/annotation/Ephemeral.java @@ -0,0 +1,14 @@ +package com.faforever.api.data.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Use to mark domain model as ephemeral that is not saved to the database + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Ephemeral { +} diff --git a/src/main/java/com/faforever/api/data/domain/Game.java b/src/main/java/com/faforever/api/data/domain/Game.java index 5621c0021..079e0f7ef 100644 --- a/src/main/java/com/faforever/api/data/domain/Game.java +++ b/src/main/java/com/faforever/api/data/domain/Game.java @@ -3,14 +3,11 @@ import com.faforever.api.data.checks.Prefab; import com.faforever.api.data.listeners.GameEnricher; import com.yahoo.elide.annotation.ComputedAttribute; +import com.yahoo.elide.annotation.ComputedRelationship; import com.yahoo.elide.annotation.Include; +import com.yahoo.elide.annotation.ReadPermission; +import com.yahoo.elide.annotation.ToMany; import com.yahoo.elide.annotation.UpdatePermission; -import lombok.Setter; -import lombok.ToString; -import org.hibernate.annotations.BatchSize; -import org.hibernate.annotations.Immutable; -import org.jetbrains.annotations.Nullable; - import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EntityListeners; @@ -25,6 +22,12 @@ import jakarta.persistence.PrimaryKeyJoinColumn; import jakarta.persistence.Table; import jakarta.persistence.Transient; +import lombok.Setter; +import lombok.ToString; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.Immutable; +import org.jetbrains.annotations.Nullable; + import java.time.OffsetDateTime; import java.util.Set; @@ -57,6 +60,7 @@ public class Game { private Set reviews; private GameReviewsSummary reviewsSummary; private Boolean replayAvailable; + private Set reviewRequests; @Id @Column(name = "id") @@ -146,6 +150,15 @@ public Boolean isReplayAvailable() { return replayAvailable; } + @ReadPermission(expression = Prefab.ALL) + @UpdatePermission(expression = Prefab.ALL) + @Transient + @ComputedRelationship + @ToMany + public Set getReviewRequests() { + return reviewRequests; + } + /** * This ManyToOne relationship leads to a double left outer join through Elide causing an additional full table * scan on the matchmaker_queue table. Even though it has only 3 records, it causes MySql 5.7 and MySQL to run diff --git a/src/main/java/com/faforever/api/data/domain/GameReviewRequest.java b/src/main/java/com/faforever/api/data/domain/GameReviewRequest.java new file mode 100644 index 000000000..06c8cb8f9 --- /dev/null +++ b/src/main/java/com/faforever/api/data/domain/GameReviewRequest.java @@ -0,0 +1,58 @@ +package com.faforever.api.data.domain; + +import com.faforever.api.data.annotation.Ephemeral; +import com.faforever.api.data.checks.Prefab; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.yahoo.elide.annotation.Audit; +import com.yahoo.elide.annotation.CreatePermission; +import com.yahoo.elide.annotation.DeletePermission; +import com.yahoo.elide.annotation.Include; +import com.yahoo.elide.annotation.ReadPermission; +import com.yahoo.elide.annotation.ToOne; +import com.yahoo.elide.annotation.UpdatePermission; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Transient; +import lombok.Setter; + +@Setter +@Include(name = "gameReviewRequest", rootLevel = false) +@Ephemeral +@CreatePermission(expression = Prefab.ALL) +@UpdatePermission(expression = Prefab.NONE) +@ReadPermission(expression = Prefab.ALL) +@DeletePermission(expression = Prefab.NONE) +@Audit(action = Audit.Action.CREATE, logStatement = "Review has been requested for game ''{0}''", logExpressions = {"${gameReviewRequest.game.id}"}) +public class GameReviewRequest implements OwnableEntity { + + private Game game; + private Player player; + private String requestDescription; + + @Id + @GeneratedValue + public String getId() { + return "N/A"; + } + + @ToOne + public Game getGame() { + return game; + } + + @ToOne + public Player getPlayer() { + return player; + } + + public String getRequestDescription() { + return requestDescription; + } + + @Transient + @Override + @JsonIgnore + public Login getEntityOwner() { + return getPlayer(); + } +} diff --git a/src/main/resources/config/application-local.yml b/src/main/resources/config/application-local.yml index 7f81df781..322e401dc 100644 --- a/src/main/resources/config/application-local.yml +++ b/src/main/resources/config/application-local.yml @@ -66,9 +66,9 @@ faf-api: spring: datasource: - url: jdbc:mariadb://${DATABASE_ADDRESS:127.0.0.1}/${DATABASE_NAME:faf}?useSSL=false + url: jdbc:mariadb://${DATABASE_ADDRESS:127.0.0.1}/${DATABASE_NAME:faf_lobby}?useSSL=false name: faf - username: ${DATABASE_USERNAME:faf-api} + username: ${DATABASE_USERNAME:faf-java-api} password: ${DATABASE_PASSWORD:banana} league-datasource: url: jdbc:mariadb://${LEAGUE_DATABASE_ADDRESS:127.0.0.1}/${LEAGUE_DATABASE_NAME:faf-league}?useSSL=false @@ -88,7 +88,7 @@ spring: resourceserver: jwt: jwk-set-uri: http://hydra.faforever.localhost/.well-known/jwks.json - issuer-uri: http://ory-hydra:4444/ + issuer-uri: http://ory-hydra:4444 logging: level: com.faforever.api: debug