diff --git a/.java-version b/.java-version index 46cbfbc7..fcc01369 100644 --- a/.java-version +++ b/.java-version @@ -1 +1 @@ -graalvm64-17.0.4 +20.0.1 diff --git a/.jvmopts b/.jvmopts index b7359b71..a2c5f585 100644 --- a/.jvmopts +++ b/.jvmopts @@ -3,4 +3,5 @@ -Xss6M -XX:ReservedCodeCacheSize=256M -Dfile.encoding=UTF-8 +--enable-preview --add-opens=java.base/java.lang=ALL-UNNAMED diff --git a/akka/build.gradle.kts b/akka/build.gradle.kts index 4ab025b9..99e22dcf 100644 --- a/akka/build.gradle.kts +++ b/akka/build.gradle.kts @@ -2,28 +2,16 @@ version = "0.1" group = "org.spongepowered.downloads" - -val akkaVersion: String by project -val scalaVersion: String by project -val akkaManagementVersion: String by project -val akkaProjection: String by project +plugins { + id("com.github.johnrengelman.shadow") + id("io.micronaut.library") +} dependencies { - implementation("com.ongres.scram:client:2.1") - implementation("jakarta.annotation:jakarta.annotation-api") - implementation(platform("com.typesafe.akka:akka-bom_${scalaVersion}:${akkaVersion}")) - implementation("com.typesafe.akka:akka-actor-typed_${scalaVersion}") - implementation("com.typesafe.akka:akka-cluster-sharding-typed_${scalaVersion}") - implementation("com.typesafe.akka:akka-cluster-typed_${scalaVersion}") - implementation("com.typesafe.akka:akka-discovery_${scalaVersion}") - implementation("com.typesafe.akka:akka-discovery_${scalaVersion}") - implementation("com.lightbend.akka.management:akka-management_${scalaVersion}:${akkaManagementVersion}") - implementation("com.lightbend.akka.management:akka-management-cluster-bootstrap_${scalaVersion}:${akkaManagementVersion}") - - runtimeOnly("ch.qos.logback:logback-classic") - compileOnly("org.graalvm.nativeimage:svm") - - implementation("io.micronaut:micronaut-validation") + annotationProcessor("io.micronaut.serde:micronaut-serde-processor") + implementation("io.micronaut.serde:micronaut-serde-jackson") + api("io.micronaut:micronaut-inject") + api(platform(libs.akkaBom)) + api(libs.bundles.actors) + implementation(libs.bundles.akkaManagement) } - - diff --git a/akka/settings.gradle b/akka/settings.gradle deleted file mode 100644 index 766979b3..00000000 --- a/akka/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ - -rootProject.name="akka" - diff --git a/akka/src/main/java/module-info.java b/akka/src/main/java/module-info.java deleted file mode 100644 index 4b98e64f..00000000 --- a/akka/src/main/java/module-info.java +++ /dev/null @@ -1,5 +0,0 @@ -module systemofadownload.akka { - requires akka.actor.typed; - requires akka.cluster.sharding; - exports org.spongepowered.downloads.akka; -} diff --git a/akka/src/main/java/org/spongepowered/downloads/akka/AkkaExtension.java b/akka/src/main/java/org/spongepowered/downloads/akka/AkkaExtension.java index 1a232f7d..5f24ff44 100644 --- a/akka/src/main/java/org/spongepowered/downloads/akka/AkkaExtension.java +++ b/akka/src/main/java/org/spongepowered/downloads/akka/AkkaExtension.java @@ -1,6 +1,7 @@ package org.spongepowered.downloads.akka; import akka.actor.typed.ActorSystem; +import akka.actor.typed.Behavior; import akka.actor.typed.Scheduler; import akka.actor.typed.SpawnProtocol; import akka.actor.typed.javadsl.Adapter; @@ -12,35 +13,32 @@ import com.typesafe.config.ConfigFactory; import io.micronaut.context.annotation.Bean; import io.micronaut.context.annotation.Factory; +import io.micronaut.core.annotation.NonNull; +import jakarta.inject.Singleton; @Factory public class AkkaExtension { @Bean - public Scheduler systemScheduler() { - return system().scheduler(); + public Scheduler systemScheduler(@NonNull ActorSystem system) { + return system.scheduler(); } @Bean public Config akkaConfig() { - return ConfigFactory.load(); + return ConfigFactory.defaultApplication(); } + @Singleton @Bean(preDestroy = "terminate") - public ActorSystem system() { - Config config = akkaConfig(); - return ActorSystem.create( - Behaviors.setup(ctx -> { - akka.actor.ActorSystem unTypedSystem = Adapter.toClassic(ctx.getSystem()); - AkkaManagement.get(unTypedSystem).start(); - ClusterBootstrap.get(unTypedSystem).start(); - return SpawnProtocol.create(); - }), config.getString("some.cluster.name")); + public ActorSystem system(@NonNull Behavior behavior, @NonNull Config config) { + return ActorSystem.create(behavior, "soad-master"); } @Bean - public ClusterSharding clusterSharding() { - return ClusterSharding.get(system()); + @Singleton + public ClusterSharding clusterSharding(@NonNull ActorSystem system) { + return ClusterSharding.get(system); } } diff --git a/akka/src/main/java/org/spongepowered/downloads/akka/AkkaSerializable.java b/akka/src/main/java/org/spongepowered/downloads/akka/AkkaSerializable.java new file mode 100644 index 00000000..793d772e --- /dev/null +++ b/akka/src/main/java/org/spongepowered/downloads/akka/AkkaSerializable.java @@ -0,0 +1,7 @@ +package org.spongepowered.downloads.akka; + +/** + * Marker interface for Akka serialization via Jackson + */ +public interface AkkaSerializable { +} diff --git a/akka/src/main/java/org/spongepowered/downloads/akka/ProductionAkkaSystem.java b/akka/src/main/java/org/spongepowered/downloads/akka/ProductionAkkaSystem.java new file mode 100644 index 00000000..62c6baa1 --- /dev/null +++ b/akka/src/main/java/org/spongepowered/downloads/akka/ProductionAkkaSystem.java @@ -0,0 +1,26 @@ +package org.spongepowered.downloads.akka; + +import akka.actor.typed.ActorSystem; +import akka.actor.typed.Behavior; +import akka.actor.typed.SpawnProtocol; +import akka.actor.typed.javadsl.Behaviors; +import akka.management.cluster.bootstrap.ClusterBootstrap; +import akka.management.javadsl.AkkaManagement; +import io.micronaut.context.annotation.Bean; +import io.micronaut.context.annotation.Factory; +import io.micronaut.context.annotation.Requires; + +@Factory +public class ProductionAkkaSystem { + + @Bean + public Behavior productionGuardian() { + return Behaviors.setup(ctx -> { + final var system = ctx.getSystem(); + ClusterBootstrap.get(system).start(); + AkkaManagement.get(system).start(); + return SpawnProtocol.create(); + }); + } + +} diff --git a/akka/src/main/resources/refrerence.conf b/akka/src/main/resources/refrerence.conf new file mode 100644 index 00000000..7aa8e90a --- /dev/null +++ b/akka/src/main/resources/refrerence.conf @@ -0,0 +1,23 @@ + +akka { + actor { + provider = "cluster" + serialization-bindings { + "org.spongepowered.downloads.akka.AkkaSerializable" = jackson-json + } + } + remote.artery { + canonical { + hostname = "127.0.0.1" + port = 2551 + } + } + + cluster { + seed-nodes = [ + "akka://ClusterSystem@127.0.0.1:2551", + "akka://ClusterSystem@127.0.0.1:2552"] + + downing-provider-class = "akka.cluster.sbr.SplitBrainResolverProvider" + } +} diff --git a/akka/testkit/build.gradle.kts b/akka/testkit/build.gradle.kts new file mode 100644 index 00000000..ff4b171c --- /dev/null +++ b/akka/testkit/build.gradle.kts @@ -0,0 +1,14 @@ + + + +plugins { + id("com.github.johnrengelman.shadow") + id("io.micronaut.library") +} +dependencies { + annotationProcessor("io.micronaut.serde:micronaut-serde-processor") + implementation("io.micronaut.serde:micronaut-serde-jackson") + api("io.micronaut:micronaut-inject") + api(project(":akka")) + api(libs.akka.testkit) +} diff --git a/akka/testkit/src/main/java/org/spongepowered/downloads/test/akka/AkkaTestExtension.java b/akka/testkit/src/main/java/org/spongepowered/downloads/test/akka/AkkaTestExtension.java new file mode 100644 index 00000000..69e37478 --- /dev/null +++ b/akka/testkit/src/main/java/org/spongepowered/downloads/test/akka/AkkaTestExtension.java @@ -0,0 +1,44 @@ +package org.spongepowered.downloads.test.akka; + +import akka.actor.testkit.typed.javadsl.ActorTestKit; +import akka.actor.testkit.typed.javadsl.BehaviorTestKit; +import akka.actor.typed.ActorSystem; +import akka.actor.typed.Behavior; +import akka.actor.typed.SpawnProtocol; +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import io.micronaut.context.annotation.Bean; +import io.micronaut.context.annotation.Factory; +import io.micronaut.context.annotation.Replaces; +import io.micronaut.core.annotation.NonNull; +import jakarta.inject.Singleton; + +@Factory +public class AkkaTestExtension { + + @Replaces + @Bean + public Behavior testBehavior() { + return SpawnProtocol.create(); + } + + @Replaces + @Bean + public Config testConfig() { + return ConfigFactory.defaultApplication() + .withFallback(BehaviorTestKit.applicationTestConfig()) + .resolve(); + } + + @Bean(preDestroy = "shutdownTestKit") + public ActorTestKit testKit() { + return ActorTestKit.create(); + } + + @Replaces(bean = ActorSystem.class) + @Singleton + public ActorSystem system(@NonNull ActorTestKit kit) { + return kit.system(); + } + +} diff --git a/akka/testkit/src/main/resources/reference.conf b/akka/testkit/src/main/resources/reference.conf new file mode 100644 index 00000000..02455b35 --- /dev/null +++ b/akka/testkit/src/main/resources/reference.conf @@ -0,0 +1,3 @@ +systemofadownload { + clustering = false +} diff --git a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/Artifact.java b/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/Artifact.java deleted file mode 100644 index 94c7ef77..00000000 --- a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/Artifact.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import java.net.URI; -import java.util.Optional; - -@JsonSerialize -public final record Artifact( - @JsonProperty Optional classifier, - @JsonProperty URI downloadUrl, - @JsonProperty String md5, - @JsonProperty String sha1, - @JsonProperty String extension -) { - @JsonCreator - public Artifact { - } - -} diff --git a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactCollection.java b/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactCollection.java deleted file mode 100644 index aab520db..00000000 --- a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactCollection.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import io.vavr.collection.List; - -@JsonDeserialize -public final record ArtifactCollection( - @JsonProperty("assets") List components, - @JsonProperty("coordinates") MavenCoordinates coordinates -) { - - @JsonCreator - public ArtifactCollection { - } - -} diff --git a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactCoordinates.java b/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactCoordinates.java deleted file mode 100644 index 99131c11..00000000 --- a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactCoordinates.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import java.util.StringJoiner; - -@JsonDeserialize -public record ArtifactCoordinates( - @JsonProperty(required = true) String groupId, - @JsonProperty(required = true) String artifactId -) { - @JsonCreator - public ArtifactCoordinates { - } - - public MavenCoordinates version(String version) { - return MavenCoordinates.parse( - new StringJoiner(":").add(this.groupId()).add(this.artifactId()).add(version).toString()); - } - - public String asMavenString() { - return this.groupId() + ":" + this.artifactId(); - } - - /** - * The group id of an artifact, as defined by the Apache Maven documentation. - * See Maven Coordinates. - */ - public String groupId() { - return groupId; - } - - /** - * The artifact id of an artifact, as defined by the Apache Maven documentation. - * See Maven Coordinates. - */ - public String artifactId() { - return artifactId; - } -} diff --git a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactService.java b/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactService.java deleted file mode 100644 index 00508fc5..00000000 --- a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactService.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api; - -import akka.NotUsed; -import com.lightbend.lagom.javadsl.api.Descriptor; -import com.lightbend.lagom.javadsl.api.Service; -import com.lightbend.lagom.javadsl.api.ServiceCall; -import com.lightbend.lagom.javadsl.api.broker.Topic; -import com.lightbend.lagom.javadsl.api.broker.kafka.KafkaProperties; -import com.lightbend.lagom.javadsl.api.transport.Method; -import org.spongepowered.downloads.artifact.api.event.ArtifactUpdate; -import org.spongepowered.downloads.artifact.api.event.GroupUpdate; -import org.spongepowered.downloads.artifact.api.query.ArtifactDetails; -import org.spongepowered.downloads.artifact.api.query.ArtifactRegistration; -import org.spongepowered.downloads.artifact.api.query.GetArtifactsResponse; -import org.spongepowered.downloads.artifact.api.query.GroupRegistration; -import org.spongepowered.downloads.artifact.api.query.GroupResponse; -import org.spongepowered.downloads.artifact.api.query.GroupsResponse; - -public interface ArtifactService extends Service { - - ServiceCall getArtifacts(String groupId); - - ServiceCall registerArtifacts( - String groupId - ); - - ServiceCall registerGroup(); - - ServiceCall, ArtifactDetails.Response> updateDetails(String groupId, String artifactId); - - ServiceCall getGroup(String groupId); - - ServiceCall getGroups(); - - Topic groupTopic(); - - Topic artifactUpdate(); - - @Override - default Descriptor descriptor() { - return Service.named("artifacts") - .withCalls( - Service.restCall(Method.GET, "/artifacts/groups/:groupId", this::getGroup), - Service.restCall(Method.GET, "/artifacts/groups", this::getGroups), - Service.restCall(Method.POST, "/artifacts/groups", this::registerGroup), - Service.restCall(Method.GET, "/artifacts/groups/:groupId/artifacts", this::getArtifacts), - Service.restCall(Method.POST, "/artifacts/groups/:groupId/artifacts", this::registerArtifacts), - Service.restCall(Method.PATCH, "/artifacts/groups/:groupId/artifacts/:artifactId/update", this::updateDetails) - ) - .withTopics( - Service.topic("group-activity", this::groupTopic) - .withProperty(KafkaProperties.partitionKeyStrategy(), GroupUpdate::groupId), - Service.topic("artifact-details-update", this::artifactUpdate) - .withProperty(KafkaProperties.partitionKeyStrategy(), ArtifactUpdate::partitionKey) - ) - .withAutoAcl(true); - } - -} diff --git a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/Group.java b/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/Group.java deleted file mode 100644 index 7e9145d1..00000000 --- a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/Group.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -@JsonDeserialize -public record Group( - @JsonProperty(required = true) String groupCoordinates, - @JsonProperty(required = true) String name, - @JsonProperty(required = true) String website -) { - - @JsonCreator - public Group { - } - -} diff --git a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/MavenCoordinates.java b/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/MavenCoordinates.java deleted file mode 100644 index c1acb521..00000000 --- a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/MavenCoordinates.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import org.apache.maven.artifact.versioning.ComparableVersion; - -import java.util.Objects; -import java.util.StringJoiner; -import java.util.regex.Pattern; - -@JsonDeserialize -public final class MavenCoordinates implements Comparable { - - private static final Pattern MAVEN_REGEX = Pattern.compile("^[-\\w.]+$"); - - /** - * The group id of an artifact, as defined by the Apache Maven documentation. - * See Maven Coordinates. - */ - @JsonProperty(required = true) - public final String groupId; - /** - * The artifact id of an artifact, as defined by the Apache Maven documentation. - * See Maven Coordinates. - */ - @JsonProperty(required = true) - public final String artifactId; - /** - * The version of an artifact, as defined by the Apache Maven documentation. This is - * traditionally specified as a Maven repository searchable version string, such as - * {@code 1.0.0-SNAPSHOT}. - * See Maven Coordinates. - */ - @JsonProperty(required = true) - public final String version; - - @JsonIgnore - public final VersionType versionType; - - @JsonIgnore - private final ComparableVersion mavenVersion; - - /** - * Parses a set of maven formatted coordinates as per - * Apache - * Maven's documentation. - * - * @param coordinates The coordinates delimited by `:` - * @return A parsed set of MavenCoordinates - */ - public static MavenCoordinates parse(final String coordinates) { - final var splitCoordinates = coordinates.split(":"); - if (splitCoordinates.length < 3) { - throw new IllegalArgumentException( - "Coordinates are not formatted or delimited by the `:` character or contains fewer than the required size"); - } - final var groupId = splitCoordinates[0]; - if (!MAVEN_REGEX.asMatchPredicate().test(groupId)) { - throw new IllegalArgumentException("GroupId does not conform to regex rules for a maven group id"); - } - final var artifactId = splitCoordinates[1]; - if (!MAVEN_REGEX.asMatchPredicate().test(artifactId)) { - throw new IllegalArgumentException("ArtifactId does not conform to regex rules for a maven artifact id"); - } - final var version = splitCoordinates[2]; - - VersionType.fromVersion(version); // validates the version is going to be valid somewhat - return new MavenCoordinates(groupId, artifactId, version); - } - - public MavenCoordinates(String coordinates) { - final var splitCoordinates = coordinates.split(":"); - if (splitCoordinates.length < 3) { - throw new IllegalArgumentException( - "Coordinates are not formatted or delimited by the `:` character or contains fewer than the required size"); - } - final var groupId = splitCoordinates[0]; - if (!MAVEN_REGEX.asMatchPredicate().test(groupId)) { - throw new IllegalArgumentException("GroupId does not conform to regex rules for a maven group id"); - } - final var artifactId = splitCoordinates[1]; - if (!MAVEN_REGEX.asMatchPredicate().test(artifactId)) { - throw new IllegalArgumentException("ArtifactId does not conform to regex rules for a maven artifact id"); - } - final var version = splitCoordinates[2]; - - VersionType.fromVersion(version); // validates the version is going to be valid somewhat - this.groupId = groupId; - this.artifactId = artifactId; - this.version = version; - this.versionType = VersionType.fromVersion(version); - this.mavenVersion = new ComparableVersion(version); - } - - @JsonCreator - public MavenCoordinates( - @JsonProperty("groupId") final String groupId, - @JsonProperty("artifactId") final String artifactId, - @JsonProperty("version") final String version - ) { - this.groupId = groupId; - this.artifactId = artifactId; - this.version = version; - this.versionType = VersionType.fromVersion(version); - this.mavenVersion = new ComparableVersion(version); - } - - @JsonIgnore - public String asStandardCoordinates() { - return new StringJoiner(":") - .add(this.groupId) - .add(this.artifactId) - .add(this.versionType.asStandardVersionString(this.version)) - .toString(); - } - - @JsonIgnore - public boolean isSnapshot() { - return this.versionType.isSnapshot(); - } - - @JsonIgnore - public ArtifactCoordinates asArtifactCoordinates() { - return new ArtifactCoordinates(this.groupId, this.artifactId); - } - - @Override - public String toString() { - return new StringJoiner(":") - .add(this.groupId) - .add(this.artifactId) - .add(this.version) - .toString(); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || this.getClass() != o.getClass()) { - return false; - } - final MavenCoordinates that = (MavenCoordinates) o; - return Objects.equals(this.groupId, that.groupId) && Objects.equals( - this.artifactId, that.artifactId) && Objects.equals(this.version, that.version); - } - - @Override - public int hashCode() { - return Objects.hash(this.groupId, this.artifactId, this.version); - } - - @Override - public int compareTo(final MavenCoordinates o) { - final var group = this.groupId.compareTo(o.groupId); - if (group != 0) { - return group; - } - final var artifact = this.artifactId.compareTo(o.artifactId); - if (artifact != 0) { - return artifact; - } - return this.mavenVersion.compareTo(o.mavenVersion); - } -} diff --git a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/VersionType.java b/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/VersionType.java deleted file mode 100644 index 471c4a5c..00000000 --- a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/VersionType.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api; - -import java.util.StringJoiner; -import java.util.regex.Pattern; - -/** - * In conjunction with {@link MavenCoordinates}, can be used to determine the - * version type of the coordinates, and whether - */ -public enum VersionType { - /** - * A timestamp based file snapshot, such as {@code 1.0.0-20210118.163210-1} - * to where it can be interpreted that the {@link #SNAPSHOT snapshot} version - * would be {@code 1.0.0-SNAPSHOT} that happened to build at date time - * {@code January 18th, 2021 at 16h32m10s} and it's the first build. - */ - TIMESTAMP_SNAPSHOT { - @Override - public boolean isSnapshot() { - return true; - } - - @Override - public String asStandardVersionString(final String version) { - final var split = version.split("-"); - final var stringJoiner = new StringJoiner("-"); - for (int i = 0; i < split.length - 2; i++) { - stringJoiner.add(split[i]); - } - - return stringJoiner.add(SNAPSHOT_VERSION).toString(); - } - }, - - /** - * A standard generic snapshot relative version of a release, such as {@code 1.0.0-SNAPSHOT}. - */ - SNAPSHOT { - @Override - public boolean isSnapshot() { - return true; - } - }, - - /** - * A standard release version not abiding by any snapshot guidelines, considered - * final and singular, such as {@code 1.0.0} - */ - RELEASE; - - /* - Simple SNAPSHOT placeholder - */ - private static final String SNAPSHOT_VERSION = "SNAPSHOT"; - - /* - Verifies the pattern that the snapshot version is date.time-build formatted, - enables the pattern match for a timestamped snapshot - */ - private static final Pattern VERSION_FILE_PATTERN = Pattern.compile("^(.*)-(\\d{8}.\\d{6})-(\\d+)$"); - - private static final Pattern TIMESTAMP_TO_REPLACE = Pattern.compile("(\\d{8}.\\d{6})-(\\d+)$"); - - public static VersionType fromVersion(final String version) { - if (version == null || version.isEmpty()) { - throw new IllegalArgumentException("Version cannot be empty"); - } - // Simple check to find out if the version ends with SNAPSHOT. - if (version.regionMatches( - true, - version.length() - SNAPSHOT_VERSION.length(), - SNAPSHOT_VERSION, - 0, - SNAPSHOT_VERSION.length() - )) { - return SNAPSHOT; - } - if (VERSION_FILE_PATTERN.matcher(version).matches()) { - return TIMESTAMP_SNAPSHOT; - } - return RELEASE; - } - - public boolean isSnapshot() { - return false; - } - - public String asStandardVersionString(final String version) { - return version; - } -} diff --git a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/event/ArtifactUpdate.java b/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/event/ArtifactUpdate.java deleted file mode 100644 index 828ea500..00000000 --- a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/event/ArtifactUpdate.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api.event; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(ArtifactUpdate.ArtifactRegistered.class), - @JsonSubTypes.Type(ArtifactUpdate.GitRepositoryAssociated.class), - @JsonSubTypes.Type(ArtifactUpdate.WebsiteUpdated.class), - @JsonSubTypes.Type(ArtifactUpdate.IssuesUpdated.class), - @JsonSubTypes.Type(ArtifactUpdate.DisplayNameUpdated.class), -}) -public interface ArtifactUpdate extends Jsonable { - - ArtifactCoordinates coordinates(); - - default String partitionKey() { - return this.coordinates().asMavenString(); - } - - @JsonTypeName("registered") - @JsonDeserialize - final record ArtifactRegistered( - ArtifactCoordinates coordinates - ) implements ArtifactUpdate { - - @JsonCreator - public ArtifactRegistered { - } - } - - @JsonTypeName("git-repository") - @JsonDeserialize - final record GitRepositoryAssociated( - ArtifactCoordinates coordinates, - String repository - ) implements ArtifactUpdate { - - @JsonCreator - public GitRepositoryAssociated { - } - } - - @JsonTypeName("website") - @JsonDeserialize - final record WebsiteUpdated( - ArtifactCoordinates coordinates, - String url - ) implements ArtifactUpdate { - - @JsonCreator - public WebsiteUpdated { - } - } - - @JsonTypeName("issues") - @JsonDeserialize - final record IssuesUpdated( - ArtifactCoordinates coordinates, - String url - ) implements ArtifactUpdate { - - @JsonCreator - public IssuesUpdated { - } - } - - @JsonTypeName("displayName") - @JsonDeserialize - final record DisplayNameUpdated( - ArtifactCoordinates coordinates, - String displayName - ) implements ArtifactUpdate { - - @JsonCreator - public DisplayNameUpdated { - } - } - -} diff --git a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/event/GroupUpdate.java b/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/event/GroupUpdate.java deleted file mode 100644 index 970a873e..00000000 --- a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/event/GroupUpdate.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api.event; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; - -import java.io.Serial; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(GroupUpdate.GroupRegistered.class), - @JsonSubTypes.Type(GroupUpdate.ArtifactRegistered.class), -}) -public interface GroupUpdate extends Jsonable { - - String groupId(); - - @JsonTypeName("group-registered") - @JsonDeserialize - record GroupRegistered(String groupId, String name, String website) - implements GroupUpdate { - - @JsonCreator - public GroupRegistered { - } - - } - - @JsonTypeName("artifact-registered") - @JsonDeserialize - final record ArtifactRegistered(ArtifactCoordinates coordinates) implements GroupUpdate { - - @Serial private static final long serialVersionUID = 6319289932327553919L; - - @JsonCreator - public ArtifactRegistered { - } - - - @Override - public String groupId() { - return this.coordinates.groupId(); - } - } - -} diff --git a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/ArtifactDetails.java b/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/ArtifactDetails.java deleted file mode 100644 index 53c9567f..00000000 --- a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/ArtifactDetails.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api.query; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.lightbend.lagom.javadsl.api.transport.BadRequest; -import io.vavr.control.Either; -import io.vavr.control.Try; - -import java.net.URL; - -public final class ArtifactDetails { - - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, - property = "type") - @JsonSubTypes({ - @JsonSubTypes.Type(value = Update.Website.class, - name = "website"), - @JsonSubTypes.Type(value = Update.DisplayName.class, - name = "displayName"), - @JsonSubTypes.Type(value = Update.Issues.class, - name = "issues"), - @JsonSubTypes.Type(value = Update.GitRepository.class, - name = "gitRepository"), - }) - @JsonDeserialize - public sealed interface Update { - - Either validate(); - - record Website( - @JsonProperty(required = true) String website - ) implements Update { - - @JsonCreator - public Website { - } - - @Override - public Either validate() { - return Try.of(() -> new URL(this.website())) - .toEither(() -> new BadRequest(String.format("Malformed url: %s", this.website()))); - } - } - - record DisplayName( - @JsonProperty(required = true) String display - ) implements Update { - - @JsonCreator - public DisplayName { - } - - @Override - public Either validate() { - return Either.right(this.display.trim()); - } - } - - record Issues( - @JsonProperty(required = true) String issues - ) implements Update { - @JsonCreator - public Issues { - } - - @Override - public Either validate() { - return Try.of(() -> new URL(this.issues())) - .toEither(() -> new BadRequest(String.format("Malformed url: %s", this.issues()))); - } - } - - record GitRepository( - @JsonProperty(required = true) String gitRepo - ) implements Update { - - @JsonCreator - public GitRepository { - } - - @Override - public Either validate() { - return Try.of(() -> new URL(this.gitRepo())) - .toEither(() -> new BadRequest(String.format("Malformed url: %s", this.gitRepo()))); - } - } - } - - @JsonSerialize - public record Response( - String name, - String displayName, - String website, - String issues, - String gitRepo - ) { - - } - - -} diff --git a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/ArtifactRegistration.java b/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/ArtifactRegistration.java deleted file mode 100644 index 362cdd88..00000000 --- a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/ArtifactRegistration.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api.query; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; - -public final class ArtifactRegistration { - - @JsonSerialize - public record RegisterArtifact( - @JsonProperty(required = true) String artifactId, - @JsonProperty(required = true) String displayName - ) { - - @JsonCreator - public RegisterArtifact(final String artifactId, final String displayName) { - this.artifactId = artifactId; - this.displayName = displayName; - } - - } - - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, - property = "type") - @JsonSubTypes({ - @JsonSubTypes.Type(value = Response.GroupMissing.class, - name = "UnknownGroup"), - @JsonSubTypes.Type(value = Response.ArtifactRegistered.class, - name = "RegisteredArtifact"), - @JsonSubTypes.Type(value = Response.ArtifactAlreadyRegistered.class, - name = "AlreadyRegistered"), - }) - public sealed interface Response extends Jsonable { - - @JsonSerialize - record ArtifactRegistered(@JsonProperty ArtifactCoordinates coordinates) implements Response { - - } - - @JsonSerialize - record ArtifactAlreadyRegistered( - @JsonProperty String artifactName, - @JsonProperty String groupId - ) implements Response { - - } - - @JsonSerialize - record GroupMissing(@JsonProperty("groupId") String s) implements Response { - - } - - } -} diff --git a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/GetArtifactsResponse.java b/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/GetArtifactsResponse.java deleted file mode 100644 index eec64a44..00000000 --- a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/GetArtifactsResponse.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api.query; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.List; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = GetArtifactsResponse.GroupMissing.class, name = "UnknownGroup"), - @JsonSubTypes.Type(value = GetArtifactsResponse.ArtifactsAvailable.class, name = "Artifacts"), -}) -public sealed interface GetArtifactsResponse extends Jsonable { - - @JsonSerialize - record GroupMissing(@JsonProperty String groupRequested) implements GetArtifactsResponse { - - @JsonCreator - public GroupMissing { - } - - } - - @JsonSerialize - record ArtifactsAvailable(@JsonProperty List artifactIds) - implements GetArtifactsResponse { - - @JsonCreator - public ArtifactsAvailable { - } - - } -} diff --git a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupRegistration.java b/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupRegistration.java deleted file mode 100644 index b9d21ab4..00000000 --- a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupRegistration.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api.query; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.Group; - -public final class GroupRegistration { - - @JsonDeserialize - public record RegisterGroupRequest( - @JsonProperty(required = true) String name, - @JsonProperty(required = true) String groupCoordinates, - @JsonProperty(required = true) String website - ) { - - @JsonCreator - public RegisterGroupRequest { } - - } - - public interface Response extends Jsonable { - - record GroupAlreadyRegistered(String groupNameRequested) implements Response { - } - - record GroupRegistered(Group group) implements Response { - - } - } -} diff --git a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupResponse.java b/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupResponse.java deleted file mode 100644 index a973e23f..00000000 --- a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupResponse.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api.query; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.Group; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = GroupResponse.Missing.class, name = "MissingGroup"), - @JsonSubTypes.Type(value = GroupResponse.Available.class, name = "Group") -}) -public sealed interface GroupResponse extends Jsonable { - - @JsonSerialize - record Missing(@JsonProperty String groupId) implements GroupResponse { - @JsonCreator - public Missing(final String groupId) { - this.groupId = groupId; - } - - } - - @JsonSerialize - record Available(@JsonProperty Group group) implements GroupResponse { - - @JsonCreator - public Available(final Group group) { - this.group = group; - } - - } - -} diff --git a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupsResponse.java b/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupsResponse.java deleted file mode 100644 index 78416658..00000000 --- a/artifact-api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupsResponse.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api.query; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.vavr.collection.List; -import org.spongepowered.downloads.artifact.api.Group; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = GroupsResponse.Available.class, name = "Groups") -}) -public interface GroupsResponse { - - @JsonSerialize - record Available(@JsonProperty List groups) - implements GroupsResponse { - @JsonCreator - public Available { - } - } -} diff --git a/artifact-query-api/src/main/java/org/spongepowered/downloads/artifacts/query/api/ArtifactQueryService.java b/artifact-query-api/src/main/java/org/spongepowered/downloads/artifacts/query/api/ArtifactQueryService.java deleted file mode 100644 index 7e002ddc..00000000 --- a/artifact-query-api/src/main/java/org/spongepowered/downloads/artifacts/query/api/ArtifactQueryService.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.query.api; - -import akka.NotUsed; -import com.lightbend.lagom.javadsl.api.Descriptor; -import com.lightbend.lagom.javadsl.api.Service; -import com.lightbend.lagom.javadsl.api.ServiceCall; -import com.lightbend.lagom.javadsl.api.transport.Method; - -public interface ArtifactQueryService extends Service { - - ServiceCall getArtifactDetails(String groupId, String artifactId); - - @Override - default Descriptor descriptor() { - return Service.named("artifact-query") - .withCalls( - Service.restCall(Method.GET, "/artifacts-query/groups/:groupId/artifacts/:artifactId", this::getArtifactDetails) - ) - .withAutoAcl(true); - } -} diff --git a/artifact-query-api/src/main/java/org/spongepowered/downloads/artifacts/query/api/GetArtifactDetailsResponse.java b/artifact-query-api/src/main/java/org/spongepowered/downloads/artifacts/query/api/GetArtifactDetailsResponse.java deleted file mode 100644 index 5113f526..00000000 --- a/artifact-query-api/src/main/java/org/spongepowered/downloads/artifacts/query/api/GetArtifactDetailsResponse.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.query.api; - -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.vavr.collection.Map; -import io.vavr.collection.SortedSet; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; - - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = GetArtifactDetailsResponse.RetrievedArtifact.class, - name = "latest") -}) -public sealed interface GetArtifactDetailsResponse { - - @JsonSerialize - record RetrievedArtifact( - ArtifactCoordinates coordinates, - String displayName, - String website, - String gitRepository, - String issues, - Map> tags - ) implements GetArtifactDetailsResponse { - - } -} diff --git a/artifacts/README.md b/artifacts/README.md deleted file mode 100644 index 47a547a0..00000000 --- a/artifacts/README.md +++ /dev/null @@ -1,87 +0,0 @@ -## Micronaut 3.8.2 Documentation - -- [User Guide](https://docs.micronaut.io/3.8.2/guide/index.html) -- [API Reference](https://docs.micronaut.io/3.8.2/api/index.html) -- [Configuration Reference](https://docs.micronaut.io/3.8.2/guide/configurationreference.html) -- [Micronaut Guides](https://guides.micronaut.io/index.html) ---- - -- [Shadow Gradle Plugin](https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow) -## Feature github-workflow-ci documentation - -- [https://docs.github.com/en/actions](https://docs.github.com/en/actions) - - -## Feature test-resources documentation - -- [Micronaut Test Resources documentation](https://micronaut-projects.github.io/micronaut-test-resources/latest/guide/) - - -## Feature micronaut-aot documentation - -- [Micronaut AOT documentation](https://micronaut-projects.github.io/micronaut-aot/latest/guide/) - - -## Feature security-ldap documentation - -- [Micronaut Security LDAP documentation](https://micronaut-projects.github.io/micronaut-security/latest/guide/index.html#ldap) - - -## Feature security-jwt documentation - -- [Micronaut Security JWT documentation](https://micronaut-projects.github.io/micronaut-security/latest/guide/index.html) - - -## Feature data-r2dbc documentation - -- [Micronaut Data R2DBC documentation](https://micronaut-projects.github.io/micronaut-data/latest/guide/#dbc) - -- [https://r2dbc.io](https://r2dbc.io) - - -## Feature kafka documentation - -- [Micronaut Kafka Messaging documentation](https://micronaut-projects.github.io/micronaut-kafka/latest/guide/index.html) - - -## Feature openapi documentation - -- [Micronaut OpenAPI Support documentation](https://micronaut-projects.github.io/micronaut-openapi/latest/guide/index.html) - -- [https://www.openapis.org](https://www.openapis.org) - - -## Feature cache-caffeine documentation - -- [Micronaut Caffeine Cache documentation](https://micronaut-projects.github.io/micronaut-cache/latest/guide/index.html) - -- [https://github.com/ben-manes/caffeine](https://github.com/ben-manes/caffeine) - - -## Feature r2dbc documentation - -- [Micronaut R2DBC documentation](https://micronaut-projects.github.io/micronaut-r2dbc/latest/guide/) - -- [https://r2dbc.io](https://r2dbc.io) - - -## Feature junit-params documentation - -- [https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests](https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests) - - -## Feature discovery-kubernetes documentation - -- [Micronaut Kubernetes Service Discovery documentation](https://micronaut-projects.github.io/micronaut-kubernetes/latest/guide/#service-discovery) - - -## Feature http-client documentation - -- [Micronaut HTTP Client documentation](https://docs.micronaut.io/latest/guide/index.html#httpClient) - - -## Feature serialization-jackson documentation - -- [Micronaut Serialization Jackson Core documentation](https://micronaut-projects.github.io/micronaut-serialization/latest/guide/) - - diff --git a/artifacts/api/build.gradle.kts b/artifacts/api/build.gradle.kts index 399e5cbf..23911298 100644 --- a/artifacts/api/build.gradle.kts +++ b/artifacts/api/build.gradle.kts @@ -1,9 +1,15 @@ -val jacksonVersion:String by project -dependencies { - api(platform("com.fasterxml.jackson:jackson-bom:${jacksonVersion}")) - api("com.fasterxml.jackson:jackson-core") - api("com.fasterxml.jackson.core:jackson-databind") - api("com.fasterxml.jackson.core:jackson-annotations") +version = "0.1" +group = "org.spongepowered.downloads" + +plugins { + `java-library` +} + +dependencies { + api(platform(libs.jacksonBom)) + api(libs.bundles.serder) + api(libs.maven) + api(libs.vavr) } diff --git a/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactCoordinates.java b/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactCoordinates.java index 99131c11..4bfc99db 100644 --- a/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactCoordinates.java +++ b/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactCoordinates.java @@ -30,6 +30,17 @@ import java.util.StringJoiner; +/** + * Representation of a simplified {@link MavenCoordinates} with representation + * of only the {@link #groupId()} and {@link #artifactId()}. In general, this is + * to represent an artifact as a whole, rather than any specific version or + * variant. + * + * @param groupId The group id of an artifact, as defined by the Apache Maven documentation. + * See Maven Coordinates. + * @param artifactId The artifact id of an artifact, as defined by the Apache Maven documentation. + * See Maven Coordinates. + */ @JsonDeserialize public record ArtifactCoordinates( @JsonProperty(required = true) String groupId, @@ -48,19 +59,4 @@ public String asMavenString() { return this.groupId() + ":" + this.artifactId(); } - /** - * The group id of an artifact, as defined by the Apache Maven documentation. - * See Maven Coordinates. - */ - public String groupId() { - return groupId; - } - - /** - * The artifact id of an artifact, as defined by the Apache Maven documentation. - * See Maven Coordinates. - */ - public String artifactId() { - return artifactId; - } } diff --git a/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactService.java b/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactService.java deleted file mode 100644 index 00508fc5..00000000 --- a/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/ArtifactService.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifact.api; - -import akka.NotUsed; -import com.lightbend.lagom.javadsl.api.Descriptor; -import com.lightbend.lagom.javadsl.api.Service; -import com.lightbend.lagom.javadsl.api.ServiceCall; -import com.lightbend.lagom.javadsl.api.broker.Topic; -import com.lightbend.lagom.javadsl.api.broker.kafka.KafkaProperties; -import com.lightbend.lagom.javadsl.api.transport.Method; -import org.spongepowered.downloads.artifact.api.event.ArtifactUpdate; -import org.spongepowered.downloads.artifact.api.event.GroupUpdate; -import org.spongepowered.downloads.artifact.api.query.ArtifactDetails; -import org.spongepowered.downloads.artifact.api.query.ArtifactRegistration; -import org.spongepowered.downloads.artifact.api.query.GetArtifactsResponse; -import org.spongepowered.downloads.artifact.api.query.GroupRegistration; -import org.spongepowered.downloads.artifact.api.query.GroupResponse; -import org.spongepowered.downloads.artifact.api.query.GroupsResponse; - -public interface ArtifactService extends Service { - - ServiceCall getArtifacts(String groupId); - - ServiceCall registerArtifacts( - String groupId - ); - - ServiceCall registerGroup(); - - ServiceCall, ArtifactDetails.Response> updateDetails(String groupId, String artifactId); - - ServiceCall getGroup(String groupId); - - ServiceCall getGroups(); - - Topic groupTopic(); - - Topic artifactUpdate(); - - @Override - default Descriptor descriptor() { - return Service.named("artifacts") - .withCalls( - Service.restCall(Method.GET, "/artifacts/groups/:groupId", this::getGroup), - Service.restCall(Method.GET, "/artifacts/groups", this::getGroups), - Service.restCall(Method.POST, "/artifacts/groups", this::registerGroup), - Service.restCall(Method.GET, "/artifacts/groups/:groupId/artifacts", this::getArtifacts), - Service.restCall(Method.POST, "/artifacts/groups/:groupId/artifacts", this::registerArtifacts), - Service.restCall(Method.PATCH, "/artifacts/groups/:groupId/artifacts/:artifactId/update", this::updateDetails) - ) - .withTopics( - Service.topic("group-activity", this::groupTopic) - .withProperty(KafkaProperties.partitionKeyStrategy(), GroupUpdate::groupId), - Service.topic("artifact-details-update", this::artifactUpdate) - .withProperty(KafkaProperties.partitionKeyStrategy(), ArtifactUpdate::partitionKey) - ) - .withAutoAcl(true); - } - -} diff --git a/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/ArtifactDetails.java b/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/ArtifactDetails.java index 27247571..56e6a717 100644 --- a/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/ArtifactDetails.java +++ b/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/ArtifactDetails.java @@ -26,7 +26,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @@ -86,15 +85,22 @@ record GitRepository( } @JsonSerialize - public record Response( - String name, - String displayName, - String website, - String issues, - String gitRepo - ) { + @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) + public sealed interface Response { + record Ok( + String name, + String displayName, + String website, + String issues, + String gitRepo + ) implements Response{ + + } + + record NotFound(String message) implements Response {} } + } diff --git a/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/ArtifactRegistration.java b/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/ArtifactRegistration.java index 362cdd88..d7f0cc82 100644 --- a/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/ArtifactRegistration.java +++ b/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/ArtifactRegistration.java @@ -29,7 +29,6 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.lightbend.lagom.serialization.Jsonable; import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; public final class ArtifactRegistration { @@ -58,7 +57,7 @@ public RegisterArtifact(final String artifactId, final String displayName) { @JsonSubTypes.Type(value = Response.ArtifactAlreadyRegistered.class, name = "AlreadyRegistered"), }) - public sealed interface Response extends Jsonable { + public sealed interface Response { @JsonSerialize record ArtifactRegistered(@JsonProperty ArtifactCoordinates coordinates) implements Response { diff --git a/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupRegistration.java b/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupRegistration.java index 040345fb..b127e321 100644 --- a/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupRegistration.java +++ b/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupRegistration.java @@ -32,18 +32,19 @@ public final class GroupRegistration { @JsonDeserialize - public record RegisterGroupRequest( - @JsonProperty(required = true) String name, - @JsonProperty(required = true) String groupCoordinates, - @JsonProperty(required = true) String website + public record RegisterGroupRequest( + @JsonProperty(required = true) String name, + @JsonProperty(required = true) String groupCoordinates, + @JsonProperty(required = true) String website ) { - @JsonCreator - public RegisterGroupRequest { } - + @JsonCreator + public RegisterGroupRequest { } - public interface Response { + } + + public sealed interface Response { record GroupAlreadyRegistered(String groupNameRequested) implements Response { } diff --git a/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupResponse.java b/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupResponse.java index a973e23f..628fc841 100644 --- a/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupResponse.java +++ b/artifacts/api/src/main/java/org/spongepowered/downloads/artifact/api/query/GroupResponse.java @@ -28,16 +28,12 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.lightbend.lagom.serialization.Jsonable; import org.spongepowered.downloads.artifact.api.Group; -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = GroupResponse.Missing.class, name = "MissingGroup"), - @JsonSubTypes.Type(value = GroupResponse.Available.class, name = "Group") -}) -public sealed interface GroupResponse extends Jsonable { +@JsonTypeInfo(use = JsonTypeInfo.Id.NONE) +public sealed interface GroupResponse { @JsonSerialize record Missing(@JsonProperty String groupId) implements GroupResponse { diff --git a/artifacts/build.gradle.kts b/artifacts/build.gradle.kts index 8c40ab6c..b28b04f6 100644 --- a/artifacts/build.gradle.kts +++ b/artifacts/build.gradle.kts @@ -1,18 +1,3 @@ -val akkaVersion: String by project -val scalaVersion: String by project -val akkaManagementVersion: String by project -val akkaProjection: String by project - -subprojects { - dependencies { - implementation(project(":akka")) - } -} -dependencies { - -} - - diff --git a/artifacts/events/build.gradle.kts b/artifacts/events/build.gradle.kts index 61ccd4eb..d2abb374 100644 --- a/artifacts/events/build.gradle.kts +++ b/artifacts/events/build.gradle.kts @@ -1,12 +1,17 @@ -val akkaVersion: String by project -val scalaVersion: String by project -val akkaManagementVersion: String by project -val akkaProjection: String by project +plugins { + `java-library` +} + +java { + sourceCompatibility = JavaVersion.toVersion("20") + targetCompatibility = JavaVersion.toVersion("20") +} dependencies { api(project(":artifacts:api")) + api(project(":akka")) } diff --git a/akka/gradle.properties b/artifacts/events/gradle.properties similarity index 100% rename from akka/gradle.properties rename to artifacts/events/gradle.properties diff --git a/artifacts/events/src/main/java/org/spongepowered/downloads/artifacts/events/ArtifactEvent.java b/artifacts/events/src/main/java/org/spongepowered/downloads/artifacts/events/ArtifactEvent.java index f2fe681c..7afa5009 100644 --- a/artifacts/events/src/main/java/org/spongepowered/downloads/artifacts/events/ArtifactEvent.java +++ b/artifacts/events/src/main/java/org/spongepowered/downloads/artifacts/events/ArtifactEvent.java @@ -1,11 +1,14 @@ package org.spongepowered.downloads.artifacts.events; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.spongepowered.downloads.akka.AkkaSerializable; import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -public sealed interface ArtifactEvent { +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +public sealed interface ArtifactEvent extends AkkaSerializable { ArtifactCoordinates coordinates(); diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/DetailsEvent.java b/artifacts/events/src/main/java/org/spongepowered/downloads/artifacts/events/DetailsEvent.java similarity index 70% rename from artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/DetailsEvent.java rename to artifacts/events/src/main/java/org/spongepowered/downloads/artifacts/events/DetailsEvent.java index 80886e24..eadddd99 100644 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/DetailsEvent.java +++ b/artifacts/events/src/main/java/org/spongepowered/downloads/artifacts/events/DetailsEvent.java @@ -22,37 +22,21 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.downloads.artifacts.server.details; +package org.spongepowered.downloads.artifacts.events; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.javadsl.persistence.AggregateEvent; -import com.lightbend.lagom.javadsl.persistence.AggregateEventShards; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTag; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTagger; -import com.lightbend.lagom.serialization.Jsonable; +import org.spongepowered.downloads.akka.AkkaSerializable; import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = DetailsEvent.ArtifactRegistered.class, name = "registered"), - @JsonSubTypes.Type(value = DetailsEvent.ArtifactDetailsUpdated.class, name = "details"), - @JsonSubTypes.Type(value = DetailsEvent.ArtifactIssuesUpdated.class, name = "issues"), - @JsonSubTypes.Type(value = DetailsEvent.ArtifactGitRepositoryUpdated.class, name = "git-repo"), - @JsonSubTypes.Type(value = DetailsEvent.ArtifactWebsiteUpdated.class, name = "website"), -}) -public interface DetailsEvent extends AggregateEvent, Jsonable { - - AggregateEventShards TAG = AggregateEventTag.sharded(DetailsEvent.class, 3); - - @Override - default AggregateEventTagger aggregateTag() { - return TAG; - } +public interface DetailsEvent extends AkkaSerializable { @JsonDeserialize + @JsonTypeName("registered") record ArtifactRegistered( ArtifactCoordinates coordinates ) implements DetailsEvent { @@ -62,6 +46,7 @@ record ArtifactRegistered( } @JsonDeserialize + @JsonTypeName("details") record ArtifactDetailsUpdated( ArtifactCoordinates coordinates, String displayName @@ -73,6 +58,7 @@ record ArtifactDetailsUpdated( } @JsonDeserialize + @JsonTypeName("issues") record ArtifactIssuesUpdated( ArtifactCoordinates coordinates, String url @@ -84,6 +70,7 @@ record ArtifactIssuesUpdated( } @JsonDeserialize + @JsonTypeName("git-repo") record ArtifactGitRepositoryUpdated( ArtifactCoordinates coordinates, String gitRepo @@ -95,6 +82,7 @@ record ArtifactGitRepositoryUpdated( } @JsonDeserialize + @JsonTypeName("website") record ArtifactWebsiteUpdated( ArtifactCoordinates coordinates, String url diff --git a/artifacts/events/src/main/java/org/spongepowered/downloads/artifacts/events/GroupUpdate.java b/artifacts/events/src/main/java/org/spongepowered/downloads/artifacts/events/GroupUpdate.java index fd838b95..9f4253bc 100644 --- a/artifacts/events/src/main/java/org/spongepowered/downloads/artifacts/events/GroupUpdate.java +++ b/artifacts/events/src/main/java/org/spongepowered/downloads/artifacts/events/GroupUpdate.java @@ -25,20 +25,14 @@ package org.spongepowered.downloads.artifacts.events; import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.spongepowered.downloads.akka.AkkaSerializable; import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import java.io.Serial; - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(GroupUpdate.GroupRegistered.class), - @JsonSubTypes.Type(GroupUpdate.ArtifactRegistered.class), -}) -public interface GroupUpdate { +public sealed interface GroupUpdate extends AkkaSerializable { String groupId(); @@ -57,8 +51,6 @@ record GroupRegistered(String groupId, String name, String website) @JsonDeserialize final record ArtifactRegistered(ArtifactCoordinates coordinates) implements GroupUpdate { - @Serial private static final long serialVersionUID = 6319289932327553919L; - @JsonCreator public ArtifactRegistered { } diff --git a/artifacts/gradle.properties b/artifacts/gradle.properties deleted file mode 100644 index a9d280a0..00000000 --- a/artifacts/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -micronautVersion=3.8.2 diff --git a/artifacts/gradle/wrapper/gradle-wrapper.properties b/artifacts/gradle/wrapper/gradle-wrapper.properties index ae04661e..fae08049 100644 --- a/artifacts/gradle/wrapper/gradle-wrapper.properties +++ b/artifacts/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/artifacts/micronaut-cli.yml b/artifacts/micronaut-cli.yml deleted file mode 100644 index dc960e1f..00000000 --- a/artifacts/micronaut-cli.yml +++ /dev/null @@ -1,6 +0,0 @@ -applicationType: default -defaultPackage: org.spongepowered.downloads.artifacts -testFramework: junit -sourceLanguage: java -buildTool: gradle -features: [annotation-api, app-name, cache-caffeine, data, data-r2dbc, discovery-kubernetes, github-workflow-ci, graalvm, gradle, h2, http-client, java, java-application, junit, junit-params, kafka, logback, micronaut-aot, micronaut-build, netty-server, openapi, r2dbc, readme, security-annotations, security-jwt, security-ldap, serialization-jackson, shade, test-resources, toml, toml-build] diff --git a/artifacts/server/build.gradle.kts b/artifacts/server/build.gradle.kts index 21f64ea2..48d5dc38 100644 --- a/artifacts/server/build.gradle.kts +++ b/artifacts/server/build.gradle.kts @@ -1,79 +1,81 @@ +import io.micronaut.gradle.testresources.StartTestResourcesService +import io.micronaut.testresources.buildtools.KnownModules - -val akkaVersion: String by project -val scalaVersion: String by project -val akkaManagementVersion: String by project -val akkaProjection: String by project -val vavr: String by project +plugins { + id("io.micronaut.application") + id("io.micronaut.test-resources") + id("com.github.johnrengelman.shadow") +} -tasks { - dockerBuild { - images.add("${project.name}:${project.version}") - } - dockerBuildNative { - images.add("${project.name}:${project.version}") - - } -} -graalvmNative.toolchainDetection.set(false) micronaut { + runtime("netty") testRuntime("junit5") processing { incremental(true) - annotations("systemofadownload.*") + annotations("org.spongepowered.downloads.artifacts.*") } testResources { - additionalModules.add("r2dbc-postgresql") + enabled.set(true) + sharedServer.set(true) + additionalModules.addAll(KnownModules.R2DBC_POSTGRESQL) } } -graalvmNative { - binaries { - named("main") { - imageName.set("mn-graalvm-application") - buildArgs("--verboase") - } + +tasks { + test { + useJUnitPlatform() } } +tasks.withType().configureEach { + useClassDataSharing.set(false) +} + +graalvmNative.toolchainDetection.set(false) dependencies { implementation(project(":artifacts:api")) - implementation("io.vavr:vavr:${vavr}") + implementation(project(":artifacts:events")) + implementation(project(":akka")) + implementation(libs.vavr) annotationProcessor("io.micronaut.data:micronaut-data-processor") - annotationProcessor("io.micronaut:micronaut-http-validation") - annotationProcessor("io.micronaut.openapi:micronaut-openapi") - annotationProcessor("io.micronaut.security:micronaut-security-annotations") + annotationProcessor("io.micronaut.validation:micronaut-validation-processor") annotationProcessor("io.micronaut.serde:micronaut-serde-processor") - implementation("com.ongres.scram:client:2.1") - implementation("io.micronaut:micronaut-http-client") implementation("io.micronaut:micronaut-jackson-databind") - implementation("io.micronaut.data:micronaut-data-r2dbc") - implementation("io.micronaut.liquibase:micronaut-liquibase") - implementation("io.micronaut.reactor:micronaut-reactor") - implementation("io.micronaut.reactor:micronaut-reactor-http-client") - implementation("io.micronaut.security:micronaut-security-ldap") implementation("io.micronaut.serde:micronaut-serde-jackson") - implementation("io.micronaut.toml:micronaut-toml") - implementation("io.micronaut.xml:micronaut-jackson-xml") - implementation("io.swagger.core.v3:swagger-annotations") + implementation("io.micronaut:micronaut-http-server-netty") + + runtimeOnly("org.yaml:snakeyaml") + + implementation(libs.bundles.appSerder) + implementation(libs.bundles.akkaManagement) + implementation(libs.bundles.actorsPersistence) + + // databases + implementation("io.micronaut.data:micronaut-data-r2dbc") +// implementation("io.micronaut.sql:micronaut-vertx-pg-client") +// implementation("io.micronaut.sql:micronaut-hibernate-reactive") implementation("io.vertx:vertx-pg-client") - implementation("jakarta.annotation:jakarta.annotation-api") - implementation(platform("com.typesafe.akka:akka-bom_${scalaVersion}:${akkaVersion}")) - implementation("com.typesafe.akka:akka-actor-typed_${scalaVersion}") - implementation("com.typesafe.akka:akka-persistence-typed_${scalaVersion}") - implementation("com.lightbend.akka:akka-projection-core_${scalaVersion}") - implementation("com.typesafe.akka:akka-cluster-sharding-typed_${scalaVersion}") - implementation("com.typesafe.akka:akka-cluster-typed_${scalaVersion}") - implementation("com.typesafe.akka:akka-discovery_${scalaVersion}") - implementation("com.typesafe.akka:akka-discovery_${scalaVersion}") - implementation("com.lightbend.akka.management:akka-management_${scalaVersion}:${akkaManagementVersion}") - implementation("com.lightbend.akka.management:akka-management-cluster-bootstrap_${scalaVersion}:${akkaManagementVersion}") - - runtimeOnly("ch.qos.logback:logback-classic") + runtimeOnly(libs.postgres.r2dbc) runtimeOnly("org.postgresql:postgresql") - runtimeOnly("org.postgresql:r2dbc-postgresql") + + + implementation("io.micronaut.liquibase:micronaut-liquibase") + implementation("io.micronaut:micronaut-http-client-jdk") + testImplementation("io.micronaut.testresources:micronaut-test-resources-extensions-junit-platform") + testImplementation("org.junit.jupiter:junit-jupiter-api") + testImplementation("io.micronaut.test:micronaut-test-junit5") + testImplementation(project(":akka:testkit")) + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") + testResourcesService("org.postgresql:postgresql") compileOnly("org.graalvm.nativeimage:svm") - implementation("io.micronaut:micronaut-validation") + + testImplementation("org.junit.jupiter:junit-jupiter-engine") + + +//// compileOnly("org.graalvm.nativeimage:svm") +// +// implementation("io.micronaut:micronaut-validation") } diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/Application.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/Application.java index 96a9adb2..bd12b8ba 100644 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/Application.java +++ b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/Application.java @@ -3,34 +3,23 @@ import akka.actor.typed.ActorSystem; import akka.actor.typed.SpawnProtocol; import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import io.micronaut.context.annotation.Bean; import io.micronaut.context.annotation.Factory; -import io.micronaut.context.event.ApplicationEventListener; import io.micronaut.runtime.Micronaut; import io.micronaut.runtime.event.annotation.EventListener; import io.micronaut.runtime.server.event.ServerStartupEvent; -import io.swagger.v3.oas.annotations.*; -import io.swagger.v3.oas.annotations.info.*; import jakarta.inject.Inject; import jakarta.inject.Singleton; -import org.spongepowered.downloads.artifacts.server.global.GlobalManager; -@OpenAPIDefinition( - info = @Info( - title = "artifacts", - version = "0.0" - ) -) @Singleton @Factory public class Application { - private final ActorSystem system; + private final ActorSystem system; private final ClusterSharding sharding; @Inject public Application( - final ActorSystem system, + final ActorSystem system, final ClusterSharding sharding ) { this.system = system; @@ -43,6 +32,5 @@ public static void main(String[] args) { @EventListener public void onApplicationEvent(final ServerStartupEvent event) { - } } diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/ArtifactDetailsEntity.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/ArtifactDetailsEntity.java index 9f014a26..f6462d9a 100644 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/ArtifactDetailsEntity.java +++ b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/ArtifactDetailsEntity.java @@ -34,27 +34,23 @@ import akka.persistence.typed.javadsl.CommandHandlerWithReply; import akka.persistence.typed.javadsl.EventHandler; import akka.persistence.typed.javadsl.EventSourcedBehaviorWithEnforcedReplies; -import com.lightbend.lagom.javadsl.api.transport.NotFound; -import com.lightbend.lagom.javadsl.persistence.AkkaTaggerAdapter; -import io.vavr.control.Either; +import io.micronaut.http.HttpResponse; import org.spongepowered.downloads.artifact.api.query.ArtifactDetails; -import org.spongepowered.downloads.artifact.details.state.DetailsState; -import org.spongepowered.downloads.artifact.details.state.EmptyState; -import org.spongepowered.downloads.artifact.details.state.PopulatedState; +import org.spongepowered.downloads.artifacts.events.DetailsEvent; +import org.spongepowered.downloads.artifacts.server.details.state.DetailsState; +import org.spongepowered.downloads.artifacts.server.details.state.EmptyState; +import org.spongepowered.downloads.artifacts.server.details.state.PopulatedState; import java.util.List; -import java.util.Set; -import java.util.function.Function; public class ArtifactDetailsEntity extends EventSourcedBehaviorWithEnforcedReplies { - private static final Either NOT_FOUND = Either.left( - new NotFound("group or artifact not found")); + + private static final HttpResponse NOT_FOUND = HttpResponse.notFound(new ArtifactDetails.Response.NotFound("group or artifact not found")); public static EntityTypeKey ENTITY_TYPE_KEY = EntityTypeKey.create( DetailsCommand.class, "DetailsEntity"); private final String artifactId; private final ActorContext ctx; - private final Function> tagger; private ArtifactDetailsEntity( ActorContext ctx, @@ -64,7 +60,6 @@ private ArtifactDetailsEntity( super(persistenceId); this.artifactId = entityId; this.ctx = ctx; - this.tagger = AkkaTaggerAdapter.fromLagom(context, DetailsEvent.TAG); } public static Behavior create( @@ -140,15 +135,13 @@ public CommandHandlerWithReply comma .persist(new DetailsEvent.ArtifactIssuesUpdated(s.coordinates(), cmd.validUrl().toString())) .thenReply( cmd.replyTo(), - us -> Either.right( - new ArtifactDetails.Response( - us.coordinates().artifactId(), - us.displayName(), - us.website(), - us.issues(), - us.gitRepository() - ) - ) + us -> HttpResponse.ok(new ArtifactDetails.Response.Ok( + us.coordinates().artifactId(), + us.displayName(), + us.website(), + us.issues(), + us.gitRepository() + )) ) ) .onCommand( @@ -157,15 +150,13 @@ public CommandHandlerWithReply comma .persist(new DetailsEvent.ArtifactIssuesUpdated(s.coordinates(), cmd.website().toString())) .thenReply( cmd.replyTo(), - us -> Either.right( - new ArtifactDetails.Response( - us.coordinates().artifactId(), - us.displayName(), - us.website(), - us.issues(), - us.gitRepository() - ) - ) + us -> HttpResponse.ok(new ArtifactDetails.Response.Ok( + us.coordinates().artifactId(), + us.displayName(), + us.website(), + us.issues(), + us.gitRepository() + )) ) ) .onCommand( @@ -174,15 +165,13 @@ public CommandHandlerWithReply comma .persist(new DetailsEvent.ArtifactIssuesUpdated(s.coordinates(), cmd.displayName())) .thenReply( cmd.replyTo(), - us -> Either.right( - new ArtifactDetails.Response( - us.coordinates().artifactId(), - us.displayName(), - us.website(), - us.issues(), - us.gitRepository() - ) - ) + us -> HttpResponse.ok(new ArtifactDetails.Response.Ok( + us.coordinates().artifactId(), + us.displayName(), + us.website(), + us.issues(), + us.gitRepository() + )) ) ) .onCommand( @@ -191,23 +180,17 @@ public CommandHandlerWithReply comma .persist(new DetailsEvent.ArtifactGitRepositoryUpdated(s.coordinates(), cmd.gitRemote())) .thenReply( cmd.replyTo(), - us -> Either.right( - new ArtifactDetails.Response( - us.coordinates().artifactId(), - us.displayName(), - us.website(), - us.issues(), - us.gitRepository() - ) - ) + us -> HttpResponse.ok(new ArtifactDetails.Response.Ok( + us.coordinates().artifactId(), + us.displayName(), + us.website(), + us.issues(), + us.gitRepository() + )) ) ); return builder.build(); } - @Override - public Set tagsFor(final DetailsEvent detailsEvent) { - return this.tagger.apply(detailsEvent); - } } diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/DetailsCommand.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/DetailsCommand.java index bcb8d442..04027110 100644 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/DetailsCommand.java +++ b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/DetailsCommand.java @@ -32,6 +32,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import io.micronaut.http.HttpResponse; import io.vavr.control.Either; +import org.spongepowered.downloads.akka.AkkaSerializable; import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; import org.spongepowered.downloads.artifact.api.query.ArtifactDetails; @@ -52,7 +53,7 @@ @JsonSubTypes.Type(value = DetailsCommand.UpdateDisplayName.class, name = "display-name") }) -public interface DetailsCommand { +public interface DetailsCommand extends AkkaSerializable { @JsonDeserialize final record RegisterArtifact(ArtifactCoordinates coordinates, diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/DetailsManager.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/DetailsManager.java deleted file mode 100644 index e851dbca..00000000 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/DetailsManager.java +++ /dev/null @@ -1,136 +0,0 @@ -package org.spongepowered.downloads.artifacts.server.details; - -import akka.NotUsed; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.cluster.sharding.typed.javadsl.Entity; -import akka.cluster.sharding.typed.javadsl.EntityRef; -import akka.persistence.typed.PersistenceId; -import io.micronaut.http.HttpResponse; -import io.vavr.control.Either; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.api.errors.GitAPIException; -import org.eclipse.jgit.api.errors.InvalidRemoteException; -import org.eclipse.jgit.lib.Ref; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.query.ArtifactDetails; -import org.spongepowered.downloads.artifact.api.query.ArtifactRegistration; - -import java.net.URL; -import java.time.Duration; -import java.util.Collection; -import java.util.concurrent.CompletionStage; - -@Singleton -public class DetailsManager { - private final ClusterSharding clusterSharding; - private final Duration askTimeout = Duration.ofHours(5); - - @Inject - public DetailsManager(final ClusterSharding clusterSharding) { - this.clusterSharding = clusterSharding; - this.clusterSharding.init( - Entity.of( - ArtifactDetailsEntity.ENTITY_TYPE_KEY, - context -> ArtifactDetailsEntity.create( - context, - context.getEntityId(), - PersistenceId.of(context.getEntityTypeKey().name(), context.getEntityId()) - ) - ) - ); - } - - public CompletionStage UpdateWebsite( - ArtifactCoordinates coords, ArtifactDetails.Update.Website w - ) { - - final Either validate = w.validate(); - if (validate.isLeft()) { - throw validate.getLeft(); - } - final var validUrl = validate.get(); - return this.getDetailsEntity(coords) - .>ask( - r -> new DetailsCommand.UpdateWebsite(coords, validUrl, r), this.askTimeout) - .thenApply(Either::get); - } - - - public CompletionStage UpdateDisplayName( - ArtifactCoordinates coords, ArtifactDetails.Update.DisplayName d - ) { - final Either validate = d.validate(); - if (validate.isLeft()) { - throw validate.getLeft(); - } - final var displayName = validate.get(); - return this.getDetailsEntity(coords) - .>ask( - r -> new DetailsCommand.UpdateDisplayName(coords, displayName, r), - this.askTimeout - ) - .thenApply(Either::get); - } - - public CompletionStage UpdateGitRepository( - final ArtifactCoordinates coords, ArtifactDetails.Update.GitRepository gr - ) { - final Either validate = gr.validate(); - if (validate.isLeft()) { - throw validate.getLeft(); - } - final Collection refs; - try { - refs = Git.lsRemoteRepository() - .setRemote(gr.gitRepo()) - .call(); - } catch (InvalidRemoteException e) { - throw new BadRequest(String.format("Invalid remote: %s", gr.gitRepo())); - } catch (GitAPIException e) { - throw new BadRequest(String.format("Error resolving repository '%s'", gr.gitRepo())); - } - if (refs.isEmpty()) { - throw new BadRequest(String.format("Remote repository '%s' has no refs", gr.gitRepo())); - } - - return this.getDetailsEntity(coords) - .>ask( - r -> new DetailsCommand.UpdateGitRepository(coords, gr.gitRepo(), r), this.askTimeout) - .thenApply(Either::get); - } - - public CompletionStage UpdateIssues( - ArtifactCoordinates coords, ArtifactDetails.Update.Issues i - ) { - final Either validate = i.validate(); - if (validate.isLeft()) { - throw validate.getLeft(); - } - final var validUrl = validate.get(); - return this.getDetailsEntity(coords) - .>ask( - r -> new DetailsCommand.UpdateIssues(coords, validUrl, r), this.askTimeout).thenApply( - Either::get); - } - - private EntityRef getDetailsEntity(final ArtifactCoordinates coordinates) { - return this.getDetailsEntity(coordinates.groupId(), coordinates.artifactId()); - } - - private EntityRef getDetailsEntity(final String groupId, final String artifactId) { - return this.clusterSharding.entityRefFor(ArtifactDetailsEntity.ENTITY_TYPE_KEY, groupId + ":" + artifactId); - } - - public CompletionStage registerArtifact( - ArtifactRegistration.Response.ArtifactRegistered registered, - String displayName - ) { - return this.getDetailsEntity(registered.coordinates()) - .ask( - replyTo -> new DetailsCommand.RegisterArtifact( - registered.coordinates(), displayName, replyTo), this.askTimeout) - .thenApply(notUsed -> registered); - } -} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/state/DetailsState.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/state/DetailsState.java index 5695e90f..15d01fdd 100644 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/state/DetailsState.java +++ b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/state/DetailsState.java @@ -24,10 +24,12 @@ */ package org.spongepowered.downloads.artifacts.server.details.state; -import com.lightbend.lagom.serialization.Jsonable; +import org.spongepowered.downloads.akka.AkkaSerializable; import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -public interface DetailsState extends Jsonable { +public sealed interface DetailsState extends AkkaSerializable + permits EmptyState, PopulatedState { + ArtifactCoordinates coordinates(); String displayName(); diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/state/PopulatedState.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/state/PopulatedState.java index e120427d..10cf2b76 100644 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/state/PopulatedState.java +++ b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/details/state/PopulatedState.java @@ -26,14 +26,13 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.CompressedJsonable; import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.details.DetailsEvent; +import org.spongepowered.downloads.artifacts.events.DetailsEvent; @JsonDeserialize public record PopulatedState(ArtifactCoordinates coordinates, String displayName, String website, String gitRepository, - String issues) implements DetailsState, CompressedJsonable { + String issues) implements DetailsState { @JsonCreator public PopulatedState { diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/global/GlobalCommand.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/global/GlobalCommand.java deleted file mode 100644 index 1fcaf252..00000000 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/global/GlobalCommand.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.server.global; - -import akka.Done; -import akka.actor.typed.ActorRef; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.Group; -import org.spongepowered.downloads.artifact.api.query.GroupsResponse; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, - property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(name = "Get", - value = GlobalCommand.GetGroups.class) -}) -public interface GlobalCommand extends Jsonable { - - @JsonDeserialize - record GetGroups(ActorRef replyTo) - implements GlobalCommand { - - @JsonCreator - public GetGroups { - } - } - - @JsonDeserialize - record RegisterGroup( - ActorRef replyTo, Group group) implements GlobalCommand { - - @JsonCreator - public RegisterGroup { - } - } -} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/global/GlobalEvent.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/global/GlobalEvent.java deleted file mode 100644 index d9162e8f..00000000 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/global/GlobalEvent.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.server.global; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.javadsl.persistence.AggregateEvent; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTag; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTagger; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.Group; - -public interface GlobalEvent extends AggregateEvent, Jsonable { - - AggregateEventTag TAG = AggregateEventTag.of(GlobalEvent.class); - - @Override - default AggregateEventTagger aggregateTag() { - return TAG; - } - - @JsonDeserialize - final class GroupRegistered implements GlobalEvent { - public final Group group; - - @JsonCreator - public GroupRegistered(Group group) { - this.group = group; - } - } -} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/global/GlobalManager.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/global/GlobalManager.java deleted file mode 100644 index 907ad2b0..00000000 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/global/GlobalManager.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.spongepowered.downloads.artifacts.server.global; - -import akka.Done; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.cluster.sharding.typed.javadsl.Entity; -import akka.cluster.sharding.typed.javadsl.EntityRef; -import akka.persistence.typed.PersistenceId; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; -import org.spongepowered.downloads.artifact.api.Group; -import org.spongepowered.downloads.artifact.api.query.GroupRegistration; -import org.spongepowered.downloads.artifact.api.query.GroupsResponse; - -import java.time.Duration; -import java.util.concurrent.CompletionStage; - -@Singleton -public final class GlobalManager { - private final Duration askTimeout = Duration.ofHours(5); - - private final ClusterSharding clusterSharding; - - @Inject - public GlobalManager(final ClusterSharding clusterSharding) { - this.clusterSharding = clusterSharding; - this.clusterSharding.init( - Entity.of( - GlobalRegistration.ENTITY_TYPE_KEY, - ctx -> GlobalRegistration.create( - ctx.getEntityId(), - PersistenceId.of(ctx.getEntityTypeKey().name(), ctx.getEntityId()) - ) - ) - ); - } - - public CompletionStage registerGroup( - GroupRegistration.Response.GroupRegistered registered - ) { - final Group group = registered.group(); - return this.getGlobalEntity() - .ask(replyTo -> new GlobalCommand.RegisterGroup(replyTo, group), this.askTimeout) - .thenApply(notUsed -> registered); - } - - - private EntityRef getGlobalEntity() { - return this.clusterSharding.entityRefFor(GlobalRegistration.ENTITY_TYPE_KEY, "global"); - } - - public CompletionStage getGroups() { - return this.getGlobalEntity().ask(GlobalCommand.GetGroups::new, this.askTimeout); - } -} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/global/GlobalRegistration.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/global/GlobalRegistration.java deleted file mode 100644 index 52d20c76..00000000 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/global/GlobalRegistration.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.server.global; - -import akka.Done; -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.cluster.sharding.typed.javadsl.EntityTypeKey; -import akka.persistence.typed.PersistenceId; -import akka.persistence.typed.javadsl.CommandHandlerWithReply; -import akka.persistence.typed.javadsl.EventHandler; -import akka.persistence.typed.javadsl.EventSourcedBehaviorWithEnforcedReplies; -import akka.persistence.typed.javadsl.ReplyEffect; -import io.vavr.collection.List; -import org.spongepowered.downloads.artifact.api.query.GroupsResponse; - -public class GlobalRegistration - extends EventSourcedBehaviorWithEnforcedReplies { - - public static EntityTypeKey ENTITY_TYPE_KEY = EntityTypeKey.create( - GlobalCommand.class, "GlobalEntity"); - private final String groupId; - private final ActorContext ctx; - - private GlobalRegistration(ActorContext ctx, String entityId, PersistenceId persistenceId) { - super(persistenceId); - this.ctx = ctx; - this.groupId = entityId; - } - - public static Behavior create(String entityId, PersistenceId persistenceId) { - return Behaviors.setup(ctx -> new GlobalRegistration(ctx, entityId, persistenceId)); - } - - @Override - public GlobalState emptyState() { - return new GlobalState(List.empty()); - } - - @Override - public EventHandler eventHandler() { - final var builder = this.newEventHandlerBuilder(); - builder.forAnyState() - .onEvent( - GlobalEvent.GroupRegistered.class, - (state, event) -> new GlobalState(state.groups().append(event.group)) - ); - return builder.build(); - } - - @Override - public CommandHandlerWithReply commandHandler() { - final var builder = this.newCommandHandlerWithReplyBuilder(); - builder.forAnyState() - .onCommand( - GlobalCommand.GetGroups.class, - (state, cmd) -> this.Effect().reply(cmd.replyTo(), new GroupsResponse.Available(state.groups())) - ) - .onCommand( - GlobalCommand.RegisterGroup.class, - this::handleRegisterGroup - ); - return builder.build(); - } - - private ReplyEffect handleRegisterGroup( - GlobalState state, GlobalCommand.RegisterGroup cmd - ) { - if (!state.groups().contains(cmd.group())) { - return this.Effect().persist(new GlobalEvent.GroupRegistered(cmd.group())) - .thenReply(cmd.replyTo(), (s) -> Done.done()); - } - return this.Effect().reply(cmd.replyTo(), Done.done()); - } - -} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/global/GlobalState.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/global/GlobalState.java deleted file mode 100644 index 9a26c915..00000000 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/global/GlobalState.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.server.global; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import io.vavr.collection.List; -import org.spongepowered.downloads.artifact.api.Group; - -@JsonDeserialize -public record GlobalState(List groups) { - - @JsonCreator - public GlobalState { - } -} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/GroupCommand.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/GroupCommand.java deleted file mode 100644 index 08c03a79..00000000 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/GroupCommand.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.server.groups; - -import akka.actor.typed.ActorRef; -import org.spongepowered.downloads.artifact.api.query.ArtifactRegistration; -import org.spongepowered.downloads.artifact.api.query.GetArtifactsResponse; -import org.spongepowered.downloads.artifact.api.query.GroupRegistration; -import org.spongepowered.downloads.artifact.api.query.GroupResponse; - -public sealed interface GroupCommand { - record GetGroup( - String groupId, - ActorRef replyTo - ) implements GroupCommand { - } - - record GetArtifacts( - String groupId, - ActorRef replyTo - ) implements GroupCommand { - - } - - record RegisterArtifact( - String artifact, - ActorRef replyTo - ) implements GroupCommand { - - } - - record RegisterGroup( - String mavenCoordinates, - String name, - String website, - ActorRef replyTo - ) implements GroupCommand { - - } - -} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/GroupEntity.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/GroupEntity.java deleted file mode 100644 index d2fa9f83..00000000 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/GroupEntity.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.server.groups; - -import akka.cluster.sharding.typed.javadsl.EntityContext; -import akka.cluster.sharding.typed.javadsl.EntityTypeKey; -import akka.persistence.typed.PersistenceId; -import akka.persistence.typed.javadsl.CommandHandlerWithReply; -import akka.persistence.typed.javadsl.CommandHandlerWithReplyBuilder; -import akka.persistence.typed.javadsl.EffectFactories; -import akka.persistence.typed.javadsl.EventHandler; -import akka.persistence.typed.javadsl.EventHandlerBuilder; -import akka.persistence.typed.javadsl.EventSourcedBehaviorWithEnforcedReplies; -import akka.persistence.typed.javadsl.ReplyEffect; -import akka.persistence.typed.javadsl.RetentionCriteria; -import io.vavr.control.Try; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.Group; -import org.spongepowered.downloads.artifact.api.query.ArtifactRegistration; -import org.spongepowered.downloads.artifact.api.query.GetArtifactsResponse; -import org.spongepowered.downloads.artifact.api.query.GroupRegistration; -import org.spongepowered.downloads.artifact.api.query.GroupResponse; -import org.spongepowered.downloads.artifacts.server.groups.state.EmptyState; -import org.spongepowered.downloads.artifacts.server.groups.state.GroupState; -import org.spongepowered.downloads.artifacts.server.groups.state.PopulatedState; - -import java.net.URL; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class GroupEntity - extends EventSourcedBehaviorWithEnforcedReplies { - - public static EntityTypeKey ENTITY_TYPE_KEY = EntityTypeKey.create(GroupCommand.class, "GroupEntity"); - private final String groupId; - private final Function> tagger; - - private GroupEntity(EntityContext context) { - super( - // PersistenceId needs a typeHint (or namespace) and entityId, - // we take then from the EntityContext - PersistenceId.of( - context.getEntityTypeKey().name(), // <- type hint - context.getEntityId() // <- business id - )); - // we keep a copy of cartI - this.groupId = context.getEntityId(); - this.tagger = AkkaTaggerAdapter.fromLagom(context, GroupEvent.TAG); - - } - - public static GroupEntity create(EntityContext context) { - return new GroupEntity(context); - } - - @Override - public GroupState emptyState() { - return new EmptyState(); - } - - @Override - public EventHandler eventHandler() { - final EventHandlerBuilder builder = this.newEventHandlerBuilder(); - - builder.forState(GroupState::isEmpty) - .onEvent( - GroupEvent.GroupRegistered.class, - this::handleRegistration - ); - builder.forStateType(PopulatedState.class) - .onEvent(GroupEvent.ArtifactRegistered.class, this::handleArtifactRegistration); - - return builder.build(); - } - - private GroupState handleRegistration( - final GroupState state, final GroupEvent.GroupRegistered event - ) { - return new PopulatedState(event.groupId, event.name, event.website, Set.of()); - } - - private GroupState handleArtifactRegistration( - final PopulatedState state, final GroupEvent.ArtifactRegistered event - ) { - final var add = Stream.concat( - state.artifacts().stream(), - Stream.of(event.artifact()) - ) - .collect(Collectors.toUnmodifiableSet()); - return new PopulatedState(state.groupCoordinates(), state.name(), state.website(), add); - } - - @Override - public CommandHandlerWithReply commandHandler() { - final CommandHandlerWithReplyBuilder builder = this.newCommandHandlerWithReplyBuilder(); - - builder.forState(GroupState::isEmpty) - .onCommand(GroupCommand.RegisterGroup.class, this::respondToRegisterGroup) - .onCommand(GroupCommand.RegisterArtifact.class, (state, cmd) -> - this.Effect().reply(cmd.replyTo(), new ArtifactRegistration.Response.GroupMissing(state.name())) - ) - .onCommand(GroupCommand.GetGroup.class, (cmd) -> - this.Effect().reply(cmd.replyTo(), new GroupResponse.Missing(cmd.groupId())) - ) - .onCommand(GroupCommand.GetArtifacts.class, (cmd) -> - this.Effect().reply(cmd.replyTo(), new GetArtifactsResponse.GroupMissing(cmd.groupId())) - ) - ; - builder.forStateType(PopulatedState.class) - .onCommand(GroupCommand.RegisterGroup.class, (cmd) -> this.Effect().reply( - cmd.replyTo(), new GroupRegistration.Response.GroupAlreadyRegistered(cmd.mavenCoordinates()))) - .onCommand(GroupCommand.RegisterArtifact.class, this::respondToRegisterArtifact) - .onCommand(GroupCommand.GetGroup.class, this::respondToGetGroup) - .onCommand(GroupCommand.GetArtifacts.class, this::respondToGetVersions); - return builder.build(); - } - - @Override - public RetentionCriteria retentionCriteria() { - return RetentionCriteria.snapshotEvery(5, 2); - } - - @Override - public Set tagsFor(final GroupEvent groupEvent) { - return this.tagger.apply(groupEvent); - } - - private ReplyEffect respondToRegisterGroup( - final GroupState state, - final GroupCommand.RegisterGroup cmd - ) { - return this.Effect() - .persist(new GroupEvent.GroupRegistered(cmd.mavenCoordinates(), cmd.name(), cmd.website())) - .thenReply( - cmd.replyTo(), - newState -> new GroupRegistration.Response.GroupRegistered( - new Group( - newState.groupCoordinates(), - newState.name(), - newState.website() - )) - ); - } - - private ReplyEffect respondToRegisterArtifact( - final PopulatedState state, - final GroupCommand.RegisterArtifact cmd - ) { - if (state.artifacts().contains(cmd.artifact())) { - this.Effect().reply(cmd.replyTo(), new ArtifactRegistration.Response.ArtifactAlreadyRegistered( - cmd.artifact(), - state.groupCoordinates() - )); - } - - final var group = state.asGroup(); - final var coordinates = new ArtifactCoordinates(group.groupCoordinates(), cmd.artifact()); - final EffectFactories effect = this.Effect(); - return effect.persist(new GroupEvent.ArtifactRegistered(state.groupCoordinates(), cmd.artifact())) - .thenReply(cmd.replyTo(), (s) -> new ArtifactRegistration.Response.ArtifactRegistered(coordinates)); - } - - private ReplyEffect respondToGetGroup( - final PopulatedState state, final GroupCommand.GetGroup cmd - ) { - final String website = state.website(); - return this.Effect().reply(cmd.replyTo(), Try.of(() -> new URL(website)) - .mapTry(url -> { - final Group group = new Group(state.groupCoordinates(), state.name(), website); - return new GroupResponse.Available(group); - }) - .getOrElseGet(throwable -> new GroupResponse.Missing(cmd.groupId()))); - } - - private ReplyEffect respondToGetVersions( - final PopulatedState state, - final GroupCommand.GetArtifacts cmd - ) { - return this.Effect().reply( - cmd.replyTo(), new GetArtifactsResponse.ArtifactsAvailable(state.artifacts().stream().toList())); - } -} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/GroupEvent.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/GroupEvent.java deleted file mode 100644 index 01fdb996..00000000 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/GroupEvent.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.server.groups; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.javadsl.persistence.AggregateEvent; -import com.lightbend.lagom.javadsl.persistence.AggregateEventShards; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTag; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTagger; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; - -import java.io.Serial; -import java.util.Objects; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(GroupEvent.GroupRegistered.class), - @JsonSubTypes.Type(GroupEvent.ArtifactRegistered.class), -}) -public interface GroupEvent extends AggregateEvent, Jsonable { - - AggregateEventShards TAG = AggregateEventTag.sharded(GroupEvent.class, 10); - - @Override - default AggregateEventTagger aggregateTag() { - return TAG; - } - - String groupId(); - - @JsonTypeName("group-registered") - @JsonDeserialize - final class GroupRegistered implements GroupEvent { - @Serial private static final long serialVersionUID = 0L; - - public final String groupId; - public final String name; - public final String website; - - @JsonCreator - public GroupRegistered(final String groupId, final String name, final String website) { - this.groupId = groupId; - this.name = name; - this.website = website; - } - - @Override - public String groupId() { - return this.groupId; - } - - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (obj == null || obj.getClass() != this.getClass()) { - return false; - } - final var that = (GroupRegistered) obj; - return Objects.equals(this.groupId, that.groupId) && - Objects.equals(this.name, that.name) && - Objects.equals(this.website, that.website); - } - - @Override - public int hashCode() { - return Objects.hash(this.groupId, this.name, this.website); - } - - @Override - public String toString() { - return "GroupRegistered[" + - "groupId=" + this.groupId + ", " + - "name=" + this.name + ", " + - "website=" + this.website + ']'; - } - - } - - @JsonTypeName("artifact-registered") - @JsonDeserialize - record ArtifactRegistered( - String groupId, - String artifact - ) implements GroupEvent { - - public ArtifactCoordinates coordinates() { - return new ArtifactCoordinates(this.groupId, this.artifact); - } - - @JsonCreator - public ArtifactRegistered { - } - } - -} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/GroupManager.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/GroupManager.java deleted file mode 100644 index 7783d3cf..00000000 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/GroupManager.java +++ /dev/null @@ -1,100 +0,0 @@ -package org.spongepowered.downloads.artifacts.server.groups; - -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.cluster.sharding.typed.javadsl.Entity; -import akka.cluster.sharding.typed.javadsl.EntityRef; -import com.lightbend.lagom.javadsl.api.transport.NotFound; -import jakarta.inject.Inject; -import jakarta.inject.Singleton; -import org.spongepowered.downloads.artifact.api.query.ArtifactRegistration; -import org.spongepowered.downloads.artifact.api.query.GetArtifactsResponse; -import org.spongepowered.downloads.artifact.api.query.GroupRegistration; -import org.spongepowered.downloads.artifact.api.query.GroupResponse; -import org.spongepowered.downloads.artifacts.server.details.DetailsManager; -import org.spongepowered.downloads.artifacts.server.global.GlobalManager; - -import java.time.Duration; -import java.util.Locale; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; - -@Singleton -public final class GroupManager { - private final ClusterSharding clusterSharding; - private final GlobalManager global; - private final Duration askTimeout = Duration.ofHours(5); - private final DetailsManager details; - - @Inject - public GroupManager(ClusterSharding clusterSharding, final DetailsManager details, final GlobalManager global) { - this.clusterSharding = clusterSharding; - this.global = global; - this.details = details; - this.clusterSharding.init( - Entity.of( - GroupEntity.ENTITY_TYPE_KEY, - GroupEntity::create - ) - ); - } - - public CompletionStage registerGroup( - GroupRegistration.RegisterGroupRequest registration - ) { - final String mavenCoordinates = registration.groupCoordinates(); - final String name = registration.name(); - final String website = registration.website(); - return this.groupEntity(registration.groupCoordinates().toLowerCase(Locale.ROOT)) - .ask( - replyTo -> new GroupCommand.RegisterGroup(mavenCoordinates, name, website, replyTo), - this.askTimeout - ).thenCompose(response -> { - if (!(response instanceof GroupRegistration.Response.GroupRegistered registered)) { - return CompletableFuture.completedFuture(response); - } - return this.global.registerGroup(registered); - - }); - } - - - private EntityRef groupEntity(final String groupId) { - return this.clusterSharding.entityRefFor(GroupEntity.ENTITY_TYPE_KEY, groupId.toLowerCase(Locale.ROOT)); - } - - public CompletionStage registerArtifact( - ArtifactRegistration.RegisterArtifact reg, String groupId - ) { - final var sanitizedGroupId = groupId.toLowerCase(Locale.ROOT); - return this.groupEntity(sanitizedGroupId) - .ask( - replyTo -> new GroupCommand.RegisterArtifact(reg.artifactId(), replyTo), this.askTimeout) - .thenCompose(response -> switch (response) { - case ArtifactRegistration.Response.GroupMissing missing -> - throw new NotFound(String.format("group %s does not exist", missing.s())); - - case ArtifactRegistration.Response.ArtifactRegistered registered -> - this.details.registerArtifact(registered, reg.displayName()); - - default -> CompletableFuture.completedFuture(response); - }); - } - - public CompletionStage getArtifacts(String groupId) { - return this.groupEntity(groupId) - .ask(replyTo -> new GroupCommand.GetArtifacts(groupId, replyTo), this.askTimeout) - .thenApply(response -> switch (response) { - case GetArtifactsResponse.GroupMissing m -> throw new NotFound(String.format("group '%s' not found", m.groupRequested())); - case GetArtifactsResponse.ArtifactsAvailable a -> a; - }); - } - - public CompletionStage get(String groupId) { - return this.groupEntity(groupId.toLowerCase(Locale.ROOT)) - .ask(replyTo -> new GroupCommand.GetGroup(groupId, replyTo), this.askTimeout) - .thenApply(response -> switch (response) { - case GroupResponse.Missing m -> throw new NotFound(String.format("group '%s' not found", m.groupId())); - case GroupResponse.Available a -> a; - }); - } -} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/GroupsQueryController.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/GroupsQueryController.java deleted file mode 100644 index 98ebb288..00000000 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/GroupsQueryController.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.spongepowered.downloads.artifacts.server.groups; - -import akka.actor.typed.ActorSystem; -import akka.actor.typed.SpawnProtocol; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.cluster.sharding.typed.javadsl.Entity; -import akka.cluster.sharding.typed.javadsl.EntityRef; -import io.micronaut.http.HttpResponse; -import io.micronaut.http.annotation.Body; -import io.micronaut.http.annotation.Controller; -import io.micronaut.http.annotation.Get; -import io.micronaut.http.annotation.Post; -import jakarta.inject.Inject; -import org.spongepowered.downloads.artifact.api.query.GroupRegistration; -import org.spongepowered.downloads.artifact.api.query.GroupResponse; - -import java.time.Duration; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; - -@Controller("/groups") -public class GroupsQueryController { - - private final ActorSystem system; - private final ClusterSharding sharding; - - @Inject - public GroupsQueryController( - final ActorSystem system, - ClusterSharding sharding - ) { - this.system = system; - this.sharding = sharding; - this.sharding.init( - Entity.of( - GroupEntity.ENTITY_TYPE_KEY, - GroupEntity::create - ) - ); - - } - - @Post("/") - public CompletableFuture> registerGroup( - @Body GroupRegistration.RegisterGroupRequest req - ) { - final var ref = this.sharding.entityRefFor( - GroupEntity.ENTITY_TYPE_KEY, - req.groupCoordinates() - ); - final var resp = ref.ask - ( - replyTo -> new GroupCommand.RegisterGroup( - req.groupCoordinates(), - req.name(), - req.website(), - replyTo - ), - Duration.ofSeconds(10) - ); - return resp - .>thenApply(HttpResponse::created) - .toCompletableFuture(); - } -} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/state/EmptyState.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/state/EmptyState.java deleted file mode 100644 index e6b3de5a..00000000 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/state/EmptyState.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.server.groups.state; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import org.spongepowered.downloads.artifact.api.Group; - -@JsonDeserialize -public record EmptyState() implements GroupState { - - @JsonCreator - public EmptyState { - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public Group asGroup() { - return new Group("", "", ""); - } - - @Override - public String website() { - return "null"; - } - - @Override - public String name() { - return "null"; - } - - @Override - public String groupCoordinates() { - return "null"; - } -} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/state/GroupState.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/state/GroupState.java deleted file mode 100644 index 8b133996..00000000 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/state/GroupState.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.server.groups.state; - -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import org.spongepowered.downloads.artifact.api.Group; - -@JsonDeserialize -@JsonSubTypes({ - @JsonSubTypes.Type(value = PopulatedState.class, name = "populated"), - @JsonSubTypes.Type(value = EmptyState.class, name = "empty") -}) -public sealed interface GroupState permits EmptyState, PopulatedState { - - boolean isEmpty(); - - Group asGroup(); - - String website(); - - String name(); - - String groupCoordinates(); -} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/state/PopulatedState.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/state/PopulatedState.java deleted file mode 100644 index 56264f25..00000000 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/groups/state/PopulatedState.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.server.groups.state; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import org.spongepowered.downloads.artifact.api.Group; - -import java.util.Set; - -@JsonDeserialize -public record PopulatedState( - String groupCoordinates, - String name, - String website, - Set artifacts -) implements GroupState { - @JsonCreator - public PopulatedState { - } - - public boolean isEmpty() { - return this.groupCoordinates().isEmpty() || this.name().isEmpty(); - } - - public Group asGroup() { - return new Group(this.groupCoordinates(), this.name(), this.website()); - } -} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/ArtifactsQuery.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/ArtifactsQuery.java deleted file mode 100644 index fb46c84f..00000000 --- a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/ArtifactsQuery.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.spongepowered.downloads.artifacts.server.query; - -import io.micronaut.http.annotation.Controller; -import io.micronaut.http.annotation.Get; - -@Controller("/groups/{groupID}/artifacts") -public class ArtifactsQuery { - - @Get("") - - -} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/group/domain/GroupOrg.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/group/domain/GroupOrg.java new file mode 100644 index 00000000..ed8de22f --- /dev/null +++ b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/group/domain/GroupOrg.java @@ -0,0 +1,27 @@ +package org.spongepowered.downloads.artifacts.server.query.group.domain; + +import io.micronaut.data.annotation.GeneratedValue; +import io.micronaut.data.annotation.Id; +import io.micronaut.data.annotation.MappedEntity; +import io.micronaut.data.annotation.MappedProperty; +import io.micronaut.data.annotation.Relation; +import io.micronaut.serde.annotation.Serdeable; +import org.spongepowered.downloads.artifacts.server.query.meta.domain.JpaArtifact; + +import java.util.List; + +@MappedEntity(value = "groups") +@Serdeable +public class GroupOrg { + + @Id + @GeneratedValue + private int id; + + @MappedProperty(value = "groupId") + private String groupId; + + @Relation(value =Relation.Kind.ONE_TO_MANY, mappedBy = "groupId") + private List artifacts; + +} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/ArtifactDto.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/ArtifactDto.java new file mode 100644 index 00000000..4ebb194d --- /dev/null +++ b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/ArtifactDto.java @@ -0,0 +1,31 @@ +package org.spongepowered.downloads.artifacts.server.query.meta; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +import java.io.Serializable; +import java.util.Set; + +/** + * DTO for {@link org.spongepowered.downloads.artifacts.server.query.meta.domain.JpaArtifact} + */ +public record ArtifactDto( + @NotNull String groupId, + @NotEmpty String artifactId, + String displayName, + String website, + String gitRepo, + String issues, + @NotNull Set tagValues +) implements Serializable { + /** + * DTO for {@link org.spongepowered.downloads.artifacts.server.query.meta.domain.JpaArtifactTagValue} + */ + public record Tag( + String artifactId, + String groupId, + String tagName, + String tagValue + ) implements Serializable { + } +} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/ArtifactQueryController.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/ArtifactQueryController.java new file mode 100644 index 00000000..87c90b5b --- /dev/null +++ b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/ArtifactQueryController.java @@ -0,0 +1,52 @@ +package org.spongepowered.downloads.artifacts.server.query.meta; + +import akka.actor.typed.ActorSystem; +import akka.actor.typed.SpawnProtocol; +import akka.cluster.sharding.typed.javadsl.ClusterSharding; +import io.micronaut.context.annotation.Requires; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.PathVariable; +import io.micronaut.http.annotation.Status; +import jakarta.inject.Inject; +import org.spongepowered.downloads.artifact.api.query.GetArtifactsResponse; +import reactor.core.publisher.Mono; + +import java.util.List; + +@Controller("/groups/{groupID}/artifacts") +@Requires("query") +public class ArtifactQueryController { + + private final ClusterSharding sharding; + private final ActorSystem system; + private final ArtifactRepository artifactsRepo; + + + @Inject + public ArtifactQueryController( + final ClusterSharding sharding, + final ActorSystem system, + final ArtifactRepository artifactsRepo + ) { + + this.sharding = sharding; + this.system = system; + this.artifactsRepo = artifactsRepo; + } + + @Get(value = "/{artifactId}", + produces = MediaType.APPLICATION_JSON + ) + @Status(HttpStatus.OK) + public Mono getArtifacts( + final @PathVariable String groupID, + final @PathVariable String artifactId + ) { + return this.artifactsRepo.findByGroupIdAndArtifactId(groupID, artifactId) + .map(a -> new GetArtifactsResponse.ArtifactsAvailable(List.of(a.getArtifactId()))) + .onErrorReturn(new GetArtifactsResponse.GroupMissing(groupID)); + } +} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/ArtifactRepository.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/ArtifactRepository.java new file mode 100644 index 00000000..5032a032 --- /dev/null +++ b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/ArtifactRepository.java @@ -0,0 +1,22 @@ +package org.spongepowered.downloads.artifacts.server.query.meta; + +import io.micronaut.core.annotation.NonNull; +import io.micronaut.data.annotation.Query; +import io.micronaut.data.model.query.builder.sql.Dialect; +import io.micronaut.data.r2dbc.annotation.R2dbcRepository; +import io.micronaut.data.repository.reactive.ReactiveStreamsCrudRepository; +import org.spongepowered.downloads.artifacts.server.query.meta.domain.JpaArtifact; +import reactor.core.publisher.Mono; + +import java.util.List; +import java.util.Optional; + +@R2dbcRepository(dialect = Dialect.POSTGRES) +public interface ArtifactRepository extends ReactiveStreamsCrudRepository { + + @NonNull + List findArtifactIdByGroupId(@NonNull String groupId); + + @NonNull + Mono findByGroupIdAndArtifactId(String groupId, String artifactId); +} diff --git a/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/domain/Group.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/domain/Group.java new file mode 100644 index 00000000..05741eba --- /dev/null +++ b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/domain/Group.java @@ -0,0 +1,27 @@ +package org.spongepowered.downloads.artifacts.server.query.meta.domain; + +import io.micronaut.data.annotation.GeneratedValue; +import io.micronaut.data.annotation.Id; +import io.micronaut.data.annotation.MappedEntity; +import io.micronaut.data.annotation.MappedProperty; +import io.micronaut.data.annotation.Relation; +import io.micronaut.serde.annotation.Serdeable; + +import java.util.List; + +@Serdeable +@MappedEntity +public class Group { + + @GeneratedValue + @Id + private Long id; + + @MappedProperty(value = "groupId") + private String groupId; + + @Relation(value = Relation.Kind.ONE_TO_MANY, + mappedBy = "groupId") + private List artifacts; + +} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/models/JpaArtifact.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/domain/JpaArtifact.java similarity index 54% rename from downloads-api/src/main/java/org/spongepowered/downloads/artifacts/models/JpaArtifact.java rename to artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/domain/JpaArtifact.java index 0ee0309d..27c5456a 100644 --- a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/models/JpaArtifact.java +++ b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/domain/JpaArtifact.java @@ -22,94 +22,59 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.downloads.artifacts.models; +package org.spongepowered.downloads.artifacts.server.query.meta.domain; +import io.micronaut.data.annotation.GeneratedValue; +import io.micronaut.data.annotation.Id; +import io.micronaut.data.annotation.Join; +import io.micronaut.data.annotation.MappedEntity; +import io.micronaut.data.annotation.MappedProperty; +import io.micronaut.data.annotation.Relation; import io.vavr.Tuple; import io.vavr.Tuple2; import io.vavr.collection.Map; import io.vavr.collection.SortedSet; import io.vavr.collection.TreeMap; import io.vavr.collection.TreeSet; +import jakarta.validation.constraints.NotEmpty; import org.apache.maven.artifact.versioning.ComparableVersion; -import org.hibernate.annotations.Immutable; -import org.spongepowered.downloads.api.ArtifactCoordinates; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.JoinColumns; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToMany; -import javax.persistence.Table; -import java.io.Serializable; +import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; + import java.util.Comparator; -import java.util.Objects; import java.util.Set; -import java.util.stream.Collectors; - -@Immutable -@Entity(name = "Artifact") -@Table(name = "artifacts", - schema = "version") -@NamedQueries({ - @NamedQuery( - name = "Artifact.findByCoordinates", - query = "select a from Artifact a where a.groupId = :groupId and a.artifactId = :artifactId" - ) -}) -public class JpaArtifact implements Serializable { + +@MappedEntity(value = "artifacts", schema = "artifacts", alias = "artifact") +public class JpaArtifact { + + @GeneratedValue @Id - @Column(name = "id", - nullable = false, - updatable = false, - insertable = false) private int id; - @Column(name = "group_id", - nullable = false, - updatable = false, - insertable = false) + public int getId() { + return id; + } + + @MappedProperty(value = "group_id") private String groupId; - @Column(name = "artifact_id", - nullable = false, - updatable = false, - insertable = false) + @NotEmpty + @MappedProperty(value = "artifact_id") private String artifactId; - @Column(name = "display_name", - updatable = false, - insertable = false) + @MappedProperty(value = "display_name") private String displayName; - @Column(name = "website", - updatable = false, - insertable = false) + @MappedProperty(value = "website") private String website; - @Column(name = "git_repository", - updatable = false, - insertable = false) + @MappedProperty(value = "git_repository") private String gitRepo; - @Column(name = "issues", - updatable = false, - insertable = false) + @MappedProperty(value = "issues") private String issues; - @OneToMany(fetch = FetchType.EAGER, - targetEntity = JpaArtifactTagValue.class) - @JoinColumns({ - @JoinColumn(name = "artifact_id", - referencedColumnName = "artifact_id"), - @JoinColumn(name = "group_id", - referencedColumnName = "group_id") - }) private Set tagValues; public String getGroupId() { @@ -140,26 +105,6 @@ public Set getTagValues() { return tagValues; } - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - JpaArtifact that = (JpaArtifact) o; - return id == that.id && groupId.equals(that.groupId) && artifactId.equals( - that.artifactId) && Objects.equals(displayName, that.displayName) && Objects.equals( - website, that.website) && Objects.equals(gitRepo, that.gitRepo) && Objects.equals( - issues, that.issues); - } - - @Override - public int hashCode() { - return Objects.hash(id, groupId, artifactId, displayName, website, gitRepo, issues); - } - public ArtifactCoordinates getCoordinates() { return new ArtifactCoordinates(this.groupId, this.artifactId); } @@ -178,4 +123,5 @@ public Map> getTagValuesForReply() { } return versionedTags; } + } diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/models/JpaArtifactTagValue.java b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/domain/JpaArtifactTagValue.java similarity index 73% rename from downloads-api/src/main/java/org/spongepowered/downloads/artifacts/models/JpaArtifactTagValue.java rename to artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/domain/JpaArtifactTagValue.java index d6a92e3d..9411baf9 100644 --- a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/models/JpaArtifactTagValue.java +++ b/artifacts/server/src/main/java/org/spongepowered/downloads/artifacts/server/query/meta/domain/JpaArtifactTagValue.java @@ -22,27 +22,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.downloads.artifacts.models; +package org.spongepowered.downloads.artifacts.server.query.meta.domain; -import org.hibernate.annotations.Immutable; +import io.micronaut.data.annotation.MappedEntity; +import io.micronaut.data.annotation.MappedProperty; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.IdClass; -import javax.persistence.JoinColumn; -import javax.persistence.JoinColumns; -import javax.persistence.ManyToOne; -import javax.persistence.Table; import java.io.Serializable; import java.util.Objects; -@Immutable -@Entity(name = "ArtifactTagValue") -@Table(name = "artifact_tag_values", - schema = "version") -@IdClass(JpaArtifactTagValue.Identifier.class) +@MappedEntity(value = "versioned_tags", schema = "version") public class JpaArtifactTagValue { /* @@ -78,38 +66,23 @@ public int hashCode() { } } - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumns({ - @JoinColumn(name = "artifact_id", - referencedColumnName = "artifact_id"), - @JoinColumn(name = "group_id", - referencedColumnName = "group_id") - }) private JpaArtifact artifact; - @Id - @Column(name = "artifact_id", - insertable = false, - updatable = false) + @MappedProperty(value = "artifact_id") private String artifactId; - @Id - @Column(name = "group_id", - insertable = false, - updatable = false) + @MappedProperty(value = "group_id") private String groupId; - @Id - @Column(name = "tag_name", - insertable = false, - updatable = false) + @MappedProperty(value = "tag_name") private String tagName; - @Id - @Column(name = "tag_value", - insertable = false, - updatable = false) + @MappedProperty(value = "tag_value") private String tagValue; + public JpaArtifact getArtifact() { + return artifact; + } + public String getTagName() { return tagName; } diff --git a/artifacts/server/src/main/resources/application.conf b/artifacts/server/src/main/resources/application.conf new file mode 100644 index 00000000..997ab44f --- /dev/null +++ b/artifacts/server/src/main/resources/application.conf @@ -0,0 +1,32 @@ +akka.persistence.journal.plugin = "akka.persistence.r2dbc.journal" +akka.persistence.snapshot-store.plugin = "akka.persistence.r2dbc.snapshot" +akka.persistence.state.plugin = "akka.persistence.r2dbc.state" + +akka.persistence.r2dbc { + journal.payload-column-type = JSONB + snapshot.payload-column-type = JSONB + state.payload-column-type = JSONB +} +akka.serialization.jackson.jackson-json.compression.algorithm = off + + +akka.persistence.r2dbc { + dialect = "postgres" + connection-factory { + driver = "postgres" + host = "localhost" + host = ${?DB_HOST} + database = "default" + database = ${?DB_NAME} + user = "admin" + user = ${?DB_USER} + password = "password" + password = ${?DB_PASSWORD} + + # ssl { + # enabled = on + # mode = "VERIFY_CA" + # root-cert = "/path/db_root.crt" + # } + } +} diff --git a/artifacts/server/src/main/resources/application.toml b/artifacts/server/src/main/resources/application.toml deleted file mode 100644 index 250d1b57..00000000 --- a/artifacts/server/src/main/resources/application.toml +++ /dev/null @@ -1,10 +0,0 @@ -micronaut.application.name = 'artifacts' -netty.default.allocator.max-order = 3 - -[r2dbc.datasources.default] -schema-generate = 'CREATE_DROP' -dialect = 'H2' - -[micronaut.security] -authentication = 'bearer' -token.jwt.signatures.secret.generator.secret = '${JWT_GENERATOR_SIGNATURE_SECRET:pleaseChangeThisSecretForANewOne}' diff --git a/artifacts/server/src/main/resources/application.yaml b/artifacts/server/src/main/resources/application.yaml new file mode 100644 index 00000000..754ed774 --- /dev/null +++ b/artifacts/server/src/main/resources/application.yaml @@ -0,0 +1,24 @@ +datasources: + default: + + db-type: postgresql + dialect: POSTGRES + driver: postgresql + options: + currentSchema: artifact + pool: + max-size: 10 + max-idle-time: 30m + driver-class-name: org.postgresql.Driver +r2dbc: + datasources: + default: + db-type: postgresql + dialect: POSTGRES + +liquibase: + enabled: true + datasources: + default: + enabled: true + change-log: 'classpath:db/liquibase-changelog.xml' # (4) diff --git a/artifacts/server/src/main/resources/db/changelog/01-create-artifacts-schema.xml b/artifacts/server/src/main/resources/db/changelog/01-create-artifacts-schema.xml new file mode 100644 index 00000000..b6adedcb --- /dev/null +++ b/artifacts/server/src/main/resources/db/changelog/01-create-artifacts-schema.xml @@ -0,0 +1,125 @@ + + + + + + + + CREATE SCHEMA IF NOT EXISTS artifacts; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select distinct a.artifact_id, a.group_id, v.version, v.recommended, v.manual_recommendation + from artifact.artifacts a inner join artifact.artifact_versions v on a.id = v.artifact_id + + + select + a.group_id, + a.artifact_id, + av.version, + va.classifier, va.extension, va.download_url, va.md5, va.sha1 + from artifact.versioned_assets va + inner join artifact.artifact_versions av on av.id = va.version_id + inner join artifact.artifacts a on a.id = av.artifact_id + + + select distinct a.group_id, a.artifact_id, v.version, v.ordering, v.id as version_id, vc.commit_sha, vc.repo, vc.branch, vc.changelog + from artifact.version_changelogs vc + inner join artifact.artifact_versions v on v.id = vc.version_id + inner join artifact.artifacts a on a.id = v.artifact_id + order by v.ordering desc + + + select distinct a.artifact_id, a.group_id, v.version, v.ordering, v.recommended, v.manual_recommendation + from artifact.artifacts a inner join artifact.artifact_versions v on a.id = v.artifact_id + order by v.ordering desc + + + + diff --git a/artifacts/server/src/main/resources/db/liquibase-changelog.xml b/artifacts/server/src/main/resources/db/liquibase-changelog.xml new file mode 100644 index 00000000..92a9682b --- /dev/null +++ b/artifacts/server/src/main/resources/db/liquibase-changelog.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/artifacts/server/src/main/resources/logback.xml b/artifacts/server/src/main/resources/logback.xml index 6010eb52..8cb499db 100644 --- a/artifacts/server/src/main/resources/logback.xml +++ b/artifacts/server/src/main/resources/logback.xml @@ -12,4 +12,9 @@ + + + + + diff --git a/artifacts/server/src/test/java/org/spongepowered/downloads/test/artifacts/server/ArtifactRepositoryTest.java b/artifacts/server/src/test/java/org/spongepowered/downloads/test/artifacts/server/ArtifactRepositoryTest.java new file mode 100644 index 00000000..8a5c796d --- /dev/null +++ b/artifacts/server/src/test/java/org/spongepowered/downloads/test/artifacts/server/ArtifactRepositoryTest.java @@ -0,0 +1,118 @@ +package org.spongepowered.downloads.test.artifacts.server; + +import io.micronaut.context.BeanContext; +import io.micronaut.core.type.Argument; +import io.micronaut.data.annotation.Query; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.client.BlockingHttpClient; +import io.micronaut.http.client.HttpClient; +import io.micronaut.http.client.annotation.Client; +import io.micronaut.inject.BeanDefinition; +import io.micronaut.inject.ExecutableMethod; +import io.micronaut.runtime.EmbeddedApplication; +import io.micronaut.serde.annotation.Serdeable; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import io.micronaut.test.extensions.junit5.annotation.TestResourcesScope; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.spongepowered.downloads.artifacts.server.query.meta.ArtifactRepository; +import org.spongepowered.downloads.artifacts.server.query.meta.domain.JpaArtifact; +import reactor.core.publisher.Mono; + +import java.util.List; +import java.util.Optional; + + +@MicronautTest +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestResourcesScope("testcontainers") +public class ArtifactRepositoryTest { + @Inject + BeanContext context; + + @Inject + EmbeddedApplication application; + + @Inject + @Client("/") + HttpClient httpClient; + + @Test + public void testItWorks() { + Assertions.assertTrue(application.isRunning()); + } + + @Test + void migrationsAreExposedViaAndEndpoint() { + BlockingHttpClient client = httpClient.toBlocking(); + + HttpResponse> response = client.exchange( + HttpRequest.GET("/liquibase"), + Argument.listOf(LiquibaseReport.class) + ); + Assertions.assertEquals(HttpStatus.OK, response.status()); + + LiquibaseReport liquibaseReport = response.body().get(0); + Assertions.assertNotNull(liquibaseReport); + Assertions.assertNotNull(liquibaseReport.getChangeSets()); + Assertions.assertEquals(2, liquibaseReport.getChangeSets().size()); + } + @Serdeable + static class LiquibaseReport { + + private List changeSets; + + public void setChangeSets(List changeSets) { + this.changeSets = changeSets; + } + + public List getChangeSets() { + return changeSets; + } + } + + @Serdeable + static class ChangeSet { + + private String id; + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + } + + @Test + public void testAnnotation() { + final BeanDefinition beanDefinition = context.getBeanDefinition(ArtifactRepository.class); + final ExecutableMethod findByGroupIdAndArtifactId = beanDefinition // (1) + .getRequiredMethod("findByGroupIdAndArtifactId", String.class, String.class); + String query = findByGroupIdAndArtifactId // (2) + .getAnnotationMetadata().stringValue(Query.class) // (3) + .orElse(null); + + final String expected = "SELECT artifact.\"id\",artifact.\"group_id\",artifact.\"artifact_id\",artifact.\"display_name\",artifact.\"website\",artifact.\"git_repository\",artifact.\"issues\",artifact.\"tag_values\",artifact.\"coordinates\",artifact.\"tag_values_for_reply\" FROM \"artifacts\".\"artifacts\" artifact WHERE (artifact.\"group_id\" = $1 AND artifact.\"artifact_id\" = $2)"; + Assertions.assertEquals( // (4) + expected, query); + } + + @Test + public void testGetArtifact() { + final ArtifactRepository repo = context.createBean(ArtifactRepository.class); + + + final Mono spongevanilla = repo.findByGroupIdAndArtifactId("org.spongepowered", "spongevanilla"); + final JpaArtifact a = spongevanilla.block(); + + } + + + +} diff --git a/artifacts/server/src/test/resources/application-test.conf b/artifacts/server/src/test/resources/application-test.conf new file mode 100644 index 00000000..a4090ab5 --- /dev/null +++ b/artifacts/server/src/test/resources/application-test.conf @@ -0,0 +1,10 @@ + +akka { + actor { + provider = "cluster" + serialization-bindings { + "org.spongepowered.downloads.akka.AkkaSerializable" = jackson-json + } + } + +} diff --git a/artifacts/server/src/test/resources/application-test.yaml b/artifacts/server/src/test/resources/application-test.yaml new file mode 100644 index 00000000..7ab844b7 --- /dev/null +++ b/artifacts/server/src/test/resources/application-test.yaml @@ -0,0 +1,22 @@ +test-resources: + containers: + postgres: + image-name: postgres:14.6 + username: testuser + password: testpassword + db-name: testdb + +liquibase: + enabled: true + datasources: + default: + change-log: 'classpath:db/liquibase-changelog.xml' # (4) +endpoints: + liquibase: + enabled: true + sensitive: false +micronaut: + environment: test + http: + client: + read-timeout: 5m diff --git a/artifacts/src/main/resources/logback.xml b/artifacts/server/src/test/resources/logback.xml similarity index 84% rename from artifacts/src/main/resources/logback.xml rename to artifacts/server/src/test/resources/logback.xml index 6010eb52..d8294a83 100644 --- a/artifacts/src/main/resources/logback.xml +++ b/artifacts/server/src/test/resources/logback.xml @@ -9,7 +9,12 @@ - + + + + + + diff --git a/artifacts/src/main/java/org/spongepowered/downloads/artifacts/server/Application.java b/artifacts/src/main/java/org/spongepowered/downloads/artifacts/server/Application.java deleted file mode 100644 index 4af14913..00000000 --- a/artifacts/src/main/java/org/spongepowered/downloads/artifacts/server/Application.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.spongepowered.downloads.artifacts.server; - -import io.micronaut.context.event.ApplicationEventListener; -import io.micronaut.runtime.Micronaut; -import io.micronaut.runtime.server.event.ServerStartupEvent; -import io.swagger.v3.oas.annotations.*; -import io.swagger.v3.oas.annotations.info.*; - -@OpenAPIDefinition( - info = @Info( - title = "artifacts", - version = "0.0" - ) -) -public class Application implements ApplicationEventListener { - - public static void main(String[] args) { - Micronaut.run(Application.class, args); - } -} diff --git a/artifacts/src/main/java/org/spongepowered/downloads/artifacts/server/query/ArtifactsQuery.java b/artifacts/src/main/java/org/spongepowered/downloads/artifacts/server/query/ArtifactsQuery.java deleted file mode 100644 index 960d4ab5..00000000 --- a/artifacts/src/main/java/org/spongepowered/downloads/artifacts/server/query/ArtifactsQuery.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.spongepowered.downloads.artifacts.server.query; - -import io.micronaut.http.annotation.Controller; - -@Controller("/groups/{groupID}/artifacts") -public class ArtifactsQuery { - - -} diff --git a/artifacts/src/main/java/org/spongepowered/downloads/artifacts/server/query/GroupsQueryController.java b/artifacts/src/main/java/org/spongepowered/downloads/artifacts/server/query/GroupsQueryController.java deleted file mode 100644 index 6c5b218c..00000000 --- a/artifacts/src/main/java/org/spongepowered/downloads/artifacts/server/query/GroupsQueryController.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.spongepowered.downloads.artifacts.server.query; - -import akka.actor.typed.ActorSystem; -import akka.actor.typed.SpawnProtocol; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import io.micronaut.http.HttpResponse; -import io.micronaut.http.annotation.Body; -import io.micronaut.http.annotation.Controller; -import io.micronaut.http.annotation.Get; -import io.micronaut.http.annotation.Post; -import jakarta.inject.Inject; - -@Controller("/groups") -public class GroupsQueryController { - - - @Inject - private ActorSystem system; - @Inject - private ClusterSharding sharding; - - @Post("/") - public HttpResponse registerGroup( - @Body GroupRegistration.RegisterGroupRequest req - ) { - return null; - } -} diff --git a/artifacts/src/main/resources/application.toml b/artifacts/src/main/resources/application.toml deleted file mode 100644 index 250d1b57..00000000 --- a/artifacts/src/main/resources/application.toml +++ /dev/null @@ -1,10 +0,0 @@ -micronaut.application.name = 'artifacts' -netty.default.allocator.max-order = 3 - -[r2dbc.datasources.default] -schema-generate = 'CREATE_DROP' -dialect = 'H2' - -[micronaut.security] -authentication = 'bearer' -token.jwt.signatures.secret.generator.secret = '${JWT_GENERATOR_SIGNATURE_SECRET:pleaseChangeThisSecretForANewOne}' diff --git a/artifacts/src/main/resources/bootstrap.toml b/artifacts/src/main/resources/bootstrap.toml deleted file mode 100644 index 82bc8221..00000000 --- a/artifacts/src/main/resources/bootstrap.toml +++ /dev/null @@ -1,4 +0,0 @@ - -[kubernetes.client.discovery] -mode = 'endpoint' -mode-configuration.endpoint.watch.enabled = true diff --git a/artifacts/src/test/java/org/spongepowered/downloads/artifacts/ArtifactsTest.java b/artifacts/src/test/java/org/spongepowered/downloads/artifacts/ArtifactsTest.java deleted file mode 100644 index 13573ede..00000000 --- a/artifacts/src/test/java/org/spongepowered/downloads/artifacts/ArtifactsTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.spongepowered.downloads.artifacts; - -import io.micronaut.runtime.EmbeddedApplication; -import io.micronaut.test.extensions.junit5.annotation.MicronautTest; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; - -import jakarta.inject.Inject; - -@MicronautTest(transactional = false) -class ArtifactsTest { - - @Inject - EmbeddedApplication application; - - @Test - void testItWorks() { - Assertions.assertTrue(application.isRunning()); - } - -} diff --git a/artifacts/worker/build.gradle.kts b/artifacts/worker/build.gradle.kts index 313f4efe..62090d55 100644 --- a/artifacts/worker/build.gradle.kts +++ b/artifacts/worker/build.gradle.kts @@ -1,47 +1,64 @@ +import org.gradle.kotlin.dsl.version +plugins { + id("com.github.johnrengelman.shadow") + id("io.micronaut.minimal.application") + id("io.micronaut.docker") + id("io.micronaut.test-resources") +} -val akkaVersion: String by project -val scalaVersion: String by project -val akkaManagementVersion: String by project -val akkaProjection: String by project +micronaut { + runtime("netty") + testRuntime("junit5") + processing { + incremental(true) + annotations("org.spongepowered.downloads.worker.*") + } + testResources { +// additionalModules.add("hibernate-reactive-postgresql") +// sharedServer.set(true) + } +} dependencies { implementation(project(":artifacts:api")) - annotationProcessor("io.micronaut.data:micronaut-data-processor") - annotationProcessor("io.micronaut:micronaut-http-validation") + implementation(project(":artifacts:events")) + implementation(libs.vavr) + + // Jackson + annotationProcessor("io.micronaut.serde:micronaut-serde-processor") + annotationProcessor("io.micronaut.validation:micronaut-validation-processor") + implementation("io.micronaut:micronaut-jackson-databind") + implementation("io.micronaut.serde:micronaut-serde-jackson") + implementation("io.micronaut.validation:micronaut-validation") + + // validation + + implementation("jakarta.annotation:jakarta.annotation-api") + implementation(libs.jakarta.validation) + + annotationProcessor("io.micronaut.microstream:micronaut-microstream-annotations") annotationProcessor("io.micronaut.openapi:micronaut-openapi") annotationProcessor("io.micronaut.security:micronaut-security-annotations") - annotationProcessor("io.micronaut.serde:micronaut-serde-processor") - implementation("com.ongres.scram:client:2.1") - implementation("io.micronaut:micronaut-http-client") + // DB Access + annotationProcessor("io.micronaut.data:micronaut-data-processor") implementation("io.micronaut:micronaut-jackson-databind") implementation("io.micronaut.data:micronaut-data-r2dbc") + implementation("io.micronaut.liquibase:micronaut-liquibase") - implementation("io.micronaut.reactor:micronaut-reactor") - implementation("io.micronaut.reactor:micronaut-reactor-http-client") - implementation("io.micronaut.security:micronaut-security-ldap") - implementation("io.micronaut.serde:micronaut-serde-jackson") - implementation("io.micronaut.toml:micronaut-toml") - implementation("io.micronaut.xml:micronaut-jackson-xml") - implementation("io.swagger.core.v3:swagger-annotations") + implementation("io.micronaut.sql:micronaut-jdbc-hikari") + runtimeOnly("ch.qos.logback:logback-classic") + runtimeOnly("org.postgresql:postgresql") implementation("io.vertx:vertx-pg-client") - implementation("jakarta.annotation:jakarta.annotation-api") - implementation(platform("com.typesafe.akka:akka-bom_${scalaVersion}:${akkaVersion}")) - implementation("com.typesafe.akka:akka-actor-typed_${scalaVersion}") - implementation("com.typesafe.akka:akka-persistence-typed_${scalaVersion}") - implementation("com.lightbend.akka:akka-projection-core_${scalaVersion}") - implementation("com.typesafe.akka:akka-cluster-sharding-typed_${scalaVersion}") - implementation("com.typesafe.akka:akka-cluster-typed_${scalaVersion}") - implementation("com.typesafe.akka:akka-discovery_${scalaVersion}") - implementation("com.typesafe.akka:akka-discovery_${scalaVersion}") - implementation("com.lightbend.akka.management:akka-management_${scalaVersion}:${akkaManagementVersion}") - implementation("com.lightbend.akka.management:akka-management-cluster-bootstrap_${scalaVersion}:${akkaManagementVersion}") +// implementation("jakarta.annotation:jakarta.annotation-api") + implementation(platform(libs.akkaBom)) + implementation(libs.bundles.actors) + implementation(libs.bundles.akkaManagement) + implementation(libs.bundles.actorsPersistence) runtimeOnly("ch.qos.logback:logback-classic") - runtimeOnly("org.postgresql:postgresql") - runtimeOnly("org.postgresql:r2dbc-postgresql") - compileOnly("org.graalvm.nativeimage:svm") +// compileOnly("org.graalvm.nativeimage:svm") - implementation("io.micronaut:micronaut-validation") +// implementation("io.micronaut:micronaut-validation") } diff --git a/artifacts/worker/src/main/java/org/spongepowered/downloads/artifacts/worker/readside/ArtifactReadside.java b/artifacts/worker/src/main/java/org/spongepowered/downloads/artifacts/worker/readside/ArtifactReadside.java new file mode 100644 index 00000000..50d8178d --- /dev/null +++ b/artifacts/worker/src/main/java/org/spongepowered/downloads/artifacts/worker/readside/ArtifactReadside.java @@ -0,0 +1,102 @@ +/* + * This file is part of SystemOfADownload, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.downloads.artifacts.worker.readside; + +import akka.persistence.query.typed.EventEnvelope; +import akka.projection.r2dbc.javadsl.R2dbcHandler; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; + + +@Singleton +public class ArtifactReadside extends R2dbcHandler> { + + @Inject + public ArtifactReadside(final ReadSide readSide) { + readSide.register(DetailsWriter.class); + } + + static final class DetailsWriter extends ReadSideProcessor { + + private final JpaReadSide readSide; + + @Inject + DetailsWriter(final JpaReadSide readSide) { + this.readSide = readSide; + } + + @Override + public ReadSideHandler buildHandler() { + return this.readSide.builder("artifact-details-builder") + .setEventHandler(DetailsEvent.ArtifactRegistered.class, (em, event) -> { + findOrRegisterArtifact(em, event.coordinates()); + }) + .setEventHandler(DetailsEvent.ArtifactDetailsUpdated.class, (em, event) -> { + final var artifact = findOrRegisterArtifact(em, event.coordinates()); + artifact.setDisplayName(event.displayName()); + }) + .setEventHandler(DetailsEvent.ArtifactWebsiteUpdated.class, (em, event) -> { + final var artifact = findOrRegisterArtifact(em, event.coordinates()); + artifact.setWebsite(event.url()); + }) + .setEventHandler(DetailsEvent.ArtifactIssuesUpdated.class, (em, event) -> { + final var artifact = findOrRegisterArtifact(em, event.coordinates()); + artifact.setIssues(event.url()); + }) + .setEventHandler(DetailsEvent.ArtifactGitRepositoryUpdated.class, (em, event) -> { + final var artifact = findOrRegisterArtifact(em, event.coordinates()); + artifact.setGitRepo(event.gitRepo()); + }) + .build(); + } + + private JpaArtifact findOrRegisterArtifact( + final EntityManager em, final ArtifactCoordinates coordinates + ) { + final var artifactQuery = em.createNamedQuery( + "Artifact.findById", + JpaArtifact.class + ); + return artifactQuery.setParameter("groupId", coordinates.groupId()) + .setParameter("artifactId", coordinates.artifactId()) + .setMaxResults(1) + .getResultStream() + .findFirst() + .orElseGet(() -> { + final var jpaArtifact = new JpaArtifact(); + jpaArtifact.setGroupId(coordinates.groupId()); + jpaArtifact.setArtifactId(coordinates.artifactId()); + em.persist(jpaArtifact); + return jpaArtifact; + }); + } + + @Override + public PSequence> aggregateTags() { + return DetailsEvent.TAG.allTags(); + } + } +} diff --git a/artifacts/worker/src/main/java/org/spongepowered/downloads/artifacts/worker/readside/JpaArtifact.java b/artifacts/worker/src/main/java/org/spongepowered/downloads/artifacts/worker/readside/JpaArtifact.java new file mode 100644 index 00000000..7d62dee2 --- /dev/null +++ b/artifacts/worker/src/main/java/org/spongepowered/downloads/artifacts/worker/readside/JpaArtifact.java @@ -0,0 +1,114 @@ +/* + * This file is part of SystemOfADownload, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.downloads.artifacts.worker.readside; + +import jakarta.persistence.Entity; +import java.util.Objects; +import io.micronaut.serde.annotation.Serdeable; + +@Entity(name = "Artifact") +@Table(name = "artifacts", + schema = "version") +@NamedQueries({ + @NamedQuery(name = "Artifact.findById", + query = "select a from Artifact a where a.groupId = :groupId and a.artifactId = :artifactId" + ) +}) +@MappedEntity +@Serderable +public class JpaArtifact { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", + updatable = false, + nullable = false) + private long id; + + @Column(name = "group_id", + nullable = false) + private String groupId; + + @Column(name = "artifact_id", + nullable = false) + private String artifactId; + + @Column(name = "display_name") + private String displayName; + + @Column(name = "website") + private String website; + + @Column(name = "git_repository") + private String gitRepo; + + @Column(name = "issues") + private String issues; + + public void setGroupId(final String groupId) { + this.groupId = groupId; + } + + public void setArtifactId(final String artifactId) { + this.artifactId = artifactId; + } + + public void setDisplayName(final String displayName) { + this.displayName = displayName; + } + + public void setWebsite(final String website) { + this.website = website; + } + + public void setGitRepo(final String gitRepo) { + this.gitRepo = gitRepo; + } + + public void setIssues(final String issues) { + this.issues = issues; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + JpaArtifact that = (JpaArtifact) o; + return id == that.id && Objects.equals(groupId, that.groupId) && Objects.equals( + artifactId, that.artifactId) && Objects.equals( + displayName, that.displayName) && Objects.equals( + website, that.website) && Objects.equals(gitRepo, that.gitRepo) && Objects.equals( + issues, that.issues); + } + + @Override + public int hashCode() { + return Objects.hash(id, groupId, artifactId, displayName, website, gitRepo, issues); + } +} diff --git a/artifacts/worker/src/main/resources/application.conf b/artifacts/worker/src/main/resources/application.conf new file mode 100644 index 00000000..997ab44f --- /dev/null +++ b/artifacts/worker/src/main/resources/application.conf @@ -0,0 +1,32 @@ +akka.persistence.journal.plugin = "akka.persistence.r2dbc.journal" +akka.persistence.snapshot-store.plugin = "akka.persistence.r2dbc.snapshot" +akka.persistence.state.plugin = "akka.persistence.r2dbc.state" + +akka.persistence.r2dbc { + journal.payload-column-type = JSONB + snapshot.payload-column-type = JSONB + state.payload-column-type = JSONB +} +akka.serialization.jackson.jackson-json.compression.algorithm = off + + +akka.persistence.r2dbc { + dialect = "postgres" + connection-factory { + driver = "postgres" + host = "localhost" + host = ${?DB_HOST} + database = "default" + database = ${?DB_NAME} + user = "admin" + user = ${?DB_USER} + password = "password" + password = ${?DB_PASSWORD} + + # ssl { + # enabled = on + # mode = "VERIFY_CA" + # root-cert = "/path/db_root.crt" + # } + } +} diff --git a/auth-api/src/main/java/org/spongepowered/downloads/auth/api/AuthService.java b/auth-api/src/main/java/org/spongepowered/downloads/auth/api/AuthService.java deleted file mode 100644 index f7ae9117..00000000 --- a/auth-api/src/main/java/org/spongepowered/downloads/auth/api/AuthService.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.auth.api; - -import akka.NotUsed; -import com.lightbend.lagom.javadsl.api.Descriptor; -import com.lightbend.lagom.javadsl.api.Service; -import com.lightbend.lagom.javadsl.api.ServiceCall; -import com.lightbend.lagom.javadsl.api.transport.Method; - -public interface AuthService extends Service { - - final class Providers { - - public static final String LDAP = "ldap"; - } - - // The response will contain a JWT if the authentication succeeded. - // Uses standard Basic auth over HTTPS to login. - ServiceCall login(); - - ServiceCall logout(); - - default Descriptor descriptor() { - return Service.named("auth") - .withCalls( - Service.restCall(Method.POST, "/auth/login", this::login), - Service.restCall(Method.POST, "/auth/logout", this::logout) - ) - .withAutoAcl(true); - } -} diff --git a/auth-api/src/main/java/org/spongepowered/downloads/auth/api/AuthenticationRequest.java b/auth-api/src/main/java/org/spongepowered/downloads/auth/api/AuthenticationRequest.java deleted file mode 100644 index 14bdde59..00000000 --- a/auth-api/src/main/java/org/spongepowered/downloads/auth/api/AuthenticationRequest.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.auth.api; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -public final class AuthenticationRequest { - - @JsonSerialize - public final record Response(@JsonProperty("token") String jwtToken) { - } - -} diff --git a/auth-api/src/main/java/org/spongepowered/downloads/auth/api/utils/AuthUtils.java b/auth-api/src/main/java/org/spongepowered/downloads/auth/api/utils/AuthUtils.java deleted file mode 100644 index 84f348f1..00000000 --- a/auth-api/src/main/java/org/spongepowered/downloads/auth/api/utils/AuthUtils.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.auth.api.utils; - -import com.lightbend.lagom.javadsl.api.ServiceCall; -import io.vavr.collection.List; -import org.pac4j.core.authorization.authorizer.Authorizer; -import org.pac4j.core.client.Client; -import org.pac4j.core.client.DirectClient; -import org.pac4j.core.config.Config; -import org.pac4j.core.context.HttpConstants; -import org.pac4j.core.credentials.TokenCredentials; -import org.pac4j.core.profile.CommonProfile; -import org.pac4j.http.client.direct.HeaderClient; -import org.pac4j.jwt.config.encryption.EncryptionConfiguration; -import org.pac4j.jwt.config.encryption.SecretEncryptionConfiguration; -import org.pac4j.jwt.config.signature.SecretSignatureConfiguration; -import org.pac4j.jwt.config.signature.SignatureConfiguration; -import org.pac4j.jwt.credentials.authenticator.JwtAuthenticator; -import org.pac4j.jwt.profile.JwtGenerator; - -import javax.inject.Inject; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Date; - -public final record AuthUtils(String encryptionSecret, String signatureSecret, - String nexusWebhookSecret, String internalHeaderSecret, - String internalHeaderKey) { - - @SuppressWarnings("rawtypes") - public Config config(final Client... additionalClients) { - final var jwtClient = this.createJwtClient(); - final var config = new Config(List.of(jwtClient).appendAll(List.of(additionalClients)).asJava()); - config.getClients().setDefaultSecurityClients(jwtClient.getName()); - this.setAuthorizers(config); - return config; - } - - public ServiceCall internalAuth(ServiceCall call) { - return call.handleRequestHeader(header -> header.withHeader(this.internalHeaderKey, this.internalHeaderSecret)); - } - - - private DirectClient createJwtClient() { - final var headerClient = new HeaderClient(); - headerClient.setName(AuthUtils.Types.JWT); - headerClient.setHeaderName(HttpConstants.AUTHORIZATION_HEADER); - headerClient.setPrefixHeader(HttpConstants.BEARER_HEADER_PREFIX); - - final var jwtAuthenticator = new JwtAuthenticator(); - if (!this.signatureSecret.isBlank()) { - jwtAuthenticator.addSignatureConfiguration(this.getSignatureConfiguration()); - } - if (!this.encryptionSecret.isBlank()) { - jwtAuthenticator.addEncryptionConfiguration(this.getEncryptionConfiguration()); - } - headerClient.setAuthenticator(jwtAuthenticator); // this should provide the correct profile automagically. - headerClient.setName(AuthUtils.Types.JWT); - return headerClient; - } - - public JwtGenerator createJwtGenerator() { - final var generator = new JwtGenerator<>(); - if (!this.signatureSecret.isBlank()) { - generator.setSignatureConfiguration(this.getSignatureConfiguration()); - } - if (!this.encryptionSecret.isBlank()) { - generator.setEncryptionConfiguration(this.getEncryptionConfiguration()); - } - generator.setExpirationTime(Date.from(Instant.now().plus(10, ChronoUnit.MINUTES))); - return generator; - } - - private void setAuthorizers(final Config config) { - config.addAuthorizer(Roles.ADMIN, Authorizers.ADMIN); - config.addAuthorizer(Roles.WEBHOOK, Authorizers.WEBHOOK); - } - - private EncryptionConfiguration getEncryptionConfiguration() { - return new SecretEncryptionConfiguration(this.encryptionSecret); - } - - private SignatureConfiguration getSignatureConfiguration() { - return new SecretSignatureConfiguration(this.signatureSecret); - } - - private SignatureConfiguration getSonatypeSignatureConfiguration() { - return new SecretSignatureConfiguration(this.nexusWebhookSecret); - } - - @Inject - public static AuthUtils configure(com.typesafe.config.Config config) { - final var authConfig = config.getConfig("systemofadownload.auth.secrets"); - final var encryptionSecret = authConfig.getString("encryption"); - final var signatureSecret = authConfig.getString("signature"); - final var nexusWebhookSecret = authConfig.getString("nexus-webhook"); - final var internalHeaderKey = authConfig.getString("internal-header"); - final var internalHeaderSecret = authConfig.getString("internal-secret"); - return new AuthUtils( - encryptionSecret, signatureSecret, nexusWebhookSecret, internalHeaderSecret, internalHeaderKey); - } - - static final class Authorizers { - static final Authorizer ADMIN = - (webContext, list) -> list.stream().anyMatch( - x -> !x.isExpired() && x.getRoles().contains(AuthUtils.Roles.ADMIN)); - static final Authorizer WEBHOOK = - (webContext, list) -> list.stream().anyMatch( - x -> !x.isExpired() && x.getRoles().contains(AuthUtils.Roles.WEBHOOK)); - } - - public static final class Types { - public static final String JWT = "jwt"; - public static final String WEBHOOK = "internal"; - } - - public static final class Roles { - public static final String ADMIN = "soad_admin"; - public static final String WEBHOOK = "soad_webhook"; - } -} diff --git a/auth-impl/src/main/java/org/spongepowered/downloads/auth/AuthModule.java b/auth-impl/src/main/java/org/spongepowered/downloads/auth/AuthModule.java deleted file mode 100644 index 2904d6c6..00000000 --- a/auth-impl/src/main/java/org/spongepowered/downloads/auth/AuthModule.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.auth; - -import com.google.inject.AbstractModule; -import com.google.inject.Provides; -import com.google.inject.name.Named; -import com.lightbend.lagom.javadsl.server.ServiceGuiceSupport; -import org.ldaptive.ConnectionConfig; -import org.ldaptive.DefaultConnectionFactory; -import org.ldaptive.auth.FormatDnResolver; -import org.ldaptive.auth.PooledBindAuthenticationHandler; -import org.ldaptive.pool.BlockingConnectionPool; -import org.ldaptive.pool.IdlePruneStrategy; -import org.ldaptive.pool.PoolConfig; -import org.ldaptive.pool.PooledConnectionFactory; -import org.ldaptive.pool.SearchValidator; -import org.pac4j.core.client.DirectClient; -import org.pac4j.core.config.Config; -import org.pac4j.core.credentials.UsernamePasswordCredentials; -import org.pac4j.core.credentials.authenticator.Authenticator; -import org.pac4j.core.exception.CredentialsException; -import org.pac4j.core.profile.CommonProfile; -import org.pac4j.http.client.direct.DirectBasicAuthClient; -import org.pac4j.jwt.profile.JwtGenerator; -import org.pac4j.ldap.profile.service.LdapProfileService; -import org.spongepowered.downloads.auth.api.AuthService; -import org.spongepowered.downloads.auth.api.utils.AuthUtils; -import play.Environment; - -import javax.inject.Inject; -import java.time.Duration; -import java.util.Collection; -import java.util.concurrent.TimeUnit; - -// See: https://github.com/pac4j/lagom-pac4j-java-demo -// -// We could alternatively go for Cookie auth that uses LDAP as an initial check, but -// JWT may suffice - we can then just use LDAP as a way enable JWT generation. We can also just -// require LDAP every time... but that's probably not what we want. -// -// JWT is great as we won't want to store sessions. However, it's not great in the sense that it'll -// be hard to expire them when done with - but we could just generate secrets on the fly when necessary -// which expire themselves after some time, and can be forcefully expired when needed. -// -// JWTs should be short lived anyway. -public final class AuthModule extends AbstractModule implements ServiceGuiceSupport { - - private boolean useDummyCredentials; - private String ldapUrl; - private String ldapBaseUserDn; - private String ldapSoadOu; - - private Duration connectionTimeout; - private Duration responseTimeout; - private Duration blockWaitTime; - - private final com.typesafe.config.Config config; - private AuthUtils auth; - - @Inject - public AuthModule(final Environment environment, final com.typesafe.config.Config config) { - this.config = config; - } - - @Override - protected void configure() { - final var authConfig = this.config.getConfig("systemofadownload.auth"); - final var ldap = authConfig.getConfig("ldap"); - this.useDummyCredentials = authConfig.getBoolean("use-dummy-ldap"); - this.ldapUrl = ldap.getString("url"); - this.ldapBaseUserDn = ldap.getString("base-user-on"); - this.ldapSoadOu = ldap.getString("soad-ou"); - this.connectionTimeout = Duration.ofMillis(ldap.getDuration("connection-timeout", TimeUnit.MILLISECONDS)); - this.responseTimeout = Duration.ofSeconds(ldap.getDuration("response-timeout", TimeUnit.SECONDS)); - this.blockWaitTime = Duration.ofSeconds(ldap.getDuration("wait-time", TimeUnit.SECONDS)); - this.auth = AuthUtils.configure(this.config); - this.bindService(AuthService.class, AuthServiceImpl.class); - } - - // TODO: If we'd rather use a GraphQL endpoint to log in, rather than - // a header, then we don't need this - we do our LDAP query manually and - // create a JWT in the body of the request/response method. - @Provides - @Named(AuthService.Providers.LDAP) - protected DirectClient providerHeaderLDAPClient() { - final var basicAuthClient = new DirectBasicAuthClient(); - basicAuthClient.setName(AuthService.Providers.LDAP); - - final Authenticator authenticator; - // TODO: Ditch this once we're running. - if (this.useDummyCredentials) { - authenticator = ((usernamePasswordCredentials, webContext) -> { - if (usernamePasswordCredentials.getUsername().equals("soad") && usernamePasswordCredentials.getPassword().equals("systemofadownload")) { - final var profile = new CommonProfile(); - profile.setId("soad"); - usernamePasswordCredentials.setUserProfile(profile); - profile.addRole(AuthUtils.Roles.ADMIN); - return; - } - throw new CredentialsException("Incorrect username and password."); - }); - } else { - // http://www.pac4j.org/3.4.x/docs/authenticators/ldap.html - // This could probably be improved... but the documentation leaves something to be desired - final var dnResolver = new FormatDnResolver(); - dnResolver.setFormat("cn=%s," + this.ldapBaseUserDn); - final var connectionConfig = new ConnectionConfig(); - connectionConfig.setConnectTimeout(this.connectionTimeout); - connectionConfig.setResponseTimeout(this.responseTimeout); - connectionConfig.setLdapUrl(this.ldapUrl); - final var connectionFactory = new DefaultConnectionFactory(); - connectionFactory.setConnectionConfig(connectionConfig); - final var poolConfig = new PoolConfig(); - poolConfig.setMinPoolSize(1); - poolConfig.setMaxPoolSize(2); - poolConfig.setValidateOnCheckOut(true); - poolConfig.setValidateOnCheckIn(true); - poolConfig.setValidatePeriodically(false); - final var searchValidator = new SearchValidator(); - final var pruneStrategy = new IdlePruneStrategy(); - final var connectionPool = new BlockingConnectionPool(); - connectionPool.setPoolConfig(poolConfig); - connectionPool.setBlockWaitTime(this.blockWaitTime); - connectionPool.setValidator(searchValidator); - connectionPool.setPruneStrategy(pruneStrategy); - connectionPool.setConnectionFactory(connectionFactory); - connectionPool.initialize(); - final var pooledConnectionFactory = new PooledConnectionFactory(); - pooledConnectionFactory.setConnectionPool(connectionPool); - // dnResolver.setConnectionFactory(pooledConnectionFactory); - final var handler = new PooledBindAuthenticationHandler(); - handler.setConnectionFactory(pooledConnectionFactory); - final var ldaptiveAuthenticator = new org.ldaptive.auth.Authenticator(); - ldaptiveAuthenticator.setDnResolver(dnResolver); - ldaptiveAuthenticator.setAuthenticationHandler(handler); - - authenticator = new LdapProfileService(pooledConnectionFactory, ldaptiveAuthenticator, "ou", this.ldapBaseUserDn); - } - - // If we're IP whitelisting it, we just need to check the webcontext. Otherwise we'll want to - // add tokens and such - basicAuthClient.setAuthenticator(authenticator); - basicAuthClient.setAuthorizationGenerator((webContext, profile) -> { - final var ouAttr = profile.getAttribute("ou"); - final boolean isAdmin; - if (ouAttr instanceof String) { - isAdmin = ouAttr.equals(this.ldapSoadOu); - } else if (ouAttr instanceof Collection) { - isAdmin = ((Collection) ouAttr).contains(this.ldapSoadOu); - } else { - isAdmin = false; - } - - if (isAdmin) { - profile.addRole(AuthUtils.Roles.ADMIN); - } - return profile; - }); - return basicAuthClient; - } - - @Provides - @SOADAuth - protected JwtGenerator provideJwtGenerator() { - return this.auth.createJwtGenerator(); - } - - @Provides - @SOADAuth - protected Config configProvider(@Named(AuthService.Providers.LDAP) final DirectClient ldapClient) { - return this.auth.config(ldapClient); - } - -} diff --git a/auth-impl/src/main/java/org/spongepowered/downloads/auth/AuthServiceImpl.java b/auth-impl/src/main/java/org/spongepowered/downloads/auth/AuthServiceImpl.java deleted file mode 100644 index 632a3242..00000000 --- a/auth-impl/src/main/java/org/spongepowered/downloads/auth/AuthServiceImpl.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.auth; - -import akka.NotUsed; -import com.google.inject.Inject; -import com.lightbend.lagom.javadsl.api.ServiceCall; -import org.pac4j.core.config.Config; -import org.pac4j.core.profile.CommonProfile; -import org.pac4j.jwt.profile.JwtGenerator; -import org.pac4j.lagom.javadsl.SecuredService; -import org.spongepowered.downloads.auth.api.AuthService; -import org.spongepowered.downloads.auth.api.AuthenticationRequest; -import org.spongepowered.downloads.auth.api.utils.AuthUtils; - -import java.sql.Date; -import java.time.Duration; -import java.time.Instant; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; - -public final class AuthServiceImpl implements AuthService, SecuredService { - - private final Config securityConfig; - private final JwtGenerator profileJwtGenerator; - private final Duration expirationTime; - - @Inject - public AuthServiceImpl( - @SOADAuth final Config config, @SOADAuth final JwtGenerator profileJwtGenerator, - com.typesafe.config.Config applicationConfig - ) { - this.securityConfig = config; - this.profileJwtGenerator = profileJwtGenerator; - this.expirationTime = Duration.ofSeconds( - applicationConfig.getDuration("systemofadownload.auth.expiration", TimeUnit.SECONDS)); - } - - @Override - public ServiceCall login() { - return this.authorize(Providers.LDAP, AuthUtils.Roles.ADMIN, profile -> { - this.profileJwtGenerator.setExpirationTime(Date.from(Instant.now().plus(this.expirationTime))); - return notUsed -> CompletableFuture.completedFuture(new AuthenticationRequest.Response(this.profileJwtGenerator.generate(profile))); - }); - } - - @Override - public ServiceCall logout() { - // TODO - if it's even possible - return this.authorize(AuthUtils.Types.JWT, AuthUtils.Roles.ADMIN, profile -> { - return notUsed -> CompletableFuture.completedFuture(NotUsed.getInstance()); - }); - } - - @Override - public Config getSecurityConfig() { - return this.securityConfig; - } -} diff --git a/auth-impl/src/main/resources/application.conf b/auth-impl/src/main/resources/application.conf deleted file mode 100644 index 31b86ef2..00000000 --- a/auth-impl/src/main/resources/application.conf +++ /dev/null @@ -1,20 +0,0 @@ - -play.http.secret.key = ${?APPLICATION_SECRET} - -play.modules { - enabled += "org.spongepowered.downloads.auth.AuthModule" -} - -systemofadownload.auth { - use-dummy-ldap = true - expiration = "1h" -} - -play.filters.enabled += "play.filters.cors.CORSFilter" -play.filters.csrf.header.protectHeaders = null -play.filters.disabled += "play.filters.csrf.CSRFFilter" -play.filters.cors { - pathPrefixes = ["/auth"] - allowedHttpMethods = ["GET", "POST"] - preflightMaxAge = 3 days -} diff --git a/auth-impl/src/main/resources/logback.xml b/auth-impl/src/main/resources/logback.xml deleted file mode 100644 index 9bd2dad8..00000000 --- a/auth-impl/src/main/resources/logback.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - System.out - - %date{hh:MM:ss.SSS} [%level] [%thread] [%logger{5}/%marker] - %coloredLevel %msg%n - - - - - 8192 - true - - - - - - - - - - - - - - - - diff --git a/auth-impl/src/main/resources/reference.conf b/auth-impl/src/main/resources/reference.conf deleted file mode 100644 index 9f66f9c6..00000000 --- a/auth-impl/src/main/resources/reference.conf +++ /dev/null @@ -1,13 +0,0 @@ -systemofadownload.auth { - use-dummy-ldap = false - expiration = "300s" - ldap { - url = "ldap://localhost:389" - base-user-on = "dc=spongepowered,dc=org" - soad-ou = "soad" - - connection-timeout = 500 - response-timeout = "1s" - wait-time = "1s" - } -} diff --git a/build.gradle.kts b/build.gradle.kts index 026df055..aaf4fe26 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,117 +1,65 @@ - +import org.gradle.plugins.ide.idea.model.IdeaLanguageLevel version = "0.1" group = "systemofadownload" +plugins { + `java-library` + id("com.github.johnrengelman.shadow") version "8.1.1" apply false + id("io.micronaut.library") version "4.0.2" apply false + id("io.micronaut.application") version "4.0.2" apply false + id("io.micronaut.docker") version "4.0.2" apply false + id("io.micronaut.test-resources") version "4.0.2" apply false +} + repositories { mavenCentral() } -plugins { - `java-library` - `application` - id("com.github.johnrengelman.shadow") - id("io.micronaut.application") - id("io.micronaut.test-resources") +tasks { + register("runLiquibase", Exec::class) { + executable("docker") + args( + "run", + "--rm", + "--mount", "type=bind,source=${project.projectDir.absolutePath}/liquibase/changelog,target=/liquibase/changelog,readonly", + "--network=host", + "liquibase/liquibase:4.23-alpine", + "--logLevel=info", + "--url=jdbc:postgresql://localhost:5432/default", + "--defaultsFile=/liquibase/changelog/liquibase.properties", + "--changeLogFile=changelog.xml", + "--classpath=/liquibase/changelog", + "--username=admin", + "--password=password", + "update") + } } -val akkaVersion: String by project -val scalaVersion: String by project -val akkaManagementVersion: String by project -val akkaProjection: String by project allprojects { - apply(plugin = "java-library") - apply(plugin = "io.micronaut.application") - apply(plugin = "io.micronaut.test-resources") - apply(plugin = "com.github.johnrengelman.shadow") - java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - if (JavaVersion.current() < JavaVersion.VERSION_17) { + sourceCompatibility = JavaVersion.VERSION_20 + targetCompatibility = JavaVersion.VERSION_20 + if (JavaVersion.current() < JavaVersion.VERSION_20) { toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) + languageVersion.set(JavaLanguageVersion.of(20)) } } } -} - -dependencies { - annotationProcessor("io.micronaut.data:micronaut-data-processor") - annotationProcessor("io.micronaut:micronaut-http-validation") - annotationProcessor("io.micronaut.openapi:micronaut-openapi") - annotationProcessor("io.micronaut.security:micronaut-security-annotations") - annotationProcessor("io.micronaut.serde:micronaut-serde-processor") - implementation("com.ongres.scram:client:2.1") - implementation("io.micronaut:micronaut-http-client") - implementation("io.micronaut:micronaut-jackson-databind") - implementation("io.micronaut.data:micronaut-data-r2dbc") - implementation("io.micronaut.liquibase:micronaut-liquibase") - implementation("io.micronaut.reactor:micronaut-reactor") - implementation("io.micronaut.reactor:micronaut-reactor-http-client") - implementation("io.micronaut.security:micronaut-security-ldap") - implementation("io.micronaut.serde:micronaut-serde-jackson") - implementation("io.micronaut.toml:micronaut-toml") - implementation("io.micronaut.xml:micronaut-jackson-xml") - implementation("io.swagger.core.v3:swagger-annotations") - implementation("io.vertx:vertx-pg-client") - implementation("jakarta.annotation:jakarta.annotation-api") - implementation(platform("com.typesafe.akka:akka-bom_${scalaVersion}:${akkaVersion}")) - implementation("com.typesafe.akka:akka-actor-typed_${scalaVersion}") - implementation("com.typesafe.akka:akka-persistence-typed_${scalaVersion}") - implementation("com.lightbend.akka:akka-projection-core_${scalaVersion}:${akkaProjection}") - implementation("com.typesafe.akka:akka-cluster-sharding-typed_${scalaVersion}") - implementation("com.typesafe.akka:akka-cluster-typed_${scalaVersion}") - implementation("com.typesafe.akka:akka-discovery_${scalaVersion}") - implementation("com.typesafe.akka:akka-discovery_${scalaVersion}") - implementation("com.lightbend.akka.management:akka-management_${scalaVersion}:${akkaManagementVersion}") - implementation("com.lightbend.akka.management:akka-management-cluster-bootstrap_${scalaVersion}:${akkaManagementVersion}") - - runtimeOnly("ch.qos.logback:logback-classic") - runtimeOnly("org.postgresql:postgresql") - runtimeOnly("org.postgresql:r2dbc-postgresql") - compileOnly("org.graalvm.nativeimage:svm") - - implementation("io.micronaut:micronaut-validation") - -} - - -application { - mainClass.set("systemofadownload.Application") -} -tasks { - dockerBuild { - images.add("${project.name}:${project.version}") - } - dockerBuildNative { - images.add("${project.name}:${project.version}") - - } -} -graalvmNative.toolchainDetection.set(false) -micronaut { - runtime("netty") - testRuntime("junit5") - processing { - incremental(true) - annotations("systemofadownload.*") - } - testResources { - additionalModules.add("r2dbc-postgresql") - } -} -graalvmNative { - binaries { - named("main") { - imageName.set("mn-graalvm-application") - buildArgs("--verboase") + tasks { + withType { + options.compilerArgs.add("--enable-preview") + } + withType { + jvmArgs("--enable-preview") } } + } + diff --git a/build.sbt b/build.sbt deleted file mode 100644 index 1cd077bb..00000000 --- a/build.sbt +++ /dev/null @@ -1,358 +0,0 @@ -import com.lightbend.lagom.core.LagomVersion -import com.typesafe.sbt.packager.docker.DockerChmodType -import de.heikoseeberger.sbtheader.HeaderPlugin.autoImport.{HeaderLicenseStyle, headerLicenseStyle} - -import scala.sys.process.Process - -ThisBuild / organization := "org.spongepowered" -ThisBuild / version := "0.2-SNAPSHOT" -ThisBuild / scalaVersion := "2.13.8" - -// License setup -ThisBuild / organizationName := "SpongePowered" -ThisBuild / startYear := Some(2020) -ThisBuild / licenses += ("MIT", url("https://opensource.org/licenses/MIT")) -ThisBuild / scmInfo := Some(ScmInfo(url("https://github.com/SpongePowered/SystemOfADownload"), - "scm:git@github.com:spongepowered/systemofadownload.git")) -ThisBuild / developers := List( - Developer( - id = "gabizou", - name = "Gabriel Harris-Rouquette", - email = "gabizou@spongepowered.org", - url = url("https://github.com/gabizou") - ) -) -ThisBuild / description := "A Web Application for indexing and cataloging Artifacts in Maven Repositories" -ThisBuild / homepage := Some(url("https://github.com/SpongePowered/SystemOfADownload")) -ThisBuild / pomIncludeRepository := { _ => false } -ThisBuild / publishMavenStyle := true -ThisBuild / versionScheme := Some("early-semver") - -// Basically, because of sbt's limited publishing to only one repository, -// we can take advantage of environment variables to publish to multiple -// repositories within the same job. -ThisBuild / publishTo := { - (sys.env.get("REPO_NAME"), sys.env.get("REPO_CREDENTIAL_FILE"), sys.env.get("SONATYPE_SNAPSHOT_REPO"), sys.env.get("SONATYPE_RELEASE_REPO")) match { - case (Some(name), Some(repoTarget), Some(snapshotRepo), Some(releaseRepo)) => { - credentials += Credentials(Path.userHome / ".sbt" / repoTarget) - if (isSnapshot.value) - Some(name at snapshotRepo) - else - Some(name at releaseRepo) - } - case (_, _, _, _) => None - } -} - -// Deployed Repositories -// TODO - Figure out deploying to our sonatype and to maven central -// Then also figure out deploying docker images??? - -// Liquibase Docker Tasks -lazy val buildLiquibaseImage = taskKey[Unit]("Build the Liquibase docker image") -val rootFilter = ScopeFilter(inProjects(soadRoot), inConfigurations(Compile)) -buildLiquibaseImage := { - val versionTag = version.all(rootFilter).value.head - Process(Seq("docker", "buildx", "build", "-t", s"spongepowered/systemofadownload-liquibase:$versionTag", "./liquibase/", "-f", "./liquibase/Dockerfile")).! -} - -lazy val runLiquibase = taskKey[Unit]("Runs the liquibase migration against a local dev database") - -lazy val setupDevEnvironment = taskKey[Unit]("Runs the necessary commands to set up a local environment to run the application") - -lazy val setupPostgres = taskKey[Unit]("Runs a postgres instance for local development") -lazy val setupKafka = taskKey[Unit]("Runs Kafka and zookeeper instance for local development") -setupDevEnvironment := { - setupPostgres.value - runLiquibase.value - setupKafka.value -} -setupPostgres := { - Process(Seq("sh", - s"${soadRoot.base.absolutePath}/dev/run_postgres.sh", - )).! -} - -setupKafka := { - Process(Seq("sh", s"${soadRoot.base.absolutePath}/dev/run_kafka.sh")).! -} - -runLiquibase := { - Process(Seq("docker", - "run", - "--rm", - "--mount", s"type=bind,source=${soadRoot.base.absolutePath}/liquibase/changelog,target=/liquibase/changelog,readonly", - "--network=host", - "liquibase/liquibase", - "--logLevel=info", - s"--url=jdbc:postgresql://localhost:5432/default", - "--defaultsFile=/liquibase/changelog/liquibase.properties", - "--changeLogFile=changelog.xml", - "--classpath=/liquibase/changelog", - "--username=admin", - "--password=password", - "update")).! -} - -// region dependency versions - -//noinspection SbtDependencyVersionInspection -ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-java8-compat" % "always" - -lazy val vavr = "io.vavr" % "vavr" % "0.10.4" -lazy val vavrJackson = "io.vavr" % "vavr-jackson" % "0.10.3" - -lazy val pac4jHttp = "org.pac4j" % "pac4j-http" % "3.7.0" -lazy val pac4jJwt = "org.pac4j" % "pac4j-jwt" % "3.7.0" - -lazy val lagomPac4j = "org.pac4j" %% "lagom-pac4j" % "2.2.1" -lazy val lagomPac4jLdap = "org.pac4j" % "pac4j-ldap" % "3.7.0" - -// Enable Junit5 -lazy val junit = "org.junit.jupiter" % "junit-jupiter-api" % "5.9.0" % Test -// sbt-jupiter-interface -lazy val jupiterInterface = "net.aichler" % "jupiter-interface" % "0.11.1" % Test - - -// Play jackson uses 2.11, but 2.12 is backwards compatible -lazy val jacksonDataBind = "com.fasterxml.jackson.core" % "jackson-databind" % "2.14.0" -lazy val jacksonDataTypeJsr310 = "com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % "2.14.0" -lazy val jacksonDataformatXml = "com.fasterxml.jackson.dataformat" % "jackson-dataformat-xml" % "2.14.0" -lazy val jacksonDataformatCbor = "com.fasterxml.jackson.dataformat" % "jackson-dataformat-cbor" % "2.14.0" -lazy val jacksonDatatypeJdk8 = "com.fasterxml.jackson.datatype" % "jackson-datatype-jdk8" % "2.14.0" -lazy val jacksonParameterNames = "com.fasterxml.jackson.module" % "jackson-module-parameter-names" % "2.14.0" -lazy val jacksonParanamer = "com.fasterxml.jackson.module" % "jackson-module-paranamer" % "2.14.0" -lazy val jacksonScala = "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.14.0" -lazy val jacksonGuava = "com.fasterxml.jackson.datatype" % "jackson-datatype-guava" % "2.14.0" -lazy val jacksonPcollections = "com.fasterxml.jackson.datatype" % "jackson-datatype-pcollections" % "2.14.0" -// endregion - -lazy val akkaHttp = "com.typesafe.akka" %% "akka-http" % LagomVersion.akkaHttp -lazy val akkaJackson = "com.typesafe.akka" %% "akka-http-jackson" % LagomVersion.akkaHttp - -lazy val akkaStreamTyped = "com.typesafe.akka" %% "akka-stream-typed" % LagomVersion.akka -lazy val akkaPersistenceTestkit = "com.typesafe.akka" %% "akka-persistence-testkit" % LagomVersion.akka % Test -lazy val akkaKubernetesDiscovery = "com.lightbend.akka.discovery" %% "akka-discovery-kubernetes-api" % "1.1.3" - -lazy val playFilterHelpers = "com.typesafe.play" %% "filters-helpers" % LagomVersion.play - -lazy val hibernate = "org.hibernate" % "hibernate-core" % "5.5.6" -lazy val postgres = "org.postgresql" % "postgresql" % "42.5.0" -lazy val hibernateTypes = "com.vladmihalcea" % "hibernate-types-55" % "2.20.0" - -lazy val guice = "com.google.inject" % "guice" % "5.1.0" - -lazy val jgit = "org.eclipse.jgit" % "org.eclipse.jgit" % "6.1.0.202203080745-r" -lazy val jgit_jsch = "org.eclipse.jgit" % "org.eclipse.jgit.ssh.jsch" % "6.1.0.202203080745-r" - -lazy val mavenArtifact = "org.apache.maven" % "maven-artifact" % "3.8.5" - -lazy val testContainers = "org.testcontainers" % "testcontainers" % "1.17.3" % Test -lazy val testContainersJunit = "org.testcontainers" % "junit-jupiter" % "1.17.3" % Test -lazy val testContainersPostgres = "org.testcontainers" % "postgresql" % "1.17.3" % Test - -// endregion - -// region - project blueprints - -def soadProject(name: String) = - Project(name, file(name)).settings( - moduleName := s"systemofadownload-$name", - Compile / javacOptions := Seq("--release", "17", "--enable-preview", "-parameters", "-encoding", "UTF-8"), //Override the settings Lagom sets - artifactName := { (_: ScalaVersion, module: ModuleID, artifact: Artifact) => - s"${artifact.name}-${module.revision}.${artifact.extension}" - }, - Compile / doc / sources := Seq.empty, - Compile / packageDoc / publishArtifact := false, - compileOrder := CompileOrder.JavaThenScala, //Needed so scalac doesn't try to parse the files - headerLicense := Some(HeaderLicense.Custom( - """This file is part of SystemOfADownload, licensed under the MIT License (MIT). - | - |Copyright (c) SpongePowered - |Copyright (c) contributors - | - |Permission is hereby granted, free of charge, to any person obtaining a copy - |of this software and associated documentation files (the "Software"), to deal - |in the Software without restriction, including without limitation the rights - |to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - |copies of the Software, and to permit persons to whom the Software is - |furnished to do so, subject to the following conditions: - | - |The above copyright notice and this permission notice shall be included in - |all copies or substantial portions of the Software. - | - |THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - |IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - |FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - |AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - |LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - |OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - |THE SOFTWARE. - |""".stripMargin - )), - headerLicenseStyle := HeaderLicenseStyle.SpdxSyntax, - headerEmptyLine := false - - ) - -def apiSoadProject(name: String) = - soadProject(name).settings( - libraryDependencies ++= Seq( - // Lagom Java API - lagomJavadslApi, - // Bump Jackson over Lagom's jackson - jacksonDataBind, - jacksonDataTypeJsr310, - jacksonDataformatCbor, - jacksonDatatypeJdk8, - jacksonParameterNames, - jacksonParanamer, - jacksonScala, - jacksonGuava, // Eventually not needed when we migrate off lagom - jacksonPcollections, // Eventually not needed when we migrate off lagom - // Then add the Lagom Jackson modules - lagomJavadslJackson, - //Language Features - vavr, - // Override guice from Lagom to support Java 16 - guice - ) - ) - -def serverSoadProject(name: String) = - soadProject(name) - .enablePlugins(LagomJava, DockerPlugin) - .settings( - libraryDependencies ++= Seq( - // Bump Jackson over Lagom's jackson - jacksonDataBind, - jacksonDataTypeJsr310, - jacksonDataformatCbor, - jacksonDatatypeJdk8, - jacksonParameterNames, - jacksonParanamer, - jacksonScala, - jacksonGuava, // Eventually not needed when we migrate off lagom - jacksonPcollections, // Eventually not needed when we migrate off lagom - //Lagom Dependencies - // Specifically set up Akka-Clustering - lagomJavadslCluster, - // Set up Discovery between Services - lagomJavadslAkkaDiscovery, - akkaKubernetesDiscovery, - // I mean, we are a server, aren't we? - lagomJavadslServer, - // Set up logging - lagomLogback, - //Language Features for Serialization/Deserialization - vavrJackson, - // Override guice from Lagom to support Java 16 - guice, - // Ensure the play filter helpers are included - playFilterHelpers, - //Test Dependencies - lagomJavadslTestKit, - junit, // Always enable Junit 5 - jupiterInterface - ) - ).settings( - dockerUpdateLatest := true, - dockerBaseImage := "eclipse-temurin:17.0.3_7-jre", - dockerChmodType := DockerChmodType.UserGroupWriteExecute, - dockerExposedPorts := Seq(9000, 8558, 2552), - dockerLabels ++= Map( - "author" -> "spongepowered" - ), - Docker / maintainer := "spongepowered", - Docker / packageName := s"systemofadownload-$name", - dockerUsername := Some("spongepowered"), - Universal / javaOptions ++= Seq( - "-Dpidfile.path=/dev/null" - ) - ) - -def implSoadProject(name: String, implFor: Project) = - serverSoadProject(name).dependsOn( - //The service we're implementing - implFor - ).settings( - libraryDependencies ++= Seq( - // We use kafka for all inter-service message forwarding - lagomJavadslKafkaBroker - ) - ) - -def implSoadProjectWithPersistence(name: String, implFor: Project) = - implSoadProject(name, implFor).settings( - libraryDependencies ++= Seq( - //Lagom Database Dependencies - lagomJavadslPersistenceJpa, - //Database Dependencies - hibernate, - postgres, - akkaPersistenceTestkit - ) - ) - -// endregion - -// region Project Definitions - -lazy val `downloads-api` = soadProject("downloads-api").enablePlugins(DockerPlugin) - .settings( - libraryDependencies ++= Seq( - // App - mavenArtifact, - - // Akka - akkaHttp, - akkaStreamTyped, - akkaKubernetesDiscovery, - - // Jackson serialization - akkaJackson, - jacksonDataBind, - jacksonDataTypeJsr310, - jacksonDataformatCbor, - jacksonDatatypeJdk8, - jacksonParameterNames, - jacksonParanamer, - jacksonScala, - //Language Features - vavr, - // Persistence - hibernate, - postgres, - // Testing - akkaPersistenceTestkit, - junit, - jupiterInterface - ), - dockerUpdateLatest := true, - dockerBaseImage := "eclipse-temurin:17.0.3_7-jre", - dockerChmodType := DockerChmodType.UserGroupWriteExecute, - dockerExposedPorts := Seq(9000, 8558, 2552), - // dockerLabels ++= Map( - // "author" -> "spongepowered" - // ), - Docker / maintainer := "spongepowered", - Docker / packageName := s"systemofadownload-$name", - dockerUsername := Some("spongepowered"), - Universal / javaOptions ++= Seq( - "-Dpidfile.path=/dev/null" - ) - ) - - -// endregion - -lazy val soadRoot = project.in(file(".")).settings( - name := "SystemOfADownload" -).aggregate( -) - -ThisBuild / lagomCassandraEnabled := false -ThisBuild / lagomKafkaEnabled := false -ThisBuild / lagomKafkaPort := 9092 -ThisBuild / lagomServicesPortRange := PortRange(21000, 23000) - diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts new file mode 100644 index 00000000..dee3e800 --- /dev/null +++ b/buildSrc/settings.gradle.kts @@ -0,0 +1,9 @@ + +dependencyResolutionManagement { + + versionCatalogs { + create("akka") { + from(files("../gradle/libs.versions.toml")) + } + } +} diff --git a/buildSrc/src/main/kotlin/soad.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/soad.java-conventions.gradle.kts deleted file mode 100644 index 58082c01..00000000 --- a/buildSrc/src/main/kotlin/soad.java-conventions.gradle.kts +++ /dev/null @@ -1,20 +0,0 @@ -plugins { - id("java") - id("application") - id("com.github.johnrengelman.shadow") version "7.1.2" - id("io.micronaut.application") version "3.7.0" - id("io.micronaut.test-resources") version "3.7.0" -} - -group = "org.spongepowered" -version = "1.0" -repositories { - mavenCentral() -} - -dependencies { - - id("com.github.johnrengelman.shadow") version "7.1.2" - id("io.micronaut.application") version "3.7.0" - id("io.micronaut.test-resources") version "3.7.0" -} diff --git a/dev/run_postgres.sh b/dev/run_postgres.sh index 3cdf3bf6..c4760d07 100755 --- a/dev/run_postgres.sh +++ b/dev/run_postgres.sh @@ -17,6 +17,6 @@ if [ ! "$(docker ps -q -f name="${postgres_name}")" ]; then -e POSTGRES_PASSWORD=password \ -e POSTGRES_DB=default \ -p 5432:5432 \ - postgres:13-alpine \ + postgres:15-alpine \ postgres -N 500 fi diff --git a/downloads-api/src/main/java/com/example/QuickstartApp.java b/downloads-api/src/main/java/com/example/QuickstartApp.java deleted file mode 100644 index ac1187ee..00000000 --- a/downloads-api/src/main/java/com/example/QuickstartApp.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.example; - -import akka.NotUsed; -import akka.actor.typed.ActorRef; -import akka.actor.typed.Behavior; -import akka.http.javadsl.ConnectHttp; -import akka.http.javadsl.Http; -import akka.http.javadsl.ServerBinding; -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.HttpResponse; -import akka.http.javadsl.server.Route; -import akka.stream.Materializer; -import akka.stream.javadsl.Flow; -import akka.actor.typed.javadsl.Adapter; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.ActorSystem; - -import java.net.InetSocketAddress; -import java.util.concurrent.CompletionStage; - -//#main-class -public class QuickstartApp { - // #start-http-server - static void startHttpServer(Route route, ActorSystem system) { - CompletionStage futureBinding = - Http.get(system).newServerAt("localhost", 8080).bind(route); - - futureBinding.whenComplete((binding, exception) -> { - if (binding != null) { - InetSocketAddress address = binding.localAddress(); - system.log().info("Server online at http://{}:{}/", - address.getHostString(), - address.getPort()); - } else { - system.log().error("Failed to bind HTTP endpoint, terminating system", exception); - system.terminate(); - } - }); - } - // #start-http-server - - public static void main(String[] args) throws Exception { - //#server-bootstrapping - Behavior rootBehavior = Behaviors.setup(context -> { - ActorRef userRegistryActor = - context.spawn(UserRegistry.create(), "UserRegistry"); - - UserRoutes userRoutes = new UserRoutes(context.getSystem(), userRegistryActor); - startHttpServer(userRoutes.userRoutes(), context.getSystem()); - - return Behaviors.empty(); - }); - - // boot up server using the route as defined below - ActorSystem.create(rootBehavior, "HelloAkkaHttpServer"); - //#server-bootstrapping - } - -} -//#main-class - - diff --git a/downloads-api/src/main/java/com/example/UserRegistry.java b/downloads-api/src/main/java/com/example/UserRegistry.java deleted file mode 100644 index c8d914d2..00000000 --- a/downloads-api/src/main/java/com/example/UserRegistry.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.example; - -import akka.actor.typed.ActorRef; -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.AbstractBehavior; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.javadsl.Receive; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.*; - -//#user-registry-actor -public class UserRegistry extends AbstractBehavior { - - // actor protocol - interface Command {} - - public record GetUsers(ActorRef replyTo) implements Command { - } - - public record CreateUser(User user, ActorRef replyTo) implements Command { - } - - public record GetUserResponse(Optional maybeUser) { - } - - public record GetUser(String name, ActorRef replyTo) implements Command { - } - - - public record DeleteUser(String name, ActorRef replyTo) implements Command { - } - - - public record ActionPerformed(String description) implements Command { - } - - //#user-case-classes - public record User( - @JsonProperty("name") String name, - @JsonProperty("age") int age, - @JsonProperty("countryOfResidence") String countryOfResidence - ) { - @JsonCreator - public User {} - } - - public record Users(List users) { } - //#user-case-classes - - private final List users = new ArrayList<>(); - - private UserRegistry(ActorContext context) { - super(context); - } - - public static Behavior create() { - return Behaviors.setup(UserRegistry::new); - } - - @Override - public Receive createReceive() { - return newReceiveBuilder() - .onMessage(GetUsers.class, this::onGetUsers) - .onMessage(CreateUser.class, this::onCreateUser) - .onMessage(GetUser.class, this::onGetUser) - .onMessage(DeleteUser.class, this::onDeleteUser) - .build(); - } - - private Behavior onGetUsers(GetUsers command) { - // We must be careful not to send out users since it is mutable - // so for this response we need to make a defensive copy - command.replyTo.tell(new Users(List.copyOf(users))); - return this; - } - - private Behavior onCreateUser(CreateUser command) { - users.add(command.user); - command.replyTo.tell(new ActionPerformed(String.format("User %s created.", command.user.name))); - return this; - } - - private Behavior onGetUser(GetUser command) { - Optional maybeUser = users.stream() - .filter(user -> user.name.equals(command.name)) - .findFirst(); - command.replyTo.tell(new GetUserResponse(maybeUser)); - return this; - } - - private Behavior onDeleteUser(DeleteUser command) { - users.removeIf(user -> user.name.equals(command.name)); - command.replyTo.tell(new ActionPerformed(String.format("User %s deleted.", command.name))); - return this; - } - -} -//#user-registry-actor diff --git a/downloads-api/src/main/java/com/example/UserRoutes.java b/downloads-api/src/main/java/com/example/UserRoutes.java deleted file mode 100644 index 6eea10f3..00000000 --- a/downloads-api/src/main/java/com/example/UserRoutes.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.example; - -import java.time.Duration; -import java.util.Optional; -import java.util.concurrent.CompletionStage; - -import com.example.UserRegistry.User; -import akka.actor.typed.ActorRef; -import akka.actor.typed.ActorSystem; -import akka.actor.typed.Scheduler; -import akka.actor.typed.javadsl.AskPattern; -import akka.http.javadsl.marshallers.jackson.Jackson; - -import static akka.http.javadsl.server.Directives.*; - -import akka.http.javadsl.model.StatusCodes; -import akka.http.javadsl.server.PathMatchers; -import akka.http.javadsl.server.Route; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Routes can be defined in separated classes like shown in here - */ -//#user-routes-class -public class UserRoutes { - //#user-routes-class - private final static Logger log = LoggerFactory.getLogger(UserRoutes.class); - private final ActorRef userRegistryActor; - private final Duration askTimeout; - private final Scheduler scheduler; - - public UserRoutes(ActorSystem system, ActorRef userRegistryActor) { - this.userRegistryActor = userRegistryActor; - scheduler = system.scheduler(); - askTimeout = system.settings().config().getDuration("my-app.routes.ask-timeout"); - } - - private CompletionStage getUser(String name) { - return AskPattern.ask(userRegistryActor, ref -> new UserRegistry.GetUser(name, ref), askTimeout, scheduler); - } - - private CompletionStage deleteUser(String name) { - return AskPattern.ask(userRegistryActor, ref -> new UserRegistry.DeleteUser(name, ref), askTimeout, scheduler); - } - - private CompletionStage getUsers() { - return AskPattern.ask(userRegistryActor, UserRegistry.GetUsers::new, askTimeout, scheduler); - } - - private CompletionStage createUser(User user) { - return AskPattern.ask(userRegistryActor, ref -> new UserRegistry.CreateUser(user, ref), askTimeout, scheduler); - } - - /** - * This method creates one route (of possibly many more that will be part of your Web App) - */ - //#all-routes - public Route userRoutes() { - return pathPrefix("users", () -> - concat( - //#users-get-delete - pathEnd(() -> - concat( - get(() -> - onSuccess(getUsers(), - users -> complete(StatusCodes.OK, users, Jackson.marshaller()) - ) - ), - post(() -> - entity( - Jackson.unmarshaller(User.class), - user -> - onSuccess(createUser(user), performed -> { - log.info("Create result: {}", performed.description()); - return complete(StatusCodes.CREATED, performed, Jackson.marshaller()); - }) - ) - ) - ) - ), - //#users-get-delete - //#users-get-post - path(PathMatchers.segment(), (String name) -> - concat( - get(() -> - //#retrieve-user-info - rejectEmptyResponse(() -> - onSuccess(getUser(name), performed -> - complete(StatusCodes.OK, performed.maybeUser(), Jackson.marshaller()) - ) - ) - //#retrieve-user-info - ), - delete(() -> - //#users-delete-logic - onSuccess(deleteUser(name), performed -> { - log.info("Delete result: {}", performed.description()); - return complete(StatusCodes.OK, performed, Jackson.marshaller()); - } - ) - //#users-delete-logic - ) - ) - ) - //#users-get-post - ) - ); - } - //#all-routes - -} diff --git a/downloads-api/src/main/java/module-info.java b/downloads-api/src/main/java/module-info.java deleted file mode 100644 index e4c4aee5..00000000 --- a/downloads-api/src/main/java/module-info.java +++ /dev/null @@ -1,17 +0,0 @@ -module org.spongepowered.downloads.app { - exports org.spongepowered.downloads.api; - - requires akka.actor.typed; - requires akka.http; - requires akka.http.core; - requires akka.stream; - requires akka.actor; - requires com.fasterxml.jackson.annotation; - requires akka.http.marshallers.jackson; - requires org.slf4j; - requires org.hibernate.orm.core; - requires java.persistence; - requires io.vavr; - requires com.fasterxml.jackson.databind; - requires maven.artifact; -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/api/Artifact.java b/downloads-api/src/main/java/org/spongepowered/downloads/api/Artifact.java deleted file mode 100644 index a1a4cb4c..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/api/Artifact.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.api; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import java.net.URI; -import java.util.Optional; - -@JsonSerialize -public final record Artifact( - @JsonProperty Optional classifier, - @JsonProperty URI downloadUrl, - @JsonProperty String md5, - @JsonProperty String sha1, - @JsonProperty String extension -) { - @JsonCreator - public Artifact { - } - -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/api/ArtifactCollection.java b/downloads-api/src/main/java/org/spongepowered/downloads/api/ArtifactCollection.java deleted file mode 100644 index a3b29fe8..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/api/ArtifactCollection.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.api; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import io.vavr.collection.List; - -@JsonDeserialize -public final record ArtifactCollection( - @JsonProperty("assets") List components, - @JsonProperty("coordinates") MavenCoordinates coordinates -) { - - @JsonCreator - public ArtifactCollection { - } - -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/api/ArtifactCoordinates.java b/downloads-api/src/main/java/org/spongepowered/downloads/api/ArtifactCoordinates.java deleted file mode 100644 index b9f11573..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/api/ArtifactCoordinates.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.api; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import java.util.StringJoiner; - -@JsonDeserialize -public record ArtifactCoordinates( - @JsonProperty(required = true) String groupId, - @JsonProperty(required = true) String artifactId -) { - @JsonCreator - public ArtifactCoordinates { - } - - public MavenCoordinates version(String version) { - return MavenCoordinates.parse( - new StringJoiner(":").add(this.groupId()).add(this.artifactId()).add(version).toString()); - } - - public String asMavenString() { - return this.groupId() + ":" + this.artifactId(); - } - - /** - * The group id of an artifact, as defined by the Apache Maven documentation. - * See Maven Coordinates. - */ - public String groupId() { - return groupId; - } - - /** - * The artifact id of an artifact, as defined by the Apache Maven documentation. - * See Maven Coordinates. - */ - public String artifactId() { - return artifactId; - } -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/api/Group.java b/downloads-api/src/main/java/org/spongepowered/downloads/api/Group.java deleted file mode 100644 index 4efe1286..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/api/Group.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.api; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -@JsonDeserialize -public record Group( - @JsonProperty(required = true) String groupCoordinates, - @JsonProperty(required = true) String name, - @JsonProperty(required = true) String website -) { - - @JsonCreator - public Group { - } - -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/api/MavenCoordinates.java b/downloads-api/src/main/java/org/spongepowered/downloads/api/MavenCoordinates.java deleted file mode 100644 index cab9ae7a..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/api/MavenCoordinates.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.api; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import org.apache.maven.artifact.versioning.ComparableVersion; - -import java.util.Objects; -import java.util.StringJoiner; -import java.util.regex.Pattern; - -@JsonDeserialize -public final class MavenCoordinates implements Comparable { - - private static final Pattern MAVEN_REGEX = Pattern.compile("^[-\\w.]+$"); - - /** - * The group id of an artifact, as defined by the Apache Maven documentation. - * See Maven Coordinates. - */ - @JsonProperty(required = true) - public final String groupId; - /** - * The artifact id of an artifact, as defined by the Apache Maven documentation. - * See Maven Coordinates. - */ - @JsonProperty(required = true) - public final String artifactId; - /** - * The version of an artifact, as defined by the Apache Maven documentation. This is - * traditionally specified as a Maven repository searchable version string, such as - * {@code 1.0.0-SNAPSHOT}. - * See Maven Coordinates. - */ - @JsonProperty(required = true) - public final String version; - - @JsonIgnore - public final VersionType versionType; - - @JsonIgnore - private final ComparableVersion mavenVersion; - - /** - * Parses a set of maven formatted coordinates as per - * Apache - * Maven's documentation. - * - * @param coordinates The coordinates delimited by `:` - * @return A parsed set of MavenCoordinates - */ - public static MavenCoordinates parse(final String coordinates) { - final var splitCoordinates = coordinates.split(":"); - if (splitCoordinates.length < 3) { - throw new IllegalArgumentException( - "Coordinates are not formatted or delimited by the `:` character or contains fewer than the required size"); - } - final var groupId = splitCoordinates[0]; - if (!MAVEN_REGEX.asMatchPredicate().test(groupId)) { - throw new IllegalArgumentException("GroupId does not conform to regex rules for a maven group id"); - } - final var artifactId = splitCoordinates[1]; - if (!MAVEN_REGEX.asMatchPredicate().test(artifactId)) { - throw new IllegalArgumentException("ArtifactId does not conform to regex rules for a maven artifact id"); - } - final var version = splitCoordinates[2]; - - VersionType.fromVersion(version); // validates the version is going to be valid somewhat - return new MavenCoordinates(groupId, artifactId, version); - } - - public MavenCoordinates(String coordinates) { - final var splitCoordinates = coordinates.split(":"); - if (splitCoordinates.length < 3) { - throw new IllegalArgumentException( - "Coordinates are not formatted or delimited by the `:` character or contains fewer than the required size"); - } - final var groupId = splitCoordinates[0]; - if (!MAVEN_REGEX.asMatchPredicate().test(groupId)) { - throw new IllegalArgumentException("GroupId does not conform to regex rules for a maven group id"); - } - final var artifactId = splitCoordinates[1]; - if (!MAVEN_REGEX.asMatchPredicate().test(artifactId)) { - throw new IllegalArgumentException("ArtifactId does not conform to regex rules for a maven artifact id"); - } - final var version = splitCoordinates[2]; - - VersionType.fromVersion(version); // validates the version is going to be valid somewhat - this.groupId = groupId; - this.artifactId = artifactId; - this.version = version; - this.versionType = VersionType.fromVersion(version); - this.mavenVersion = new ComparableVersion(version); - } - - @JsonCreator - public MavenCoordinates( - @JsonProperty("groupId") final String groupId, - @JsonProperty("artifactId") final String artifactId, - @JsonProperty("version") final String version - ) { - this.groupId = groupId; - this.artifactId = artifactId; - this.version = version; - this.versionType = VersionType.fromVersion(version); - this.mavenVersion = new ComparableVersion(version); - } - - @JsonIgnore - public String asStandardCoordinates() { - return new StringJoiner(":") - .add(this.groupId) - .add(this.artifactId) - .add(this.versionType.asStandardVersionString(this.version)) - .toString(); - } - - @JsonIgnore - public boolean isSnapshot() { - return this.versionType.isSnapshot(); - } - - @JsonIgnore - public ArtifactCoordinates asArtifactCoordinates() { - return new ArtifactCoordinates(this.groupId, this.artifactId); - } - - @Override - public String toString() { - return new StringJoiner(":") - .add(this.groupId) - .add(this.artifactId) - .add(this.version) - .toString(); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || this.getClass() != o.getClass()) { - return false; - } - final MavenCoordinates that = (MavenCoordinates) o; - return Objects.equals(this.groupId, that.groupId) && Objects.equals( - this.artifactId, that.artifactId) && Objects.equals(this.version, that.version); - } - - @Override - public int hashCode() { - return Objects.hash(this.groupId, this.artifactId, this.version); - } - - @Override - public int compareTo(final MavenCoordinates o) { - final var group = this.groupId.compareTo(o.groupId); - if (group != 0) { - return group; - } - final var artifact = this.artifactId.compareTo(o.artifactId); - if (artifact != 0) { - return artifact; - } - return this.mavenVersion.compareTo(o.mavenVersion); - } -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/app/SystemOfADownloadsApp.java b/downloads-api/src/main/java/org/spongepowered/downloads/app/SystemOfADownloadsApp.java deleted file mode 100644 index f5e37946..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/app/SystemOfADownloadsApp.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.spongepowered.downloads.app; - -import akka.NotUsed; -import akka.actor.typed.ActorRef; -import akka.actor.typed.ActorSystem; -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.Behaviors; -import akka.http.javadsl.Http; -import akka.http.javadsl.ServerBinding; -import akka.http.javadsl.server.Route; -import org.spongepowered.downloads.artifacts.ArtifactQueries; -import org.spongepowered.downloads.artifacts.ArtifactRoutes; - -import java.net.InetSocketAddress; -import java.util.concurrent.CompletionStage; - -public final class SystemOfADownloadsApp { - - public static void main(final String[] args) { - //#server-bootstrapping - Behavior rootBehavior = Behaviors.setup(context -> { - ActorRef userRegistryActor = - context.spawn(ArtifactQueries.create(), "ArtifactQueries"); - - ArtifactRoutes userRoutes = new ArtifactRoutes(context.getSystem(), userRegistryActor); - startHttpServer(userRoutes.artifactRoutes(), context.getSystem()); - - return Behaviors.empty(); - }); - - // boot up server using the route as defined below - ActorSystem.create(rootBehavior, "HelloAkkaHttpServer"); - //#server-bootstrapping - } - - static void startHttpServer(Route route, ActorSystem system) { - CompletionStage futureBinding = - Http.get(system).newServerAt("localhost", 8080).bind(route); - - futureBinding.whenComplete((binding, exception) -> { - if (binding != null) { - InetSocketAddress address = binding.localAddress(); - system.log().info("Server online at http://{}:{}/", - address.getHostString(), - address.getPort()); - } else { - system.log().error("Failed to bind HTTP endpoint, terminating system", exception); - system.terminate(); - } - }); - } -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/ArtifactQueries.java b/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/ArtifactQueries.java deleted file mode 100644 index a066952c..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/ArtifactQueries.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.spongepowered.downloads.artifacts; - -import akka.actor.typed.ActorRef; -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.AbstractBehavior; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.javadsl.Receive; -import org.spongepowered.downloads.artifacts.transport.GetArtifactDetailsResponse; -import org.spongepowered.downloads.artifacts.transport.GetArtifactsResponse; -import org.spongepowered.downloads.artifacts.transport.GroupResponse; -import org.spongepowered.downloads.artifacts.transport.GroupsResponse; - -public class ArtifactQueries extends AbstractBehavior { - - public static Behavior create() { - return Behaviors.receive(Command.class) - .build(); - } - - public ArtifactQueries(final ActorContext context) { - super(context); - } - - public sealed interface Command { - - record GetGroup( - String groupId, - ActorRef replyTo - ) implements Command { - } - - record GetArtifacts( - String groupId, - ActorRef replyTo - ) implements Command { - } - - record GetGroups( - ActorRef replyTo - ) implements Command { - } - - record GetArtifactDetails( - String groupId, - String artifactId, - ActorRef replyTo - ) implements Command { - } - - } - - - @Override - public Receive createReceive() { - return this.newReceiveBuilder() - .build(); - } -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/ArtifactRoutes.java b/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/ArtifactRoutes.java deleted file mode 100644 index 1733a99c..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/ArtifactRoutes.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.spongepowered.downloads.artifacts; - -import static akka.http.javadsl.server.Directives.complete; -import static akka.http.javadsl.server.Directives.concat; -import static akka.http.javadsl.server.Directives.get; -import static akka.http.javadsl.server.Directives.onSuccess; -import static akka.http.javadsl.server.Directives.path; -import static akka.http.javadsl.server.Directives.pathPrefix; -import static akka.http.javadsl.server.Directives.rejectEmptyResponse; - -import akka.actor.typed.ActorRef; -import akka.actor.typed.ActorSystem; -import akka.actor.typed.Scheduler; -import akka.actor.typed.javadsl.AskPattern; -import akka.http.javadsl.marshallers.jackson.Jackson; -import akka.http.javadsl.model.StatusCodes; -import akka.http.javadsl.server.PathMatchers; -import akka.http.javadsl.server.Route; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.spongepowered.downloads.artifacts.transport.GetArtifactsResponse; -import org.spongepowered.downloads.artifacts.transport.GroupResponse; -import org.spongepowered.downloads.artifacts.transport.GroupsResponse; - -import java.time.Duration; -import java.util.concurrent.CompletionStage; - -public final class ArtifactRoutes { - - public static final Logger logger = LoggerFactory.getLogger(ArtifactRoutes.class); - - private final ActorRef artifactQueries; - private final Duration askTimeout; - private final Scheduler scheduler; - - public ArtifactRoutes(ActorSystem system, ActorRef artifactQueries) { - this.artifactQueries = artifactQueries; - scheduler = system.scheduler(); - askTimeout = system.settings().config().getDuration("my-app.routes.ask-timeout"); - } - - private CompletionStage getGroups() { - return AskPattern.ask(artifactQueries, ArtifactQueries.Command.GetGroups::new, askTimeout, scheduler); - } - - private CompletionStage getGroup(String name) { - return AskPattern.ask( - artifactQueries, ref -> new ArtifactQueries.Command.GetGroup(name, ref), askTimeout, scheduler); - } - - private CompletionStage getArtifacts(String name) { - return AskPattern.ask( - artifactQueries, ref -> new ArtifactQueries.Command.GetArtifacts(name, ref), askTimeout, scheduler); - } - - private Route getGroup() { - return get(() -> onSuccess(getGroups(), groups -> - complete(StatusCodes.OK, groups, Jackson.marshaller()) - )); - } - - /** - * This method creates one route (of possibly many more that will be part of your Web App) - */ - public Route artifactRoutes() { - // v1/groups - return pathPrefix("groups", () -> - concat( - getGroup(), - // v1/groups/:groupId/ - path(PathMatchers.segment(), (String groupId) -> - concat( - get(() -> rejectEmptyResponse(() -> - onSuccess(getGroup(groupId), performed -> { - final var group = performed.group(); - logger.info("Groups get: {}", group); - if (group.isEmpty()) { - return complete(StatusCodes.BAD_REQUEST, "group not found"); - } - return complete(StatusCodes.OK, group, Jackson.marshaller()); - }))), - // v1/groups/:groupId/artifacts - pathPrefix("artifacts", () -> get(() -> rejectEmptyResponse(() -> - onSuccess(getArtifacts(groupId), performed -> { - logger.info("Get result: {}", performed.artifactIds()); - return complete(StatusCodes.OK, performed, Jackson.marshaller()); - }))) - )) - ) - )); - } -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/ArtifactDetails.java b/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/ArtifactDetails.java deleted file mode 100644 index 7fad1598..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/ArtifactDetails.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.transport; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.vavr.control.Either; -import io.vavr.control.Try; - -import java.net.URL; - -public final class ArtifactDetails { - - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, - property = "type") - @JsonSubTypes({ - @JsonSubTypes.Type(value = Update.Website.class, - name = "website"), - @JsonSubTypes.Type(value = Update.DisplayName.class, - name = "displayName"), - @JsonSubTypes.Type(value = Update.Issues.class, - name = "issues"), - @JsonSubTypes.Type(value = Update.GitRepository.class, - name = "gitRepository"), - }) - @JsonDeserialize - public sealed interface Update { - - Either validate(); - - record Website( - @JsonProperty(required = true) String website - ) implements Update { - - @JsonCreator - public Website { - } - - @Override - public Either validate() { - return Try.of(() -> new URL(this.website())) - .toEither(() -> new BadRequest(String.format("Malformed url: %s", this.website()))); - } - } - - record DisplayName( - @JsonProperty(required = true) String display - ) implements Update { - - @JsonCreator - public DisplayName { - } - - @Override - public Either validate() { - return Either.right(this.display.trim()); - } - } - - record Issues( - @JsonProperty(required = true) String issues - ) implements Update { - @JsonCreator - public Issues { - } - - @Override - public Either validate() { - return Try.of(() -> new URL(this.issues())) - .toEither(() -> new BadRequest(String.format("Malformed url: %s", this.issues()))); - } - } - - record GitRepository( - @JsonProperty(required = true) String gitRepo - ) implements Update { - - @JsonCreator - public GitRepository { - } - - @Override - public Either validate() { - return Try.of(() -> new URL(this.gitRepo())) - .toEither(() -> new BadRequest(String.format("Malformed url: %s", this.gitRepo()))); - } - } - } - - @JsonSerialize - public record Response( - String name, - String displayName, - String website, - String issues, - String gitRepo - ) { - - } - - -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/ArtifactRegistration.java b/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/ArtifactRegistration.java deleted file mode 100644 index 4ab6dcc7..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/ArtifactRegistration.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.transport; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; - -public final class ArtifactRegistration { - - @JsonSerialize - public record RegisterArtifact( - @JsonProperty(required = true) String artifactId, - @JsonProperty(required = true) String displayName - ) { - - @JsonCreator - public RegisterArtifact(final String artifactId, final String displayName) { - this.artifactId = artifactId; - this.displayName = displayName; - } - - } - - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, - property = "type") - @JsonSubTypes({ - @JsonSubTypes.Type(value = Response.GroupMissing.class, - name = "UnknownGroup"), - @JsonSubTypes.Type(value = Response.ArtifactRegistered.class, - name = "RegisteredArtifact"), - @JsonSubTypes.Type(value = Response.ArtifactAlreadyRegistered.class, - name = "AlreadyRegistered"), - }) - public sealed interface Response extends Jsonable { - - @JsonSerialize - record ArtifactRegistered(@JsonProperty ArtifactCoordinates coordinates) implements Response { - - } - - @JsonSerialize - record ArtifactAlreadyRegistered( - @JsonProperty String artifactName, - @JsonProperty String groupId - ) implements Response { - - } - - @JsonSerialize - record GroupMissing(@JsonProperty("groupId") String s) implements Response { - - } - - } -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/GetArtifactDetailsResponse.java b/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/GetArtifactDetailsResponse.java deleted file mode 100644 index 065628bf..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/GetArtifactDetailsResponse.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.transport; - -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.vavr.collection.Map; -import io.vavr.collection.SortedSet; -import org.spongepowered.downloads.api.ArtifactCoordinates; - -import java.io.Serializable; -import java.util.Optional; - -@JsonSerialize -public record GetArtifactDetailsResponse(Optional maybeArtifact) { - - @JsonSerialize - record RetrievedArtifact( - ArtifactCoordinates coordinates, - String displayName, - String website, - String gitRepository, - String issues, - Map> tags - ) implements Serializable { - - } -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/GetArtifactsResponse.java b/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/GetArtifactsResponse.java deleted file mode 100644 index f22f221b..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/GetArtifactsResponse.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.transport; - - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.vavr.collection.List; - -import java.io.Serializable; - - -@JsonSerialize -public record GetArtifactsResponse(@JsonProperty List artifactIds) implements Serializable { - @JsonCreator - public GetArtifactsResponse {} -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/GroupRegistration.java b/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/GroupRegistration.java deleted file mode 100644 index e7815bb9..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/GroupRegistration.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.transport; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.Group; - -public final class GroupRegistration { - - @JsonDeserialize - public record RegisterGroupRequest( - @JsonProperty(required = true) String name, - @JsonProperty(required = true) String groupCoordinates, - @JsonProperty(required = true) String website - ) { - - @JsonCreator - public RegisterGroupRequest { } - - } - - public interface Response extends Jsonable { - - record GroupAlreadyRegistered(String groupNameRequested) implements Response { - } - - record GroupRegistered(Group group) implements Response { - - } - } -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/GroupResponse.java b/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/GroupResponse.java deleted file mode 100644 index 2a3e2790..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/GroupResponse.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.transport; - -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import org.spongepowered.downloads.api.Group; - -import java.io.Serializable; -import java.util.Optional; - -@JsonSerialize -public record GroupResponse(Optional group) implements Serializable { - -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/GroupsResponse.java b/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/GroupsResponse.java deleted file mode 100644 index c2947a8a..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/artifacts/transport/GroupsResponse.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.artifacts.transport; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.vavr.collection.List; -import org.spongepowered.downloads.api.Group; - -@JsonSerialize -public record GroupsResponse( - @JsonProperty("groups") - List groups -) { - -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/routes/VersionRoutes.java b/downloads-api/src/main/java/org/spongepowered/downloads/routes/VersionRoutes.java deleted file mode 100644 index 7d35e43f..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/routes/VersionRoutes.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.spongepowered.downloads.routes; - -public class VersionRoutes { -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/versions/VersionQueries.java b/downloads-api/src/main/java/org/spongepowered/downloads/versions/VersionQueries.java deleted file mode 100644 index b4723af2..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/versions/VersionQueries.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.spongepowered.downloads.versions; - -import akka.actor.typed.ActorRef; -import org.spongepowered.downloads.api.ArtifactCoordinates; -import org.spongepowered.downloads.api.MavenCoordinates; -import org.spongepowered.downloads.versions.transport.QueryLatest; -import org.spongepowered.downloads.versions.transport.QueryVersions; - -class VersionQueries { - - sealed interface Command { - record GetVersion(MavenCoordinates coordinates, ActorRef replyTo) implements Command {} - record GetVersions(ArtifactCoordinates coordinates, ActorRef replyTo) implements Command {} - } -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/versions/VersionRoutes.java b/downloads-api/src/main/java/org/spongepowered/downloads/versions/VersionRoutes.java deleted file mode 100644 index 0a44794b..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/versions/VersionRoutes.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.spongepowered.downloads.versions; - - -import static akka.http.javadsl.server.Directives.complete; -import static akka.http.javadsl.server.Directives.concat; -import static akka.http.javadsl.server.Directives.get; -import static akka.http.javadsl.server.Directives.onSuccess; -import static akka.http.javadsl.server.Directives.pathPrefix; -import static akka.http.javadsl.server.PathMatchers.segment; - -import akka.actor.typed.ActorRef; -import akka.actor.typed.ActorSystem; -import akka.actor.typed.Scheduler; -import akka.actor.typed.javadsl.AskPattern; -import akka.http.javadsl.marshallers.jackson.Jackson; -import akka.http.javadsl.model.StatusCodes; -import akka.http.javadsl.server.PathMatchers; -import akka.http.javadsl.server.Route; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.spongepowered.downloads.api.ArtifactCoordinates; -import org.spongepowered.downloads.versions.transport.QueryVersions; - -import java.time.Duration; -import java.util.concurrent.CompletionStage; - -public final class VersionRoutes { - - public static final Logger logger = LoggerFactory.getLogger( - org.spongepowered.downloads.artifacts.ArtifactRoutes.class); - - private final ActorRef artifactQueries; - private final Duration askTimeout; - private final Scheduler scheduler; - - public VersionRoutes(ActorSystem system, ActorRef artifactQueries) { - this.artifactQueries = artifactQueries; - scheduler = system.scheduler(); - askTimeout = system.settings().config().getDuration("my-app.routes.ask-timeout"); - } - - CompletionStage getVersions(ArtifactCoordinates coordinates) { - return AskPattern.ask(this.artifactQueries, ref -> new VersionQueries.Command.GetVersions( - coordinates, ref - ), this.askTimeout, this.scheduler); - } - - - /** - * This method creates one route (of possibly many more that will be part of your Web App) - */ - public Route artifactRoutes() { - // v1/groups - return pathPrefix( - segment("groups").slash(segment()).slash(segment("artifacts").slash(segment())), - (groupId, artifactId) -> - concat( - get(() -> onSuccess(this.getVersions(new ArtifactCoordinates(groupId, artifactId)), (resp) -> { - if (resp.size() <= 0) { - return complete(StatusCodes.NOT_FOUND); - } - return complete(StatusCodes.OK, resp, Jackson.marshaller()); - })), - path(PathMatchers.segment("versions").slash(), version -> get(() -> - onSuccess(this.getVersions(), resp -> { - return complete(StatusCodes.OK, resp, Jackson.marshaller()); - }))) - - ) - ); - } -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/JpaTaggedVersion.java b/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/JpaTaggedVersion.java deleted file mode 100644 index b8d61a97..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/JpaTaggedVersion.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.models; - -import org.hibernate.annotations.Immutable; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.JoinColumns; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; -import java.io.Serializable; -import java.util.Objects; - -@Entity(name = "TaggedVersion") -@Immutable -@Table(name = "versioned_tags", schema = "version") -@NamedQueries({ - @NamedQuery(name = "TaggedVersion.findAllForVersion", - query = - """ - select view from TaggedVersion view - where view.mavenGroupId = :groupId and view.mavenArtifactId = :artifactId and view.version = :version - """ - ), - @NamedQuery( - name = "TaggedVersion.findAllMatchingTagValues", - query = - """ - select view from TaggedVersion view - where view.mavenGroupId = :groupId - and view.mavenArtifactId = :artifactId - and view.tagName = :tagName - and view.tagValue like :tagValue - """ - ), - @NamedQuery( - name = "TaggedVersion.findMatchingTagValuesAndRecommendation", - query = - """ - select view from TaggedVersion view - where view.mavenGroupId = :groupId - and view.mavenArtifactId = :artifactId - and view.tagName = :tagName - and view.tagValue like :tagValue - and (view.versionView.recommended = :recommended or view.versionView.manuallyRecommended = :recommended) - """ - ) -}) -public class JpaTaggedVersion implements Serializable { - - @Id - @Column(name = "version_id", updatable = false) - private long versionId; - - @Id - @Column(updatable = false, name = "artifact_internal_id") - private long artifactId; - - @Id - @Column(name = "maven_group_id", updatable = false) - private String mavenGroupId; - - @Id - @Column(name = "maven_artifact_id", updatable = false) - private String mavenArtifactId; - - @Id - @Column(name = "maven_version", - updatable = false) - private String version; - - @Id - @Column(name = "tag_name", - updatable = false) - private String tagName; - - @Column(name = "tag_value", - updatable = false) - private String tagValue; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumns({ - @JoinColumn(name = "maven_version", - referencedColumnName = "version", - nullable = false, - updatable = false, - insertable = false), - @JoinColumn(name = "maven_group_id", - referencedColumnName = "group_id", - nullable = false, - updatable = false, - insertable = false), - @JoinColumn(name = "maven_artifact_id", - referencedColumnName = "artifact_id", - nullable = false, - updatable = false, - insertable = false) - }) - private JpaVersionedArtifactView versionView; - - public String getTagName() { - return tagName; - } - - public String getTagValue() { - return tagValue; - } - - public MavenCoordinates asMavenCoordinates() { - return new MavenCoordinates(this.mavenGroupId, this.mavenArtifactId, this.version); - } - - public JpaVersionedArtifactView getVersion() { - return this.versionView; - } - - public String getMavenVersion() { - return this.version; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - JpaTaggedVersion that = (JpaTaggedVersion) o; - return versionId == that.versionId && artifactId == that.artifactId && Objects.equals( - mavenGroupId, that.mavenGroupId) && Objects.equals( - mavenArtifactId, that.mavenArtifactId) && Objects.equals( - version, that.version) && Objects.equals(tagName, that.tagName) && Objects.equals( - tagValue, that.tagValue); - } - - @Override - public int hashCode() { - return Objects.hash(versionId, artifactId, mavenGroupId, mavenArtifactId, version, tagName, tagValue); - } -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/JpaVersionedArtifactView.java b/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/JpaVersionedArtifactView.java deleted file mode 100644 index 5b9a2f40..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/JpaVersionedArtifactView.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.models; - -import io.vavr.Tuple; -import io.vavr.collection.HashMap; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import org.hibernate.annotations.Immutable; -import org.spongepowered.downloads.artifact.api.Artifact; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.query.api.models.TagCollection; -import org.spongepowered.downloads.versions.query.api.models.VersionedChangelog; - -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.Table; -import java.io.Serializable; -import java.net.URI; -import java.util.Comparator; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; - -@Immutable -@Entity(name = "VersionedArtifactView") -@Table(name = "versioned_artifacts", - schema = "version") -@NamedQueries({ - @NamedQuery( - name = "VersionedArtifactView.count", - query = """ - select count(v) from VersionedArtifactView v - where v.groupId = :groupId and v.artifactId = :artifactId - """ - ), - @NamedQuery( - name = "VersionedArtifactView.recommendedCount", - query = """ - select count(v) from VersionedArtifactView v - where v.groupId = :groupId and v.artifactId = :artifactId and v.recommended = :recommended - """ - ), - @NamedQuery( - name = "VersionedArtifactView.findByArtifact", - query = """ - select v from VersionedArtifactView v where v.artifactId = :artifactId and v.groupId = :groupId - """ - ), - @NamedQuery( - name = "VersionedArtifactView.findByArtifactAndRecommendation", - query = """ - select v from VersionedArtifactView v - where v.artifactId = :artifactId and v.groupId = :groupId and (v.recommended = :recommended or v.manuallyRecommended = :recommended) - """ - ), - @NamedQuery( - name = "VersionedArtifactView.findExplicitly", - query = """ - select v from VersionedArtifactView v - left join fetch v.tags - where v.artifactId = :artifactId and v.groupId = :groupId and v.version = :version - """ - ), - @NamedQuery( - name = "VersionedArtifactView.findFullVersionDetails", - query = """ - select v from VersionedArtifactView v - left join fetch v.tags - left join fetch v.assets - where v.artifactId = :artifactId and v.groupId = :groupId and v.version = :version - """ - ) -}) -public class JpaVersionedArtifactView implements Serializable { - - @Id - @Column(name = "artifact_id", - updatable = false) - private String artifactId; - - @Id - @Column(name = "group_id", - updatable = false) - private String groupId; - - @Id - @Column(name = "version", - updatable = false) - private String version; - - @Column(name = "recommended") - private boolean recommended; - - @Column(name = "manual_recommendation") - private boolean manuallyRecommended; - - @Column(name = "ordering") - private int ordering; - - @OneToMany( - targetEntity = JpaTaggedVersion.class, - fetch = FetchType.LAZY, - cascade = CascadeType.ALL, - orphanRemoval = true, - mappedBy = "versionView") - private Set tags; - - @OneToMany( - targetEntity = JpaVersionedAsset.class, - cascade = CascadeType.ALL, - fetch = FetchType.LAZY, - orphanRemoval = true, - mappedBy = "versionView" - ) - private Set assets; - - @OneToOne( - targetEntity = JpaVersionedChangelog.class, - mappedBy = "versionView", - fetch = FetchType.LAZY - ) - private JpaVersionedChangelog changelog; - - public Set getTags() { - return tags; - } - - public String version() { - return this.version; - } - - public Map getTagValues() { - final var results = this.getTags(); - final var tuple2Stream = results.stream().map( - taggedVersion -> Tuple.of(taggedVersion.getTagName(), taggedVersion.getTagValue())); - return tuple2Stream - .collect(HashMap.collector()); - } - - public TagCollection asTagCollection() { - final var results = this.getTags(); - final var tuple2Stream = results.stream().map( - taggedVersion -> Tuple.of(taggedVersion.getTagName(), taggedVersion.getTagValue())); - return new TagCollection(tuple2Stream - .collect(HashMap.collector()), this.recommended); - } - - public boolean isRecommended() { - return this.recommended || this.manuallyRecommended; - } - - public MavenCoordinates asMavenCoordinates() { - return new MavenCoordinates(this.groupId + ":" + this.artifactId + ":" + this.version); - } - - Set getAssets() { - return assets; - } - - public List asArtifactList() { - return this.getAssets() - .stream() - .map( - asset -> new Artifact( - Optional.ofNullable(asset.getClassifier()), - URI.create(asset.getDownloadUrl()), - new String(asset.getMd5()), - new String(asset.getSha1()), - asset.getExtension() - ) - ).collect(List.collector()) - .sorted(Comparator.comparing(artifact -> artifact.classifier().orElse(""))); - } - - public Optional asVersionedCommit() { - final var changelog = this.changelog; - if (changelog == null) { - return Optional.empty(); - } - return Optional.of(changelog.getChangelog()); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - JpaVersionedArtifactView that = (JpaVersionedArtifactView) o; - return Objects.equals(artifactId, that.artifactId) && Objects.equals( - groupId, that.groupId) && Objects.equals(version, that.version); - } - - @Override - public int hashCode() { - return Objects.hash(artifactId, groupId, version); - } - -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/JpaVersionedAsset.java b/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/JpaVersionedAsset.java deleted file mode 100644 index 3f3cd1d4..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/JpaVersionedAsset.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.models; - -import org.hibernate.annotations.Immutable; -import org.hibernate.annotations.Type; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.IdClass; -import javax.persistence.JoinColumn; -import javax.persistence.JoinColumns; -import javax.persistence.Lob; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import java.io.Serializable; -import java.util.Arrays; -import java.util.Objects; - -@Immutable -@Entity(name = "VersionedAsset") -@Table(name = "artifact_versioned_assets", - schema = "version") -@IdClass(VersionedAssetID.class) -public class JpaVersionedAsset implements Serializable { - - @Id - @Column(name = "group_id") - private String groupId; - @Id - @Column(name = "artifact_id") - private String artifactId; - - @Id - @Column(name = "version") - private String version; - - @Id - @Column(name = "classifier") - private String classifier; - - @Id - @Column(name = "extension") - private String extension; - - @Column(name = "download_url") - private String downloadUrl; - - @Lob - @Type(type = "org.hibernate.type.BinaryType") - @Column(name = "md5") - private byte[] md5; - - @Lob - @Type(type = "org.hibernate.type.BinaryType") - @Column(name = "sha1") - private byte[] sha1; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumns({ - @JoinColumn(name = "version", - referencedColumnName = "version", - nullable = false, - updatable = false, - insertable = false), - @JoinColumn(name = "group_id", - referencedColumnName = "group_id", - nullable = false, - updatable = false, - insertable = false), - @JoinColumn(name = "artifact_id", - referencedColumnName = "artifact_id", - nullable = false, - updatable = false, - insertable = false) - }) - private JpaVersionedArtifactView versionView; - - public String getClassifier() { - return classifier; - } - - public String getExtension() { - return extension; - } - - public String getDownloadUrl() { - return downloadUrl; - } - - public byte[] getMd5() { - return md5; - } - - public byte[] getSha1() { - return sha1; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - JpaVersionedAsset that = (JpaVersionedAsset) o; - return Objects.equals(groupId, that.groupId) && Objects.equals( - artifactId, that.artifactId) && Objects.equals(version, that.version) && Objects.equals( - classifier, that.classifier) && Objects.equals(extension, that.extension) && Objects.equals( - downloadUrl, that.downloadUrl) && Arrays.equals(md5, that.md5) && Arrays.equals( - sha1, that.sha1) && Objects.equals(versionView, that.versionView); - } - - @Override - public int hashCode() { - int result = Objects.hash(groupId, artifactId, version, classifier, extension, downloadUrl, versionView); - result = 31 * result + Arrays.hashCode(md5); - result = 31 * result + Arrays.hashCode(sha1); - return result; - } -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/JpaVersionedChangelog.java b/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/JpaVersionedChangelog.java deleted file mode 100644 index 68396320..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/JpaVersionedChangelog.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.models; - -import com.vladmihalcea.hibernate.type.json.JsonBinaryType; -import org.hibernate.annotations.Immutable; -import org.hibernate.annotations.Type; -import org.hibernate.annotations.TypeDef; -import org.hibernate.annotations.TypeDefs; -import org.spongepowered.downloads.versions.query.api.models.VersionedChangelog; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.JoinColumns; -import javax.persistence.OneToOne; -import javax.persistence.Table; -import java.io.Serializable; -import java.net.URL; -import java.util.Objects; - -@Immutable -@Entity(name = "VersionedChangelog") -@Table( - name = "versioned_changelogs", - schema = "version" -) -@TypeDefs({ - @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) -}) -public class JpaVersionedChangelog implements Serializable { - - @Id - @Column(name = "version_id", updatable = false) - private String versionId; - - @Id - @Column(name = "group_id", updatable = false) - private String groupId; - - @Id - @Column(name = "artifact_id", updatable = false) - private String artifactId; - - @OneToOne(fetch = FetchType.LAZY) - @JoinColumns({ - @JoinColumn(name = "version", referencedColumnName = "version", insertable = false, updatable = false), - @JoinColumn(name = "group_id", referencedColumnName = "group_id", insertable = false, updatable = false), - @JoinColumn(name = "artifact_id", referencedColumnName = "artifact_id", insertable = false, updatable = false) - }) - private JpaVersionedArtifactView versionView; - - @Column(name = "commit_sha", nullable = false) - private String sha; - - @Type(type = "jsonb") - @Column(name = "changelog", columnDefinition = "jsonb") - private VersionedChangelog changelog; - - @Column(name = "repo") - private URL repo; - - @Column(name = "branch") - private String branch; - - public String getSha() { - return sha; - } - - public VersionedChangelog getChangelog() { - return changelog; - } - - public URL getRepo() { - return repo; - } - - public String getBranch() { - return branch; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - JpaVersionedChangelog that = (JpaVersionedChangelog) o; - return Objects.equals(versionId, that.versionId) && Objects.equals( - groupId, that.groupId) && Objects.equals(artifactId, that.artifactId) && Objects.equals( - versionView, that.versionView) && Objects.equals(sha, that.sha) && Objects.equals( - changelog, that.changelog) && Objects.equals(repo, that.repo) && Objects.equals( - branch, that.branch); - } - - @Override - public int hashCode() { - return Objects.hash(versionId, groupId, artifactId, versionView, sha, changelog, repo, branch); - } - - @Override - public String toString() { - return "JpaVersionedChangelog{" + - "versionId='" + versionId + '\'' + - ", groupId='" + groupId + '\'' + - ", artifactId='" + artifactId + '\'' + - ", versionView=" + versionView + - ", sha='" + sha + '\'' + - ", changelog=" + changelog + - ", repo=" + repo + - ", branch='" + branch + '\'' + - '}'; - } -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/VersionedArtifactID.java b/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/VersionedArtifactID.java deleted file mode 100644 index 3156c74d..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/VersionedArtifactID.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.models; - -import java.io.Serializable; -import java.util.Objects; - -public class VersionedArtifactID implements Serializable { - private String artifactId; - - private String groupId; - - private String version; - - public VersionedArtifactID(final String artifactId, final String groupId, final String version) { - this.artifactId = artifactId; - this.groupId = groupId; - this.version = version; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - VersionedArtifactID that = (VersionedArtifactID) o; - return artifactId.equals(that.artifactId) && groupId.equals(that.groupId) && version.equals(that.version); - } - - @Override - public int hashCode() { - return Objects.hash(artifactId, groupId, version); - } -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/VersionedAssetID.java b/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/VersionedAssetID.java deleted file mode 100644 index 7bbba094..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/versions/models/VersionedAssetID.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.spongepowered.downloads.versions.models; - -import java.io.Serializable; - -public record VersionedAssetID( - String groupId, - String artifactId, - String version, - String classifier, - String extension - ) implements Serializable { -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/versions/transport/QueryLatest.java b/downloads-api/src/main/java/org/spongepowered/downloads/versions/transport/QueryLatest.java deleted file mode 100644 index 80d4a01d..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/versions/transport/QueryLatest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.transport; - -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import org.spongepowered.downloads.artifact.api.Artifact; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -import java.util.Optional; - -public interface QueryLatest { - - @JsonSerialize - record VersionInfo(MavenCoordinates coordinates, - List assets, - Map tagValues, - Optional commit, - boolean recommended - ) { - } - -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/versions/transport/QueryVersions.java b/downloads-api/src/main/java/org/spongepowered/downloads/versions/transport/QueryVersions.java deleted file mode 100644 index 2ac34a11..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/versions/transport/QueryVersions.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.transport; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import org.spongepowered.downloads.api.Artifact; -import org.spongepowered.downloads.api.MavenCoordinates; - -import java.util.Optional; - -public interface QueryVersions { - - @JsonSerialize - record VersionInfo(@JsonProperty Map artifacts, int offset, int limit, int size) { - - @JsonCreator - public VersionInfo { - } - } - - @JsonSerialize - record VersionDetails( - @JsonProperty("coordinates") MavenCoordinates coordinates, - @JsonProperty("commit") Optional commit, - @JsonProperty("assets") List components, - @JsonProperty("tags") Map tagValues, - @JsonProperty("recommended") boolean recommended - ) { - } - -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/versions/transport/TagCollection.java b/downloads-api/src/main/java/org/spongepowered/downloads/versions/transport/TagCollection.java deleted file mode 100644 index f740b7a8..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/versions/transport/TagCollection.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.transport; - -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.vavr.collection.Map; - -@JsonSerialize -public record TagCollection( - Map tagValues, - boolean recommended -) { -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/versions/transport/VersionedChangelog.java b/downloads-api/src/main/java/org/spongepowered/downloads/versions/transport/VersionedChangelog.java deleted file mode 100644 index efb8d8f0..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/versions/transport/VersionedChangelog.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.transport; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import io.vavr.collection.List; - -import java.net.URI; - -@JsonDeserialize -public final record VersionedChangelog( - List commits, - @JsonInclude(JsonInclude.Include.NON_DEFAULT) boolean processing -) { - - @JsonCreator - public VersionedChangelog { - } - - @JsonDeserialize - public final record IndexedCommit( - VersionedCommit commit, - List submoduleCommits - ) { - @JsonCreator - public IndexedCommit { - } - } - - @JsonDeserialize - public final record Submodule( - String name, - URI gitRepository, - List commits - ) { - @JsonCreator - public Submodule { - } - } - -} diff --git a/downloads-api/src/main/java/org/spongepowered/downloads/versions/transport/VersionedCommit.java b/downloads-api/src/main/java/org/spongepowered/downloads/versions/transport/VersionedCommit.java deleted file mode 100644 index 869d6888..00000000 --- a/downloads-api/src/main/java/org/spongepowered/downloads/versions/transport/VersionedCommit.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.transport; - - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import java.net.URI; -import java.time.ZonedDateTime; - -@JsonDeserialize -public record VersionedCommit( - String message, - String body, - String sha, - Author author, - Commiter commiter, - URI link, - ZonedDateTime commitDate -) { - - @JsonCreator - public VersionedCommit { - } - - @JsonDeserialize - public record Author( - String name, - String email - ) { - @JsonCreator - public Author { - } - } - - @JsonDeserialize - public record Commiter( - String name, - String email - ) { - @JsonCreator - public Commiter { - } - } -} - diff --git a/downloads-api/src/main/resources/application.conf b/downloads-api/src/main/resources/application.conf deleted file mode 100644 index acd17dfa..00000000 --- a/downloads-api/src/main/resources/application.conf +++ /dev/null @@ -1,6 +0,0 @@ -my-app { - routes { - # If ask takes more time than this to complete the request is failed - ask-timeout = 5s - } -} diff --git a/downloads-api/src/main/resources/logback.xml b/downloads-api/src/main/resources/logback.xml deleted file mode 100644 index b1fe9ae9..00000000 --- a/downloads-api/src/main/resources/logback.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - [%date{ISO8601}] [%level] [%logger] [%thread] [%X{akkaSource}] - %msg%n - - - - - 1024 - true - - - - - - - - diff --git a/downloads-api/src/test/java/com/example/UserRoutesTest.java b/downloads-api/src/test/java/com/example/UserRoutesTest.java deleted file mode 100644 index 02896cae..00000000 --- a/downloads-api/src/test/java/com/example/UserRoutesTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.example; - - -//#test-top -import akka.actor.typed.ActorRef; -import akka.http.javadsl.model.*; -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRoute; -import org.junit.*; -import org.junit.runners.MethodSorters; -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.StatusCodes; -import akka.actor.testkit.typed.javadsl.TestKitJunitResource; - - -//#set-up -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class UserRoutesTest extends JUnitRouteTest { - - @ClassRule - public static TestKitJunitResource testkit = new TestKitJunitResource(); - - //#test-top - // shared registry for all tests - private static ActorRef userRegistry; - private TestRoute appRoute; - - @BeforeClass - public static void beforeClass() { - userRegistry = testkit.spawn(UserRegistry.create()); - } - - @Before - public void before() { - UserRoutes userRoutes = new UserRoutes(testkit.system(), userRegistry); - appRoute = testRoute(userRoutes.userRoutes()); - } - - @AfterClass - public static void afterClass() { - testkit.stop(userRegistry); - } - - //#set-up - //#actual-test - @Test - public void test1NoUsers() { - appRoute.run(HttpRequest.GET("/users")) - .assertStatusCode(StatusCodes.OK) - .assertMediaType("application/json") - .assertEntity("{\"users\":[]}"); - } - - //#actual-test - //#testing-post - @Test - public void test2HandlePOST() { - appRoute.run(HttpRequest.POST("/users") - .withEntity(MediaTypes.APPLICATION_JSON.toContentType(), - "{\"name\": \"Kapi\", \"age\": 42, \"countryOfResidence\": \"jp\"}")) - .assertStatusCode(StatusCodes.CREATED) - .assertMediaType("application/json") - .assertEntity("{\"description\":\"User Kapi created.\"}"); - } - //#testing-post - - @Test - public void test3Remove() { - appRoute.run(HttpRequest.DELETE("/users/Kapi")) - .assertStatusCode(StatusCodes.OK) - .assertMediaType("application/json") - .assertEntity("{\"description\":\"User Kapi deleted.\"}"); - - } - //#set-up -} -//#set-up diff --git a/downloads-api/src/test/resources/application-test.conf b/downloads-api/src/test/resources/application-test.conf deleted file mode 100644 index 7f763324..00000000 --- a/downloads-api/src/test/resources/application-test.conf +++ /dev/null @@ -1,3 +0,0 @@ -include "application" - -# default config for tests, we just import the regular conf \ No newline at end of file diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 52b199d6..00000000 --- a/gradle.properties +++ /dev/null @@ -1,7 +0,0 @@ -micronautVersion=3.8.1 -akkaVersion =2.7.0 -scalaVersion=2.13 -akkaManagementVersion=1.2.0 -akkaProjection =1.3.1 -jacksonVersion = 2.14.2 -vavr = 0.10.4 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..839385ba --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,46 @@ +[versions] +micronaut = "4.0.5" +scala = "2.13" +akka = "2.8.3" +jackson = "2.15.1" +maven_artifact = "3.8.5" +akkaManagementVersion = "1.4.1" +akkaProjection = "1.4.2" +akkaR2DBC = "1.1.1" +vavr = "0.10.4" +jakartaValidation = "3.0.2" + +[libraries] +vavr = { module = "io.vavr:vavr", version.ref = "vavr"} +akkaBom = { module = "com.typesafe.akka:akka-bom_2.13", version.ref = "akka" } +akka-actor = { module = "com.typesafe.akka:akka-actor-typed_2.13" } +akka-cluster-sharding = { module = "com.typesafe.akka:akka-cluster-sharding-typed_2.13" } +akka-cluster-typed = { module = "com.typesafe.akka:akka-cluster-typed_2.13" } + +akka-testkit = { module = "com.typesafe.akka:akka-actor-testkit-typed_2.13"} + +akka-persistence = { module ="com.typesafe.akka:akka-persistence-typed_2.13"} +akka-projection = { module = "com.lightbend.akka:akka-projection-r2dbc_2.13", version.ref = "akkaProjection"} +akka-r2dbc = { module = "com.lightbend.akka:akka-persistence-r2dbc_2.13", version.ref = "akkaR2DBC"} +postgres-r2dbc = { module = "org.postgresql:r2dbc-postgresql"} + +akka-discovery = { module = "com.typesafe.akka:akka-discovery_2.13" } +lightbend_management = { module = "com.lightbend.akka.management:akka-management_2.13", version.ref = "akkaManagementVersion"} +lightbend_bootstrap = { module = "com.lightbend.akka.management:akka-management-cluster-bootstrap_2.13", version.ref = "akkaManagementVersion"} + +jacksonBom = { module = "com.fasterxml.jackson:jackson-bom", version.ref = "jackson" } +jackson-core = { module = "com.fasterxml.jackson.core:jackson-core" } +jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations" } +jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind" } +akka-jackson = { module = "com.typesafe.akka:akka-serialization-jackson_2.13"} +maven = { module = "org.apache.maven:maven-artifact", version.ref = "maven_artifact" } + +jakarta-validation = { module = "jakarta.validation:jakarta.validation-api", version.ref = "jakartaValidation"} + + +[bundles] +serder = ["jackson-core", "jackson-annotations", "jackson-databind"] +appSerder = ["jackson-databind", "jackson-annotations", "jackson-core", "akka-jackson"] +actors = ["akka-actor", "akka-cluster-typed", "akka-cluster-sharding"] +actorsPersistence = ["akka-persistence", "akka-projection", "akka-r2dbc", "postgres-r2dbc"] +akkaManagement = ["akka-discovery", "lightbend_bootstrap", "lightbend_management"] diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 249e5832..c1962a79 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 070cb702..37aef8d3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index a69d9cb6..aeb74cbb 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -143,12 +140,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,6 +194,10 @@ if "$cygwin" || "$msys" ; then done fi + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in diff --git a/gradlew.bat b/gradlew.bat index f127cfd4..93e3f59f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% diff --git a/liquibase/changelog/akka/akka_001_init.sql b/liquibase/changelog/akka/akka_001_init.sql new file mode 100644 index 00000000..045c8cbc --- /dev/null +++ b/liquibase/changelog/akka/akka_001_init.sql @@ -0,0 +1,62 @@ +CREATE TABLE IF NOT EXISTS event_journal +( + slice INT NOT NULL, + entity_type VARCHAR(255) NOT NULL, + persistence_id VARCHAR(255) NOT NULL, + seq_nr BIGINT NOT NULL, + db_timestamp timestamp with time zone NOT NULL, + + event_ser_id INTEGER NOT NULL, + event_ser_manifest VARCHAR(255) NOT NULL, + event_payload JSONB NOT NULL, + + deleted BOOLEAN DEFAULT FALSE NOT NULL, + writer VARCHAR(255) NOT NULL, + adapter_manifest VARCHAR(255), + tags TEXT ARRAY, + + meta_ser_id INTEGER, + meta_ser_manifest VARCHAR(255), + meta_payload BYTEA, + + PRIMARY KEY (persistence_id, seq_nr) +); + +-- `event_journal_slice_idx` is only needed if the slice based queries are used +CREATE INDEX IF NOT EXISTS event_journal_slice_idx ON event_journal (slice, entity_type, db_timestamp, seq_nr); + +CREATE TABLE IF NOT EXISTS snapshot +( + slice INT NOT NULL, + entity_type VARCHAR(255) NOT NULL, + persistence_id VARCHAR(255) NOT NULL, + seq_nr BIGINT NOT NULL, + write_timestamp BIGINT NOT NULL, + ser_id INTEGER NOT NULL, + ser_manifest VARCHAR(255) NOT NULL, + snapshot JSONB NOT NULL, + meta_ser_id INTEGER, + meta_ser_manifest VARCHAR(255), + meta_payload BYTEA, + + PRIMARY KEY (persistence_id) +); + +CREATE TABLE IF NOT EXISTS durable_state +( + slice INT NOT NULL, + entity_type VARCHAR(255) NOT NULL, + persistence_id VARCHAR(255) NOT NULL, + revision BIGINT NOT NULL, + db_timestamp timestamp with time zone NOT NULL, + + state_ser_id INTEGER NOT NULL, + state_ser_manifest VARCHAR(255), + state_payload JSONB NOT NULL, + tags TEXT ARRAY, + + PRIMARY KEY (persistence_id, revision) +); + +-- `durable_state_slice_idx` is only needed if the slice based queries are used +CREATE INDEX IF NOT EXISTS durable_state_slice_idx ON durable_state (slice, entity_type, db_timestamp, revision); diff --git a/liquibase/changelog/akka/akka_2_8_2.xml b/liquibase/changelog/akka/akka_2_8_2.xml new file mode 100644 index 00000000..a42d4c88 --- /dev/null +++ b/liquibase/changelog/akka/akka_2_8_2.xml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/liquibase/changelog/changelog.xml b/liquibase/changelog/changelog.xml index 64c2215a..9d5a66c2 100644 --- a/liquibase/changelog/changelog.xml +++ b/liquibase/changelog/changelog.xml @@ -3,5 +3,5 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd"> - + diff --git a/liquibase/changelog/lagom/lagom_1_6.xml b/liquibase/changelog/lagom/lagom_1_6.xml deleted file mode 100644 index 746eef08..00000000 --- a/liquibase/changelog/lagom/lagom_1_6.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/project/build.properties b/project/build.properties deleted file mode 100644 index 3161d214..00000000 --- a/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.1 diff --git a/project/plugins.sbt b/project/plugins.sbt deleted file mode 100644 index a77de1ce..00000000 --- a/project/plugins.sbt +++ /dev/null @@ -1,8 +0,0 @@ -addSbtPlugin("com.lightbend.lagom" % "lagom-sbt-plugin" % "1.6.7") -addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.7.0") -addDependencyTreePlugin -addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") - -resolvers += Resolver.jcenterRepo - -addSbtPlugin("net.aichler" % "sbt-jupiter-interface" % "0.10.0") diff --git a/server-auth/src/main/java/org/spongepowered/downloads/auth/AuthenticatedInternalService.java b/server-auth/src/main/java/org/spongepowered/downloads/auth/AuthenticatedInternalService.java deleted file mode 100644 index c5726e99..00000000 --- a/server-auth/src/main/java/org/spongepowered/downloads/auth/AuthenticatedInternalService.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.auth; - -import com.lightbend.lagom.javadsl.api.ServiceCall; -import com.lightbend.lagom.javadsl.server.HeaderServiceCall; -import com.lightbend.lagom.javadsl.server.ServerServiceCall; -import org.pac4j.core.profile.CommonProfile; -import org.pac4j.lagom.javadsl.SecuredService; -import org.spongepowered.downloads.auth.api.utils.AuthUtils; - -import java.util.function.Function; - -public interface AuthenticatedInternalService extends SecuredService { - - @Override - default ServerServiceCall authorize( - final String clientName, - final String authorizerName, - final Function> serviceCall - ) { - return HeaderServiceCall.compose(requestHeader -> - requestHeader.getHeader(auth().internalHeaderKey()) - .filter(header -> header.equals(auth().internalHeaderSecret())) - .map(verified -> new InternalApplicationProfile()) - .map(serviceCall) - .orElseGet(() -> SecuredService.super.authorize(clientName, authorizerName, serviceCall)) - ); - } - - default ServiceCall authorizeInvoke( - final ServiceCall call - ) { - return call.handleRequestHeader(requestHeader -> requestHeader.withHeader( - this.auth().internalHeaderKey(), - this.auth().internalHeaderSecret() - )); - } - - AuthUtils auth(); -} diff --git a/server-auth/src/main/java/org/spongepowered/downloads/auth/InternalApplicationProfile.java b/server-auth/src/main/java/org/spongepowered/downloads/auth/InternalApplicationProfile.java deleted file mode 100644 index a1691f33..00000000 --- a/server-auth/src/main/java/org/spongepowered/downloads/auth/InternalApplicationProfile.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.auth; - -import org.pac4j.core.profile.CommonProfile; - -public class InternalApplicationProfile extends CommonProfile { - - public InternalApplicationProfile() { - this.setId("InternalApplication"); - } - -} diff --git a/server-auth/src/main/java/org/spongepowered/downloads/auth/SOADAuth.java b/server-auth/src/main/java/org/spongepowered/downloads/auth/SOADAuth.java deleted file mode 100644 index d132b022..00000000 --- a/server-auth/src/main/java/org/spongepowered/downloads/auth/SOADAuth.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.auth; - -/** - * Marker annotation to indicate that the returnable object should be the - * SOAD one. - */ -public @interface SOADAuth { -} diff --git a/server-auth/src/main/resources/reference.conf b/server-auth/src/main/resources/reference.conf deleted file mode 100644 index 6d98104e..00000000 --- a/server-auth/src/main/resources/reference.conf +++ /dev/null @@ -1,16 +0,0 @@ - -systemofadownload.auth.secrets { - # The encryption key must be at least 256 bits, so just populate a really - # big number here. Note that if multiple services are being used, the key - # must be the same across all the services, otherwise the tokens generated - # by the auth service will not be valid for other services. - encryption = "12345678901234567890123456789012" - signature = "12345678901234567890123456789012" - # For the webhook module, the specific secret when available, may well be - # deprecated in the future for a more registration-based webhook solution. - nexus-webhook = "" - # The internal header/secret key combination for inter-service - # communication. These should NOT remain the same in production! - internal-header = "some-header" - internal-secret = "some-secret" -} diff --git a/settings.gradle.kts b/settings.gradle.kts index bb2f00f9..50233302 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,20 +1,8 @@ -pluginManagement { - repositories { - mavenCentral() - gradlePluginPortal() - } - plugins { - id("com.github.johnrengelman.shadow") version "7.1.2" - id("io.micronaut.application") version "3.7.0" - id("io.micronaut.test-resources") version "3.7.0" - } -} -rootProject.name="systemofadownload" +rootProject.name="SystemOfADownload" dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.PREFER_PROJECT) // needed for forge-loom, unfortunately repositories { mavenCentral() maven("https://repo.spongepowered.org/repository/maven-public/") { @@ -24,9 +12,11 @@ dependencyResolutionManagement { } include( + "akka", + "akka:testkit", "artifacts", "artifacts:api", "artifacts:worker", "artifacts:server", - "artifacts:events") -include("akka") + "artifacts:events", + ) diff --git a/sonatype/src/main/java/org/spongepowered/downloads/maven/MavenConstants.java b/sonatype/src/main/java/org/spongepowered/downloads/maven/MavenConstants.java deleted file mode 100644 index 4c8a2bcd..00000000 --- a/sonatype/src/main/java/org/spongepowered/downloads/maven/MavenConstants.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.maven; - -public final class MavenConstants { - public static final String MAVEN_METADATA_FILE = "maven-metadata.xml"; -} diff --git a/sonatype/src/main/java/org/spongepowered/downloads/maven/artifact/ArtifactMavenMetadata.java b/sonatype/src/main/java/org/spongepowered/downloads/maven/artifact/ArtifactMavenMetadata.java deleted file mode 100644 index dd18167c..00000000 --- a/sonatype/src/main/java/org/spongepowered/downloads/maven/artifact/ArtifactMavenMetadata.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.maven.artifact; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import java.util.Objects; - -@JsonDeserialize -@JsonIgnoreProperties(value = "modelVersion", ignoreUnknown = true) -public final class ArtifactMavenMetadata { - private final String groupId; - private final String artifactId; - private final Versioning versioning; - - @JsonCreator - public ArtifactMavenMetadata( - @JsonProperty("groupId") final String groupId, - @JsonProperty("artifactId") final String artifactId, - @JsonProperty("versioning") final Versioning versioning - ) { - this.groupId = groupId; - this.artifactId = artifactId; - this.versioning = versioning; - } - - public String groupId() { - return this.groupId; - } - - public String artifactId() { - return artifactId; - } - - public Versioning versioning() { - return this.versioning; - } - - @Override - public boolean equals(final Object obj) { - if (obj == this) return true; - if (obj == null || obj.getClass() != this.getClass()) return false; - final var that = (ArtifactMavenMetadata) obj; - return Objects.equals(this.groupId, that.groupId) && - Objects.equals(this.versioning, that.versioning); - } - - @Override - public int hashCode() { - return Objects.hash(this.groupId, this.versioning); - } - - @Override - public String toString() { - return "ArtifactMavenMetadata[" + - "groupId=" + this.groupId + ", " + - "versioning=" + this.versioning + ']'; - } - - -} diff --git a/sonatype/src/main/java/org/spongepowered/downloads/maven/artifact/Versioning.java b/sonatype/src/main/java/org/spongepowered/downloads/maven/artifact/Versioning.java deleted file mode 100644 index 3153c06f..00000000 --- a/sonatype/src/main/java/org/spongepowered/downloads/maven/artifact/Versioning.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.maven.artifact; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import io.vavr.collection.List; - -import java.util.Objects; -import java.util.Optional; - -@JsonDeserialize -public final class Versioning { - public final String latest; - public final String release; - public final String lastUpdated; - public final List versions; - - public Versioning() { - this.latest = ""; - this.release = ""; - this.lastUpdated = ""; - this.versions = List.empty(); - } - - @JsonCreator - public Versioning( - @JsonProperty("latest") final String latest, - @JsonProperty("release") final String release, - @JsonProperty("lastUpdated") String lastUpdated, - @JsonProperty("versions") List versions - ) { - this.latest = latest == null ? "" : latest; - this.release = release == null ? "" : release; - this.lastUpdated = lastUpdated; - this.versions = versions; - } - - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (obj == null || obj.getClass() != this.getClass()) { - return false; - } - final var that = (Versioning) obj; - return Objects.equals(this.latest, that.latest) && - Objects.equals(this.release, that.release) && - Objects.equals(this.lastUpdated, that.lastUpdated) && - Objects.equals(this.versions, that.versions); - } - - @Override - public int hashCode() { - return Objects.hash(this.latest, this.release, this.lastUpdated, this.versions); - } - - @Override - public String toString() { - return "Versioning[" + - "latest=" + this.latest + ", " + - "release=" + this.release + ", " + - "lastUpdated=" + this.lastUpdated + ", " + - "versions=" + this.versions+ ']'; - } -} diff --git a/sonatype/src/main/java/org/spongepowered/downloads/maven/snapshot/Snapshot.java b/sonatype/src/main/java/org/spongepowered/downloads/maven/snapshot/Snapshot.java deleted file mode 100644 index 5936ecc7..00000000 --- a/sonatype/src/main/java/org/spongepowered/downloads/maven/snapshot/Snapshot.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.maven.snapshot; - -public class Snapshot { - - public final String timestamp; - public final int buildNumber; - - public Snapshot(final String timestamp, final int buildNumber) { - this.timestamp = timestamp; - this.buildNumber = buildNumber; - } -} diff --git a/sonatype/src/main/java/org/spongepowered/downloads/maven/snapshot/SnapshotAsset.java b/sonatype/src/main/java/org/spongepowered/downloads/maven/snapshot/SnapshotAsset.java deleted file mode 100644 index 299e138c..00000000 --- a/sonatype/src/main/java/org/spongepowered/downloads/maven/snapshot/SnapshotAsset.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.maven.snapshot; - -import java.util.Objects; - -public final class SnapshotAsset { - private final String classifier; - private final String extension; - private final String value; - private final String updated; - - SnapshotAsset(final String classifier, final String extension, final String value, final String updated) { - this.classifier = classifier; - this.extension = extension; - this.value = value; - this.updated = updated; - } - - public String classifier() { - return this.classifier; - } - - public String extension() { - return this.extension; - } - - public String value() { - return this.value; - } - - public String updated() { - return this.updated; - } - - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (obj == null || obj.getClass() != this.getClass()) { - return false; - } - final var that = (SnapshotAsset) obj; - return Objects.equals(this.classifier, that.classifier) && - Objects.equals(this.extension, that.extension) && - Objects.equals(this.value, that.value) && - Objects.equals(this.updated, that.updated); - } - - @Override - public int hashCode() { - return Objects.hash(this.classifier, this.extension, this.value, this.updated); - } - - @Override - public String toString() { - return "SnapshotAsset[" + - "classifier=" + this.classifier + ", " + - "extension=" + this.extension + ", " + - "value=" + this.value + ", " + - "updated=" + this.updated + ']'; - } -} diff --git a/sonatype/src/main/java/org/spongepowered/downloads/maven/snapshot/SnapshotMetadata.java b/sonatype/src/main/java/org/spongepowered/downloads/maven/snapshot/SnapshotMetadata.java deleted file mode 100644 index f78d43f6..00000000 --- a/sonatype/src/main/java/org/spongepowered/downloads/maven/snapshot/SnapshotMetadata.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.maven.snapshot; - -public final class SnapshotMetadata { - public final String groupId; - public final String artifactId; - public final String version; - public final SnapshotVersioning versioning; - - public SnapshotMetadata( - final String groupId, final String artifactId, final String version, - final SnapshotVersioning versioning - ) { - this.groupId = groupId; - this.artifactId = artifactId; - this.version = version; - this.versioning = versioning; - } -} diff --git a/sonatype/src/main/java/org/spongepowered/downloads/maven/snapshot/SnapshotVersioning.java b/sonatype/src/main/java/org/spongepowered/downloads/maven/snapshot/SnapshotVersioning.java deleted file mode 100644 index b5106301..00000000 --- a/sonatype/src/main/java/org/spongepowered/downloads/maven/snapshot/SnapshotVersioning.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.maven.snapshot; - -import io.vavr.collection.List; - -/** - * Represents a snapshot versioned maven metadata xml that SOAD will use to represent - * possible artifacts of snapshots. Note that due to the implicit requirements of - */ -public class SnapshotVersioning { - - public final Snapshot snapshot; - public final String lastUpdated; - public final List snapshotVersions; - - public SnapshotVersioning( - final Snapshot snapshot, final String lastUpdated, - final List snapshotVersions - ) { - this.snapshot = snapshot; - this.lastUpdated = lastUpdated; - this.snapshotVersions = snapshotVersions; - } -} diff --git a/sonatype/src/main/java/org/spongepowered/downloads/sonatype/AssetSearchResponse.java b/sonatype/src/main/java/org/spongepowered/downloads/sonatype/AssetSearchResponse.java deleted file mode 100644 index 5868b14c..00000000 --- a/sonatype/src/main/java/org/spongepowered/downloads/sonatype/AssetSearchResponse.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.sonatype; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import io.vavr.collection.List; - -import java.util.Optional; - -@JsonDeserialize -public record AssetSearchResponse( - @JsonProperty Optional continuationToken, - @JsonProperty(required = true) List items -) { - - @JsonCreator - public AssetSearchResponse { - } -} diff --git a/sonatype/src/main/java/org/spongepowered/downloads/sonatype/Component.java b/sonatype/src/main/java/org/spongepowered/downloads/sonatype/Component.java deleted file mode 100644 index bce10302..00000000 --- a/sonatype/src/main/java/org/spongepowered/downloads/sonatype/Component.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.sonatype; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.vavr.collection.List; - -@JsonDeserialize -@JsonSerialize -public record Component(String id, String repository, String format, - String group, String name, String version, - List assets) { - @JsonCreator - public Component( - @JsonProperty(value = "id", - required = true) final String id, - @JsonProperty(value = "repository", - required = true) final String repository, - @JsonProperty(value = "format", - required = true) final String format, - @JsonProperty(value = "group", - required = true) final String group, - @JsonProperty(value = "name", - required = true) final String name, - @JsonProperty(value = "version", - required = true) final String version, - @JsonProperty(value = "assets", - required = true) final List assets - ) { - this.id = id; - this.repository = repository; - this.format = format; - this.group = group; - this.name = name; - this.version = version; - this.assets = assets; - } - - @JsonDeserialize - public static record Asset( - @JsonProperty(value = "downloadUrl", - required = true) String downloadUrl, - @JsonProperty(value = "path", - required = true) String path, - @JsonProperty(value = "id", - required = true) String id, - @JsonProperty(value = "repository", - required = true) String repository, - @JsonProperty(value = "format", - required = true) String format, - @JsonProperty(value = "checksum", - required = true) Checksum checksum, - @JsonProperty(value = "contentType", - required = true) String contentType, - @JsonProperty(value = "lastModified", - required = true) String lastModified, - @JsonProperty(value = "maven2") Maven2 mavenData - ) { - @JsonCreator - public Asset { - } - } - - public static record Maven2( - @JsonProperty(value = "extension") String extension, - @JsonProperty(value = "groupId") String groupId, - @JsonProperty(value = "classifier") String classifier, - @JsonProperty(value = "artifactId") String artifactId, - @JsonProperty(value = "version") String version - ) { - - } - - @JsonDeserialize - public static record Checksum( - @JsonProperty(value = "sha1") String sha1, - @JsonProperty(value = "sha256") String sha256, - @JsonProperty(value = "sha512") String sha512, - @JsonProperty(value = "md5") String md5 - ) { - @JsonCreator - public Checksum { - } - - } -} diff --git a/sonatype/src/main/java/org/spongepowered/downloads/sonatype/ComponentSearchResponse.java b/sonatype/src/main/java/org/spongepowered/downloads/sonatype/ComponentSearchResponse.java deleted file mode 100644 index 877692f1..00000000 --- a/sonatype/src/main/java/org/spongepowered/downloads/sonatype/ComponentSearchResponse.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.sonatype; - -import io.vavr.collection.List; - -import java.util.Objects; -import java.util.Optional; - -public final class ComponentSearchResponse { - private final List items; - private final Optional continuationToken; - - public ComponentSearchResponse(final List items, final Optional continuationToken) { - this.items = items; - this.continuationToken = continuationToken; - } - - public List items() { - return this.items; - } - - public Optional continuationToken() { - return this.continuationToken; - } - - @Override - public boolean equals(final Object obj) { - if (obj == this) return true; - if (obj == null || obj.getClass() != this.getClass()) return false; - final var that = (ComponentSearchResponse) obj; - return Objects.equals(this.items, that.items) && - Objects.equals(this.continuationToken, that.continuationToken); - } - - @Override - public int hashCode() { - return Objects.hash(this.items, this.continuationToken); - } - - @Override - public String toString() { - return "ComponentSearchResponse[" + - "items=" + this.items + ", " + - "continuationToken=" + this.continuationToken + ']'; - } - - public final static class Item { - private final String id; - private final String repository; - private final String format; - private final String group; - private final String name; - private final String version; - - public Item(final String id, final String repository, final String format, final String group, final String name, final String version) { - this.id = id; - this.repository = repository; - this.format = format; - this.group = group; - this.name = name; - this.version = version; - } - - public String id() { - return this.id; - } - - public String repository() { - return this.repository; - } - - public String format() { - return this.format; - } - - public String group() { - return this.group; - } - - public String name() { - return this.name; - } - - public String version() { - return this.version; - } - - @Override - public boolean equals(final Object obj) { - if (obj == this) return true; - if (obj == null || obj.getClass() != this.getClass()) return false; - final var that = (Item) obj; - return Objects.equals(this.id, that.id) && - Objects.equals(this.repository, that.repository) && - Objects.equals(this.format, that.format) && - Objects.equals(this.group, that.group) && - Objects.equals(this.name, that.name) && - Objects.equals(this.version, that.version); - } - - @Override - public int hashCode() { - return Objects.hash(this.id, this.repository, this.format, this.group, this.name, this.version); - } - - @Override - public String toString() { - return "Item[" + - "id=" + this.id + ", " + - "repository=" + this.repository + ", " + - "format=" + this.format + ", " + - "group=" + this.group + ", " + - "name=" + this.name + ", " + - "version=" + this.version + ']'; - } - } -} diff --git a/sonatype/src/main/java/org/spongepowered/downloads/sonatype/MavenPom.java b/sonatype/src/main/java/org/spongepowered/downloads/sonatype/MavenPom.java deleted file mode 100644 index 6c8a2a27..00000000 --- a/sonatype/src/main/java/org/spongepowered/downloads/sonatype/MavenPom.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.sonatype; - -import java.util.Objects; - -public final class MavenPom { - private final String groupId; - private final String artifactId; - private final String version; - private final String name; - - public MavenPom(final String groupId, final String artifactId, final String version, final String name) { - this.groupId = groupId; - this.artifactId = artifactId; - this.version = version; - this.name = name; - } - - public String groupId() { - return this.groupId; - } - - public String artifactId() { - return this.artifactId; - } - - public String version() { - return this.version; - } - - public String name() { - return this.name; - } - - @Override - public boolean equals(final Object obj) { - if (obj == this) return true; - if (obj == null || obj.getClass() != this.getClass()) return false; - final var that = (MavenPom) obj; - return Objects.equals(this.groupId, that.groupId) && - Objects.equals(this.artifactId, that.artifactId) && - Objects.equals(this.version, that.version) && - Objects.equals(this.name, that.name); - } - - @Override - public int hashCode() { - return Objects.hash(this.groupId, this.artifactId, this.version, this.name); - } - - @Override - public String toString() { - return "MavenPom[" + - "groupId=" + this.groupId + ", " + - "artifactId=" + this.artifactId + ", " + - "version=" + this.version + ", " + - "name=" + this.name + ']'; - } -} diff --git a/sonatype/src/test/java/org/spongepowered/downloads/maven/MavenMetadataTest.java b/sonatype/src/test/java/org/spongepowered/downloads/maven/MavenMetadataTest.java deleted file mode 100644 index 46e9bba3..00000000 --- a/sonatype/src/test/java/org/spongepowered/downloads/maven/MavenMetadataTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.maven; - - -import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import io.vavr.collection.List; -import io.vavr.jackson.datatype.VavrModule; -import org.junit.jupiter.api.Test; -import org.spongepowered.downloads.maven.artifact.ArtifactMavenMetadata; -import org.spongepowered.downloads.maven.artifact.Versioning; - -import java.io.IOException; - -public final class MavenMetadataTest { - - @Test - public void TestMavenMetadataDeserialization() throws IOException { - final var mapper = new XmlMapper(); - mapper.registerModule(new VavrModule()); - final var mavenMetadataFile = getClass().getClassLoader().getResourceAsStream("maven-metadata-example.xml"); - final var artifactMavenMetadata = mapper.readValue(mavenMetadataFile, ArtifactMavenMetadata.class); - // assertions - assert "spongeapi".equals(artifactMavenMetadata.artifactId()); - assert "org.spongepowered".equals(artifactMavenMetadata.groupId()); - final var existingVersions = List.of( - "1.0.0-SNAPSHOT", - "1.0", - "1.1-SNAPSHOT", - "2.0", - "2.1-SNAPSHOT", - "3.0.0", - "3.0.1-SNAPSHOT", - "3.0.1-indev", - "3.1.0-SNAPSHOT", - "3.1.0", - "4.0.0-SNAPSHOT", - "4.0.0", - "4.0.1", - "4.0.2", - "4.0.3", - "4.1.0-SNAPSHOT", - "4.1.0", - "4.2.0-SNAPSHOT", - "5.0.0-SNAPSHOT", - "5.0.0", - "5.1.0-SNAPSHOT", - "5.1.0", - "5.2.0-SNAPSHOT", - "6.0.0-SNAPSHOT", - "6.0.0", - "6.1.0-SNAPSHOT", - "7.0.0-SNAPSHOT", - "7.0.0", - "7.1.0-SNAPSHOT", - "7.1.0", - "7.2.0-SNAPSHOT", - "7.2.0", - "7.3.0-SNAPSHOT", - "7.3.0", - "7.4.0-SNAPSHOT", - "8.0.0-SNAPSHOT", - "9.0.0-SNAPSHOT" - ); - final var expected = new Versioning( - "9.0.0-SNAPSHOT", - "7.3.0", - "20210616221657", - existingVersions - ); - assert expected.equals(artifactMavenMetadata.versioning()); - } -} diff --git a/sonatype/src/test/resources/maven-metadata-example.xml b/sonatype/src/test/resources/maven-metadata-example.xml deleted file mode 100644 index 9d47228b..00000000 --- a/sonatype/src/test/resources/maven-metadata-example.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - org.spongepowered - spongeapi - - 9.0.0-SNAPSHOT - 7.3.0 - - 1.0.0-SNAPSHOT - 1.0 - 1.1-SNAPSHOT - 2.0 - 2.1-SNAPSHOT - 3.0.0 - 3.0.1-SNAPSHOT - 3.0.1-indev - 3.1.0-SNAPSHOT - 3.1.0 - 4.0.0-SNAPSHOT - 4.0.0 - 4.0.1 - 4.0.2 - 4.0.3 - 4.1.0-SNAPSHOT - 4.1.0 - 4.2.0-SNAPSHOT - 5.0.0-SNAPSHOT - 5.0.0 - 5.1.0-SNAPSHOT - 5.1.0 - 5.2.0-SNAPSHOT - 6.0.0-SNAPSHOT - 6.0.0 - 6.1.0-SNAPSHOT - 7.0.0-SNAPSHOT - 7.0.0 - 7.1.0-SNAPSHOT - 7.1.0 - 7.2.0-SNAPSHOT - 7.2.0 - 7.3.0-SNAPSHOT - 7.3.0 - 7.4.0-SNAPSHOT - 8.0.0-SNAPSHOT - 9.0.0-SNAPSHOT - - 20210616221657 - - diff --git a/src/main/java/systemofadownload/AkkaExtension.java b/src/main/java/systemofadownload/AkkaExtension.java deleted file mode 100644 index 25cd72da..00000000 --- a/src/main/java/systemofadownload/AkkaExtension.java +++ /dev/null @@ -1,56 +0,0 @@ -package systemofadownload; - -import akka.actor.typed.ActorRef; -import akka.actor.typed.ActorSystem; -import akka.actor.typed.Scheduler; -import akka.actor.typed.SpawnProtocol; -import akka.actor.typed.javadsl.Adapter; -import akka.actor.typed.javadsl.Behaviors; -import akka.cluster.sharding.typed.ClusterShardingSettings; -import akka.cluster.sharding.typed.ShardingEnvelope; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.cluster.sharding.typed.javadsl.Entity; -import akka.management.cluster.bootstrap.ClusterBootstrap; -import akka.management.javadsl.AkkaManagement; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; -import io.micronaut.context.annotation.Bean; -import io.micronaut.context.annotation.Factory; -import jakarta.inject.Singleton; - -@Factory -public class AkkaExtension { - - - @Bean - public Scheduler systemScheduler() { - return system().scheduler(); - } - - @Bean - public Config akkaConfig() { - return ConfigFactory.load(); - } - - @Bean(preDestroy = "terminate") - public ActorSystem system() { - Config config = akkaConfig(); - return ActorSystem.create( - Behaviors.setup(ctx -> { - akka.actor.ActorSystem unTypedSystem = Adapter.toClassic(ctx.getSystem()); - AkkaManagement.get(unTypedSystem).start(); - ClusterBootstrap.get(unTypedSystem).start(); - return SpawnProtocol.create(); - }), config.getString("some.cluster.name")); - } - - @Bean - public ClusterSharding clusterSharding() { - return ClusterSharding.get(system()); - } - - @Bean - public ActorRef> someShardRegion() { - return clusterSharding().init(Entity.of(null, null)); - } -} diff --git a/src/main/java/systemofadownload/Application.java b/src/main/java/systemofadownload/Application.java deleted file mode 100644 index 6b627497..00000000 --- a/src/main/java/systemofadownload/Application.java +++ /dev/null @@ -1,18 +0,0 @@ -package systemofadownload; - -import io.micronaut.runtime.Micronaut; -import io.swagger.v3.oas.annotations.*; -import io.swagger.v3.oas.annotations.info.*; - -@OpenAPIDefinition( - info = @Info( - title = "systemofadownload", - version = "0.0" - ) -) -public class Application { - - public static void main(String[] args) { - Micronaut.run(Application.class, args); - } -} \ No newline at end of file diff --git a/src/main/java/systemofadownload/SystemofadownloadController.java b/src/main/java/systemofadownload/SystemofadownloadController.java deleted file mode 100644 index a27608d5..00000000 --- a/src/main/java/systemofadownload/SystemofadownloadController.java +++ /dev/null @@ -1,58 +0,0 @@ -package systemofadownload; - -import akka.Done; -import akka.actor.typed.ActorRef; -import akka.actor.typed.Scheduler; -import akka.actor.typed.javadsl.AskPattern; -import akka.cluster.sharding.typed.ShardingEnvelope; -import io.micronaut.http.annotation.*; -import io.micronaut.security.annotation.Secured; -import io.micronaut.security.rules.SecurityRule; -import io.micronaut.serde.annotation.Serdeable; -import jakarta.inject.Inject; -import reactor.core.publisher.Mono; - -import java.time.Duration; -import java.util.List; -import java.util.concurrent.CompletionStage; - -@Controller("/systemofadownload") -public class SystemofadownloadController { - record Command() {} - - private final ActorRef> region; - private final Scheduler scheduler; - - @Inject - public SystemofadownloadController( - ActorRef> region, - Scheduler scheduler - ) { - this.region = region; - this.scheduler = scheduler; - - } - - @Get(uri="/", produces="text/plain") - @Secured(SecurityRule.IS_ANONYMOUS) - public String index() { - return "Hello world"; - } - - public static final Duration TIMEOUT = Duration.ofSeconds(10); - - public void someFireForgetMethod(){ - this.region.tell(new ShardingEnvelope<>("foo", new Command())); - } - - record Foo() {} - public Mono someNeedResponseMethod(){ - CompletionStage willBeResponse = AskPattern.ask( - this.region, - replyTo -> new ShardingEnvelope<>("entityId", new Command()), - TIMEOUT, - scheduler - ); - return Mono.fromCompletionStage(willBeResponse); - } -} diff --git a/src/main/java/systemofadownload/UnauthorizedHandler.java b/src/main/java/systemofadownload/UnauthorizedHandler.java deleted file mode 100644 index f7c73c20..00000000 --- a/src/main/java/systemofadownload/UnauthorizedHandler.java +++ /dev/null @@ -1,30 +0,0 @@ -package systemofadownload; - -import io.micronaut.context.annotation.Replaces; -import io.micronaut.http.HttpRequest; -import io.micronaut.http.MutableHttpResponse; -import io.micronaut.security.authentication.AuthorizationException; -import io.micronaut.security.authentication.DefaultAuthorizationExceptionHandler; -import io.micronaut.serde.annotation.Serdeable; -import jakarta.inject.Singleton; - -import java.util.List; - -@Singleton -@Replaces(DefaultAuthorizationExceptionHandler.class) -public class UnauthorizedHandler extends DefaultAuthorizationExceptionHandler { - - @Override - public MutableHttpResponse handle(final HttpRequest request, final AuthorizationException exception) { - return super.handle(request, exception) - .body(new UnauthorizedError(401, List.of("Unauthorized"))); - } - - - @Serdeable - record UnauthorizedError( - int code, List errors - ) { - - } -} diff --git a/src/main/java/systemofadownload/artifacts/ArtifactController.java b/src/main/java/systemofadownload/artifacts/ArtifactController.java deleted file mode 100644 index 001967ae..00000000 --- a/src/main/java/systemofadownload/artifacts/ArtifactController.java +++ /dev/null @@ -1,33 +0,0 @@ -package systemofadownload.artifacts; - -import io.micronaut.http.HttpResponse; -import io.micronaut.http.annotation.Body; -import io.micronaut.http.annotation.Controller; -import io.micronaut.http.annotation.Get; -import io.micronaut.http.annotation.PathVariable; -import io.micronaut.http.annotation.Post; -import io.micronaut.http.annotation.QueryValue; -import systemofadownload.artifacts.api.query.ArtifactRegistration; -import systemofadownload.artifacts.api.query.GetArtifactsResponse; - -import java.util.concurrent.Flow; - -@Controller("/groups/{groupID}/artifacts") -public class ArtifactController { - - - @Post("/") - public HttpResponse createArtifact( - @PathVariable String groupID, - @Body ArtifactRegistration.RegisterArtifact registration - ) { - return null; - } - - @Get("/{artifactID}") - public Flow.Publisher> getArtifact( - @PathVariable String groupID, - @PathVariable String artifactID) { - return null; - } -} diff --git a/src/main/java/systemofadownload/artifacts/api/Artifact.java b/src/main/java/systemofadownload/artifacts/api/Artifact.java deleted file mode 100644 index 58746d7d..00000000 --- a/src/main/java/systemofadownload/artifacts/api/Artifact.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package systemofadownload.artifacts.api; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import java.net.URI; -import java.util.Optional; - -@JsonSerialize -public final record Artifact( - @JsonProperty Optional classifier, - @JsonProperty URI downloadUrl, - @JsonProperty String md5, - @JsonProperty String sha1, - @JsonProperty String extension -) { - @JsonCreator - public Artifact { - } - -} diff --git a/src/main/java/systemofadownload/artifacts/api/ArtifactCollection.java b/src/main/java/systemofadownload/artifacts/api/ArtifactCollection.java deleted file mode 100644 index 718b7f57..00000000 --- a/src/main/java/systemofadownload/artifacts/api/ArtifactCollection.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package systemofadownload.artifacts.api; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import java.util.List; - -@JsonDeserialize -public final record ArtifactCollection( - @JsonProperty("assets") List components, - @JsonProperty("coordinates") MavenCoordinates coordinates -) { - - @JsonCreator - public ArtifactCollection { - } - -} diff --git a/src/main/java/systemofadownload/artifacts/api/ArtifactCoordinates.java b/src/main/java/systemofadownload/artifacts/api/ArtifactCoordinates.java deleted file mode 100644 index 1cd351fa..00000000 --- a/src/main/java/systemofadownload/artifacts/api/ArtifactCoordinates.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package systemofadownload.artifacts.api; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import java.util.StringJoiner; - -@JsonDeserialize -public record ArtifactCoordinates( - @JsonProperty(required = true) String groupId, - @JsonProperty(required = true) String artifactId -) { - @JsonCreator - public ArtifactCoordinates { - } - - public MavenCoordinates version(String version) { - return MavenCoordinates.parse( - new StringJoiner(":").add(this.groupId()).add(this.artifactId()).add(version).toString()); - } - - public String asMavenString() { - return this.groupId() + ":" + this.artifactId(); - } - - /** - * The group id of an artifact, as defined by the Apache Maven documentation. - * See Maven Coordinates. - */ - public String groupId() { - return groupId; - } - - /** - * The artifact id of an artifact, as defined by the Apache Maven documentation. - * See Maven Coordinates. - */ - public String artifactId() { - return artifactId; - } -} diff --git a/src/main/java/systemofadownload/artifacts/api/ArtifactService.java b/src/main/java/systemofadownload/artifacts/api/ArtifactService.java deleted file mode 100644 index 3f7773c4..00000000 --- a/src/main/java/systemofadownload/artifacts/api/ArtifactService.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package systemofadownload.artifacts.api; - -import akka.NotUsed; -import systemofadownload.artifacts.api.event.ArtifactUpdate; -import systemofadownload.artifacts.api.event.GroupUpdate; -import systemofadownload.artifacts.api.query.ArtifactDetails; -import systemofadownload.artifacts.api.query.ArtifactRegistration; -import systemofadownload.artifacts.api.query.GetArtifactsResponse; -import systemofadownload.artifacts.api.query.GroupRegistration; -import systemofadownload.artifacts.api.query.GroupResponse; -import systemofadownload.artifacts.api.query.GroupsResponse; - -public interface ArtifactService { - - ServiceCall, ArtifactDetails.Response> updateDetails(String groupId, String artifactId); - - ServiceCall getGroup(String groupId); - - ServiceCall getGroups(); - - Topic groupTopic(); - - Topic artifactUpdate(); - - @Override - default Descriptor descriptor() { - return Service.named("artifacts") - .withCalls( - Service.restCall(Method.GET, "/artifacts/groups/:groupId", this::getGroup), - Service.restCall(Method.GET, "/artifacts/groups", this::getGroups), - Service.restCall(Method.POST, "/artifacts/groups", this::registerGroup), - Service.restCall(Method.GET, "/artifacts/groups/:groupId/artifacts", this::getArtifacts), - Service.restCall(Method.POST, "/artifacts/groups/:groupId/artifacts", this::registerArtifacts), - Service.restCall(Method.PATCH, "/artifacts/groups/:groupId/artifacts/:artifactId/update", this::updateDetails) - ) - .withTopics( - Service.topic("group-activity", this::groupTopic) - .withProperty(KafkaProperties.partitionKeyStrategy(), GroupUpdate::groupId), - Service.topic("artifact-details-update", this::artifactUpdate) - .withProperty(KafkaProperties.partitionKeyStrategy(), ArtifactUpdate::partitionKey) - ) - .withAutoAcl(true); - } - -} diff --git a/src/main/java/systemofadownload/artifacts/api/Group.java b/src/main/java/systemofadownload/artifacts/api/Group.java deleted file mode 100644 index d42e7ee5..00000000 --- a/src/main/java/systemofadownload/artifacts/api/Group.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package systemofadownload.artifacts.api; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -@JsonDeserialize -public record Group( - @JsonProperty(required = true) String groupCoordinates, - @JsonProperty(required = true) String name, - @JsonProperty(required = true) String website -) { - - @JsonCreator - public Group { - } - -} diff --git a/src/main/java/systemofadownload/artifacts/api/MavenCoordinates.java b/src/main/java/systemofadownload/artifacts/api/MavenCoordinates.java deleted file mode 100644 index 18ee831b..00000000 --- a/src/main/java/systemofadownload/artifacts/api/MavenCoordinates.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package systemofadownload.artifacts.api; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import org.apache.maven.artifact.versioning.ComparableVersion; - -import java.util.Objects; -import java.util.StringJoiner; -import java.util.regex.Pattern; - -@JsonDeserialize -public final class MavenCoordinates implements Comparable { - - private static final Pattern MAVEN_REGEX = Pattern.compile("^[-\\w.]+$"); - - /** - * The group id of an artifact, as defined by the Apache Maven documentation. - * See Maven Coordinates. - */ - @JsonProperty(required = true) - public final String groupId; - /** - * The artifact id of an artifact, as defined by the Apache Maven documentation. - * See Maven Coordinates. - */ - @JsonProperty(required = true) - public final String artifactId; - /** - * The version of an artifact, as defined by the Apache Maven documentation. This is - * traditionally specified as a Maven repository searchable version string, such as - * {@code 1.0.0-SNAPSHOT}. - * See Maven Coordinates. - */ - @JsonProperty(required = true) - public final String version; - - @JsonIgnore - public final VersionType versionType; - - @JsonIgnore - private final ComparableVersion mavenVersion; - - /** - * Parses a set of maven formatted coordinates as per - * Apache - * Maven's documentation. - * - * @param coordinates The coordinates delimited by `:` - * @return A parsed set of MavenCoordinates - */ - public static MavenCoordinates parse(final String coordinates) { - final var splitCoordinates = coordinates.split(":"); - if (splitCoordinates.length < 3) { - throw new IllegalArgumentException( - "Coordinates are not formatted or delimited by the `:` character or contains fewer than the required size"); - } - final var groupId = splitCoordinates[0]; - if (!MAVEN_REGEX.asMatchPredicate().test(groupId)) { - throw new IllegalArgumentException("GroupId does not conform to regex rules for a maven group id"); - } - final var artifactId = splitCoordinates[1]; - if (!MAVEN_REGEX.asMatchPredicate().test(artifactId)) { - throw new IllegalArgumentException("ArtifactId does not conform to regex rules for a maven artifact id"); - } - final var version = splitCoordinates[2]; - - VersionType.fromVersion(version); // validates the version is going to be valid somewhat - return new MavenCoordinates(groupId, artifactId, version); - } - - public MavenCoordinates(String coordinates) { - final var splitCoordinates = coordinates.split(":"); - if (splitCoordinates.length < 3) { - throw new IllegalArgumentException( - "Coordinates are not formatted or delimited by the `:` character or contains fewer than the required size"); - } - final var groupId = splitCoordinates[0]; - if (!MAVEN_REGEX.asMatchPredicate().test(groupId)) { - throw new IllegalArgumentException("GroupId does not conform to regex rules for a maven group id"); - } - final var artifactId = splitCoordinates[1]; - if (!MAVEN_REGEX.asMatchPredicate().test(artifactId)) { - throw new IllegalArgumentException("ArtifactId does not conform to regex rules for a maven artifact id"); - } - final var version = splitCoordinates[2]; - - VersionType.fromVersion(version); // validates the version is going to be valid somewhat - this.groupId = groupId; - this.artifactId = artifactId; - this.version = version; - this.versionType = VersionType.fromVersion(version); - this.mavenVersion = new ComparableVersion(version); - } - - @JsonCreator - public MavenCoordinates( - @JsonProperty("groupId") final String groupId, - @JsonProperty("artifactId") final String artifactId, - @JsonProperty("version") final String version - ) { - this.groupId = groupId; - this.artifactId = artifactId; - this.version = version; - this.versionType = VersionType.fromVersion(version); - this.mavenVersion = new ComparableVersion(version); - } - - @JsonIgnore - public String asStandardCoordinates() { - return new StringJoiner(":") - .add(this.groupId) - .add(this.artifactId) - .add(this.versionType.asStandardVersionString(this.version)) - .toString(); - } - - @JsonIgnore - public boolean isSnapshot() { - return this.versionType.isSnapshot(); - } - - @JsonIgnore - public ArtifactCoordinates asArtifactCoordinates() { - return new ArtifactCoordinates(this.groupId, this.artifactId); - } - - @Override - public String toString() { - return new StringJoiner(":") - .add(this.groupId) - .add(this.artifactId) - .add(this.version) - .toString(); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || this.getClass() != o.getClass()) { - return false; - } - final MavenCoordinates that = (MavenCoordinates) o; - return Objects.equals(this.groupId, that.groupId) && Objects.equals( - this.artifactId, that.artifactId) && Objects.equals(this.version, that.version); - } - - @Override - public int hashCode() { - return Objects.hash(this.groupId, this.artifactId, this.version); - } - - @Override - public int compareTo(final MavenCoordinates o) { - final var group = this.groupId.compareTo(o.groupId); - if (group != 0) { - return group; - } - final var artifact = this.artifactId.compareTo(o.artifactId); - if (artifact != 0) { - return artifact; - } - return this.mavenVersion.compareTo(o.mavenVersion); - } -} diff --git a/src/main/java/systemofadownload/artifacts/api/VersionType.java b/src/main/java/systemofadownload/artifacts/api/VersionType.java deleted file mode 100644 index fc9c8a5b..00000000 --- a/src/main/java/systemofadownload/artifacts/api/VersionType.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package systemofadownload.artifacts.api; - -import java.util.StringJoiner; -import java.util.regex.Pattern; - -/** - * In conjunction with {@link MavenCoordinates}, can be used to determine the - * version type of the coordinates, and whether - */ -public enum VersionType { - /** - * A timestamp based file snapshot, such as {@code 1.0.0-20210118.163210-1} - * to where it can be interpreted that the {@link #SNAPSHOT snapshot} version - * would be {@code 1.0.0-SNAPSHOT} that happened to build at date time - * {@code January 18th, 2021 at 16h32m10s} and it's the first build. - */ - TIMESTAMP_SNAPSHOT { - @Override - public boolean isSnapshot() { - return true; - } - - @Override - public String asStandardVersionString(final String version) { - final var split = version.split("-"); - final var stringJoiner = new StringJoiner("-"); - for (int i = 0; i < split.length - 2; i++) { - stringJoiner.add(split[i]); - } - - return stringJoiner.add(SNAPSHOT_VERSION).toString(); - } - }, - - /** - * A standard generic snapshot relative version of a release, such as {@code 1.0.0-SNAPSHOT}. - */ - SNAPSHOT { - @Override - public boolean isSnapshot() { - return true; - } - }, - - /** - * A standard release version not abiding by any snapshot guidelines, considered - * final and singular, such as {@code 1.0.0} - */ - RELEASE; - - /* - Simple SNAPSHOT placeholder - */ - private static final String SNAPSHOT_VERSION = "SNAPSHOT"; - - /* - Verifies the pattern that the snapshot version is date.time-build formatted, - enables the pattern match for a timestamped snapshot - */ - private static final Pattern VERSION_FILE_PATTERN = Pattern.compile("^(.*)-(\\d{8}.\\d{6})-(\\d+)$"); - - private static final Pattern TIMESTAMP_TO_REPLACE = Pattern.compile("(\\d{8}.\\d{6})-(\\d+)$"); - - public static VersionType fromVersion(final String version) { - if (version == null || version.isEmpty()) { - throw new IllegalArgumentException("Version cannot be empty"); - } - // Simple check to find out if the version ends with SNAPSHOT. - if (version.regionMatches( - true, - version.length() - SNAPSHOT_VERSION.length(), - SNAPSHOT_VERSION, - 0, - SNAPSHOT_VERSION.length() - )) { - return SNAPSHOT; - } - if (VERSION_FILE_PATTERN.matcher(version).matches()) { - return TIMESTAMP_SNAPSHOT; - } - return RELEASE; - } - - public boolean isSnapshot() { - return false; - } - - public String asStandardVersionString(final String version) { - return version; - } -} diff --git a/src/main/java/systemofadownload/artifacts/api/event/ArtifactUpdate.java b/src/main/java/systemofadownload/artifacts/api/event/ArtifactUpdate.java deleted file mode 100644 index 7a88ccb6..00000000 --- a/src/main/java/systemofadownload/artifacts/api/event/ArtifactUpdate.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package systemofadownload.artifacts.api.event; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import systemofadownload.artifacts.api.ArtifactCoordinates; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(ArtifactUpdate.ArtifactRegistered.class), - @JsonSubTypes.Type(ArtifactUpdate.GitRepositoryAssociated.class), - @JsonSubTypes.Type(ArtifactUpdate.WebsiteUpdated.class), - @JsonSubTypes.Type(ArtifactUpdate.IssuesUpdated.class), - @JsonSubTypes.Type(ArtifactUpdate.DisplayNameUpdated.class), -}) -public interface ArtifactUpdate extends Jsonable { - - ArtifactCoordinates coordinates(); - - default String partitionKey() { - return this.coordinates().asMavenString(); - } - - @JsonTypeName("registered") - @JsonDeserialize - final record ArtifactRegistered( - ArtifactCoordinates coordinates - ) implements ArtifactUpdate { - - @JsonCreator - public ArtifactRegistered { - } - } - - @JsonTypeName("git-repository") - @JsonDeserialize - final record GitRepositoryAssociated( - ArtifactCoordinates coordinates, - String repository - ) implements ArtifactUpdate { - - @JsonCreator - public GitRepositoryAssociated { - } - } - - @JsonTypeName("website") - @JsonDeserialize - final record WebsiteUpdated( - ArtifactCoordinates coordinates, - String url - ) implements ArtifactUpdate { - - @JsonCreator - public WebsiteUpdated { - } - } - - @JsonTypeName("issues") - @JsonDeserialize - final record IssuesUpdated( - ArtifactCoordinates coordinates, - String url - ) implements ArtifactUpdate { - - @JsonCreator - public IssuesUpdated { - } - } - - @JsonTypeName("displayName") - @JsonDeserialize - final record DisplayNameUpdated( - ArtifactCoordinates coordinates, - String displayName - ) implements ArtifactUpdate { - - @JsonCreator - public DisplayNameUpdated { - } - } - -} diff --git a/src/main/java/systemofadownload/artifacts/api/event/GroupUpdate.java b/src/main/java/systemofadownload/artifacts/api/event/GroupUpdate.java deleted file mode 100644 index 06267ebd..00000000 --- a/src/main/java/systemofadownload/artifacts/api/event/GroupUpdate.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package systemofadownload.artifacts.api.event; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import systemofadownload.artifacts.api.ArtifactCoordinates; - -import java.io.Serial; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(GroupUpdate.GroupRegistered.class), - @JsonSubTypes.Type(GroupUpdate.ArtifactRegistered.class), -}) -public interface GroupUpdate extends Jsonable { - - String groupId(); - - @JsonTypeName("group-registered") - @JsonDeserialize - record GroupRegistered(String groupId, String name, String website) - implements GroupUpdate { - - @JsonCreator - public GroupRegistered { - } - - } - - @JsonTypeName("artifact-registered") - @JsonDeserialize - final record ArtifactRegistered(ArtifactCoordinates coordinates) implements GroupUpdate { - - @Serial private static final long serialVersionUID = 6319289932327553919L; - - @JsonCreator - public ArtifactRegistered { - } - - - @Override - public String groupId() { - return this.coordinates.groupId(); - } - } - -} diff --git a/src/main/java/systemofadownload/artifacts/api/query/ArtifactDetails.java b/src/main/java/systemofadownload/artifacts/api/query/ArtifactDetails.java deleted file mode 100644 index 92aa234f..00000000 --- a/src/main/java/systemofadownload/artifacts/api/query/ArtifactDetails.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package systemofadownload.artifacts.api.query; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.lightbend.lagom.javadsl.api.transport.BadRequest; -import io.vavr.control.Either; -import io.vavr.control.Try; - -import java.net.URL; - -public final class ArtifactDetails { - - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, - property = "type") - @JsonSubTypes({ - @JsonSubTypes.Type(value = Update.Website.class, - name = "website"), - @JsonSubTypes.Type(value = Update.DisplayName.class, - name = "displayName"), - @JsonSubTypes.Type(value = Update.Issues.class, - name = "issues"), - @JsonSubTypes.Type(value = Update.GitRepository.class, - name = "gitRepository"), - }) - @JsonDeserialize - public sealed interface Update { - - Either validate(); - - record Website( - @JsonProperty(required = true) String website - ) implements Update { - - @JsonCreator - public Website { - } - - @Override - public Either validate() { - return Try.of(() -> new URL(this.website())) - .toEither(() -> new BadRequest(String.format("Malformed url: %s", this.website()))); - } - } - - record DisplayName( - @JsonProperty(required = true) String display - ) implements Update { - - @JsonCreator - public DisplayName { - } - - @Override - public Either validate() { - return Either.right(this.display.trim()); - } - } - - record Issues( - @JsonProperty(required = true) String issues - ) implements Update { - @JsonCreator - public Issues { - } - - @Override - public Either validate() { - return Try.of(() -> new URL(this.issues())) - .toEither(() -> new BadRequest(String.format("Malformed url: %s", this.issues()))); - } - } - - record GitRepository( - @JsonProperty(required = true) String gitRepo - ) implements Update { - - @JsonCreator - public GitRepository { - } - - @Override - public Either validate() { - return Try.of(() -> new URL(this.gitRepo())) - .toEither(() -> new BadRequest(String.format("Malformed url: %s", this.gitRepo()))); - } - } - } - - @JsonSerialize - public record Response( - String name, - String displayName, - String website, - String issues, - String gitRepo - ) { - - } - - -} diff --git a/src/main/java/systemofadownload/artifacts/api/query/ArtifactRegistration.java b/src/main/java/systemofadownload/artifacts/api/query/ArtifactRegistration.java deleted file mode 100644 index b125349d..00000000 --- a/src/main/java/systemofadownload/artifacts/api/query/ArtifactRegistration.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package systemofadownload.artifacts.api.query; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import systemofadownload.artifacts.api.ArtifactCoordinates; - -public final class ArtifactRegistration { - - @JsonSerialize - public record RegisterArtifact( - @JsonProperty(required = true) String artifactId, - @JsonProperty(required = true) String displayName - ) { - - @JsonCreator - public RegisterArtifact(final String artifactId, final String displayName) { - this.artifactId = artifactId; - this.displayName = displayName; - } - - } - - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, - property = "type") - @JsonSubTypes({ - @JsonSubTypes.Type(value = Response.GroupMissing.class, - name = "UnknownGroup"), - @JsonSubTypes.Type(value = Response.ArtifactRegistered.class, - name = "RegisteredArtifact"), - @JsonSubTypes.Type(value = Response.ArtifactAlreadyRegistered.class, - name = "AlreadyRegistered"), - }) - public sealed interface Response { - - @JsonSerialize - record ArtifactRegistered(@JsonProperty ArtifactCoordinates coordinates) implements Response { - - } - - @JsonSerialize - record ArtifactAlreadyRegistered( - @JsonProperty String artifactName, - @JsonProperty String groupId - ) implements Response { - - } - - @JsonSerialize - record GroupMissing(@JsonProperty("groupId") String s) implements Response { - - } - - } -} diff --git a/src/main/java/systemofadownload/artifacts/api/query/GetArtifactsResponse.java b/src/main/java/systemofadownload/artifacts/api/query/GetArtifactsResponse.java deleted file mode 100644 index 13c33681..00000000 --- a/src/main/java/systemofadownload/artifacts/api/query/GetArtifactsResponse.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package systemofadownload.artifacts.api.query; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import java.util.List; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = GetArtifactsResponse.GroupMissing.class, name = "UnknownGroup"), - @JsonSubTypes.Type(value = GetArtifactsResponse.ArtifactsAvailable.class, name = "Artifacts"), -}) -public sealed interface GetArtifactsResponse { - - @JsonSerialize - record GroupMissing(@JsonProperty String groupRequested) implements GetArtifactsResponse { - - @JsonCreator - public GroupMissing { - } - - } - - @JsonSerialize - record ArtifactsAvailable(@JsonProperty List artifactIds) - implements GetArtifactsResponse { - - @JsonCreator - public ArtifactsAvailable { - } - - } -} diff --git a/src/main/java/systemofadownload/artifacts/api/query/GroupRegistration.java b/src/main/java/systemofadownload/artifacts/api/query/GroupRegistration.java deleted file mode 100644 index 61e38c2d..00000000 --- a/src/main/java/systemofadownload/artifacts/api/query/GroupRegistration.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package systemofadownload.artifacts.api.query; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import systemofadownload.artifacts.api.Group; - -public final class GroupRegistration { - - @JsonDeserialize - public record RegisterGroupRequest( - @JsonProperty(required = true) String name, - @JsonProperty(required = true) String groupCoordinates, - @JsonProperty(required = true) String website - ) { - - @JsonCreator - public RegisterGroupRequest { } - - } - - public interface Response { - - record GroupAlreadyRegistered(String groupNameRequested) implements Response { - } - - record GroupRegistered(Group group) implements Response { - - } - } -} diff --git a/src/main/java/systemofadownload/artifacts/api/query/GroupResponse.java b/src/main/java/systemofadownload/artifacts/api/query/GroupResponse.java deleted file mode 100644 index ca32acef..00000000 --- a/src/main/java/systemofadownload/artifacts/api/query/GroupResponse.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package systemofadownload.artifacts.api.query; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.lightbend.lagom.serialization.Jsonable; -import systemofadownload.artifacts.api.Group; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = GroupResponse.Missing.class, name = "MissingGroup"), - @JsonSubTypes.Type(value = GroupResponse.Available.class, name = "Group") -}) -public sealed interface GroupResponse extends Jsonable { - - @JsonSerialize - record Missing(@JsonProperty String groupId) implements GroupResponse { - @JsonCreator - public Missing(final String groupId) { - this.groupId = groupId; - } - - } - - @JsonSerialize - record Available(@JsonProperty Group group) implements GroupResponse { - - @JsonCreator - public Available(final Group group) { - this.group = group; - } - - } - -} diff --git a/src/main/java/systemofadownload/artifacts/api/query/GroupsResponse.java b/src/main/java/systemofadownload/artifacts/api/query/GroupsResponse.java deleted file mode 100644 index 5a0dd0ce..00000000 --- a/src/main/java/systemofadownload/artifacts/api/query/GroupsResponse.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package systemofadownload.artifacts.api.query; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.vavr.collection.List; -import systemofadownload.artifacts.api.Group; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = GroupsResponse.Available.class, name = "Groups") -}) -public interface GroupsResponse { - - @JsonSerialize - record Available(@JsonProperty List groups) - implements GroupsResponse { - @JsonCreator - public Available { - } - } -} diff --git a/src/main/java/systemofadownload/artifacts/query/ArtifactQueryController.java b/src/main/java/systemofadownload/artifacts/query/ArtifactQueryController.java deleted file mode 100644 index 0e4b9992..00000000 --- a/src/main/java/systemofadownload/artifacts/query/ArtifactQueryController.java +++ /dev/null @@ -1,23 +0,0 @@ -package systemofadownload.artifacts.query; - -import io.micronaut.http.HttpResponse; -import io.micronaut.http.annotation.Controller; -import io.micronaut.http.annotation.Get; -import io.micronaut.http.annotation.PathVariable; -import io.micronaut.http.annotation.Post; -import systemofadownload.artifacts.api.query.ArtifactRegistration; -import systemofadownload.artifacts.api.query.GetArtifactsResponse; - -import java.util.concurrent.Flow; - -@Controller("/groups/{groupID}/artifacts") -public class ArtifactQueryController { - - - @Get("/") - public Flow.Publisher> getArtifacts( - @PathVariable String groupID - ) { - return null; - } -} diff --git a/src/main/java/systemofadownload/groups/GroupController.java b/src/main/java/systemofadownload/groups/GroupController.java deleted file mode 100644 index ddcbadd2..00000000 --- a/src/main/java/systemofadownload/groups/GroupController.java +++ /dev/null @@ -1,41 +0,0 @@ -package systemofadownload.groups; - -import akka.actor.typed.ActorSystem; -import akka.actor.typed.SpawnProtocol; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.stream.javadsl.AsPublisher; -import akka.stream.javadsl.JavaFlowSupport; -import akka.stream.javadsl.Source; -import akka.stream.typed.javadsl.ActorFlow; -import akka.stream.typed.javadsl.ActorSink; -import io.micronaut.context.annotation.Bean; -import io.micronaut.http.HttpResponse; -import io.micronaut.http.annotation.Body; -import io.micronaut.http.annotation.Controller; -import io.micronaut.http.annotation.Get; -import io.micronaut.http.annotation.PathVariable; -import io.micronaut.http.annotation.Post; -import jakarta.inject.Inject; -import systemofadownload.artifacts.api.query.GetArtifactsResponse; -import systemofadownload.artifacts.api.query.GroupRegistration; - -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.Flow; - -@Controller("/groups") -public class GroupController { - - @Inject - private ActorSystem system; - @Inject - private ClusterSharding sharding; - - @Post("/") - public HttpResponse registerGroup( - @Body GroupRegistration.RegisterGroupRequest req - ) { - return null; - } - -} diff --git a/src/main/java/systemofadownload/groups/query/GroupsQueryController.java b/src/main/java/systemofadownload/groups/query/GroupsQueryController.java deleted file mode 100644 index 3e40b5ab..00000000 --- a/src/main/java/systemofadownload/groups/query/GroupsQueryController.java +++ /dev/null @@ -1,32 +0,0 @@ -package systemofadownload.groups.query; - -import akka.actor.typed.ActorSystem; -import akka.stream.javadsl.AsPublisher; -import akka.stream.javadsl.JavaFlowSupport; -import akka.stream.javadsl.Source; -import io.micronaut.http.HttpResponse; -import io.micronaut.http.annotation.Controller; -import io.micronaut.http.annotation.Get; -import io.micronaut.http.annotation.PathVariable; -import jakarta.inject.Inject; -import systemofadownload.artifacts.api.query.GetArtifactsResponse; - -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.Flow; - -@Controller("/groups") -public class GroupsQueryController { - - @Inject - private ActorSystem system; - - @Get(uri = "/{groupID}") - public Flow.Publisher> g(@PathVariable String groupID) { - return Source.from(Arrays.asList("", "b")) - .via(akka.stream.javadsl.Flow.>fromFunction( - s -> HttpResponse.ok(new GetArtifactsResponse.ArtifactsAvailable(List.of(s))) - )) - .runWith(JavaFlowSupport.Sink.asPublisher(AsPublisher.WITH_FANOUT), this.system); - } -} diff --git a/src/main/resources/application.toml b/src/main/resources/application.toml deleted file mode 100644 index 4c3e2b17..00000000 --- a/src/main/resources/application.toml +++ /dev/null @@ -1,20 +0,0 @@ -micronaut.application.name = 'systemofadownload' -liquibase.datasources.default.change-log = 'classpath:db/liquibase-changelog.xml' -jpa.default.reactive = false -netty.default.allocator.max-order = 3 - -[r2dbc.datasources.default] -url = 'r2dbc:postgresql://localhost:5432/postgres' -username = 'postgres' -password = '' -dialect = 'POSTGRES' - -[micronaut.router.static-resources.swagger] -paths = 'classpath:META-INF/swagger' -mapping = '/swagger/**' - -[micronaut.router.static-resources.swagger-ui] -paths = 'classpath:META-INF/swagger/views/swagger-ui' -mapping = '/swagger-ui/**' - -micronaut.security.enabled=false diff --git a/src/main/resources/db/changelog/01-schema.xml b/src/main/resources/db/changelog/01-schema.xml deleted file mode 100644 index 7e9ab55b..00000000 --- a/src/main/resources/db/changelog/01-schema.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/main/resources/db/liquibase-changelog.xml b/src/main/resources/db/liquibase-changelog.xml deleted file mode 100644 index 468b33ab..00000000 --- a/src/main/resources/db/liquibase-changelog.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml deleted file mode 100644 index 6010eb52..00000000 --- a/src/main/resources/logback.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - true - - - %cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n - - - - - - - diff --git a/src/test/java/systemofadownload/SystemofadownloadTest.java b/src/test/java/systemofadownload/SystemofadownloadTest.java deleted file mode 100644 index 15fe3114..00000000 --- a/src/test/java/systemofadownload/SystemofadownloadTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package systemofadownload; - -import io.micronaut.runtime.EmbeddedApplication; -import io.micronaut.test.extensions.junit5.annotation.MicronautTest; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Assertions; - -import jakarta.inject.Inject; - -@MicronautTest(transactional = false) -class SystemofadownloadTest { - - @Inject - EmbeddedApplication application; - - @Test - void testItWorks() { - Assertions.assertTrue(application.isRunning()); - } - -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/SonatypeSynchronizer.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/SonatypeSynchronizer.java deleted file mode 100644 index 41bd9fec..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/SonatypeSynchronizer.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer; - -import akka.actor.typed.Behavior; -import akka.actor.typed.SupervisorStrategy; -import akka.actor.typed.javadsl.Behaviors; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.cluster.sharding.typed.javadsl.Entity; -import akka.cluster.typed.ClusterSingleton; -import akka.cluster.typed.SingletonActor; -import akka.persistence.typed.PersistenceId; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.spongepowered.downloads.artifact.api.ArtifactService; -import org.spongepowered.downloads.versions.api.VersionsService; -import org.spongepowered.synchronizer.actor.CommitRegistrar; -import org.spongepowered.synchronizer.assetsync.VersionConsumer; -import org.spongepowered.synchronizer.gitmanaged.ArtifactSubscriber; -import org.spongepowered.synchronizer.gitmanaged.CommitConsumer; -import org.spongepowered.synchronizer.gitmanaged.ScheduledCommitResolver; -import org.spongepowered.synchronizer.gitmanaged.domain.GitManagedArtifact; -import org.spongepowered.synchronizer.resync.ResyncManager; -import org.spongepowered.synchronizer.resync.domain.ArtifactSynchronizerAggregate; -import org.spongepowered.synchronizer.versionsync.ArtifactConsumer; - -public final class SonatypeSynchronizer { - - public interface Command { - } - - public static Behavior create( - final ArtifactService artifactService, - final VersionsService versionsService, - final ClusterSharding clusterSharding, - final ObjectMapper mapper - ) { - return Behaviors.setup(context -> { - // don't do this, eventually we can swap this out from service layers - context.spawnAnonymous(Behaviors.supervise(CommitRegistrar.register(versionsService)) - .onFailure(SupervisorStrategy.restart())); - CommitConsumer.setupSubscribers(versionsService, context); - ArtifactSubscriber.setup(artifactService, context); - ScheduledCommitResolver.setup(artifactService, context); - - final var settings = SynchronizationExtension.SettingsProvider.get(context.getSystem()); - clusterSharding - .init( - Entity.of( - ArtifactSynchronizerAggregate.ENTITY_TYPE_KEY, - ArtifactSynchronizerAggregate::create - ) - ); - clusterSharding.init(Entity.of(GitManagedArtifact.ENTITY_TYPE_KEY, ctx -> GitManagedArtifact.create(PersistenceId.of(ctx.getEntityTypeKey().name(), ctx.getEntityId()), ctx.getEntityId()))); - - ArtifactConsumer.subscribeToArtifactUpdates( - context, artifactService, versionsService, clusterSharding, settings); - - VersionConsumer.subscribeToVersionedArtifactUpdates(versionsService, mapper, context, settings); - - final var resyncManager = ResyncManager.create(artifactService, settings.versionSync); - final var resyncBehavior = Behaviors.supervise(resyncManager) - .onFailure( - SupervisorStrategy.restart()); - final var actor = SingletonActor.of(resyncBehavior, "artifact-sync"); - ClusterSingleton.get(context.getSystem()).init(actor); - - // Scheduled full resynchronization with maven and therefor sonatype - return Behaviors.receive(SonatypeSynchronizer.Command.class) - .build(); - }); - } - -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/SynchronizationExtension.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/SynchronizationExtension.java deleted file mode 100644 index d22e5ee6..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/SynchronizationExtension.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer; - -import akka.actor.AbstractExtensionId; -import akka.actor.ExtendedActorSystem; -import akka.actor.Extension; -import akka.actor.ExtensionId; -import akka.actor.ExtensionIdProvider; - -public class SynchronizationExtension extends AbstractExtensionId implements ExtensionIdProvider { - - public static final SynchronizationExtension SettingsProvider = new SynchronizationExtension(); - - @Override - public SynchronizerSettings createExtension(final ExtendedActorSystem system) { - return new SynchronizerSettings(system.settings().config().getConfig("systemofadownload.synchronizer")); - } - - - @Override - public ExtensionId lookup() { - return SettingsProvider; - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/SynchronizerModule.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/SynchronizerModule.java deleted file mode 100644 index f840b72e..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/SynchronizerModule.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer; - -import akka.actor.ActorSystem; -import akka.actor.typed.ActorRef; -import akka.actor.typed.javadsl.Adapter; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.inject.AbstractModule; -import com.google.inject.Inject; -import com.google.inject.Provider; -import com.google.inject.Provides; -import com.google.inject.TypeLiteral; -import com.lightbend.lagom.javadsl.api.ServiceInfo; -import com.lightbend.lagom.javadsl.server.ServiceGuiceSupport; -import org.pac4j.core.config.Config; -import org.spongepowered.downloads.artifact.api.ArtifactService; -import org.spongepowered.downloads.auth.SOADAuth; -import org.spongepowered.downloads.auth.api.utils.AuthUtils; -import org.spongepowered.downloads.versions.api.VersionsService; -import play.Environment; -import play.api.libs.concurrent.AkkaGuiceSupport; - -public class SynchronizerModule extends AbstractModule implements ServiceGuiceSupport, AkkaGuiceSupport { - - private final AuthUtils auth; - - @Inject - public SynchronizerModule(final Environment environment, final com.typesafe.config.Config config) { - this.auth = AuthUtils.configure(config); - } - - @Override - protected void configure() { - this.bindClient(VersionsService.class); - this.bindClient(ArtifactService.class); - this.bindServiceInfo(ServiceInfo.of("Sonatype-Synchronizer")); - this.bind(new TypeLiteral>() { - }) - .toProvider(SynchronizerProvider.class) - .asEagerSingleton(); - } - - @Provides - @SOADAuth - protected Config configProvider() { - return this.auth.config(); - } - - public record SynchronizerProvider( - ArtifactService artifactService, - VersionsService versionsService, - ClusterSharding clusterSharding, - ObjectMapper mapper, - ActorSystem system - ) implements Provider> { - - @Inject - public SynchronizerProvider { - } - - @Override - public ActorRef get() { - return Adapter.spawn(this.system, SonatypeSynchronizer.create( - this.artifactService, - this.versionsService, - this.clusterSharding, - this.mapper - ), "Synchronizer"); - } - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/SynchronizerSettings.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/SynchronizerSettings.java deleted file mode 100644 index 660363f0..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/SynchronizerSettings.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer; - -import akka.actor.Extension; -import akka.actor.typed.BackoffSupervisorStrategy; -import akka.actor.typed.SupervisorStrategy; -import com.typesafe.config.Config; - -import java.time.Duration; -import java.util.concurrent.TimeUnit; - -public class SynchronizerSettings implements Extension { - - public final ReactiveSync reactiveSync; - public final Asset asset; - public final VersionSync versionSync; - - public static final class Asset { - public final int poolSize; - public final int parallelism; - public final Duration initialBackoff; - public final Duration maximumBackoff; - public final double backoffFactor; - public final Duration timeout; - - public final BackoffSupervisorStrategy backoff; - - Asset(Config config) { - this.poolSize = config.getInt("pool-size"); - this.initialBackoff = Duration.ofSeconds(config.getDuration("initial-backoff", TimeUnit.SECONDS)); - this.maximumBackoff = Duration.ofSeconds(config.getDuration("maximum-backoff", TimeUnit.SECONDS)); - this.backoffFactor = config.getDouble("backoff-factor"); - this.backoff = SupervisorStrategy.restartWithBackoff( - this.initialBackoff, this.maximumBackoff, this.backoffFactor); - this.parallelism = config.getInt("parallelism"); - this.timeout = Duration.ofMinutes(config.getDuration("time-out", TimeUnit.MINUTES)); - } - } - - public static final class VersionSync { - public final int versionSyncPoolSize; - public final Duration interval; - public final Duration startupDelay; - - VersionSync(Config config) { - this.versionSyncPoolSize = config.getInt("pool-size"); - this.interval = Duration.ofSeconds(config.getDuration("interval", TimeUnit.SECONDS)); - this.startupDelay = Duration.ofSeconds(config.getDuration("delay", TimeUnit.SECONDS)); - } - } - - public static final class ReactiveSync { - public final int poolSize; - public final int parallelism; - public final Duration timeOut; - - ReactiveSync(Config config) { - this.poolSize = config.getInt("pool-size"); - this.parallelism = config.getInt("parallelism"); - this.timeOut = Duration.ofMinutes(config.getDuration("time-out", TimeUnit.MINUTES)); - } - } - - public SynchronizerSettings(Config config) { - final var assetConfig = config.getConfig("asset"); - this.asset = new Asset(assetConfig); - - final var versionSync = config.getConfig("version-sync"); - this.versionSync = new VersionSync(versionSync); - - final var reactiveSync = config.getConfig("reactive-sync"); - this.reactiveSync = new ReactiveSync(reactiveSync); - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/actor/ArtifactSyncExtension.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/actor/ArtifactSyncExtension.java deleted file mode 100644 index 3e4f69c5..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/actor/ArtifactSyncExtension.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.actor; - -import akka.actor.AbstractExtensionId; -import akka.actor.ExtendedActorSystem; -import akka.actor.Extension; -import akka.actor.ExtensionId; -import akka.actor.ExtensionIdProvider; -import com.typesafe.config.Config; - -import java.time.Duration; -import java.util.StringJoiner; -import java.util.concurrent.TimeUnit; - -public class ArtifactSyncExtension extends AbstractExtensionId - implements ExtensionIdProvider { - - public static final ArtifactSyncExtension SettingsProvider = new ArtifactSyncExtension(); - - @Override - public Settings createExtension(final ExtendedActorSystem system) { - return new Settings( - system.settings().config().getConfig("systemofadownload.synchronizer.worker.version-registration")); - } - - - @Override - public ExtensionId lookup() { - return SettingsProvider; - } - - public static final class Settings implements Extension { - - public final int poolSize; - public final int versionFanoutParallelism; - public final int parallelism; - public final Duration timeOut; - public final Duration individualTimeOut; - - public Settings(Config config) { - this.poolSize = config.getInt("pool-size"); - this.versionFanoutParallelism = config.getInt("fan-out-parallelism"); - this.parallelism = config.getInt("parallelism"); - this.timeOut = Duration.ofSeconds(config.getDuration("time-out", TimeUnit.SECONDS)); - this.individualTimeOut = Duration.ofSeconds(config.getDuration("registration-time-out", TimeUnit.SECONDS)); - - } - - @Override - public String toString() { - return new StringJoiner( - ", ", Settings.class.getSimpleName() + "[", "]") - .add("poolSize=" + poolSize) - .add("versionFanoutParallelism=" + versionFanoutParallelism) - .add("parallelism=" + parallelism) - .add("timeOut=" + timeOut) - .add("individualTimeOut=" + individualTimeOut) - .toString(); - } - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/actor/ArtifactSyncWorker.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/actor/ArtifactSyncWorker.java deleted file mode 100644 index adfc6f72..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/actor/ArtifactSyncWorker.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.actor; - -import akka.Done; -import akka.actor.typed.ActorRef; -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.Behaviors; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import io.vavr.collection.List; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.synchronizer.resync.domain.ArtifactSynchronizerAggregate; -import org.spongepowered.synchronizer.versionsync.ArtifactVersionSyncEntity; -import org.spongepowered.synchronizer.versionsync.SyncRegistration; - -import java.time.Duration; - -public final class ArtifactSyncWorker { - - public interface Command { - } - - /** - * Starts the request that the specific artifact described by the - * {@link ArtifactCoordinates coordinates} are synchronized, versioned, - * and other side effects. A {@link Done} response is always given, - * regardless of outcome, since the sync performs multiple jobs in the - * background. - */ - public record PerformResync(ArtifactCoordinates coordinates, ActorRef replyTo) - implements Command { - } - - /** - * For use with just getting a {@link Done} reply back, some messages can be ignored. - */ - public record Ignored(ActorRef replyTo) implements Command { - } - - private record Failed(ArtifactCoordinates coordinates, ActorRef replyTo) implements Command { - } - - /** - * A post-reqeusted result signifying that the initial request for an - * artifact's versions to sync has been completed. - */ - private record WrappedResult(ActorRef replyTo) implements Command { - } - - public static Behavior create( - final ClusterSharding clusterSharding - ) { - return Behaviors.setup(ctx -> { - final ArtifactSyncExtension.Settings settings = ArtifactSyncExtension.SettingsProvider.get(ctx.getSystem()); - return awaiting(clusterSharding, settings); - }); - } - - private static Behavior awaiting( - final ClusterSharding clusterSharding, - final ArtifactSyncExtension.Settings settings - ) { - return Behaviors.setup(ctx -> Behaviors.receive(Command.class) - .onMessage(PerformResync.class, msg -> { - ctx.getLog().debug("Running Sync"); - final var globalResyncRef = clusterSharding.entityRefFor( - ArtifactSynchronizerAggregate.ENTITY_TYPE_KEY, - msg.coordinates.asMavenString() - ); - final var versionSyncRef = clusterSharding.entityRefFor( - ArtifactVersionSyncEntity.ENTITY_TYPE_KEY, - msg.coordinates.asMavenString() - ); - ctx.pipeToSelf( - globalResyncRef - .>ask( - replyTo -> new org.spongepowered.synchronizer.resync.domain.Command.Resync(msg.coordinates, replyTo), settings.individualTimeOut) - .thenCompose(response -> - versionSyncRef - .ask( - replyTo -> new SyncRegistration.SyncBatch(msg.coordinates, response, replyTo), - Duration.ofMinutes(10) - ) - ), - (ok, exception) -> { - if (exception != null) { - ctx.getLog().error("Failed to resync by maven coordinates, may ask again", exception); - return new Failed(msg.coordinates, msg.replyTo); - } - - return new WrappedResult(msg.replyTo); - } - ); - return Behaviors.same(); - }) - .onMessage(WrappedResult.class, msg -> { - msg.replyTo.tell(Done.done()); - return Behaviors.same(); - }) - .onMessage(Ignored.class, msg -> { - msg.replyTo.tell(Done.done()); - return Behaviors.same(); - }) - .onMessage(Failed.class, msg -> { - msg.replyTo.tell(Done.done()); - return Behaviors.same(); - }) - .build()); - } - -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/actor/CommitDetailsRegistrar.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/actor/CommitDetailsRegistrar.java deleted file mode 100644 index 8523c78b..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/actor/CommitDetailsRegistrar.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.actor; - -import akka.Done; -import akka.actor.typed.ActorRef; -import akka.actor.typed.receptionist.ServiceKey; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.api.models.VersionedCommit; - -import java.net.URI; - -public final class CommitDetailsRegistrar { - - public static final ServiceKey SERVICE_KEY = ServiceKey.create(Command.class, "commit-details-registrar"); - - @JsonDeserialize - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") - @JsonSubTypes({ - @JsonSubTypes.Type(HandleVersionedCommitReport.class), - @JsonSubTypes.Type(CommitNotFound.class), - @JsonSubTypes.Type(CompletedWork.class) - }) - public sealed interface Command extends Jsonable {} - - @JsonTypeName("handle-version-commit") - public record HandleVersionedCommitReport( - URI repo, - VersionedCommit versionedCommit, - MavenCoordinates coordinates, - ActorRef replyTo - ) implements Command { - @JsonCreator - public HandleVersionedCommitReport { - } - } - - @JsonTypeName("commit-not-found") - public record CommitNotFound( - URI repo, - String commitId, - MavenCoordinates coordinates, - ActorRef replyTo - ) implements Command { - @JsonCreator - public CommitNotFound { - } - } - - @JsonTypeName("completed-work") - record CompletedWork(ActorRef replyTo) implements Command { - @JsonCreator - public CompletedWork { - } - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/actor/CommitRegistrar.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/actor/CommitRegistrar.java deleted file mode 100644 index 9fb61f92..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/actor/CommitRegistrar.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.actor; - -import akka.Done; -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.receptionist.Receptionist; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import org.spongepowered.downloads.auth.api.utils.AuthUtils; -import org.spongepowered.downloads.versions.api.VersionsService; -import org.spongepowered.downloads.versions.api.models.CommitRegistration; -import org.spongepowered.synchronizer.gitmanaged.domain.GitCommand; -import org.spongepowered.synchronizer.gitmanaged.domain.GitManagedArtifact; - -import java.time.Duration; - -public class CommitRegistrar { - - public static Behavior register( - VersionsService versionsService - ) { - return Behaviors.setup(ctx -> { - final var registration = Receptionist.register(CommitDetailsRegistrar.SERVICE_KEY, ctx.getSelf()); - ctx.getSystem().receptionist().tell(registration); - final var sharding = ClusterSharding.get(ctx.getSystem()); - final var auth = AuthUtils.configure(ctx.getSystem().settings().config()); - return Behaviors.receive(CommitDetailsRegistrar.Command.class) - .onMessage(CommitDetailsRegistrar.HandleVersionedCommitReport.class, msg -> { - final var future = auth.internalAuth(versionsService.registerCommit( - msg.coordinates().groupId, msg.coordinates().artifactId, msg.coordinates().version - )).invoke(new CommitRegistration.ResolvedCommit( - msg.repo(), - msg.versionedCommit(), - msg.coordinates() - )).toCompletableFuture(); - ctx.pipeToSelf(future, (done, failure) -> { - if (failure != null) { - ctx.getLog().warn("Failed registering git details", failure); - } - return new CommitDetailsRegistrar.CompletedWork(msg.replyTo()); - }); - return Behaviors.same(); - }) - .onMessage(CommitDetailsRegistrar.CommitNotFound.class, msg -> { - final var future = auth.internalAuth(versionsService.registerCommit( - msg.coordinates().groupId, msg.coordinates().artifactId, msg.coordinates().version - )).invoke(new CommitRegistration.FailedCommit(msg.commitId(), msg.repo())) - .thenCompose(done -> sharding.entityRefFor(GitManagedArtifact.ENTITY_TYPE_KEY, - msg.coordinates().asArtifactCoordinates().asMavenString()) - .ask(replyTo -> new GitCommand.MarkVersionAsUnresolveable(replyTo, msg.coordinates(), msg.commitId()), - Duration.ofMinutes(10))) - .toCompletableFuture(); - - ctx.pipeToSelf(future, (done, failure) -> { - if (failure != null) { - ctx.getLog().warn("Failed registering git details", failure); - } - return new CommitDetailsRegistrar.CompletedWork(msg.replyTo()); - }); - return Behaviors.same(); - }) - .onMessage(CommitDetailsRegistrar.CompletedWork.class, msg -> { - msg.replyTo().tell(Done.getInstance()); - return Behaviors.same(); - }) - .build(); - }); - - } - -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/akka/FlowUtil.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/akka/FlowUtil.java deleted file mode 100644 index 879b77d9..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/akka/FlowUtil.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.akka; - -import akka.Done; -import akka.NotUsed; -import akka.japi.Pair; -import akka.stream.FlowShape; -import akka.stream.UniformFanInShape; -import akka.stream.UniformFanOutShape; -import akka.stream.javadsl.Broadcast; -import akka.stream.javadsl.Flow; -import akka.stream.javadsl.GraphDSL; -import akka.stream.javadsl.Merge; -import akka.stream.javadsl.Partition; -import io.vavr.Tuple; -import io.vavr.collection.HashMap; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import io.vavr.control.Option; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Arrays; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -public final class FlowUtil { - - private static final Logger LOGGER = LoggerFactory.getLogger("FlowUtil"); - - @SuppressWarnings("unchecked") - @SafeVarargs - public static Flow broadcast(Flow... flows) { - final var gatheredFlows = Arrays.stream(flows).collect(List.collector()); - final var count = gatheredFlows.size(); - return Flow.fromGraph(GraphDSL.create(builder -> { - final var broadcast = builder.add(Broadcast.create(count)); - final var merge = builder.add(Merge.create(count)); - for (int i = 0; i < count; i++) { - builder.from(broadcast.out(i)) - .via(builder.add(gatheredFlows.get(i))) - .toInlet(merge.in(i)); - } - return FlowShape.apply(broadcast.in(), merge.out()); - })); - } - - @SuppressWarnings("unchecked") - @SafeVarargs - public static Flow splitClassFlows( - Pair, Flow>... pairs - ) { - final List, Flow>> flowPairs = Arrays.stream(pairs) - .collect(List.collector()); - final var count = flowPairs.size(); - - final Map, Integer> classToIndex = HashMap.ofEntries( - IntStream.range(0, count) - .mapToObj(i -> Tuple.of(pairs[i].first(), i)) - .collect(Collectors.toList()) - ); - - final Function decider = (message) -> flowPairs.map(Pair::first) - .filter(clazz -> clazz.isInstance(message)) - .map(classToIndex::get) - .filter(Option::isDefined) - .map(Option::get) - .getOrElse(count); - - final Flow ignored = Flow.fromFunction(message -> { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("ignoring message {}", message); - } - return Done.done(); - }); - return Flow.fromGraph(GraphDSL.create(builder -> { - final UniformFanInShape merge = builder.add(Merge.create(count + 1)); - final UniformFanOutShape fanout = builder - .add(Partition.create(count + 1, decider::apply)); - for (int i = 0; i < count; i++) { - builder.from(fanout.out(i)) - .via(builder.add((Flow) flowPairs.get(i).second().async())) - .toInlet(merge.in(i)); - } - builder.from(fanout.out(count)) - .via(builder.add(ignored)) - .toInlet(merge.in(count)); - return FlowShape.of(fanout.in(), merge.out()); - })); - } - - @SuppressWarnings("unchecked") - public static Flow subClassFlow(Flow subFlow) { - return Flow.create() - .map(t -> (S) t) - .via(subFlow); - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/assetsync/AssetSettingsExtension.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/assetsync/AssetSettingsExtension.java deleted file mode 100644 index bcae2f5c..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/assetsync/AssetSettingsExtension.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.assetsync; - -import akka.actor.AbstractExtensionId; -import akka.actor.ExtendedActorSystem; -import akka.actor.Extension; -import akka.actor.ExtensionIdProvider; -import com.typesafe.config.Config; -import io.vavr.collection.List; - -import java.time.Duration; -import java.util.concurrent.TimeUnit; - -class AssetSettingsExtension extends AbstractExtensionId - implements ExtensionIdProvider { - public static final AssetSettingsExtension SettingsProvider = new AssetSettingsExtension(); - - @Override - public AssetRetrievalSettings createExtension(final ExtendedActorSystem system) { - return new AssetRetrievalSettings( - system.settings().config().getConfig("systemofadownload.synchronizer.worker.assets")); - } - - @Override - public AssetSettingsExtension lookup() { - return SettingsProvider; - } - - public static class AssetRetrievalSettings implements Extension { - public final String repository; - public final Duration timeout; - public final int retryCount; - public final List filesToIndex; - public final int poolSize; - - public AssetRetrievalSettings(Config config) { - this.repository = config.getString("repository"); - this.retryCount = config.getInt("retry"); - final var seconds = config.getDuration("timeout", TimeUnit.SECONDS); - this.timeout = Duration.ofSeconds(seconds); - final var stringList = config.getStringList("files-to-index"); - this.filesToIndex = List.ofAll(stringList); - this.poolSize = config.getInt("pool-size"); - } - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/assetsync/VersionConsumer.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/assetsync/VersionConsumer.java deleted file mode 100644 index b626b098..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/assetsync/VersionConsumer.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.assetsync; - -import akka.Done; -import akka.NotUsed; -import akka.actor.typed.DispatcherSelector; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.javadsl.Routers; -import akka.stream.javadsl.Flow; -import akka.stream.typed.javadsl.ActorFlow; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.spongepowered.downloads.versions.api.VersionsService; -import org.spongepowered.downloads.versions.api.models.ArtifactUpdate; -import org.spongepowered.synchronizer.SonatypeSynchronizer; -import org.spongepowered.synchronizer.SynchronizerSettings; - -public class VersionConsumer { - public static void subscribeToVersionedArtifactUpdates( - final VersionsService versionsService, final ObjectMapper mapper, - final ActorContext context, - final SynchronizerSettings settings - ) { - // region Synchronize Versioned Assets through Sonatype Search - final var componentPool = Routers.pool( - settings.asset.poolSize, - Behaviors.supervise(VersionedComponentWorker.gatherComponents(versionsService, mapper)) - .onFailure(settings.asset.backoff) - ); - final var componentRef = context.spawn( - componentPool, - "version-component-registration", - DispatcherSelector.defaultDispatcher() - ); - final Flow versionedFlow = ActorFlow.ask( - settings.asset.parallelism, - componentRef, - settings.asset.timeout, - (g, b) -> { - if (!(g instanceof ArtifactUpdate.ArtifactVersionRegistered a)) { - return new VersionedComponentWorker.Ignored(b); - } - return new VersionedComponentWorker.GatherComponentsForArtifact(a.coordinates(), b); - } - ); - versionsService.artifactUpdateTopic() - .subscribe() - .atLeastOnce(versionedFlow); - // endregion - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/assetsync/VersionedComponentWorker.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/assetsync/VersionedComponentWorker.java deleted file mode 100644 index fec41c61..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/assetsync/VersionedComponentWorker.java +++ /dev/null @@ -1,427 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.assetsync; - -import akka.Done; -import akka.actor.typed.ActorRef; -import akka.actor.typed.Behavior; -import akka.actor.typed.DispatcherSelector; -import akka.actor.typed.SupervisorStrategy; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.javadsl.Routers; -import akka.pattern.CircuitBreakerOpenException; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.vavr.collection.List; -import io.vavr.control.Try; -import org.spongepowered.downloads.artifact.api.Artifact; -import org.spongepowered.downloads.artifact.api.ArtifactCollection; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.auth.api.utils.AuthUtils; -import org.spongepowered.downloads.sonatype.AssetSearchResponse; -import org.spongepowered.downloads.sonatype.Component; -import org.spongepowered.downloads.versions.api.VersionsService; -import org.spongepowered.downloads.versions.api.models.VersionRegistration; - -import java.net.URI; -import java.net.URL; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.time.Duration; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.TimeoutException; - -public final class VersionedComponentWorker { - public static final String ASSET_SEARCH_ENDPOINT = - """ - /service/rest/v1/search/assets?maven.groupId=%s&maven.artifactId=%s&maven.baseVersion=%s&maven.extension=%s - """.trim(); - public static final String ASSET_SEARCH_WITH_TOKEN = - """ - /service/rest/v1/search/assets?continuationToken=%s&maven.groupId=%s&maven.artifactId=%s&maven.baseVersion=%s&maven.extension=%s - """.trim(); - - public interface Command { - } - - public record GatherComponentsForArtifact( - MavenCoordinates coordinates, - int count, ActorRef replyTo) - implements Command { - - public GatherComponentsForArtifact(MavenCoordinates coordinates, ActorRef replyTo) { - this(coordinates, 0, replyTo); - } - } - - public record Ignored(ActorRef replyTo) implements Command { - - } - - private interface ChildResponse extends Command { - } - - private record ComponentsAvailable( - MavenCoordinates coordinates, - List assets, - ActorRef replyTo - ) implements ChildResponse { - } - - private record FailedAssetRetrieval( - MavenCoordinates coordinates, - int count, - ActorRef replyTo - ) implements ChildResponse { - } - - public static Behavior gatherComponents( - final VersionsService service, - final ObjectMapper mapper - ) { - return Behaviors.setup(ctx -> { - final AssetSettingsExtension.AssetRetrievalSettings config = AssetSettingsExtension.SettingsProvider.get( - ctx.getSystem()); - final var assetDispatcher = DispatcherSelector.fromConfig("asset-retrieval-dispatcher"); - final var gathererPool = Routers.pool(config.poolSize, idleFetcher(config, mapper)); - final var gathererRef = ctx.spawn( - Behaviors.supervise(gathererPool).onFailure(SupervisorStrategy.restart()), - "search-versioned-components", - assetDispatcher - ); - final var auth = AuthUtils.configure(ctx.getSystem().settings().config()); - final var assetRegisters = Routers.pool(config.poolSize, registerAssets(service, auth)); - final var assetRegistersRef = ctx.spawn( - Behaviors.supervise(assetRegisters).onFailure(SupervisorStrategy.restart()), - "versioned-asset-register", - assetDispatcher - ); - return idleGatherer(gathererRef, assetRegistersRef, config); - }); - } - - private static Behavior idleGatherer( - final ActorRef gathererRef, - final ActorRef assetRegistersRef, - final AssetSettingsExtension.AssetRetrievalSettings config - ) { - return Behaviors.setup(ctx -> Behaviors.receive(Command.class) - .onMessage(GatherComponentsForArtifact.class, cmd -> { - config.filesToIndex.forEach(fileType -> ctx.ask( - ChildResponse.class, - gathererRef, - config.timeout, - ref -> new StartRequest(cmd.coordinates, fileType, ref, cmd.replyTo), - (response, throwable) -> { - if (throwable != null) { - return new FailedAssetRetrieval(cmd.coordinates, cmd.count + 1, cmd.replyTo); - } - // In case the child returned a failed asset retrieval - if (response instanceof FailedAssetRetrieval f) { - return new FailedAssetRetrieval(f.coordinates, f.count + 1, cmd.replyTo); - } - return response; - } - )); - return Behaviors.same(); - }) - .onMessage(ComponentsAvailable.class, available -> { - // now we've gotta register the components to the version service - available.replyTo.tell(Done.done()); - assetRegistersRef.tell(new AttemptRegistration(available.coordinates, available.assets)); - return Behaviors.same(); - }) - .onMessage(FailedAssetRetrieval.class, failed -> { - // Retry basically - ctx.getLog().error("Failed artifact attempt"); - if (failed.count >= config.retryCount) { - ctx.getLog().error("Aborting attempt to sync artifact assets"); - failed.replyTo.tell(Done.done()); - return Behaviors.same(); - } - ctx.getSelf().tell(new GatherComponentsForArtifact(failed.coordinates, failed.count, failed.replyTo)); - return Behaviors.same(); - }) - .onMessage(Ignored.class, ignored -> { - ignored.replyTo.tell(Done.done()); - return Behaviors.same(); - }) - .build()); - } - - private interface Gather { - } - - private record StartRequest( - MavenCoordinates coordinates, - String fileType, - ActorRef ref, - ActorRef completedReplyTo - ) implements Gather { - } - - private record ContinueRequest( - MavenCoordinates coordinates, - String fileType, - List existing, - String continuationToken, - ActorRef replyTo, - ActorRef completedReplyTo - ) implements Gather { - } - - private record Completed( - MavenCoordinates coordinates, - List existing, - ActorRef replyTo, - ActorRef completedReplyTo - ) implements Gather { - } - - private record Failed( - MavenCoordinates coordinates, - List recovered, - ActorRef replyTo, - ActorRef completedReplyTo - ) implements Gather { - } - - private static Behavior idleFetcher( - final AssetSettingsExtension.AssetRetrievalSettings config, - final ObjectMapper playMapper - ) { - return Behaviors.setup(ctx -> { - final var client = HttpClient.newBuilder().connectTimeout(config.timeout) - .executor(ctx.getExecutionContext()) - .build(); - return Behaviors.receive(Gather.class) - .onMessage(StartRequest.class, req -> { - runRequest(config, playMapper, ctx, client, req); - return working(config, playMapper, req, List.empty()); - }) - .onMessage(ContinueRequest.class, cont -> { - ctx.getLog().warn("Somehow got a continuation while idling"); - return Behaviors.same(); - }) - .onMessage(Completed.class, completed -> { - ctx.getLog().warn("Somehow got completed while idling"); - return Behaviors.same(); - }) - .onMessage(Failed.class, failed -> { - failed.replyTo.tell(new FailedAssetRetrieval(failed.coordinates, 0, failed.completedReplyTo)); - return Behaviors.same(); - }) - .build(); - }); - } - - private static void runRequest( - AssetSettingsExtension.AssetRetrievalSettings config, ObjectMapper playMapper, ActorContext ctx, - HttpClient client, StartRequest req - ) { - final var formatted = String.format( - config.repository + ASSET_SEARCH_ENDPOINT, - req.coordinates.groupId, - req.coordinates.artifactId, - req.coordinates.version, - req.fileType - ); - ctx.pipeToSelf(searchAssets(playMapper, client, formatted), (response, failure) -> { - if (failure != null) { - return new Failed(req.coordinates, List.empty(), req.ref, req.completedReplyTo); - } - return response.continuationToken() - .map( - token -> new ContinueRequest( - req.coordinates, req.fileType, response.items(), token, req.ref, - req.completedReplyTo - )) - .orElseGet( - () -> new Completed(req.coordinates, response.items(), req.ref, req.completedReplyTo)); - }); - } - - private static Behavior working(final AssetSettingsExtension.AssetRetrievalSettings config, - final ObjectMapper playMapper, - final StartRequest working, - final List queue - ) { - return Behaviors.setup(ctx -> { - final var client = HttpClient.newBuilder().connectTimeout(config.timeout) - .executor(ctx.getExecutionContext()) - .build(); - - return Behaviors.receive(Gather.class) - .onMessage(StartRequest.class, req -> working(config, playMapper, working, queue.append(req))) - .onMessage(ContinueRequest.class, cont -> { - final var formatted = String.format( - config.repository + ASSET_SEARCH_WITH_TOKEN, - cont.continuationToken, - cont.coordinates.groupId, - cont.coordinates.artifactId, - cont.coordinates.version, - cont.fileType - ); - ctx.pipeToSelf(searchAssets(playMapper, client, formatted), (response, throwable) -> { - if (throwable != null) { - return new Failed(cont.coordinates, cont.existing, cont.replyTo, cont.completedReplyTo); - } - final var completedAssets = cont.existing.appendAll(response.items()); - return response.continuationToken() - .map( - token -> new ContinueRequest( - cont.coordinates, cont.fileType, completedAssets, token, cont.replyTo, - cont.completedReplyTo - )) - .orElseGet(() -> new Completed(cont.coordinates, completedAssets, cont.replyTo, - cont.completedReplyTo - )); - }); - return Behaviors.same(); - }) - .onMessage(Completed.class, completed -> { - completed.replyTo.tell( - new ComponentsAvailable(completed.coordinates, completed.existing, completed.completedReplyTo)); - if (queue.isEmpty()) { - return idleFetcher(config, playMapper); - } - final var next = queue.head(); - runRequest(config, playMapper, ctx, client, next); - return working(config, playMapper, next, queue.tail()); - }) - .onMessage(Failed.class, failed -> { - runRequest(config, playMapper, ctx, client, working); - return Behaviors.same(); - }) - .build(); - }); - } - - private static CompletableFuture searchAssets( - final ObjectMapper playMapper, - final HttpClient client, - final String assetSearchUrl - ) { - return Try.of(() -> new URL(assetSearchUrl)) - .mapTry(URL::toURI) - .map(url -> HttpRequest.newBuilder(url).GET()) - .toCompletableFuture() - .thenCompose(req -> client.sendAsync(req.build(), HttpResponse.BodyHandlers.ofInputStream())) - .thenApply(HttpResponse::body) - .thenApply(stream -> Try.withResources(() -> stream) - .of(is -> playMapper.readValue(is, AssetSearchResponse.class)) - .get()); - } - - private interface Registrar { - } - - private record AttemptRegistration(MavenCoordinates coordinates, - List assets) implements Registrar { - } - - private interface RegistrationResult extends Registrar { - } - - private record AssetRegistrationCompleted(MavenCoordinates coordinates) implements RegistrationResult { - } - - private record AssetRegistrationFailed(MavenCoordinates coordinates) implements RegistrationResult { - } - - private record WrappedResult(RegistrationResult response) implements Registrar { - } - - private static Behavior registerAssets( - final VersionsService service, - final AuthUtils auth - ) { - return Behaviors.setup(ctx -> - Behaviors.withTimers(timers -> Behaviors.receive(Registrar.class) - .onMessage(AttemptRegistration.class, registration -> { - final var artifacts = registration.assets.map(asset -> - Try.of(() -> URI.create(asset.downloadUrl())) - .map(downloadUri -> Optional.ofNullable(asset.mavenData()) - .map(data -> new Artifact( - Optional.ofNullable(data.classifier()), - downloadUri, - asset.checksum().md5(), - asset.checksum().sha1(), - asset.mavenData().extension() - )) - ) - .filter(Optional::isPresent) - .map(Optional::get) - .toJavaOptional() - ) - .filter(Optional::isPresent) - .map(Optional::get); - final var collection = new ArtifactCollection(artifacts, registration.coordinates); - - ; - final var registrationFuture = auth.internalAuth(service.registerArtifactCollection( - registration.coordinates.groupId, - registration.coordinates.artifactId - )).invoke(new VersionRegistration.Register.Collection(collection)); - - ctx.pipeToSelf(registrationFuture, (response, throwable) -> { - if (throwable != null) { - if (throwable instanceof CompletionException ce) { - throwable = ce.getCause(); - } - if (throwable instanceof CircuitBreakerOpenException cboe) { - ctx.getLog().warn("Rescheduling asset registration for {}", registration.coordinates); - timers.startSingleTimer(registration.coordinates, registration, Duration.ofMillis(cboe.remainingDuration().toMillis())); - return new WrappedResult(new AssetRegistrationFailed(registration.coordinates)); - } else if (throwable instanceof TimeoutException) { - ctx.getLog().warn("Rescheduling asset registration for {}", registration.coordinates); - timers.startSingleTimer(registration.coordinates, registration, Duration.ofSeconds(2)); - return new WrappedResult(new AssetRegistrationFailed(registration.coordinates)); - } - ctx.getLog().error("Failed to register asset for " + registration.coordinates.asStandardCoordinates(), throwable); - return new WrappedResult(new AssetRegistrationFailed(registration.coordinates)); - - } - return new WrappedResult(new AssetRegistrationCompleted(registration.coordinates)); - }); - return Behaviors.same(); - }) - .onMessage(WrappedResult.class, result -> { - final var response = result.response; - if (response instanceof AssetRegistrationCompleted a) { - ctx.getLog().debug("Successful Asset registration of {}", a.coordinates); - } else if (response instanceof AssetRegistrationFailed f) { - ctx.getLog().warn("Failed registration of {}", f.coordinates); - } - return Behaviors.same(); - }) - .build()) - ); - } - -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/ArtifactSubscriber.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/ArtifactSubscriber.java deleted file mode 100644 index dcb00fe0..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/ArtifactSubscriber.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.gitmanaged; - -import akka.Done; -import akka.NotUsed; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Routers; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.japi.Pair; -import akka.stream.FlowShape; -import akka.stream.javadsl.Balance; -import akka.stream.javadsl.Flow; -import akka.stream.javadsl.GraphDSL; -import akka.stream.javadsl.Merge; -import akka.stream.javadsl.Source; -import akka.stream.typed.javadsl.ActorFlow; -import io.vavr.collection.HashMap; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import org.spongepowered.downloads.artifact.api.ArtifactService; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.artifact.api.event.ArtifactUpdate; -import org.spongepowered.synchronizer.akka.FlowUtil; -import org.spongepowered.synchronizer.gitmanaged.domain.GitCommand; -import org.spongepowered.synchronizer.gitmanaged.domain.GitManagedArtifact; -import org.spongepowered.synchronizer.gitmanaged.util.jgit.CommitResolutionManager; - -import java.net.URI; -import java.time.Duration; - -public final class ArtifactSubscriber { - - public static void setup(ArtifactService artifacts, ActorContext ctx) { - final Flow flow = generateFlows(ctx); - - artifacts.artifactUpdate() - .subscribe() - .atLeastOnce(flow); - } - - private static Flow generateFlows(ActorContext ctx) { - final var sharding = ClusterSharding.get(ctx.getSystem()); - final var repoAssociatedFlow = getRepoAssociatedFlow(sharding, ctx); - - return FlowUtil.splitClassFlows( - Pair.create(ArtifactUpdate.GitRepositoryAssociated.class, repoAssociatedFlow) - ); - } - - @SuppressWarnings("unchecked") - private static Flow getRepoAssociatedFlow( - ClusterSharding sharding, - ActorContext ctx - ) { - // Step 1 - Register the repository with GitManagedArtifact - final var registerRepo = Flow.fromFunction( - c -> sharding.entityRefFor(GitManagedArtifact.ENTITY_TYPE_KEY, c.coordinates().asMavenString()) - .ask( - replyTo -> new GitCommand.RegisterRepository(URI.create(c.repository()), replyTo), - Duration.ofSeconds(20) - ) - .thenApply(d -> c) - .toCompletableFuture() - .join() - ); - final var key = Routers.group(CommitResolutionManager.SERVICE_KEY); - final var workerRef = ctx.spawn(key, "repo-associated-commit-resolver"); - // Step 2 - Get unresolved commits from GitManagedArtifact to perform work - final Flow registerRepoFlow = Flow.create() - .mapAsync( - 1, c -> sharding.entityRefFor(GitManagedArtifact.ENTITY_TYPE_KEY, c.coordinates().asMavenString()) - .ask(GitCommand.GetUnresolvedVersions::new, Duration.ofSeconds(20)) - ) - .flatMapConcat(work -> { - final Map tuple2s = work.repositories().isEmpty() - ? HashMap.empty() - : work.unresolvedCommits(); - final List requests = tuple2s - .toList().map( - (t) -> new CommitResolutionManager.ResolveCommitDetails( - t._1(), t._2(), work.repositories(), null) - ); - return Source.from(requests); - }); - // Step 2a - Resolve the commits - final var flow = ActorFlow.ask( - 4, - workerRef, - Duration.ofMinutes(10), - (msg, replyTo) -> new CommitResolutionManager.ResolveCommitDetails( - msg.coordinates(), msg.commit(), msg.gitRepo(), replyTo) - ); - - return Flow.fromGraph(GraphDSL.create(b -> { - final var balance = b.add(Balance.create(4)); - final var zip = b.add(Merge.create(4)); - final var add = b.add(registerRepo.via(registerRepoFlow)); - b.from(add.out()) - .toFanOut(balance); - for (int i = 0; i < 4; i++) { - final var resolver = b.add(flow); - b.from(balance.out(i)) - .via(resolver) - .toInlet(zip.in(i)); - - } - return FlowShape.of(add.in(), zip.out()); - })); - } - -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/CommitConsumer.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/CommitConsumer.java deleted file mode 100644 index 3af605f2..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/CommitConsumer.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.gitmanaged; - -import akka.Done; -import akka.NotUsed; -import akka.actor.typed.ActorRef; -import akka.actor.typed.DispatcherSelector; -import akka.actor.typed.SupervisorStrategy; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.javadsl.Routers; -import akka.actor.typed.receptionist.Receptionist; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.cluster.typed.Cluster; -import akka.japi.Pair; -import akka.stream.javadsl.Flow; -import org.spongepowered.downloads.versions.api.VersionsService; -import org.spongepowered.downloads.versions.api.models.VersionedArtifactUpdates; -import org.spongepowered.synchronizer.actor.CommitDetailsRegistrar; -import org.spongepowered.synchronizer.actor.CommitRegistrar; -import org.spongepowered.synchronizer.akka.FlowUtil; -import org.spongepowered.synchronizer.gitmanaged.domain.GitCommand; -import org.spongepowered.synchronizer.gitmanaged.domain.GitManagedArtifact; -import org.spongepowered.synchronizer.gitmanaged.util.jgit.CommitResolutionManager; - -import java.time.Duration; -import java.util.UUID; - -public class CommitConsumer { - - public static void setupSubscribers(VersionsService versionsService, ActorContext ctx) { - final var member = Cluster.get(ctx.getSystem()).selfMember(); - final var uid = UUID.randomUUID(); - final var workerName = "commit-resolver-" + uid; - if (member.hasRole("commit-resolver")) { - final var registrar = ctx.spawn(CommitRegistrar.register(versionsService), "commit-registrar-" + uid); - spawnCommitResolver(ctx, uid, workerName, registrar); - } - final var versionedAssetFlows = CommitConsumer.createVersionedAssetFlows(ctx); - versionsService.versionedArtifactUpdatesTopic() - .subscribe() - .atLeastOnce(versionedAssetFlows); - } - - public static Flow createVersionedAssetFlows( - ActorContext ctx - ) { - final var sharding = ClusterSharding.get(ctx.getSystem()); - final var associateCommitFlow = registerRawCommitFlow(sharding); - - final var registerResolvedCommits = registerResolvedCommitDetails(sharding); - - return FlowUtil.splitClassFlows( - Pair.create(VersionedArtifactUpdates.CommitExtracted.class, associateCommitFlow), - Pair.create(VersionedArtifactUpdates.GitCommitDetailsAssociated.class, registerResolvedCommits) - ); - } - - private static void spawnCommitResolver( - ActorContext ctx, UUID uid, - String workerName, - final ActorRef registrar - ) { - final ActorRef workerRef; - - final var resolver = CommitResolutionManager.resolveCommit(registrar); - final var supervisedResolver = Behaviors.supervise(resolver) - .onFailure(SupervisorStrategy.restartWithBackoff( - Duration.ofMillis(100), - Duration.ofSeconds(40), - 0.1 - )); - final var pool = Routers.pool(4, supervisedResolver); - - workerRef = ctx.spawn( - pool, - workerName + "-" + uid, - DispatcherSelector.defaultDispatcher() - ); - // Announce it to the cluster - ctx.getSystem().receptionist().tell(Receptionist.register(CommitResolutionManager.SERVICE_KEY, workerRef)); - - } - - private static Flow registerRawCommitFlow( - ClusterSharding sharding - ) { - return Flow.fromFunction(msg -> sharding.entityRefFor( - GitManagedArtifact.ENTITY_TYPE_KEY, - msg.coordinates().asArtifactCoordinates().asMavenString() - ) - .ask( - replyTo -> new GitCommand.RegisterRawCommit(msg.coordinates(), msg.commit(), replyTo), - Duration.ofSeconds(20) - ) - .toCompletableFuture() - .join() - ); - } - - private static Flow registerResolvedCommitDetails( - ClusterSharding sharding - ) { - return Flow.fromFunction(event -> sharding - .entityRefFor( - GitManagedArtifact.ENTITY_TYPE_KEY, - event.coordinates().asArtifactCoordinates().asMavenString() - ) - .ask( - replyTo -> new GitCommand.MarkVersionAsResolved(event.coordinates(), event.commit(), replyTo), - Duration.ofSeconds(20) - ) - .toCompletableFuture() - .join() - ); - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/ScheduledCommitResolver.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/ScheduledCommitResolver.java deleted file mode 100644 index 404430c3..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/ScheduledCommitResolver.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.gitmanaged; - -import akka.Done; -import akka.NotUsed; -import akka.actor.typed.ActorRef; -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.javadsl.Routers; -import akka.actor.typed.receptionist.Receptionist; -import akka.actor.typed.receptionist.ServiceKey; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.cluster.typed.ClusterSingleton; -import akka.cluster.typed.SingletonActor; -import akka.japi.Pair; -import akka.stream.Graph; -import akka.stream.Materializer; -import akka.stream.SourceShape; -import akka.stream.javadsl.Balance; -import akka.stream.javadsl.Flow; -import akka.stream.javadsl.GraphDSL; -import akka.stream.javadsl.Merge; -import akka.stream.javadsl.Sink; -import akka.stream.javadsl.Source; -import akka.stream.typed.javadsl.ActorFlow; -import akka.stream.typed.javadsl.ActorSink; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.HashSet; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.ArtifactService; -import org.spongepowered.downloads.artifact.api.Group; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.artifact.api.event.ArtifactUpdate; -import org.spongepowered.downloads.artifact.api.query.GetArtifactsResponse; -import org.spongepowered.downloads.artifact.api.query.GroupsResponse; -import org.spongepowered.synchronizer.SonatypeSynchronizer; -import org.spongepowered.synchronizer.akka.FlowUtil; -import org.spongepowered.synchronizer.gitmanaged.domain.GitCommand; -import org.spongepowered.synchronizer.gitmanaged.domain.GitManagedArtifact; -import org.spongepowered.synchronizer.gitmanaged.util.jgit.CommitResolutionManager; - -import java.time.Duration; - -public final class ScheduledCommitResolver { - - public static final ServiceKey SCHEDULED_REFRESH = ServiceKey.create( - ScheduledRefresh.class, "scheduled-refresh"); - - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, - property = "type") - @JsonSubTypes({ - @JsonSubTypes.Type(Refresh.class), - @JsonSubTypes.Type(Register.class), - @JsonSubTypes.Type(CommitResolved.class), - @JsonSubTypes.Type(WorkCompleted.class), - @JsonSubTypes.Type(Resync.class) - }) - public sealed interface ScheduledRefresh extends Jsonable { - } - - @JsonTypeName("refresh") - public record Refresh() implements ScheduledRefresh { - } - - @JsonTypeName("resync") - record Resync() implements ScheduledRefresh { - } - - @JsonTypeName("register-coordinates") - record Register(ArtifactCoordinates coordinates, ActorRef replyTo) implements ScheduledRefresh { - } - - @JsonTypeName("resolved") - record CommitResolved() implements ScheduledRefresh { - } - - @JsonTypeName("completed") - record WorkCompleted() implements ScheduledRefresh { - } - - public static void setup( - ArtifactService artifactService, - ActorContext context - ) { - final var singleton = SingletonActor.of(setup(artifactService), "ScheduledCommitResolver"); - final var scheduler = ClusterSingleton.get(context.getSystem()).init(singleton); - final var sync = registerForSync(scheduler); - artifactService.artifactUpdate() - .subscribe() - .atLeastOnce(FlowUtil.splitClassFlows(Pair.create(ArtifactUpdate.ArtifactRegistered.class, sync))); - } - - private static Source parseResponseIntoArtifacts( - ArtifactService artifactService, - GroupsResponse r - ) { - final Source groups; - if (r instanceof GroupsResponse.Available a) { - groups = Source.from(a.groups().map(Group::groupCoordinates)); - } else { - groups = Source.empty(); - } - final var groupsToArtifacts = Flow.create() - .mapConcat(g -> { - final var join = artifactService.getArtifacts(g).invoke().toCompletableFuture().join(); - if (join instanceof GetArtifactsResponse.ArtifactsAvailable aa) { - return aa.artifactIds().map(id -> new ArtifactCoordinates(g, id)); - } else { - return List.empty(); - } - }); - return groups.via(groupsToArtifacts); - } - - private static Flow registerForSync( - final ActorRef scheduler - ) { - return ActorFlow.ask( - scheduler, Duration.ofMinutes(1), (reg, replyTo) -> new Register(reg.coordinates(), replyTo)); - } - - private static Behavior setup( - final ArtifactService artifactService - ) { - return Behaviors.setup(ctx -> Behaviors.withTimers(timers -> { - ctx.getSystem().receptionist().tell(Receptionist.register(SCHEDULED_REFRESH, ctx.getSelf())); - ctx.getLog().info("Starting ScheduledCommitResolver"); - timers.startSingleTimer("resync", new Resync(), Duration.ofSeconds(30)); - timers.startPeriodicTimer("refresh", new Refresh(), Duration.ofMinutes(1)); - ctx.getLog().info("Scheduled refresh every minute"); - - return Behaviors.receive(ScheduledRefresh.class) - .onMessage(Register.class, msg -> { - msg.replyTo.tell(Done.done()); - final var key = Routers.group(CommitResolutionManager.SERVICE_KEY); - final ActorRef workerRef = ctx.spawn( - key, "repo-associated-commit-resolver"); - return waiting(workerRef, HashSet.of(msg.coordinates()), artifactService); - }) - .onMessage(Refresh.class, msg -> Behaviors.same()) - .onMessage(Resync.class, msg -> resyncArtifactCoordinates(artifactService, ctx)) - .build(); - })); - } - - private static Behavior resyncArtifactCoordinates( - ArtifactService artifactService, ActorContext ctx - ) { - final Flow getGroups = Flow.fromFunction(s -> s.getGroups() - .invoke() - .toCompletableFuture() - .join()); - Source.single(artifactService) - .via(getGroups - .flatMapConcat((GroupsResponse r) -> parseResponseIntoArtifacts(artifactService, r))) - .via(ActorFlow.ask(ctx.getSelf(), Duration.ofSeconds(10), Register::new)) - .to(Sink.ignore()) - .run(ctx.getSystem()); - return Behaviors.same(); - } - - private static Behavior waiting( - final ActorRef workerRef, - final HashSet artifactsKeepingTracked, - final ArtifactService artifactService - ) { - return Behaviors.setup(ctx -> Behaviors.withTimers(timers -> { - final ClusterSharding sharding = ClusterSharding.get(ctx.getSystem()); - final Materializer mat = Materializer.createMaterializer(ctx.getSystem()); - return Behaviors.receive(ScheduledRefresh.class) - .onMessage(Resync.class, msg -> resyncArtifactCoordinates(artifactService, ctx)) - .onMessage(Refresh.class, msg -> { - performRefresh(workerRef, artifactsKeepingTracked, ctx, sharding, mat); - return working(workerRef, artifactsKeepingTracked, artifactService); - }) - .onMessage(Register.class, msg -> { - msg.replyTo.tell(Done.done()); - return waiting(workerRef, artifactsKeepingTracked.add(msg.coordinates()), artifactService); - }) - .build(); - })); - } - - @SuppressWarnings("unchecked") - private static void performRefresh( - final ActorRef workerRef, - final HashSet artifactsKeepingTracked, - final ActorContext ctx, - ClusterSharding sharding, - Materializer mat - ) { - final Source artifactsToWorkOn = Source.from(artifactsKeepingTracked); - final Flow workFetcherFlow = getWork(sharding).log( - "work-fetcher"); - final Flow parseWorkFlow = Flow.create() - .log("parse-work") - .flatMapConcat(work -> { - final Map tuple2s = work.unresolvedCommits(); - final List requests = tuple2s - .toList().map( - (t) -> new CommitResolutionManager.ResolveCommitDetails( - t._1(), t._2(), work.repositories(), null) - ); - return Source.from(requests); - }); - final Flow commitResolverAskFlow = ActorFlow.ask( - 4, - workerRef, - Duration.ofMinutes(10), - (m, replyTo) -> new CommitResolutionManager.ResolveCommitDetails( - m.coordinates(), m.commit(), m.gitRepo(), replyTo) - ); - final Graph, NotUsed> graph = GraphDSL.create(b -> { - final SourceShape artifacts = b.add(artifactsToWorkOn); - final var balance = b.add(Balance.create(4)); - final var zip = b.add(Merge.create(4)); - final var getWork = b.add(workFetcherFlow); - final var parseWork = b.add(parseWorkFlow.async()); - b.from(artifacts.out()) - .via(getWork) - .via(parseWork) - .toFanOut(balance); - for (int i = 0; i < 4; i++) { - final var resolver = b.add(commitResolverAskFlow.async()); - b.from(balance.out(i)) - .via(resolver) - .toInlet(zip.in(i)); - } - return SourceShape.of(zip.out()); - }); - final var log = ctx.getLog(); - final Sink sink = ActorSink.actorRef( - ctx.getSelf(), new WorkCompleted(), t -> { - log.error("Got an error while resolving commits", t); - return new WorkCompleted(); - }); - Source.fromGraph(graph) - .via(Flow.fromFunction(d -> new CommitResolved())) - .to(sink) - .run(mat); - } - - private static Behavior working( - ActorRef workerRef, - final HashSet artifactsKeepingTracked, - final ArtifactService artifactService - ) { - return Behaviors.setup(ctx -> Behaviors.withTimers(timers -> Behaviors.receive(ScheduledRefresh.class) - .onMessage(Resync.class, msg -> resyncArtifactCoordinates(artifactService, ctx)) - .onMessage(Refresh.class, msg -> Behaviors.same()) - .onMessage(Register.class, msg -> { - msg.replyTo.tell(Done.done()); - return working(workerRef, artifactsKeepingTracked.add(msg.coordinates()), artifactService - ); - }) - .onMessage(CommitResolved.class, msg -> Behaviors.same()) - .onMessage(WorkCompleted.class, msg -> { - timers.startSingleTimer(new Refresh(), Duration.ofMinutes(1)); - return waiting(workerRef, artifactsKeepingTracked, artifactService); - }) - .build())); - } - - private static Flow getWork(ClusterSharding sharding) { - return Flow.create() - .map(artifact -> sharding.entityRefFor(GitManagedArtifact.ENTITY_TYPE_KEY, artifact.asMavenString()) - .ask(GitCommand.GetUnresolvedVersions::new, Duration.ofSeconds(30)) - .toCompletableFuture() - .join()) - .filter(GitCommand.UnresolvedWork::hasWork); - } - -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/domain/GitCommand.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/domain/GitCommand.java deleted file mode 100644 index 7d0c2247..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/domain/GitCommand.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.gitmanaged.domain; - -import akka.Done; -import akka.actor.typed.ActorRef; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.HashMap; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.api.models.VersionedCommit; - -import java.net.URI; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonDeserialize -@JsonSubTypes({ - @JsonSubTypes.Type(value = GitCommand.RegisterRepository.class, name = "register-repository"), - @JsonSubTypes.Type(value = GitCommand.RegisterRawCommit.class, name = "register-raw-commit"), - @JsonSubTypes.Type(value = GitCommand.GetRepositories.class, name = "get-repositories"), - @JsonSubTypes.Type(value = GitCommand.GetUnresolvedVersions.class, name = "get-unresolved-versions"), - @JsonSubTypes.Type(value = GitCommand.MarkVersionAsResolved.class, name = "mark-version-as-resolved"), - @JsonSubTypes.Type(value = GitCommand.MarkVersionAsUnresolveable.class, name = "mark-version-as-unresolveable"), -}) -public sealed interface GitCommand extends Jsonable { - - record RegisterRepository(URI repository, ActorRef replyTo) implements GitCommand { - @JsonCreator - public RegisterRepository { - } - } - - record RegisterRawCommit( - MavenCoordinates coordinates, - String commitSha, - ActorRef replyTo - ) implements GitCommand { - @JsonCreator - public RegisterRawCommit { - } - } - - @JsonDeserialize - @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION) - sealed interface RepositoryResponse extends Jsonable { - List repositories(); - } - - @JsonDeserialize - record RepositoriesAvaiable(List repositories) implements RepositoryResponse { - @JsonCreator - public RepositoriesAvaiable { - } - } - - @JsonDeserialize - record NoRepositories() implements RepositoryResponse { - @JsonCreator - public NoRepositories { - } - - @Override - public List repositories() { - return List.empty(); - } - } - - record GetRepositories(ActorRef replyTo) implements GitCommand { - @JsonCreator - public GetRepositories { - } - } - - record GetUnresolvedVersions(ActorRef replyTo) implements GitCommand { - @JsonCreator - public GetUnresolvedVersions { - } - } - - record MarkVersionAsResolved( - MavenCoordinates coordinates, - VersionedCommit commit, - ActorRef replyTo - ) implements GitCommand { - @JsonCreator - public MarkVersionAsResolved { - } - } - - record MarkVersionAsUnresolveable( - ActorRef replyTo, - MavenCoordinates coordinates, - String commitSha - ) implements GitCommand {} - - - static final UnresolvedWork EMPTY = new UnresolvedWork(HashMap.empty(), List.empty()); - @JsonDeserialize - @JsonSerialize - record UnresolvedWork( - Map unresolvedCommits, - List repositories - ) implements Jsonable { - @JsonCreator - public UnresolvedWork { - } - - public boolean isEmpty() { - return unresolvedCommits.isEmpty(); - } - - public boolean hasWork() { - return !unresolvedCommits.isEmpty() && !this.repositories.isEmpty(); - } - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/domain/GitEvent.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/domain/GitEvent.java deleted file mode 100644 index a4c63b9d..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/domain/GitEvent.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.gitmanaged.domain; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.javadsl.persistence.AggregateEvent; -import com.lightbend.lagom.javadsl.persistence.AggregateEventShards; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTag; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTagger; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.api.models.VersionedCommit; - -import java.net.URI; - -@JsonDeserialize -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = GitEvent.RepositoryRegistered.class), - @JsonSubTypes.Type(value = GitEvent.CommitRegistered.class), - @JsonSubTypes.Type(value = GitEvent.CommitResolved.class), -}) -public sealed interface GitEvent extends AggregateEvent, Jsonable { - - AggregateEventShards INSTANCE = AggregateEventTag.sharded(GitEvent.class, 3); - - @Override - default AggregateEventTagger aggregateTag() { - return INSTANCE; - } - - @JsonTypeName("repository-registered") - record RepositoryRegistered(URI repository) implements GitEvent { - @JsonCreator - public RepositoryRegistered { - } - } - @JsonTypeName("commit-extracted") - record CommitRegistered(MavenCoordinates coordinates, String commit) implements GitEvent { - @JsonCreator - public CommitRegistered { - } - } - @JsonTypeName("commit-resolved") - record CommitResolved(MavenCoordinates coordinates, VersionedCommit resolvedCommit) implements GitEvent { - @JsonCreator - public CommitResolved { - } - } - - @JsonTypeName("commit-unresolved") - record CommitUnresolvable(MavenCoordinates coordinates, String commit) implements GitEvent { - @JsonCreator - public CommitUnresolvable { - } - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/domain/GitManagedArtifact.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/domain/GitManagedArtifact.java deleted file mode 100644 index 361f0e11..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/domain/GitManagedArtifact.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.gitmanaged.domain; - -import akka.Done; -import akka.actor.typed.ActorRef; -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.javadsl.Routers; -import akka.cluster.sharding.typed.javadsl.EntityTypeKey; -import akka.persistence.typed.PersistenceId; -import akka.persistence.typed.javadsl.CommandHandlerWithReply; -import akka.persistence.typed.javadsl.EventHandler; -import akka.persistence.typed.javadsl.EventSourcedBehaviorWithEnforcedReplies; -import akka.persistence.typed.javadsl.RetentionCriteria; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.synchronizer.gitmanaged.ScheduledCommitResolver; - -public class GitManagedArtifact extends EventSourcedBehaviorWithEnforcedReplies { - - public static final EntityTypeKey ENTITY_TYPE_KEY = EntityTypeKey.create( - GitCommand.class, "git-managed-artifact"); - - private final ActorContext ctx; - private final ArtifactCoordinates coordinates; - private ActorRef scheduledRefresh; - - public static Behavior create(final PersistenceId persistenceId, final String entityId) { - return Behaviors.setup(ctx -> { - final var group = Routers.group(ScheduledCommitResolver.SCHEDULED_REFRESH); - final var scheduledRefresh = ctx.spawnAnonymous(group); - return new GitManagedArtifact(persistenceId, entityId, ctx, scheduledRefresh); - }); - } - - private GitManagedArtifact( - final PersistenceId persistenceId, - final String entityId, final ActorContext ctx, - final ActorRef scheduledRefresh - ) { - super(persistenceId); - this.scheduledRefresh = scheduledRefresh; - final var split = entityId.split(":"); - this.coordinates = new ArtifactCoordinates(split[0], split[1]); - this.ctx = ctx; - } - - @Override - public GitState emptyState() { - return new GitState.Empty(); - } - - @Override - public CommandHandlerWithReply commandHandler() { - final var builder = this.newCommandHandlerWithReplyBuilder(); - builder.forAnyState() - .onCommand(GitCommand.RegisterRepository.class, (state, cmd) -> this.Effect() - .persist(new GitEvent.RepositoryRegistered(cmd.repository())) - .thenRun(ns -> this.scheduledRefresh.tell(new ScheduledCommitResolver.Refresh())) - .thenReply(cmd.replyTo(), ns -> Done.done()) - ) - .onCommand(GitCommand.GetRepositories.class, (state, cmd) -> this.Effect() - .reply(cmd.replyTo(), state.repositories().isEmpty() ? - new GitCommand.NoRepositories() : new GitCommand.RepositoriesAvaiable(state.repositories())) - ) - .onCommand(GitCommand.GetUnresolvedVersions.class, (state, cmd) -> { - final var unresolvedWork = state.unresolvedVersions(); - this.ctx.getLog().debug("Unresolved versions: {}", unresolvedWork); - return this.Effect() - .reply(cmd.replyTo(), unresolvedWork); - } - ) - .onCommand(GitCommand.MarkVersionAsResolved.class, (state, cmd) -> this.Effect() - .persist(new GitEvent.CommitResolved(cmd.coordinates(), cmd.commit())) - .thenRun(() -> this.scheduledRefresh.tell(new ScheduledCommitResolver.Refresh())) - .thenReply(cmd.replyTo(), ns -> Done.done()) - ) - .onCommand(GitCommand.MarkVersionAsUnresolveable.class, (state, cmd) -> this.Effect() - .persist(new GitEvent.CommitUnresolvable(cmd.coordinates(), cmd.commitSha())) - .thenRun(() -> this.scheduledRefresh.tell(new ScheduledCommitResolver.Refresh())) - .thenReply(cmd.replyTo(), ns -> Done.done())) - .onCommand(GitCommand.RegisterRawCommit.class, (state, cmd) -> this.Effect() - .persist(new GitEvent.CommitRegistered(cmd.coordinates(), cmd.commitSha())) - .thenRun(() -> this.scheduledRefresh.tell(new ScheduledCommitResolver.Refresh())) - .thenReply(cmd.replyTo(), ns -> Done.done()) - ) - ; - return builder.build(); - } - - @Override - public EventHandler eventHandler() { - final var builder = this.newEventHandlerBuilder(); - builder.forAnyState() - .onEvent( - GitEvent.RepositoryRegistered.class, - (state, event) -> state.withRepository(event.repository()) - ) - .onEvent( - GitEvent.CommitResolved.class, - (state, event) -> state.withResolvedVersion(event.coordinates(), event.resolvedCommit()) - ) - .onEvent( - GitEvent.CommitRegistered.class, - (state, event) -> state.withRawCommit(event.coordinates(), event.commit()) - ).onEvent( - GitEvent.CommitUnresolvable.class, - (state, event) -> state.withUnresolvedVersion(event.coordinates(), event.commit()) - ) - ; - return builder.build(); - } - - @Override - public RetentionCriteria retentionCriteria() { - return RetentionCriteria.snapshotEvery(10, 2); - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/domain/GitState.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/domain/GitState.java deleted file mode 100644 index 520ccc3a..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/domain/GitState.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.gitmanaged.domain; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.HashMap; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import io.vavr.collection.TreeMap; -import org.apache.maven.artifact.versioning.ComparableVersion; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.api.models.VersionedCommit; - -import java.net.URI; -import java.util.Comparator; -import java.util.Optional; - -@JsonDeserialize -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, - property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(GitState.Empty.class), - @JsonSubTypes.Type(GitState.Registered.class) -}) -public sealed interface GitState extends Jsonable { - - Comparator LATEST_COMPARATOR = Comparator.comparing( - m -> new ComparableVersion(m.version)) - .reversed(); - - GitState withRepository(URI repository); - - List repositories(); - - GitCommand.UnresolvedWork unresolvedVersions(); - - GitState withResolvedVersion( - MavenCoordinates coordinates, final VersionedCommit commit - ); - - GitState withRawCommit(MavenCoordinates coordinates, String commitSha); - - GitState withUnresolvedVersion(MavenCoordinates coordinates, final String commit); - - @JsonTypeName("empty") - final record Empty() implements GitState { - @JsonCreator - public Empty { - } - - @Override - public GitState withRepository(final URI repository) { - return new Registered( - List.of(repository), TreeMap.empty(LATEST_COMPARATOR), - HashMap.empty() - ); - } - - @Override - public List repositories() { - return List.empty(); - } - - @Override - public GitCommand.UnresolvedWork unresolvedVersions() { - return GitCommand.EMPTY; - } - - @Override - public GitState withResolvedVersion( - final MavenCoordinates coordinates, - final VersionedCommit commit - ) { - return new Registered( - List.empty(), - TreeMap.empty(LATEST_COMPARATOR), - HashMap.of(coordinates, commit) - ); - } - - @Override - public GitState withRawCommit(final MavenCoordinates coordinates, final String commitSha) { - return new Registered( - List.empty(), - TreeMap.of(LATEST_COMPARATOR, coordinates, Optional.of(commitSha)), - HashMap.empty() - ); - } - - @Override - public GitState withUnresolvedVersion( - final MavenCoordinates coordinates, - final String commit - ) { - return new Registered( - List.empty(), - TreeMap.of(LATEST_COMPARATOR, coordinates, Optional.of(commit)), - HashMap.empty(), - HashMap.of(coordinates, commit) - ); - } - } - - @JsonTypeName("registered") - final record Registered( - List repository, - Map> commits, - Map resolved, - Map unresolvable - ) implements GitState { - @JsonCreator - public Registered { - } - - public Registered( - final List repository, - final Map> commits, - final Map resolved - ) { - this(repository, commits, resolved, HashMap.empty()); - } - - @Override - public GitState withRepository(final URI repository) { - final var repos = this.repository.toSet().add(repository).toList(); - // Reset unresolved commits by re-merging with the existing set of commits - final var newCommits = this.commits.merge( - this.unresolvable.mapValues(Optional::of), (a, b) -> a.isEmpty() ? b : a); - return new Registered(repos, newCommits, this.resolved, HashMap.empty()); - } - - @Override - public List repositories() { - return this.repository; - } - - @Override - public GitCommand.UnresolvedWork unresolvedVersions() { - if (this.repository.isEmpty()) { - return GitCommand.EMPTY; - } - final var unresolvedCommits = this.commits.filterValues(Optional::isPresent) - .mapValues(Optional::get); - // Try to get the latest to work with in the first place - final var versionsWithCommits = unresolvedCommits - .toSortedMap(LATEST_COMPARATOR.reversed(), t -> t._1, t -> t._2) - .take(16); - return new GitCommand.UnresolvedWork(versionsWithCommits, this.repository); - } - - @Override - public GitState withResolvedVersion( - final MavenCoordinates coordinates, - final VersionedCommit commit - ) { - if (this.resolved.containsKey(coordinates)) { - return this; - } - final var newCommits = this.commits.remove(coordinates); - final var newResolved = this.resolved.put(coordinates, commit); - final var newUnresolved = this.unresolvable.remove(coordinates); - return new Registered(this.repository, newCommits, newResolved, newUnresolved); - } - - @Override - public GitState withRawCommit(final MavenCoordinates coordinates, final String commitSha) { - if (this.resolved.containsKey(coordinates)) { - return this; - } - final var newCommits = this.commits.put(coordinates, Optional.of(commitSha)); - return new Registered(this.repository, newCommits, this.resolved); - } - - @Override - public GitState withUnresolvedVersion( - final MavenCoordinates coordinates, - final String commit - ) { - if (this.resolved.containsKey(coordinates)) { - return this; - } - if (this.unresolvable.containsKey(coordinates)) { - return this; - } - final var newUnresolvable = this.unresolvable.put(coordinates, commit); - final var newCommits = this.commits.remove(coordinates); - return new Registered( - this.repository, - newCommits, - this.resolved, - newUnresolvable - ); - } - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/ActorLoggerPrinterWriter.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/ActorLoggerPrinterWriter.java deleted file mode 100644 index 0123f08c..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/ActorLoggerPrinterWriter.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.gitmanaged.util.jgit; - -import org.eclipse.jgit.lib.BatchingProgressMonitor; -import org.eclipse.jgit.lib.ProgressMonitor; -import org.slf4j.Logger; - -import java.util.Objects; - -public final class ActorLoggerPrinterWriter extends BatchingProgressMonitor implements ProgressMonitor { - private final Logger logger; - - public ActorLoggerPrinterWriter(Logger logger) { - this.logger = logger; - } - - @Override - protected void onUpdate(final String taskName, final int workCurr) { - StringBuilder s = new StringBuilder(); - format(s, taskName, workCurr); - if (this.logger.isDebugEnabled()) { - this.logger.debug(s.toString()); - } - } - - private void format(StringBuilder s, String taskName, int workCurr) { - s.append("\r"); //$NON-NLS-1$ - s.append(taskName); - s.append(": "); //$NON-NLS-1$ - while (s.length() < 25) - s.append(' '); - s.append(workCurr); - } - @Override - protected void onEndTask(final String taskName, final int workCurr) { - StringBuilder s = new StringBuilder(); - format(s, taskName, workCurr); - if (this.logger.isDebugEnabled()) { - this.logger.debug(s.toString()); - } - } - - @Override - protected void onUpdate(final String taskName, final int workCurr, final int workTotal, final int percentDone) { - StringBuilder s = new StringBuilder(); - format(s, taskName, workCurr, workTotal, percentDone); - } - - @Override - protected void onEndTask(final String taskName, final int workCurr, final int workTotal, final int percentDone) { - StringBuilder s = new StringBuilder(); - format(s, taskName, workCurr, workTotal, percentDone); - } - - private void format(StringBuilder s, String taskName, int cmp, - int totalWork, int pcnt) { - s.append("\r"); //$NON-NLS-1$ - s.append(taskName); - s.append(": "); //$NON-NLS-1$ - while (s.length() < 25) - s.append(' '); - - String endStr = String.valueOf(totalWork); - StringBuilder curStr = new StringBuilder(String.valueOf(cmp)); - while (curStr.length() < endStr.length()) - curStr.insert(0, " "); //$NON-NLS-1$ - if (pcnt < 100) - s.append(' '); - if (pcnt < 10) - s.append(' '); - s.append(pcnt); - s.append("% ("); //$NON-NLS-1$ - s.append(curStr); - s.append("/"); //$NON-NLS-1$ - s.append(endStr); - s.append(")"); //$NON-NLS-1$ - } - - @Override - public boolean equals(Object obj) { - if (obj == this) return true; - if (obj == null || obj.getClass() != this.getClass()) return false; - var that = (ActorLoggerPrinterWriter) obj; - return Objects.equals(this.logger, that.logger); - } - - @Override - public int hashCode() { - return Objects.hash(logger); - } - - @Override - public String toString() { - return "ActorLoggerPrinterWriter[" + - "logger=" + logger + ']'; - } - -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/AssetCommitResolver.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/AssetCommitResolver.java deleted file mode 100644 index b6c7d9d2..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/AssetCommitResolver.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.gitmanaged.util.jgit; - -import akka.NotUsed; -import akka.actor.typed.javadsl.ActorContext; -import akka.japi.Pair; -import akka.stream.javadsl.Flow; -import akka.stream.javadsl.Source; -import io.vavr.CheckedFunction1; -import io.vavr.Tuple3; -import io.vavr.collection.List; -import io.vavr.control.Try; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.SubmoduleConfig; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.transport.TagOpt; -import org.slf4j.Logger; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.api.models.VersionedCommit; - -import java.net.URI; -import java.nio.file.Path; -import java.time.Instant; -import java.util.Collections; -import java.util.Optional; - -public final class AssetCommitResolver { - - - static Source startCommitResolution( - final ActorContext ctx, - final CommitResolutionManager.ArtifactRepoState newState, - final CommitResolutionManager.ResolveCommitDetails request - ) { - final var log = ctx.getLog(); - final var checkedOut = newState.checkedOut(); - final var commitSha = request.commit(); - return Source.from(Collections.singleton(ObjectId.fromString(commitSha))) - .mapConcat(objectId -> checkedOut.map(tuple -> tuple.append(objectId))) - .async() - .via(tryResolvingCommitFromGitDirectory(request.coordinates(), log)) - .async() - .via(parseResponse(request)); - } - - sealed interface CommitResolutionResponse { - } - - record CommitResolved( - URI repo, VersionedCommit commit - ) implements CommitResolutionResponse { - } - - public record CommitNotFound(MavenCoordinates coordinates, List uris, String commit) implements CommitResolutionResponse { - } - - - static Flow>, CommitResolutionResponse, NotUsed> parseResponse( - CommitResolutionManager.ResolveCommitDetails request - ) { - return Flow.>, CommitResolutionResponse>fromFunction( - opt -> - opt.map(pair -> new CommitResolved(pair.first(), pair.second())) - .orElseGet(() -> new CommitNotFound(request.coordinates(), request.gitRepo(), request.commit())) - ); - } - - static Flow, Optional>, NotUsed> tryResolvingCommitFromGitDirectory( - MavenCoordinates coordinates, Logger log - ) { - return Flow., Optional>>fromFunction(tuple3 -> { - if (log.isDebugEnabled()) { - log.debug("Resolving commit {} for {}", tuple3._3.getName(), coordinates); - log.debug("Opening Git directory {}", tuple3._2); - } - final var directory = tuple3._2().toFile(); - final var git = Git.open(directory); - final var fetchCmd = git.fetch() - .setRecurseSubmodules(SubmoduleConfig.FetchRecurseSubmodulesMode.YES) - .setTagOpt(TagOpt.FETCH_TAGS) - .setRemoveDeletedRefs(false); - fetchCmd.call(); - - return Try.withResources(() -> new RevWalk(git.getRepository())) - .of(tryFetchAndResolveCommit(coordinates, log, tuple3._3)) - .map(convertCommitToVersionedCommit(log, tuple3._1)) - .map(Optional::of) - .getOrElseGet(t -> { - log.error("Failed to resolve commit {} for {}", tuple3._3.getName(), coordinates); - return Optional.empty(); - }); - }); - } - - static CheckedFunction1 tryFetchAndResolveCommit( - MavenCoordinates coordinates, Logger log, ObjectId objectId - ) { - return revWalk -> { - final var commit = revWalk.lookupCommit(objectId); - revWalk.parseBody(commit); - if (log.isTraceEnabled()) { - log.trace("Commit Body Parsed {}", commit); - } - revWalk.parseHeaders(commit); - if (log.isTraceEnabled()) { - log.trace("Commit Headers Parsed {}", commit.getShortMessage()); - } - final var commitTime = commit.getCommitTime(); - if (log.isTraceEnabled()) { - log.trace("Commit Revision {}", commit.getCommitTime()); - } - if (log.isDebugEnabled()) { - log.debug( - "{} at {} has {}", coordinates, objectId.name(), commitTime); - } - return commit; - }; - } - - static java.util.function.Function> convertCommitToVersionedCommit( - Logger log, URI repo - ) { - return commit -> { - if (log.isTraceEnabled()) { - log.trace("Commit Resolved {}", commit); - } - final var commitMessage = commit.getShortMessage(); - final var commitBody = commit.getFullMessage(); - final var commitId = commit.getId().getName(); - final var commitDate = commit.getCommitTime(); - final var instant = Instant.ofEpochSecond(commitDate); - final var committerIdent = commit.getCommitterIdent(); - final var committer = new VersionedCommit.Commiter( - committerIdent.getName(), committerIdent.getEmailAddress()); - final var authorIdent = commit.getAuthorIdent(); - final var author = new VersionedCommit.Author( - authorIdent.getName(), authorIdent.getEmailAddress()); - final var timeZone = committerIdent - .getTimeZone(); - final var commitDateTime = instant.atZone(timeZone.toZoneId()); - final var commitUrl = URI.create(repo.toString() + "/commit/" + commitId); - return Pair.apply(repo, new VersionedCommit( - commitMessage, - commitBody, - commitId, - author, - committer, - Optional.of(commitUrl), - commitDateTime - )); - }; - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/CommitResolutionManager.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/CommitResolutionManager.java deleted file mode 100644 index 71e77786..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/CommitResolutionManager.java +++ /dev/null @@ -1,410 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.gitmanaged.util.jgit; - -import akka.Done; -import akka.NotUsed; -import akka.actor.typed.ActorRef; -import akka.actor.typed.Behavior; -import akka.actor.typed.SupervisorStrategy; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.receptionist.ServiceKey; -import akka.japi.function.Function2; -import akka.stream.Materializer; -import akka.stream.javadsl.Flow; -import akka.stream.javadsl.Sink; -import akka.stream.javadsl.Source; -import akka.stream.typed.javadsl.ActorSink; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.HashMap; -import io.vavr.collection.HashSet; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import io.vavr.collection.Set; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.synchronizer.actor.CommitDetailsRegistrar; - -import java.net.URI; -import java.nio.file.Path; -import java.time.Duration; -import java.util.UUID; -import java.util.function.Predicate; - -/** - * An {@link akka.actor.Actor} that accepts a Versioned Artifact with a commit - * sha to resolve the - * underlying Git Commit information (like the commit message, author, etc.) as - * the sha's are being resolved. - */ -public final class CommitResolutionManager { - - public static final ServiceKey SERVICE_KEY = ServiceKey.create(Command.class, "commit-resolver"); - - - @JsonDeserialize - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, - property = "type") - @JsonSubTypes({ - @JsonSubTypes.Type(ResolveCommitDetails.class), - }) - public sealed interface Command extends Jsonable { - } - - @JsonTypeName("resolve-commit-details") - public record ResolveCommitDetails( - MavenCoordinates coordinates, - String commit, - List gitRepo, - ActorRef replyTo - ) implements Command { - - @JsonCreator - public ResolveCommitDetails { - } - } - - public static Behavior resolveCommit(ActorRef registrar) { - return Behaviors.setup(ctx -> { - final var clonerBehavior = Behaviors.supervise(RepositoryCloner.cloner()) - .onFailure(SupervisorStrategy.restart()); - StubSystemReader.init(); - final var uuid = UUID.randomUUID(); - final var cloner = ctx.spawn(clonerBehavior, "repository-cloner-" + uuid); - final var materializer = Materializer.createMaterializer(ctx); - return awaiting(cloner, registrar, materializer, ClonedRepoState.EMPTY); - }); - } - - private static Behavior awaiting( - final ActorRef cloner, - final ActorRef registrar, - final Materializer materializer, - final ClonedRepoState state - ) { - return Behaviors.setup(ctx -> Behaviors.receive(Command.class) - .onMessage(ResolveCommitDetails.class, msg -> { - if (ctx.getLog().isTraceEnabled()) { - ctx.getLog().trace("Resolving commit details for {}", msg.coordinates); - } - final var repoState = state.repositoriesCloned.get(msg.coordinates.asArtifactCoordinates()); - if (repoState.isEmpty()) { - if (msg.gitRepo.isEmpty()) { - msg.replyTo.tell(Done.done()); - return Behaviors.same(); - } - if (ctx.getLog().isDebugEnabled()) { - ctx.getLog().debug("[{}] Cloning {}", msg.coordinates, msg.gitRepo); - } - ctx.ask( - RepositoryCloner.CloneResponse.class, - cloner, - Duration.ofMinutes(20), - replyTo -> new RepositoryCloner.CloneRepos( - msg.coordinates.asArtifactCoordinates(), msg.gitRepo, replyTo), - handleCloneResponse(ctx, msg, state) - ); - return waitingForClones(cloner, registrar, materializer, state, List.empty()); - } - final var artifactRepoState = repoState.get(); - final var unclonedRepos = artifactRepoState.repositories.filter(Predicate.not(artifactRepoState.checkedOut::containsKey)); - final var newUncloned = msg.gitRepo.filter(Predicate.not(artifactRepoState.checkedOut::containsKey)); - final var allUncloned = unclonedRepos.addAll(newUncloned); - if (!allUncloned.isEmpty()) { - if (ctx.getLog().isDebugEnabled()) { - ctx.getLog().debug("[{}] Cloning {}", msg.coordinates.asStandardCoordinates(), allUncloned); - } - ctx.ask( - RepositoryCloner.CloneResponse.class, - cloner, - Duration.ofMinutes(20), - replyTo -> new RepositoryCloner.CloneRepos( - msg.coordinates.asArtifactCoordinates(), allUncloned.toList(), replyTo), - handleCloneResponse(ctx, msg, state) - ); - - return waitingForClones(cloner, registrar, materializer, state, List.empty()); - } - - startCommitResolution(materializer, ctx, msg, artifactRepoState); - - return waitingForResolution(cloner, registrar, materializer, state, List.empty()); - }) - .build() - ); - } - - private static Behavior waitingForClones( - final ActorRef cloner, - final ActorRef registrar, - final Materializer materializer, - final ClonedRepoState state, - final List queue - ) { - return Behaviors.setup(ctx -> Behaviors.receive(Command.class) - .onMessage( - ResolveCommitDetails.class, - msg -> waitingForClones(cloner, registrar, materializer, state, queue.append(msg)) - ) - .onMessage(FailedCheckout.class, msg -> { - msg.msg.replyTo.tell(Done.done()); - if (queue.isEmpty()) { - return awaiting(cloner, registrar, materializer, state); - } - final var head = queue.head(); - ctx.ask( - RepositoryCloner.CloneResponse.class, - cloner, - Duration.ofMinutes(20), - replyTo -> new RepositoryCloner.CloneRepos( - head.coordinates.asArtifactCoordinates(), head.gitRepo, replyTo), - handleCloneResponse(ctx, head, state) - ); - queue.tail(); - return waitingForClones(cloner, registrar, materializer, state, queue); - }) - .onMessage(RepositoriesClonedReadyToResolve.class, msg -> { - if (msg.checkedOut.isEmpty()) { - ctx.getLog().warn( - "[{}] No repositories successfully checked out with {}", msg.msg.coordinates, msg.msg.gitRepo); - msg.msg.replyTo.tell(Done.done()); - return completeResolution(cloner, registrar, materializer, state, queue, ctx); - } - final ClonedRepoState newState = state.withClonedRepos(msg); - final var request = msg.msg; - final var repos = newState.repositoriesCloned.get( - request.coordinates.asArtifactCoordinates()).get(); - startCommitResolution(materializer, ctx, request, repos); - - return waitingForResolution(cloner, registrar, materializer, newState, queue); - }) - .build()); - } - - private static Behavior waitingForResolution( - final ActorRef cloner, - final ActorRef registrar, - final Materializer materializer, - final ClonedRepoState state, - final List queue - ) { - return Behaviors.setup(ctx -> Behaviors.receive(Command.class) - .onMessage( - ResolveCommitDetails.class, - msg -> waitingForResolution(cloner, registrar, materializer, state, queue.append(msg)) - ) - .onMessage(RepositoriesClonedReadyToResolve.class, msg -> { - final ClonedRepoState newState = state.withClonedRepos(msg); - return waitingForResolution(cloner, registrar, materializer, newState, queue.append(msg.msg)); - }) - .onMessage(WrappedCommitResolutionResult.class, msg -> { - final var request = msg.msg; - final var result = msg.result; - if (result instanceof AssetCommitResolver.CommitNotFound notFound) { - registrar.tell( - new CommitDetailsRegistrar.CommitNotFound( - notFound.uris().head(), - notFound.commit(), - request.coordinates, - msg.msg.replyTo - ) - ); - } else if (result instanceof AssetCommitResolver.CommitResolved resolved) { - if (ctx.getLog().isTraceEnabled()) { - ctx.getLog().info("[{}] Resolved commit {}", request.coordinates, resolved.commit().link()); - } - final var commit = resolved.commit(); - - registrar.tell( - new CommitDetailsRegistrar.HandleVersionedCommitReport(resolved.repo(), commit, - request.coordinates, - request.replyTo - )); - } - return Behaviors.same(); - }) - .onMessage(CompletedResolutionAttempts.class, msg -> { - ctx.getLog().info("[{}] Completed commit {} resolution", msg.msg.coordinates.asStandardCoordinates(), msg.msg.commit); - msg.msg.replyTo.tell(Done.done()); - return completeResolution(cloner, registrar, materializer, state, queue, ctx); - }) - .onMessage(ExceptionallyCompletedResultion.class, msg -> { - msg.msg.replyTo.tell(Done.done()); - return completeResolution(cloner, registrar, materializer, state, queue, ctx); - }) - .build() - ); - } - - private static Behavior completeResolution( - ActorRef cloner, ActorRef registrar, - Materializer materializer, ClonedRepoState state, List queue, ActorContext ctx - ) { - if (queue.isEmpty()) { - return awaiting(cloner, registrar, materializer, state); - } - final var head = queue.head(); - final var artifactCoordinates = head.coordinates.asArtifactCoordinates(); - if (state.repositoriesCloned.get(artifactCoordinates).isEmpty()) { - ctx.ask( - RepositoryCloner.CloneResponse.class, - cloner, - Duration.ofMinutes(20), - replyTo -> new RepositoryCloner.CloneRepos( - head.coordinates.asArtifactCoordinates(), head.gitRepo, replyTo), - handleCloneResponse(ctx, head, state) - ); - return waitingForClones(cloner, registrar, materializer, state, queue.tail()); - } - final var artifactRepoStates = state.repositoriesCloned.get(artifactCoordinates).get(); - - startCommitResolution(materializer, ctx, head, artifactRepoStates); - - return waitingForResolution(cloner, registrar, materializer, state, queue.tail()); - } - - - private static Function2 handleCloneResponse( - ActorContext ctx, ResolveCommitDetails msg, ClonedRepoState empty - ) { - return (reply, throwable) -> { - if (throwable != null) { - ctx.getLog().error("Failed to clone repo", throwable); - return new FailedCheckout(msg, empty); - } - if (reply instanceof RepositoryCloner.SuccessfullyCloned sc) { - return new RepositoriesClonedReadyToResolve( - msg, - new ClonedRepoState(HashMap.empty()), - sc.checkedOut() - ); - } - return new FailedCheckout(msg, empty); - }; - } - - private static void startCommitResolution( - Materializer materializer, ActorContext ctx, ResolveCommitDetails head, - ArtifactRepoState artifactRepoStates - ) { - final var flow = interpretResult(head); - final Source via = AssetCommitResolver.startCommitResolution(ctx, artifactRepoStates, head) - .async() - .via(flow); - final Sink sink = ActorSink.actorRef( - ctx.getSelf(), - new CompletedResolutionAttempts(head), - t -> new ExceptionallyCompletedResultion(head, t) - ); - via.to(sink).run(materializer); - } - - private static Flow interpretResult( - ResolveCommitDetails msg - ) { - return Flow.fromFunction( - response -> new WrappedCommitResolutionResult( - msg, response)); - } - - @JsonDeserialize - public record ArtifactRepoState( - Set repositories, - Map checkedOut, - Set inUse - ) implements Jsonable { - @JsonCreator - public ArtifactRepoState { - } - } - - @JsonDeserialize - public record ClonedRepoState( - Map repositoriesCloned - ) implements Jsonable { - static final ClonedRepoState EMPTY = new ClonedRepoState(HashMap.empty()); - - @JsonCreator - public ClonedRepoState { - } - - public ClonedRepoState withClonedRepos(RepositoriesClonedReadyToResolve msg) { - final var coordinates = msg.msg.coordinates.asArtifactCoordinates(); - - if (this.repositoriesCloned.containsKey(coordinates)) { - return this; - } - final var artifactState = new ArtifactRepoState( - msg.msg.gitRepo.toSet(), - msg.checkedOut, - msg.checkedOut.keySet() - ); - final var newRepositories = this.repositoriesCloned.put(coordinates, artifactState); - return new ClonedRepoState(newRepositories); - } - - public ClonedRepoState reset() { - return new ClonedRepoState(this.repositoriesCloned.mapValues(s -> new ArtifactRepoState( - s.repositories, - s.checkedOut, - HashSet.empty() - ))); - } - } - - private record RepositoriesClonedReadyToResolve( - ResolveCommitDetails msg, - ClonedRepoState state, - Map checkedOut - ) implements Command { - } - - private record FailedCheckout(ResolveCommitDetails msg, ClonedRepoState state) implements Command { - } - - private record WrappedCommitResolutionResult( - ResolveCommitDetails msg, - AssetCommitResolver.CommitResolutionResponse result - ) implements Command { - } - - private record CompletedResolutionAttempts( - ResolveCommitDetails msg - ) implements Command { - } - - private record ExceptionallyCompletedResultion( - ResolveCommitDetails msg, - Throwable error - ) implements Command { - } - -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/FileWalkerConsumer.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/FileWalkerConsumer.java deleted file mode 100644 index 5b2a56be..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/FileWalkerConsumer.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.gitmanaged.util.jgit; - -import akka.actor.typed.ActorRef; -import io.vavr.CheckedConsumer; - -import java.io.IOException; -import java.nio.file.FileVisitResult; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.function.Function; - -public final class FileWalkerConsumer extends SimpleFileVisitor { - - private final CheckedConsumer acceptFile; - - public static FileWalkerConsumer tell(final ActorRef ref, Function messageCreator) { - return new FileWalkerConsumer(path -> ref.tell(messageCreator.apply(path))); - } - - public FileWalkerConsumer(final CheckedConsumer acceptFile) { - this.acceptFile = acceptFile; - } - - @Override - public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { - try { - acceptFile.accept(file); - } catch (Throwable e) { - throw new IOException(e); - } - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException { - try { - acceptFile.accept(dir); - } catch (Throwable e) { - throw new IOException(e); - } - return FileVisitResult.CONTINUE; - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/RepositoryCloner.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/RepositoryCloner.java deleted file mode 100644 index 62af456e..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/RepositoryCloner.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.gitmanaged.util.jgit; - -import akka.NotUsed; -import akka.actor.typed.ActorRef; -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.japi.Pair; -import akka.stream.Materializer; -import akka.stream.javadsl.Flow; -import akka.stream.javadsl.Sink; -import akka.stream.javadsl.Source; -import akka.stream.typed.javadsl.ActorSink; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.HashMap; -import io.vavr.collection.HashSet; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import io.vavr.collection.Set; -import io.vavr.control.Either; -import io.vavr.control.Try; -import org.eclipse.jgit.api.Git; -import org.slf4j.Logger; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; - -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.UUID; -import java.util.function.Function; - -public final class RepositoryCloner { - - sealed interface CloneCommand extends Jsonable { - } - - sealed interface CloneResponse extends Jsonable { - } - - record CloneRepos( - ArtifactCoordinates coordinates, - List urls, - ActorRef replyTo - ) implements CloneCommand { - } - - public static Behavior cloner() { - return Behaviors.setup(ctx -> { - final var materializer = Materializer.createMaterializer(ctx); - return waiting(materializer); - }); - } - - private static Behavior waiting(Materializer materializer) { - return Behaviors.setup(ctx -> Behaviors.receive(CloneCommand.class) - .onMessage(CloneRepos.class, msg -> { - callClone(ctx, materializer, msg); - return cloning(new CloneState(msg, msg.coordinates), materializer, List.empty()); - }) - .build()); - } - - record SuccessfullyCloned( - ArtifactCoordinates coordinates, - Map checkedOut - ) implements CloneResponse { - } - - record PartialClone( - ArtifactCoordinates coordinates, - Map checkedOut - ) implements CloneResponse { - } - - private record CloneState( - CloneRepos command, - ArtifactCoordinates coordinates, - Set completed, - Map checkedOut, - Set failed - ) { - - public CloneState(CloneRepos msg, ArtifactCoordinates coordinates) { - this(msg, coordinates, HashSet.empty(), HashMap.empty(), HashSet.empty()); - } - - CloneState withFailed(URI uri) { - if (this.completed.contains(uri)) { - return this; - } - return new CloneState( - this.command, - this.coordinates, - this.completed.add(uri), - this.checkedOut, - this.failed.add(uri) - ); - } - - CloneState withSucceeded(URI repo, Path path) { - if (this.completed.contains(repo)) { - return this; - } - return new CloneState( - this.command, - this.coordinates, - this.completed.add(repo), - this.checkedOut.put(repo, path), - this.failed - ); - } - } - - - private static Behavior cloning(CloneState state, Materializer materializer, List queue) { - return Behaviors.setup(ctx -> Behaviors.receive(CloneCommand.class) - .onMessage(CloneRepos.class, msg -> cloning(state, materializer, queue.append(msg))) - .onMessage(CloneFailed.class, msg -> { - ctx.getLog().warn(String.format("[%s] Clone failed for %s", msg.coordinates, msg.repo), msg.cause); - final var newState = state.withFailed(msg.repo); - return cloning(newState, materializer, queue); - }) - .onMessage(CloneSucceeded.class, msg -> { - final var newState = state.withSucceeded(msg.repo, msg.path); - if (ctx.getLog().isDebugEnabled()) { - ctx.getLog().debug("Clone Succeeded for {}", msg.repo); - } - return cloning(newState, materializer, queue); - }) - .onMessage(CloneFailedCompletion.class, msg -> { - ctx.getLog().warn("[{}] Clone failed for {}", msg.coordinates, msg.cause); - state.command.replyTo.tell(new PartialClone(state.coordinates, state.checkedOut)); - return cloneNextOrWait(materializer, queue, ctx); - }) - .onMessage(CloneCompleted.class, msg -> { - ctx.getLog().info("Clone completed for {}", state.coordinates); - state.command.replyTo.tell(new SuccessfullyCloned(state.coordinates, state.checkedOut)); - return cloneNextOrWait(materializer, queue, ctx); - }) - .build()); - } - - private static Behavior cloneNextOrWait( - final Materializer materializer, final List queue, - final ActorContext ctx - ) { - if (queue.isEmpty()) { - return waiting(materializer); - } - final var head = queue.head(); - final var nextState = new CloneState( - head, - head.coordinates - ); - callClone(ctx, materializer, head); - return cloning(nextState, materializer, queue.tail()); - } - - private static void callClone(ActorContext ctx, Materializer materializer, CloneRepos head) { - final Source urls = Source.from(head.urls); - final var log = ctx.getLog(); - final Flow flow = Flow.fromFunction(url -> cloneRepo(log, head.coordinates, url) - .map(path -> { - if (log.isDebugEnabled()) { - log.debug("[{}] Cloned {} to {}", head.coordinates.asMavenString(), url, path); - } - return new CloneSucceeded(head.coordinates, url, path); - }) - .mapLeft(uri -> { - if (log.isDebugEnabled()) { - log.debug("[{}] Clone failed for {}", head.coordinates.asMavenString(), uri); - } - return new CloneFailed(head.coordinates, uri.first(), uri.second()); - }) - .fold(Function.identity(), Function.identity()) - ); - final Sink sink = ActorSink.actorRef( - ctx.getSelf(), new CloneCompleted(head.coordinates), - t -> new CloneFailedCompletion(head.coordinates, t) - ); - urls - .via(flow) - .to(sink) - .run(materializer); - } - - - private record CloneFailed( - ArtifactCoordinates coordinates, - URI repo, - Throwable cause - ) implements CloneCommand { - } - - private record CloneFailedCompletion( - ArtifactCoordinates coordinates, - Throwable cause - ) implements CloneCommand { - - } - - private record CloneSucceeded( - ArtifactCoordinates coordinates, - URI repo, - Path path - ) implements CloneCommand { - } - - private record CloneCompleted(ArtifactCoordinates coordinates) implements CloneCommand { - } - - private static Either, Path> cloneRepo( - final Logger log, - final ArtifactCoordinates coordinates, - final URI remoteRepo - ) { - final var tempdirPrefix = String.format( - "soad-%s-%s", coordinates.artifactId(), - UUID.randomUUID() - ); - final var repoDirectory = Try.of(() -> Files.createTempDirectory( - tempdirPrefix - )); - - if (log.isTraceEnabled()) { - log.trace("Preparing directory for checkout {}", repoDirectory); - } - final var writer = new ActorLoggerPrinterWriter(log); - final var cloneCommand = repoDirectory.mapTry(Path::toFile) - .map(directory -> Git.cloneRepository() - .setDirectory(directory) - .setProgressMonitor(writer) - .setCloneAllBranches(false) - .setCloneSubmodules(true) - .setURI(remoteRepo.toString()) - ); - if (log.isTraceEnabled()) { - log.trace("Checking out {} to {}", remoteRepo, repoDirectory); - } - return cloneCommand.mapTry(org.eclipse.jgit.api.CloneCommand::call) - .flatMapTry(repo -> repoDirectory) - .toEither() - .mapLeft(t -> Pair.create(remoteRepo, t)); - } - -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/StubSystemReader.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/StubSystemReader.java deleted file mode 100644 index 01232b26..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/gitmanaged/util/jgit/StubSystemReader.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.gitmanaged.util.jgit; - -import org.eclipse.jgit.lib.Config; -import org.eclipse.jgit.storage.file.FileBasedConfig; -import org.eclipse.jgit.util.FS; -import org.eclipse.jgit.util.SystemReader; - -import java.io.File; -import java.net.URL; - -public class StubSystemReader extends SystemReader { - - private static final class Holder { - private static final URL dummyGitConfig = Holder.class.getClassLoader().getResource( - "soad.gitconfig"); - private static final File gitConfig = new File(dummyGitConfig.getFile()); - static final SystemReader INSTANCE = new StubSystemReader(gitConfig); - static { - SystemReader.setInstance(INSTANCE); - } - } - - public static SystemReader getInstance() { - return Holder.INSTANCE; - } - - public static void init() { - final var instance = getInstance(); - if (SystemReader.getInstance() != instance) { - SystemReader.setInstance(instance); - } - } - - private static final SystemReader proxy = SystemReader.getInstance(); - private final File userGitConfig; - - public StubSystemReader(File userGitConfig) { - super(); - this.userGitConfig = userGitConfig; - } - - @Override - public String getenv(String variable) { - return proxy.getenv(variable); - } - - @Override - public String getHostname() { - return proxy.getHostname(); - } - - @Override - public String getProperty(String key) { - return proxy.getProperty(key); - } - - @Override - public long getCurrentTime() { - return proxy.getCurrentTime(); - } - - @Override - public int getTimezone(long when) { - return proxy.getTimezone(when); - } - - @Override - public FileBasedConfig openUserConfig(Config parent, FS fs) { - return new FileBasedConfig(parent, userGitConfig, fs); - } - - // Return an empty system configuration, based on example in SystemReader.Default#openSystemConfig - @Override - public FileBasedConfig openSystemConfig(Config parent, FS fs) { - return new FileBasedConfig(parent, this.userGitConfig, fs) { - @Override - public void load() { - } - - @Override - public boolean isOutdated() { - return false; - } - }; - } - - @Override - public FileBasedConfig openJGitConfig(final Config parent, final FS fs) { - return new FileBasedConfig(parent, this.userGitConfig, fs) { - @Override - public void load() { - } - - @Override - public boolean isOutdated() { - return false; - } - }; - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/RequestArtifactsToSync.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/RequestArtifactsToSync.java deleted file mode 100644 index b787fbd3..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/RequestArtifactsToSync.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.resync; - -import akka.actor.typed.ActorRef; -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.Behaviors; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.List; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.ArtifactService; -import org.spongepowered.downloads.artifact.api.query.GetArtifactsResponse; - -public class RequestArtifactsToSync { - - @JsonDeserialize - @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION) - public sealed interface Command extends Jsonable { - } - - public record GatherGroupArtifacts( - String groupCoordinates, - ActorRef replyTo - ) implements Command { - } - - private record WrappedArtifactsToSync( - ArtifactsToSync result, - ActorRef replyTo - ) implements Command { - } - - public record ArtifactsToSync( - List artifactsNeeded - ) { - } - - public static Behavior create( - final ArtifactService artifactService - ) { - return Behaviors.setup(ctx -> Behaviors.receive(RequestArtifactsToSync.Command.class) - .onMessage(GatherGroupArtifacts.class, (g) -> { - final var listCompletableFuture = artifactService.getArtifacts(g.groupCoordinates).invoke() - .thenApply(artifactsResponse -> { - if (!(artifactsResponse instanceof GetArtifactsResponse.ArtifactsAvailable a)) { - return List.empty(); - } - return a.artifactIds() - .map(id -> new ArtifactCoordinates(g.groupCoordinates, id)); - }); - ctx.pipeToSelf(listCompletableFuture, (ok, exception) -> { - if (exception == null) { - return new WrappedArtifactsToSync(new ArtifactsToSync(ok), g.replyTo); - } - return new WrappedArtifactsToSync(new ArtifactsToSync(List.empty()), g.replyTo); - }); - return Behaviors.same(); - }) - .onMessage(WrappedArtifactsToSync.class, (w) -> { - w.replyTo.tell(w.result); - return Behaviors.same(); - }) - .build()); - } - -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/ResyncExtension.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/ResyncExtension.java deleted file mode 100644 index a058f185..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/ResyncExtension.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.resync; - -import akka.actor.AbstractExtensionId; -import akka.actor.ExtendedActorSystem; -import akka.actor.ExtensionIdProvider; - -public class ResyncExtension extends AbstractExtensionId - implements ExtensionIdProvider { - public static final ResyncExtension SettingsProvider = new ResyncExtension(); - - @Override - public ResyncSettings createExtension(final ExtendedActorSystem system) { - return new ResyncSettings(system.settings().config().getConfig("systemofadownload.synchronizer.worker.resync")); - } - - @Override - public ResyncExtension lookup() { - return SettingsProvider; - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/ResyncManager.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/ResyncManager.java deleted file mode 100644 index 5251200a..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/ResyncManager.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.resync; - -import akka.Done; -import akka.NotUsed; -import akka.actor.typed.Behavior; -import akka.actor.typed.DispatcherSelector; -import akka.actor.typed.SupervisorStrategy; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.javadsl.Routers; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.stream.javadsl.Flow; -import akka.stream.javadsl.Sink; -import akka.stream.javadsl.Source; -import akka.stream.typed.javadsl.ActorFlow; -import io.vavr.collection.List; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.ArtifactService; -import org.spongepowered.downloads.artifact.api.Group; -import org.spongepowered.downloads.artifact.api.query.GroupsResponse; -import org.spongepowered.synchronizer.SynchronizerSettings; -import org.spongepowered.synchronizer.actor.ArtifactSyncWorker; - -import java.time.Duration; -import java.util.UUID; -import java.util.concurrent.CompletionStage; - -public final class ResyncManager { - - sealed interface Resync { - } - - record PerformResync() implements Resync { - } - - record ArtifactsToSync(List artifacts) implements Resync { - } - - public static Behavior create( - final ArtifactService artifactService, - final SynchronizerSettings.VersionSync versionSync - ) { - return Behaviors.withTimers(t -> { - t.startTimerWithFixedDelay("resync", new PerformResync(), versionSync.interval); - t.startSingleTimer("start", new PerformResync(), versionSync.startupDelay); - return setup(artifactService); - }); - } - - private static Behavior setup( - final ArtifactService artifactService - ) { - return Behaviors.setup(ctx -> { - final var sharding = ClusterSharding.get(ctx.getSystem()); - final var dispatch = DispatcherSelector.defaultDispatcher(); - final var requester = ctx.spawn( - Behaviors.supervise(RequestArtifactsToSync.create(artifactService)).onFailure( - SupervisorStrategy.restart()), - String.format("requester-%s-%d", UUID.randomUUID(), System.currentTimeMillis()), - dispatch - ); - final var artifactSyncPool = Routers.pool( - 4, - Behaviors.supervise(ArtifactSyncWorker.create(sharding)) - .onFailure( - SupervisorStrategy.restartWithBackoff(Duration.ofSeconds(1), Duration.ofMinutes(10), 0.2)) - ); - final var syncWorker = ctx.spawn( - artifactSyncPool, - String.format("pooled-artifact-synchronization-workers-%s-%d", UUID.randomUUID(), System.currentTimeMillis()), - dispatch - ); - final var requestFlow = ActorFlow.ask( - requester, - Duration.ofSeconds(30), - RequestArtifactsToSync.GatherGroupArtifacts::new - ); - - final var registrationFlow = ActorFlow.ask( - syncWorker, - Duration.ofMinutes(20), - ArtifactSyncWorker.PerformResync::new - ); - return awaiting(artifactService, requestFlow, registrationFlow); - }); - } - - private static Behavior awaiting( - final ArtifactService artifactService, - final Flow requestFlow, - final Flow registrationFlow - ) { - return Behaviors.setup(ctx -> Behaviors.receive(Resync.class) - .onMessage(ArtifactsToSync.class, result -> { - Source.from(result.artifacts) - .async() - .via(registrationFlow.async()) - .runWith(Sink.ignore(), ctx.getSystem()); - return Behaviors.same(); - }) - .onMessage(PerformResync.class, g -> { - final var makeRequest = artifactService.getGroups() - .invoke() - .thenApply(groups -> ((GroupsResponse.Available) groups).groups()) - .thenCompose(groups -> { - final Sink, CompletionStage>> fold = Sink.fold( - List.empty(), List::appendAll); - return Source.from(groups.map(Group::groupCoordinates).asJava()) - .async() - .via(requestFlow) - .map(RequestArtifactsToSync.ArtifactsToSync::artifactsNeeded) - .runWith(fold, ctx.getSystem()); - }); - ctx.pipeToSelf(makeRequest, (ok, exception) -> { - if (exception != null) { - ctx.getLog().error("Failed to process sync", exception); - } - return new ArtifactsToSync(ok); - }); - return Behaviors.same(); - }) - .build()); - - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/ResyncSettings.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/ResyncSettings.java deleted file mode 100644 index 85533b3f..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/ResyncSettings.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.resync; - -import akka.actor.Extension; -import com.typesafe.config.Config; - -import java.time.Duration; -import java.util.concurrent.TimeUnit; - -public class ResyncSettings implements Extension { - public final String repository; - public final Duration timeout; - public final int retryCount; - public final String agentName; - - public ResyncSettings(Config config) { - this.repository = config.getString("repository"); - this.retryCount = config.getInt("retry"); - this.agentName = config.getString("agent-name"); - this.timeout = Duration.ofSeconds(config.getDuration("timeout", TimeUnit.SECONDS)); - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/domain/ArtifactSynchronizerAggregate.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/domain/ArtifactSynchronizerAggregate.java deleted file mode 100644 index 7bb8d69d..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/domain/ArtifactSynchronizerAggregate.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.resync.domain; - -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.cluster.sharding.typed.javadsl.EntityContext; -import akka.cluster.sharding.typed.javadsl.EntityTypeKey; -import akka.persistence.typed.PersistenceId; -import akka.persistence.typed.javadsl.CommandHandlerWithReply; -import akka.persistence.typed.javadsl.EventHandler; -import akka.persistence.typed.javadsl.EventSourcedBehaviorWithEnforcedReplies; -import akka.persistence.typed.javadsl.ReplyEffect; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import io.vavr.collection.List; -import io.vavr.control.Try; -import io.vavr.jackson.datatype.VavrModule; -import org.spongepowered.downloads.maven.artifact.ArtifactMavenMetadata; -import org.spongepowered.synchronizer.resync.ResyncExtension; -import org.spongepowered.synchronizer.resync.ResyncSettings; - -import javax.xml.stream.XMLInputFactory; -import java.io.Serial; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.util.StringJoiner; -import java.util.concurrent.CompletableFuture; - -public final class ArtifactSynchronizerAggregate - extends EventSourcedBehaviorWithEnforcedReplies { - public static EntityTypeKey ENTITY_TYPE_KEY = EntityTypeKey.create(Command.class, "ArtifactSynchronizer"); - private final ActorContext ctx; - private final ResyncSettings settings; - private final HttpClient httpClient; - private final XmlMapper mapper; - - public ArtifactSynchronizerAggregate( - final EntityContext context, final ActorContext ctx, - final ResyncSettings settings - ) { - super( - // PersistenceId needs a typeHint (or namespace) and entityId, - // we take then from the EntityContext - PersistenceId.of( - context.getEntityTypeKey().name(), // <- type hint - context.getEntityId() // <- business id - )); - this.ctx = ctx; - this.settings = settings; - final var mapper = new XmlMapper(); - mapper.registerModule(new VavrModule()); - this.httpClient = HttpClient.newBuilder() - .connectTimeout(this.settings.timeout) - .version(HttpClient.Version.HTTP_2) - .build(); - this.mapper = mapper; - } - - public static Behavior create(EntityContext context) { - return Behaviors.setup(ctx -> { - final ResyncSettings settings = ResyncExtension.SettingsProvider.get(ctx.getSystem()); - - return new ArtifactSynchronizerAggregate(context, ctx, settings); - }); - } - - @Override - public SyncState emptyState() { - return SyncState.EMPTY; - } - - @Override - public EventHandler eventHandler() { - final var builder = newEventHandlerBuilder() - .forAnyState() - .onEvent( - SynchronizeEvent.SynchronizedArtifacts.class, - (event) -> new SyncState(event.updatedTime(), event.metadata()) - ); - return builder.build(); - } - - @Override - public CommandHandlerWithReply commandHandler() { - final var builder = this.newCommandHandlerWithReplyBuilder() - .forAnyState() - .onCommand(Command.Resync.class, this::handleResync) - .onCommand(Command.WrappedResync.class, this::handleResponse); - return builder.build(); - } - - private ReplyEffect handleResponse(SyncState state, Command.WrappedResync cmd) { - if (cmd.response() instanceof Command.Failed) { - return this.Effect().reply(cmd.replyTo(), List.empty()); - } - if (cmd.response() instanceof Command.Completed c) { - final var metadata = c.metadata(); - if (metadata.versioning().lastUpdated.equals(state.lastUpdated)) { - final var versionsToSync = state.versions.versioning() - .versions.map(state.coordinates()::version); - return this.Effect() - .reply(cmd.replyTo(), versionsToSync); - } - return this.Effect() - .persist(new SynchronizeEvent.SynchronizedArtifacts(metadata, metadata.versioning().lastUpdated)) - .thenReply(cmd.replyTo(), s -> s.versions.versioning().versions.map(s.coordinates()::version)); - } - return this.Effect().reply(cmd.replyTo(), List.empty()); - } - - - private ReplyEffect handleResync(SyncState state, Command.Resync cmd) { - final var groupId = !state.groupId.equals(cmd.coordinates().groupId()) - ? cmd.coordinates().groupId() - : state.groupId; - final var artifactId = !state.artifactId.equals(cmd.coordinates().artifactId()) - ? cmd.coordinates().artifactId() - : state.artifactId; - ctx.pipeToSelf( - getArtifactMetadata(groupId, artifactId), - (response, throwable) -> { - if (throwable != null) { - if (throwable instanceof UnsupportedArtifactException ua) { - this.ctx.getLog().warn( - String.format("Unsupported artifact %s", state.coordinates()), - throwable - ); - } else { - this.ctx.getLog().error( - String.format("Unable to get maven-metadata.xml for artifact: %s", state.coordinates()), - throwable - ); - } - return new Command.WrappedResync(new Command.Failed(), cmd.replyTo()); - } - return new Command.WrappedResync(new Command.Completed(response), cmd.replyTo()); - } - ); - return this.Effect().noReply(); - } - - private static final class UnsupportedArtifactException extends Exception { - - - @Serial private static final long serialVersionUID = 4579607644429804821L; - - public UnsupportedArtifactException(final String artifact, final String url) { - super(String.format("Unsupported artifact by id %s using url: %s", artifact, url)); - } - } - - private CompletableFuture getArtifactMetadata(String groupId, String artifactId) { - final var url = new StringJoiner("/", this.settings.repository, "") - .add(groupId.replace(".", "/")) - .add(artifactId) - .add("maven-metadata.xml") - .toString(); - return Try.of(() -> URI.create(url)) - .map(uri -> HttpRequest.newBuilder() - .uri(uri) - .header("User-Agent", this.settings.agentName) - .build()) - .toCompletableFuture() - .thenCompose(request -> this.httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()) - .thenCompose(response -> Try.of(() -> response) - .filterTry( - r -> r.statusCode() == 200, - () -> new UnsupportedArtifactException(groupId + ":" + artifactId, url) - ) - .toCompletableFuture() - ) - .thenApply(HttpResponse::body) - .thenApply(is -> Try.of(() -> XMLInputFactory.newFactory().createXMLStreamReader(is)).get()) - .thenCompose(reader -> Try.of( - () -> this.mapper.readValue(reader, ArtifactMavenMetadata.class)).toCompletableFuture()) - ); - } - -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/domain/Command.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/domain/Command.java deleted file mode 100644 index 8a36be72..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/domain/Command.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.resync.domain; - -import akka.actor.typed.ActorRef; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.List; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.maven.artifact.ArtifactMavenMetadata; - -@JsonDeserialize -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = Command.Resync.class, name = "resync"), - @JsonSubTypes.Type(value = Command.Failed.class, name = "failed"), - @JsonSubTypes.Type(value = Command.WrappedResync.class, name = "wrapped-resync"), - @JsonSubTypes.Type(value = Command.Completed.class, name = "completed"), -}) -public interface Command extends Jsonable { - @JsonDeserialize - record Resync( - ArtifactCoordinates coordinates, - ActorRef> replyTo - ) implements Command { - @JsonCreator - public Resync {} - - } - - record Completed(ArtifactMavenMetadata metadata) implements Response { - @JsonCreator - public Completed { - } - } - - @JsonDeserialize - sealed interface Response extends Command { - } - - @JsonDeserialize - record Failed() implements Response { - @JsonCreator - public Failed { - } - } - - record WrappedResync( - Response response, - ActorRef> replyTo - ) implements Response { - @JsonCreator - public WrappedResync { - } - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/domain/SyncState.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/domain/SyncState.java deleted file mode 100644 index a8cdbaff..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/domain/SyncState.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.resync.domain; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.maven.artifact.ArtifactMavenMetadata; -import org.spongepowered.downloads.maven.artifact.Versioning; - -@JsonDeserialize -final class SyncState implements Jsonable { - - public final String groupId; - public final String artifactId; - public final String lastUpdated; - public final ArtifactMavenMetadata versions; - - @JsonIgnore - static final SyncState EMPTY = new SyncState("", new ArtifactMavenMetadata("", "", new Versioning())); - - @JsonCreator - public SyncState( - final String lastUpdated, - final ArtifactMavenMetadata versions - ) { - this.lastUpdated = lastUpdated; - this.versions = versions; - this.groupId = versions.groupId(); - this.artifactId = versions.artifactId(); - } - - ArtifactCoordinates coordinates() { - return new ArtifactCoordinates(this.groupId, this.artifactId); - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/domain/SynchronizeEvent.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/domain/SynchronizeEvent.java deleted file mode 100644 index ea505c28..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/resync/domain/SynchronizeEvent.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.resync.domain; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.javadsl.persistence.AggregateEvent; -import com.lightbend.lagom.javadsl.persistence.AggregateEventShards; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTag; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTagger; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.maven.artifact.ArtifactMavenMetadata; - -interface SynchronizeEvent extends Jsonable, AggregateEvent { - - AggregateEventShards TAG = AggregateEventTag.sharded(SynchronizeEvent.class, 3); - - @Override - default AggregateEventTagger aggregateTag() { - return TAG; - } - - @JsonDeserialize - record SynchronizedArtifacts( - ArtifactMavenMetadata metadata, - String updatedTime - ) implements SynchronizeEvent { - @JsonCreator - public SynchronizedArtifacts {} - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/ArtifactConsumer.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/ArtifactConsumer.java deleted file mode 100644 index 1fddd092..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/ArtifactConsumer.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.versionsync; - -import akka.Done; -import akka.NotUsed; -import akka.actor.typed.DispatcherSelector; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Routers; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.stream.javadsl.Flow; -import akka.stream.typed.javadsl.ActorFlow; -import org.spongepowered.downloads.artifact.api.ArtifactService; -import org.spongepowered.downloads.artifact.api.event.GroupUpdate; -import org.spongepowered.downloads.auth.api.utils.AuthUtils; -import org.spongepowered.downloads.versions.api.VersionsService; -import org.spongepowered.synchronizer.SonatypeSynchronizer; -import org.spongepowered.synchronizer.SynchronizerSettings; -import org.spongepowered.synchronizer.actor.ArtifactSyncWorker; - -public final class ArtifactConsumer { - public static void subscribeToArtifactUpdates( - final ActorContext context, - final ArtifactService artifactService, - final VersionsService versionsService, - final ClusterSharding clusterSharding, - final SynchronizerSettings settings - ) { - // region Synchronize Artifact Versions from Maven - final AuthUtils auth = AuthUtils.configure(context.getSystem().settings().config()); - ArtifactVersionSyncModule.setup(context, clusterSharding, auth, versionsService); - - final var syncWorkerBehavior = ArtifactSyncWorker.create(clusterSharding); - final var pool = Routers.pool( - settings.reactiveSync.poolSize, - syncWorkerBehavior - ); - - final var registrationRef = context.spawn( - pool, - "group-event-subscriber", - DispatcherSelector.defaultDispatcher() - ); - final Flow actorAsk = ActorFlow.ask( - settings.reactiveSync.parallelism, - registrationRef, settings.reactiveSync.timeOut, - (g, b) -> { - if (!(g instanceof GroupUpdate.ArtifactRegistered a)) { - return new ArtifactSyncWorker.Ignored(b); - } - return new ArtifactSyncWorker.PerformResync(a.coordinates(), b); - } - ); - artifactService.groupTopic() - .subscribe() - .atLeastOnce(actorAsk); - // endregion - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/ArtifactVersionSyncEntity.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/ArtifactVersionSyncEntity.java deleted file mode 100644 index e4675ae9..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/ArtifactVersionSyncEntity.java +++ /dev/null @@ -1,307 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.versionsync; - -import akka.Done; -import akka.actor.typed.ActorRef; -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.javadsl.Routers; -import akka.actor.typed.javadsl.TimerScheduler; -import akka.cluster.sharding.typed.javadsl.EntityTypeKey; -import akka.pattern.CircuitBreakerOpenException; -import akka.persistence.typed.PersistenceId; -import akka.persistence.typed.RecoveryCompleted; -import akka.persistence.typed.javadsl.CommandHandlerWithReply; -import akka.persistence.typed.javadsl.EventHandler; -import akka.persistence.typed.javadsl.EventSourcedBehaviorWithEnforcedReplies; -import akka.persistence.typed.javadsl.ReplyEffect; -import akka.persistence.typed.javadsl.RetentionCriteria; -import akka.persistence.typed.javadsl.SignalHandler; -import io.vavr.collection.List; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.auth.api.utils.AuthUtils; -import org.spongepowered.downloads.versions.api.VersionsService; -import org.spongepowered.downloads.versions.api.models.VersionRegistration; - -import java.time.Duration; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.TimeoutException; -import java.util.function.BiFunction; -import java.util.function.Supplier; - -public class ArtifactVersionSyncEntity - extends EventSourcedBehaviorWithEnforcedReplies { - - public static final EntityTypeKey ENTITY_TYPE_KEY = EntityTypeKey.create( - SyncRegistration.class, "artifact-version-sync"); - - private final ActorContext ctx; - private final TimerScheduler timers; - private final AuthUtils auth; - private final VersionsService service; - private final ActorRef batchSync; - - public static Behavior create( - final AuthUtils auth, final VersionsService service, final PersistenceId persistenceId - ) { - - return Behaviors.setup(ctx -> { - final var router = Routers.group(BatchVersionSyncManager.KEY); - final var ref = ctx.spawnAnonymous(router); - return Behaviors.withTimers( - timers -> new ArtifactVersionSyncEntity(ctx, timers, auth, service, persistenceId, ref)); - }); - } - - public ArtifactVersionSyncEntity( - final ActorContext ctx, - final TimerScheduler timers, - final AuthUtils auth, - final VersionsService service, - final PersistenceId persistenceId, - final ActorRef ref - ) { - super(persistenceId); - this.ctx = ctx; - this.timers = timers; - this.auth = auth; - this.service = service; - this.batchSync = ref; - } - - @Override - public VersionRegistrationState emptyState() { - return new VersionRegistrationState.Empty(); - } - - @Override - public EventHandler eventHandler() { - final var builder = this.newEventHandlerBuilder(); - builder.forAnyState() - .onEvent( - VersionSyncEvent.RegisteredBatch.class, - (state, evt) -> { - this.batchSync.tell(new BatchVersionSyncManager.ArtifactToSync(evt.artifact())); - return state.acceptBatch(evt.artifact(), evt.coordinates()); - } - ) - .onEvent(VersionSyncEvent.RegisteredVersion.class, (state, evt) -> { - this.batchSync.tell( - new BatchVersionSyncManager.ArtifactToSync(evt.coordinates().asArtifactCoordinates())); - return state.acceptVersion(evt.coordinates()); - }) - .onEvent(VersionSyncEvent.ResolvedVersion.class, (state, evt) -> { - this.batchSync.tell( - new BatchVersionSyncManager.ArtifactToSync(evt.coordinates().asArtifactCoordinates())); - return state.resolvedVersion(evt.coordinates()); - }) - .onEvent(VersionSyncEvent.StartedBatchRegistration.class, (state, evt) -> state.startBatch(evt.batched())) - .onEvent(VersionSyncEvent.FailedVersion.class, (state, evt) -> state.failedVersion(evt.coordinates())) - ; - return builder.build(); - } - - @Override - public CommandHandlerWithReply commandHandler() { - final var builder = this.newCommandHandlerWithReplyBuilder(); - builder.forAnyState() - .onCommand(SyncRegistration.Register.class, (state, cmd) -> { - if (state.hasVersion(cmd.coordinates())) { - return this.Effect() - .noReply(); - } - return this.Effect() - .persist(new VersionSyncEvent.RegisteredVersion(cmd.coordinates())) - .thenRun( - () -> this.timers.startSingleTimer( - cmd.coordinates(), SyncRegistration.Timeout.INSTANCE, Duration.ofSeconds(1))) - .thenNoReply(); - }) - .onCommand(SyncRegistration.MarkRegistered.class, (state, cmd) -> this.Effect() - .persist(new VersionSyncEvent.ResolvedVersion(cmd.coordinates())) - .thenRun(this::checkIfStillHasPending) - .thenNoReply() - ) - .onCommand(SyncRegistration.DelayRegistration.class, (state, cmd) -> { - this.timers.startSingleTimer( - cmd.coordinates(), new SyncRegistration.RetryFailed(cmd.coordinates()), cmd.duration()); - return this.Effect().noReply(); - }) - .onCommand(SyncRegistration.RetryFailed.class, (state, cmd) -> { - final var request = this.createRegistrationRequest(cmd.coordinates()); - this.ctx.pipeToSelf(request.serviceCall().get(), request.onComplete()::apply); - return this.Effect() - .noReply(); - }) - .onCommand(SyncRegistration.Refresh.class, (state, cmd) -> { - final List pending = state.getPending(); - if (state.isActive() && !pending.isEmpty()) { - if (this.ctx.getLog().isDebugEnabled()) { - this.ctx.getLog().debug("Still awaiting for versions to complete registration: {}", pending); - } - pending.forEachWithIndex((coordinates, index) -> this.timers.startSingleTimer(coordinates, - new SyncRegistration.RetryFailed(coordinates), Duration.ofSeconds((int) (1 + (0.2 * index))) - )); - this.timers.startSingleTimer("refresh", SyncRegistration.Refresh.INSTANCE, Duration.ofSeconds(20 + pending.size())); - return this.Effect().noReply(); - } - return this.registerVersionsInBatches(state); - }) - .onCommand(SyncRegistration.AlreadyRegistered.class, (state, cmd) -> this.Effect() - .persist(new VersionSyncEvent.ResolvedVersion(cmd.coordinates())) - .thenRun(this::checkIfStillHasPending) - .thenNoReply() - ) - .onCommand(SyncRegistration.GroupUnregistered.class, (state, cmd) -> this.Effect() - .persist(new VersionSyncEvent.FailedVersion(cmd.coordinates())) - .thenRun(this::checkIfStillHasPending) - .thenNoReply() - ) - .onCommand(SyncRegistration.SyncBatch.class, (state, cmd) -> this.Effect() - .persist(new VersionSyncEvent.RegisteredBatch(cmd.artifact(), cmd.coordinates())) - .thenRun(() -> this.timers.startSingleTimer("timeout", SyncRegistration.Timeout.INSTANCE, - Duration.ofSeconds(1) - )) - .thenReply(cmd.replyTo(), ns -> Done.done())) - .onCommand(SyncRegistration.Timeout.class, (state, cmd) -> this.registerVersionsInBatches(state)) - ; - return builder.build(); - } - - private void checkIfStillHasPending(VersionRegistrationState state) { - // Go ahead and ask the state to update for the next batch - if (!state.isActive()) { - if (this.ctx.getLog().isTraceEnabled()) { - this.ctx.getLog().trace("No more pending versions, scheduling a refresh in 500 milliseconds"); - } - this.timers.startSingleTimer("refresh", SyncRegistration.Refresh.INSTANCE, Duration.ofMillis(500)); - } else { - if (this.ctx.getLog().isTraceEnabled()) { - this.ctx.getLog().trace("State is currently active, {}", state.getPending()); - } - } - } - - private ReplyEffect registerVersionsInBatches( - VersionRegistrationState state - ) { - if (state.isActive() && !state.getPending().isEmpty()) { - final var pending = state.getPending(); - this.ctx.getLog().warn("Resubmitting version registration due to pending versions: {}", pending); - pending.map(this::createRegistrationRequest).forEach( - p -> this.ctx.pipeToSelf(p.serviceCall().get(), p.onComplete::apply)); - return this.Effect().noReply(); - } - if (!state.getPending().isEmpty()) { - if (this.ctx.getLog().isDebugEnabled()) { - this.ctx.getLog().debug("Rescheduling Refresh due to Pending registrations: {}", state.getPending()); - } - this.timers.startSingleTimer("refresh", SyncRegistration.Refresh.INSTANCE, Duration.ofSeconds(2)); - this.batchSync.tell(new BatchVersionSyncManager.ArtifactToSync(state.coordinates())); - return this.Effect().noReply(); - } - final List batched = state.getNextBatch(); - if (batched.isEmpty()) { - if (this.ctx.getLog().isTraceEnabled()) { - this.ctx.getLog().trace("No more pending batches"); - } - return this.Effect().noReply(); - } - final var map = batched.map(this::createRegistrationRequest); - return this.Effect() - .persist(new VersionSyncEvent.StartedBatchRegistration(batched)) - .thenRun(ns -> map.forEach(p -> this.ctx.pipeToSelf(p.serviceCall().get(), p.onComplete()::apply))) - .thenRun( - ns -> this.timers.startSingleTimer("refresh", SyncRegistration.Refresh.INSTANCE, Duration.ofSeconds(2))) - .thenRun(ns -> this.batchSync.tell(new BatchVersionSyncManager.ArtifactToSync(ns.coordinates()))) - .thenNoReply(); - } - - private VersionRegistrationParams createRegistrationRequest(MavenCoordinates c) { - - final var serviceCallSupplier = (Supplier>) () -> this.auth.internalAuth( - this.service.registerArtifactCollection(c.groupId, c.artifactId)) - .invoke(new VersionRegistration.Register.Version(c)) - .toCompletableFuture(); - final var onComplete = (BiFunction) (ok, failure) -> { - if (failure != null) { - if (failure instanceof CompletionException ce) { - failure = ce.getCause(); - } - if (failure instanceof CircuitBreakerOpenException cbe) { - this.ctx.getLog().warn("Circuit breaker is open, delaying registration of {}", c); - return new SyncRegistration.DelayRegistration( - c, Duration.ofMillis(cbe.remainingDuration().toMillis()).plus(Duration.ofSeconds(4))); - } else if (failure instanceof TimeoutException) { - ctx.getLog().warn("Rescheduling asset registration for {}", c); - return new SyncRegistration.DelayRegistration(c, Duration.ofSeconds(2)); - } - ctx.getLog().error( - String.format( - "Received error trying to synchronize %s", - c.asStandardCoordinates() - ), failure); - return new SyncRegistration.RetryFailed(c); - } - if (ok instanceof VersionRegistration.Response.ArtifactAlreadyRegistered a) { - ctx.getLog().trace("Redundant registration of {}", a.coordinates()); - return new SyncRegistration.AlreadyRegistered(c); - } else if (ok instanceof VersionRegistration.Response.GroupMissing gm) { - ctx.getLog().error("Group missing for {}", gm.groupId()); - return new SyncRegistration.GroupUnregistered(c); - } else if (ok instanceof VersionRegistration.Response.RegisteredArtifact r) { - ctx.getLog().trace("Successful registration of {}", r.mavenCoordinates()); - return new SyncRegistration.MarkRegistered(c); - } - ctx.getLog().warn("Failed registration synchronizing {} with response {}", c, ok); - return new SyncRegistration.RetryFailed(c); - }; - return new VersionRegistrationParams(serviceCallSupplier, onComplete); - } - - record VersionRegistrationParams( - Supplier> serviceCall, - BiFunction onComplete - ) { - } - - @Override - public SignalHandler signalHandler() { - final var builder = this.newSignalHandlerBuilder(); - // Enable restarting our timers - builder.onSignal(RecoveryCompleted.class, (state, signal) -> { - this.timers.startSingleTimer("refresh", SyncRegistration.Refresh.INSTANCE, Duration.ofMillis(500)); - }); - return super.signalHandler(); - } - - @Override - public RetentionCriteria retentionCriteria() { - return RetentionCriteria.snapshotEvery(100, 2); - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/ArtifactVersionSyncModule.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/ArtifactVersionSyncModule.java deleted file mode 100644 index b8f1658a..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/ArtifactVersionSyncModule.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.versionsync; - -import akka.actor.typed.SupervisorStrategy; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.cluster.sharding.typed.javadsl.Entity; -import akka.persistence.typed.PersistenceId; -import org.spongepowered.downloads.auth.api.utils.AuthUtils; -import org.spongepowered.downloads.versions.api.VersionsService; -import org.spongepowered.synchronizer.SonatypeSynchronizer; - -public class ArtifactVersionSyncModule { - - public static void setup( - final ActorContext ctx, - final ClusterSharding sharding, - final AuthUtils authUtils, - final VersionsService service - ) { - ctx.spawnAnonymous(Behaviors.supervise(BatchVersionSyncManager.setup()) - .onFailure(SupervisorStrategy.resume())); - sharding.init(Entity.of( - ArtifactVersionSyncEntity.ENTITY_TYPE_KEY, - context -> ArtifactVersionSyncEntity.create(authUtils, service, - PersistenceId.of( - context.getEntityTypeKey().name(), - context.getEntityId() - ) - ) - )); - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/BatchVersionSyncManager.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/BatchVersionSyncManager.java deleted file mode 100644 index e277da25..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/BatchVersionSyncManager.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.versionsync; - -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.receptionist.Receptionist; -import akka.actor.typed.receptionist.ServiceKey; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.HashSet; -import io.vavr.collection.Set; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; - -import java.time.Duration; - -public final class BatchVersionSyncManager { - - static final ServiceKey KEY = ServiceKey.create(Command.class, "batch-version-sync"); - - interface Command extends Jsonable { - } - - public record ArtifactToSync(ArtifactCoordinates coordinates) implements Command { - } - - public enum Refresh implements Command { - INSTANCE - } - - public static Behavior setup() { - return Behaviors.setup(ctx -> { - ctx.getSystem().receptionist().tell(Receptionist.register(KEY, ctx.getSelf())); - return timedSync(HashSet.empty()); - }); - } - - private static Behavior timedSync(Set coordinates) { - return Behaviors.setup(ctx -> Behaviors.withTimers(timers -> { - timers.startSingleTimer("refresh", Refresh.INSTANCE, Duration.ofSeconds(20)); - return Behaviors.receive(Command.class) - .onMessage(ArtifactToSync.class, msg -> { - ctx.getLog().info("Received artifact to sync: {}", msg.coordinates); - if (coordinates.contains(msg.coordinates)) { - return Behaviors.same(); - } - final var sharding = ClusterSharding.get(ctx.getSystem()); - sharding.entityRefFor(ArtifactVersionSyncEntity.ENTITY_TYPE_KEY, msg.coordinates.asMavenString()) - .tell(SyncRegistration.Refresh.INSTANCE); - return timedSync(coordinates.add(msg.coordinates)); - }) - .onMessage(Refresh.class, msg -> { - if (ctx.getLog().isDebugEnabled()) { - ctx.getLog().debug("Refreshing all artifacts: {}", coordinates); - } - final var sharding = ClusterSharding.get(ctx.getSystem()); - coordinates.forEach( - c -> sharding.entityRefFor(ArtifactVersionSyncEntity.ENTITY_TYPE_KEY, c.asMavenString()).tell( - SyncRegistration.Refresh.INSTANCE) - ); - timers.startSingleTimer("refresh", Refresh.INSTANCE, Duration.ofMinutes(1)); - return Behaviors.same(); - }) - .build(); - })); - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/SyncRegistration.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/SyncRegistration.java deleted file mode 100644 index ac626136..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/SyncRegistration.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.versionsync; - -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.List; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -import java.time.Duration; - -@JsonDeserialize -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(SyncRegistration.Register.class), - @JsonSubTypes.Type(SyncRegistration.SyncBatch.class), - @JsonSubTypes.Type(SyncRegistration.AlreadyRegistered.class), - @JsonSubTypes.Type(SyncRegistration.Timeout.class), - @JsonSubTypes.Type(SyncRegistration.GroupUnregistered.class), - @JsonSubTypes.Type(SyncRegistration.MarkRegistered.class), - @JsonSubTypes.Type(SyncRegistration.Refresh.class), - @JsonSubTypes.Type(SyncRegistration.RetryFailed.class) -}) -public sealed interface SyncRegistration extends Jsonable { - - @JsonTypeName("register") - record Register(MavenCoordinates coordinates) implements SyncRegistration { - public Register { - } - } - - @JsonTypeName("register-batch") - record SyncBatch(ArtifactCoordinates artifact, List coordinates, akka.actor.typed.ActorRef replyTo) implements SyncRegistration { - - } - - @JsonTypeName("stop-batch") - enum Timeout implements SyncRegistration { - INSTANCE; - } - - @JsonTypeName("update-batch") - enum Refresh implements SyncRegistration { - INSTANCE; - } - - @JsonTypeName("duplicate-registration") - record AlreadyRegistered(MavenCoordinates coordinates) implements SyncRegistration { - } - - @JsonTypeName("failed-registration") - record RetryFailed(MavenCoordinates coordinates) implements SyncRegistration { - } - - @JsonTypeName("delay-retry-registration") - record DelayRegistration(MavenCoordinates coordinates, Duration duration) implements SyncRegistration { - - } - - @JsonTypeName("group-missing") - record GroupUnregistered(MavenCoordinates coordinates) implements SyncRegistration { - } - - @JsonTypeName("successful-registration") - record MarkRegistered(MavenCoordinates coordinates) implements SyncRegistration { - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/VersionRegistrationState.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/VersionRegistrationState.java deleted file mode 100644 index dd3ec5b3..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/VersionRegistrationState.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.versionsync; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.HashMap; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -@JsonDeserialize -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, - property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = VersionRegistrationState.Empty.class, - name = "empty"), - @JsonSubTypes.Type(value = VersionRegistrationState.Registered.class, - name = "registered") -}) -public sealed interface VersionRegistrationState extends Jsonable { - - ArtifactCoordinates coordinates(); - - default boolean isActive() { - return false; - } - - VersionRegistrationState acceptBatch( - final ArtifactCoordinates artifact, final List coordinates - ); - - VersionRegistrationState acceptVersion(MavenCoordinates coordinates); - - default boolean hasVersion(MavenCoordinates coordinates) { - return false; - } - - default List getNextBatch() { - return List.empty(); - } - - VersionRegistrationState resolvedVersion(MavenCoordinates coordinates); - - default List getPending() { - return List.empty(); - } - - VersionRegistrationState startBatch(List batched); - - VersionRegistrationState failedVersion(MavenCoordinates coordinates); - - @JsonDeserialize - record Empty() implements VersionRegistrationState { - @JsonCreator - public Empty { - } - - @Override - public ArtifactCoordinates coordinates() { - return new ArtifactCoordinates("", ""); - } - - @Override - public VersionRegistrationState acceptBatch( - final ArtifactCoordinates artifact, - final List coordinates - ) { - return new Registered(artifact, coordinates.toMap(c -> c.version, (c) -> Registration.UNREGISTERED)); - } - - @Override - public VersionRegistrationState acceptVersion(final MavenCoordinates coordinates) { - return new Registered( - coordinates.asArtifactCoordinates(), - HashMap.of(coordinates.version, Registration.UNREGISTERED) - ); - } - - @Override - public VersionRegistrationState resolvedVersion(final MavenCoordinates coordinates) { - return new Registered( - coordinates.asArtifactCoordinates(), - HashMap.of(coordinates.version, Registration.REGISTERED) - ); - } - - @Override - public VersionRegistrationState startBatch( - final List batched - ) { - return new Active( - batched.head().asArtifactCoordinates(), - batched.toMap(c -> c.version, (c) -> Registration.UNREGISTERED), - batched - ); - } - - @Override - public VersionRegistrationState failedVersion(final MavenCoordinates coordinates) { - return new Registered(coordinates.asArtifactCoordinates(), HashMap.of(coordinates.version, Registration.UNREGISTERED)); - } - } - - @JsonDeserialize - enum Registration { - REGISTERED, - UNREGISTERED; - } - - @JsonDeserialize - record Registered( - ArtifactCoordinates coordinates, - Map versions - ) implements VersionRegistrationState { - @JsonCreator - public Registered { - } - - @Override - public boolean isActive() { - return false; - } - - @Override - public boolean hasVersion(final MavenCoordinates coordinates) { - return this.versions.containsKey(coordinates.version); - } - - @Override - public VersionRegistrationState acceptBatch( - final ArtifactCoordinates artifact, - final List coordinates - ) { - return new Registered( - this.coordinates, - this.versions.merge( - coordinates.toMap(c -> c.version, (c) -> Registration.UNREGISTERED), - (existing, registration) -> existing - ) - ); - } - - @Override - public VersionRegistrationState acceptVersion(final MavenCoordinates coordinates) { - if (this.versions.containsKey(coordinates.version)) { - return this; - } - return new Registered( - this.coordinates, - this.versions.put(coordinates.version, Registration.UNREGISTERED) - ); - } - - @Override - public List getNextBatch() { - return this.versions.filterValues(registration -> registration == Registration.UNREGISTERED) - .keySet() - .map(this.coordinates::version) - .toList() - .sorted() - .take(10); - } - - @Override - public VersionRegistrationState resolvedVersion(final MavenCoordinates coordinates) { - return new Registered( - this.coordinates, - this.versions.put(coordinates.version, Registration.REGISTERED) - ); - } - - @Override - public VersionRegistrationState startBatch( - final List batched - ) { - final var toBatch = batched.filter( - c -> this.versions.getOrElse(c.version, Registration.UNREGISTERED) == Registration.UNREGISTERED); - return new Active( - this.coordinates, - this.versions.merge( - toBatch.toMap(c -> c.version, (c) -> Registration.UNREGISTERED), - (existing, registration) -> existing - ), - toBatch - ); - } - - @Override - public VersionRegistrationState failedVersion(final MavenCoordinates coordinates) { - if (this.versions.containsKey(coordinates.version)) { - return this; - } - return new Registered( - this.coordinates, - this.versions.put(coordinates.version, Registration.UNREGISTERED) - ); - } - } - - record Active( - ArtifactCoordinates coordinates, - Map versions, - List queue - ) implements VersionRegistrationState { - @Override - public boolean isActive() { - return true; - } - - @Override - public boolean hasVersion(final MavenCoordinates coordinates) { - return this.versions.containsKey(coordinates.version); - } - - @Override - public VersionRegistrationState resolvedVersion(final MavenCoordinates coordinates) { - final var pending = this.queue.remove(coordinates); - final var updated = this.versions.put(coordinates.version, Registration.REGISTERED); - if (pending.isEmpty()) { - return new Registered( - this.coordinates, - updated - ); - } - return new Active( - this.coordinates, - updated, - pending - ); - } - - @Override - public VersionRegistrationState acceptBatch( - final ArtifactCoordinates artifact, - final List coordinates - ) { - return new Active( - this.coordinates, - this.versions.merge( - coordinates.toMap(c -> c.version, (c) -> Registration.UNREGISTERED), - (existing, registration) -> existing - ), - this.queue - ); - } - - @Override - public VersionRegistrationState acceptVersion(final MavenCoordinates coordinates) { - if (this.versions.containsKey(coordinates.version)) { - return this; - } - return new Active( - this.coordinates, - this.versions.put(coordinates.version, Registration.UNREGISTERED), - this.queue - ); - } - - @Override - public List getPending() { - return this.queue; - } - - @Override - public VersionRegistrationState startBatch( - final List batched - ) { - if (!this.queue.isEmpty()) { - return this; - } - final var toBatch = batched.filter( - c -> this.versions.getOrElse(c.version, Registration.UNREGISTERED) == Registration.UNREGISTERED - ).appendAll(this.queue); - return new Active( - this.coordinates, - this.versions.merge( - toBatch.toMap(c -> c.version, (c) -> Registration.UNREGISTERED), - (existing, registration) -> existing - ), - toBatch - ); - } - - @Override - public VersionRegistrationState failedVersion(final MavenCoordinates coordinates) { - if (this.versions.containsKey(coordinates.version)) { - return this; - } - return new Active( - this.coordinates, - this.versions.put(coordinates.version, Registration.UNREGISTERED), - this.queue - ); - } - - @Override - public List getNextBatch() { - if (this.queue.isEmpty()) { - return this.versions.filterValues(registration -> registration == Registration.UNREGISTERED) - .keySet() - .map(this.coordinates::version) - .toList() - .sorted() - .take(10); - } - return this.queue; - } - } -} diff --git a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/VersionSyncEvent.java b/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/VersionSyncEvent.java deleted file mode 100644 index 67a3a7d4..00000000 --- a/version-synchronizer/src/main/java/org/spongepowered/synchronizer/versionsync/VersionSyncEvent.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.synchronizer.versionsync; - -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.javadsl.persistence.AggregateEvent; -import com.lightbend.lagom.javadsl.persistence.AggregateEventShards; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTag; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTagger; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.List; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - - -@JsonDeserialize -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(VersionSyncEvent.RegisteredVersion.class), - @JsonSubTypes.Type(VersionSyncEvent.RegisteredBatch.class), - @JsonSubTypes.Type(VersionSyncEvent.StartedBatchRegistration.class), - @JsonSubTypes.Type(VersionSyncEvent.ResolvedVersion.class), - @JsonSubTypes.Type(VersionSyncEvent.FailedVersion.class) -}) -public sealed interface VersionSyncEvent extends AggregateEvent, Jsonable { - - AggregateEventShards INSTANCE = AggregateEventTag.sharded(VersionSyncEvent.class, 3); - - @Override - default AggregateEventTagger aggregateTag() { - return INSTANCE; - } - - @JsonTypeName("registered-version") - record RegisteredVersion(MavenCoordinates coordinates) implements VersionSyncEvent {} - - @JsonTypeName("registered-batch") - record RegisteredBatch(ArtifactCoordinates artifact, List coordinates) implements VersionSyncEvent { - } - - @JsonTypeName("started-batch") - record StartedBatchRegistration(List batched) implements VersionSyncEvent { - } - - @JsonTypeName("resolved-version") - record ResolvedVersion(MavenCoordinates coordinates) implements VersionSyncEvent { - } - - @JsonTypeName("failed-version") - record FailedVersion(MavenCoordinates coordinates) implements VersionSyncEvent { - } -} diff --git a/version-synchronizer/src/main/resources/META-INF/persistence.xml b/version-synchronizer/src/main/resources/META-INF/persistence.xml deleted file mode 100644 index df63fd1b..00000000 --- a/version-synchronizer/src/main/resources/META-INF/persistence.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - org.hibernate.jpa.HibernatePersistenceProvider - DefaultDS - true - - - - - - - diff --git a/version-synchronizer/src/main/resources/application.conf b/version-synchronizer/src/main/resources/application.conf deleted file mode 100644 index e0dd3d6d..00000000 --- a/version-synchronizer/src/main/resources/application.conf +++ /dev/null @@ -1,84 +0,0 @@ -play.modules.enabled += org.spongepowered.synchronizer.SynchronizerModule - -db.default { - driver = "org.postgresql.Driver" - url = "jdbc:postgresql://localhost:5432/default" - url = ${?POSTGRES_URL} - username = admin - username = ${?POSTGRES_USERNAME} - password = password - password = ${?POSTGRES_PASSWORD} -} -akka.cluster.roles += "commit-resolver" - -jdbc-defaults.slick.profile = "slick.jdbc.PostgresProfile$" - -akka.serialization.jackson { - jackson-modules += "io.vavr.jackson.datatype.VavrModule" -} -akka.persistence.snapshot-store { - snapshot-is-optional = true -} - -akka { - extensions = ${akka.extensions} [ - "org.spongepowered.synchronizer.assetsync.AssetSettingsExtension", - "org.spongepowered.synchronizer.resync.ResyncExtension", - "org.spongepowered.synchronizer.SynchronizationExtension", - "org.spongepowered.synchronizer.actor.ArtifactSyncExtension" - ] -} -asset-retrieval-dispatcher { - type = Dispatcher - executor = "thread-pool-executor" - thread-pool-executor { - fixed-pool-size = 16 - } - throughput = 1 -} -systemofadownload.synchronizer { - version-sync { - pool-size = 1 - interval = "300s" - delay = "60s" - } - reactive-sync { - pool-size = 1 - parallelism = 1 - time-out = "1h" - } - asset { - pool-size = 1 - parallelism = 1 - initial-backoff = "1m" - maximum-backoff = "600m" - backoff-factor = 10 - time-out = "1h" - } - timed-sync { - - } - worker { - assets { - repository = "https://repo.spongepowered.org" - timeout = "1h" - retry = 3 - files-to-index = ["jar", "pom"] - pool-size = 1 - } - resync { - repository = "https://repo.spongepowered.org/repository/maven-public/" - timeout = "1h" - retry = 1 - agent-name = "SystemOfADownload-Synchronizer" - } - version-registration { - parallelism = 1 - fan-out-parallelism = 1 - pool-size = 1 - time-out = "1h" - registration-time-out = "90000s" - } - } - -} diff --git a/version-synchronizer/src/main/resources/logback.xml b/version-synchronizer/src/main/resources/logback.xml deleted file mode 100644 index 8ca52f08..00000000 --- a/version-synchronizer/src/main/resources/logback.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - System.out - - %date{hh:MM:ss.SSS} [%level] [%thread] [%logger{5}/%marker] - %coloredLevel %msg%n - - - - - 8192 - true - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/version-synchronizer/src/main/resources/reference.conf b/version-synchronizer/src/main/resources/reference.conf deleted file mode 100644 index 15e6e946..00000000 --- a/version-synchronizer/src/main/resources/reference.conf +++ /dev/null @@ -1,25 +0,0 @@ -systemofadownload.commit-resolver { - cloner { - type = Dispatcher - executor = "thread-pool-executor" - thread-pool-executor { - fixed-pool-size = 2 - keep-alive-time = 60s - } - throughput = 1 - } - dispatcher { - type = Dispatcher - executor = "fork-join-executor" - fork-join-executor { - # Min number of threads to cap factor-based parallelism number to - parallelism-min = 2 - # Parallelism (threads) ... ceil(available processors * factor) - parallelism-factor = 2.0 - # Max number of threads to cap factor-based parallelism number to - parallelism-max = 10 - } - throughput = 2 - } - -} diff --git a/version-synchronizer/src/main/resources/soad.gitconfig b/version-synchronizer/src/main/resources/soad.gitconfig deleted file mode 100644 index e69de29b..00000000 diff --git a/version-synchronizer/src/test/java/org/spongepowered/synchronizer/test/worker/CommitDetailsRegistrarTest.java b/version-synchronizer/src/test/java/org/spongepowered/synchronizer/test/worker/CommitDetailsRegistrarTest.java deleted file mode 100644 index 6f687f1e..00000000 --- a/version-synchronizer/src/test/java/org/spongepowered/synchronizer/test/worker/CommitDetailsRegistrarTest.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.spongepowered.synchronizer.test.worker; - - -public class CommitDetailsRegistrarTest { - -} diff --git a/version-synchronizer/src/test/java/org/spongepowered/synchronizer/test/worker/CommitResolutionManagerTest.java b/version-synchronizer/src/test/java/org/spongepowered/synchronizer/test/worker/CommitResolutionManagerTest.java deleted file mode 100644 index f431e2e8..00000000 --- a/version-synchronizer/src/test/java/org/spongepowered/synchronizer/test/worker/CommitResolutionManagerTest.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.spongepowered.synchronizer.test.worker; - -import akka.Done; -import akka.actor.testkit.typed.javadsl.FishingOutcomes; -import akka.actor.testkit.typed.javadsl.TestKitJunitResource; -import io.vavr.collection.List; -import org.eclipse.jgit.util.SystemReader; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.synchronizer.actor.CommitDetailsRegistrar; -import org.spongepowered.synchronizer.gitmanaged.util.jgit.CommitResolutionManager; - -import java.io.File; -import java.net.URI; -import java.time.Duration; - -public class CommitResolutionManagerTest { - - private TestKitJunitResource testKit; - - @BeforeEach - public void setup() { - this.testKit = new TestKitJunitResource(); - testKit.system().log().info("Starting test"); - // Because jgit has to be told to ignore a lot of things about the system - // we need to proxy - final var dummyGitConfig = this.getClass().getClassLoader().getResource("dummy.gitconfig"); - Assertions.assertNotNull(dummyGitConfig, "dummy git config cannot be null"); - final var gitConfig = new File(dummyGitConfig.getFile()); - SystemReader.setInstance(new TestGitSystemReader(gitConfig)); - } - - @AfterEach - public void teardown() { - testKit.system().log().info("Finishing test"); - testKit.system().terminate(); - } - - @Test - public void testCommitResolver() { - final var teller = testKit.createTestProbe(CommitDetailsRegistrar.Command.class); - final var actor = testKit.spawn(CommitResolutionManager.resolveCommit(teller.ref())); - final var coords = new ArtifactCoordinates("com.example", "test").version("1.0"); - final var commit = "d838fee5d8e834ba9fd4d1c4fe0f8214d6dc90fc"; - final var url = "https://github.com/spongepowered/configurate.git"; - final var uri = List.of(URI.create(url)); - final var probe = testKit - .createTestProbe(); - final var replyTo = probe - .ref(); - actor.tell(new CommitResolutionManager.ResolveCommitDetails( - coords, commit, uri, replyTo - )); - // This teller is the intermediary registrar that should receive HandleVersionedCommitReport - // that ultimately tells the probe a Done. - teller.fishForMessage(Duration.ofSeconds(60), m -> { - ((CommitDetailsRegistrar.HandleVersionedCommitReport) m).replyTo().tell(Done.getInstance()); - return FishingOutcomes.complete(); - }); - - probe.fishForMessage(Duration.ofSeconds(60), m -> FishingOutcomes.complete()); - } - - @Test - public void verifyNonExistentRepo() { - final var teller = testKit.createTestProbe(CommitDetailsRegistrar.Command.class); - final var actor = testKit.spawn(CommitResolutionManager.resolveCommit(teller.ref())); - final var coords = new ArtifactCoordinates("com.example", "test").version("1.0"); - final var commit = "d838fee5d8e834ba9fd4d1c4fe0f8214d6dc90fc"; - final var url = "https://example.com/git/doesnt-exist.git"; - final var uri = List.of(URI.create(url)); - final var probe = testKit - .createTestProbe(); - final var replyTo = probe - .ref(); - actor.tell(new CommitResolutionManager.ResolveCommitDetails( - coords, commit, uri, replyTo - )); - probe.fishForMessage(Duration.ofSeconds(10), m -> FishingOutcomes.complete()); - } - - - @Test - public void verifyNonExistentCommit() { - final var teller = testKit.createTestProbe(CommitDetailsRegistrar.Command.class); - final var actor = testKit.spawn(CommitResolutionManager.resolveCommit(teller.ref())); - final var coords = new ArtifactCoordinates("com.example", "test").version("1.0"); - final var commit = "a830fee5d8e894ba9aa4a1a4fe0f8214d6dc90fc"; - final var url = "https://github.com/spongepowered/configurate.git"; - final var uri = List.of(URI.create(url)); - final var probe = testKit - .createTestProbe(); - final var replyTo = probe - .ref(); - actor.tell(new CommitResolutionManager.ResolveCommitDetails( - coords, commit, uri, replyTo - )); - probe.fishForMessage(Duration.ofSeconds(60), m -> FishingOutcomes.complete()); - } - - @Test - public void verifyCommitFromTwoRepositories() { - final var teller = testKit.createTestProbe(CommitDetailsRegistrar.Command.class); - final var actor = testKit.spawn(CommitResolutionManager.resolveCommit(teller.ref())); - final var coords = new ArtifactCoordinates("org.spongepowered", "spongevanilla").version("1.16.5-8.1.0-RC1184"); - final var commit = "6e443ec04ded4385d12c2e609360e81a770fbfcb"; - final var url = "https://github.com/spongepowered/spongevanilla.git"; - final var newUrl = "https://github.com/spongepowered/sponge.git"; - final var repos = List.of(URI.create(newUrl), URI.create(url)); - final var probe = testKit.createTestProbe(); - final var replyTo = probe.ref(); - actor.tell(new CommitResolutionManager.ResolveCommitDetails( - coords, commit, repos, replyTo - )); - probe.fishForMessage(Duration.ofMinutes(10), m -> FishingOutcomes.complete()); - } -} diff --git a/version-synchronizer/src/test/java/org/spongepowered/synchronizer/test/worker/TestGitSystemReader.java b/version-synchronizer/src/test/java/org/spongepowered/synchronizer/test/worker/TestGitSystemReader.java deleted file mode 100644 index ff574c14..00000000 --- a/version-synchronizer/src/test/java/org/spongepowered/synchronizer/test/worker/TestGitSystemReader.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.spongepowered.synchronizer.test.worker; - -import org.eclipse.jgit.lib.Config; -import org.eclipse.jgit.storage.file.FileBasedConfig; -import org.eclipse.jgit.util.FS; -import org.eclipse.jgit.util.SystemReader; - -import java.io.File; - -public class TestGitSystemReader extends SystemReader { - private static final SystemReader proxy = SystemReader.getInstance(); - private final File userGitConfig; - - public TestGitSystemReader(File userGitConfig) { - super(); - this.userGitConfig = userGitConfig; - } - - @Override - public String getenv(String variable) { - return proxy.getenv(variable); - } - - @Override - public String getHostname() { - return proxy.getHostname(); - } - - @Override - public String getProperty(String key) { - return proxy.getProperty(key); - } - - @Override - public long getCurrentTime() { - return proxy.getCurrentTime(); - } - - @Override - public int getTimezone(long when) { - return proxy.getTimezone(when); - } - - @Override - public FileBasedConfig openUserConfig(Config parent, FS fs) { - return new FileBasedConfig(parent, userGitConfig, fs); - } - - // Return an empty system configuration, based on example in SystemReader.Default#openSystemConfig - @Override - public FileBasedConfig openSystemConfig(Config parent, FS fs) { - return new FileBasedConfig(parent, this.userGitConfig, fs) { - @Override - public void load() { - } - - @Override - public boolean isOutdated() { - return false; - } - }; - } - - @Override - public FileBasedConfig openJGitConfig(final Config parent, final FS fs) { - return new FileBasedConfig(parent, this.userGitConfig, fs) { - @Override - public void load() { - } - - @Override - public boolean isOutdated() { - return false; - } - }; - } -} diff --git a/version-synchronizer/src/test/resources/dummy.gitconfig b/version-synchronizer/src/test/resources/dummy.gitconfig deleted file mode 100644 index e69de29b..00000000 diff --git a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/VersionsService.java b/versions-api/src/main/java/org/spongepowered/downloads/versions/api/VersionsService.java deleted file mode 100644 index d1cbfdcb..00000000 --- a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/VersionsService.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.api; - -import akka.NotUsed; -import com.lightbend.lagom.javadsl.api.Descriptor; -import com.lightbend.lagom.javadsl.api.Service; -import com.lightbend.lagom.javadsl.api.ServiceCall; -import com.lightbend.lagom.javadsl.api.broker.Topic; -import com.lightbend.lagom.javadsl.api.transport.Method; -import org.spongepowered.downloads.versions.api.models.ArtifactUpdate; -import org.spongepowered.downloads.versions.api.models.CommitRegistration; -import org.spongepowered.downloads.versions.api.models.TagRegistration; -import org.spongepowered.downloads.versions.api.models.TagVersion; -import org.spongepowered.downloads.versions.api.models.VersionRegistration; -import org.spongepowered.downloads.versions.api.models.VersionedArtifactUpdates; - -public interface VersionsService extends Service { - - ServiceCall registerArtifactCollection( - String groupId, String artifactId - ); - - ServiceCall registerArtifactTag(String groupId, String artifactId); - ServiceCall updateArtifactTag(String groupId, String artifactId); - - ServiceCall tagVersion(String groupId, String artifactId); - - ServiceCall registerCommit(String groupId, String artifactId, String version); - - Topic artifactUpdateTopic(); - - Topic versionedArtifactUpdatesTopic(); - - @Override - default Descriptor descriptor() { - return Service.named("versions") - .withCalls( - Service.restCall(Method.POST, "/versions/groups/:groupId/artifacts/:artifactId/versions", this::registerArtifactCollection), - Service.restCall(Method.POST, "/versions/groups/:groupId/artifacts/:artifactId/tags", this::registerArtifactTag), - Service.restCall(Method.PATCH, "/versions/groups/:groupId/artifacts/:artifactId/tags", this::updateArtifactTag), - Service.restCall(Method.POST, "/versions/groups/:groupId/artifacts/:artifactId/promotion", this::tagVersion), - Service.restCall(Method.PUT, "/versions/groups/:groupId/artifacts/:artifactId/versions/:version/commit", this::registerCommit) - ) - .withTopics( - Service.topic("artifact-update", this::artifactUpdateTopic), - Service.topic("versioned-artifact-updates", this::versionedArtifactUpdatesTopic) - ) - .withAutoAcl(true); - } - -} diff --git a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/ArtifactUpdate.java b/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/ArtifactUpdate.java deleted file mode 100644 index f57902e0..00000000 --- a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/ArtifactUpdate.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.api.models; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.api.models.tags.ArtifactTagEntry; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = ArtifactUpdate.ArtifactVersionRegistered.class, name = "new-version"), - @JsonSubTypes.Type(value = ArtifactUpdate.TagRegistered.class, name = "tag-update"), -}) -public interface ArtifactUpdate { - - @JsonDeserialize - final record ArtifactVersionRegistered(@JsonProperty("coordinates") MavenCoordinates coordinates) implements ArtifactUpdate { - - @JsonCreator - public ArtifactVersionRegistered { - } - } - - @JsonDeserialize - record TagRegistered(@JsonProperty("coordinates") ArtifactCoordinates coordinates, @JsonProperty("tag") ArtifactTagEntry entry) implements ArtifactUpdate { - - @JsonCreator - public TagRegistered { - } - } - -} diff --git a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/CommitRegistration.java b/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/CommitRegistration.java deleted file mode 100644 index 51410e28..00000000 --- a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/CommitRegistration.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.api.models; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -import java.net.URI; - -@JsonDeserialize -@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION) -@JsonSubTypes({ - @JsonSubTypes.Type(CommitRegistration.ResolvedCommit.class), - @JsonSubTypes.Type(CommitRegistration.FailedCommit.class) -}) -public sealed interface CommitRegistration extends Jsonable { - - record ResolvedCommit( - URI repo, - VersionedCommit versionedCommit, - MavenCoordinates coordinates - ) implements CommitRegistration { - @JsonCreator - public ResolvedCommit { - } - } - - record FailedCommit( - String commitSha, URI repo - ) implements CommitRegistration { - @JsonCreator - public FailedCommit { - } - } -} diff --git a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/TagRegistration.java b/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/TagRegistration.java deleted file mode 100644 index fb6dfbc0..00000000 --- a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/TagRegistration.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.api.models; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.versions.api.models.tags.ArtifactTagEntry; - -public interface TagRegistration { - - @JsonDeserialize - final record Register(@JsonProperty("tag") ArtifactTagEntry entry) {} - - @JsonDeserialize - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") - @JsonSubTypes({ - @JsonSubTypes.Type(value = Response.TagAlreadyRegistered.class, name = "AlreadyRegistered"), - @JsonSubTypes.Type(value = Response.TagSuccessfullyRegistered.class, name = "Success") - }) - interface Response extends Jsonable { - - final record TagAlreadyRegistered(@JsonProperty String name) implements TagRegistration.Response {} - - @JsonSerialize - final record TagSuccessfullyRegistered() implements TagRegistration.Response {} - - } -} diff --git a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/TagVersion.java b/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/TagVersion.java deleted file mode 100644 index 544dbe72..00000000 --- a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/TagVersion.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.api.models; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.List; - -public interface TagVersion { - - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") - @JsonSubTypes({ - @JsonSubTypes.Type(value = Request.SetRecommendationRegex.class, name = "recommendation") - }) - interface Request { - - @JsonDeserialize - final record SetRecommendationRegex( - String regex, - List valid, - List invalid, - boolean enableManualMarking - ) implements Request { - - @JsonCreator - public SetRecommendationRegex( - @JsonProperty(required = true) final String regex, - @JsonProperty(required = true) final List valid, - @JsonProperty(required = true) final List invalid, - @JsonProperty(required = true) final boolean enableManualMarking - ) { - this.regex = regex; - this.enableManualMarking = enableManualMarking; - this.valid = valid; - this.invalid = invalid; - } - } - } - - @JsonSubTypes({ - @JsonSubTypes.Type(value = TagVersion.Response.TagSuccessfullyRegistered.class, name = "Success") - }) - interface Response extends Jsonable { - - @JsonSerialize - final record TagSuccessfullyRegistered() implements TagVersion.Response {} - - } -} diff --git a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/VersionRegistration.java b/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/VersionRegistration.java deleted file mode 100644 index 240fbf19..00000000 --- a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/VersionRegistration.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.api.models; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.ArtifactCollection; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -public final class VersionRegistration { - - - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, - property = "type") - @JsonSubTypes({ - @JsonSubTypes.Type(value = Register.Collection.class, - name = "Collection"), - @JsonSubTypes.Type(value = Register.Version.class, - name = "Version"), - }) - public interface Register { - - @JsonDeserialize - final record Collection( - @JsonProperty ArtifactCollection collection - ) implements Register { - - @JsonCreator - public Collection { - } - - } - - @JsonDeserialize - record Version(@JsonProperty MavenCoordinates coordinates) - implements Register { - @JsonCreator - public Version { - } - - } - - - } - - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, - property = "type") - @JsonSubTypes({ - @JsonSubTypes.Type(value = Response.GroupMissing.class, - name = "GroupMissing"), - @JsonSubTypes.Type(value = Response.ArtifactAlreadyRegistered.class, - name = "AlreadyRegistered"), - @JsonSubTypes.Type(value = Response.RegisteredArtifact.class, - name = "Registered"), - }) - public interface Response extends Jsonable { - - @JsonDeserialize - final record ArtifactAlreadyRegistered( - @JsonProperty(required = true) MavenCoordinates coordinates - ) implements Response { - - @JsonCreator - public ArtifactAlreadyRegistered { - } - } - - @JsonDeserialize - final record RegisteredArtifact( - @JsonProperty(required = true) MavenCoordinates mavenCoordinates - ) implements Response { - - @JsonCreator - public RegisteredArtifact { - } - } - - @JsonDeserialize - final record GroupMissing(@JsonProperty(required = true) String groupId) implements Response { - - } - } -} diff --git a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/VersionedArtifactUpdates.java b/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/VersionedArtifactUpdates.java deleted file mode 100644 index 4c8042c5..00000000 --- a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/VersionedArtifactUpdates.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.api.models; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.List; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -import java.net.URI; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = VersionedArtifactUpdates.GitCommitDetailsAssociated.class), - @JsonSubTypes.Type(value = VersionedArtifactUpdates.CommitExtracted.class), -}) -public interface VersionedArtifactUpdates extends Jsonable { - - @JsonTypeName("commit-extracted") - final record CommitExtracted( - MavenCoordinates coordinates, - List gitRepositories, - String commit - ) implements VersionedArtifactUpdates { - - @JsonCreator - public CommitExtracted { - } - } - @JsonTypeName("commit-associated") - final record GitCommitDetailsAssociated( - MavenCoordinates coordinates, - URI repo, VersionedCommit commit - ) implements VersionedArtifactUpdates { - - } -} diff --git a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/VersionedChangelog.java b/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/VersionedChangelog.java deleted file mode 100644 index c7a8b2e4..00000000 --- a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/VersionedChangelog.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.api.models; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import io.vavr.collection.List; - -import java.net.URI; - -@JsonDeserialize -public final record VersionedChangelog( - List commits, - @JsonInclude(JsonInclude.Include.NON_DEFAULT) boolean processing -) { - - @JsonCreator - public VersionedChangelog { - } - - @JsonDeserialize - public final record IndexedCommit( - VersionedCommit commit, - List submoduleCommits - ) { - @JsonCreator - public IndexedCommit { - } - } - - @JsonDeserialize - public final record Submodule( - String name, - URI gitRepository, - List commits - ) { - @JsonCreator - public Submodule { - } - } - -} diff --git a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/VersionedCommit.java b/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/VersionedCommit.java deleted file mode 100644 index e73d9bc0..00000000 --- a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/VersionedCommit.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.api.models; - - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import java.net.URI; -import java.time.ZonedDateTime; -import java.util.Optional; - -@JsonDeserialize -public record VersionedCommit( - String message, - String body, - String sha, - Author author, - Commiter commiter, - Optional link, - ZonedDateTime commitDate -) { - - @JsonCreator - public VersionedCommit { - } - - @JsonDeserialize - public record Author( - String name, - String email - ) { - @JsonCreator - public Author { - } - } - - @JsonDeserialize - public record Commiter( - String name, - String email - ) { - @JsonCreator - public Commiter { - } - } -} - diff --git a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/tags/ArtifactTagEntry.java b/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/tags/ArtifactTagEntry.java deleted file mode 100644 index 266ccd63..00000000 --- a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/tags/ArtifactTagEntry.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.api.models.tags; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import io.vavr.control.Try; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -import java.util.regex.Pattern; - -@JsonDeserialize -public record ArtifactTagEntry( - @JsonProperty(required = true) String name, - @JsonProperty(required = true) int matchingGroup, - @JsonProperty(required = true) String regex -) { - - @JsonCreator - public ArtifactTagEntry { - } - - public VersionTagValue generateValue(MavenCoordinates coordinates) { - final var expectedGroup = this.matchingGroup(); - final var matcher = Pattern.compile(this.regex()).matcher(coordinates.version); - final String value; - if (matcher.find()) { - value = Try.of(() -> matcher.group(expectedGroup)) - .getOrElse(""); - } else { - value = ""; - } - return new VersionTagValue(coordinates, this, value); - } -} diff --git a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/tags/ArtifactTagValue.java b/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/tags/ArtifactTagValue.java deleted file mode 100644 index 01ec9590..00000000 --- a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/tags/ArtifactTagValue.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.api.models.tags; - -import io.vavr.collection.Map; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -public final record ArtifactTagValue( - MavenCoordinates coordinates, - Map tagValues, - boolean recommended -) { - public ArtifactTagValue promote(boolean promoted) { - return new ArtifactTagValue( - this.coordinates, - this.tagValues, - promoted - ); - } -} - diff --git a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/tags/VersionTagValue.java b/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/tags/VersionTagValue.java deleted file mode 100644 index a5d80b5e..00000000 --- a/versions-api/src/main/java/org/spongepowered/downloads/versions/api/models/tags/VersionTagValue.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.api.models.tags; - -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -public final record VersionTagValue( - MavenCoordinates coordinates, - ArtifactTagEntry tag, - String tagValue -) { - -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/VersionsModule.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/VersionsModule.java deleted file mode 100644 index 773c2ed5..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/VersionsModule.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server; - -import com.google.inject.AbstractModule; -import com.google.inject.Provides; -import com.lightbend.lagom.javadsl.server.ServiceGuiceSupport; -import org.pac4j.core.config.Config; -import org.spongepowered.downloads.artifact.api.ArtifactService; -import org.spongepowered.downloads.auth.SOADAuth; -import org.spongepowered.downloads.auth.api.utils.AuthUtils; -import org.spongepowered.downloads.versions.api.VersionsService; -import org.spongepowered.downloads.versions.server.readside.AssetReadsidePersistence; -import org.spongepowered.downloads.versions.server.readside.VersionReadSidePersistence; -import org.spongepowered.downloads.versions.worker.readside.CommitProcessor; -import play.Environment; -import play.libs.akka.AkkaGuiceSupport; - -import javax.inject.Inject; - -public class VersionsModule extends AbstractModule implements ServiceGuiceSupport, AkkaGuiceSupport { - - private final AuthUtils auth; - - @SuppressWarnings("unused") // These parameters must match for Play's Guice handling to work. - @Inject - public VersionsModule(final Environment environment, final com.typesafe.config.Config config) { - this.auth = AuthUtils.configure(config); - } - - @Override - protected void configure() { - this.bindService(VersionsService.class, VersionsServiceImpl.class); - this.bindClient(ArtifactService.class); - - this.bind(VersionReadSidePersistence.class).asEagerSingleton(); - this.bind(AssetReadsidePersistence.class).asEagerSingleton(); - this.bind(CommitProcessor.class).asEagerSingleton(); - - } - - @Provides - @SOADAuth - protected Config configProvider() { - return this.auth.config(); - } - - @Provides - protected AuthUtils authProvider() { - return this.auth; - } - -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/VersionsServiceImpl.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/VersionsServiceImpl.java deleted file mode 100644 index f0ffaddf..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/VersionsServiceImpl.java +++ /dev/null @@ -1,370 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server; - -import akka.Done; -import akka.NotUsed; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.cluster.sharding.typed.javadsl.Entity; -import akka.cluster.sharding.typed.javadsl.EntityRef; -import akka.japi.Pair; -import akka.stream.javadsl.Flow; -import com.google.inject.Inject; -import com.lightbend.lagom.javadsl.api.ServiceCall; -import com.lightbend.lagom.javadsl.api.broker.Topic; -import com.lightbend.lagom.javadsl.api.transport.BadRequest; -import com.lightbend.lagom.javadsl.api.transport.NotFound; -import com.lightbend.lagom.javadsl.broker.TopicProducer; -import com.lightbend.lagom.javadsl.persistence.Offset; -import com.lightbend.lagom.javadsl.persistence.PersistentEntityRegistry; -import com.lightbend.lagom.javadsl.server.ServerServiceCall; -import io.vavr.control.Try; -import org.pac4j.core.config.Config; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.ArtifactService; -import org.spongepowered.downloads.artifact.api.event.GroupUpdate; -import org.spongepowered.downloads.auth.AuthenticatedInternalService; -import org.spongepowered.downloads.auth.SOADAuth; -import org.spongepowered.downloads.auth.api.utils.AuthUtils; -import org.spongepowered.downloads.versions.api.VersionsService; -import org.spongepowered.downloads.versions.api.models.ArtifactUpdate; -import org.spongepowered.downloads.versions.api.models.CommitRegistration; -import org.spongepowered.downloads.versions.api.models.TagRegistration; -import org.spongepowered.downloads.versions.api.models.TagVersion; -import org.spongepowered.downloads.versions.api.models.VersionRegistration; -import org.spongepowered.downloads.versions.api.models.VersionedArtifactUpdates; -import org.spongepowered.downloads.versions.server.domain.ACCommand; -import org.spongepowered.downloads.versions.server.domain.ACEvent; -import org.spongepowered.downloads.versions.server.domain.InvalidRequest; -import org.spongepowered.downloads.versions.server.domain.VersionedArtifactAggregate; -import org.spongepowered.downloads.versions.worker.domain.versionedartifact.ArtifactEvent; -import org.spongepowered.downloads.versions.worker.domain.versionedartifact.VersionedArtifactCommand; -import org.spongepowered.downloads.versions.worker.domain.versionedartifact.VersionedArtifactEntity; - -import java.net.URI; -import java.time.Duration; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.concurrent.CompletableFuture; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class VersionsServiceImpl implements VersionsService, - AuthenticatedInternalService { - private final PersistentEntityRegistry persistentEntityRegistry; - private final Config securityConfig; - private final ClusterSharding clusterSharding; - private final Duration streamTimeout = Duration.ofSeconds(30); - private final AuthUtils auth; - - public static final Pattern VALID_COORDINATE_PORTION = Pattern.compile("^[\\w.-]+$"); - - @Inject - public VersionsServiceImpl( - final ClusterSharding clusterSharding, - final ArtifactService artifactService, - final PersistentEntityRegistry persistentEntityRegistry, - @SOADAuth final Config securityConfig, - final AuthUtils auth - ) { - this.clusterSharding = clusterSharding; - this.persistentEntityRegistry = persistentEntityRegistry; - this.securityConfig = securityConfig; - this.auth = auth; - - this.clusterSharding.init( - Entity.of( - VersionedArtifactAggregate.ENTITY_TYPE_KEY, - VersionedArtifactAggregate::create - ) - ); - - artifactService.groupTopic() - .subscribe() - .atLeastOnce(Flow.create().map(this::processGroupEvent)); - - } - - private Done processGroupEvent(GroupUpdate a) { - if (!(a instanceof GroupUpdate.ArtifactRegistered g)) { - return Done.done(); - } - final var coordinates = g.coordinates(); - return this.getCollection(coordinates) - .ask( - replyTo -> new ACCommand.RegisterArtifact(coordinates, replyTo), - this.streamTimeout - ) - .thenApply(notUsed -> Done.done()) - .toCompletableFuture() - .join(); - } - - @Override - public Config getSecurityConfig() { - return this.securityConfig; - } - - @Override - public ServerServiceCall registerArtifactCollection( - final String groupId, - final String artifactId - ) { - return this.authorize(AuthUtils.Types.JWT, AuthUtils.Roles.ADMIN, profile -> registration -> { - final var coordinates = parseCoordinates(groupId, artifactId); - if (registration instanceof VersionRegistration.Register.Version v) { - return this.getCollection(coordinates) - .ask( - replyTo -> new ACCommand.RegisterVersion(v.coordinates(), replyTo), - this.streamTimeout - ).thenApply(response -> { - if (response instanceof InvalidRequest) { - throw new NotFound("unknown artifact or group"); - } - return response; - }) - .thenCompose(r -> this.clusterSharding.entityRefFor( - VersionedArtifactEntity.ENTITY_TYPE_KEY, - v.coordinates().asStandardCoordinates() - ) - .ask( - replyTo -> new VersionedArtifactCommand.Register(v.coordinates(), replyTo), - this.streamTimeout - ) - .thenApply(notUsed -> r)); - } - if (registration instanceof VersionRegistration.Register.Collection c) { - return this.clusterSharding.entityRefFor( - VersionedArtifactEntity.ENTITY_TYPE_KEY, c.collection().coordinates().asStandardCoordinates()) - .ask( - replyTo -> new VersionedArtifactCommand.RegisterAssets( - c.collection().coordinates(), c.collection(), replyTo), this.streamTimeout) - .thenApply(response -> { - if (response instanceof InvalidRequest) { - throw new NotFound("unknown artifact or group"); - } - return response; - }); - } - throw new BadRequest("unknown registration request"); - }); - } - - @Override - public ServiceCall registerArtifactTag( - final String groupId, - final String artifactId - ) { - return this.authorize(AuthUtils.Types.JWT, AuthUtils.Roles.ADMIN, profile -> registration -> { - final var coordinates = parseCoordinates(groupId, artifactId); - return this.getCollection(coordinates) - .ask( - replyTo -> new ACCommand.RegisterArtifactTag(registration.entry(), replyTo), this.streamTimeout) - .thenApply(response -> { - if (response instanceof InvalidRequest) { - throw new NotFound("unknown artifact or group"); - } - return response; - }); - }); - } - - @Override - public ServiceCall updateArtifactTag( - final String groupId, - final String artifactId - ) { - return this.authorize(AuthUtils.Types.JWT, AuthUtils.Roles.ADMIN, profile -> registration -> { - final var coordinates = parseCoordinates(groupId, artifactId); - return this.getCollection(coordinates) - .ask( - replyTo -> new ACCommand.UpdateArtifactTag(registration.entry(), replyTo), this.streamTimeout) - .thenApply(response -> { - if (response instanceof InvalidRequest) { - throw new NotFound("unknown artifact or group"); - } - return response; - }); - }); - } - - @Override - public ServiceCall tagVersion( - final String groupId, - final String artifactId - ) { - return this.authorize(AuthUtils.Types.JWT, AuthUtils.Roles.ADMIN, profile -> request -> { - final var coordinates = parseCoordinates(groupId, artifactId); - if (!(request instanceof TagVersion.Request.SetRecommendationRegex s)) { - throw new BadRequest("unknown request"); - } - final var regex = Try.of(() -> Pattern.compile(s.regex())); - final var validFailures = s.valid() - .filter(valid -> regex.map(pattern -> pattern.matcher(valid)) - .mapTry(Matcher::find) - .map(b -> !b) - .getOrElse(true) // If exception, keep the version as failed - ); - final var invalidSuccesses = s.invalid() - .filter(invalid -> regex - .map(pattern -> pattern.matcher(invalid)) - .mapTry(Matcher::find) - .getOrElse(true) - ); - if (!validFailures.isEmpty()) { - throw new BadRequest("expected valid versions did not match regex: " + validFailures); - } - if (!invalidSuccesses.isEmpty()) { - throw new BadRequest("expected invalid versions matched regex successfully:" + invalidSuccesses); - } - return this.getCollection(coordinates) - .ask( - replyTo -> new ACCommand.RegisterPromotion(s.regex(), replyTo, s.enableManualMarking()), - this.streamTimeout - ) - .thenApply(response -> { - if (response instanceof InvalidRequest) { - throw new NotFound("unknown artifact or group"); - } - return response; - }); - }); - } - - @Override - public ServiceCall registerCommit( - final String groupId, final String artifactId, final String version - ) { - return this.authorize(AuthUtils.Types.JWT, AuthUtils.Roles.ADMIN, profile -> request -> { - final var coordinates = parseCoordinates(groupId, artifactId); - if (!VALID_COORDINATE_PORTION.matcher(version).matches()) { - throw new BadRequest("Invalid version: " + version); - } - final var mavenCoordinates = coordinates.version(version); - if (request instanceof CommitRegistration.ResolvedCommit rc) { - return this.clusterSharding.entityRefFor( - VersionedArtifactEntity.ENTITY_TYPE_KEY, - mavenCoordinates.asStandardCoordinates() - ) - .ask( - replyTo -> new VersionedArtifactCommand.RegisterResolvedCommit( - rc.versionedCommit(), - rc.repo(), - replyTo - ), - Duration.ofSeconds(30) - ) - .thenApply(done -> NotUsed.notUsed()); - } else if (request instanceof CommitRegistration.FailedCommit uc) { - return this.clusterSharding.entityRefFor( - VersionedArtifactEntity.ENTITY_TYPE_KEY, - mavenCoordinates.asStandardCoordinates() - ) - .ask( - replyTo -> new VersionedArtifactCommand.RegisterFailedCommit( - uc.commitSha(), - uc.repo(), - replyTo - ), - Duration.ofSeconds(30) - ) - .thenApply(done -> NotUsed.notUsed()); - } - - return CompletableFuture.completedStage(NotUsed.notUsed()); - }); - } - - private static List> convertEvent(Pair pair) { - final ACEvent event = pair.first(); - final ArtifactUpdate update; - if (event instanceof ACEvent.ArtifactVersionRegistered r) { - update = new ArtifactUpdate.ArtifactVersionRegistered(r.version()); - } else if (event instanceof ACEvent.ArtifactTagRegistered r) { - update = new ArtifactUpdate.TagRegistered(r.coordinates(), r.entry()); - } else { - return Collections.emptyList(); - } - return List.of(Pair.apply(update, pair.second())); - } - - @Override - public Topic artifactUpdateTopic() { - return TopicProducer.taggedStreamWithOffset( - ACEvent.INSTANCE.allTags(), - (aggregateTag, fromOffset) -> this.persistentEntityRegistry - .eventStream(aggregateTag, fromOffset) - .mapConcat(VersionsServiceImpl::convertEvent) - ); - } - - @Override - public Topic versionedArtifactUpdatesTopic() { - return TopicProducer.taggedStreamWithOffset( - ArtifactEvent.INSTANCE.allTags(), - (aggregateTag, fromOffset) -> this.persistentEntityRegistry - .eventStream(aggregateTag, fromOffset) - .mapConcat(VersionsServiceImpl::convertGitEvents) - ); - } - - private static List> convertGitEvents(Pair pair) { - final ArtifactEvent event = pair.first(); - final VersionedArtifactUpdates update; - if (event instanceof ArtifactEvent.CommitAssociated r) { - update = new VersionedArtifactUpdates.CommitExtracted( - r.coordinates(), r.repos().map(URI::create), r.commitSha()); - } else if (event instanceof ArtifactEvent.CommitResolved r) { - update = new VersionedArtifactUpdates.GitCommitDetailsAssociated( - r.coordinates(), r.repo(), r.versionedCommit()); - } else { - return Collections.emptyList(); - } - return List.of(Pair.apply(update, pair.second())); - } - - private EntityRef getCollection(final ArtifactCoordinates coordinates) { - return this.clusterSharding.entityRefFor( - VersionedArtifactAggregate.ENTITY_TYPE_KEY, coordinates.asMavenString()); - } - - private static ArtifactCoordinates parseCoordinates(final String groupID, final String artifactID) { - final String sanitizedGroupId = groupID.toLowerCase(Locale.ROOT); - if (!VALID_COORDINATE_PORTION.matcher(sanitizedGroupId).matches()) { - throw new BadRequest("Invalid groupId: " + groupID); - } - final String sanitizedArtifactId = artifactID.toLowerCase(Locale.ROOT); - if (!VALID_COORDINATE_PORTION.matcher(sanitizedArtifactId).matches()) { - throw new BadRequest("Invalid artifactId: " + artifactID); - } - return new ArtifactCoordinates(sanitizedGroupId, sanitizedArtifactId); - } - - @Override - public AuthUtils auth() { - return this.auth; - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/ACCommand.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/ACCommand.java deleted file mode 100644 index 0335c164..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/ACCommand.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server.domain; - -import akka.NotUsed; -import akka.actor.typed.ActorRef; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.api.models.TagRegistration; -import org.spongepowered.downloads.versions.api.models.TagVersion; -import org.spongepowered.downloads.versions.api.models.VersionRegistration; -import org.spongepowered.downloads.versions.api.models.tags.ArtifactTagEntry; - -@JsonDeserialize -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = ACCommand.RegisterArtifact.class, name = "register-artifact"), - @JsonSubTypes.Type(value = ACCommand.RegisterVersion.class, name = "register-version"), - @JsonSubTypes.Type(value = ACCommand.RegisterArtifactTag.class, name = "register-tag"), - @JsonSubTypes.Type(value = ACCommand.UpdateArtifactTag.class, name = "update-tag"), - @JsonSubTypes.Type(value = ACCommand.RegisterPromotion.class, name = "register-promotion"), -}) -public sealed interface ACCommand extends Jsonable{ - - record RegisterArtifact( - ArtifactCoordinates coordinates, - ActorRef replyTo - ) implements ACCommand { - } - - record RegisterVersion( - MavenCoordinates coordinates, - ActorRef replyTo - ) implements ACCommand { - } - - record RegisterArtifactTag( - ArtifactTagEntry entry, - ActorRef replyTo - ) implements ACCommand { - } - - record UpdateArtifactTag( - ArtifactTagEntry entry, - ActorRef replyTo - ) implements ACCommand { - } - - record RegisterPromotion( - String regex, - ActorRef replyTo, - boolean enableManualMarking - ) implements ACCommand { - } - -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/ACEvent.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/ACEvent.java deleted file mode 100644 index 8897e94f..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/ACEvent.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server.domain; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.javadsl.persistence.AggregateEvent; -import com.lightbend.lagom.javadsl.persistence.AggregateEventShards; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTag; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTagger; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.Map; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.api.models.tags.ArtifactTagEntry; - -import java.io.Serial; -import java.util.Objects; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME) -@JsonSubTypes({ - @JsonSubTypes.Type( - value = ACEvent.ArtifactTagRegistered.class, - name = "tag-registered" - ), - @JsonSubTypes.Type( - value = ACEvent.ArtifactCoordinatesUpdated.class, - name = "updated-coordinates" - ), - @JsonSubTypes.Type( - value = ACEvent.ArtifactVersionRegistered.class, - name = "version-registered" - ), - @JsonSubTypes.Type( - value = ACEvent.PromotionSettingModified.class, - name = "promotion-settings-modified" - ), - @JsonSubTypes.Type( - value = ACEvent.ArtifactVersionsResorted.class, - name = "versions-resorted" - ) -}) -public interface ACEvent extends AggregateEvent, Jsonable { - AggregateEventShards INSTANCE = AggregateEventTag.sharded(ACEvent.class, 10); - - @Override - default AggregateEventTagger aggregateTag() { - return INSTANCE; - } - - record ArtifactCoordinatesUpdated(ArtifactCoordinates coordinates) implements ACEvent { - - @JsonCreator - public ArtifactCoordinatesUpdated { - } - - } - - record ArtifactVersionRegistered( - MavenCoordinates version, - int sorting - ) implements ACEvent { - @Serial private static final long serialVersionUID = 0L; - - @JsonCreator - public ArtifactVersionRegistered { - } - - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (obj == null || obj.getClass() != this.getClass()) { - return false; - } - final var that = (ArtifactVersionRegistered) obj; - return Objects.equals(this.version, that.version); - } - - @Override - public int hashCode() { - return Objects.hash(this.version); - } - - @Override - public String toString() { - return "ArtifactVersionRegistered[" + - "version=" + this.version + ", "; - } - } - - @JsonDeserialize - record ArtifactTagRegistered(ArtifactCoordinates coordinates, @JsonProperty("entry") ArtifactTagEntry entry) - implements ACEvent { - - } - - @JsonDeserialize - record PromotionSettingModified(ArtifactCoordinates coordinates, String regex, boolean enableManualPromotion) - implements ACEvent { - } - - @JsonDeserialize - record ArtifactVersionsResorted( - ArtifactCoordinates coordinates, Map versionordering - ) implements ACEvent{ - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/InvalidRequest.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/InvalidRequest.java deleted file mode 100644 index fa444b46..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/InvalidRequest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server.domain; - -import org.spongepowered.downloads.versions.api.models.TagRegistration; -import org.spongepowered.downloads.versions.api.models.TagVersion; -import org.spongepowered.downloads.versions.api.models.VersionRegistration; - -/** - * An invalid request to return to the asker in the service implementation to - * signify the current state is literally invalid to perform the specified - * action. - */ -public record InvalidRequest() - implements TagRegistration.Response, - TagVersion.Response, - VersionRegistration.Response { -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/State.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/State.java deleted file mode 100644 index d69a07fd..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/State.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server.domain; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.CompressedJsonable; -import io.vavr.Tuple2; -import io.vavr.collection.HashMap; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import io.vavr.collection.SortedMap; -import io.vavr.collection.TreeMap; -import org.apache.maven.artifact.versioning.ComparableVersion; -import org.spongepowered.downloads.artifact.api.Artifact; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.api.models.tags.ArtifactTagEntry; -import org.spongepowered.downloads.versions.api.models.tags.ArtifactTagValue; - -import java.util.Collections; -import java.util.Comparator; -import java.util.Locale; -import java.util.Objects; -import java.util.StringJoiner; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.regex.Pattern; - -public interface State { - boolean isRegistered(); - - static Empty empty() { - return new Empty(); - } - - final record Empty() implements State { - @Override - public boolean isRegistered() { - return false; - } - - public ACState register(ACEvent.ArtifactCoordinatesUpdated event) { - return new ACState(event.coordinates()); - } - } - - @JsonDeserialize - final record ACState( - ArtifactCoordinates coordinates, - SortedMap collection, - Map> versionedArtifacts, - boolean unregistered, - Map tags, - String promotionRegex, - boolean manualPromotionAllowed - ) implements CompressedJsonable, State { - - ACState(ArtifactCoordinates coordinates) { - this(coordinates, TreeMap.empty(), HashMap.empty(), false, HashMap.empty(), "", false); - } - - public boolean isRegistered() { - return !this.unregistered; - } - - public ACState withVersion(String version) { - final var versionMap = this.collection - .computeIfAbsent(version, convertArtifactVersionToTagValues(this, this.tags)) - ._2 - .toSortedMap(Comparator.comparing(ComparableVersion::new).reversed(), Tuple2::_1, Tuple2::_2); - return new ACState( - this.coordinates, - versionMap, - this.versionedArtifacts, - this.unregistered, - this.tags, - "", - false - ); - } - - public ACState withTag(ArtifactTagEntry entry) { - final var tagMap = this.tags().put(entry.name().toLowerCase(Locale.ROOT), entry); - final var versionedTags = this.collection - .replaceAll((version, values) -> convertArtifactVersionToTagValues(this, tagMap).apply(version)); - return new ACState( - this.coordinates, - versionedTags, - this.versionedArtifacts, - this.unregistered, - tagMap, - "", - false - ); - } - - private Function convertArtifactVersionToTagValues( - ACState state, Map tagMap - ) { - return version -> { - final var mavenCoordinates = state.coordinates.version(version); - final Map tagValues = tagMap.mapValues( - tag -> tag.generateValue(mavenCoordinates).tagValue()); - return new ArtifactTagValue(state.coordinates().version(version), tagValues, false); - }; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ACState acState = (ACState) o; - return Objects.equals(coordinates, acState.coordinates) && Objects.equals( - collection, acState.collection); - } - - @Override - public int hashCode() { - return Objects.hash(coordinates, collection); - } - - @Override - public String toString() { - return new StringJoiner( - ", ", ACState.class.getSimpleName() + "[", "]") - .add("coordinates=" + coordinates) - .add("collection=" + collection) - .toString(); - } - - public ACState withPromotionDetails(String regex, boolean enableManualPromotion) { - final var pattern = Pattern.compile(regex); - final var versionedTags = this.collection - .replaceAll((version, value) -> value.promote(pattern.matcher(version).find())) - .toSortedMap(Comparator.comparing(ComparableVersion::new).reversed(), Tuple2::_1, Tuple2::_2); - return new ACState( - this.coordinates, - versionedTags, - this.versionedArtifacts, - false, - this.tags, - regex, - enableManualPromotion - ); - } - - public ACState withAddedArtifacts(MavenCoordinates coordinates, List newArtifacts) { - final var existing = this.versionedArtifacts.get(coordinates.version) - .getOrElse(List::empty); - final var existingArtifactsByClassifier = existing.toMap(a -> a.classifier().orElse(""), Function.identity()); - final var newArtifactList = existing.appendAll( - newArtifacts.filter(Predicate.not(artifact -> existingArtifactsByClassifier.containsKey(artifact.classifier().orElse(""))))); - final var versionedArtifacts = this.versionedArtifacts.put(coordinates.version, newArtifactList); - return new ACState( - this.coordinates, - this.collection, - versionedArtifacts, - false, - this.tags, - this.promotionRegex, - this.manualPromotionAllowed - ); - } - - public java.util.List addVersion(MavenCoordinates coordinates) { - final var versions = this.collection - .keySet() - .toSortedSet(Comparator.comparing(ComparableVersion::new)); - final var newVersions = versions.add(coordinates.version); - final var newIndex = newVersions - .toList() - .indexOf(coordinates.version); - final var versionRegistered = new ACEvent.ArtifactVersionRegistered(coordinates, newIndex); - final var events = List.empty(); - if (newIndex >= versions.size()) { - return events.append(versionRegistered).toJavaList(); - } - if (versions.size() == newVersions.size()) { - return Collections.emptyList(); - } - // Figure out how many versions are being resorted - final var sortedVersions = newVersions.toSortedSet(Comparator.comparing(ComparableVersion::new)); - final java.util.Map versionsByIndex = new java.util.HashMap<>(); - versions.forEachWithIndex(versionsByIndex::put); - final java.util.Map updatedVersionsIndecies = new java.util.HashMap<>(); - sortedVersions.forEachWithIndex(updatedVersionsIndecies::put); - versionsByIndex.forEach((version, oldIndex) -> { - if (Objects.equals(updatedVersionsIndecies.get(version), oldIndex)) { - updatedVersionsIndecies.remove(version); - } - }); - final var trimmedIndecies = HashMap.ofAll(updatedVersionsIndecies); - final var versionMoved = new ACEvent.ArtifactVersionsResorted(this.coordinates, trimmedIndecies); - return events.append(versionRegistered).append(versionMoved).toJavaList(); - } - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/VersionedArtifactAggregate.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/VersionedArtifactAggregate.java deleted file mode 100644 index e475d30e..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/VersionedArtifactAggregate.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server.domain; - -import akka.NotUsed; -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.cluster.sharding.typed.javadsl.EntityContext; -import akka.cluster.sharding.typed.javadsl.EntityTypeKey; -import akka.persistence.typed.PersistenceId; -import akka.persistence.typed.javadsl.CommandHandlerWithReply; -import akka.persistence.typed.javadsl.EventHandler; -import akka.persistence.typed.javadsl.EventSourcedBehaviorWithEnforcedReplies; -import akka.persistence.typed.javadsl.ReplyEffect; -import akka.persistence.typed.javadsl.RetentionCriteria; -import com.lightbend.lagom.javadsl.persistence.AkkaTaggerAdapter; -import org.spongepowered.downloads.versions.api.models.TagRegistration; -import org.spongepowered.downloads.versions.api.models.TagVersion; -import org.spongepowered.downloads.versions.api.models.VersionRegistration; - -import java.util.Locale; -import java.util.Set; -import java.util.function.Function; - -public final class VersionedArtifactAggregate - extends EventSourcedBehaviorWithEnforcedReplies { - - public static EntityTypeKey ENTITY_TYPE_KEY = EntityTypeKey.create(ACCommand.class, "VersionedArtifact"); - private final Function> tagger; - private final ActorContext ctx; - - public static Behavior create(final EntityContext context) { - return Behaviors.setup(ctx -> new VersionedArtifactAggregate(context, ctx)); - } - - private VersionedArtifactAggregate( - final EntityContext context, - final ActorContext ctx - ) { - super( - // PersistenceId needs a typeHint (or namespace) and entityId, - // we take then from the EntityContext - PersistenceId.of( - context.getEntityTypeKey().name(), // <- type hint - context.getEntityId() // <- business id - )); - this.tagger = AkkaTaggerAdapter.fromLagom(context, ACEvent.INSTANCE); - this.ctx = ctx; - } - - @Override - public State emptyState() { - return State.empty(); - } - - @Override - public EventHandler eventHandler() { - final var builder = this.newEventHandlerBuilder(); - builder.forStateType(State.Empty.class) - .onEvent(ACEvent.ArtifactCoordinatesUpdated.class, State.Empty::register); - builder.forStateType(State.ACState.class) - .onEvent(ACEvent.ArtifactTagRegistered.class, (state1, event1) -> state1.withTag(event1.entry())) - .onEvent( - ACEvent.ArtifactVersionRegistered.class, - (state2, event2) -> state2.withVersion(event2.version().version) - ) - .onEvent( - ACEvent.PromotionSettingModified.class, - (state, event) -> state.withPromotionDetails(event.regex(), event.enableManualPromotion()) - ) - .onEvent(ACEvent.ArtifactVersionsResorted.class, (state, event) -> state) - ; - return builder.build(); - } - - @Override - public Set tagsFor(final ACEvent acEvent) { - return this.tagger.apply(acEvent); - } - - @Override - public RetentionCriteria retentionCriteria() { - return RetentionCriteria.snapshotEvery(10, 2); - } - - @Override - public CommandHandlerWithReply commandHandler() { - final var builder = this.newCommandHandlerWithReplyBuilder(); - builder.forStateType(State.Empty.class) - .onCommand(ACCommand.RegisterArtifact.class, this::handleRegisterArtifact) - .onCommand( - ACCommand.RegisterArtifactTag.class, (cmd) -> this.Effect().reply(cmd.replyTo(), new InvalidRequest())) - .onCommand( - ACCommand.RegisterVersion.class, (cmd) -> this.Effect().reply(cmd.replyTo(), new InvalidRequest())) - .onCommand( - ACCommand.RegisterArtifactTag.class, (cmd) -> this.Effect().reply(cmd.replyTo(), new InvalidRequest())) - .onCommand( - ACCommand.UpdateArtifactTag.class, (cmd) -> this.Effect().reply(cmd.replyTo(), new InvalidRequest())) - .onCommand( - ACCommand.RegisterPromotion.class, (cmd) -> this.Effect().reply(cmd.replyTo(), new InvalidRequest())) - ; - builder.forStateType(State.ACState.class) - .onCommand(ACCommand.RegisterArtifact.class, (cmd) -> this.Effect().reply(cmd.replyTo(), NotUsed.notUsed())) - .onCommand(ACCommand.RegisterVersion.class, this::handleRegisterVersion) - .onCommand(ACCommand.RegisterArtifactTag.class, this::handlRegisterTag) - .onCommand(ACCommand.UpdateArtifactTag.class, this::handleUpdateTag) - .onCommand(ACCommand.RegisterPromotion.class, this::handlePromotionSetting) - ; - return builder.build(); - } - - private ReplyEffect handleRegisterVersion( - final State.ACState state, final ACCommand.RegisterVersion cmd - ) { - if (state.collection().containsKey(cmd.coordinates().version)) { - return this.Effect().reply( - cmd.replyTo(), - new VersionRegistration.Response.ArtifactAlreadyRegistered(cmd.coordinates()) - ); - } - return this.Effect() - .persist(state.addVersion(cmd.coordinates())) - .thenReply(cmd.replyTo(), (s) -> new VersionRegistration.Response.RegisteredArtifact(cmd.coordinates())); - } - - private ReplyEffect handleRegisterArtifact( - final State.Empty state, - final ACCommand.RegisterArtifact cmd - ) { - return this.Effect() - .persist(new ACEvent.ArtifactCoordinatesUpdated(cmd.coordinates())) - .thenReply(cmd.replyTo(), (s) -> NotUsed.notUsed()); - } - - private ReplyEffect handlRegisterTag( - final State.ACState state, - final ACCommand.RegisterArtifactTag cmd - ) { - if (state.tags().containsKey(cmd.entry().name().toLowerCase(Locale.ROOT))) { - return this.Effect().reply( - cmd.replyTo(), new TagRegistration.Response.TagAlreadyRegistered(cmd.entry().name())); - } - return this.Effect() - .persist(new ACEvent.ArtifactTagRegistered(state.coordinates(), cmd.entry())) - .thenReply(cmd.replyTo(), (s) -> new TagRegistration.Response.TagSuccessfullyRegistered()); - } - - private ReplyEffect handlePromotionSetting( - final State.ACState state, - final ACCommand.RegisterPromotion cmd - ) { - return this.Effect() - .persist(new ACEvent.PromotionSettingModified(state.coordinates(), cmd.regex(), cmd.enableManualMarking())) - .thenReply(cmd.replyTo(), (s) -> new TagVersion.Response.TagSuccessfullyRegistered()); - } - - private ReplyEffect handleUpdateTag( - final State.ACState state, - final ACCommand.UpdateArtifactTag cmd - ) { - return this.Effect() - .persist(new ACEvent.ArtifactTagRegistered(state.coordinates(), cmd.entry())) - .thenReply(cmd.replyTo(), (s) -> new TagRegistration.Response.TagSuccessfullyRegistered()); - } - -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/VersionedArtifactEvent.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/VersionedArtifactEvent.java deleted file mode 100644 index 0dc24738..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/domain/VersionedArtifactEvent.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server.domain; - -import com.lightbend.lagom.javadsl.persistence.AggregateEvent; -import com.lightbend.lagom.javadsl.persistence.AggregateEventShards; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTag; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTagger; -import com.lightbend.lagom.serialization.Jsonable; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -import java.util.Objects; -import java.util.StringJoiner; - -public interface VersionedArtifactEvent extends AggregateEvent, Jsonable { - - AggregateEventShards TAG = AggregateEventTag.sharded(VersionedArtifactEvent.class, 10); - - @Override - default AggregateEventTagger aggregateTag() { - return TAG; - } - - String asMavenCoordinates(); - - class VersionRegistered implements VersionedArtifactEvent { - - public final MavenCoordinates coordinates; - - public VersionRegistered(final MavenCoordinates coordinates) { - this.coordinates = coordinates; - } - - - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - VersionRegistered that = (VersionRegistered) o; - return Objects.equals(coordinates, that.coordinates); - } - - @Override - public int hashCode() { - return Objects.hash(coordinates); - } - - @Override - public String toString() { - return new StringJoiner(", ", VersionRegistered.class.getSimpleName() + "[", "]") - .add("coordinates=" + coordinates) - .toString(); - } - - @Override - public String asMavenCoordinates() { - return this.coordinates.asStandardCoordinates(); - } - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/AssetReadsidePersistence.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/AssetReadsidePersistence.java deleted file mode 100644 index 72936abd..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/AssetReadsidePersistence.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server.readside; - -import com.lightbend.lagom.javadsl.persistence.AggregateEventTag; -import com.lightbend.lagom.javadsl.persistence.ReadSide; -import com.lightbend.lagom.javadsl.persistence.ReadSideProcessor; -import com.lightbend.lagom.javadsl.persistence.jpa.JpaReadSide; -import com.lightbend.lagom.javadsl.persistence.jpa.JpaSession; -import org.pcollections.PSequence; -import org.spongepowered.downloads.artifact.api.Artifact; -import org.spongepowered.downloads.versions.worker.domain.versionedartifact.ArtifactEvent; - -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.persistence.EntityManager; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.atomic.AtomicInteger; - -@Singleton -public class AssetReadsidePersistence { - - private final JpaSession session; - - @Inject - public AssetReadsidePersistence( - final ReadSide readSide, - final JpaSession session - ) { - this.session = session; - readSide.register(AssetReadsidePersistence.AssetWriter.class); - } - - static final class AssetWriter extends ReadSideProcessor { - - private final JpaReadSide readSide; - - private static final AtomicInteger counter = new AtomicInteger(); - - @Inject - AssetWriter(final JpaReadSide readSide) { - this.readSide = readSide; - } - - @Override - public ReadSideHandler buildHandler() { - return this.readSide.builder("asset_read_side_processor_" + counter.incrementAndGet()) - .setGlobalPrepare((em) -> {}) - .setEventHandler(ArtifactEvent.AssetsUpdated.class, (em, event) -> { - final var coordinates = event.coordinates(); - final var version = em.createNamedQuery( - "ArtifactVersion.findByCoordinates", - JpaArtifactVersion.class - ) - .setParameter("groupId", coordinates.groupId) - .setParameter("artifactId", coordinates.artifactId) - .setParameter("version", coordinates.version) - .setMaxResults(1) - .getSingleResult(); - event.artifacts() - .forEach(asset -> { - final var versionedAsset = findOrCreateVersionedAsset(em, version, asset); - versionedAsset.setDownloadUrl(asset.downloadUrl().toString()); - versionedAsset.setMd5(asset.md5().getBytes(StandardCharsets.UTF_8)); - versionedAsset.setSha1(asset.sha1().getBytes(StandardCharsets.UTF_8)); - versionedAsset.setExtension(asset.extension()); - }); - }) - .build(); - } - - private static JpaVersionedArtifactAsset findOrCreateVersionedAsset( - EntityManager em, JpaArtifactVersion version, Artifact asset - ) { - return em.createNamedQuery( - "VersionedAsset.findByVersion", - JpaVersionedArtifactAsset.class - ) - .setParameter("id", version.getId()) - .setParameter("classifier", asset.classifier().orElse("")) - .setParameter("extension", asset.extension()) - .setMaxResults(1) - .getResultStream() - .findFirst() - .orElseGet(() -> { - final var jpaAsset = new JpaVersionedArtifactAsset(); - jpaAsset.setClassifier(asset.classifier().orElse("")); - version.addAsset(jpaAsset); - return jpaAsset; - }); - } - - @Override - public PSequence> aggregateTags() { - return ArtifactEvent.INSTANCE.allTags(); - } - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/JpaArtifact.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/JpaArtifact.java deleted file mode 100644 index 54d85836..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/JpaArtifact.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server.readside; - -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.Table; -import java.io.Serializable; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -@Entity(name = "Artifact") -@Table(name = "artifacts", - schema = "version", - indexes = { - @Index(name = "grouped_artifact", - columnList = "group_id, artifact_id", - unique = true) - }) -@NamedQueries({ - @NamedQuery( - name = "Artifact.selectByGroupAndArtifact", - query = """ - select a from Artifact a where a.groupId = :groupId and a.artifactId = :artifactId - """ - ), - @NamedQuery( - name = "Artifact.selectWithTags", - query = """ - select a from Artifact a where a.groupId = :groupId and a.artifactId = :artifactId - """ - ) -}) -public class JpaArtifact implements Serializable { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id", - updatable = false, - nullable = false) - private int id; - - @Column(name = "group_id", - nullable = false) - private String groupId; - - @Column(name = "artifact_id", - nullable = false) - private String artifactId; - - @OneToMany( - targetEntity = JpaArtifactTag.class, - cascade = CascadeType.ALL, - orphanRemoval = true, - mappedBy = "artifact") - private Set tags = new HashSet<>(); - - @OneToMany( - targetEntity = JpaArtifactVersion.class, - cascade = CascadeType.ALL, - orphanRemoval = true, - mappedBy = "artifact") - private Set versions = new HashSet<>(); - - @OneToOne( - targetEntity = JpaArtifactRegexRecommendation.class, - cascade = CascadeType.ALL, - orphanRemoval = true, - mappedBy = "artifact" - ) - private JpaArtifactRegexRecommendation regexRecommendation; - - @Column(name = "git_repository") - private String repo; - - public int getId() { - return id; - } - - public void setId(final int id) { - this.id = id; - } - - public String getGroupId() { - return groupId; - } - - public void setGroupId(final String groupId) { - this.groupId = groupId; - } - - public String getArtifactId() { - return artifactId; - } - - public void setArtifactId(final String artifactId) { - this.artifactId = artifactId; - } - - public Set getTags() { - return tags; - } - - public void setTags(final Set tags) { - this.tags = tags; - } - - public void addVersion(JpaArtifactVersion version) { - this.versions.add(version); - version.setArtifact(this); - } - - public Set getVersions() { - return versions; - } - - public void setVersions(final Set versions) { - this.versions = versions; - } - - public void setRecommendation( - final JpaArtifactRegexRecommendation regexRecommendation - ) { - this.regexRecommendation = regexRecommendation; - regexRecommendation.setArtifact(this); - } - - public String getRepo() { - return repo; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - JpaArtifact that = (JpaArtifact) o; - return id == that.id && Objects.equals(groupId, that.groupId) && Objects.equals( - artifactId, that.artifactId); - } - - @Override - public int hashCode() { - return Objects.hash(id, groupId, artifactId); - } - - public void addTag(JpaArtifactTag newTag) { - this.tags.add(newTag); - newTag.setArtifact(this); - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/JpaArtifactRegexRecommendation.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/JpaArtifactRegexRecommendation.java deleted file mode 100644 index 28794554..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/JpaArtifactRegexRecommendation.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server.readside; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.NamedQuery; -import javax.persistence.OneToOne; -import javax.persistence.Table; - -@Entity(name = "RegexBasedRecommendation") -@Table(name = "artifact_recommendations", - schema = "version") -@NamedQuery(name = "RegexRecommendation.findByArtifact", - query = """ - select r from RegexBasedRecommendation r where r.artifact.id = :artifactId - """) -public class JpaArtifactRegexRecommendation { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id", - updatable = false, - nullable = false) - private long id; - - @OneToOne - @JoinColumn(name = "artifact_id", - referencedColumnName = "id", - nullable = false) - private JpaArtifact artifact; - - @Column(name = "recommendation_regex", - nullable = false) - private String regex; - - @Column(name = "allow_manual_promotion") - private boolean manual; - - void setArtifact(JpaArtifact artifact) { - this.artifact = artifact; - } - - public String getRegex() { - return regex; - } - - public void setRegex(final String regex) { - this.regex = regex; - } - - public boolean isManual() { - return manual; - } - - public void setManual(final boolean manual) { - this.manual = manual; - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/JpaArtifactTag.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/JpaArtifactTag.java deleted file mode 100644 index 98fe79fb..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/JpaArtifactTag.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server.readside; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; - -@Entity(name = "ArtifactTag") -@Table(name = "artifact_tags", - schema = "version") -public class JpaArtifactTag { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id", - updatable = false, - nullable = false) - private int id; - - @ManyToOne(optional = false, fetch = FetchType.LAZY) - @JoinColumn(name = "artifact_id", referencedColumnName = "id", nullable = false) - private JpaArtifact artifact; - - @Column(name = "tag_name", - nullable = false) - private String name; - - @Column(name = "tag_regex") - private String regex; - - @Column(name = "use_capture_group") - private int group; - - public JpaArtifactTag() { - } - - public JpaArtifact getArtifact() { - return artifact; - } - - void setArtifact(final JpaArtifact taggedVersion) { - this.artifact = taggedVersion; - } - - public String getName() { - return name; - } - - public void setName(final String name) { - this.name = name; - } - - public String getRegex() { - return regex; - } - - public void setRegex(final String regex) { - this.regex = regex; - } - - public int getGroup() { - return group; - } - - public void setGroup(final int group) { - this.group = group; - } - -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/JpaArtifactVersion.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/JpaArtifactVersion.java deleted file mode 100644 index 809581cd..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/JpaArtifactVersion.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server.readside; - -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToMany; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; -import java.io.Serializable; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -@Entity(name = "ArtifactVersion") -@Table(name = "artifact_versions", - schema = "version", - uniqueConstraints = @UniqueConstraint( - columnNames = {"artifact_id", "version"}, - name = "artifact_version_unique_idx") -) -@NamedQueries({ - @NamedQuery( - name = "ArtifactVersion.findByVersion", - query = - """ - select distinct v from ArtifactVersion v where v.artifact.id = :artifactId and v.version = :version - """ - ), - - @NamedQuery( - name = "ArtifactVersion.findByCoordinates", - query = - """ - select distinct v from ArtifactVersion v - where v.artifact.groupId = :groupId and v.artifact.artifactId = :artifactId and v.version = :version - """ - ) -}) -class JpaArtifactVersion implements Serializable { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id", - updatable = false, - nullable = false) - private long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "artifact_id", - foreignKey = @ForeignKey(name = "artifact_versions_artifact_id_fkey"), - nullable = false) - private JpaArtifact artifact; - - @Column(name = "version", - nullable = false) - private String version; - - @Column(name = "ordering") - private int ordering; - - @OneToMany( - targetEntity = JpaVersionedArtifactAsset.class, - cascade = CascadeType.ALL, - orphanRemoval = true, - mappedBy = "versionedArtifact") - private Set assets = new HashSet<>(); - - void setArtifact(final JpaArtifact artifact) { - this.artifact = artifact; - } - - public JpaArtifact getArtifact() { - return artifact; - } - - public String getVersion() { - return version; - } - - public long getId() { - return id; - } - - public void setVersion(final String version) { - this.version = version; - } - - public void addAsset(final JpaVersionedArtifactAsset asset) { - this.assets.add(asset); - asset.setVersion(this); - } - - public void setOrdering(final int ordering) { - this.ordering = ordering; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - JpaArtifactVersion that = (JpaArtifactVersion) o; - return id == that.id && Objects.equals(artifact, that.artifact) && Objects.equals( - version, that.version); - } - - @Override - public int hashCode() { - return Objects.hash(id, artifact, version); - } - -} - diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/JpaVersionedArtifactAsset.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/JpaVersionedArtifactAsset.java deleted file mode 100644 index a2b94925..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/JpaVersionedArtifactAsset.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server.readside; - -import org.hibernate.annotations.Type; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.Lob; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; -import java.util.Arrays; -import java.util.Objects; - -@Entity(name = "VersionedAsset") -@Table(name = "versioned_assets", - schema = "version") -@NamedQueries({ - @NamedQuery( - name = "VersionedAsset.findByVersion", - query = - """ - select a from VersionedAsset a - where a.versionedArtifact.id = :id and a.classifier = :classifier - and a.extension = :extension - """ - ) -}) -public class JpaVersionedArtifactAsset { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id", - updatable = false, - nullable = false) - private long id; - - @ManyToOne(targetEntity = JpaArtifactVersion.class, - fetch = FetchType.LAZY) - @JoinColumn(name = "version_id", - referencedColumnName = "id", - nullable = false) - private JpaArtifactVersion versionedArtifact; - - @Column(name = "classifier", - nullable = false) - private String classifier; - - @Column(name = "download_url", - nullable = false) - private String downloadUrl; - - @Column(name = "extension", - nullable = false) - private String extension; - - @Lob - @Type(type = "org.hibernate.type.BinaryType") - @Column(name = "md5") - private byte[] md5; - - @Lob - @Type(type = "org.hibernate.type.BinaryType") - @Column(name = "sha1") - private byte[] sha1; - - public void setClassifier(final String classifier) { - this.classifier = classifier; - } - - public void setExtension(final String extension) { - this.extension = extension; - } - - public void setDownloadUrl(final String downloadUrl) { - this.downloadUrl = downloadUrl; - } - - public void setMd5(final byte[] md5) { - this.md5 = md5; - } - - public void setSha1(final byte[] sha1) { - this.sha1 = sha1; - } - - void setVersion(JpaArtifactVersion jpaArtifactVersion) { - this.versionedArtifact = jpaArtifactVersion; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - JpaVersionedArtifactAsset that = (JpaVersionedArtifactAsset) o; - return id == that.id && Objects.equals( - versionedArtifact, that.versionedArtifact) && Objects.equals( - classifier, that.classifier) && Objects.equals( - downloadUrl, that.downloadUrl) && Objects.equals( - extension, that.extension) && Arrays.equals( - md5, that.md5) && Arrays.equals(sha1, that.sha1); - } - - @Override - public int hashCode() { - int result = Objects.hash(id, versionedArtifact, classifier, downloadUrl, extension); - result = 31 * result + Arrays.hashCode(md5); - result = 31 * result + Arrays.hashCode(sha1); - return result; - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/VersionReadSidePersistence.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/VersionReadSidePersistence.java deleted file mode 100644 index 7b796485..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/VersionReadSidePersistence.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server.readside; - -import akka.actor.ActorSystem; -import akka.actor.typed.ActorRef; -import akka.actor.typed.SupervisorStrategy; -import akka.actor.typed.javadsl.Adapter; -import akka.actor.typed.javadsl.Behaviors; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTag; -import com.lightbend.lagom.javadsl.persistence.ReadSide; -import com.lightbend.lagom.javadsl.persistence.ReadSideProcessor; -import com.lightbend.lagom.javadsl.persistence.jpa.JpaReadSide; -import com.lightbend.lagom.javadsl.persistence.jpa.JpaSession; -import org.pcollections.PSequence; -import org.spongepowered.downloads.versions.server.domain.ACEvent; - -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.persistence.EntityManager; -import java.util.concurrent.atomic.AtomicInteger; - -@Singleton -public class VersionReadSidePersistence { - - private final JpaSession session; - - @Inject - public VersionReadSidePersistence( - final ReadSide readSide, - final JpaSession session - ) { - this.session = session; - readSide.register(VersionWriter.class); - } - - static final class VersionWriter extends ReadSideProcessor { - - private final JpaReadSide readSide; - private final ActorRef refresher; - - private static final AtomicInteger counter = new AtomicInteger(); - - @Inject - VersionWriter(final JpaReadSide readSide, final JpaSession session, final ActorSystem system) { - this.readSide = readSide; - final var taggedWorker = VersionedTagWorker.create(session); - final var commandBehavior = Behaviors.supervise(taggedWorker).onFailure(SupervisorStrategy.restart()); - this.refresher = Adapter.spawn( - system.classicSystem(), commandBehavior, "version-tag-db-worker-" + counter.incrementAndGet()); - } - - @Override - public ReadSideHandler buildHandler() { - return this.readSide.builder("version-query-builder") - .setGlobalPrepare(this::createSchema) - .setEventHandler(ACEvent.ArtifactCoordinatesUpdated.class, (em, artifactCreated) -> { - System.out.printf("Herpaderp coordinates updated %s\n", artifactCreated.coordinates().toString()); - final var coordinates = artifactCreated.coordinates(); - final var artifactQuery = em.createNamedQuery( - "Artifact.selectByGroupAndArtifact", - JpaArtifact.class - ); - final var singleResult = artifactQuery.setParameter("groupId", coordinates.groupId()) - .setParameter("artifactId", coordinates.artifactId()) - .setMaxResults(1) - .getResultList(); - if (singleResult.isEmpty()) { - final var jpaArtifact = new JpaArtifact(); - jpaArtifact.setGroupId(coordinates.groupId()); - jpaArtifact.setArtifactId(coordinates.artifactId()); - em.persist(jpaArtifact); - } - }) - .setEventHandler(ACEvent.ArtifactVersionRegistered.class, (em, versionRegistered) -> { - final var coordinates = versionRegistered.version(); - final var query = em.createNamedQuery( - "Artifact.selectByGroupAndArtifact", - JpaArtifact.class - ); - query.setParameter("groupId", coordinates.groupId); - query.setParameter("artifactId", coordinates.artifactId); - final var artifact = query.getSingleResult(); - final var version = coordinates.version; - em.createNamedQuery( - "ArtifactVersion.findByVersion", - JpaArtifactVersion.class - ) - .setParameter("artifactId", artifact.getId()) - .setParameter("version", version) - .setMaxResults(1) - .getResultList() - .stream().findFirst() - .orElseGet(() -> { - final var jpaArtifactVersion = new JpaArtifactVersion(); - jpaArtifactVersion.setVersion(version); - jpaArtifactVersion.setOrdering(versionRegistered.sorting()); - artifact.addVersion(jpaArtifactVersion); - refresher.tell(new VersionedTagWorker.RefreshVersionTags()); - refresher.tell(new VersionedTagWorker.RefreshVersionRecommendation(coordinates.asArtifactCoordinates())); - return jpaArtifactVersion; - }); - }) - .setEventHandler(ACEvent.ArtifactTagRegistered.class, (em, tagRegistered) -> { - final var coordinates = tagRegistered.coordinates(); - final var artifactQuery = em.createNamedQuery( - "Artifact.selectWithTags", - JpaArtifact.class - ); - artifactQuery.setParameter("groupId", coordinates.groupId()); - artifactQuery.setParameter("artifactId", coordinates.artifactId()); - final var tag = tagRegistered.entry(); - final var artifact = artifactQuery.getSingleResult(); - final var jpaTag = artifact.getTags().stream() - .filter(jpatag -> jpatag.getName().equals(tag.name())) - .findFirst() - .orElseGet(() -> { - final var jpaArtifactTag = new JpaArtifactTag(); - artifact.addTag(jpaArtifactTag); - return jpaArtifactTag; - }); - jpaTag.setRegex(tag.regex()); - jpaTag.setName(tag.name()); - jpaTag.setGroup(tag.matchingGroup()); - refresher.tell(new VersionedTagWorker.RefreshVersionTags()); - }) - .setEventHandler(ACEvent.PromotionSettingModified.class, (em, promotion) -> { - final var coordinates = promotion.coordinates(); - final var artifactQuery = em.createNamedQuery( - "Artifact.selectWithTags", - JpaArtifact.class - ); - - artifactQuery.setParameter("groupId", coordinates.groupId()); - artifactQuery.setParameter("artifactId", coordinates.artifactId()); - final var artifact = artifactQuery.getSingleResult(); - final var recommendation = em.createNamedQuery( - "RegexRecommendation.findByArtifact", JpaArtifactRegexRecommendation.class) - .setParameter("artifactId", artifact.getId()) - .getResultList() - .stream() - .findFirst() - .orElseGet(() -> { - final var regexRecommendation = new JpaArtifactRegexRecommendation(); - artifact.setRecommendation(regexRecommendation); - return regexRecommendation; - }); - recommendation.setRegex(promotion.regex()); - recommendation.setManual(promotion.enableManualPromotion()); - - refresher.tell(new VersionedTagWorker.RefreshVersionRecommendation(promotion.coordinates())); - }) - .setEventHandler(ACEvent.ArtifactVersionsResorted.class, (em, event) -> { - event.versionordering().forEach((v, ordering) -> { - final var jpaVersion = em.createNamedQuery( - "ArtifactVersion.findByCoordinates", - JpaArtifactVersion.class - ) - .setParameter("groupId", event.coordinates().groupId()) - .setParameter("artifactId", event.coordinates().artifactId()) - .setParameter("version", v) - .setMaxResults(1) - .getSingleResult(); - jpaVersion.setOrdering(ordering); - }); - }) - .build(); - } - - private void createSchema(EntityManager em) { - } - - @Override - public PSequence> aggregateTags() { - return ACEvent.INSTANCE.allTags(); - } - } - -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/VersionedTagWorker.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/VersionedTagWorker.java deleted file mode 100644 index 204e0cdf..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/server/readside/VersionedTagWorker.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.server.readside; - -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.Behaviors; -import com.lightbend.lagom.javadsl.persistence.jpa.JpaSession; -import io.vavr.collection.HashSet; -import io.vavr.collection.Set; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; - -import java.time.Duration; -import java.time.Instant; -import java.util.Optional; - -public final class VersionedTagWorker { - - public interface Command { - } - - static final record RefreshVersionTags() implements Command { - } - - static final record RefreshVersionRecommendation(ArtifactCoordinates coordinates) implements Command { - } - - private static final record TimedOut() implements Command { - } - - private static final record Completed(Data data, boolean updatedVersionedTags, int rowsAffected) - implements Command { - } - - private static final record Failed(Data data) implements Command { - } - - private static final record Data(Optional refreshVersions, - Set refreshRecommendations) { - - Data requestedRefreshVersions() { - return new Data(Optional.of(Instant.now()), this.refreshRecommendations); - } - - Data updateArtifactRecommendation(ArtifactCoordinates coordinates) { - return new Data(this.refreshVersions, this.refreshRecommendations.add(coordinates)); - } - } - - public static Behavior create( - final JpaSession session - ) { - return idle(session); - } - - private static Behavior idle(final JpaSession session) { - return Behaviors.setup(ctx -> Behaviors.receive(Command.class) - .onMessage( - RefreshVersionTags.class, - cmd -> timed(new Data(Optional.of(Instant.now()), HashSet.empty()), session) - ) - .onMessage( - RefreshVersionRecommendation.class, - cmd -> timed(new Data(Optional.empty(), HashSet.of(cmd.coordinates)), session) - ) - .onMessage(Completed.class, cmd -> { - ctx.getLog().info("Completed refresh of {}, affected {}", cmd.data, cmd.rowsAffected); - return Behaviors.same(); - }) - .onMessage(Failed.class, cmd -> { - ctx.getLog().warn("Recovering from failed update, will re-attempt"); - return timed(cmd.data, session); - }) - .build() - ); - } - - private static Behavior timed( - final Data data, final JpaSession session - ) { - return Behaviors.setup(ctx -> Behaviors.withTimers(timers -> { - timers.startSingleTimer(new TimedOut(), Duration.ofSeconds(10)); - return waiting(data, session); - })); - } - - private static Behavior waiting( - final Data data, final JpaSession session - ) { - return Behaviors.setup(ctx -> Behaviors.receive(Command.class) - .onMessage( - RefreshVersionTags.class, - cmd -> waiting(data.requestedRefreshVersions(), session) - ) - .onMessage( - RefreshVersionRecommendation.class, - cmd -> waiting(data.updateArtifactRecommendation(cmd.coordinates), session) - ) - .onMessage(TimedOut.class, timeout -> { - ctx.pipeToSelf(session.withTransaction(em -> { - final var updatedVersionedTags = data.refreshVersions - .map(time -> em - .createNativeQuery("select version.refreshversionedtags()") - .getSingleResult()) - .isPresent(); - final int rowsAffected = data.refreshRecommendations - .map(coordinates -> em.createNativeQuery( - "select version.refreshVersionRecommendations(:artifactId, :groupId)") - .setParameter("artifactId", coordinates.artifactId()) - .setParameter("groupId", coordinates.groupId()) - .getSingleResult()) - .sum().intValue(); - return new Completed(data, updatedVersionedTags, rowsAffected); - }), (msg, throwable) -> { - if (throwable != null) { - ctx.getLog().error("Failed to handle updating artifacts, aborting", throwable); - return new Failed(data); - } - return msg; - }); - return idle(session); - }) - .build()); - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/EntityStore.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/EntityStore.java deleted file mode 100644 index 779384a6..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/EntityStore.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker; - -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.cluster.sharding.typed.javadsl.Entity; -import org.spongepowered.downloads.versions.worker.domain.versionedartifact.VersionedArtifactEntity; - -public final class EntityStore { - - public static void setupPersistedEntities(ClusterSharding sharding) { - sharding.init(Entity.of(VersionedArtifactEntity.ENTITY_TYPE_KEY, VersionedArtifactEntity::create)); - } - -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/VersionConfig.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/VersionConfig.java deleted file mode 100644 index ebb956b8..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/VersionConfig.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker; - -import akka.actor.Extension; -import com.typesafe.config.Config; - -import java.time.Duration; - -public class VersionConfig implements Extension { - - public final CommitFetch commitFetch; - - public VersionConfig(Config config) { - final var commitConfig = config.getConfig("commit-fetch"); - this.commitFetch = new CommitFetch(commitConfig); - } - - public static final class CommitFetch { - public final int poolSize; - public final int parallelism; - public final Duration timeout; - - CommitFetch(Config config) { - this.poolSize = config.getInt("pool-size"); - this.parallelism = config.getInt("parallelism"); - this.timeout = config.getDuration("timeout"); - } - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/VersionExtension.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/VersionExtension.java deleted file mode 100644 index 20624e55..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/VersionExtension.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker; - -import akka.actor.AbstractExtensionId; -import akka.actor.ExtendedActorSystem; -import akka.actor.Extension; -import akka.actor.ExtensionId; -import akka.actor.ExtensionIdProvider; - -public class VersionExtension extends AbstractExtensionId implements ExtensionIdProvider { - - public static final VersionExtension Settings = new VersionExtension(); - - @Override - public VersionConfig createExtension(final ExtendedActorSystem system) { - return new VersionConfig(system.settings().config().getConfig("systemofadownload.versions")); - } - - @Override - public ExtensionId lookup() { - return Settings; - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/VersionsWorkerSupervisor.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/VersionsWorkerSupervisor.java deleted file mode 100644 index 7afae9be..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/VersionsWorkerSupervisor.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker; - -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.Behaviors; -import akka.cluster.Cluster; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import org.spongepowered.downloads.artifact.api.ArtifactService; -import org.spongepowered.downloads.versions.api.VersionsService; - -/** - * The "reactive" side of Versions where it performs all the various tasks - * involved with managing Artifact Versions and their artifact assets with - * relation to those versions. Crucially, this performs the various sync jobs - * required to derive a Version from an artifact, as well as the various - * metadata with that version, such as assets and the commit information for - * that asset. This is mostly a guardian actor, one that wires up children - * actors to perform the actual work against topic subscribers, either from - * {@link ArtifactService#artifactUpdate()} or - * {@link VersionsService#artifactUpdateTopic()}. - *

The important reasoning why this is split out from the Version Service - * implementation is that this particular supervisor may well be able to handle - * updates while the VersionsService implementation is the "organizer" of - * root information. - */ -public final class VersionsWorkerSupervisor { - - public static Behavior bootstrapWorkers() { - return Behaviors.setup(ctx -> { - final ClusterSharding sharding = ClusterSharding.get(ctx.getSystem()); - // Persistent EventBased Actors - EntityStore.setupPersistedEntities(sharding); - - // Workers available to do most jobs - final var member = Cluster.get(ctx.getSystem()).selfMember(); - final var system = ctx.getSystem(); - WorkerSpawner.spawnWorkers(system, member, ctx); - - // Finally, self, the supervisor - return Behaviors.receive(Void.class) - .build(); - }); - - } - -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/WorkerModule.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/WorkerModule.java deleted file mode 100644 index 68c25c64..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/WorkerModule.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker; - -import akka.actor.ActorSystem; -import akka.actor.typed.ActorRef; -import akka.actor.typed.javadsl.Adapter; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import com.google.inject.AbstractModule; -import com.google.inject.Provider; -import com.google.inject.TypeLiteral; -import com.lightbend.lagom.javadsl.server.ServiceGuiceSupport; -import org.spongepowered.downloads.artifact.api.ArtifactService; -import org.spongepowered.downloads.auth.api.utils.AuthUtils; -import org.spongepowered.downloads.versions.api.VersionsService; -import play.Environment; -import play.libs.akka.AkkaGuiceSupport; - -import javax.inject.Inject; - -public class WorkerModule extends AbstractModule implements ServiceGuiceSupport, AkkaGuiceSupport { - - private final AuthUtils auth; - - @SuppressWarnings("unused") // These parameters must match for Play's Guice handling to work. - @Inject - public WorkerModule(final Environment environment, final com.typesafe.config.Config config) { - this.auth = AuthUtils.configure(config); - } - - @Override - protected void configure() { - this.bind(new TypeLiteral>() { - }) - .toProvider(WorkerProvider.class) - .asEagerSingleton(); - - } - - public static record WorkerProvider( - ArtifactService artifacts, - VersionsService versions, - ClusterSharding sharding, - ActorSystem system - ) implements Provider> { - - @Inject - public WorkerProvider { - } - - @Override - public ActorRef get() { - return Adapter.spawn( - this.system, - VersionsWorkerSupervisor.bootstrapWorkers(), - "VersionsWorkerSupervisor" - ); - } - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/WorkerSpawner.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/WorkerSpawner.java deleted file mode 100644 index a5c4f73d..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/WorkerSpawner.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker; - -import akka.actor.typed.ActorSystem; -import akka.actor.typed.DispatcherSelector; -import akka.actor.typed.SupervisorStrategy; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.javadsl.Routers; -import akka.actor.typed.receptionist.Receptionist; -import akka.cluster.Member; -import org.spongepowered.downloads.versions.worker.actor.artifacts.CommitExtractor; -import org.spongepowered.downloads.versions.worker.actor.artifacts.FileCollectionOperator; -import org.spongepowered.downloads.versions.worker.actor.delegates.RawCommitReceiver; - -import java.util.UUID; - -public final class WorkerSpawner { - - public static void spawnWorkers(ActorSystem system, Member member, ActorContext ctx) { - // Set up the usual actors - final var versionConfig = VersionExtension.Settings.get(system); - final var poolSizePerInstance = versionConfig.commitFetch.poolSize; - - if (member.hasRole("file-extractor")) { - final var commitFetcherUID = UUID.randomUUID(); - final var behavior = CommitExtractor.extractCommitFromAssets(); - final var assetRefresher = Behaviors.supervise(behavior) - .onFailure(SupervisorStrategy.resume()); - final var pool = Routers.pool(poolSizePerInstance, assetRefresher); - - final var commitExtractorRef = ctx.spawn( - pool, - "file-commit-worker-" + commitFetcherUID, - DispatcherSelector.defaultDispatcher() - ); - // Announce it to the cluster - ctx.getSystem().receptionist().tell(Receptionist.register(CommitExtractor.SERVICE_KEY, commitExtractorRef)); - - final var receiver = Behaviors.supervise(RawCommitReceiver.receive()) - .onFailure(SupervisorStrategy.resume()); - final var receiverRef = ctx.spawn(receiver, "file-scan-result-receiver-" + commitFetcherUID); - final var jarScanner = FileCollectionOperator.scanJarFilesForCommit(commitExtractorRef, receiverRef); - final var supervisedScanner = Behaviors.supervise(jarScanner).onFailure(SupervisorStrategy.resume()); - final var scannerPool = Routers.pool(poolSizePerInstance, supervisedScanner); - final var scannerRef = ctx.spawn(scannerPool, "file-collection-worker-" + commitFetcherUID); - ctx.getSystem().receptionist().tell(Receptionist.register(FileCollectionOperator.KEY, scannerRef)); - } - } - - -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/actor/artifacts/CommitExtractor.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/actor/artifacts/CommitExtractor.java deleted file mode 100644 index b0a87bee..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/actor/artifacts/CommitExtractor.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker.actor.artifacts; - -import akka.actor.typed.ActorRef; -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.receptionist.ServiceKey; -import akka.japi.function.Function2; -import io.vavr.Tuple; -import io.vavr.Tuple2; -import io.vavr.collection.List; -import io.vavr.control.Try; -import org.eclipse.jgit.lib.ObjectId; - -import java.net.URL; -import java.nio.channels.Channels; -import java.nio.channels.FileChannel; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.nio.file.attribute.FileAttribute; -import java.nio.file.attribute.PosixFilePermission; -import java.nio.file.attribute.PosixFilePermissions; -import java.util.EnumSet; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.jar.JarInputStream; -import java.util.jar.Manifest; - -/** - * Actor that extracts the commit from a jar file. - */ -public final class CommitExtractor { - - public static final ServiceKey SERVICE_KEY = ServiceKey.create( - ChildCommand.class, "commit-extractor"); - private static final FileAttribute> OWNER_READ_WRITE_ATTRIBUTE = PosixFilePermissions.asFileAttribute( - EnumSet.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE)); - - public sealed interface ChildCommand { - } - - public final record AttemptFileCommit( - PotentiallyUsableAsset asset, - ActorRef ref - ) implements ChildCommand { - } - - private final record NoCommitsFound(PotentiallyUsableAsset asset, ActorRef ref) implements ChildCommand { - } - - private final record FailedFile( - Throwable throwable, - PotentiallyUsableAsset asset, - ActorRef replyTo - ) implements ChildCommand { - } - - private final record CommitRetrievedFromFile( - String sha, - PotentiallyUsableAsset asset, - ActorRef replyTo - ) implements ChildCommand { - } - - public sealed interface AssetCommitResponse { - } - - public final record DiscoveredCommitFromFile(String sha, PotentiallyUsableAsset asset) - implements AssetCommitResponse { - } - - - public final record NoCommitsFoundForFile(PotentiallyUsableAsset asset) implements AssetCommitResponse { - } - - public final record FailedToRetrieveCommit(PotentiallyUsableAsset asset) - implements AssetCommitResponse { - } - - public static Behavior extractCommitFromAssets() { - return Behaviors.setup(ctx -> Behaviors.receive(ChildCommand.class) - .onMessage(AttemptFileCommit.class, cmd -> { - if (ctx.getLog().isTraceEnabled()) { - ctx.getLog().trace("Attempting file commit extraction from {}", cmd.asset); - } - ctx.pipeToSelf(fetchCommitIdFromFile(cmd), handleFileExtractionResult(ctx, cmd)); - return working(cmd.asset, List.empty()); - }) - .build()); - } - - private static Function2, Throwable, ChildCommand> handleFileExtractionResult( - ActorContext ctx, AttemptFileCommit cmd - ) { - return (sha, throwable) -> { - if (throwable != null) { - ctx.getLog().info("Marking file {} as failed", cmd.asset); - return new FailedFile(throwable, cmd.asset, cmd.ref); - } - if (sha.isEmpty()) { - if (ctx.getLog().isDebugEnabled()) { - ctx.getLog().info("File {} doesn't have a commit", cmd.asset); - } - return new NoCommitsFound(cmd.asset, cmd.ref); - } - final var commit = sha.get(); - if (ctx.getLog().isDebugEnabled()) { - ctx.getLog().debug( - "[{}] Commit extracted from jar {}", cmd.asset.mavenCoordinates().asStandardCoordinates(), - commit.name() - ); - } - return new CommitRetrievedFromFile(commit.name(), cmd.asset, cmd.ref); - }; - } - - - private static Behavior working( - final PotentiallyUsableAsset working, - final List queue - ) { - return Behaviors.setup(ctx -> Behaviors.receive(ChildCommand.class) - .onMessage(AttemptFileCommit.class, cmd -> { - if (ctx.getLog().isTraceEnabled()) { - ctx.getLog().trace("Received additional work while processing {}", working.mavenCoordinates()); - } - return working(working, queue.append(cmd)); - }) - .onMessage(FailedFile.class, cmd -> { - if (ctx.getLog().isTraceEnabled()) { - ctx.getLog().trace("Failed commit extraction for " + cmd.asset.mavenCoordinates(), cmd.throwable); - } - cmd.replyTo.tell(new FailedToRetrieveCommit(cmd.asset)); - return swapToNext(ctx, queue); - }) - .onMessage(NoCommitsFound.class, cmd -> { - if (ctx.getLog().isTraceEnabled()) { - ctx.getLog().trace("No commits found for " + working.mavenCoordinates()); - } - cmd.ref.tell(new NoCommitsFoundForFile(cmd.asset)); - return swapToNext(ctx, queue); - }) - .onMessage(CommitRetrievedFromFile.class, cmd -> { - if (ctx.getLog().isTraceEnabled()) { - ctx.getLog().trace( - "Commit extracted from {} for {}", cmd.asset.coordinates(), working.mavenCoordinates()); - } - cmd.replyTo.tell(new DiscoveredCommitFromFile(cmd.sha, cmd.asset)); - return swapToNext(ctx, queue); - }) - .build()); - } - - private static Behavior swapToNext( - final ActorContext ctx, - final List queue - ) { - if (queue.isEmpty()) { - return extractCommitFromAssets(); - } - final var next = queue.head(); - ctx.pipeToSelf(fetchCommitIdFromFile(next), handleFileExtractionResult(ctx, next)); - return working(next.asset, queue.tail()); - } - - private static CompletableFuture> fetchCommitIdFromFile( - AttemptFileCommit cmd - ) { - return Try.of(cmd.asset.downloadURL()::toURL) - .mapTry(req -> { - final var tempFile = Files.createTempFile( - String.format( - "commit-check-%s", - cmd.asset.coordinates() - ), - ".jar", - OWNER_READ_WRITE_ATTRIBUTE - ); - return Tuple.of(req, tempFile); - }) - .flatMap(CommitExtractor::getCommitFromFile) - .mapTry(CommitExtractor::extractCommitFromJarManifest) - .toCompletableFuture(); - } - - private static Try getCommitFromFile(Tuple2 tuple) { - return Try.withResources( - () -> Channels.newChannel(tuple._1.openStream()), - () -> FileChannel.open(tuple._2, StandardOpenOption.WRITE) - ) - .of((remoteFile, transfer) -> { - transfer.transferFrom(remoteFile, 0, Long.MAX_VALUE); - return tuple._2; - }); - } - - private static Optional extractCommitFromJarManifest(Path path) { - return Try.withResources( - () -> new JarInputStream(Files.newInputStream(path, StandardOpenOption.DELETE_ON_CLOSE))) - .of(JarInputStream::getManifest) - .filter(Objects::nonNull) - .mapTry(Manifest::getMainAttributes) - .mapTry(attributes -> attributes.getValue("Git-Commit")) - .map(Optional::ofNullable) - .map(opt -> opt.flatMap(sha -> Try.of(() -> ObjectId.fromString(sha)) - .map(Optional::of) - .getOrElse(Optional::empty) - )) - .getOrElse(Optional::empty); - } - -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/actor/artifacts/FileCollectionOperator.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/actor/artifacts/FileCollectionOperator.java deleted file mode 100644 index c4755bbc..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/actor/artifacts/FileCollectionOperator.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker.actor.artifacts; - -import akka.actor.typed.ActorRef; -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.receptionist.ServiceKey; -import akka.stream.javadsl.Sink; -import akka.stream.javadsl.Source; -import akka.stream.typed.javadsl.ActorFlow; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.List; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -import java.time.Duration; - -public final class FileCollectionOperator { - - public static final ServiceKey KEY = ServiceKey.create(Request.class, "file-collection-operator"); - - @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION) - @JsonSubTypes({ - @JsonSubTypes.Type(TryFindingCommitForFiles.class) - }) - @JsonDeserialize - public sealed interface Request extends Jsonable {} - - @JsonDeserialize - public static final record TryFindingCommitForFiles( - List files, - MavenCoordinates coordinates - ) implements Request {} - - public static Behavior scanJarFilesForCommit( - final ActorRef commitExtractor, - final ActorRef receiverRef - ) { - return Behaviors.setup(ctx -> Behaviors.receive(Request.class) - .onMessage(TryFindingCommitForFiles.class, msg -> { - if (ctx.getLog().isTraceEnabled()) { - ctx.getLog().trace("Received request for {}", msg); - } - final List files = msg.files(); - final var from = Source.from(files); - final var extraction = ActorFlow.ask( - 4, - commitExtractor, - Duration.ofMinutes(20), - CommitExtractor.AttemptFileCommit::new - ); - final var receiverSink = Sink.foreach(receiverRef::tell); - from.via(extraction).to(receiverSink).run(ctx.getSystem()); - return Behaviors.same(); - }) - .build()); - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/actor/artifacts/PotentiallyUsableAsset.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/actor/artifacts/PotentiallyUsableAsset.java deleted file mode 100644 index 3f186135..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/actor/artifacts/PotentiallyUsableAsset.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker.actor.artifacts; - -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -import java.net.URI; - -public record PotentiallyUsableAsset( - MavenCoordinates mavenCoordinates, - String coordinates, - URI downloadURL -) { -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/actor/delegates/RawCommitReceiver.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/actor/delegates/RawCommitReceiver.java deleted file mode 100644 index b1a29844..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/actor/delegates/RawCommitReceiver.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker.actor.delegates; - -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.Behaviors; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import org.spongepowered.downloads.versions.worker.actor.artifacts.CommitExtractor; -import org.spongepowered.downloads.versions.worker.domain.versionedartifact.VersionedArtifactCommand; -import org.spongepowered.downloads.versions.worker.domain.versionedartifact.VersionedArtifactEntity; - -public final class RawCommitReceiver { - - public static Behavior receive() { - return Behaviors.setup(ctx -> { - final var sharding = ClusterSharding.get(ctx.getSystem()); - return Behaviors.receive(CommitExtractor.AssetCommitResponse.class) - .onMessage(CommitExtractor.FailedToRetrieveCommit.class, msg -> { - if (ctx.getLog().isDebugEnabled()) { - ctx.getLog().debug( - "[{}] Failed to retrieve commit", msg.asset().mavenCoordinates().asStandardCoordinates()); - } - sharding.entityRefFor(VersionedArtifactEntity.ENTITY_TYPE_KEY, msg.asset().mavenCoordinates().asStandardCoordinates()) - .tell(new VersionedArtifactCommand.MarkFilesAsErrored()); - return Behaviors.same(); - }) - .onMessage(CommitExtractor.DiscoveredCommitFromFile.class, msg -> { - if (ctx.getLog().isDebugEnabled()) { - ctx.getLog().debug( - "[{}] Retrieved commit", msg.asset().mavenCoordinates().asStandardCoordinates()); - } - sharding.entityRefFor(VersionedArtifactEntity.ENTITY_TYPE_KEY, msg.asset().mavenCoordinates().asStandardCoordinates()) - .tell(new VersionedArtifactCommand.RegisterRawCommit(msg.sha())); - return Behaviors.same(); - }) - .onMessage(CommitExtractor.NoCommitsFoundForFile.class, msg -> { - if (ctx.getLog().isDebugEnabled()) { - ctx.getLog().debug( - "[{}] No commit found", msg.asset().mavenCoordinates().asStandardCoordinates()); - } - sharding.entityRefFor(VersionedArtifactEntity.ENTITY_TYPE_KEY, msg.asset().mavenCoordinates().asStandardCoordinates()) - .tell(new VersionedArtifactCommand.MarkFilesAsErrored()); - return Behaviors.same(); - }) - .build(); - }); - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/domain/versionedartifact/ArtifactEvent.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/domain/versionedartifact/ArtifactEvent.java deleted file mode 100644 index 191c6176..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/domain/versionedartifact/ArtifactEvent.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker.domain.versionedartifact; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.javadsl.persistence.AggregateEvent; -import com.lightbend.lagom.javadsl.persistence.AggregateEventShards; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTag; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTagger; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.List; -import org.spongepowered.downloads.artifact.api.Artifact; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.api.models.VersionedCommit; - -import java.net.URI; - -@JsonDeserialize -@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION) -public sealed interface ArtifactEvent extends AggregateEvent, Jsonable { - - AggregateEventShards INSTANCE = AggregateEventTag.sharded(ArtifactEvent.class, 3); - - @Override - default AggregateEventTagger aggregateTag() { - return INSTANCE; - } - - final record Registered(MavenCoordinates coordinates) implements ArtifactEvent { - } - - final record AssetsUpdated(MavenCoordinates coordinates, List artifacts) implements ArtifactEvent { - } - - final record FilesErrored() implements ArtifactEvent { - } - - final record CommitAssociated(MavenCoordinates coordinates, List repos, String commitSha) - implements ArtifactEvent { - @JsonCreator - public CommitAssociated { - } - } - - final record CommitResolved( - MavenCoordinates coordinates, - URI repo, - VersionedCommit versionedCommit - ) implements ArtifactEvent { - } - - public record CommitUnresolved( - MavenCoordinates coordinates, - String commitId - ) - implements ArtifactEvent { - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/domain/versionedartifact/ArtifactState.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/domain/versionedartifact/ArtifactState.java deleted file mode 100644 index 8b76931c..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/domain/versionedartifact/ArtifactState.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker.domain.versionedartifact; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.HashSet; -import org.spongepowered.downloads.artifact.api.Artifact; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.api.models.VersionedCommit; - -import java.net.URI; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.function.Predicate; - -@JsonDeserialize -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = ArtifactState.Unregistered.class, name = "unregistered"), - @JsonSubTypes.Type(value = ArtifactState.Registered.class, name = "registered") -}) -public sealed interface ArtifactState extends Jsonable { - - io.vavr.collection.List artifacts(); - - default boolean needsArtifactScan() { - return false; - } - - default boolean needsCommitResolution() { - return false; - } - - default Optional commitSha() { - return Optional.empty(); - } - - default io.vavr.collection.List repositories() { - return io.vavr.collection.List.empty(); - } - - final record Unregistered() implements ArtifactState { - private static final Unregistered INSTANCE = new Unregistered(); - - public ArtifactEvent register(VersionedArtifactCommand.Register cmd) { - return new ArtifactEvent.Registered(cmd.coordinates()); - } - - @Override - public io.vavr.collection.List artifacts() { - return io.vavr.collection.List.empty(); - } - } - - @JsonDeserialize - final record FileStatus( - Optional commitSha, - Optional commit, - boolean scanned, - io.vavr.collection.List artifacts, - boolean resolutionError) { - @JsonCreator - public FileStatus { - } - - static final FileStatus EMPTY = new FileStatus( - Optional.empty(), - Optional.empty(), - true, - io.vavr.collection.List.empty(), - false - ); - - public FileStatus withResultionError(boolean b) { - return new FileStatus( - commitSha, - commit, - scanned, - artifacts, - b - ); - } - } - - static Registered register(final ArtifactEvent.Registered event) { - return new Registered(event.coordinates(), HashSet.empty(), FileStatus.EMPTY); - } - - final record Registered( - MavenCoordinates coordinates, - HashSet repo, - FileStatus fileStatus - ) implements ArtifactState { - - public Registered { - } - - @Override - public Optional commitSha() { - return this.fileStatus.commitSha; - } - - @Override - public boolean needsArtifactScan() { - return !this.fileStatus.scanned; - } - - @Override - public boolean needsCommitResolution() { - return this.fileStatus.commit.isEmpty() && this.fileStatus.commitSha.isPresent(); - } - - @Override - public io.vavr.collection.List repositories() { - return this.repo.toList().map(URI::create); - } - - public List addAssets(io.vavr.collection.List artifacts) { - - final var filtered = artifacts - .filter(Predicate.not(a -> this.fileStatus.artifacts.map(Artifact::downloadUrl).contains(a.downloadUrl()))); - if (filtered.isEmpty()) { - return List.of(); - } - - return List.of(new ArtifactEvent.AssetsUpdated(this.coordinates, filtered)); - } - - public ArtifactState withAssets(io.vavr.collection.List artifacts) { - return new Registered( - this.coordinates, - this.repo, - new FileStatus( - this.fileStatus.commitSha, - this.fileStatus.commit, - this.fileStatus.commit.isPresent() || this.fileStatus.commitSha.isPresent(), - artifacts, - false - ) - ); - } - - @Override - public io.vavr.collection.List artifacts() { - return this.fileStatus.artifacts(); - } - - public ArtifactState markFilesErrored() { - return new Registered( - this.coordinates, - this.repo, - new FileStatus( - this.fileStatus().commitSha, - this.fileStatus.commit, - true, - this.fileStatus.artifacts, - false - ) - ); - } - - public List associateCommit(String commitSha) { - if (this.fileStatus.commitSha.isPresent()) { - return List.of(); - } - return List.of(new ArtifactEvent.CommitAssociated(this.coordinates, this.repo.toList(), commitSha)); - } - - public ArtifactState withCommit(String commitSha) { - if (this.fileStatus.commitSha.isPresent()) { - return this; - } - return new Registered( - this.coordinates, - this.repo, - new FileStatus( - Optional.of(commitSha), - this.fileStatus.commit, - true, - this.fileStatus.artifacts, - false - ) - ); - } - - public ArtifactState resolveCommit(ArtifactEvent.CommitResolved event) { - return new Registered( - this.coordinates, - this.repo, - new FileStatus( - this.fileStatus.commitSha, - Optional.of(event.versionedCommit()), - this.fileStatus.scanned, - this.fileStatus.artifacts, - false - ) - ); - } - - public ArtifactState withRepository(String repository) { - return new Registered( - this.coordinates, - this.repo.add(repository), - this.fileStatus - ); - } - - public List failedCommit(String commitId) { - return this.fileStatus.commit.map(v -> Collections.emptyList()) - .orElseGet(() -> List.of(new ArtifactEvent.CommitUnresolved(this.coordinates, commitId))); - } - - public ArtifactState markCommitAsUnresolved(ArtifactEvent.CommitUnresolved a) { - final var status = this.fileStatus.withResultionError(true); - return new Registered( - this.coordinates, - this.repo, - status - ); - } - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/domain/versionedartifact/VersionedArtifactCommand.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/domain/versionedartifact/VersionedArtifactCommand.java deleted file mode 100644 index 25e7b6e9..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/domain/versionedartifact/VersionedArtifactCommand.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker.domain.versionedartifact; - -import akka.Done; -import akka.actor.typed.ActorRef; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.lightbend.lagom.serialization.Jsonable; -import io.vavr.collection.List; -import org.spongepowered.downloads.artifact.api.Artifact; -import org.spongepowered.downloads.artifact.api.ArtifactCollection; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.api.models.VersionRegistration; -import org.spongepowered.downloads.versions.api.models.VersionedCommit; - -import java.net.URI; - -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ - @JsonSubTypes.Type(value = VersionedArtifactCommand.Register.class, name = "register"), - @JsonSubTypes.Type(value = VersionedArtifactCommand.AddAssets.class, name = "add-assets"), - @JsonSubTypes.Type(value = VersionedArtifactCommand.MarkFilesAsErrored.class, name = "errored-assets"), - @JsonSubTypes.Type(value = VersionedArtifactCommand.RegisterAssets.class, name = "register-assets"), - @JsonSubTypes.Type(value = VersionedArtifactCommand.RegisterRawCommit.class, name = "register-sha"), - @JsonSubTypes.Type(value = VersionedArtifactCommand.RegisterResolvedCommit.class, name = "register-commit"), -}) -@JsonDeserialize -public sealed interface VersionedArtifactCommand extends Jsonable { - - final record Register( - MavenCoordinates coordinates, - ActorRef replyTo - ) implements VersionedArtifactCommand { - @JsonCreator - public Register { - } - } - - record RegisterAssets( - MavenCoordinates coordinates, - ArtifactCollection collection, - ActorRef replyTo - ) implements VersionedArtifactCommand { - @JsonCreator - public RegisterAssets { - } - } - - final record AddAssets( - MavenCoordinates coordinates, - List artifacts, - ActorRef replyTo - ) implements VersionedArtifactCommand { - @JsonCreator - public AddAssets { - } - } - - final record MarkFilesAsErrored() implements VersionedArtifactCommand { - @JsonCreator - public MarkFilesAsErrored { - } - } - - final record RegisterRawCommit(String commitSha) implements VersionedArtifactCommand {} - - final record RegisterResolvedCommit( - VersionedCommit versionedCommit, - URI repo, - ActorRef replyTo - ) - implements VersionedArtifactCommand { - @JsonCreator - public RegisterResolvedCommit { - } - } - - record RegisterFailedCommit( - String commitId, - URI repo, - ActorRef replyTo - ) - implements VersionedArtifactCommand { - } - - public record RefreshCommitResolution() implements VersionedArtifactCommand { } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/domain/versionedartifact/VersionedArtifactEntity.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/domain/versionedartifact/VersionedArtifactEntity.java deleted file mode 100644 index 7ff0ebf0..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/domain/versionedartifact/VersionedArtifactEntity.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker.domain.versionedartifact; - -import akka.Done; -import akka.actor.typed.ActorRef; -import akka.actor.typed.Behavior; -import akka.actor.typed.javadsl.ActorContext; -import akka.actor.typed.javadsl.Behaviors; -import akka.actor.typed.javadsl.Routers; -import akka.cluster.sharding.typed.javadsl.EntityContext; -import akka.cluster.sharding.typed.javadsl.EntityTypeKey; -import akka.persistence.typed.PersistenceId; -import akka.persistence.typed.javadsl.CommandHandlerWithReply; -import akka.persistence.typed.javadsl.EventHandler; -import akka.persistence.typed.javadsl.EventSourcedBehaviorWithEnforcedReplies; -import akka.persistence.typed.javadsl.ReplyEffect; -import akka.persistence.typed.javadsl.RetentionCriteria; -import com.lightbend.lagom.javadsl.persistence.AkkaTaggerAdapter; -import org.spongepowered.downloads.artifact.api.Artifact; -import org.spongepowered.downloads.versions.api.models.VersionRegistration; -import org.spongepowered.downloads.versions.worker.actor.artifacts.FileCollectionOperator; -import org.spongepowered.downloads.versions.worker.actor.artifacts.PotentiallyUsableAsset; - -import java.util.Arrays; -import java.util.Set; -import java.util.function.Function; - -public class VersionedArtifactEntity - extends EventSourcedBehaviorWithEnforcedReplies { - public static EntityTypeKey ENTITY_TYPE_KEY = EntityTypeKey.create( - VersionedArtifactCommand.class, "VersionedArtifactEntity"); - private final Function> tagger; - private final ActorRef scanFiles; - private final ActorContext ctx; - - public static Behavior create(final EntityContext context) { - return Behaviors.setup(ctx -> { - final var scanFiles = Routers.group(FileCollectionOperator.KEY); - final var scanRef = ctx.spawn(scanFiles, "scan-files-" + context.getEntityId()); - if (ctx.getLog().isTraceEnabled()) { - ctx.getLog().trace("Entity Context {}", context.getEntityId()); - } - return new VersionedArtifactEntity(context, ctx, scanRef); - }); - } - - private VersionedArtifactEntity( - final EntityContext entityContext, - final ActorContext ctx, - final ActorRef scanRef - ) { - super(PersistenceId.of(entityContext.getEntityTypeKey().name(), entityContext.getEntityId())); - this.ctx = ctx; - this.tagger = AkkaTaggerAdapter.fromLagom(entityContext, ArtifactEvent.INSTANCE); - this.scanFiles = scanRef; - } - - @Override - public ArtifactState emptyState() { - return new ArtifactState.Unregistered(); - } - - @Override - public EventHandler eventHandler() { - final var builder = this.newEventHandlerBuilder(); - builder.forStateType(ArtifactState.Unregistered.class) - .onEvent(ArtifactEvent.Registered.class, ArtifactState::register) - ; - builder.forStateType(ArtifactState.Registered.class) - .onEvent(ArtifactEvent.AssetsUpdated.class, (s, e) -> s.withAssets(e.artifacts())) - .onEvent(ArtifactEvent.FilesErrored.class, (s, e) -> s.markFilesErrored()) - .onEvent(ArtifactEvent.CommitAssociated.class, (s, e) -> s.withCommit(e.commitSha())) - .onEvent(ArtifactEvent.CommitResolved.class, ArtifactState.Registered::resolveCommit) - .onEvent(ArtifactEvent.CommitUnresolved.class, ArtifactState.Registered::markCommitAsUnresolved) - ; - return builder.build(); - } - - @Override - public CommandHandlerWithReply commandHandler() { - final var builder = this.newCommandHandlerWithReplyBuilder(); - builder.forStateType(ArtifactState.Unregistered.class) - .onCommand(VersionedArtifactCommand.Register.class, this::onRegister) - .onCommand(VersionedArtifactCommand.AddAssets.class, this::onEmptyAddAssets) - .onCommand(VersionedArtifactCommand.RegisterAssets.class, this::onEmptyRegisterAssets) - ; - builder.forStateType(ArtifactState.Registered.class) - .onCommand(VersionedArtifactCommand.Register.class, cmd -> this.Effect().reply(cmd.replyTo(), Done.done())) - .onCommand(VersionedArtifactCommand.RegisterAssets.class, this::onRegisterAssets) - .onCommand(VersionedArtifactCommand.AddAssets.class, this::onAddAssets) - .onCommand(VersionedArtifactCommand.MarkFilesAsErrored.class, this::onMarkFilesAsErrored) - .onCommand(VersionedArtifactCommand.RegisterRawCommit.class, this::onRegisterRawCommit) - .onCommand(VersionedArtifactCommand.RegisterResolvedCommit.class, this::handleCompletedCommit) - .onCommand(VersionedArtifactCommand.RegisterFailedCommit.class, this::handleFailedCommit) - .onCommand(VersionedArtifactCommand.RefreshCommitResolution.class, this::handleRefreshCommitStatus) - ; - return builder.build(); - } - - private ReplyEffect handleRefreshCommitStatus(final ArtifactState.Registered state, final VersionedArtifactCommand.RefreshCommitResolution cmd) { - if (state.fileStatus().commit().isPresent()) { - return this.Effect().noReply(); - } - if (state.commitSha().isEmpty()) { - return this.Effect().noReply(); - } - return this.Effect() - .persist(new ArtifactEvent.CommitAssociated(state.coordinates(), state.repo().toList(), state.commitSha().get())) - .thenNoReply(); - } - - private ReplyEffect handleFailedCommit( - final ArtifactState.Registered state, - final VersionedArtifactCommand.RegisterFailedCommit cmd - ) { - if (ctx.getLog().isDebugEnabled()) { - ctx.getLog().debug( - "[{}] Commit {} failed to resolve", state.coordinates().asStandardCoordinates(), cmd.commitId()); - } - return this.Effect() - .persist(state.failedCommit(cmd.commitId())) - .thenReply(cmd.replyTo(), ns -> Done.done()); - } - - private ReplyEffect onRegisterAssets( - final ArtifactState.Registered state, - final VersionedArtifactCommand.RegisterAssets cmd - ) { - final var artifacts = cmd.collection().components(); - if (this.ctx.getLog().isTraceEnabled()) { - this.ctx.getLog().trace("[{}] Current assets: {}", state.coordinates(), state.artifacts()); - this.ctx.getLog().trace("[{}] Adding assets {}", state.coordinates(), artifacts); - } - return this.Effect() - .persist(state.addAssets(artifacts)) - .thenRun(ns -> { - if (this.ctx.getLog().isTraceEnabled()) { - this.ctx.getLog().trace("[{}] Updated assets", state.coordinates()); - } - if (ns.needsArtifactScan()) { - if (this.ctx.getLog().isDebugEnabled()) { - this.ctx.getLog().debug( - "[{}] Telling FileCollectionOperator to fetch commit from files", - state.coordinates() - ); - } - if (this.ctx.getLog().isTraceEnabled()) { - this.ctx.getLog().trace( - "[{}] Assets available {}", state.coordinates(), ns.artifacts().map(Artifact::toString)); - } - final var usableAssets = ns.artifacts() - .filter(a -> "jar".equalsIgnoreCase(a.extension())) - .filter(a -> a.classifier().isEmpty() || a.classifier().filter(""::equals).isPresent()) - .map(a -> new PotentiallyUsableAsset(state.coordinates(), a.extension(), a.downloadUrl())); - if (!usableAssets.isEmpty()) { - if (this.ctx.getLog().isDebugEnabled()) { - this.ctx.getLog().debug("[{}] Assets to scan {}", state.coordinates(), usableAssets); - } - this.scanFiles.tell( - new FileCollectionOperator.TryFindingCommitForFiles(usableAssets, state.coordinates())); - } - } - }) - .thenReply(cmd.replyTo(), ns -> new VersionRegistration.Response.RegisteredArtifact(cmd.coordinates())); - } - - private ReplyEffect onEmptyRegisterAssets( - final VersionedArtifactCommand.RegisterAssets cmd - ) { - this.ctx.getLog().warn("[{}] Registering collection on empty state", cmd.coordinates().asStandardCoordinates()); - return this.Effect() - .persist(Arrays.asList(new ArtifactEvent.Registered(cmd.coordinates()), new ArtifactEvent.AssetsUpdated(cmd.coordinates(), cmd.collection().components()))) - .thenReply(cmd.replyTo(), ns -> new VersionRegistration.Response.RegisteredArtifact(cmd.coordinates())); - } - - private ReplyEffect onEmptyAddAssets( - final VersionedArtifactCommand.AddAssets cmd - ) { - this.ctx.getLog().warn("[{}] Registering assets with empty state", cmd.coordinates().asStandardCoordinates()); - return this.Effect() - .persist(Arrays.asList(new ArtifactEvent.Registered(cmd.coordinates()), new ArtifactEvent.AssetsUpdated(cmd.coordinates(), cmd.artifacts()))) - .thenReply(cmd.replyTo(), ns -> Done.done()); - } - - private ReplyEffect handleCompletedCommit( - final ArtifactState.Registered state, final VersionedArtifactCommand.RegisterResolvedCommit cmd - ) { - if (this.ctx.getLog().isTraceEnabled()) { - this.ctx.getLog().trace("Received completed commit {}", cmd.versionedCommit()); - } - if (state.fileStatus().commit().isPresent()) { - this.ctx.getLog().warn("[{}] Ignoring {} with already registered commit {}", state.coordinates().asStandardCoordinates(), cmd.versionedCommit(), state.fileStatus().commit().get()); - return this.Effect().reply(cmd.replyTo(), Done.done()); - } - return this.Effect() - .persist(new ArtifactEvent.CommitResolved(state.coordinates(), cmd.repo(), cmd.versionedCommit())) - .thenReply(cmd.replyTo(), ns -> Done.done()); - } - - private ReplyEffect onRegisterRawCommit( - final ArtifactState.Registered state, - final VersionedArtifactCommand.RegisterRawCommit cmd - ) { - this.ctx.getLog().debug("Raw commit registered {}", cmd); - return this.Effect() - .persist(state.associateCommit(cmd.commitSha())) - .thenNoReply(); - } - - private ReplyEffect onMarkFilesAsErrored(ArtifactState state, VersionedArtifactCommand.MarkFilesAsErrored cmd) { - if (state.needsArtifactScan()) { - return this.Effect() - .persist(new ArtifactEvent.FilesErrored()) - .thenRun(ns -> this.ctx.getLog().debug("File as failed {}", ns)) - .thenNoReply(); - } - return this.Effect() - .persist(new ArtifactEvent.FilesErrored()) - .thenRun(ns -> this.ctx.getLog().debug("File as failed {}", ns)) - .thenNoReply(); - } - - private ReplyEffect onAddAssets( - final ArtifactState.Registered state, - final VersionedArtifactCommand.AddAssets cmd - ) { - if (this.ctx.getLog().isTraceEnabled()) { - this.ctx.getLog().trace("[{}] Current assets: {}", state.coordinates(), state.artifacts()); - this.ctx.getLog().trace("[{}] Adding assets {}", state.coordinates(), cmd.artifacts()); - } - return this.Effect() - .persist(state.addAssets(cmd.artifacts())) - .thenRun(ns -> { - if (this.ctx.getLog().isTraceEnabled()) { - this.ctx.getLog().trace("[{}] Updated assets", state.coordinates()); - } - if (ns.needsArtifactScan()) { - if (this.ctx.getLog().isDebugEnabled()) { - this.ctx.getLog().debug( - "[{}] Telling FileCollectionOperator to fetch commit from files", - state.coordinates() - ); - } - if (this.ctx.getLog().isTraceEnabled()) { - this.ctx.getLog().trace( - "[{}] Assets available {}", state.coordinates(), ns.artifacts().map(Artifact::toString)); - } - final var usableAssets = ns.artifacts() - .filter(a -> "jar".equalsIgnoreCase(a.extension())) - .filter(a -> a.classifier().isEmpty() || a.classifier().filter(""::equals).isPresent()) - .map(a -> new PotentiallyUsableAsset(state.coordinates(), a.extension(), a.downloadUrl())); - if (!usableAssets.isEmpty()) { - if (this.ctx.getLog().isDebugEnabled()) { - this.ctx.getLog().debug("[{}] Assets to scan {}", state.coordinates(), usableAssets); - } - this.scanFiles.tell( - new FileCollectionOperator.TryFindingCommitForFiles(usableAssets, state.coordinates())); - } - } - }) - .thenReply(cmd.replyTo(), ns -> Done.done()); - } - - private ReplyEffect onRegister( - final ArtifactState.Unregistered state, - final VersionedArtifactCommand.Register cmd - ) { - return this.Effect() - .persist(state.register(cmd)) - .thenReply(cmd.replyTo(), ns -> Done.done()); - } - - - @Override - public Set tagsFor(final ArtifactEvent assetEvent) { - return this.tagger.apply(assetEvent); - } - - @Override - public RetentionCriteria retentionCriteria() { - return RetentionCriteria.snapshotEvery(5, 2); - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/intro.md b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/intro.md deleted file mode 100644 index 11aef813..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/intro.md +++ /dev/null @@ -1,40 +0,0 @@ -# Versions Worker - -Versions Worker is a collection of workers that perform versioned artifact introspection. -The primary use case is to organize all of the reactive pieces of work that come about from -"a new version is registered". The workers are designed to be pluggable, and the -[`VersionsWorkerSupervisor`](VersionsWorkerSupervisor.java) is the main entry point for this -side of the system. - -## Overview - -While a normal Lagom application is a single service with a single Guice module, the -Versions Worker has a separate paired module to initialize the guardian of the child worker -actors. There is an additional side effect that is gained out of this: the supervisor is able -to reference the VersionsService indirectly as a consumer, and therefore subscribe to the -[`VersionsService.artifactUpdateTopic()`](./../../../../../../../../../versions-api/src/main/java/org/spongepowered/downloads/versions/api/VersionsService.java) topic. -An advantage as well is that while each binary of the workers are capable of delegating their -work to avoid blocking eachother, the VersionsService remains active for the primitive information -as a query reference. - -## Interfaces between systems - -### Kafka - -Through heavy use of Akka Clustering and Akka remoting, we can spin off specific workloads to -specific instances, such as git commit resolution, or artifact binary downloading and processing, -or changelog parsing. To safely coordinate the flow of work and prevent soft failures, we rely heavily -on the TopicSubscriber pattern to have the assurance the work will be done, even if the actors/binaries -restart for any reason. Likewise, we can coordinate the flow of work to single instances for longer -running jobs, while allowing for rapid fire handling of "basic" data being transformed and -messages sent off. - -### ReadSideProcessors - -We take advantage of EventSourceBased actors to describe the state of the various entities (such as -a versioned artifact with assets not having a git commit, to becoming a git commit hash extracted -from the binary, to extracting the commit details from the git repository, to resolving the changes -between two ordinal versions) and take advantage that we can populate our database with the important -details from the various events, therefor enabling the Query service to simply query for the state of -whatever models it is interested in. - diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/readside/CommitProcessor.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/readside/CommitProcessor.java deleted file mode 100644 index db808b8d..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/readside/CommitProcessor.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker.readside; - -import akka.actor.ActorSystem; -import com.lightbend.lagom.javadsl.persistence.AggregateEventTag; -import com.lightbend.lagom.javadsl.persistence.ReadSide; -import com.lightbend.lagom.javadsl.persistence.ReadSideProcessor; -import com.lightbend.lagom.javadsl.persistence.jpa.JpaReadSide; -import com.lightbend.lagom.javadsl.persistence.jpa.JpaSession; -import io.vavr.collection.List; -import io.vavr.control.Try; -import org.pcollections.PSequence; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.api.models.VersionedChangelog; -import org.spongepowered.downloads.versions.api.models.VersionedCommit; -import org.spongepowered.downloads.versions.worker.domain.versionedartifact.ArtifactEvent; -import org.spongepowered.downloads.versions.worker.readside.model.JpaVersionChangelog; -import org.spongepowered.downloads.versions.worker.readside.model.JpaVersionedArtifact; - -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.persistence.EntityManager; -import java.net.URI; -import java.net.URL; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.util.Optional; - -@Singleton -public final record CommitProcessor( - ReadSide readSide, - JpaSession session -) { - - @Inject - public CommitProcessor { - readSide.register(CommitWriter.class); - } - - static final class CommitWriter extends ReadSideProcessor { - - private static final Logger LOGGER = LoggerFactory.getLogger("CommitWriter"); - public static final ZonedDateTime EPOCH = ZonedDateTime.of(LocalDateTime.MIN, ZoneOffset.UTC); - - private final JpaReadSide readSide; - - @Inject - CommitWriter(final JpaReadSide readSide, final JpaSession session, final ActorSystem system) { - this.readSide = readSide; - } - - @Override - public ReadSideHandler buildHandler() { - return this.readSide.builder("version-commit-writer") - .setGlobalPrepare((em) -> { - }) - .setEventHandler(ArtifactEvent.FilesErrored.class, (em, e) -> { - }) - .setEventHandler(ArtifactEvent.Registered.class, (em, e) -> { - }) - .setEventHandler(ArtifactEvent.AssetsUpdated.class, (em, e) -> { - }) - .setEventHandler( - ArtifactEvent.CommitAssociated.class, - (em, e) -> { - final var coordinates = e.coordinates(); - final var results = getVersionedArtifacts(em, coordinates); - if (results.isEmpty()) { - return; - } - final JpaVersionedArtifact jpaVersionedArtifact = results.get(0); - if (jpaVersionedArtifact.getChangelog() == null) { - final var jpaVersionChangelog = new JpaVersionChangelog(); - jpaVersionedArtifact.setChangelog(jpaVersionChangelog); - } - final var jpaChangelog = jpaVersionedArtifact.getChangelog(); - final var author = new VersionedCommit.Author("", ""); - final var committer = new VersionedCommit.Commiter("", ""); - final ZonedDateTime epoch = ZonedDateTime.of( - LocalDate.EPOCH, LocalTime.MAX, ZoneId.systemDefault()); - final var artifactRepo = jpaVersionedArtifact.getArtifact().getRepo(); - final var commitLink = Optional.ofNullable(artifactRepo).map(URI::create); - final VersionedCommit rawCommit = new VersionedCommit( - "", "", e.commitSha(), author, committer, commitLink, epoch); - final var changelog = new VersionedChangelog( - List.of(new VersionedChangelog.IndexedCommit(rawCommit, List.empty())), true); - jpaChangelog.setSha(rawCommit.sha()); - jpaChangelog.setChangelog(changelog); - Try.ofSupplier(commitLink::get) - .mapTry(URI::toURL) - .toJavaOptional() - .ifPresent(jpaChangelog::setRepo); - em.persist(jpaChangelog); - } - ) - .setEventHandler( - ArtifactEvent.CommitResolved.class, - (em, e) -> { - final var coordinates = e.coordinates(); - final var results = getVersionedArtifacts(em, coordinates); - if (results.isEmpty()) { - return; - } - final JpaVersionedArtifact jpaVersionedArtifact = results.get(0); - if (jpaVersionedArtifact.getChangelog() == null) { - final var jpaVersionChangelog = new JpaVersionChangelog(); - jpaVersionedArtifact.setChangelog(jpaVersionChangelog); - jpaVersionChangelog.setSha(e.versionedCommit().sha()); - jpaVersionChangelog.setBranch("foo"); - final var commitUrl = e.repo().toString().replace(".git", "") + "/commit/" + e.versionedCommit().sha(); - Try.of(() -> new URL(commitUrl)) - .toJavaOptional() - .ifPresent(jpaVersionChangelog::setRepo); - } - final var jpaChangelog = jpaVersionedArtifact.getChangelog(); - final var commit = new VersionedChangelog.IndexedCommit(e.versionedCommit(), List.empty()); - final var changelog = new VersionedChangelog(List.of(commit), true); - jpaChangelog.setChangelog(changelog); - em.persist(jpaChangelog); - } - ) - .setEventHandler( - ArtifactEvent.CommitUnresolved.class, - (em, e) -> { - final var coordinates = e.coordinates(); - final var results = getVersionedArtifacts(em, coordinates); - if (results.isEmpty()) { - return; - } - final JpaVersionedArtifact jpaVersionedArtifact = results.get(0); - final var changelog = new VersionedChangelog(List.of( - new VersionedChangelog.IndexedCommit( - convertToInvalidCommit(e), List.empty() - )), false); - if (jpaVersionedArtifact.getChangelog() == null) { - final var jpaVersionChangelog = new JpaVersionChangelog(); - jpaVersionedArtifact.setChangelog(jpaVersionChangelog); - jpaVersionChangelog.setSha(e.commitId()); - jpaVersionChangelog.setBranch("foo"); - jpaVersionChangelog.setChangelog(changelog); - } - final var jpaChangelog = jpaVersionedArtifact.getChangelog(); - jpaChangelog.setChangelog(changelog); - em.persist(jpaChangelog); - } - ) - .build(); - } - - private static VersionedCommit convertToInvalidCommit(ArtifactEvent.CommitUnresolved e) { - return new VersionedCommit( - "Commit not available", - """ - This build has a commit that cannot be resolved, it may be - possible to resolve it in some backup, but generally this - build will not be supported by the community due to the - lack of a commit. - """, - e.commitId(), - new VersionedCommit.Author("", ""), - new VersionedCommit.Commiter("", ""), - Optional.empty(), - EPOCH - ); - } - - private java.util.List getVersionedArtifacts( - EntityManager em, MavenCoordinates coordinates - ) { - return em.createNamedQuery( - "GitVersionedArtifact.findByCoordinates", JpaVersionedArtifact.class) - .setParameter("groupId", coordinates.groupId) - .setParameter("artifactId", coordinates.artifactId) - .setParameter("version", coordinates.version) - .setMaxResults(1) - .getResultList(); - } - - @Override - public PSequence> aggregateTags() { - return ArtifactEvent.INSTANCE.allTags(); - } - } - -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/readside/model/JpaVersionChangelog.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/readside/model/JpaVersionChangelog.java deleted file mode 100644 index 9a43464d..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/readside/model/JpaVersionChangelog.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker.readside.model; - -import com.vladmihalcea.hibernate.type.json.JsonBinaryType; -import org.hibernate.annotations.Type; -import org.hibernate.annotations.TypeDef; -import org.hibernate.annotations.TypeDefs; -import org.spongepowered.downloads.versions.api.models.VersionedChangelog; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.MapsId; -import javax.persistence.OneToOne; -import javax.persistence.Table; -import java.net.URL; -import java.util.Objects; - -@Entity(name = "VersionedChangelog") -@Table(name = "version_changelogs", schema = "version") -@TypeDefs({ - @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) -}) -public class JpaVersionChangelog { - - @Id - @Column(name = "version_id") - private long id; - - @OneToOne(mappedBy = "changelog", optional = false) - @MapsId("id") - private JpaVersionedArtifact artifact; - - @Column(name = "commit_sha", nullable = false) - private String sha; - - @Type(type = "jsonb") - @Column(name = "changelog", columnDefinition = "jsonb") - private VersionedChangelog changelog; - - @Column(name = "repo") - private URL repo; - - @Column(name = "branch") - private String branch; - - public long getId() { - return id; - } - - void setId(long id) { - this.id = id; - } - - public void setSha(final String sha) { - this.sha = sha; - } - - public void setRepo(final URL repo) { - this.repo = repo; - } - - public void setBranch(final String branch) { - this.branch = branch; - } - - public JpaVersionedArtifact getArtifact() { - return artifact; - } - - void setArtifact(JpaVersionedArtifact artifact) { - this.artifact = artifact; - } - - public void setChangelog(VersionedChangelog changelog) { - this.changelog = changelog; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - JpaVersionChangelog that = (JpaVersionChangelog) o; - return id == that.id && Objects.equals(artifact, that.artifact) && Objects.equals( - sha, that.sha) && Objects.equals(changelog, that.changelog) && Objects.equals( - repo, that.repo) && Objects.equals(branch, that.branch); - } - - @Override - public int hashCode() { - return Objects.hash(id, artifact, sha, changelog, repo, branch); - } - - @Override - public String toString() { - return "JpaVersionChangelog{" + - "id=" + id + - ", artifact=" + artifact + - ", sha='" + sha + '\'' + - ", changelog=" + changelog + - ", repo=" + repo + - ", branch='" + branch + '\'' + - '}'; - } -} diff --git a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/readside/model/JpaVersionedArtifact.java b/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/readside/model/JpaVersionedArtifact.java deleted file mode 100644 index d2b702f8..00000000 --- a/versions-impl/src/main/java/org/spongepowered/downloads/versions/worker/readside/model/JpaVersionedArtifact.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.worker.readside.model; - -import org.spongepowered.downloads.versions.server.readside.JpaArtifact; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToOne; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; -import java.io.Serializable; -import java.util.Objects; - -@Entity(name = "GitVersionedArtifact") -@Table(name = "artifact_versions", - schema = "version", - uniqueConstraints = @UniqueConstraint( - columnNames = {"artifact_id", "version"}, - name = "artifact_version_unique_idx") -) -@NamedQueries({ - @NamedQuery( - name = "GitVersionedArtifact.findByCoordinates", - query = - """ - select distinct v from GitVersionedArtifact v - where v.artifact.groupId = :groupId and v.artifact.artifactId = :artifactId and v.version = :version - """ - ) -}) -public class JpaVersionedArtifact implements Serializable { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id", - updatable = false, - nullable = false) - private long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "artifact_id", - foreignKey = @ForeignKey(name = "artifact_versions_artifact_id_fkey"), - nullable = false) - private JpaArtifact artifact; - - @Column(name = "version", - nullable = false) - private String version; - - @OneToOne(targetEntity = JpaVersionChangelog.class, fetch = FetchType.LAZY) - @JoinColumn(name = "id", referencedColumnName = "version_id") - private JpaVersionChangelog changelog; - - public JpaArtifact getArtifact() { - return artifact; - } - - public String getVersion() { - return version; - } - - public JpaVersionChangelog getChangelog() { - return changelog; - } - - public void setChangelog(final JpaVersionChangelog changelog) { - this.changelog = changelog; - changelog.setId(this.id); - changelog.setArtifact(this); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - JpaVersionedArtifact that = (JpaVersionedArtifact) o; - return id == that.id && Objects.equals(artifact, that.artifact) && Objects.equals( - version, that.version) && Objects.equals(changelog, that.changelog); - } - - @Override - public int hashCode() { - return Objects.hash(id, artifact, version, changelog); - } -} diff --git a/versions-impl/src/main/resources/META-INF/persistence.xml b/versions-impl/src/main/resources/META-INF/persistence.xml deleted file mode 100644 index 344130a1..00000000 --- a/versions-impl/src/main/resources/META-INF/persistence.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - org.hibernate.jpa.HibernatePersistenceProvider - DefaultDS - org.spongepowered.downloads.versions.server.readside.JpaArtifactVersion - org.spongepowered.downloads.versions.server.readside.JpaArtifact - org.spongepowered.downloads.versions.server.readside.JpaArtifactTag - org.spongepowered.downloads.versions.server.readside.JpaArtifactRegexRecommendation - org.spongepowered.downloads.versions.server.readside.JpaVersionedArtifactAsset - org.spongepowered.downloads.versions.worker.readside.model.JpaVersionedArtifact - org.spongepowered.downloads.versions.worker.readside.model.JpaVersionChangelog - true - - - - - - - - - diff --git a/versions-impl/src/main/resources/application.conf b/versions-impl/src/main/resources/application.conf deleted file mode 100644 index 4e842b41..00000000 --- a/versions-impl/src/main/resources/application.conf +++ /dev/null @@ -1,42 +0,0 @@ -play.modules.enabled = ${play.modules.enabled} [ - org.spongepowered.downloads.versions.server.VersionsModule, - org.spongepowered.downloads.versions.worker.WorkerModule -] - -db.default { - driver = "org.postgresql.Driver" - url = "jdbc:postgresql://localhost:5432/default" - url = ${?POSTGRES_URL} - username = admin - username = ${?POSTGRES_USERNAME} - password = password - password = ${?POSTGRES_PASSWORD} -} - -jdbc-defaults.slick.profile = "slick.jdbc.PostgresProfile$" - -lagom.persistence.jpa { - # This must match the name in persistence.xml - persistence-unit = "default" -} -akka.serialization.jackson { - jackson-modules += "io.vavr.jackson.datatype.VavrModule" -} -akka.actor.allow-java-serialization = true - -play.http.parser.maxMemoryBuffer = 200k - -akka { - extensions = ${akka.extensions} [ - "org.spongepowered.downloads.versions.worker.VersionExtension" - ] -} - -play.filters.disabled += "play.filters.csrf.CSRFFilter" - -play.filters.enabled += "play.filters.cors.CORSFilter" -play.filters.cors { - pathPrefixes = ["/versions"] - allowedHttpMethods = ["GET", "POST", "PATCH"] - preflightMaxAge = 3 days -} diff --git a/versions-impl/src/main/resources/logback.xml b/versions-impl/src/main/resources/logback.xml deleted file mode 100644 index dbe42030..00000000 --- a/versions-impl/src/main/resources/logback.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - System.out - - %date{hh:MM:ss.SSS} [%level] [%thread] [%logger{5}/%marker] - %coloredLevel %msg%n - - - - - 8192 - true - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/versions-impl/src/main/resources/reference.conf b/versions-impl/src/main/resources/reference.conf deleted file mode 100644 index ec0e520b..00000000 --- a/versions-impl/src/main/resources/reference.conf +++ /dev/null @@ -1,8 +0,0 @@ -akka.cluster.roles = ${akka.cluster.roles} ["asset-fetcher", "file-extractor", "refresher", "commit-resolver"] -systemofadownload.versions { - commit-fetch { - pool-size = 8 - parallelism = 4 - timeout = "1h" - } -} diff --git a/versions-impl/src/test/java/org/spongepowered/downloads/test/server/collection/VersionedArtifactAggregateTest.java b/versions-impl/src/test/java/org/spongepowered/downloads/test/server/collection/VersionedArtifactAggregateTest.java deleted file mode 100644 index bdcbeb20..00000000 --- a/versions-impl/src/test/java/org/spongepowered/downloads/test/server/collection/VersionedArtifactAggregateTest.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.spongepowered.downloads.test.server.collection; - - -import io.vavr.collection.HashMap; -import io.vavr.collection.List; -import io.vavr.control.Option; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.Test; -import org.spongepowered.downloads.artifact.api.Artifact; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.versions.server.domain.ACEvent; -import org.spongepowered.downloads.versions.server.domain.State; - -import java.net.URISyntaxException; -import java.net.URL; -import java.util.Optional; - -public class VersionedArtifactAggregateTest { - - @Test - public void emptyStateTransition() { - final State empty = State.empty(); - Assertions.assertFalse(empty.isRegistered()); - } - - @Test - public void emptyStateRegistration() { - final State example = State.empty().register( - new ACEvent.ArtifactCoordinatesUpdated(new ArtifactCoordinates("com.example", "example"))); - - Assertions.assertTrue(example.isRegistered()); - } - - @Test - public void stateRegistrationWithVersion() throws URISyntaxException { - final ArtifactCoordinates exampleCoordinates = new ArtifactCoordinates("com.example", "example"); - State.ACState example = State.empty().register(new ACEvent.ArtifactCoordinatesUpdated(exampleCoordinates)); - final var exampleVersion = exampleCoordinates.version("0.0.1"); - final URL exampleJar = this.getClass().getClassLoader().getResource("test-jar.jar"); - Assumptions.assumeTrue(exampleJar != null); - Assertions.assertNotNull(exampleJar, "Example jar is missing, needed for various tests"); - final var exampleArtifact = new Artifact(Optional.of("universal"), exampleJar.toURI(), "foo", "bar", ".jar"); - example = example.withAddedArtifacts(exampleVersion, List.of(exampleArtifact)); - Assertions.assertFalse(example.versionedArtifacts().isEmpty(), "Should have a new versioned artifact"); - final Option> artifacts = example.versionedArtifacts().get(exampleVersion.version); - Assertions.assertFalse(artifacts.isEmpty(), "The artifact list for " + exampleVersion.version + " should be non-empty"); - Assertions.assertEquals(artifacts.get(), List.of(exampleArtifact), "List should be equal"); - - final var exampleNoClassifier = new Artifact(Optional.empty(), exampleJar.toURI(), "foo", "bar", ".jar"); - example = example.withAddedArtifacts(exampleVersion, List.of(exampleNoClassifier)); - Assertions.assertFalse(example.versionedArtifacts().isEmpty(), "Should have a new versioned artifact"); - final Option> newArtifacts = example.versionedArtifacts().get(exampleVersion.version); - Assertions.assertFalse(newArtifacts.isEmpty(), "The artifact list for " + exampleVersion.version + " should be non-empty"); - Assertions.assertEquals(newArtifacts.get(), List.of(exampleArtifact, exampleNoClassifier), "List should be equal"); - } - - @Test - public void stateReordering() { - final ArtifactCoordinates exampleCoordinates = new ArtifactCoordinates("com.example", "example"); - State.ACState example = State.empty().register(new ACEvent.ArtifactCoordinatesUpdated(exampleCoordinates)); - final var acEvents = example.addVersion(exampleCoordinates.version("0.0.1")); - Assertions.assertEquals(acEvents.size(), 1, "Should have one event"); - final var zero1 = example.withVersion("0.0.1"); - final var newEvents = zero1.addVersion(exampleCoordinates.version("0.0.2")); - Assertions.assertEquals(newEvents.size(), 1, "0.0.2 should be the only new event"); - final var zero3 = zero1.withVersion("0.0.3"); - final var addingZero2 = zero3.addVersion(exampleCoordinates.version("0.0.2")); - Assertions.assertEquals(2, addingZero2.size(), "Should have 1 event"); - Assertions.assertEquals(new ACEvent.ArtifactVersionRegistered(exampleCoordinates.version("0.0.2"), 1), addingZero2.get(0),"Should have the new event"); - Assertions.assertEquals(new ACEvent.ArtifactVersionsResorted(exampleCoordinates, HashMap.of("0.0.2", 1, "0.0.3", 2)), addingZero2.get(1),"Should have the new event"); - } - -} diff --git a/versions-impl/src/test/java/org/spongepowered/downloads/test/versions/worker/CommitExtractorTest.java b/versions-impl/src/test/java/org/spongepowered/downloads/test/versions/worker/CommitExtractorTest.java deleted file mode 100644 index 52fc46ac..00000000 --- a/versions-impl/src/test/java/org/spongepowered/downloads/test/versions/worker/CommitExtractorTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.spongepowered.downloads.test.versions.worker; - - -import akka.actor.testkit.typed.javadsl.TestKitJunitResource; -import akka.actor.testkit.typed.javadsl.TestProbe; -import akka.persistence.testkit.javadsl.EventSourcedBehaviorTestKit; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.worker.actor.artifacts.CommitExtractor; -import org.spongepowered.downloads.versions.worker.actor.artifacts.PotentiallyUsableAsset; - -import java.net.URISyntaxException; - -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class CommitExtractorTest { - - public static final TestKitJunitResource testkit = new TestKitJunitResource(EventSourcedBehaviorTestKit.config()); - - @Test - public void VerifyCommitExtraction() throws URISyntaxException { - final TestProbe replyTo = testkit.createTestProbe(); - final var extractor = testkit.spawn(CommitExtractor.extractCommitFromAssets()); - final var coordinates = MavenCoordinates.parse("org.spongepowered.downloads:test-bin:0.0.1"); - - final var testJarPath = this.getClass().getClassLoader().getResource("test-jar.jar"); - Assertions.assertNotNull(testJarPath, "test-jar.jar is null"); - final var asset = new PotentiallyUsableAsset(coordinates, ".jar", testJarPath.toURI()); - extractor.tell(new CommitExtractor.AttemptFileCommit(asset, replyTo.ref())); - - replyTo.expectMessage(new CommitExtractor.DiscoveredCommitFromFile("d838fee5d8e834ba9fd4d1c4fe0f8214d6dc90fc", asset)); - } - - @Test - public void InvalidCommitFailure() throws URISyntaxException { - final TestProbe replyTo = testkit.createTestProbe(); - final var extractor = testkit.spawn(CommitExtractor.extractCommitFromAssets()); - final var coordinates = MavenCoordinates.parse("org.spongepowered.downloads:test-bin:0.0.1"); - final var testJarPath = this.getClass().getClassLoader().getResource("bad-commit-test-jar.jar"); - Assertions.assertNotNull(testJarPath, "bad-commit-test-jar.jar is null"); - final var asset = new PotentiallyUsableAsset(coordinates, ".jar", testJarPath.toURI()); - extractor.tell(new CommitExtractor.AttemptFileCommit(asset, replyTo.ref())); - - replyTo.expectMessageClass(CommitExtractor.NoCommitsFoundForFile.class); - } - - @Test - public void NoCommitFailure() throws URISyntaxException { - final TestProbe replyTo = testkit.createTestProbe(); - final var extractor = testkit.spawn(CommitExtractor.extractCommitFromAssets()); - final var coordinates = MavenCoordinates.parse("org.spongepowered.downloads:test-bin:0.0.1"); - final var testJarPath = this.getClass().getClassLoader().getResource("no-commit-test-jar.jar"); - Assertions.assertNotNull(testJarPath, "no-commit-test-jar.jar is null"); - final var asset = new PotentiallyUsableAsset(coordinates, ".jar", testJarPath.toURI()); - extractor.tell(new CommitExtractor.AttemptFileCommit(asset, replyTo.ref())); - - replyTo.expectMessageClass(CommitExtractor.NoCommitsFoundForFile.class); - } - -} diff --git a/versions-impl/src/test/java/org/spongepowered/downloads/test/versions/worker/FileCollectionOperatorTest.java b/versions-impl/src/test/java/org/spongepowered/downloads/test/versions/worker/FileCollectionOperatorTest.java deleted file mode 100644 index 2bb12db0..00000000 --- a/versions-impl/src/test/java/org/spongepowered/downloads/test/versions/worker/FileCollectionOperatorTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.spongepowered.downloads.test.versions.worker; - -import akka.actor.testkit.typed.javadsl.FishingOutcomes; -import akka.actor.testkit.typed.javadsl.TestKitJunitResource; -import akka.persistence.testkit.javadsl.EventSourcedBehaviorTestKit; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; -import io.vavr.collection.List; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.worker.actor.artifacts.CommitExtractor; -import org.spongepowered.downloads.versions.worker.actor.artifacts.FileCollectionOperator; -import org.spongepowered.downloads.versions.worker.actor.artifacts.PotentiallyUsableAsset; - -import java.io.File; -import java.net.URISyntaxException; -import java.time.Duration; - -public class FileCollectionOperatorTest { - - private TestKitJunitResource testKit; - - private static Config config; - - @BeforeAll - public static void setupConfig() { - final var resource = VersionedArtifactEntityTest.class.getClassLoader().getResource("application-test.conf"); - Assertions.assertNotNull(resource); - final var file = new File(resource.getFile()); - config = ConfigFactory.parseFile(file); - } - - @BeforeEach - public void setup() { - this.testKit = new TestKitJunitResource(config.resolve() // Resolve the config first - .withFallback(EventSourcedBehaviorTestKit.config())); - } - - @AfterEach - public void teardown() { - testKit.system().terminate(); - } - - @Test - public void testKickoffFileCollection() throws URISyntaxException { - final var commandProbe = this.testKit.createTestProbe(); - final var responseProbe = this.testKit.createTestProbe(); - final var requestBehavior = FileCollectionOperator.scanJarFilesForCommit( - commandProbe.ref(), responseProbe.ref()); - final var testJarPath = this.getClass().getClassLoader().getResource("test-jar.jar"); - Assertions.assertNotNull(testJarPath, "test-jar.jar is missing"); - final var spawn = this.testKit.spawn(requestBehavior); - final var coordinates = MavenCoordinates.parse("com.example:example:1.0.0"); - final var asset = new PotentiallyUsableAsset( - coordinates, ".jar", testJarPath.toURI()); - spawn.tell(new FileCollectionOperator.TryFindingCommitForFiles(List.of(asset), coordinates)); - commandProbe.fishForMessage(Duration.ofSeconds(10), msg -> { - if (!(msg instanceof CommitExtractor.AttemptFileCommit afc)) { - return FishingOutcomes.fail("got a different command"); - } - afc.ref().tell(new CommitExtractor.NoCommitsFoundForFile(afc.asset())); - return FishingOutcomes.complete(); - }); - responseProbe.fishForMessage(Duration.ofSeconds(10), msg -> { - if (!(msg instanceof CommitExtractor.NoCommitsFoundForFile)) { - return FishingOutcomes.fail("got a different response than test expected"); - } - return FishingOutcomes.complete(); - }); - } -} diff --git a/versions-impl/src/test/java/org/spongepowered/downloads/test/versions/worker/VersionedArtifactEntityTest.java b/versions-impl/src/test/java/org/spongepowered/downloads/test/versions/worker/VersionedArtifactEntityTest.java deleted file mode 100644 index 470577ce..00000000 --- a/versions-impl/src/test/java/org/spongepowered/downloads/test/versions/worker/VersionedArtifactEntityTest.java +++ /dev/null @@ -1,131 +0,0 @@ -package org.spongepowered.downloads.test.versions.worker; - -import akka.Done; -import akka.actor.testkit.typed.javadsl.FishingOutcomes; -import akka.actor.testkit.typed.javadsl.TestKitJunitResource; -import akka.actor.testkit.typed.javadsl.TestProbe; -import akka.actor.typed.receptionist.Receptionist; -import akka.cluster.sharding.typed.javadsl.ClusterSharding; -import akka.cluster.sharding.typed.javadsl.EntityContext; -import akka.persistence.testkit.javadsl.EventSourcedBehaviorTestKit; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; -import io.vavr.collection.List; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.spongepowered.downloads.artifact.api.Artifact; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.worker.actor.artifacts.FileCollectionOperator; -import org.spongepowered.downloads.versions.worker.domain.versionedartifact.ArtifactEvent; -import org.spongepowered.downloads.versions.worker.domain.versionedartifact.ArtifactState; -import org.spongepowered.downloads.versions.worker.domain.versionedartifact.VersionedArtifactCommand; -import org.spongepowered.downloads.versions.worker.domain.versionedartifact.VersionedArtifactEntity; - -import java.io.File; -import java.net.URISyntaxException; -import java.time.Duration; -import java.util.Optional; - -public class VersionedArtifactEntityTest { - - private TestKitJunitResource testKit; - private EventSourcedBehaviorTestKit eventSourcedTestKit; - - private static Config config; - private TestProbe fileOperatorProbe; - - @BeforeAll - public static void setupConfig() { - final var resource = VersionedArtifactEntityTest.class.getClassLoader().getResource("application-test.conf"); - Assertions.assertNotNull(resource); - final var file = new File(resource.getFile()); - config = ConfigFactory.parseFile(file); - } - - @BeforeEach - public void setup() { - this.testKit = new TestKitJunitResource(config.resolve() // Resolve the config first - .withFallback(EventSourcedBehaviorTestKit.config())); - testKit.system().log().info("Starting test"); - final var probe = this.testKit.createTestProbe(ClusterSharding.ShardCommand.class); - - final var ctx = new EntityContext<>( - VersionedArtifactEntity.ENTITY_TYPE_KEY, - "com.example:example:1.0.0", - probe.ref() - ); - this.fileOperatorProbe = this.testKit.createTestProbe(); - this.testKit.system().receptionist().tell(Receptionist.register(FileCollectionOperator.KEY, fileOperatorProbe.ref())); - - this.eventSourcedTestKit = - EventSourcedBehaviorTestKit.create(testKit.system(), VersionedArtifactEntity.create(ctx)); - } - - @AfterEach - public void teardown() { - testKit.system().log().info("Finishing test"); - testKit.system().terminate(); - } - - @Test - public void testEmptyState() { - final var state = this.eventSourcedTestKit.getState(); - Assertions.assertTrue(state.artifacts().isEmpty()); - Assertions.assertTrue(state.repositories().isEmpty()); - Assertions.assertFalse(state.needsArtifactScan()); - Assertions.assertFalse(state.needsCommitResolution()); - } - - @Test - public void testRegistration() { - final var state = this.eventSourcedTestKit - .runCommand(ref -> new VersionedArtifactCommand.Register( - MavenCoordinates.parse("com.example:example:1.0.0"), ref) - ) - .state(); - Assertions.assertTrue(state.artifacts().isEmpty()); - Assertions.assertTrue(state.repositories().isEmpty()); - Assertions.assertFalse(state.needsArtifactScan()); - Assertions.assertFalse(state.needsCommitResolution()); - } - - @Test - public void testAssetRegistration() throws URISyntaxException { - final var coordinates = MavenCoordinates.parse("com.example:example:1.0.0"); - this.eventSourcedTestKit - .runCommand(ref -> new VersionedArtifactCommand.Register( - coordinates, ref) - ) - .state(); - final var testJarPath = this.getClass().getClassLoader().getResource("test-jar.jar"); - Assertions.assertNotNull(testJarPath, "test-jar.jar isn't available, should be checked in"); - final var downloadUrl = testJarPath.toURI(); - final var artifact = new Artifact(Optional.empty(), downloadUrl, "test-jar.jar", "jar", "jar"); - final var newState = this.eventSourcedTestKit - .runCommand(ref -> new VersionedArtifactCommand.AddAssets(coordinates, List.of(artifact), ref)) - .state(); - this.fileOperatorProbe.fishForMessage(Duration.ofSeconds(10), r -> { - if (!(r instanceof FileCollectionOperator.TryFindingCommitForFiles tfcf)) { - return FishingOutcomes.fail("expected a try to commit"); - } - final var files = tfcf.files(); - if (files.size() != 1) { - return FishingOutcomes.fail("Expected 1 file only"); - } - final var file = files.get(0); - if (!file.downloadURL().equals(downloadUrl)) { - return FishingOutcomes.fail("Expected the same download url"); - } - return FishingOutcomes.complete(); - }); - Assertions.assertFalse(newState.artifacts().isEmpty()); - Assertions.assertTrue(newState.repositories().isEmpty()); - Assertions.assertTrue(newState.needsArtifactScan()); - Assertions.assertFalse(newState.needsCommitResolution()); - } - -} - diff --git a/versions-impl/src/test/java/org/spongepowered/downloads/versions/RegexValidations.java b/versions-impl/src/test/java/org/spongepowered/downloads/versions/RegexValidations.java deleted file mode 100644 index b2c16cb7..00000000 --- a/versions-impl/src/test/java/org/spongepowered/downloads/versions/RegexValidations.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import io.vavr.collection.List; -import io.vavr.control.Try; -import org.junit.jupiter.api.Test; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public final class RegexValidations { - - @Test - public void ValidateTagVersionRegistration() { - final var patternRegex = Pattern.compile( - "^\\d\\.\\d{1,2}(\\.\\d{1,2})?(-((rc)|(pre))\\d)?-(\\d{1,2}\\.\\d{1,2})\\.\\d$" - ); - final var valids = List.of("1.12.2-7.3.0", "1.16.5-8.0.0", "1.9-4.1.0"); - final var invalids = List.of("1.12.2-7.3.0-RC1723", "1.16.5-8.0.0-RC495", "1.12.2-2838-7.3.1-RC3482"); - final var regex = Try.of(() -> patternRegex); - final var validSuccess = valids - .map(valid -> regex.map(pattern -> pattern.matcher(valid)) - .mapTry(Matcher::find).getOrElse(false) - ) - .filter(b -> !b) - .isEmpty(); - final var invalidSuccess = invalids - .map(invalid -> regex.map(pattern -> pattern.matcher(invalid)) - .mapTry(Matcher::find) - .getOrElse(() -> true) - ) - .filter(b -> !b) - .isEmpty(); - - assertTrue(validSuccess); - assertFalse(invalidSuccess); - } -} diff --git a/versions-impl/src/test/resources/application-test.conf b/versions-impl/src/test/resources/application-test.conf deleted file mode 100644 index 5da7a5df..00000000 --- a/versions-impl/src/test/resources/application-test.conf +++ /dev/null @@ -1,11 +0,0 @@ -akka.serialization.jackson { - # The Jackson JSON serializer will register these modules. - jackson-modules += "akka.serialization.jackson.AkkaJacksonModule" - jackson-modules += "akka.serialization.jackson.AkkaTypedJacksonModule" - # AkkaStreamsModule optionally included if akka-streams is in classpath - jackson-modules += "akka.serialization.jackson.AkkaStreamJacksonModule" - jackson-modules += "com.fasterxml.jackson.module.paramnames.ParameterNamesModule" - jackson-modules += "com.fasterxml.jackson.datatype.jdk8.Jdk8Module" - jackson-modules += "com.fasterxml.jackson.module.scala.DefaultScalaModule" - jackson-modules += "io.vavr.jackson.datatype.VavrModule" -} diff --git a/versions-impl/src/test/resources/bad-commit-test-jar.jar b/versions-impl/src/test/resources/bad-commit-test-jar.jar deleted file mode 100644 index f6c8294b..00000000 Binary files a/versions-impl/src/test/resources/bad-commit-test-jar.jar and /dev/null differ diff --git a/versions-impl/src/test/resources/manifest b/versions-impl/src/test/resources/manifest deleted file mode 100644 index 919c25c6..00000000 --- a/versions-impl/src/test/resources/manifest +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -Git-Commit: d838fee5d8e834ba9fd4d1c4fe0f8214d6dc90fc - diff --git a/versions-impl/src/test/resources/no-commit-test-jar.jar b/versions-impl/src/test/resources/no-commit-test-jar.jar deleted file mode 100644 index 0552202f..00000000 Binary files a/versions-impl/src/test/resources/no-commit-test-jar.jar and /dev/null differ diff --git a/versions-impl/src/test/resources/test-jar.jar b/versions-impl/src/test/resources/test-jar.jar deleted file mode 100644 index 51161b86..00000000 Binary files a/versions-impl/src/test/resources/test-jar.jar and /dev/null differ diff --git a/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/VersionsQueryService.java b/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/VersionsQueryService.java deleted file mode 100644 index 6de09b6e..00000000 --- a/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/VersionsQueryService.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.query.api; - -import akka.NotUsed; -import com.lightbend.lagom.javadsl.api.Descriptor; -import com.lightbend.lagom.javadsl.api.Service; -import com.lightbend.lagom.javadsl.api.ServiceCall; -import com.lightbend.lagom.javadsl.api.transport.Method; -import org.spongepowered.downloads.versions.query.api.models.QueryVersions; - -import java.util.Optional; - -public interface VersionsQueryService extends Service { - - ServiceCall artifactVersions( - String groupId, String artifactId, Optional tags, Optional limit, - Optional offset, Optional recommended - ); - - ServiceCall latestArtifact( - String groupId, String artifactId, Optional tags, Optional recommended - ); - - ServiceCall versionDetails( - String groupId, String artifactId, String version - ); - - @Override - default Descriptor descriptor() { - return Service.named("version-query") - .withCalls( - Service.restCall( - Method.GET, "/versions-query/groups/:groupId/artifacts/:artifactId/versions?tags&limit&offset&recommended", - this::artifactVersions - ), - Service.restCall( - Method.GET, "/versions-query/groups/:groupId/artifacts/:artifactId/versions/:version", - this::versionDetails - ), - Service.restCall( - Method.GET, "/versions-query/groups/:groupId/artifacts/:artifactId/latest?tags&recommended", - this::latestArtifact - ) - ) - .withAutoAcl(true); - } -} diff --git a/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/models/QueryLatest.java b/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/models/QueryLatest.java deleted file mode 100644 index c0715262..00000000 --- a/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/models/QueryLatest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.query.api.models; - -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import org.spongepowered.downloads.artifact.api.Artifact; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -import java.util.Optional; - -public interface QueryLatest { - - @JsonSerialize - record VersionInfo(MavenCoordinates coordinates, - List assets, - Map tagValues, - Optional commit, - boolean recommended - ) { - } - -} diff --git a/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/models/QueryVersions.java b/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/models/QueryVersions.java deleted file mode 100644 index ec03727a..00000000 --- a/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/models/QueryVersions.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.query.api.models; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import org.spongepowered.downloads.artifact.api.Artifact; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -import java.util.Optional; - -public interface QueryVersions { - - @JsonSerialize - record VersionInfo(@JsonProperty Map artifacts, int offset, int limit, int size) { - - @JsonCreator - public VersionInfo { - } - } - - @JsonSerialize - record VersionDetails( - @JsonProperty("coordinates") MavenCoordinates coordinates, - @JsonProperty("commit") Optional commit, - @JsonProperty("assets") List components, - @JsonProperty("tags") Map tagValues, - @JsonProperty("recommended") boolean recommended - ) { - } - -} diff --git a/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/models/TagCollection.java b/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/models/TagCollection.java deleted file mode 100644 index ee04816c..00000000 --- a/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/models/TagCollection.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.query.api.models; - -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.vavr.collection.Map; - -@JsonSerialize -public record TagCollection( - Map tagValues, - boolean recommended -) { -} diff --git a/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/models/VersionedChangelog.java b/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/models/VersionedChangelog.java deleted file mode 100644 index 7a1b714b..00000000 --- a/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/models/VersionedChangelog.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.query.api.models; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import io.vavr.collection.List; - -import java.net.URI; - -@JsonDeserialize -public final record VersionedChangelog( - List commits, - @JsonInclude(JsonInclude.Include.NON_DEFAULT) boolean processing -) { - - @JsonCreator - public VersionedChangelog { - } - - @JsonDeserialize - public final record IndexedCommit( - VersionedCommit commit, - List submoduleCommits - ) { - @JsonCreator - public IndexedCommit { - } - } - - @JsonDeserialize - public final record Submodule( - String name, - URI gitRepository, - List commits - ) { - @JsonCreator - public Submodule { - } - } - -} diff --git a/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/models/VersionedCommit.java b/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/models/VersionedCommit.java deleted file mode 100644 index 6f11529b..00000000 --- a/versions-query-api/src/main/java/org/spongepowered/downloads/versions/query/api/models/VersionedCommit.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.query.api.models; - - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import java.net.URI; -import java.time.ZonedDateTime; - -@JsonDeserialize -public record VersionedCommit( - String message, - String body, - String sha, - Author author, - Commiter commiter, - URI link, - ZonedDateTime commitDate -) { - - @JsonCreator - public VersionedCommit { - } - - @JsonDeserialize - public record Author( - String name, - String email - ) { - @JsonCreator - public Author { - } - } - - @JsonDeserialize - public record Commiter( - String name, - String email - ) { - @JsonCreator - public Commiter { - } - } -} - diff --git a/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/VersionQueryModule.java b/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/VersionQueryModule.java deleted file mode 100644 index 6b75f6be..00000000 --- a/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/VersionQueryModule.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.query.impl; - -import com.google.inject.AbstractModule; -import com.lightbend.lagom.javadsl.server.ServiceGuiceSupport; -import org.spongepowered.downloads.versions.query.api.VersionsQueryService; - -public class VersionQueryModule extends AbstractModule implements ServiceGuiceSupport { - - @Override - protected void configure() { - this.bindService(VersionsQueryService.class, VersionQueryServiceImpl.class); - } -} diff --git a/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/VersionQueryServiceImpl.java b/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/VersionQueryServiceImpl.java deleted file mode 100644 index 1a4580ec..00000000 --- a/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/VersionQueryServiceImpl.java +++ /dev/null @@ -1,324 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.query.impl; - -import akka.NotUsed; -import com.lightbend.lagom.javadsl.api.ServiceCall; -import com.lightbend.lagom.javadsl.api.deser.ExceptionMessage; -import com.lightbend.lagom.javadsl.api.transport.BadRequest; -import com.lightbend.lagom.javadsl.api.transport.NotFound; -import com.lightbend.lagom.javadsl.api.transport.TransportErrorCode; -import com.lightbend.lagom.javadsl.api.transport.TransportException; -import com.lightbend.lagom.javadsl.persistence.jpa.JpaSession; -import io.vavr.Tuple; -import io.vavr.Tuple2; -import io.vavr.Value; -import io.vavr.collection.HashMap; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import org.apache.maven.artifact.versioning.ComparableVersion; -import org.spongepowered.downloads.artifact.api.ArtifactCoordinates; -import org.spongepowered.downloads.versions.query.api.VersionsQueryService; -import org.spongepowered.downloads.versions.query.api.models.QueryVersions; -import org.spongepowered.downloads.versions.query.api.models.VersionedChangelog; -import org.spongepowered.downloads.versions.query.impl.models.JpaTaggedVersion; -import org.spongepowered.downloads.versions.query.impl.models.JpaVersionedArtifactView; - -import javax.inject.Inject; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceException; -import java.util.Comparator; -import java.util.Locale; -import java.util.Optional; -import java.util.regex.Pattern; - -public record VersionQueryServiceImpl(JpaSession session) - implements VersionsQueryService { - public static final Pattern VALID_COORDINATE_PORTION = Pattern.compile("^[\\w.-]+$"); - - @Inject - public VersionQueryServiceImpl { - } - - @Override - public ServiceCall artifactVersions( - final String groupId, - final String artifactId, - final Optional tags, - final Optional limit, - final Optional offset, - final Optional recommended - ) { - return request -> { - validateCoordinates(groupId, artifactId); - return this.session.withTransaction( - t -> { - if (groupId.isBlank() || artifactId.isBlank()) { - throw new NotFound("unknown artifact"); - } - try { - final var query = new VersionQuery(groupId, artifactId, tags, limit, offset, recommended); - - if (query.tags.isEmpty()) { - return getUntaggedVersions(t, query); - } - return getTaggedVersions(t, query); - } catch (PersistenceException e) { - throw new TransportException( - TransportErrorCode.InternalServerError, new ExceptionMessage("Internal Server Error", "")); - } - }); - }; - } - - @Override - public ServiceCall latestArtifact( - final String groupId, - final String artifactId, - final Optional tags, - final Optional recommended - ) { - return request -> { - validateCoordinates(groupId, artifactId); - - return this.session.withTransaction( - t -> { - if (groupId.isBlank() || artifactId.isBlank()) { - throw new NotFound("unknown artifact"); - } - try { - final var query = new VersionQuery(groupId, artifactId, tags, recommended.orElse(false)); - - final var info = query.tags.isEmpty() - ? getUntaggedVersions(t, query) - : getTaggedVersions(t, query); - final var version = info.artifacts().keySet().head(); - final var coordinates = query.coordinates.version(version); - final var artifacts = t.createNamedQuery( - "VersionedArtifactView.findExplicitly", JpaVersionedArtifactView.class) - .setParameter("groupId", coordinates.groupId) - .setParameter("artifactId", coordinates.artifactId) - .setParameter("version", coordinates.version) - .getResultList(); - if (artifacts.isEmpty()) { - throw new NotFound("versioned artifact not found"); - } - final var versionedArtifact = artifacts.get(0); - final Optional commit = versionedArtifact.asVersionedCommit(); - return new QueryVersions.VersionDetails( - coordinates, commit, versionedArtifact.asArtifactList(), - versionedArtifact.getTagValues(), versionedArtifact.isRecommended() - - ); - } catch (PersistenceException e) { - e.printStackTrace(); - throw new TransportException( - TransportErrorCode.InternalServerError, new ExceptionMessage("Internal Server Error", "")); - } - }); - }; - } - - @Override - public ServiceCall versionDetails( - final String groupId, final String artifactId, final String version - ) { - return notUsed -> { - validateCoordinates(groupId, artifactId); - return this.session.withTransaction(em -> { - final var sanitizedGroupId = groupId.toLowerCase(Locale.ROOT).trim(); - final var sanitizedArtifactId = artifactId.toLowerCase(Locale.ROOT).trim(); - final var sanitizedVersion = version.trim(); - return em.createNamedQuery( - "VersionedArtifactView.findFullVersionDetails", JpaVersionedArtifactView.class) - .setParameter("groupId", sanitizedGroupId) - .setParameter("artifactId", sanitizedArtifactId) - .setParameter("version", sanitizedVersion) - .getResultList() - .stream() - .findFirst() - .map(versionView -> { - final var coordinates = versionView.asMavenCoordinates(); - final var assets = versionView.asArtifactList(); - final var tags = versionView.getTagValues(); - final var commit = versionView.asVersionedCommit(); - return new QueryVersions.VersionDetails( - coordinates, commit, assets, tags, versionView.isRecommended()); - }) - .orElseThrow(() -> new NotFound("group or artifact or version not found")); - }); - }; - } - - private static record ParameterizedTag(String tagName, String tagValue) { - } - - private static record VersionQuery( - ArtifactCoordinates coordinates, - int limit, - int offset, - Optional recommended, - List tags) { - - VersionQuery( - String groupId, String artifactId, - Optional tags, - boolean recommended - ) { - this( - new ArtifactCoordinates(groupId.toLowerCase(Locale.ROOT), artifactId.toLowerCase(Locale.ROOT)), - 25, - 0, - Optional.of(recommended), - gatherTags(tags) - ); - } - - VersionQuery( - String groupId, String artifactId, - final Optional tags, - final Optional limitOpt, - final Optional offsetOpt, - final Optional recommended - ) { - this( - new ArtifactCoordinates(groupId.toLowerCase(Locale.ROOT), artifactId.toLowerCase(Locale.ROOT)), - limitOpt.map(l -> Math.min(Math.max(l, 1), 25)).orElse(25), - offsetOpt.map(o -> Math.max(o, 0)).orElse(0), - recommended, - gatherTags(tags) - ); - } - - private static List gatherTags(Optional tags) { - return tags.map(rw -> rw.split(",")) - .map(List::of).orElseGet(List::of) - .map(tag -> tag.split(":")) - .filter(array -> array.length == 2) - .map(array -> new ParameterizedTag(array[0].toLowerCase(Locale.ROOT), array[1].strip())); - } - } - - private static QueryVersions.VersionInfo getUntaggedVersions( - EntityManager em, VersionQuery query - ) { - final int totalCount = query.recommended - .map(isRecommended -> em.createNamedQuery("VersionedArtifactView.recommendedCount", Long.class) - .setParameter("recommended", isRecommended) - ) - .orElseGet(() -> em.createNamedQuery("VersionedArtifactView.count", Long.class)) - .setParameter("groupId", query.coordinates.groupId()) - .setParameter("artifactId", query.coordinates.artifactId()) - .getSingleResult().intValue(); - if (totalCount <= 0) { - throw new NotFound("group or artifact not found"); - } - final var untaggedVersions = query.recommended - .map(isRecommended -> em.createNamedQuery( - "VersionedArtifactView.findByArtifactAndRecommendation", - JpaVersionedArtifactView.class - ) - .setParameter("recommended", isRecommended) - ) - .orElseGet(() -> em.createNamedQuery( - "VersionedArtifactView.findByArtifact", JpaVersionedArtifactView.class - )) - .setParameter("groupId", query.coordinates.groupId()) - .setParameter("artifactId", query.coordinates.artifactId()) - .setMaxResults(query.offset + query.limit) - .getResultList(); - final var mappedByCoordinates = untaggedVersions.stream() - .collect(List.collector()) - .drop(query.offset) - .take(query.limit); - final var versionsWithTags = mappedByCoordinates - .toSortedMap( - Comparator.comparing(ComparableVersion::new).reversed(), - JpaVersionedArtifactView::version, - JpaVersionedArtifactView::asTagCollection - ); - return new QueryVersions.VersionInfo(versionsWithTags, query.offset, query.limit, totalCount); - } - - private static QueryVersions.VersionInfo getTaggedVersions( - EntityManager em, VersionQuery query - ) { - // Otherwise, get the tagged versions that match the given tags - // which is a little advanced, because we'll have to literally gather the versioned values - // that match the tags, then do a shake down - final var map = query.tags.map(tag -> query.recommended.map( - recommended -> em.createNamedQuery( - "TaggedVersion.findMatchingTagValuesAndRecommendation", - JpaTaggedVersion.class - ) - .setParameter("recommended", recommended)) - .orElseGet(() -> em.createNamedQuery( - "TaggedVersion.findAllMatchingTagValues", JpaTaggedVersion.class - )) - .setParameter("groupId", query.coordinates.groupId()) - .setParameter("artifactId", query.coordinates.artifactId()) - .setParameter("tagName", tag.tagName) - .setParameter("tagValue", tag.tagValue + "%") - .getResultStream() - .map(tv -> Tuple.of(tv, Tuple.of(tv.getTagName(), tv.getTagValue()))) - .collect(List.collector()) - ).flatMap(Value::toStream); - if (map.isEmpty()) { - throw new NotFound("group or artifact not found"); - } - var versionedTags = HashMap.>>empty(); - - for (final Tuple2> tagged : map) { - versionedTags = versionedTags.put( - tagged._1.getMavenVersion(), - tagged.map(JpaTaggedVersion::getVersion, HashMap::of), - (o, i) -> Tuple.of(o._1, o._2.merge(i._2)) - ); - } - final var wantedTagNames = query.tags.map(ParameterizedTag::tagName); - final var validatedVersion = versionedTags - .filter((coordinates, tagMap) -> tagMap._2.keySet().containsAll(wantedTagNames)); - final var versionsForQuery = validatedVersion - .toSortedMap(Comparator.comparing(ComparableVersion::new).reversed(), tuple -> tuple._1, tuple -> tuple._2) - .drop(query.offset) - .take(query.limit); - return new QueryVersions.VersionInfo( - versionsForQuery.mapValues(tuple -> tuple._1.asTagCollection()), query.offset, query.limit, - validatedVersion.size() - ); - } - - private static void validateCoordinates(final String groupID, final String artifactID) { - final String sanitizedGroupId = groupID.toLowerCase(Locale.ROOT); - if (!VALID_COORDINATE_PORTION.matcher(sanitizedGroupId).matches()) { - throw new BadRequest("Invalid groupId: " + groupID); - } - final String sanitizedArtifactId = artifactID.toLowerCase(Locale.ROOT); - if (!VALID_COORDINATE_PORTION.matcher(sanitizedArtifactId).matches()) { - throw new BadRequest("Invalid artifactId: " + artifactID); - } - } - -} diff --git a/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/models/JpaTaggedVersion.java b/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/models/JpaTaggedVersion.java deleted file mode 100644 index 11dddae7..00000000 --- a/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/models/JpaTaggedVersion.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.query.impl.models; - -import org.hibernate.annotations.Immutable; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.JoinColumns; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; -import java.io.Serializable; -import java.util.Objects; - -@Entity(name = "TaggedVersion") -@Immutable -@Table(name = "versioned_tags", schema = "version") -@NamedQueries({ - @NamedQuery(name = "TaggedVersion.findAllForVersion", - query = - """ - select view from TaggedVersion view - where view.mavenGroupId = :groupId and view.mavenArtifactId = :artifactId and view.version = :version - """ - ), - @NamedQuery( - name = "TaggedVersion.findAllMatchingTagValues", - query = - """ - select view from TaggedVersion view - where view.mavenGroupId = :groupId - and view.mavenArtifactId = :artifactId - and view.tagName = :tagName - and view.tagValue like :tagValue - """ - ), - @NamedQuery( - name = "TaggedVersion.findMatchingTagValuesAndRecommendation", - query = - """ - select view from TaggedVersion view - where view.mavenGroupId = :groupId - and view.mavenArtifactId = :artifactId - and view.tagName = :tagName - and view.tagValue like :tagValue - and (view.versionView.recommended = :recommended or view.versionView.manuallyRecommended = :recommended) - """ - ) -}) -public class JpaTaggedVersion implements Serializable { - - @Id - @Column(name = "version_id", updatable = false) - private long versionId; - - @Id - @Column(updatable = false, name = "artifact_internal_id") - private long artifactId; - - @Id - @Column(name = "maven_group_id", updatable = false) - private String mavenGroupId; - - @Id - @Column(name = "maven_artifact_id", updatable = false) - private String mavenArtifactId; - - @Id - @Column(name = "maven_version", - updatable = false) - private String version; - - @Id - @Column(name = "tag_name", - updatable = false) - private String tagName; - - @Column(name = "tag_value", - updatable = false) - private String tagValue; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumns({ - @JoinColumn(name = "maven_version", - referencedColumnName = "version", - nullable = false, - updatable = false, - insertable = false), - @JoinColumn(name = "maven_group_id", - referencedColumnName = "group_id", - nullable = false, - updatable = false, - insertable = false), - @JoinColumn(name = "maven_artifact_id", - referencedColumnName = "artifact_id", - nullable = false, - updatable = false, - insertable = false) - }) - private JpaVersionedArtifactView versionView; - - public String getTagName() { - return tagName; - } - - public String getTagValue() { - return tagValue; - } - - public MavenCoordinates asMavenCoordinates() { - return new MavenCoordinates(this.mavenGroupId, this.mavenArtifactId, this.version); - } - - public JpaVersionedArtifactView getVersion() { - return this.versionView; - } - - public String getMavenVersion() { - return this.version; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - JpaTaggedVersion that = (JpaTaggedVersion) o; - return versionId == that.versionId && artifactId == that.artifactId && Objects.equals( - mavenGroupId, that.mavenGroupId) && Objects.equals( - mavenArtifactId, that.mavenArtifactId) && Objects.equals( - version, that.version) && Objects.equals(tagName, that.tagName) && Objects.equals( - tagValue, that.tagValue); - } - - @Override - public int hashCode() { - return Objects.hash(versionId, artifactId, mavenGroupId, mavenArtifactId, version, tagName, tagValue); - } -} diff --git a/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/models/JpaVersionedArtifactView.java b/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/models/JpaVersionedArtifactView.java deleted file mode 100644 index 3fe2881c..00000000 --- a/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/models/JpaVersionedArtifactView.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.query.impl.models; - -import io.vavr.Tuple; -import io.vavr.collection.HashMap; -import io.vavr.collection.List; -import io.vavr.collection.Map; -import org.hibernate.annotations.Immutable; -import org.spongepowered.downloads.artifact.api.Artifact; -import org.spongepowered.downloads.artifact.api.MavenCoordinates; -import org.spongepowered.downloads.versions.query.api.models.TagCollection; -import org.spongepowered.downloads.versions.query.api.models.VersionedChangelog; - -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.Table; -import java.io.Serializable; -import java.net.URI; -import java.util.Comparator; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; - -@Immutable -@Entity(name = "VersionedArtifactView") -@Table(name = "versioned_artifacts", - schema = "version") -@NamedQueries({ - @NamedQuery( - name = "VersionedArtifactView.count", - query = """ - select count(v) from VersionedArtifactView v - where v.groupId = :groupId and v.artifactId = :artifactId - """ - ), - @NamedQuery( - name = "VersionedArtifactView.recommendedCount", - query = """ - select count(v) from VersionedArtifactView v - where v.groupId = :groupId and v.artifactId = :artifactId and v.recommended = :recommended - """ - ), - @NamedQuery( - name = "VersionedArtifactView.findByArtifact", - query = """ - select v from VersionedArtifactView v where v.artifactId = :artifactId and v.groupId = :groupId - """ - ), - @NamedQuery( - name = "VersionedArtifactView.findByArtifactAndRecommendation", - query = """ - select v from VersionedArtifactView v - where v.artifactId = :artifactId and v.groupId = :groupId and (v.recommended = :recommended or v.manuallyRecommended = :recommended) - """ - ), - @NamedQuery( - name = "VersionedArtifactView.findExplicitly", - query = """ - select v from VersionedArtifactView v - left join fetch v.tags - where v.artifactId = :artifactId and v.groupId = :groupId and v.version = :version - """ - ), - @NamedQuery( - name = "VersionedArtifactView.findFullVersionDetails", - query = """ - select v from VersionedArtifactView v - left join fetch v.tags - left join fetch v.assets - where v.artifactId = :artifactId and v.groupId = :groupId and v.version = :version - """ - ) -}) -public class JpaVersionedArtifactView implements Serializable { - - @Id - @Column(name = "artifact_id", - updatable = false) - private String artifactId; - - @Id - @Column(name = "group_id", - updatable = false) - private String groupId; - - @Id - @Column(name = "version", - updatable = false) - private String version; - - @Column(name = "recommended") - private boolean recommended; - - @Column(name = "manual_recommendation") - private boolean manuallyRecommended; - - @Column(name = "ordering") - private int ordering; - - @OneToMany( - targetEntity = JpaTaggedVersion.class, - fetch = FetchType.LAZY, - cascade = CascadeType.ALL, - orphanRemoval = true, - mappedBy = "versionView") - private Set tags; - - @OneToMany( - targetEntity = JpaVersionedAsset.class, - cascade = CascadeType.ALL, - fetch = FetchType.LAZY, - orphanRemoval = true, - mappedBy = "versionView" - ) - private Set assets; - - @OneToOne( - targetEntity = JpaVersionedChangelog.class, - mappedBy = "versionView", - fetch = FetchType.LAZY - ) - private JpaVersionedChangelog changelog; - - public Set getTags() { - return tags; - } - - public String version() { - return this.version; - } - - public Map getTagValues() { - final var results = this.getTags(); - final var tuple2Stream = results.stream().map( - taggedVersion -> Tuple.of(taggedVersion.getTagName(), taggedVersion.getTagValue())); - return tuple2Stream - .collect(HashMap.collector()); - } - - public TagCollection asTagCollection() { - final var results = this.getTags(); - final var tuple2Stream = results.stream().map( - taggedVersion -> Tuple.of(taggedVersion.getTagName(), taggedVersion.getTagValue())); - return new TagCollection(tuple2Stream - .collect(HashMap.collector()), this.recommended); - } - - public boolean isRecommended() { - return this.recommended || this.manuallyRecommended; - } - - public MavenCoordinates asMavenCoordinates() { - return new MavenCoordinates(this.groupId + ":" + this.artifactId + ":" + this.version); - } - - Set getAssets() { - return assets; - } - - public List asArtifactList() { - return this.getAssets() - .stream() - .map( - asset -> new Artifact( - Optional.ofNullable(asset.getClassifier()), - URI.create(asset.getDownloadUrl()), - new String(asset.getMd5()), - new String(asset.getSha1()), - asset.getExtension() - ) - ).collect(List.collector()) - .sorted(Comparator.comparing(artifact -> artifact.classifier().orElse(""))); - } - - public Optional asVersionedCommit() { - final var changelog = this.changelog; - if (changelog == null) { - return Optional.empty(); - } - return Optional.of(changelog.getChangelog()); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - JpaVersionedArtifactView that = (JpaVersionedArtifactView) o; - return Objects.equals(artifactId, that.artifactId) && Objects.equals( - groupId, that.groupId) && Objects.equals(version, that.version); - } - - @Override - public int hashCode() { - return Objects.hash(artifactId, groupId, version); - } - -} diff --git a/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/models/JpaVersionedAsset.java b/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/models/JpaVersionedAsset.java deleted file mode 100644 index 2e67c520..00000000 --- a/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/models/JpaVersionedAsset.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.query.impl.models; - -import org.hibernate.annotations.Immutable; -import org.hibernate.annotations.Type; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.JoinColumns; -import javax.persistence.Lob; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import java.io.Serializable; -import java.util.Arrays; -import java.util.Objects; - -@Immutable -@Entity(name = "VersionedAsset") -@Table(name = "artifact_versioned_assets", - schema = "version") -public class JpaVersionedAsset implements Serializable { - - @Id - @Column(name = "group_id") - private String groupId; - @Id - @Column(name = "artifact_id") - private String artifactId; - - @Id - @Column(name = "version") - private String version; - - @Id - @Column(name = "classifier") - private String classifier; - - @Id - @Column(name = "extension") - private String extension; - - @Column(name = "download_url") - private String downloadUrl; - - @Lob - @Type(type = "org.hibernate.type.BinaryType") - @Column(name = "md5") - private byte[] md5; - - @Lob - @Type(type = "org.hibernate.type.BinaryType") - @Column(name = "sha1") - private byte[] sha1; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumns({ - @JoinColumn(name = "version", - referencedColumnName = "version", - nullable = false, - updatable = false, - insertable = false), - @JoinColumn(name = "group_id", - referencedColumnName = "group_id", - nullable = false, - updatable = false, - insertable = false), - @JoinColumn(name = "artifact_id", - referencedColumnName = "artifact_id", - nullable = false, - updatable = false, - insertable = false) - }) - private JpaVersionedArtifactView versionView; - - public String getClassifier() { - return classifier; - } - - public String getExtension() { - return extension; - } - - public String getDownloadUrl() { - return downloadUrl; - } - - public byte[] getMd5() { - return md5; - } - - public byte[] getSha1() { - return sha1; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - JpaVersionedAsset that = (JpaVersionedAsset) o; - return Objects.equals(groupId, that.groupId) && Objects.equals( - artifactId, that.artifactId) && Objects.equals(version, that.version) && Objects.equals( - classifier, that.classifier) && Objects.equals(extension, that.extension) && Objects.equals( - downloadUrl, that.downloadUrl) && Arrays.equals(md5, that.md5) && Arrays.equals( - sha1, that.sha1) && Objects.equals(versionView, that.versionView); - } - - @Override - public int hashCode() { - int result = Objects.hash(groupId, artifactId, version, classifier, extension, downloadUrl, versionView); - result = 31 * result + Arrays.hashCode(md5); - result = 31 * result + Arrays.hashCode(sha1); - return result; - } -} diff --git a/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/models/JpaVersionedChangelog.java b/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/models/JpaVersionedChangelog.java deleted file mode 100644 index 90f9acb5..00000000 --- a/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/models/JpaVersionedChangelog.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.query.impl.models; - -import com.vladmihalcea.hibernate.type.json.JsonBinaryType; -import org.hibernate.annotations.Immutable; -import org.hibernate.annotations.Type; -import org.hibernate.annotations.TypeDef; -import org.hibernate.annotations.TypeDefs; -import org.spongepowered.downloads.versions.query.api.models.VersionedChangelog; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.JoinColumns; -import javax.persistence.OneToOne; -import javax.persistence.Table; -import java.io.Serializable; -import java.net.URL; -import java.util.Objects; - -@Immutable -@Entity(name = "VersionedChangelog") -@Table( - name = "versioned_changelogs", - schema = "version" -) -@TypeDefs({ - @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) -}) -public class JpaVersionedChangelog implements Serializable { - - @Id - @Column(name = "version_id", updatable = false) - private String versionId; - - @Id - @Column(name = "group_id", updatable = false) - private String groupId; - - @Id - @Column(name = "artifact_id", updatable = false) - private String artifactId; - - @OneToOne(fetch = FetchType.LAZY) - @JoinColumns({ - @JoinColumn(name = "version", referencedColumnName = "version", insertable = false, updatable = false), - @JoinColumn(name = "group_id", referencedColumnName = "group_id", insertable = false, updatable = false), - @JoinColumn(name = "artifact_id", referencedColumnName = "artifact_id", insertable = false, updatable = false) - }) - private JpaVersionedArtifactView versionView; - - @Column(name = "commit_sha", nullable = false) - private String sha; - - @Type(type = "jsonb") - @Column(name = "changelog", columnDefinition = "jsonb") - private VersionedChangelog changelog; - - @Column(name = "repo") - private URL repo; - - @Column(name = "branch") - private String branch; - - public String getSha() { - return sha; - } - - public VersionedChangelog getChangelog() { - return changelog; - } - - public URL getRepo() { - return repo; - } - - public String getBranch() { - return branch; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - JpaVersionedChangelog that = (JpaVersionedChangelog) o; - return Objects.equals(versionId, that.versionId) && Objects.equals( - groupId, that.groupId) && Objects.equals(artifactId, that.artifactId) && Objects.equals( - versionView, that.versionView) && Objects.equals(sha, that.sha) && Objects.equals( - changelog, that.changelog) && Objects.equals(repo, that.repo) && Objects.equals( - branch, that.branch); - } - - @Override - public int hashCode() { - return Objects.hash(versionId, groupId, artifactId, versionView, sha, changelog, repo, branch); - } - - @Override - public String toString() { - return "JpaVersionedChangelog{" + - "versionId='" + versionId + '\'' + - ", groupId='" + groupId + '\'' + - ", artifactId='" + artifactId + '\'' + - ", versionView=" + versionView + - ", sha='" + sha + '\'' + - ", changelog=" + changelog + - ", repo=" + repo + - ", branch='" + branch + '\'' + - '}'; - } -} diff --git a/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/models/VersionedArtifactID.java b/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/models/VersionedArtifactID.java deleted file mode 100644 index 8450b0eb..00000000 --- a/versions-query-impl/src/main/java/org/spongepowered/downloads/versions/query/impl/models/VersionedArtifactID.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is part of SystemOfADownload, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.spongepowered.downloads.versions.query.impl.models; - -import java.io.Serializable; -import java.util.Objects; - -public class VersionedArtifactID implements Serializable { - private String artifactId; - - private String groupId; - - private String version; - - public VersionedArtifactID(final String artifactId, final String groupId, final String version) { - this.artifactId = artifactId; - this.groupId = groupId; - this.version = version; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - VersionedArtifactID that = (VersionedArtifactID) o; - return artifactId.equals(that.artifactId) && groupId.equals(that.groupId) && version.equals(that.version); - } - - @Override - public int hashCode() { - return Objects.hash(artifactId, groupId, version); - } -} diff --git a/versions-query-impl/src/main/resources/META-INF/persistence.xml b/versions-query-impl/src/main/resources/META-INF/persistence.xml deleted file mode 100644 index ba026222..00000000 --- a/versions-query-impl/src/main/resources/META-INF/persistence.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - org.hibernate.jpa.HibernatePersistenceProvider - DefaultDS - org.spongepowered.downloads.versions.query.impl.models.JpaTaggedVersion - org.spongepowered.downloads.versions.query.impl.models.JpaVersionedArtifactView - org.spongepowered.downloads.versions.query.impl.models.JpaVersionedAsset - org.spongepowered.downloads.versions.query.impl.models.JpaVersionedChangelog - true - - - - - - - diff --git a/versions-query-impl/src/main/resources/application.conf b/versions-query-impl/src/main/resources/application.conf deleted file mode 100644 index 6b3bb4ed..00000000 --- a/versions-query-impl/src/main/resources/application.conf +++ /dev/null @@ -1,29 +0,0 @@ -play.modules.enabled += org.spongepowered.downloads.versions.query.impl.VersionQueryModule - -db.default { - driver = "org.postgresql.Driver" - url = "jdbc:postgresql://localhost:5432/default" - url = ${?POSTGRES_URL} - username = admin - username = ${?POSTGRES_USERNAME} - password = password - password = ${?POSTGRES_PASSWORD} -} - -jdbc-defaults.slick.profile = "slick.jdbc.PostgresProfile$" - -lagom.persistence.jpa { - # This must match the name in persistence.xml - persistence-unit = "default" -} - -akka.serialization.jackson { - jackson-modules += "io.vavr.jackson.datatype.VavrModule" -} - -play.filters.enabled += "play.filters.cors.CORSFilter" -play.filters.cors { - pathPrefixes = ["/versions-query"] - allowedHttpMethods = ["GET"] - preflightMaxAge = 3 days -} diff --git a/versions-query-impl/src/main/resources/logback.xml b/versions-query-impl/src/main/resources/logback.xml deleted file mode 100644 index 9ef110a0..00000000 --- a/versions-query-impl/src/main/resources/logback.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - System.out - - %date{hh:MM:ss.SSS} [%level] [%thread] [%logger{5}/%marker] - %coloredLevel %msg MDC: {%mdc}%n - - - - - 8192 - true - - - - - - - - - - - - - - - - - - - - - - -