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 b1cbb9d..f1cc9d4 100644 --- a/src/main/java/io/papermc/bibliothek/controller/v2/VersionController.java +++ b/src/main/java/io/papermc/bibliothek/controller/v2/VersionController.java @@ -29,6 +29,7 @@ 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.exception.ChannelNotFound; 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.apache.commons.lang3.EnumUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.CacheControl; import org.springframework.http.MediaType; @@ -47,6 +49,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @@ -85,12 +88,19 @@ public ResponseEntity version( @Parameter(description = "A version of the project.") @PathVariable("version") @Pattern(regexp = Version.PATTERN) // - final String versionName + final String versionName, + @Parameter(description = "The channel to filter builds.") + @RequestParam(required = false, defaultValue = "all") // + final String channel ) { 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); + final boolean hasChannel = !channel.isBlank() && !channel.equalsIgnoreCase("all"); + if (hasChannel && !EnumUtils.isValidEnumIgnoreCase(Build.Channel.class, channel)) { + throw new ChannelNotFound(channel); + } + final List builds = (!hasChannel) ? this.builds.findAllByProjectAndVersion(project._id(), version._id()) : this.builds.findAllByProjectAndVersionAndChannel(project._id(), version._id(), channel.toUpperCase()); + return HTTP.cachedOk(VersionResponse.from(project, version, channel, builds), CACHE); } @Schema @@ -100,14 +110,17 @@ private record VersionResponse( @Schema(name = "project_name", example = "Paper") String project_name, @Schema(name = "version", pattern = Version.PATTERN, example = "1.18") + String channel, + @Schema(name = "channel", examples = {"stable", "all"}) String version, @Schema(name = "builds") List builds ) { - static VersionResponse from(final Project project, final Version version, final List builds) { + static VersionResponse from(final Project project, final Version version, final String channel, final List builds) { return new VersionResponse( project.name(), project.friendlyName(), + channel, version.name(), builds.stream().map(Build::number).toList() ); diff --git a/src/main/java/io/papermc/bibliothek/database/repository/BuildCollection.java b/src/main/java/io/papermc/bibliothek/database/repository/BuildCollection.java index 0b82be1..04ccf2d 100644 --- a/src/main/java/io/papermc/bibliothek/database/repository/BuildCollection.java +++ b/src/main/java/io/papermc/bibliothek/database/repository/BuildCollection.java @@ -35,6 +35,8 @@ public interface BuildCollection extends MongoRepository { List findAllByProjectAndVersion(final ObjectId project, final ObjectId version); + List findAllByProjectAndVersionAndChannel(final ObjectId project, final ObjectId version, final String channel); + List findAllByProjectAndVersionIn(final ObjectId project, final Collection version); Optional findByProjectAndVersionAndNumber(final ObjectId project, final ObjectId version, final int number); diff --git a/src/main/java/io/papermc/bibliothek/exception/Advice.java b/src/main/java/io/papermc/bibliothek/exception/Advice.java index fbf34e8..54228c6 100644 --- a/src/main/java/io/papermc/bibliothek/exception/Advice.java +++ b/src/main/java/io/papermc/bibliothek/exception/Advice.java @@ -24,6 +24,9 @@ package io.papermc.bibliothek.exception; import com.fasterxml.jackson.databind.ObjectMapper; +import io.papermc.bibliothek.database.model.Build; +import java.util.stream.Collectors; +import org.apache.commons.lang3.EnumUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -72,6 +75,12 @@ public ResponseEntity versionNotFound(final VersionNotFound exception) { return this.error(HttpStatus.NOT_FOUND, "Version not found."); } + @ExceptionHandler(ChannelNotFound.class) + @ResponseBody + public ResponseEntity channelNotFound(final ChannelNotFound exception) { + return this.error(HttpStatus.NOT_FOUND, "Channel '%s' not found. Only allowed [all,%s]".formatted(exception.getValue(), EnumUtils.getEnumList(Build.Channel.class).stream().map(channel -> channel.toString().toLowerCase()).collect(Collectors.joining(",")))); + } + @ExceptionHandler(NoHandlerFoundException.class) @ResponseBody public ResponseEntity endpointNotFound(final NoHandlerFoundException exception) { diff --git a/src/main/java/io/papermc/bibliothek/exception/ChannelNotFound.java b/src/main/java/io/papermc/bibliothek/exception/ChannelNotFound.java new file mode 100644 index 0000000..368243c --- /dev/null +++ b/src/main/java/io/papermc/bibliothek/exception/ChannelNotFound.java @@ -0,0 +1,43 @@ +/* + * This file is part of bibliothek, licensed under the MIT License. + * + * Copyright (c) 2019-2023 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.exception; + +import java.io.Serial; + +public class ChannelNotFound extends RuntimeException { + @Serial + private static final long serialVersionUID = 1716350953824887164L; + + private String value; + + public ChannelNotFound(final String value) { + super(); + this.value = value; + } + + @SuppressWarnings("checkstyle:MethodName") + public String getValue() { + return this.value; + } +}