From 002522d5d6c25707aaa903c887d401b3753853ec Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Sat, 15 Apr 2023 11:56:10 +0200 Subject: [PATCH] Add versions/latestrelease and /latest api endpoints --- .../controller/api/v1/VersionsController.java | 17 +++++++--- .../v1/interfaces/IVersionsController.java | 33 +++++++++++++++++++ .../hangar/db/dao/v1/VersionsApiDAO.java | 27 +++++++++++++-- .../service/api/VersionsApiService.java | 14 ++++++++ 4 files changed, 85 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/io/papermc/hangar/controller/api/v1/VersionsController.java b/backend/src/main/java/io/papermc/hangar/controller/api/v1/VersionsController.java index a22f4bab..7b19c9b3 100644 --- a/backend/src/main/java/io/papermc/hangar/controller/api/v1/VersionsController.java +++ b/backend/src/main/java/io/papermc/hangar/controller/api/v1/VersionsController.java @@ -22,7 +22,6 @@ import io.papermc.hangar.security.annotations.ratelimit.RateLimit; import io.papermc.hangar.security.annotations.unlocked.Unlocked; import io.papermc.hangar.security.annotations.visibility.VisibilityRequired; import io.papermc.hangar.service.api.VersionsApiService; -import io.papermc.hangar.service.internal.file.FileService; import io.papermc.hangar.service.internal.versions.DownloadService; import jakarta.servlet.http.HttpServletResponse; import java.time.OffsetDateTime; @@ -45,13 +44,11 @@ public class VersionsController implements IVersionsController { private final DownloadService downloadService; private final VersionsApiService versionsApiService; - private final FileService fileService; @Autowired - public VersionsController(final DownloadService downloadService, final VersionsApiService versionsApiService, final FileService fileService) { + public VersionsController(final DownloadService downloadService, final VersionsApiService versionsApiService) { this.downloadService = downloadService; this.versionsApiService = versionsApiService; - this.fileService = fileService; } @Unlocked @@ -75,6 +72,18 @@ public class VersionsController implements IVersionsController { return this.versionsApiService.getVersions(author, slug, pagination); } + @Override + @VisibilityRequired(type = VisibilityRequired.Type.PROJECT, args = "{#author, #slug}") + public String getLatestReleaseVersion(final String author, final String slug) { + return this.versionsApiService.latestVersion(author, slug); + } + + @Override + @VisibilityRequired(type = VisibilityRequired.Type.PROJECT, args = "{#author, #slug}") + public String getLatestVersion(final String author, final String slug, final @NotNull String channel) { + return this.versionsApiService.latestVersion(author, slug, channel); + } + @Override public Map getVersionStats(final String author, final String slug, final String versionString, final @NotNull OffsetDateTime fromDate, final @NotNull OffsetDateTime toDate) { return this.versionsApiService.getVersionStats(author, slug, versionString, fromDate, toDate); diff --git a/backend/src/main/java/io/papermc/hangar/controller/api/v1/interfaces/IVersionsController.java b/backend/src/main/java/io/papermc/hangar/controller/api/v1/interfaces/IVersionsController.java index 3347e5ad..4e49034e 100644 --- a/backend/src/main/java/io/papermc/hangar/controller/api/v1/interfaces/IVersionsController.java +++ b/backend/src/main/java/io/papermc/hangar/controller/api/v1/interfaces/IVersionsController.java @@ -86,6 +86,39 @@ public interface IVersionsController { @Parameter(description = "The slug of the project to return versions for") @PathVariable String slug, @Parameter(description = "Pagination information") @NotNull RequestPagination pagination); + @Operation( + summary = "Returns the latest release version of a project", + operationId = "latestReleaseVersion", + description = "Returns the latest version of a project. Requires the `view_public_info` permission in the project or owning organizations.", + security = @SecurityRequirement(name = "HangarAuth", scopes = "view_public_info"), + tags = "Versions" + ) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "Ok"), + @ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"), + @ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint") + }) + @GetMapping("/projects/{author}/{slug}/latestrelease") + String getLatestReleaseVersion(@Parameter(description = "The author of the project to return the latest version for") @PathVariable String author, + @Parameter(description = "The slug of the project to return the latest version for") @PathVariable String slug); + + @Operation( + summary = "Returns the latest version of a project for a specific channel", + operationId = "latestVersion", + description = "Returns the latest version of a project. Requires the `view_public_info` permission in the project or owning organization.", + security = @SecurityRequirement(name = "HangarAuth", scopes = "view_public_info"), + tags = "Versions" + ) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "Ok"), + @ApiResponse(responseCode = "401", description = "Api session missing, invalid or expired"), + @ApiResponse(responseCode = "403", description = "Not enough permissions to use this endpoint") + }) + @GetMapping("/projects/{author}/{slug}/latest") + String getLatestVersion(@Parameter(description = "The author of the project to return the latest version for") @PathVariable String author, + @Parameter(description = "The slug of the project to return the latest version for") @PathVariable String slug, + @Parameter(description = "The channel to return the latest version for", required = true) @NotNull String channel); + @Operation( summary = "Returns the stats for a version", operationId = "showVersionStats", diff --git a/backend/src/main/java/io/papermc/hangar/db/dao/v1/VersionsApiDAO.java b/backend/src/main/java/io/papermc/hangar/db/dao/v1/VersionsApiDAO.java index e30603a7..c045d0e4 100644 --- a/backend/src/main/java/io/papermc/hangar/db/dao/v1/VersionsApiDAO.java +++ b/backend/src/main/java/io/papermc/hangar/db/dao/v1/VersionsApiDAO.java @@ -22,6 +22,7 @@ import org.jdbi.v3.sqlobject.config.ValueColumn; import org.jdbi.v3.sqlobject.customizer.Define; import org.jdbi.v3.sqlobject.statement.SqlQuery; import org.jdbi.v3.stringtemplate4.UseStringTemplateEngine; +import org.jetbrains.annotations.Nullable; import org.springframework.stereotype.Repository; @Repository @@ -66,7 +67,7 @@ public interface VersionsApiDAO { pv.id = :versionId ORDERED BY pv.created_at DESC """) - Map.Entry getVersion(long versionId, @Define boolean canSeeHidden, @Define Long userId); + @Nullable Map.Entry getVersion(long versionId, @Define boolean canSeeHidden, @Define Long userId); @KeyColumn("id") @RegisterColumnMapper(VersionStatsMapper.class) @@ -105,7 +106,7 @@ public interface VersionsApiDAO { lower(p.slug) = lower(:slug) AND pv.version_string = :versionString """) - Map.Entry getVersionWithVersionString(String author, String slug, String versionString, @Define boolean canSeeHidden, @Define Long userId); + @Nullable Map.Entry getVersionWithVersionString(String author, String slug, String versionString, @Define boolean canSeeHidden, @Define Long userId); @KeyColumn("id") @RegisterColumnMapper(VersionStatsMapper.class) @@ -213,4 +214,26 @@ public interface VersionsApiDAO { GROUP BY date; """) Map getVersionStats(String author, String slug, String versionString, OffsetDateTime fromDate, OffsetDateTime toDate); + + @SqlQuery(""" + SELECT pv.version_string + FROM project_versions pv + JOIN project_channels pc ON pv.channel_id = pc.id + JOIN projects p ON pv.project_id = p.id + LEFT JOIN users u ON pv.author_id = u.id + WHERE + + (pv.visibility = 0 + + OR ( IN (SELECT pm.user_id FROM project_members_all pm WHERE pm.id = p.id) AND pv.visibility != 4) + ) + AND + + lower(p.owner_name) = lower(:author) AND + lower(p.slug) = lower(:slug) AND + pc.name = :channel + ORDER BY pv.created_at DESC + LIMIT 1 + """) + @Nullable String getLatestVersion(String author, String slug, String channel, @Define boolean canSeeHidden, @Define Long userId); } diff --git a/backend/src/main/java/io/papermc/hangar/service/api/VersionsApiService.java b/backend/src/main/java/io/papermc/hangar/service/api/VersionsApiService.java index 6ab4b383..20df9a54 100644 --- a/backend/src/main/java/io/papermc/hangar/service/api/VersionsApiService.java +++ b/backend/src/main/java/io/papermc/hangar/service/api/VersionsApiService.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; +import org.jetbrains.annotations.Nullable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @@ -173,4 +174,17 @@ public class VersionsApiService extends HangarComponent { } return this.versionsApiDAO.getVersionStats(author, slug, versionString, fromDate, toDate); } + + public @Nullable String latestVersion(final String author, final String slug) { + return this.latestVersion(author, slug, this.config.channels.nameDefault()); + } + + public @Nullable String latestVersion(final String author, final String slug, final String channel) { + final boolean canSeeHidden = this.getGlobalPermissions().has(Permission.SeeHidden); + final String version = this.versionsApiDAO.getLatestVersion(author, slug, channel, canSeeHidden, this.getHangarUserId()); + if (version == null) { + throw new HangarApiException(HttpStatus.NOT_FOUND, "No version found for " + author + "/" + slug + " on channel " + channel); + } + return version; + } }