Add versions/latestrelease and /latest api endpoints

This commit is contained in:
Nassim Jahnke 2023-04-15 11:56:10 +02:00
parent 0f39404c6a
commit 002522d5d6
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
4 changed files with 85 additions and 6 deletions

View File

@ -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<String, VersionStats> 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);

View File

@ -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",

View File

@ -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<Long, Version> getVersion(long versionId, @Define boolean canSeeHidden, @Define Long userId);
@Nullable Map.Entry<Long, Version> 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<Long, Version> getVersionWithVersionString(String author, String slug, String versionString, @Define boolean canSeeHidden, @Define Long userId);
@Nullable Map.Entry<Long, Version> 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<String, VersionStats> 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
<if(!canSeeHidden)>
(pv.visibility = 0
<if(userId)>
OR (<userId> IN (SELECT pm.user_id FROM project_members_all pm WHERE pm.id = p.id) AND pv.visibility != 4)
<endif>)
AND
<endif>
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);
}

View File

@ -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;
}
}