mirror of
https://github.com/HangarMC/Hangar.git
synced 2025-03-07 15:31:00 +08:00
project version list api
This commit is contained in:
parent
34ad0d6974
commit
33819f7d1b
@ -8,6 +8,8 @@ import me.minidigger.hangar.db.dao.HangarDao;
|
||||
import me.minidigger.hangar.db.mappers.RoleMapper;
|
||||
import org.jdbi.v3.core.Jdbi;
|
||||
import org.jdbi.v3.core.spi.JdbiPlugin;
|
||||
import org.jdbi.v3.core.statement.SqlLogger;
|
||||
import org.jdbi.v3.core.statement.StatementContext;
|
||||
import org.jdbi.v3.postgres.PostgresPlugin;
|
||||
import org.jdbi.v3.postgres.PostgresTypes;
|
||||
import org.jdbi.v3.sqlobject.SqlObjectPlugin;
|
||||
@ -21,6 +23,7 @@ import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@Configuration
|
||||
public class JDBIConfig {
|
||||
@ -37,8 +40,15 @@ public class JDBIConfig {
|
||||
|
||||
@Bean
|
||||
public Jdbi jdbi(DataSource dataSource, List<JdbiPlugin> jdbiPlugins) {
|
||||
SqlLogger myLogger = new SqlLogger() {
|
||||
@Override
|
||||
public void logAfterExecution(StatementContext context) {
|
||||
Logger.getLogger("sql").info("sql: " + context.getRenderedSql());
|
||||
}
|
||||
};
|
||||
TransactionAwareDataSourceProxy dataSourceProxy = new TransactionAwareDataSourceProxy(dataSource);
|
||||
Jdbi jdbi = Jdbi.create(dataSourceProxy);
|
||||
// jdbi.setSqlLogger(myLogger); // TODO for debugging sql statements
|
||||
jdbiPlugins.forEach(jdbi::installPlugin);
|
||||
PostgresTypes config = jdbi.getConfig(PostgresTypes.class);
|
||||
jdbi.registerRowMapper(new RoleMapper());
|
||||
|
@ -65,7 +65,7 @@ public interface VersionsApi {
|
||||
@ApiResponse(code = 200, message = "Ok", response = Version.class),
|
||||
@ApiResponse(code = 401, message = "Api session missing, invalid or expired"),
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")})
|
||||
@GetMapping(value = "/projects/{pluginId}/versions/{name}",
|
||||
@GetMapping(value = "/projects/{pluginId}/versions/{name:.*}",
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
ResponseEntity<Version> showVersion(@ApiParam(value = "The plugin id of the project to return the version for", required = true) @PathVariable("pluginId") String pluginId
|
||||
, @ApiParam(value = "The name of the version to return", required = true) @PathVariable("name") String name
|
||||
|
@ -1,9 +1,13 @@
|
||||
package me.minidigger.hangar.controller.api;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import me.minidigger.hangar.db.dao.HangarDao;
|
||||
import me.minidigger.hangar.db.dao.api.ApiVersionsDao;
|
||||
import me.minidigger.hangar.model.generated.DeployVersionInfo;
|
||||
import me.minidigger.hangar.model.generated.PaginatedVersionResult;
|
||||
import me.minidigger.hangar.model.generated.Pagination;
|
||||
import me.minidigger.hangar.model.generated.Version;
|
||||
import me.minidigger.hangar.model.generated.VersionStatsDay;
|
||||
import me.minidigger.hangar.service.UserService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -12,18 +16,13 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import me.minidigger.hangar.model.generated.DeployVersionInfo;
|
||||
import me.minidigger.hangar.model.generated.PaginatedVersionResult;
|
||||
import me.minidigger.hangar.model.generated.Version;
|
||||
import me.minidigger.hangar.model.generated.VersionStatsDay;
|
||||
|
||||
@Controller
|
||||
public class VersionsApiController implements VersionsApi {
|
||||
@ -59,23 +58,21 @@ public class VersionsApiController implements VersionsApi {
|
||||
|
||||
@Override
|
||||
public ResponseEntity<PaginatedVersionResult> listVersions(String pluginId, List<String> tags, Long limit, Long offset) {
|
||||
apiVersionsDao.get().listVersions(pluginId, tags, false, limit, offset, null); // TODO finish api
|
||||
try {
|
||||
return new ResponseEntity<>(objectMapper.readValue("{\n \"result\" : [ {\n \"visibility\" : \"public\",\n \"stats\" : {\n \"downloads\" : 0\n },\n \"author\" : \"author\",\n \"file_info\" : {\n \"size_bytes\" : 6,\n \"md_5_hash\" : \"md_5_hash\",\n \"name\" : \"name\"\n },\n \"name\" : \"name\",\n \"created_at\" : \"2000-01-23T04:56:07.000+00:00\",\n \"description\" : \"description\",\n \"dependencies\" : [ {\n \"plugin_id\" : \"plugin_id\",\n \"version\" : \"version\"\n }, {\n \"plugin_id\" : \"plugin_id\",\n \"version\" : \"version\"\n } ],\n \"review_state\" : \"unreviewed\",\n \"tags\" : [ {\n \"data\" : \"data\",\n \"color\" : {\n \"background\" : \"background\",\n \"foreground\" : \"foreground\"\n },\n \"name\" : \"name\"\n }, {\n \"data\" : \"data\",\n \"color\" : {\n \"background\" : \"background\",\n \"foreground\" : \"foreground\"\n },\n \"name\" : \"name\"\n } ]\n }, {\n \"visibility\" : \"public\",\n \"stats\" : {\n \"downloads\" : 0\n },\n \"author\" : \"author\",\n \"file_info\" : {\n \"size_bytes\" : 6,\n \"md_5_hash\" : \"md_5_hash\",\n \"name\" : \"name\"\n },\n \"name\" : \"name\",\n \"created_at\" : \"2000-01-23T04:56:07.000+00:00\",\n \"description\" : \"description\",\n \"dependencies\" : [ {\n \"plugin_id\" : \"plugin_id\",\n \"version\" : \"version\"\n }, {\n \"plugin_id\" : \"plugin_id\",\n \"version\" : \"version\"\n } ],\n \"review_state\" : \"unreviewed\",\n \"tags\" : [ {\n \"data\" : \"data\",\n \"color\" : {\n \"background\" : \"background\",\n \"foreground\" : \"foreground\"\n },\n \"name\" : \"name\"\n }, {\n \"data\" : \"data\",\n \"color\" : {\n \"background\" : \"background\",\n \"foreground\" : \"foreground\"\n },\n \"name\" : \"name\"\n } ]\n } ],\n \"pagination\" : {\n \"offset\" : 6,\n \"limit\" : 0,\n \"count\" : 1\n }\n}", PaginatedVersionResult.class), HttpStatus.OK); // TODO Implement me
|
||||
} catch (IOException e) {
|
||||
log.error("Couldn't serialize response for content type application/json", e);
|
||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
// TODO handling users
|
||||
List<Version> versions = apiVersionsDao.get().listVersions(pluginId, null, tags, false, limit, offset, null);
|
||||
int count = apiVersionsDao.get().listVersions(pluginId, null,tags, false, limit, offset, null).size();
|
||||
return new ResponseEntity<>(new PaginatedVersionResult().result(versions).pagination(new Pagination().limit(limit).offset(offset).count((long) count)), HttpStatus.OK);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Version> showVersion(String pluginId, String name) {
|
||||
try {
|
||||
return new ResponseEntity<>(objectMapper.readValue("{\n \"visibility\" : \"public\",\n \"stats\" : {\n \"downloads\" : 0\n },\n \"author\" : \"author\",\n \"file_info\" : {\n \"size_bytes\" : 6,\n \"md_5_hash\" : \"md_5_hash\",\n \"name\" : \"name\"\n },\n \"name\" : \"name\",\n \"created_at\" : \"2000-01-23T04:56:07.000+00:00\",\n \"description\" : \"description\",\n \"dependencies\" : [ {\n \"plugin_id\" : \"plugin_id\",\n \"version\" : \"version\"\n }, {\n \"plugin_id\" : \"plugin_id\",\n \"version\" : \"version\"\n } ],\n \"review_state\" : \"unreviewed\",\n \"tags\" : [ {\n \"data\" : \"data\",\n \"color\" : {\n \"background\" : \"background\",\n \"foreground\" : \"foreground\"\n },\n \"name\" : \"name\"\n }, {\n \"data\" : \"data\",\n \"color\" : {\n \"background\" : \"background\",\n \"foreground\" : \"foreground\"\n },\n \"name\" : \"name\"\n } ]\n}", Version.class), HttpStatus.OK); // TODO Implement me
|
||||
} catch (IOException e) {
|
||||
log.error("Couldn't serialize response for content type application/json", e);
|
||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
// TODO handling users
|
||||
Version version = apiVersionsDao.get().listVersions(pluginId, name, null, false, 1L, 0, null).stream().findFirst().orElse(null);
|
||||
if (version == null) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND);
|
||||
} else {
|
||||
return new ResponseEntity<>(version, HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
package me.minidigger.hangar.db.dao.api;
|
||||
|
||||
import me.minidigger.hangar.db.dao.api.mappers.VersionMapper;
|
||||
import me.minidigger.hangar.model.generated.Version;
|
||||
import org.jdbi.v3.sqlobject.config.RegisterBeanMapper;
|
||||
import org.jdbi.v3.sqlobject.config.RegisterRowMapper;
|
||||
import org.jdbi.v3.sqlobject.customizer.BindList;
|
||||
import org.jdbi.v3.sqlobject.customizer.BindList.EmptyHandling;
|
||||
import org.jdbi.v3.sqlobject.customizer.Define;
|
||||
@ -16,20 +18,21 @@ import java.util.List;
|
||||
public interface ApiVersionsDao {
|
||||
|
||||
@UseStringTemplateEngine
|
||||
@RegisterRowMapper(VersionMapper.class)
|
||||
@SqlQuery("SELECT pv.created_at," +
|
||||
"pv.version_string," +
|
||||
"pv.dependencies," +
|
||||
"pv.visibility," +
|
||||
"pv.description," +
|
||||
"coalesce((SELECT sum(pvd.downloads) FROM project_versions_downloads pvd WHERE p.id = pvd.project_id AND pv.id = pvd.version_id), 0) stat_downloads," +
|
||||
"coalesce((SELECT sum(pvd.downloads) FROM project_versions_downloads pvd WHERE p.id = pvd.project_id AND pv.id = pvd.version_id), 0) downloads," +
|
||||
"pv.file_size fi_size_bytes," +
|
||||
"pv.hash fi_md5_hash," +
|
||||
"pv.file_name fi_name," +
|
||||
"u.name author," +
|
||||
"pv.review_state," +
|
||||
"array_append(array_agg(pvt.name ORDER BY (pvt.name)) FILTER ( WHERE pvt.name IS NOT NULL ), 'Channel') AS tag_names," +
|
||||
"array_append(array_agg(pvt.data ORDER BY (pvt.name)) FILTER ( WHERE pvt.name IS NOT NULL ), pc.name) AS tag_datas," +
|
||||
"array_append(array_agg(pvt.color ORDER BY (pvt.name)) FILTER ( WHERE pvt.name IS NOT NULL ), pc.color + 9) AS tag_colors " +
|
||||
"array_append(array_agg(pvt.name ORDER BY (pvt.name)) FILTER ( WHERE pvt.name IS NOT NULL ), 'Channel') AS tag_name," +
|
||||
"array_append(array_agg(pvt.data ORDER BY (pvt.name)) FILTER ( WHERE pvt.name IS NOT NULL ), pc.name) AS tag_data," +
|
||||
"array_append(array_agg(pvt.color ORDER BY (pvt.name)) FILTER ( WHERE pvt.name IS NOT NULL ), pc.color + 9) AS tag_color " +
|
||||
"FROM projects p" +
|
||||
" JOIN project_versions pv ON p.id = pv.project_id" +
|
||||
" LEFT JOIN users u ON pv.author_id = u.id" +
|
||||
@ -37,14 +40,15 @@ public interface ApiVersionsDao {
|
||||
" LEFT JOIN project_channels pc ON pv.channel_id = pc.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> " +
|
||||
"p.plugin_id = :pluginId AND " +
|
||||
"p.plugin_id = :pluginId <if(versionName)> AND " +
|
||||
" pv.version_string = '<versionName>' <endif> <if(tags)> AND " +
|
||||
"(" +
|
||||
" pvt.name || ':' || pvt.data IN (<tags>) OR " +
|
||||
" pvt.name IN (<tags>) OR " +
|
||||
" 'Channel:' || pc.name IN (<tags>) OR " +
|
||||
" 'Channel' IN (<tags>)" +
|
||||
" ) " +
|
||||
" )<endif> " +
|
||||
"GROUP BY p.id, pv.id, u.id, pc.id " +
|
||||
"ORDER BY pv.created_at DESC LIMIT :limit OFFSET :offset")
|
||||
List<Version> listVersions(String pluginId, @BindList(value = "tags", onEmpty = EmptyHandling.NULL_STRING) List<String> tags, @Define boolean canSeeHidden, Long limit, long offset, @Define Long userId);
|
||||
List<Version> listVersions(String pluginId, @Define String versionName, @BindList(value = "tags", onEmpty = EmptyHandling.NULL_VALUE) List<String> tags, @Define boolean canSeeHidden, Long limit, long offset, @Define Long userId);
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
package me.minidigger.hangar.db.dao.api.mappers;
|
||||
|
||||
import me.minidigger.hangar.model.Visibility;
|
||||
import me.minidigger.hangar.model.generated.Dependency;
|
||||
import me.minidigger.hangar.model.generated.FileInfo;
|
||||
import me.minidigger.hangar.model.generated.ReviewState;
|
||||
import me.minidigger.hangar.model.generated.Version;
|
||||
import me.minidigger.hangar.model.generated.VersionStatsAll;
|
||||
import org.jdbi.v3.core.mapper.ColumnMapper;
|
||||
import org.jdbi.v3.core.mapper.RowMapper;
|
||||
import org.jdbi.v3.core.statement.StatementContext;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
public class VersionMapper implements RowMapper<Version> {
|
||||
@Override
|
||||
public Version map(ResultSet rs, StatementContext ctx) throws SQLException {
|
||||
Optional<ColumnMapper<String[]>> mapper = ctx.findColumnMapperFor(String[].class);
|
||||
if (mapper.isEmpty()) throw new UnsupportedOperationException("couldn't find a mapper for String[]");
|
||||
return new Version()
|
||||
.createdAt(rs.getObject("created_at", OffsetDateTime.class))
|
||||
.name(rs.getString("version_string"))
|
||||
.dependencies(Dependency.from(Arrays.asList(mapper.get().map(rs, "dependencies", ctx))))
|
||||
.visibility(Visibility.fromId(rs.getLong("visibility")))
|
||||
.description(rs.getString("description"))
|
||||
.stats(new VersionStatsAll().downloads(rs.getLong("downloads")))
|
||||
.fileInfo(new FileInfo().name(rs.getString("fi_name")).md5Hash(rs.getString("fi_md5_hash")).sizeBytes(rs.getLong("fi_size_bytes")))
|
||||
.author(rs.getString("author"))
|
||||
.reviewState(ReviewState.values()[rs.getInt("review_state")]);
|
||||
|
||||
}
|
||||
}
|
@ -32,7 +32,6 @@ public class Version {
|
||||
private List<Dependency> dependencies = new ArrayList<>();
|
||||
|
||||
@JsonProperty("visibility")
|
||||
@EnumByOrdinal
|
||||
private Visibility visibility = null;
|
||||
|
||||
@JsonProperty("description")
|
||||
@ -50,7 +49,6 @@ public class Version {
|
||||
private String author = null;
|
||||
|
||||
@JsonProperty("review_state")
|
||||
@EnumByOrdinal
|
||||
private ReviewState reviewState = null;
|
||||
|
||||
@JsonProperty("tags")
|
||||
@ -140,10 +138,12 @@ public class Version {
|
||||
@ApiModelProperty(required = true, value = "")
|
||||
@NotNull
|
||||
|
||||
@EnumByOrdinal
|
||||
public Visibility getVisibility() {
|
||||
return visibility;
|
||||
}
|
||||
|
||||
@EnumByOrdinal
|
||||
public void setVisibility(Visibility visibility) {
|
||||
this.visibility = visibility;
|
||||
}
|
||||
@ -245,10 +245,12 @@ public class Version {
|
||||
@ApiModelProperty(required = true, value = "")
|
||||
@NotNull
|
||||
|
||||
@EnumByOrdinal
|
||||
public ReviewState getReviewState() {
|
||||
return reviewState;
|
||||
}
|
||||
|
||||
@EnumByOrdinal
|
||||
public void setReviewState(ReviewState reviewState) {
|
||||
this.reviewState = reviewState;
|
||||
}
|
||||
@ -271,10 +273,12 @@ public class Version {
|
||||
@ApiModelProperty(required = true, value = "")
|
||||
@NotNull
|
||||
@Valid
|
||||
@Nested("tag")
|
||||
public List<Tag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
@Nested("tag")
|
||||
public void setTags(List<Tag> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
@ -97,7 +97,7 @@
|
||||
</#if>
|
||||
</td>
|
||||
<td style="vertical-align: middle; text-align: right; padding-right: 15px;">
|
||||
<a href="/${utils.urlEncode(entry.namespace)}/versions/entry.versionString/reviews"><i class="fas fa-2x fa-fw fa-info"></i></a>
|
||||
<a href="/${utils.urlEncode(entry.namespace.toString())}/versions/entry.versionString/reviews"><i class="fas fa-2x fa-fw fa-info"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
</#list>
|
||||
@ -138,7 +138,7 @@
|
||||
</tr>
|
||||
</#if>
|
||||
<#list versions?sort_by("versionCreatedAt") as entry>
|
||||
<tr data-version="${utils.urlEncode(entry.namespace)}/versions/entry.versionString">
|
||||
<tr data-version="${utils.urlEncode(entry.namespace.toString())}/versions/entry.versionString">
|
||||
<td>
|
||||
<#import "*/utils/userAvatar.ftlh" as userAvatar>
|
||||
<@userAvatar.userAvatar userName=entry.getNamespace().getOwner() avatarUrl=utils.avatarUrl(entry.getNamespace().getOwner()) clazz="user-avatar-xs"></@userAvatar.userAvatar>
|
||||
@ -161,7 +161,7 @@
|
||||
</#if>
|
||||
<br>
|
||||
<td style="vertical-align: middle; text-align: right">
|
||||
<a class="btn btn-success" href="/${utils.urlEncode(entry.namespace)}/versions/entry.versionString/reviews">Start review</a>
|
||||
<a class="btn btn-success" href="/${utils.urlEncode(entry.namespace.toString())}/versions/entry.versionString/reviews">Start review</a>
|
||||
</td>
|
||||
</tr>
|
||||
</#list>
|
||||
|
Loading…
Reference in New Issue
Block a user