diff --git a/build.gradle.kts b/build.gradle.kts index d01d750..5abe8d6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -96,6 +96,7 @@ jib { dependencies { annotationProcessor("org.springframework.boot", "spring-boot-configuration-processor") checkstyle(libs.stylecheck) + compileOnlyApi(libs.jspecify) implementation(libs.jetbrains.annotations) implementation(libs.springdoc.openapi.starter.webmvc.ui) implementation("org.springframework.boot", "spring-boot-starter-data-mongodb") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6f8c021..6f00e25 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,6 +11,7 @@ spring-boot = { id = "org.springframework.boot", version = "3.2.5" } spring-dependency-management = { id = "io.spring.dependency-management", version = "1.1.5" } [libraries] +jspecify = { module = "org.jspecify:jspecify", version = "0.3.0" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "24.1.0" } springdoc-openapi-starter-webmvc-ui = { group = "org.springdoc", name = "springdoc-openapi-starter-webmvc-ui", version = "2.5.0" } stylecheck = { group = "ca.stellardrift", name = "stylecheck", version = "0.2.1" } diff --git a/src/main/java/io/papermc/bibliothek/BibliothekApplication.java b/src/main/java/io/papermc/bibliothek/BibliothekApplication.java index 09d1d08..ee6a5c3 100644 --- a/src/main/java/io/papermc/bibliothek/BibliothekApplication.java +++ b/src/main/java/io/papermc/bibliothek/BibliothekApplication.java @@ -24,6 +24,7 @@ package io.papermc.bibliothek; import io.papermc.bibliothek.configuration.AppConfiguration; +import org.jspecify.annotations.NullMarked; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -32,8 +33,9 @@ @EnableConfigurationProperties({ AppConfiguration.class }) -@SpringBootApplication +@NullMarked @ServletComponentScan +@SpringBootApplication public class BibliothekApplication { public static void main(final String[] args) { SpringApplication.run(BibliothekApplication.class, args); diff --git a/src/main/java/io/papermc/bibliothek/database/model/Project.java b/src/main/java/io/papermc/bibliothek/api/model/Change.java similarity index 76% rename from src/main/java/io/papermc/bibliothek/database/model/Project.java rename to src/main/java/io/papermc/bibliothek/api/model/Change.java index 92a3f26..e47ea1e 100644 --- a/src/main/java/io/papermc/bibliothek/database/model/Project.java +++ b/src/main/java/io/papermc/bibliothek/api/model/Change.java @@ -21,17 +21,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package io.papermc.bibliothek.database.model; +package io.papermc.bibliothek.api.model; -import org.bson.types.ObjectId; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.index.Indexed; -import org.springframework.data.mongodb.core.mapping.Document; +import org.jspecify.annotations.NullMarked; -@Document(collection = "projects") -public record Project( - @Id ObjectId _id, - @Indexed String name, - String friendlyName +@NullMarked +public record Change( + String commit, + String summary, + String message ) { } diff --git a/src/main/java/io/papermc/bibliothek/api/model/Channel.java b/src/main/java/io/papermc/bibliothek/api/model/Channel.java new file mode 100644 index 0000000..a7c8559 --- /dev/null +++ b/src/main/java/io/papermc/bibliothek/api/model/Channel.java @@ -0,0 +1,32 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2024 PaperMC + * + * 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 io.papermc.bibliothek.api.model; + +import org.jspecify.annotations.NullMarked; + +@NullMarked +public enum Channel { + DEFAULT, + EXPERIMENTAL; +} diff --git a/src/main/java/io/papermc/bibliothek/api/model/Download.java b/src/main/java/io/papermc/bibliothek/api/model/Download.java new file mode 100644 index 0000000..691a93d --- /dev/null +++ b/src/main/java/io/papermc/bibliothek/api/model/Download.java @@ -0,0 +1,33 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2024 PaperMC + * + * 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 io.papermc.bibliothek.api.model; + +import org.jspecify.annotations.NullMarked; + +@NullMarked +public record Download( + String name, + String sha256 +) { +} diff --git a/src/main/java/io/papermc/bibliothek/api/serialization/LegacyChannelSerializer.java b/src/main/java/io/papermc/bibliothek/api/serialization/LegacyChannelSerializer.java new file mode 100644 index 0000000..edbf450 --- /dev/null +++ b/src/main/java/io/papermc/bibliothek/api/serialization/LegacyChannelSerializer.java @@ -0,0 +1,44 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2024 PaperMC + * + * 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 io.papermc.bibliothek.api.serialization; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import io.papermc.bibliothek.api.model.Channel; +import java.io.IOException; +import java.util.Locale; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public class LegacyChannelSerializer extends StdSerializer { + public LegacyChannelSerializer() { + super(Channel.class); + } + + @Override + public void serialize(final Channel value, final JsonGenerator gen, final SerializerProvider provider) throws IOException { + gen.writeString(value.name().toLowerCase(Locale.ROOT)); + } +} diff --git a/src/main/java/io/papermc/bibliothek/api/v2/Constants2.java b/src/main/java/io/papermc/bibliothek/api/v2/Constants2.java new file mode 100644 index 0000000..d1a74bc --- /dev/null +++ b/src/main/java/io/papermc/bibliothek/api/v2/Constants2.java @@ -0,0 +1,68 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2024 PaperMC + * + * 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 io.papermc.bibliothek.api.v2; + +import org.intellij.lang.annotations.Language; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public final class Constants2 { + public static final String FIELD_BUILD = "build"; + public static final String FIELD_BUILDS = "builds"; + public static final String FIELD_CHANGES = "changes"; + public static final String FIELD_CHANNEL = "channel"; + public static final String FIELD_COMMIT = "commit"; + public static final String FIELD_DOWNLOADS = "downloads"; + public static final String FIELD_MESSAGE = "message"; + public static final String FIELD_NAME = "name"; + public static final String FIELD_PROJECTS = "projects"; + public static final String FIELD_PROJECT_ID = "project_id"; + public static final String FIELD_PROJECT_NAME = "project_name"; + public static final String FIELD_PROMOTED = "promoted"; + public static final String FIELD_SHA256 = "sha256"; + public static final String FIELD_SUMMARY = "summary"; + public static final String FIELD_TIME = "time"; + public static final String FIELD_VERSION = "version"; + public static final String FIELD_VERSIONS = "versions"; + public static final String FIELD_VERSION_GROUP = "version_group"; + public static final String FIELD_VERSION_GROUPS = "version_groups"; + + @Language("RegExp") + public static final String PATTERN_DOWNLOAD = "[a-z0-9._-]+"; + @Language("RegExp") + public static final String PATTERN_PROJECT_ID = "[a-z]+"; + @Language("RegExp") + public static final String PATTERN_SHA256 = "[a-f0-9]{64}"; + @Language("RegExp") + public static final String PATTERN_VERSION = "[0-9.]+-?(?:pre|SNAPSHOT)?(?:[0-9.]+)?"; + + public static final String EXAMPLE_DOWNLOAD = "paper-1.18-10.jar"; + public static final String EXAMPLE_PROJECT_ID = "paper"; + public static final String EXAMPLE_PROJECT_NAME = "Paper"; + public static final String EXAMPLE_SHA256 = "f065e2d345d9d772d5cf2a1ce5c495c4cc56eb2fcd6820e82856485fa19414c8"; + public static final String EXAMPLE_VERSION = "1.18"; + + private Constants2() { + } +} diff --git a/src/main/java/io/papermc/bibliothek/api/v2/model/Change2.java b/src/main/java/io/papermc/bibliothek/api/v2/model/Change2.java new file mode 100644 index 0000000..16dd329 --- /dev/null +++ b/src/main/java/io/papermc/bibliothek/api/v2/model/Change2.java @@ -0,0 +1,61 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2024 PaperMC + * + * 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 io.papermc.bibliothek.api.v2.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.papermc.bibliothek.api.model.Change; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import org.jspecify.annotations.NullMarked; + +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_COMMIT; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_MESSAGE; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_SUMMARY; + +@NullMarked +@Schema +public record Change2( + @JsonProperty(FIELD_COMMIT) + @Schema(name = FIELD_COMMIT) + String commit, + + @JsonProperty(FIELD_SUMMARY) + @Schema(name = FIELD_SUMMARY) + String summary, + + @JsonProperty(FIELD_MESSAGE) + @Schema(name = FIELD_MESSAGE) + String message +) { + private Change2(final Change that) { + this(that.commit(), that.summary(), that.message()); + } + + public static List map(final List list) { + return list + .stream() + .map(Change2::new) + .toList(); + } +} diff --git a/src/main/java/io/papermc/bibliothek/api/v2/model/Download2.java b/src/main/java/io/papermc/bibliothek/api/v2/model/Download2.java new file mode 100644 index 0000000..995735a --- /dev/null +++ b/src/main/java/io/papermc/bibliothek/api/v2/model/Download2.java @@ -0,0 +1,61 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2024 PaperMC + * + * 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 io.papermc.bibliothek.api.v2.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.papermc.bibliothek.api.model.Download; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.Map; +import java.util.stream.Collectors; +import org.jspecify.annotations.NullMarked; + +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_DOWNLOAD; +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_SHA256; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_NAME; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_SHA256; +import static io.papermc.bibliothek.api.v2.Constants2.PATTERN_DOWNLOAD; +import static io.papermc.bibliothek.api.v2.Constants2.PATTERN_SHA256; + +@NullMarked +@Schema +public record Download2( + @JsonProperty(FIELD_NAME) + @Schema(name = FIELD_NAME, pattern = PATTERN_DOWNLOAD, example = EXAMPLE_DOWNLOAD) + String name, + + @JsonProperty(FIELD_SHA256) + @Schema(name = FIELD_SHA256, pattern = PATTERN_SHA256, example = EXAMPLE_SHA256) + String sha256 +) { + private Download2(final Download that) { + this(that.name(), that.sha256()); + } + + public static Map map(final Map map) { + return map.entrySet() + .stream() + .map(e -> Map.entry(e.getKey(), new Download2(e.getValue()))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } +} diff --git a/src/main/java/io/papermc/bibliothek/api/v2/response/BuildResponse.java b/src/main/java/io/papermc/bibliothek/api/v2/response/BuildResponse.java new file mode 100644 index 0000000..49e3b33 --- /dev/null +++ b/src/main/java/io/papermc/bibliothek/api/v2/response/BuildResponse.java @@ -0,0 +1,92 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2024 PaperMC + * + * 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 io.papermc.bibliothek.api.v2.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.papermc.bibliothek.api.model.Channel; +import io.papermc.bibliothek.api.serialization.LegacyChannelSerializer; +import io.papermc.bibliothek.api.v2.model.Change2; +import io.papermc.bibliothek.api.v2.model.Download2; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.Instant; +import java.util.List; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_PROJECT_NAME; +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_VERSION; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_BUILD; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_CHANGES; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_CHANNEL; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_DOWNLOADS; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROJECT_NAME; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROMOTED; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_TIME; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_VERSION; +import static io.papermc.bibliothek.api.v2.Constants2.PATTERN_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.PATTERN_VERSION; + +@NullMarked +public record BuildResponse( + @JsonProperty(FIELD_PROJECT_ID) + @Schema(name = FIELD_PROJECT_ID, pattern = PATTERN_PROJECT_ID, example = EXAMPLE_PROJECT_ID) + String projectId, + + @JsonProperty(FIELD_PROJECT_NAME) + @Schema(name = FIELD_PROJECT_NAME, example = EXAMPLE_PROJECT_NAME) + String projectName, + + @JsonProperty(FIELD_VERSION) + @Schema(name = FIELD_VERSION, pattern = PATTERN_VERSION, example = EXAMPLE_VERSION) + String version, + + @JsonProperty(FIELD_BUILD) + @Schema(name = FIELD_BUILD, pattern = "\\d+", example = "10") + int build, + + @JsonProperty(FIELD_TIME) + @Schema(name = FIELD_TIME) + Instant time, + + @JsonProperty(FIELD_CHANNEL) + @JsonSerialize(using = LegacyChannelSerializer.class) + @Schema(name = FIELD_CHANNEL) + Channel channel, + + @JsonProperty(FIELD_PROMOTED) + @Schema(name = FIELD_PROMOTED) + boolean promoted, + + @JsonProperty(FIELD_CHANGES) + @Schema(name = FIELD_CHANGES) + List changes, + + @JsonProperty(FIELD_DOWNLOADS) + @Schema(name = FIELD_DOWNLOADS) + Map downloads +) { +} diff --git a/src/main/java/io/papermc/bibliothek/api/v2/response/ProjectResponse.java b/src/main/java/io/papermc/bibliothek/api/v2/response/ProjectResponse.java new file mode 100644 index 0000000..f9a85a0 --- /dev/null +++ b/src/main/java/io/papermc/bibliothek/api/v2/response/ProjectResponse.java @@ -0,0 +1,57 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2024 PaperMC + * + * 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 io.papermc.bibliothek.api.v2.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import org.jspecify.annotations.NullMarked; + +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_PROJECT_NAME; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROJECT_NAME; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_VERSIONS; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_VERSION_GROUPS; +import static io.papermc.bibliothek.api.v2.Constants2.PATTERN_PROJECT_ID; + +@NullMarked +public record ProjectResponse( + @JsonProperty(FIELD_PROJECT_ID) + @Schema(name = FIELD_PROJECT_ID, pattern = PATTERN_PROJECT_ID, example = EXAMPLE_PROJECT_ID) + String projectId, + + @JsonProperty(FIELD_PROJECT_NAME) + @Schema(name = FIELD_PROJECT_NAME, example = EXAMPLE_PROJECT_NAME) + String projectName, + + @JsonProperty(FIELD_VERSION_GROUPS) + @Schema(name = FIELD_VERSION_GROUPS) + List versionGroups, + + @JsonProperty(FIELD_VERSIONS) + @Schema(name = FIELD_VERSIONS) + List versions +) { +} diff --git a/src/main/java/io/papermc/bibliothek/database/repository/VersionFamilyCollection.java b/src/main/java/io/papermc/bibliothek/api/v2/response/ProjectsResponse.java similarity index 69% rename from src/main/java/io/papermc/bibliothek/database/repository/VersionFamilyCollection.java rename to src/main/java/io/papermc/bibliothek/api/v2/response/ProjectsResponse.java index f678f8d..adeb3b3 100644 --- a/src/main/java/io/papermc/bibliothek/database/repository/VersionFamilyCollection.java +++ b/src/main/java/io/papermc/bibliothek/api/v2/response/ProjectsResponse.java @@ -21,18 +21,19 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package io.papermc.bibliothek.database.repository; +package io.papermc.bibliothek.api.v2.response; -import io.papermc.bibliothek.database.model.VersionFamily; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; -import java.util.Optional; -import org.bson.types.ObjectId; -import org.springframework.data.mongodb.repository.MongoRepository; -import org.springframework.stereotype.Repository; +import org.jspecify.annotations.NullMarked; -@Repository -public interface VersionFamilyCollection extends MongoRepository { - List findAllByProject(final ObjectId project); +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROJECTS; - Optional findByProjectAndName(final ObjectId project, final String name); +@NullMarked +public record ProjectsResponse( + @JsonProperty(FIELD_PROJECTS) + @Schema(name = FIELD_PROJECTS) + List projects +) { } diff --git a/src/main/java/io/papermc/bibliothek/api/v2/response/VersionBuildsResponse.java b/src/main/java/io/papermc/bibliothek/api/v2/response/VersionBuildsResponse.java new file mode 100644 index 0000000..882265e --- /dev/null +++ b/src/main/java/io/papermc/bibliothek/api/v2/response/VersionBuildsResponse.java @@ -0,0 +1,101 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2024 PaperMC + * + * 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 io.papermc.bibliothek.api.v2.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.papermc.bibliothek.api.model.Channel; +import io.papermc.bibliothek.api.serialization.LegacyChannelSerializer; +import io.papermc.bibliothek.api.v2.model.Change2; +import io.papermc.bibliothek.api.v2.model.Download2; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.Instant; +import java.util.List; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_PROJECT_NAME; +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_VERSION; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_BUILD; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_BUILDS; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_CHANGES; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_CHANNEL; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_DOWNLOADS; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROJECT_NAME; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROMOTED; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_TIME; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_VERSION; +import static io.papermc.bibliothek.api.v2.Constants2.PATTERN_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.PATTERN_VERSION; + +@NullMarked +public record VersionBuildsResponse( + @Schema(name = FIELD_PROJECT_ID, pattern = PATTERN_PROJECT_ID, example = EXAMPLE_PROJECT_ID) + @JsonProperty(FIELD_PROJECT_ID) + String projectId, + + @JsonProperty(FIELD_PROJECT_NAME) + @Schema(name = FIELD_PROJECT_NAME, example = EXAMPLE_PROJECT_NAME) + String projectName, + + @JsonProperty(FIELD_VERSION) + @Schema(name = FIELD_VERSION, pattern = PATTERN_VERSION, example = EXAMPLE_VERSION) + String version, + + @JsonProperty(FIELD_BUILDS) + @Schema(name = FIELD_BUILDS) + List builds +) { + + @NullMarked + public record VersionBuild( + @JsonProperty(FIELD_BUILD) + @Schema(name = FIELD_BUILD, pattern = "\\d+", example = "10") + int build, + + @JsonProperty(FIELD_TIME) + @Schema(name = FIELD_TIME) + Instant time, + + @JsonProperty(FIELD_CHANNEL) + @JsonSerialize(using = LegacyChannelSerializer.class) + @Schema(name = FIELD_CHANNEL) + Channel channel, + + @JsonProperty(FIELD_PROMOTED) + @Schema(name = FIELD_PROMOTED) + boolean promoted, + + @JsonProperty(FIELD_CHANGES) + @Schema(name = FIELD_CHANGES) + List changes, + + @JsonProperty(FIELD_DOWNLOADS) + @Schema(name = FIELD_DOWNLOADS) + Map downloads + ) { + } +} diff --git a/src/main/java/io/papermc/bibliothek/api/v2/response/VersionFamilyBuildsResponse.java b/src/main/java/io/papermc/bibliothek/api/v2/response/VersionFamilyBuildsResponse.java new file mode 100644 index 0000000..aa02f89 --- /dev/null +++ b/src/main/java/io/papermc/bibliothek/api/v2/response/VersionFamilyBuildsResponse.java @@ -0,0 +1,111 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2024 PaperMC + * + * 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 io.papermc.bibliothek.api.v2.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.papermc.bibliothek.api.model.Channel; +import io.papermc.bibliothek.api.serialization.LegacyChannelSerializer; +import io.papermc.bibliothek.api.v2.model.Change2; +import io.papermc.bibliothek.api.v2.model.Download2; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.Instant; +import java.util.List; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_PROJECT_NAME; +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_VERSION; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_BUILD; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_BUILDS; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_CHANGES; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_CHANNEL; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_DOWNLOADS; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROJECT_NAME; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROMOTED; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_TIME; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_VERSION; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_VERSIONS; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_VERSION_GROUP; +import static io.papermc.bibliothek.api.v2.Constants2.PATTERN_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.PATTERN_VERSION; + +@NullMarked +public record VersionFamilyBuildsResponse( + @JsonProperty(FIELD_PROJECT_ID) + @Schema(name = FIELD_PROJECT_ID, pattern = PATTERN_PROJECT_ID, example = EXAMPLE_PROJECT_ID) + String projectId, + + @JsonProperty(FIELD_PROJECT_NAME) + @Schema(name = FIELD_PROJECT_NAME, example = EXAMPLE_PROJECT_NAME) + String projectName, + + @JsonProperty(FIELD_VERSION_GROUP) + @Schema(name = FIELD_VERSION_GROUP, pattern = PATTERN_VERSION, example = EXAMPLE_VERSION) + String versionGroup, + + @JsonProperty(FIELD_VERSIONS) + @Schema(name = FIELD_VERSIONS) + List versions, + + @JsonProperty(FIELD_BUILDS) + @Schema(name = FIELD_BUILDS) + List builds +) { + + @NullMarked + public record VersionFamilyBuild( + @JsonProperty(FIELD_VERSION) + @Schema(name = FIELD_VERSION, pattern = PATTERN_VERSION, example = EXAMPLE_VERSION) + String version, + + @JsonProperty(FIELD_BUILD) + @Schema(name = FIELD_BUILD, pattern = "\\d+", example = "10") + int build, + + @JsonProperty(FIELD_TIME) + @Schema(name = FIELD_TIME) + Instant time, + + @JsonProperty(FIELD_CHANNEL) + @JsonSerialize(using = LegacyChannelSerializer.class) + @Schema(name = FIELD_CHANNEL) + Channel channel, + + @JsonProperty(FIELD_PROMOTED) + @Schema(name = FIELD_PROMOTED) + boolean promoted, + + @JsonProperty(FIELD_CHANGES) + @Schema(name = FIELD_CHANGES) + List changes, + + @JsonProperty(FIELD_DOWNLOADS) + @Schema(name = FIELD_DOWNLOADS) + Map downloads + ) { + } +} diff --git a/src/main/java/io/papermc/bibliothek/api/v2/response/VersionFamilyResponse.java b/src/main/java/io/papermc/bibliothek/api/v2/response/VersionFamilyResponse.java new file mode 100644 index 0000000..8ed7a84 --- /dev/null +++ b/src/main/java/io/papermc/bibliothek/api/v2/response/VersionFamilyResponse.java @@ -0,0 +1,59 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2024 PaperMC + * + * 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 io.papermc.bibliothek.api.v2.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import org.jspecify.annotations.NullMarked; + +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_PROJECT_NAME; +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_VERSION; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROJECT_NAME; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_VERSIONS; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_VERSION_GROUP; +import static io.papermc.bibliothek.api.v2.Constants2.PATTERN_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.PATTERN_VERSION; + +@NullMarked +public record VersionFamilyResponse( + @JsonProperty(FIELD_PROJECT_ID) + @Schema(name = FIELD_PROJECT_ID, pattern = PATTERN_PROJECT_ID, example = EXAMPLE_PROJECT_ID) + String projectId, + + @JsonProperty(FIELD_PROJECT_NAME) + @Schema(name = FIELD_PROJECT_NAME, example = EXAMPLE_PROJECT_NAME) + String projectName, + + @JsonProperty(FIELD_VERSION_GROUP) + @Schema(name = FIELD_VERSION_GROUP, pattern = PATTERN_VERSION, example = EXAMPLE_VERSION) + String versionGroup, + + @JsonProperty(FIELD_VERSIONS) + @Schema(name = FIELD_VERSIONS) + List versions +) { +} diff --git a/src/main/java/io/papermc/bibliothek/api/v2/response/VersionResponse.java b/src/main/java/io/papermc/bibliothek/api/v2/response/VersionResponse.java new file mode 100644 index 0000000..e85927d --- /dev/null +++ b/src/main/java/io/papermc/bibliothek/api/v2/response/VersionResponse.java @@ -0,0 +1,59 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2024 PaperMC + * + * 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 io.papermc.bibliothek.api.v2.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import org.jspecify.annotations.NullMarked; + +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_PROJECT_NAME; +import static io.papermc.bibliothek.api.v2.Constants2.EXAMPLE_VERSION; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_BUILDS; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_PROJECT_NAME; +import static io.papermc.bibliothek.api.v2.Constants2.FIELD_VERSION; +import static io.papermc.bibliothek.api.v2.Constants2.PATTERN_PROJECT_ID; +import static io.papermc.bibliothek.api.v2.Constants2.PATTERN_VERSION; + +@NullMarked +public record VersionResponse( + @JsonProperty(FIELD_PROJECT_ID) + @Schema(name = FIELD_PROJECT_ID, pattern = PATTERN_PROJECT_ID, example = EXAMPLE_PROJECT_ID) + String projectId, + + @JsonProperty(FIELD_PROJECT_NAME) + @Schema(name = FIELD_PROJECT_NAME, example = EXAMPLE_PROJECT_NAME) + String projectName, + + @JsonProperty(FIELD_VERSION) + @Schema(name = FIELD_VERSION, pattern = PATTERN_VERSION, example = EXAMPLE_VERSION) + String version, + + @JsonProperty(FIELD_BUILDS) + @Schema(name = FIELD_BUILDS) + List builds +) { +} diff --git a/src/main/java/io/papermc/bibliothek/configuration/AppConfiguration.java b/src/main/java/io/papermc/bibliothek/configuration/AppConfiguration.java index 29011e0..22c7990 100644 --- a/src/main/java/io/papermc/bibliothek/configuration/AppConfiguration.java +++ b/src/main/java/io/papermc/bibliothek/configuration/AppConfiguration.java @@ -26,10 +26,12 @@ import jakarta.validation.constraints.NotNull; import java.net.URL; import java.nio.file.Path; +import org.jspecify.annotations.NullMarked; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.validation.annotation.Validated; @ConfigurationProperties(prefix = "app") +@NullMarked @Validated public class AppConfiguration { private URL apiBaseUrl; diff --git a/src/main/java/io/papermc/bibliothek/configuration/MongoConfiguration.java b/src/main/java/io/papermc/bibliothek/configuration/MongoConfiguration.java index 7e158ed..05e159d 100644 --- a/src/main/java/io/papermc/bibliothek/configuration/MongoConfiguration.java +++ b/src/main/java/io/papermc/bibliothek/configuration/MongoConfiguration.java @@ -23,6 +23,7 @@ */ package io.papermc.bibliothek.configuration; +import org.jspecify.annotations.NullMarked; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.MongoDatabaseFactory; @@ -34,6 +35,7 @@ import org.springframework.data.mongodb.core.mapping.MongoMappingContext; @Configuration +@NullMarked class MongoConfiguration { @Bean MappingMongoConverter mappingMongoConverter( diff --git a/src/main/java/io/papermc/bibliothek/configuration/OpenAPIConfiguration.java b/src/main/java/io/papermc/bibliothek/configuration/OpenAPIConfiguration.java index 38f089d..fcf2da5 100644 --- a/src/main/java/io/papermc/bibliothek/configuration/OpenAPIConfiguration.java +++ b/src/main/java/io/papermc/bibliothek/configuration/OpenAPIConfiguration.java @@ -31,11 +31,13 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; +import org.jspecify.annotations.NullMarked; import org.springdoc.core.customizers.OpenApiCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration +@NullMarked class OpenAPIConfiguration { @Bean OpenAPI openAPI(final AppConfiguration configuration) { diff --git a/src/main/java/io/papermc/bibliothek/configuration/WebConfiguration.java b/src/main/java/io/papermc/bibliothek/configuration/WebConfiguration.java index 0f885c6..7bb98a7 100644 --- a/src/main/java/io/papermc/bibliothek/configuration/WebConfiguration.java +++ b/src/main/java/io/papermc/bibliothek/configuration/WebConfiguration.java @@ -24,11 +24,13 @@ package io.papermc.bibliothek.configuration; import jakarta.servlet.Filter; +import org.jspecify.annotations.NullMarked; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.filter.ShallowEtagHeaderFilter; @Configuration +@NullMarked class WebConfiguration { @Bean Filter shallowETagHeaderFilter() { diff --git a/src/main/java/io/papermc/bibliothek/controller/v2/DownloadController.java b/src/main/java/io/papermc/bibliothek/controller/v2/DownloadController.java index 41e9a11..4ce3186 100644 --- a/src/main/java/io/papermc/bibliothek/controller/v2/DownloadController.java +++ b/src/main/java/io/papermc/bibliothek/controller/v2/DownloadController.java @@ -23,13 +23,14 @@ */ package io.papermc.bibliothek.controller.v2; +import io.papermc.bibliothek.api.model.Download; import io.papermc.bibliothek.configuration.AppConfiguration; -import io.papermc.bibliothek.database.model.Build; -import io.papermc.bibliothek.database.model.Project; -import io.papermc.bibliothek.database.model.Version; -import io.papermc.bibliothek.database.repository.BuildCollection; -import io.papermc.bibliothek.database.repository.ProjectCollection; -import io.papermc.bibliothek.database.repository.VersionCollection; +import io.papermc.bibliothek.database.model.BuildEntity; +import io.papermc.bibliothek.database.model.ProjectEntity; +import io.papermc.bibliothek.database.model.VersionEntity; +import io.papermc.bibliothek.database.repository.BuildRepository; +import io.papermc.bibliothek.database.repository.ProjectRepository; +import io.papermc.bibliothek.database.repository.VersionRepository; import io.papermc.bibliothek.exception.BuildNotFound; import io.papermc.bibliothek.exception.DownloadFailed; import io.papermc.bibliothek.exception.DownloadNotFound; @@ -49,6 +50,8 @@ import java.nio.file.Path; import java.time.Duration; import java.util.Map; +import org.intellij.lang.annotations.Language; +import org.jspecify.annotations.NullMarked; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.FileSystemResource; import org.springframework.http.CacheControl; @@ -61,21 +64,25 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -@RestController +@NullMarked @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) +@RestController public class DownloadController { + // NOTE: this pattern cannot contain any capturing groups + @Language("RegExp") + private static final String DOWNLOAD_NAME_PATTERN = "[a-zA-Z0-9._-]+"; private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofDays(7)); private final AppConfiguration configuration; - private final ProjectCollection projects; - private final VersionCollection versions; - private final BuildCollection builds; + private final ProjectRepository projects; + private final VersionRepository versions; + private final BuildRepository builds; @Autowired private DownloadController( final AppConfiguration configuration, - final ProjectCollection projects, - final VersionCollection versions, - final BuildCollection builds + final ProjectRepository projects, + final VersionRepository versions, + final BuildRepository builds ) { this.configuration = configuration; this.projects = projects; @@ -104,7 +111,7 @@ private DownloadController( } ) @GetMapping( - value = "/v2/projects/{project:[a-z]+}/versions/{version:" + Version.PATTERN + "}/builds/{build:\\d+}/downloads/{download:" + Build.Download.PATTERN + "}", + value = "/v2/projects/{project:[a-z]+}/versions/{version:" + VersionEntity.PATTERN + "}/builds/{build:\\d+}/downloads/{download:" + DOWNLOAD_NAME_PATTERN + "}", produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.ALL_VALUE @@ -118,7 +125,7 @@ public ResponseEntity download( final String projectName, @Parameter(description = "A version of the project.") @PathVariable("version") - @Pattern(regexp = Version.PATTERN) // + @Pattern(regexp = VersionEntity.PATTERN) // final String versionName, @Parameter(description = "A build of the version.") @PathVariable("build") @@ -126,14 +133,14 @@ public ResponseEntity download( final int buildNumber, @Parameter(description = "A download of the build.") @PathVariable("download") - @Pattern(regexp = Build.Download.PATTERN) // + @Pattern(regexp = DOWNLOAD_NAME_PATTERN) // final String downloadName ) { - final Project project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); - final Version version = this.versions.findByProjectAndName(project._id(), versionName).orElseThrow(VersionNotFound::new); - final Build build = this.builds.findByProjectAndVersionAndNumber(project._id(), version._id(), buildNumber).orElseThrow(BuildNotFound::new); + final ProjectEntity project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); + final VersionEntity version = this.versions.findByProjectAndName(project, versionName).orElseThrow(VersionNotFound::new); + final BuildEntity build = this.builds.findByProjectAndVersionAndNumber(project, version, buildNumber).orElseThrow(BuildNotFound::new); - for (final Map.Entry download : build.downloads().entrySet()) { + for (final Map.Entry download : build.downloads().entrySet()) { if (download.getValue().name().equals(downloadName)) { try { return new JavaArchive( diff --git a/src/main/java/io/papermc/bibliothek/controller/v2/ProjectController.java b/src/main/java/io/papermc/bibliothek/controller/v2/ProjectController.java index 7accce1..d225121 100644 --- a/src/main/java/io/papermc/bibliothek/controller/v2/ProjectController.java +++ b/src/main/java/io/papermc/bibliothek/controller/v2/ProjectController.java @@ -23,12 +23,13 @@ */ package io.papermc.bibliothek.controller.v2; -import io.papermc.bibliothek.database.model.Project; -import io.papermc.bibliothek.database.model.Version; -import io.papermc.bibliothek.database.model.VersionFamily; -import io.papermc.bibliothek.database.repository.ProjectCollection; -import io.papermc.bibliothek.database.repository.VersionCollection; -import io.papermc.bibliothek.database.repository.VersionFamilyCollection; +import io.papermc.bibliothek.api.v2.response.ProjectResponse; +import io.papermc.bibliothek.database.model.ProjectEntity; +import io.papermc.bibliothek.database.model.VersionEntity; +import io.papermc.bibliothek.database.model.VersionFamilyEntity; +import io.papermc.bibliothek.database.repository.ProjectRepository; +import io.papermc.bibliothek.database.repository.VersionFamilyRepository; +import io.papermc.bibliothek.database.repository.VersionRepository; import io.papermc.bibliothek.exception.ProjectNotFound; import io.papermc.bibliothek.util.HTTP; import io.swagger.v3.oas.annotations.Operation; @@ -39,6 +40,7 @@ import jakarta.validation.constraints.Pattern; import java.time.Duration; import java.util.List; +import org.jspecify.annotations.NullMarked; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.CacheControl; import org.springframework.http.MediaType; @@ -48,19 +50,20 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -@RestController +@NullMarked @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) +@RestController public class ProjectController { private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofMinutes(30)); - private final ProjectCollection projects; - private final VersionFamilyCollection families; - private final VersionCollection versions; + private final ProjectRepository projects; + private final VersionFamilyRepository families; + private final VersionRepository versions; @Autowired private ProjectController( - final ProjectCollection projects, - final VersionFamilyCollection families, - final VersionCollection versions + final ProjectRepository projects, + final VersionFamilyRepository families, + final VersionRepository versions ) { this.projects = projects; this.families = families; @@ -81,30 +84,14 @@ public ResponseEntity project( @Pattern(regexp = "[a-z]+") // final String projectName ) { - final Project project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); - final List families = this.families.findAllByProject(project._id()); - final List versions = this.versions.findAllByProject(project._id()); - return HTTP.cachedOk(ProjectResponse.from(project, families, versions), CACHE); - } - - @Schema - private record ProjectResponse( - @Schema(name = "project_id", pattern = "[a-z]+", example = "paper") - String project_id, - @Schema(name = "project_name", example = "Paper") - String project_name, - @Schema(name = "version_groups") - List version_groups, - @Schema(name = "versions") - List versions - ) { - static ProjectResponse from(final Project project, final List families, final List versions) { - return new ProjectResponse( - project.name(), - project.friendlyName(), - families.stream().sorted(VersionFamily.COMPARATOR).map(VersionFamily::name).toList(), - versions.stream().sorted(Version.COMPARATOR).map(Version::name).toList() - ); - } + final ProjectEntity project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); + final List families = this.families.findAllByProject(project); + final List versions = this.versions.findAllByProject(project); + return HTTP.cachedOk(new ProjectResponse( + project.name(), + project.friendlyName(), + families.stream().sorted(VersionFamilyEntity.COMPARATOR).map(VersionFamilyEntity::name).toList(), + versions.stream().sorted(VersionEntity.COMPARATOR).map(VersionEntity::name).toList() + ), CACHE); } } diff --git a/src/main/java/io/papermc/bibliothek/controller/v2/ProjectsController.java b/src/main/java/io/papermc/bibliothek/controller/v2/ProjectsController.java index 505e642..56cf6b4 100644 --- a/src/main/java/io/papermc/bibliothek/controller/v2/ProjectsController.java +++ b/src/main/java/io/papermc/bibliothek/controller/v2/ProjectsController.java @@ -23,8 +23,9 @@ */ package io.papermc.bibliothek.controller.v2; -import io.papermc.bibliothek.database.model.Project; -import io.papermc.bibliothek.database.repository.ProjectCollection; +import io.papermc.bibliothek.api.v2.response.ProjectsResponse; +import io.papermc.bibliothek.database.model.ProjectEntity; +import io.papermc.bibliothek.database.repository.ProjectRepository; import io.papermc.bibliothek.util.HTTP; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; @@ -32,6 +33,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import java.time.Duration; import java.util.List; +import org.jspecify.annotations.NullMarked; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.CacheControl; import org.springframework.http.MediaType; @@ -40,14 +42,15 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -@RestController +@NullMarked @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) +@RestController public class ProjectsController { private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofDays(7)); - private final ProjectCollection projects; + private final ProjectRepository projects; @Autowired - private ProjectsController(final ProjectCollection projects) { + private ProjectsController(final ProjectRepository projects) { this.projects = projects; } @@ -60,17 +63,9 @@ private ProjectsController(final ProjectCollection projects) { @GetMapping("/v2/projects") @Operation(summary = "Gets a list of all available projects.") public ResponseEntity projects() { - final List projects = this.projects.findAll(); - return HTTP.cachedOk(ProjectsResponse.from(projects), CACHE); - } - - @Schema - private record ProjectsResponse( - @Schema(name = "projects") - List projects - ) { - static ProjectsResponse from(final List projects) { - return new ProjectsResponse(projects.stream().map(Project::name).toList()); - } + final List projects = this.projects.findAll(); + return HTTP.cachedOk(new ProjectsResponse( + projects.stream().map(ProjectEntity::name).toList() + ), CACHE); } } diff --git a/src/main/java/io/papermc/bibliothek/controller/v2/VersionBuildController.java b/src/main/java/io/papermc/bibliothek/controller/v2/VersionBuildController.java index c63be29..78aa6a9 100644 --- a/src/main/java/io/papermc/bibliothek/controller/v2/VersionBuildController.java +++ b/src/main/java/io/papermc/bibliothek/controller/v2/VersionBuildController.java @@ -23,12 +23,15 @@ */ package io.papermc.bibliothek.controller.v2; -import io.papermc.bibliothek.database.model.Build; -import io.papermc.bibliothek.database.model.Project; -import io.papermc.bibliothek.database.model.Version; -import io.papermc.bibliothek.database.repository.BuildCollection; -import io.papermc.bibliothek.database.repository.ProjectCollection; -import io.papermc.bibliothek.database.repository.VersionCollection; +import io.papermc.bibliothek.api.v2.model.Change2; +import io.papermc.bibliothek.api.v2.model.Download2; +import io.papermc.bibliothek.api.v2.response.BuildResponse; +import io.papermc.bibliothek.database.model.BuildEntity; +import io.papermc.bibliothek.database.model.ProjectEntity; +import io.papermc.bibliothek.database.model.VersionEntity; +import io.papermc.bibliothek.database.repository.BuildRepository; +import io.papermc.bibliothek.database.repository.ProjectRepository; +import io.papermc.bibliothek.database.repository.VersionRepository; import io.papermc.bibliothek.exception.BuildNotFound; import io.papermc.bibliothek.exception.ProjectNotFound; import io.papermc.bibliothek.exception.VersionNotFound; @@ -41,9 +44,7 @@ import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Positive; import java.time.Duration; -import java.time.Instant; -import java.util.List; -import java.util.Map; +import org.jspecify.annotations.NullMarked; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.CacheControl; import org.springframework.http.MediaType; @@ -53,19 +54,20 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -@RestController +@NullMarked @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) +@RestController public class VersionBuildController { private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofDays(7)); - private final ProjectCollection projects; - private final VersionCollection versions; - private final BuildCollection builds; + private final ProjectRepository projects; + private final VersionRepository versions; + private final BuildRepository builds; @Autowired private VersionBuildController( - final ProjectCollection projects, - final VersionCollection versions, - final BuildCollection builds + final ProjectRepository projects, + final VersionRepository versions, + final BuildRepository builds ) { this.projects = projects; this.versions = versions; @@ -78,7 +80,7 @@ private VersionBuildController( ), responseCode = "200" ) - @GetMapping("/v2/projects/{project:[a-z]+}/versions/{version:" + Version.PATTERN + "}/builds/{build:\\d+}") + @GetMapping("/v2/projects/{project:[a-z]+}/versions/{version:" + VersionEntity.PATTERN + "}/builds/{build:\\d+}") @Operation(summary = "Gets information related to a specific build.") public ResponseEntity build( @Parameter(name = "project", description = "The project identifier.", example = "paper") @@ -87,52 +89,26 @@ public ResponseEntity build( final String projectName, @Parameter(description = "A version of the project.") @PathVariable("version") - @Pattern(regexp = Version.PATTERN) // + @Pattern(regexp = VersionEntity.PATTERN) // final String versionName, @Parameter(description = "A build of the version.") @PathVariable("build") @Positive // final int buildNumber ) { - final Project project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); - final Version version = this.versions.findByProjectAndName(project._id(), versionName).orElseThrow(VersionNotFound::new); - final Build build = this.builds.findByProjectAndVersionAndNumber(project._id(), version._id(), buildNumber).orElseThrow(BuildNotFound::new); - return HTTP.cachedOk(BuildResponse.from(project, version, build), CACHE); - } - - @Schema - private record BuildResponse( - @Schema(name = "project_id", pattern = "[a-z]+", example = "paper") - String project_id, - @Schema(name = "project_name", example = "Paper") - String project_name, - @Schema(name = "version", pattern = Version.PATTERN, example = "1.18") - String version, - @Schema(name = "build", pattern = "\\d+", example = "10") - int build, - @Schema(name = "time") - Instant time, - @Schema(name = "channel") - Build.Channel channel, - @Schema(name = "promoted") - boolean promoted, - @Schema(name = "changes") - List changes, - @Schema(name = "downloads") - Map downloads - ) { - static BuildResponse from(final Project project, final Version version, final Build build) { - return new BuildResponse( - project.name(), - project.friendlyName(), - version.name(), - build.number(), - build.time(), - build.channelOrDefault(), - build.promotedOrDefault(), - build.changes(), - build.downloads() - ); - } + final ProjectEntity project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); + final VersionEntity version = this.versions.findByProjectAndName(project, versionName).orElseThrow(VersionNotFound::new); + final BuildEntity build = this.builds.findByProjectAndVersionAndNumber(project, version, buildNumber).orElseThrow(BuildNotFound::new); + return HTTP.cachedOk(new BuildResponse( + project.name(), + project.friendlyName(), + version.name(), + build.number(), + build.time(), + build.channelOrDefault(), + build.promotedOrDefault(), + Change2.map(build.changes()), + Download2.map(build.downloads()) + ), CACHE); } } diff --git a/src/main/java/io/papermc/bibliothek/controller/v2/VersionBuildsController.java b/src/main/java/io/papermc/bibliothek/controller/v2/VersionBuildsController.java index dfdb715..aa5c314 100644 --- a/src/main/java/io/papermc/bibliothek/controller/v2/VersionBuildsController.java +++ b/src/main/java/io/papermc/bibliothek/controller/v2/VersionBuildsController.java @@ -23,12 +23,15 @@ */ package io.papermc.bibliothek.controller.v2; -import io.papermc.bibliothek.database.model.Build; -import io.papermc.bibliothek.database.model.Project; -import io.papermc.bibliothek.database.model.Version; -import io.papermc.bibliothek.database.repository.BuildCollection; -import io.papermc.bibliothek.database.repository.ProjectCollection; -import io.papermc.bibliothek.database.repository.VersionCollection; +import io.papermc.bibliothek.api.v2.model.Change2; +import io.papermc.bibliothek.api.v2.model.Download2; +import io.papermc.bibliothek.api.v2.response.VersionBuildsResponse; +import io.papermc.bibliothek.database.model.BuildEntity; +import io.papermc.bibliothek.database.model.ProjectEntity; +import io.papermc.bibliothek.database.model.VersionEntity; +import io.papermc.bibliothek.database.repository.BuildRepository; +import io.papermc.bibliothek.database.repository.ProjectRepository; +import io.papermc.bibliothek.database.repository.VersionRepository; import io.papermc.bibliothek.exception.ProjectNotFound; import io.papermc.bibliothek.exception.VersionNotFound; import io.papermc.bibliothek.util.HTTP; @@ -39,9 +42,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.validation.constraints.Pattern; import java.time.Duration; -import java.time.Instant; import java.util.List; -import java.util.Map; +import org.jspecify.annotations.NullMarked; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.CacheControl; import org.springframework.http.MediaType; @@ -51,19 +53,20 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -@RestController +@NullMarked @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) +@RestController public class VersionBuildsController { private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofMinutes(5)); - private final ProjectCollection projects; - private final VersionCollection versions; - private final BuildCollection builds; + private final ProjectRepository projects; + private final VersionRepository versions; + private final BuildRepository builds; @Autowired private VersionBuildsController( - final ProjectCollection projects, - final VersionCollection versions, - final BuildCollection builds + final ProjectRepository projects, + final VersionRepository versions, + final BuildRepository builds ) { this.projects = projects; this.versions = versions; @@ -72,11 +75,11 @@ private VersionBuildsController( @ApiResponse( content = @Content( - schema = @Schema(implementation = BuildsResponse.class) + schema = @Schema(implementation = VersionBuildsResponse.class) ), responseCode = "200" ) - @GetMapping("/v2/projects/{project:[a-z]+}/versions/{version:" + Version.PATTERN + "}/builds") + @GetMapping("/v2/projects/{project:[a-z]+}/versions/{version:" + VersionEntity.PATTERN + "}/builds") @Operation(summary = "Gets all available builds for a project's version.") public ResponseEntity builds( @Parameter(name = "project", description = "The project identifier.", example = "paper") @@ -85,57 +88,24 @@ public ResponseEntity builds( final String projectName, @Parameter(description = "A version of the project.") @PathVariable("version") - @Pattern(regexp = Version.PATTERN) // + @Pattern(regexp = VersionEntity.PATTERN) // final String versionName ) { - final Project project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); - final Version version = this.versions.findByProjectAndName(project._id(), versionName).orElseThrow(VersionNotFound::new); - final List builds = this.builds.findAllByProjectAndVersion(project._id(), version._id()); - return HTTP.cachedOk(BuildsResponse.from(project, version, builds), CACHE); - } - - @Schema - private record BuildsResponse( - @Schema(name = "project_id", pattern = "[a-z]+", example = "paper") - String project_id, - @Schema(name = "project_name", example = "Paper") - String project_name, - @Schema(name = "version", pattern = Version.PATTERN, example = "1.18") - String version, - @Schema(name = "builds") - List builds - ) { - static BuildsResponse from(final Project project, final Version version, final List builds) { - return new BuildsResponse( - project.name(), - project.friendlyName(), - version.name(), - builds.stream().map(build -> new VersionBuild( - build.number(), - build.time(), - build.channelOrDefault(), - build.promotedOrDefault(), - build.changes(), - build.downloads() - )).toList() - ); - } - - @Schema - public record VersionBuild( - @Schema(name = "build", pattern = "\\d+", example = "10") - int build, - @Schema(name = "time") - Instant time, - @Schema(name = "channel") - Build.Channel channel, - @Schema(name = "promoted") - boolean promoted, - @Schema(name = "changes") - List changes, - @Schema(name = "downloads") - Map downloads - ) { - } + final ProjectEntity project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); + final VersionEntity version = this.versions.findByProjectAndName(project, versionName).orElseThrow(VersionNotFound::new); + final List builds = this.builds.findAllByProjectAndVersion(project, version); + return HTTP.cachedOk(new VersionBuildsResponse( + project.name(), + project.friendlyName(), + version.name(), + builds.stream().map(build -> new VersionBuildsResponse.VersionBuild( + build.number(), + build.time(), + build.channelOrDefault(), + build.promotedOrDefault(), + Change2.map(build.changes()), + Download2.map(build.downloads()) + )).toList() + ), CACHE); } } diff --git a/src/main/java/io/papermc/bibliothek/controller/v2/VersionController.java b/src/main/java/io/papermc/bibliothek/controller/v2/VersionController.java index 6058148..7fd6394 100644 --- a/src/main/java/io/papermc/bibliothek/controller/v2/VersionController.java +++ b/src/main/java/io/papermc/bibliothek/controller/v2/VersionController.java @@ -23,12 +23,13 @@ */ package io.papermc.bibliothek.controller.v2; -import io.papermc.bibliothek.database.model.Build; -import io.papermc.bibliothek.database.model.Project; -import io.papermc.bibliothek.database.model.Version; -import io.papermc.bibliothek.database.repository.BuildCollection; -import io.papermc.bibliothek.database.repository.ProjectCollection; -import io.papermc.bibliothek.database.repository.VersionCollection; +import io.papermc.bibliothek.api.v2.response.VersionResponse; +import io.papermc.bibliothek.database.model.BuildEntity; +import io.papermc.bibliothek.database.model.ProjectEntity; +import io.papermc.bibliothek.database.model.VersionEntity; +import io.papermc.bibliothek.database.repository.BuildRepository; +import io.papermc.bibliothek.database.repository.ProjectRepository; +import io.papermc.bibliothek.database.repository.VersionRepository; import io.papermc.bibliothek.exception.ProjectNotFound; import io.papermc.bibliothek.exception.VersionNotFound; import io.papermc.bibliothek.util.HTTP; @@ -40,6 +41,7 @@ import jakarta.validation.constraints.Pattern; import java.time.Duration; import java.util.List; +import org.jspecify.annotations.NullMarked; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.CacheControl; import org.springframework.http.MediaType; @@ -49,19 +51,20 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -@RestController +@NullMarked @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) +@RestController public class VersionController { private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofMinutes(5)); - private final ProjectCollection projects; - private final VersionCollection versions; - private final BuildCollection builds; + private final ProjectRepository projects; + private final VersionRepository versions; + private final BuildRepository builds; @Autowired private VersionController( - final ProjectCollection projects, - final VersionCollection versions, - final BuildCollection builds + final ProjectRepository projects, + final VersionRepository versions, + final BuildRepository builds ) { this.projects = projects; this.versions = versions; @@ -74,7 +77,7 @@ private VersionController( ), responseCode = "200" ) - @GetMapping("/v2/projects/{project:[a-z]+}/versions/{version:" + Version.PATTERN + "}") + @GetMapping("/v2/projects/{project:[a-z]+}/versions/{version:" + VersionEntity.PATTERN + "}") @Operation(summary = "Gets information about a version.") public ResponseEntity version( @Parameter(name = "project", description = "The project identifier.", example = "paper") @@ -83,33 +86,17 @@ public ResponseEntity version( final String projectName, @Parameter(description = "A version of the project.") @PathVariable("version") - @Pattern(regexp = Version.PATTERN) // + @Pattern(regexp = VersionEntity.PATTERN) // final String versionName ) { - final Project project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); - final Version version = this.versions.findByProjectAndName(project._id(), versionName).orElseThrow(VersionNotFound::new); - final List builds = this.builds.findAllByProjectAndVersion(project._id(), version._id()); - return HTTP.cachedOk(VersionResponse.from(project, version, builds), CACHE); - } - - @Schema - private record VersionResponse( - @Schema(name = "project_id", pattern = "[a-z]+", example = "paper") - String project_id, - @Schema(name = "project_name", example = "Paper") - String project_name, - @Schema(name = "version", pattern = Version.PATTERN, example = "1.18") - String version, - @Schema(name = "builds") - List builds - ) { - static VersionResponse from(final Project project, final Version version, final List builds) { - return new VersionResponse( - project.name(), - project.friendlyName(), - version.name(), - builds.stream().map(Build::number).toList() - ); - } + final ProjectEntity project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); + final VersionEntity version = this.versions.findByProjectAndName(project, versionName).orElseThrow(VersionNotFound::new); + final List builds = this.builds.findAllByProjectAndVersion(project, version); + return HTTP.cachedOk(new VersionResponse( + project.name(), + project.friendlyName(), + version.name(), + builds.stream().map(BuildEntity::number).toList() + ), CACHE); } } diff --git a/src/main/java/io/papermc/bibliothek/controller/v2/VersionFamilyBuildsController.java b/src/main/java/io/papermc/bibliothek/controller/v2/VersionFamilyBuildsController.java index d014599..6758b78 100644 --- a/src/main/java/io/papermc/bibliothek/controller/v2/VersionFamilyBuildsController.java +++ b/src/main/java/io/papermc/bibliothek/controller/v2/VersionFamilyBuildsController.java @@ -23,14 +23,17 @@ */ package io.papermc.bibliothek.controller.v2; -import io.papermc.bibliothek.database.model.Build; -import io.papermc.bibliothek.database.model.Project; -import io.papermc.bibliothek.database.model.Version; -import io.papermc.bibliothek.database.model.VersionFamily; -import io.papermc.bibliothek.database.repository.BuildCollection; -import io.papermc.bibliothek.database.repository.ProjectCollection; -import io.papermc.bibliothek.database.repository.VersionCollection; -import io.papermc.bibliothek.database.repository.VersionFamilyCollection; +import io.papermc.bibliothek.api.v2.model.Change2; +import io.papermc.bibliothek.api.v2.model.Download2; +import io.papermc.bibliothek.api.v2.response.VersionFamilyBuildsResponse; +import io.papermc.bibliothek.database.model.BuildEntity; +import io.papermc.bibliothek.database.model.ProjectEntity; +import io.papermc.bibliothek.database.model.VersionEntity; +import io.papermc.bibliothek.database.model.VersionFamilyEntity; +import io.papermc.bibliothek.database.repository.BuildRepository; +import io.papermc.bibliothek.database.repository.ProjectRepository; +import io.papermc.bibliothek.database.repository.VersionFamilyRepository; +import io.papermc.bibliothek.database.repository.VersionRepository; import io.papermc.bibliothek.exception.ProjectNotFound; import io.papermc.bibliothek.exception.VersionNotFound; import io.papermc.bibliothek.util.HTTP; @@ -41,12 +44,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.validation.constraints.Pattern; import java.time.Duration; -import java.time.Instant; import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -import org.bson.types.ObjectId; +import org.jspecify.annotations.NullMarked; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.CacheControl; import org.springframework.http.MediaType; @@ -56,21 +55,22 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -@RestController +@NullMarked @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) +@RestController public class VersionFamilyBuildsController { private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofMinutes(5)); - private final ProjectCollection projects; - private final VersionFamilyCollection families; - private final VersionCollection versions; - private final BuildCollection builds; + private final ProjectRepository projects; + private final VersionFamilyRepository families; + private final VersionRepository versions; + private final BuildRepository builds; @Autowired private VersionFamilyBuildsController( - final ProjectCollection projects, - final VersionFamilyCollection families, - final VersionCollection versions, - final BuildCollection builds + final ProjectRepository projects, + final VersionFamilyRepository families, + final VersionRepository versions, + final BuildRepository builds ) { this.projects = projects; this.families = families; @@ -84,7 +84,7 @@ private VersionFamilyBuildsController( ), responseCode = "200" ) - @GetMapping("/v2/projects/{project:[a-z]+}/version_group/{family:" + Version.PATTERN + "}/builds") + @GetMapping("/v2/projects/{project:[a-z]+}/version_group/{family:" + VersionEntity.PATTERN + "}/builds") @Operation(summary = "Gets all available builds for a project's version group.") public ResponseEntity familyBuilds( @Parameter(name = "project", description = "The project identifier.", example = "paper") @@ -93,65 +93,27 @@ public ResponseEntity familyBuilds( final String projectName, @Parameter(description = "The version group name.") @PathVariable("family") - @Pattern(regexp = Version.PATTERN) // + @Pattern(regexp = VersionEntity.PATTERN) // final String familyName ) { - final Project project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); - final VersionFamily family = this.families.findByProjectAndName(project._id(), familyName).orElseThrow(VersionNotFound::new); - final Map versions = this.versions.findAllByProjectAndGroup(project._id(), family._id()).stream() - .collect(Collectors.toMap(Version::_id, Function.identity())); - final List builds = this.builds.findAllByProjectAndVersionIn(project._id(), versions.keySet()); - return HTTP.cachedOk(VersionFamilyBuildsResponse.from(project, family, versions, builds), CACHE); - } - - @Schema - private record VersionFamilyBuildsResponse( - @Schema(name = "project_id", pattern = "[a-z]+", example = "paper") - String project_id, - @Schema(name = "project_name", example = "Paper") - String project_name, - @Schema(name = "version_group", pattern = Version.PATTERN, example = "1.18") - String version_group, - @Schema(name = "versions") - List versions, - @Schema(name = "builds") - List builds - ) { - static VersionFamilyBuildsResponse from(final Project project, final VersionFamily family, final Map versions, final List builds) { - return new VersionFamilyBuildsResponse( - project.name(), - project.friendlyName(), - family.name(), - versions.values().stream().sorted(Version.COMPARATOR).map(Version::name).toList(), - builds.stream().map(build -> new VersionFamilyBuild( - versions.get(build.version()).name(), - build.number(), - build.time(), - build.channelOrDefault(), - build.promotedOrDefault(), - build.changes(), - build.downloads() - )).toList() - ); - } - - @Schema - public static record VersionFamilyBuild( - @Schema(name = "version", pattern = Version.PATTERN, example = "1.18") - String version, - @Schema(name = "build", pattern = "\\d+", example = "10") - int build, - @Schema(name = "time") - Instant time, - @Schema(name = "channel") - Build.Channel channel, - @Schema(name = "promoted") - boolean promoted, - @Schema(name = "changes") - List changes, - @Schema(name = "downloads") - Map downloads - ) { - } + final ProjectEntity project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); + final VersionFamilyEntity family = this.families.findByProjectAndName(project, familyName).orElseThrow(VersionNotFound::new); + final List versions = this.versions.findAllByProjectAndGroup(project, family); + final List builds = this.builds.findAllByProjectAndVersionIn(project, versions); + return HTTP.cachedOk(new VersionFamilyBuildsResponse( + project.name(), + project.friendlyName(), + family.name(), + versions.stream().sorted(VersionEntity.COMPARATOR).map(VersionEntity::name).toList(), + builds.stream().map(build -> new VersionFamilyBuildsResponse.VersionFamilyBuild( + build.version().name(), + build.number(), + build.time(), + build.channelOrDefault(), + build.promotedOrDefault(), + Change2.map(build.changes()), + Download2.map(build.downloads()) + )).toList() + ), CACHE); } } diff --git a/src/main/java/io/papermc/bibliothek/controller/v2/VersionFamilyController.java b/src/main/java/io/papermc/bibliothek/controller/v2/VersionFamilyController.java index 97b1996..bd063b8 100644 --- a/src/main/java/io/papermc/bibliothek/controller/v2/VersionFamilyController.java +++ b/src/main/java/io/papermc/bibliothek/controller/v2/VersionFamilyController.java @@ -23,12 +23,13 @@ */ package io.papermc.bibliothek.controller.v2; -import io.papermc.bibliothek.database.model.Project; -import io.papermc.bibliothek.database.model.Version; -import io.papermc.bibliothek.database.model.VersionFamily; -import io.papermc.bibliothek.database.repository.ProjectCollection; -import io.papermc.bibliothek.database.repository.VersionCollection; -import io.papermc.bibliothek.database.repository.VersionFamilyCollection; +import io.papermc.bibliothek.api.v2.response.VersionFamilyResponse; +import io.papermc.bibliothek.database.model.ProjectEntity; +import io.papermc.bibliothek.database.model.VersionEntity; +import io.papermc.bibliothek.database.model.VersionFamilyEntity; +import io.papermc.bibliothek.database.repository.ProjectRepository; +import io.papermc.bibliothek.database.repository.VersionFamilyRepository; +import io.papermc.bibliothek.database.repository.VersionRepository; import io.papermc.bibliothek.exception.ProjectNotFound; import io.papermc.bibliothek.exception.VersionNotFound; import io.papermc.bibliothek.util.HTTP; @@ -40,6 +41,7 @@ import jakarta.validation.constraints.Pattern; import java.time.Duration; import java.util.List; +import org.jspecify.annotations.NullMarked; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.CacheControl; import org.springframework.http.MediaType; @@ -49,19 +51,20 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -@RestController +@NullMarked @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) +@RestController public class VersionFamilyController { private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofMinutes(5)); - private final ProjectCollection projects; - private final VersionFamilyCollection families; - private final VersionCollection versions; + private final ProjectRepository projects; + private final VersionFamilyRepository families; + private final VersionRepository versions; @Autowired private VersionFamilyController( - final ProjectCollection projects, - final VersionFamilyCollection families, - final VersionCollection versions + final ProjectRepository projects, + final VersionFamilyRepository families, + final VersionRepository versions ) { this.projects = projects; this.families = families; @@ -74,7 +77,7 @@ private VersionFamilyController( ), responseCode = "200" ) - @GetMapping("/v2/projects/{project:[a-z]+}/version_group/{family:" + Version.PATTERN + "}") + @GetMapping("/v2/projects/{project:[a-z]+}/version_group/{family:" + VersionEntity.PATTERN + "}") @Operation(summary = "Gets information about a project's version group.") public ResponseEntity family( @Parameter(name = "project", description = "The project identifier.", example = "paper") @@ -83,33 +86,17 @@ public ResponseEntity family( final String projectName, @Parameter(description = "The version group name.") @PathVariable("family") - @Pattern(regexp = Version.PATTERN) // + @Pattern(regexp = VersionEntity.PATTERN) // final String familyName ) { - final Project project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); - final VersionFamily family = this.families.findByProjectAndName(project._id(), familyName).orElseThrow(VersionNotFound::new); - final List versions = this.versions.findAllByProjectAndGroup(project._id(), family._id()); - return HTTP.cachedOk(VersionFamilyResponse.from(project, family, versions), CACHE); - } - - @Schema - private record VersionFamilyResponse( - @Schema(name = "project_id", pattern = "[a-z]+", example = "paper") - String project_id, - @Schema(name = "project_name", example = "Paper") - String project_name, - @Schema(name = "version_group", pattern = Version.PATTERN, example = "1.18") - String version_group, - @Schema(name = "versions") - List versions - ) { - static VersionFamilyResponse from(final Project project, final VersionFamily family, final List versions) { - return new VersionFamilyResponse( - project.name(), - project.friendlyName(), - family.name(), - versions.stream().sorted(Version.COMPARATOR).map(Version::name).toList() - ); - } + final ProjectEntity project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); + final VersionFamilyEntity family = this.families.findByProjectAndName(project, familyName).orElseThrow(VersionNotFound::new); + final List versions = this.versions.findAllByProjectAndGroup(project, family); + return HTTP.cachedOk(new VersionFamilyResponse( + project.name(), + project.friendlyName(), + family.name(), + versions.stream().sorted(VersionEntity.COMPARATOR).map(VersionEntity::name).toList() + ), CACHE); } } diff --git a/src/main/java/io/papermc/bibliothek/database/model/Build.java b/src/main/java/io/papermc/bibliothek/database/model/BuildEntity.java similarity index 55% rename from src/main/java/io/papermc/bibliothek/database/model/Build.java rename to src/main/java/io/papermc/bibliothek/database/model/BuildEntity.java index efaaacf..a47f34a 100644 --- a/src/main/java/io/papermc/bibliothek/database/model/Build.java +++ b/src/main/java/io/papermc/bibliothek/database/model/BuildEntity.java @@ -23,71 +23,102 @@ */ package io.papermc.bibliothek.database.model; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; +import io.papermc.bibliothek.api.model.Change; +import io.papermc.bibliothek.api.model.Channel; +import io.papermc.bibliothek.api.model.Download; import java.time.Instant; import java.util.List; import java.util.Map; import java.util.Objects; import org.bson.types.ObjectId; -import org.intellij.lang.annotations.Language; -import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.index.CompoundIndex; import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.DocumentReference; +import org.springframework.data.mongodb.core.mapping.Field; @CompoundIndex(def = "{'project': 1, 'version': 1}") @CompoundIndex(def = "{'project': 1, 'version': 1, 'number': 1}") @Document(collection = "builds") -public record Build( - @Id ObjectId _id, - ObjectId project, - ObjectId version, - int number, - Instant time, - List changes, - Map downloads, - @JsonProperty - @Nullable Channel channel, - @JsonInclude(JsonInclude.Include.NON_NULL) - @Nullable Boolean promoted -) { - public Channel channelOrDefault() { - return Objects.requireNonNullElse(this.channel(), Build.Channel.DEFAULT); +@NullMarked +public class BuildEntity { + @Field + @Id + private ObjectId _id; + + @DocumentReference + @Field + private ProjectEntity project; + + @DocumentReference + @Field + private VersionEntity version; + + @Field + private int number; + + @Field + private Instant time; + + @Field + private List changes; + + @Field + private Map downloads; + + @Field + private @Nullable Channel channel; + + @Field + private @Nullable Boolean promoted; + + public BuildEntity() { } - public boolean promotedOrDefault() { - return Objects.requireNonNullElse(this.promoted(), false); + @Id + public ObjectId _id() { + return this._id; } - public enum Channel { - @JsonProperty("default") - DEFAULT, - @JsonProperty("experimental") - EXPERIMENTAL; + public ProjectEntity project() { + return this.project; } - @Schema - public record Change( - @Schema(name = "commit") - String commit, - @Schema(name = "summary") - String summary, - @Schema(name = "message") - String message - ) { + public VersionEntity version() { + return this.version; } - @Schema - public record Download( - @Schema(name = "name", pattern = "[a-z0-9._-]+", example = "paper-1.18-10.jar") - String name, - @Schema(name = "sha256", pattern = "[a-f0-9]{64}", example = "f065e2d345d9d772d5cf2a1ce5c495c4cc56eb2fcd6820e82856485fa19414c8") - String sha256 - ) { - // NOTE: this pattern cannot contain any capturing groups - @Language("RegExp") - public static final String PATTERN = "[a-zA-Z0-9._-]+"; + public int number() { + return this.number; + } + + public Instant time() { + return this.time; + } + + public List changes() { + return this.changes; + } + + public Map downloads() { + return this.downloads; + } + + public @Nullable Channel channel() { + return this.channel; + } + + public Channel channelOrDefault() { + return Objects.requireNonNullElse(this.channel(), Channel.DEFAULT); + } + + public @Nullable Boolean promoted() { + return this.promoted; + } + + public boolean promotedOrDefault() { + return Objects.requireNonNullElse(this.promoted(), false); } } diff --git a/src/main/java/io/papermc/bibliothek/database/model/VersionFamily.java b/src/main/java/io/papermc/bibliothek/database/model/ProjectEntity.java similarity index 67% rename from src/main/java/io/papermc/bibliothek/database/model/VersionFamily.java rename to src/main/java/io/papermc/bibliothek/database/model/ProjectEntity.java index 2aadc71..f722ece 100644 --- a/src/main/java/io/papermc/bibliothek/database/model/VersionFamily.java +++ b/src/main/java/io/papermc/bibliothek/database/model/ProjectEntity.java @@ -23,24 +23,39 @@ */ package io.papermc.bibliothek.database.model; -import io.papermc.bibliothek.util.BringOrderToChaos; -import io.papermc.bibliothek.util.NameSource; -import io.papermc.bibliothek.util.TimeSource; -import java.time.Instant; -import java.util.Comparator; import org.bson.types.ObjectId; -import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NullMarked; import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.index.CompoundIndex; +import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; -@CompoundIndex(def = "{'project': 1, 'name': 1}") -@Document(collection = "version_groups") -public record VersionFamily( - @Id ObjectId _id, - ObjectId project, - String name, - @Nullable Instant time -) implements NameSource, TimeSource { - public static final Comparator COMPARATOR = BringOrderToChaos.timeOrNameComparator(); +@Document(collection = "projects") +@NullMarked +public class ProjectEntity { + @Field + @Id + private ObjectId _id; + + @Field + @Indexed + private String name; + + @Field + private String friendlyName; + + public ProjectEntity() { + } + + public ObjectId _id() { + return this._id; + } + + public String name() { + return this.name; + } + + public String friendlyName() { + return this.friendlyName; + } } diff --git a/src/main/java/io/papermc/bibliothek/database/model/Version.java b/src/main/java/io/papermc/bibliothek/database/model/VersionEntity.java similarity index 66% rename from src/main/java/io/papermc/bibliothek/database/model/Version.java rename to src/main/java/io/papermc/bibliothek/database/model/VersionEntity.java index 8c6a5bd..b40c1cf 100644 --- a/src/main/java/io/papermc/bibliothek/database/model/Version.java +++ b/src/main/java/io/papermc/bibliothek/database/model/VersionEntity.java @@ -30,23 +30,64 @@ import java.util.Comparator; import org.bson.types.ObjectId; import org.intellij.lang.annotations.Language; -import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.index.CompoundIndex; import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.DocumentReference; +import org.springframework.data.mongodb.core.mapping.Field; @CompoundIndex(def = "{'project': 1, 'group': 1}") @CompoundIndex(def = "{'project': 1, 'name': 1}") @Document(collection = "versions") -public record Version( - @Id ObjectId _id, - ObjectId project, - ObjectId group, - String name, - @Nullable Instant time -) implements NameSource, TimeSource { +@NullMarked +public class VersionEntity implements NameSource, TimeSource { // NOTE: this pattern cannot contain any capturing groups @Language("RegExp") public static final String PATTERN = "[0-9.]+-?(?:pre|SNAPSHOT)?(?:[0-9.]+)?"; - public static final Comparator COMPARATOR = BringOrderToChaos.timeOrNameComparator(); + public static final Comparator COMPARATOR = BringOrderToChaos.timeOrNameComparator(); + + @Field + @Id + private ObjectId _id; + + @DocumentReference + @Field + private ProjectEntity project; + + @DocumentReference + @Field + private VersionFamilyEntity group; + + @Field + private String name; + + @Field + private @Nullable Instant time; + + public VersionEntity() { + } + + public ObjectId _id() { + return this._id; + } + + public ProjectEntity project() { + return this.project; + } + + public VersionFamilyEntity group() { + return this.group; + } + + @Override + public String name() { + return this.name; + } + + @Override + public @Nullable Instant time() { + return this.time; + } } diff --git a/src/main/java/io/papermc/bibliothek/database/model/VersionFamilyEntity.java b/src/main/java/io/papermc/bibliothek/database/model/VersionFamilyEntity.java new file mode 100644 index 0000000..a6cc22b --- /dev/null +++ b/src/main/java/io/papermc/bibliothek/database/model/VersionFamilyEntity.java @@ -0,0 +1,80 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2024 PaperMC + * + * 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 io.papermc.bibliothek.database.model; + +import io.papermc.bibliothek.util.BringOrderToChaos; +import io.papermc.bibliothek.util.NameSource; +import io.papermc.bibliothek.util.TimeSource; +import java.time.Instant; +import java.util.Comparator; +import org.bson.types.ObjectId; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.index.CompoundIndex; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.DocumentReference; +import org.springframework.data.mongodb.core.mapping.Field; + +@CompoundIndex(def = "{'project': 1, 'name': 1}") +@Document(collection = "version_groups") +@NullMarked +public class VersionFamilyEntity implements NameSource, TimeSource { + public static final Comparator COMPARATOR = BringOrderToChaos.timeOrNameComparator(); + + @Field + @Id + private ObjectId _id; + + @DocumentReference + @Field + private ProjectEntity project; + + @Field + private String name; + + @Field + private @Nullable Instant time; + + public VersionFamilyEntity() { + } + + public ObjectId _id() { + return this._id; + } + + public ProjectEntity project() { + return this.project; + } + + @Override + public String name() { + return this.name; + } + + @Override + public @Nullable Instant time() { + return this.time; + } +} diff --git a/src/main/java/io/papermc/bibliothek/database/repository/BuildRepository.java b/src/main/java/io/papermc/bibliothek/database/repository/BuildRepository.java new file mode 100644 index 0000000..d7e7cc3 --- /dev/null +++ b/src/main/java/io/papermc/bibliothek/database/repository/BuildRepository.java @@ -0,0 +1,45 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2024 PaperMC + * + * 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 io.papermc.bibliothek.database.repository; + +import io.papermc.bibliothek.database.model.BuildEntity; +import io.papermc.bibliothek.database.model.ProjectEntity; +import io.papermc.bibliothek.database.model.VersionEntity; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import org.bson.types.ObjectId; +import org.jspecify.annotations.NullMarked; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +@NullMarked +@Repository +public interface BuildRepository extends MongoRepository { + List findAllByProjectAndVersion(final ProjectEntity project, final VersionEntity version); + + List findAllByProjectAndVersionIn(final ProjectEntity project, final Collection version); + + Optional findByProjectAndVersionAndNumber(final ProjectEntity project, final VersionEntity version, final int number); +} diff --git a/src/main/java/io/papermc/bibliothek/database/repository/ProjectCollection.java b/src/main/java/io/papermc/bibliothek/database/repository/ProjectRepository.java similarity index 84% rename from src/main/java/io/papermc/bibliothek/database/repository/ProjectCollection.java rename to src/main/java/io/papermc/bibliothek/database/repository/ProjectRepository.java index eb132d2..6849893 100644 --- a/src/main/java/io/papermc/bibliothek/database/repository/ProjectCollection.java +++ b/src/main/java/io/papermc/bibliothek/database/repository/ProjectRepository.java @@ -23,13 +23,15 @@ */ package io.papermc.bibliothek.database.repository; -import io.papermc.bibliothek.database.model.Project; +import io.papermc.bibliothek.database.model.ProjectEntity; import java.util.Optional; import org.bson.types.ObjectId; +import org.jspecify.annotations.NullMarked; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.stereotype.Repository; +@NullMarked @Repository -public interface ProjectCollection extends MongoRepository { - Optional findByName(final String name); +public interface ProjectRepository extends MongoRepository { + Optional findByName(final String name); } diff --git a/src/main/java/io/papermc/bibliothek/database/repository/VersionCollection.java b/src/main/java/io/papermc/bibliothek/database/repository/VersionFamilyRepository.java similarity index 76% rename from src/main/java/io/papermc/bibliothek/database/repository/VersionCollection.java rename to src/main/java/io/papermc/bibliothek/database/repository/VersionFamilyRepository.java index 6825fed..f1f6965 100644 --- a/src/main/java/io/papermc/bibliothek/database/repository/VersionCollection.java +++ b/src/main/java/io/papermc/bibliothek/database/repository/VersionFamilyRepository.java @@ -23,18 +23,19 @@ */ package io.papermc.bibliothek.database.repository; -import io.papermc.bibliothek.database.model.Version; +import io.papermc.bibliothek.database.model.ProjectEntity; +import io.papermc.bibliothek.database.model.VersionFamilyEntity; import java.util.List; import java.util.Optional; import org.bson.types.ObjectId; +import org.jspecify.annotations.NullMarked; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.stereotype.Repository; +@NullMarked @Repository -public interface VersionCollection extends MongoRepository { - List findAllByProject(final ObjectId project); +public interface VersionFamilyRepository extends MongoRepository { + List findAllByProject(final ProjectEntity project); - List findAllByProjectAndGroup(final ObjectId project, final ObjectId group); - - Optional findByProjectAndName(final ObjectId project, final String name); + Optional findByProjectAndName(final ProjectEntity project, final String name); } diff --git a/src/main/java/io/papermc/bibliothek/database/repository/BuildCollection.java b/src/main/java/io/papermc/bibliothek/database/repository/VersionRepository.java similarity index 70% rename from src/main/java/io/papermc/bibliothek/database/repository/BuildCollection.java rename to src/main/java/io/papermc/bibliothek/database/repository/VersionRepository.java index 9e83935..823186a 100644 --- a/src/main/java/io/papermc/bibliothek/database/repository/BuildCollection.java +++ b/src/main/java/io/papermc/bibliothek/database/repository/VersionRepository.java @@ -23,19 +23,22 @@ */ package io.papermc.bibliothek.database.repository; -import io.papermc.bibliothek.database.model.Build; -import java.util.Collection; +import io.papermc.bibliothek.database.model.ProjectEntity; +import io.papermc.bibliothek.database.model.VersionEntity; +import io.papermc.bibliothek.database.model.VersionFamilyEntity; import java.util.List; import java.util.Optional; import org.bson.types.ObjectId; +import org.jspecify.annotations.NullMarked; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.stereotype.Repository; +@NullMarked @Repository -public interface BuildCollection extends MongoRepository { - List findAllByProjectAndVersion(final ObjectId project, final ObjectId version); +public interface VersionRepository extends MongoRepository { + List findAllByProject(final ProjectEntity project); - List findAllByProjectAndVersionIn(final ObjectId project, final Collection version); + List findAllByProjectAndGroup(final ProjectEntity project, final VersionFamilyEntity group); - Optional findByProjectAndVersionAndNumber(final ObjectId project, final ObjectId version, final int number); + Optional findByProjectAndName(final ProjectEntity project, final String name); } diff --git a/src/main/java/io/papermc/bibliothek/exception/Advice.java b/src/main/java/io/papermc/bibliothek/exception/Advice.java index b44e970..9a6ff2a 100644 --- a/src/main/java/io/papermc/bibliothek/exception/Advice.java +++ b/src/main/java/io/papermc/bibliothek/exception/Advice.java @@ -24,6 +24,7 @@ package io.papermc.bibliothek.exception; import com.fasterxml.jackson.databind.ObjectMapper; +import org.jspecify.annotations.NullMarked; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -33,6 +34,7 @@ import org.springframework.web.servlet.NoHandlerFoundException; @ControllerAdvice +@NullMarked class Advice { private final ObjectMapper json; diff --git a/src/main/java/io/papermc/bibliothek/exception/BuildNotFound.java b/src/main/java/io/papermc/bibliothek/exception/BuildNotFound.java index 0d77783..639ad6e 100644 --- a/src/main/java/io/papermc/bibliothek/exception/BuildNotFound.java +++ b/src/main/java/io/papermc/bibliothek/exception/BuildNotFound.java @@ -24,7 +24,9 @@ package io.papermc.bibliothek.exception; import java.io.Serial; +import org.jspecify.annotations.NullMarked; +@NullMarked public class BuildNotFound extends RuntimeException { @Serial private static final long serialVersionUID = 4345323173317573160L; diff --git a/src/main/java/io/papermc/bibliothek/exception/DownloadFailed.java b/src/main/java/io/papermc/bibliothek/exception/DownloadFailed.java index 12c0a76..97d6933 100644 --- a/src/main/java/io/papermc/bibliothek/exception/DownloadFailed.java +++ b/src/main/java/io/papermc/bibliothek/exception/DownloadFailed.java @@ -24,7 +24,9 @@ package io.papermc.bibliothek.exception; import java.io.Serial; +import org.jspecify.annotations.NullMarked; +@NullMarked public class DownloadFailed extends RuntimeException { @Serial private static final long serialVersionUID = -1573093686056663600L; diff --git a/src/main/java/io/papermc/bibliothek/exception/DownloadNotFound.java b/src/main/java/io/papermc/bibliothek/exception/DownloadNotFound.java index 520c093..d0dba64 100644 --- a/src/main/java/io/papermc/bibliothek/exception/DownloadNotFound.java +++ b/src/main/java/io/papermc/bibliothek/exception/DownloadNotFound.java @@ -24,7 +24,9 @@ package io.papermc.bibliothek.exception; import java.io.Serial; +import org.jspecify.annotations.NullMarked; +@NullMarked public class DownloadNotFound extends RuntimeException { @Serial private static final long serialVersionUID = -1709491048606353671L; diff --git a/src/main/java/io/papermc/bibliothek/exception/ProjectNotFound.java b/src/main/java/io/papermc/bibliothek/exception/ProjectNotFound.java index 52640b5..0e83589 100644 --- a/src/main/java/io/papermc/bibliothek/exception/ProjectNotFound.java +++ b/src/main/java/io/papermc/bibliothek/exception/ProjectNotFound.java @@ -24,7 +24,9 @@ package io.papermc.bibliothek.exception; import java.io.Serial; +import org.jspecify.annotations.NullMarked; +@NullMarked public class ProjectNotFound extends RuntimeException { @Serial private static final long serialVersionUID = 210738408624095602L; diff --git a/src/main/java/io/papermc/bibliothek/exception/VersionNotFound.java b/src/main/java/io/papermc/bibliothek/exception/VersionNotFound.java index 431b014..33300e4 100644 --- a/src/main/java/io/papermc/bibliothek/exception/VersionNotFound.java +++ b/src/main/java/io/papermc/bibliothek/exception/VersionNotFound.java @@ -24,7 +24,9 @@ package io.papermc.bibliothek.exception; import java.io.Serial; +import org.jspecify.annotations.NullMarked; +@NullMarked public class VersionNotFound extends RuntimeException { @Serial private static final long serialVersionUID = 1716350953824887164L; diff --git a/src/main/java/io/papermc/bibliothek/filter/CorsFilter.java b/src/main/java/io/papermc/bibliothek/filter/CorsFilter.java index 179caa9..b86bed4 100644 --- a/src/main/java/io/papermc/bibliothek/filter/CorsFilter.java +++ b/src/main/java/io/papermc/bibliothek/filter/CorsFilter.java @@ -31,10 +31,11 @@ import jakarta.servlet.annotation.WebFilter; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; +import org.jspecify.annotations.NullMarked; +@NullMarked @WebFilter("/*") public class CorsFilter implements Filter { - @Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException { final HttpServletResponse httpServletResponse = (HttpServletResponse) response; diff --git a/src/main/java/io/papermc/bibliothek/util/BringOrderToChaos.java b/src/main/java/io/papermc/bibliothek/util/BringOrderToChaos.java index ca81fc6..494bef1 100644 --- a/src/main/java/io/papermc/bibliothek/util/BringOrderToChaos.java +++ b/src/main/java/io/papermc/bibliothek/util/BringOrderToChaos.java @@ -26,7 +26,9 @@ import java.time.Instant; import java.util.Comparator; import java.util.Objects; +import org.jspecify.annotations.NullMarked; +@NullMarked public final class BringOrderToChaos { private BringOrderToChaos() { } diff --git a/src/main/java/io/papermc/bibliothek/util/HTTP.java b/src/main/java/io/papermc/bibliothek/util/HTTP.java index 132e94b..2e560b3 100644 --- a/src/main/java/io/papermc/bibliothek/util/HTTP.java +++ b/src/main/java/io/papermc/bibliothek/util/HTTP.java @@ -26,10 +26,12 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.time.Duration; +import org.jspecify.annotations.NullMarked; import org.springframework.http.CacheControl; import org.springframework.http.ContentDisposition; import org.springframework.http.ResponseEntity; +@NullMarked public final class HTTP { private HTTP() { } diff --git a/src/main/java/io/papermc/bibliothek/util/MediaTypes.java b/src/main/java/io/papermc/bibliothek/util/MediaTypes.java index ef04049..b1c05f0 100644 --- a/src/main/java/io/papermc/bibliothek/util/MediaTypes.java +++ b/src/main/java/io/papermc/bibliothek/util/MediaTypes.java @@ -23,12 +23,13 @@ */ package io.papermc.bibliothek.util; -import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.springframework.http.MediaType; import org.springframework.http.MediaTypeFactory; +@NullMarked public final class MediaTypes { - public static final String APPLICATION_ZIP_VALUE = "application/zip"; public static final MediaType APPLICATION_ZIP = MediaType.parseMediaType(APPLICATION_ZIP_VALUE); diff --git a/src/main/java/io/papermc/bibliothek/util/NameSource.java b/src/main/java/io/papermc/bibliothek/util/NameSource.java index 3b7be6f..542ef81 100644 --- a/src/main/java/io/papermc/bibliothek/util/NameSource.java +++ b/src/main/java/io/papermc/bibliothek/util/NameSource.java @@ -23,6 +23,9 @@ */ package io.papermc.bibliothek.util; +import org.jspecify.annotations.NullMarked; + +@NullMarked public interface NameSource { String name(); } diff --git a/src/main/java/io/papermc/bibliothek/util/TimeSource.java b/src/main/java/io/papermc/bibliothek/util/TimeSource.java index e6c9fa5..f37cf55 100644 --- a/src/main/java/io/papermc/bibliothek/util/TimeSource.java +++ b/src/main/java/io/papermc/bibliothek/util/TimeSource.java @@ -24,8 +24,10 @@ package io.papermc.bibliothek.util; import java.time.Instant; -import org.jetbrains.annotations.UnknownNullability; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +@NullMarked public interface TimeSource { - @UnknownNullability Instant time(); + @Nullable Instant time(); } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 77a5739..bdc253a 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,10 +1,12 @@ +server: + error: + whitelabel: + enabled: false spring: data: mongodb: # "library" instead of "bibliothek" is intentional here database: "library" - mvc: - throw-exception-if-no-handler-found: true web: resources: add-mappings: false @@ -17,7 +19,3 @@ springdoc: operations-sorter: "alpha" path: "/docs/" show-common-extensions: true -server: - error: - whitelabel: - enabled: false diff --git a/src/test/java/io/papermc/bibliothek/util/MediaTypesTest.java b/src/test/java/io/papermc/bibliothek/util/MediaTypesTest.java new file mode 100644 index 0000000..1eb6494 --- /dev/null +++ b/src/test/java/io/papermc/bibliothek/util/MediaTypesTest.java @@ -0,0 +1,38 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2024 PaperMC + * + * 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 io.papermc.bibliothek.util; + +import org.jspecify.annotations.NullMarked; +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@NullMarked +class MediaTypesTest { + @Test + void testFromFileExtension() { + assertEquals(MediaType.APPLICATION_JSON, MediaTypes.fromFileExtension("json")); + } +}