mirror of
https://github.com/HangarMC/Hangar.git
synced 2025-03-19 15:40:50 +08:00
added all v2 api project routes
This commit is contained in:
parent
7252f47069
commit
d0544f8a31
@ -1,6 +1,5 @@
|
||||
package io.papermc.hangar.controller.api;
|
||||
|
||||
import io.papermc.hangar.model.ApiAuthInfo;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
@ -45,8 +44,7 @@ public interface ProjectsApi {
|
||||
, @ApiParam(value = "How to sort the projects") @Valid @RequestParam(value = "sort", required = false, defaultValue = "updated") ProjectSortingStrategy sort
|
||||
, @ApiParam(value = "If how relevant the project is to the given query should be used when sorting the projects") @RequestParam(value = "relevance", required = false, defaultValue = "true") boolean relevance
|
||||
, @ApiParam(value = "The maximum amount of projects to return") @Valid @RequestParam(value = "limit", required = false) Long limit
|
||||
, @ApiParam(value = "Where to start searching", defaultValue = "0") @Valid @RequestParam(value = "offset", required = false, defaultValue = "0") Long offset,
|
||||
ApiAuthInfo apiAuthInfo);
|
||||
, @ApiParam(value = "Where to start searching", defaultValue = "0") @Valid @RequestParam(value = "offset", required = false, defaultValue = "0") Long offset);
|
||||
|
||||
@ApiOperation(value = "Returns the members of a project", nickname = "showMembers", notes = "Returns the members of a project. Requires the `view_public_info` permission.", response = ProjectMember.class, authorizations = {
|
||||
@Authorization(value = "Session")}, tags = "Projects")
|
||||
@ -56,7 +54,7 @@ public interface ProjectsApi {
|
||||
@ApiResponse(code = 403, message = "Not enough permissions to use this endpoint")})
|
||||
@GetMapping(value = "/projects/{pluginId}/members",
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
ResponseEntity<ProjectMember> showMembers(@ApiParam(value = "The plugin id of the project to return members for", required = true) @PathVariable("pluginId") String pluginId
|
||||
ResponseEntity<List<ProjectMember>> showMembers(@ApiParam(value = "The plugin id of the project to return members for", required = true) @PathVariable("pluginId") String pluginId
|
||||
, @ApiParam(value = "The maximum amount of members to return") @Valid @RequestParam(value = "limit", required = false) Long limit
|
||||
, @ApiParam(value = "Where to start returning", defaultValue = "0") @Valid @RequestParam(value = "offset", required = false, defaultValue = "0") Long offset
|
||||
);
|
||||
@ -81,7 +79,7 @@ public interface ProjectsApi {
|
||||
@GetMapping(value = "/projects/{pluginId}/stats",
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
ResponseEntity<Map<String, ProjectStatsDay>> showProjectStats(@ApiParam(value = "The plugin id of the project to return the stats for", required = true) @PathVariable("pluginId") String pluginId
|
||||
, @NotNull @ApiParam(value = "The first date to include in the result", required = true) @Valid @RequestParam(value = "fromDate", required = true) LocalDate fromDate
|
||||
, @NotNull @ApiParam(value = "The last date to include in the result", required = true) @Valid @RequestParam(value = "toDate", required = true) LocalDate toDate
|
||||
, @NotNull @ApiParam(value = "The first date to include in the result", required = true) @Valid @RequestParam(value = "fromDate", required = true) String fromDate
|
||||
, @NotNull @ApiParam(value = "The last date to include in the result", required = true) @Valid @RequestParam(value = "toDate", required = true) String toDate
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,7 @@
|
||||
package io.papermc.hangar.controller.api;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import io.papermc.hangar.config.hangar.HangarConfig;
|
||||
import io.papermc.hangar.db.model.UsersTable;
|
||||
import io.papermc.hangar.model.ApiAuthInfo;
|
||||
import io.papermc.hangar.service.UserService;
|
||||
import io.papermc.hangar.util.ApiUtil;
|
||||
import io.papermc.hangar.model.Category;
|
||||
import io.papermc.hangar.model.Permission;
|
||||
import io.papermc.hangar.model.generated.PaginatedProjectResult;
|
||||
@ -16,8 +11,9 @@ import io.papermc.hangar.model.generated.ProjectMember;
|
||||
import io.papermc.hangar.model.generated.ProjectSortingStrategy;
|
||||
import io.papermc.hangar.model.generated.ProjectStatsDay;
|
||||
import io.papermc.hangar.model.generated.Tag;
|
||||
import io.papermc.hangar.service.PermissionService;
|
||||
import io.papermc.hangar.service.api.ProjectApiService;
|
||||
import io.papermc.hangar.service.project.ProjectService;
|
||||
import io.papermc.hangar.util.ApiUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -25,38 +21,36 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Controller
|
||||
public class ProjectsApiController implements ProjectsApi {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ProjectsApiController.class);
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final ProjectService projectService;
|
||||
private final HangarConfig hangarConfig;
|
||||
private final UserService userService;
|
||||
private final PermissionService permissionService;
|
||||
private final ApiAuthInfo apiAuthInfo;
|
||||
private final ProjectService projectService;
|
||||
private final ProjectApiService projectApiService;
|
||||
|
||||
@Autowired
|
||||
public ProjectsApiController(ObjectMapper objectMapper, ProjectService projectService, HangarConfig hangarConfig, UserService userService, PermissionService permissionService) {
|
||||
this.objectMapper = objectMapper;
|
||||
this.projectService = projectService;
|
||||
public ProjectsApiController(HangarConfig hangarConfig, ApiAuthInfo apiAuthInfo, ProjectService projectService, ProjectApiService projectApiService) {
|
||||
this.hangarConfig = hangarConfig;
|
||||
this.userService = userService;
|
||||
this.permissionService = permissionService;
|
||||
this.apiAuthInfo = apiAuthInfo;
|
||||
this.projectService = projectService;
|
||||
this.projectApiService = projectApiService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@PreAuthorize("@authenticationService.authApiRequest(T(io.papermc.hangar.model.Permission).ViewPublicInfo, T(io.papermc.hangar.controller.util.ApiScope).forGlobal())")
|
||||
public ResponseEntity<PaginatedProjectResult> listProjects(String q, List<Category> categories, List<String> tags, String owner, ProjectSortingStrategy sort, boolean orderWithRelevance, Long inLimit, Long inOffset, ApiAuthInfo apiAuthInfo) {
|
||||
public ResponseEntity<PaginatedProjectResult> listProjects(String q, List<Category> categories, List<String> tags, String owner, ProjectSortingStrategy sort, boolean orderWithRelevance, Long inLimit, Long inOffset) {
|
||||
// handle input
|
||||
long limit = ApiUtil.limitOrDefault(inLimit, hangarConfig.getProjects().getInitLoad());
|
||||
long offset = ApiUtil.offsetOrZero(inOffset);
|
||||
@ -71,15 +65,11 @@ public class ProjectsApiController implements ProjectsApi {
|
||||
parsedTags.add(new Tag().name(split[0]).data(split.length > 1 ? split[1] : null));
|
||||
}
|
||||
|
||||
UsersTable currentUser = userService.getCurrentUser();
|
||||
// TODO this is really meh, we want to save global permissions somewhere
|
||||
boolean seeHidden = currentUser != null && permissionService.getGlobalPermissions(currentUser.getId()).has(Permission.SeeHidden);
|
||||
Long requesterId = currentUser == null ? null : currentUser.getId();
|
||||
|
||||
String pluginId = null;
|
||||
boolean seeHidden = apiAuthInfo.getGlobalPerms().has(Permission.SeeHidden);
|
||||
Long requesterId = apiAuthInfo.getUser() == null ? null : apiAuthInfo.getUser().getId();
|
||||
|
||||
List<Project> projects = projectService.getProjects(
|
||||
pluginId,
|
||||
null,
|
||||
categories,
|
||||
parsedTags,
|
||||
q,
|
||||
@ -93,7 +83,7 @@ public class ProjectsApiController implements ProjectsApi {
|
||||
);
|
||||
|
||||
long count = projectService.countProjects(
|
||||
pluginId,
|
||||
null,
|
||||
categories,
|
||||
parsedTags,
|
||||
q,
|
||||
@ -110,38 +100,44 @@ public class ProjectsApiController implements ProjectsApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<ProjectMember> showMembers(String pluginId, Long inLimit, Long inOffset) {
|
||||
@PreAuthorize("@authenticationService.authApiRequest(T(io.papermc.hangar.model.Permission).ViewPublicInfo, T(io.papermc.hangar.controller.util.ApiScope).forProject(#pluginId))")
|
||||
public ResponseEntity<List<ProjectMember>> showMembers(String pluginId, Long inLimit, Long inOffset) {
|
||||
long limit = ApiUtil.limitOrDefault(inLimit, hangarConfig.getProjects().getInitLoad());
|
||||
long offset = ApiUtil.offsetOrZero(inOffset);
|
||||
|
||||
try {
|
||||
return new ResponseEntity<>(objectMapper.readValue("{\n \"roles\" : [ {\n \"color\" : \"color\",\n \"name\" : \"name\",\n \"title\" : \"title\"\n }, {\n \"color\" : \"color\",\n \"name\" : \"name\",\n \"title\" : \"title\"\n } ],\n \"user\" : \"user\"\n}", ProjectMember.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);
|
||||
List<ProjectMember> projectMembers = projectApiService.getProjectMembers(pluginId, limit, offset);
|
||||
if (projectMembers == null || projectMembers.isEmpty()) { // TODO this will also happen when the offset is too high
|
||||
log.error("Couldn't find a project for that pluginId");
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
return ResponseEntity.ok(projectMembers);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("@authenticationService.authApiRequest(T(io.papermc.hangar.model.Permission).ViewPublicInfo, T(io.papermc.hangar.controller.util.ApiScope).forProject(#pluginId))")
|
||||
public ResponseEntity<Project> showProject(String pluginId) {
|
||||
Project project = projectService.getProjectApi(pluginId);
|
||||
if (project == null) {
|
||||
log.error("Couldn't find a project for that pluginId");
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
return new ResponseEntity<>(project, HttpStatus.OK);
|
||||
return ResponseEntity.ok(project);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Map<String, ProjectStatsDay>> showProjectStats(String pluginId, LocalDate fromDate, LocalDate toDate) {
|
||||
try {
|
||||
return new ResponseEntity<Map<String, ProjectStatsDay>>(objectMapper.readValue("{\n \"key\" : {\n \"downloads\" : 0,\n \"views\" : 6\n }\n}", Map.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);
|
||||
@PreAuthorize("@authenticationService.authApiRequest(T(io.papermc.hangar.model.Permission).IsProjectMember, T(io.papermc.hangar.controller.util.ApiScope).forProject(#pluginId))")
|
||||
public ResponseEntity<Map<String, ProjectStatsDay>> showProjectStats(String pluginId, @NotNull @Valid String fromDate, @NotNull @Valid String toDate) {
|
||||
LocalDate from = ApiUtil.parseDate(fromDate);
|
||||
LocalDate to = ApiUtil.parseDate(toDate);
|
||||
if (from.isAfter(to)) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "From date is after to date");
|
||||
}
|
||||
Map<String, ProjectStatsDay> projectStats = projectApiService.getProjectStats(pluginId, from, to);
|
||||
if (projectStats == null || projectStats.size() == 0) {
|
||||
log.error("Couldn't find a project for that pluginId");
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
return ResponseEntity.ok(projectStats);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -85,8 +85,8 @@ public class VersionsApiController implements VersionsApi {
|
||||
@Override
|
||||
@PreAuthorize("@authenticationService.authApiRequest(T(io.papermc.hangar.model.Permission).IsProjectMember, T(io.papermc.hangar.controller.util.ApiScope).forProject(#pluginId))")
|
||||
public ResponseEntity<Map<String, VersionStatsDay>> showVersionStats(String pluginId, String version, @NotNull @Valid String fromDate, @NotNull @Valid String toDate) {
|
||||
LocalDate from = parseDate(fromDate);
|
||||
LocalDate to = parseDate(toDate);
|
||||
LocalDate from = ApiUtil.parseDate(fromDate);
|
||||
LocalDate to = ApiUtil.parseDate(toDate);
|
||||
if (from.isAfter(to)) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "From date is after to date");
|
||||
}
|
||||
@ -96,12 +96,4 @@ public class VersionsApiController implements VersionsApi {
|
||||
}
|
||||
return ResponseEntity.ok(versionStats);
|
||||
}
|
||||
|
||||
private LocalDate parseDate(String date) {
|
||||
try {
|
||||
return LocalDate.parse(date);
|
||||
} catch (DateTimeParseException e) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Badly formatted date " + date);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,9 @@ package io.papermc.hangar.db.dao.api;
|
||||
|
||||
import io.papermc.hangar.db.mappers.PromotedVersionMapper;
|
||||
import io.papermc.hangar.model.generated.Project;
|
||||
import io.papermc.hangar.model.generated.ProjectMember;
|
||||
import io.papermc.hangar.model.generated.ProjectStatsDay;
|
||||
import org.jdbi.v3.sqlobject.config.KeyColumn;
|
||||
import org.jdbi.v3.sqlobject.config.RegisterBeanMapper;
|
||||
import org.jdbi.v3.sqlobject.config.RegisterColumnMapper;
|
||||
import org.jdbi.v3.sqlobject.customizer.BindList;
|
||||
@ -11,7 +14,9 @@ import org.jdbi.v3.sqlobject.statement.SqlQuery;
|
||||
import org.jdbi.v3.stringtemplate4.UseStringTemplateEngine;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Repository
|
||||
@RegisterBeanMapper(Project.class)
|
||||
@ -109,4 +114,26 @@ public interface ProjectApiDao {
|
||||
@BindList(onEmpty = BindList.EmptyHandling.NULL_VALUE) List<Integer> categories,
|
||||
@BindList(onEmpty = BindList.EmptyHandling.NULL_VALUE) List<String> tags, //TODO: implement tags with mc_version('data')
|
||||
String query, @Define String queryStatement);
|
||||
|
||||
@RegisterBeanMapper(ProjectMember.class)
|
||||
@SqlQuery("SELECT u.name AS user, array_agg(r.name) roles " +
|
||||
" FROM projects p " +
|
||||
" JOIN user_project_roles upr ON p.id = upr.project_id " +
|
||||
" JOIN users u ON upr.user_id = u.id " +
|
||||
" JOIN roles r ON upr.role_type = r.name " +
|
||||
" WHERE p.plugin_id = :pluginId" +
|
||||
" GROUP BY u.name ORDER BY max(r.permission::BIGINT) DESC " +
|
||||
" LIMIT :limit OFFSET :offset")
|
||||
List<ProjectMember> projectMembers(String pluginId, long limit, long offset);
|
||||
|
||||
@KeyColumn("dateKey")
|
||||
@RegisterBeanMapper(ProjectStatsDay.class)
|
||||
@SqlQuery("SELECT CAST(dates.day AS date) dateKey, coalesce(sum(pvd.downloads), 0) AS downloads, coalesce(pv.views, 0) AS views" +
|
||||
" FROM projects p," +
|
||||
" (SELECT generate_series(:startDate::DATE, :endDate::DATE, INTERVAL '1 DAY') AS day) dates" +
|
||||
" LEFT JOIN project_versions_downloads pvd ON dates.day = pvd.day" +
|
||||
" LEFT JOIN project_views pv ON dates.day = pv.day AND pvd.project_id = pv.project_id" +
|
||||
" WHERE p.plugin_id = :pluginId AND (pvd IS NULL OR pvd.project_id = p.id)" +
|
||||
" GROUP BY pv.views, dates.day")
|
||||
Map<String, ProjectStatsDay> projectStats(String pluginId, LocalDate startDate, LocalDate endDate);
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
package io.papermc.hangar.service.api;
|
||||
|
||||
import io.papermc.hangar.db.dao.HangarDao;
|
||||
import io.papermc.hangar.db.dao.api.ProjectApiDao;
|
||||
import io.papermc.hangar.model.generated.ProjectMember;
|
||||
import io.papermc.hangar.model.generated.ProjectStatsDay;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class ProjectApiService {
|
||||
|
||||
private final HangarDao<ProjectApiDao> projectApiDao;
|
||||
|
||||
public ProjectApiService(HangarDao<ProjectApiDao> projectApiDao) {
|
||||
this.projectApiDao = projectApiDao;
|
||||
}
|
||||
|
||||
public List<ProjectMember> getProjectMembers(String pluginId, long limit, long offset) {
|
||||
return projectApiDao.get().projectMembers(pluginId, limit, offset);
|
||||
}
|
||||
|
||||
public Map<String, ProjectStatsDay> getProjectStats(String pluginId, LocalDate fromDate, LocalDate toDate) {
|
||||
return projectApiDao.get().projectStats(pluginId, fromDate, toDate);
|
||||
}
|
||||
}
|
@ -1,12 +1,18 @@
|
||||
package io.papermc.hangar.util;
|
||||
|
||||
import io.papermc.hangar.db.model.UsersTable;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeParseException;
|
||||
|
||||
public class ApiUtil {
|
||||
|
||||
private ApiUtil() { }
|
||||
|
||||
public static long limitOrDefault(Long limit, long defaultValue) {
|
||||
if (limit < 1) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Limit should be greater than 0");
|
||||
return Math.min(limit == null ? defaultValue : limit, defaultValue);
|
||||
}
|
||||
|
||||
@ -17,4 +23,12 @@ public class ApiUtil {
|
||||
public static Long userIdOrNull(UsersTable usersTable) {
|
||||
return usersTable == null ? null : usersTable.getId();
|
||||
}
|
||||
|
||||
public static LocalDate parseDate(String date) {
|
||||
try {
|
||||
return LocalDate.parse(date);
|
||||
} catch (DateTimeParseException e) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Badly formatted date " + date);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user