Skip to content

Commit

Permalink
fix: properly use pagination objects for some user apis, fixes openap…
Browse files Browse the repository at this point in the history
…i, adds tests for openapi validity
  • Loading branch information
MiniDigger committed Jul 18, 2024
1 parent 8b805f9 commit c11b607
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 16 deletions.
6 changes: 6 additions & 0 deletions backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@

<!-- test -->
<pg.version>1.19.8</pg.version>
<openapi-generator.version>7.7.0</openapi-generator.version>

<!-- plugins -->
<maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version>
Expand Down Expand Up @@ -412,6 +413,11 @@
<artifactId>junit-platform-suite</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-cli</artifactId>
<version>${openapi-generator.version}</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ public ResponseEntity<PaginatedResult<User>> getUsers(final String query, final
}

@Override
@ApplicableSorters({SorterRegistry.USER_NAME, SorterRegistry.USER_JOIN_DATE, SorterRegistry.USER_PROJECT_COUNT, SorterRegistry.USER_LOCKED, SorterRegistry.USER_ORG, SorterRegistry.USER_ROLES})
public ResponseEntity<PaginatedResult<ProjectCompact>> getUserStarred(final String userName, final ProjectSortingStrategy sort, final @NotNull RequestPagination pagination) {
return ResponseEntity.ok(this.usersApiService.getUserStarred(userName, sort, pagination));
@ApplicableSorters({SorterRegistry.VIEWS, SorterRegistry.DOWNLOADS, SorterRegistry.NEWEST, SorterRegistry.STARS, SorterRegistry.UPDATED, SorterRegistry.RECENT_DOWNLOADS, SorterRegistry.RECENT_VIEWS, SorterRegistry.SLUG})
public ResponseEntity<PaginatedResult<ProjectCompact>> getUserStarred(final String userName, final @NotNull RequestPagination pagination) {
return ResponseEntity.ok(this.usersApiService.getUserStarred(userName, pagination));
}

@Override
@ApplicableSorters({SorterRegistry.USER_NAME, SorterRegistry.USER_JOIN_DATE, SorterRegistry.USER_PROJECT_COUNT, SorterRegistry.USER_LOCKED, SorterRegistry.USER_ORG, SorterRegistry.USER_ROLES})
public ResponseEntity<PaginatedResult<ProjectCompact>> getUserWatching(final String userName, final ProjectSortingStrategy sort, final @NotNull RequestPagination pagination) {
return ResponseEntity.ok(this.usersApiService.getUserWatching(userName, sort, pagination));
@ApplicableSorters({SorterRegistry.VIEWS, SorterRegistry.DOWNLOADS, SorterRegistry.NEWEST, SorterRegistry.STARS, SorterRegistry.UPDATED, SorterRegistry.RECENT_DOWNLOADS, SorterRegistry.RECENT_VIEWS, SorterRegistry.SLUG})
public ResponseEntity<PaginatedResult<ProjectCompact>> getUserWatching(final String userName, final @NotNull RequestPagination pagination) {
return ResponseEntity.ok(this.usersApiService.getUserWatching(userName, pagination));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ ResponseEntity<PaginatedResult<User>> getUsers(@Parameter(description = "The sea
})
@GetMapping("/users/{user}/starred")
ResponseEntity<PaginatedResult<ProjectCompact>> getUserStarred(@Parameter(description = "The user to return starred projects for") @PathVariable("user") String userName,
@Parameter(description = "How to sort the projects") @RequestParam(defaultValue = "updated") ProjectSortingStrategy sort,
@Parameter(description = "Pagination information") @NotNull RequestPagination pagination);

@Operation(
Expand All @@ -87,7 +86,6 @@ ResponseEntity<PaginatedResult<ProjectCompact>> getUserStarred(@Parameter(descri
})
@GetMapping("/users/{user}/watching")
ResponseEntity<PaginatedResult<ProjectCompact>> getUserWatching(@Parameter(description = "The user to return watched projects for") @PathVariable("user") String userName,
@Parameter(description = "How to sort the projects") @RequestParam(defaultValue = "updated") ProjectSortingStrategy sort,
@Parameter(description = "Pagination information") @NotNull RequestPagination pagination);

@Operation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ public interface UsersApiDAO {
" <if(!canSeeHidden)> (p.visibility = 0" +
" <if(userId)>OR (<userId> = ANY(hp.project_members) AND p.visibility != 4)<endif>) AND<endif>" +
" lower(u.name) = lower(:user)" +
" ORDER BY <sortOrder> LIMIT :limit OFFSET :offset")
List<ProjectCompact> getUserStarred(String user, @Define boolean canSeeHidden, @Define Long userId, @Define String sortOrder, long limit, long offset);
" <sorters>" +
" <offsetLimit>")
List<ProjectCompact> getUserStarred(String user, @Define boolean canSeeHidden, @Define Long userId, @BindPagination RequestPagination pagination);

@UseStringTemplateEngine
@SqlQuery("SELECT count(*)" +
Expand Down Expand Up @@ -82,8 +83,9 @@ public interface UsersApiDAO {
" <if(!canSeeHidden)> (p.visibility = 0" +
" <if(userId)>OR (<userId> = ANY(hp.project_members) AND p.visibility != 4)<endif>) AND<endif>" +
" lower(u.name) = lower(:user)" +
" ORDER BY <sortOrder> LIMIT :limit OFFSET :offset")
List<ProjectCompact> getUserWatching(String user, @Define boolean canSeeHidden, @Define Long userId, @Define String sortOrder, long limit, long offset);
" <sorters>" +
" <offsetLimit>")
List<ProjectCompact> getUserWatching(String user, @Define boolean canSeeHidden, @Define Long userId, @BindPagination RequestPagination pagination);

@UseStringTemplateEngine
@SqlQuery("SELECT count(*)" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,20 +90,20 @@ public <T extends User> PaginatedResult<T> getUsers(final String query, final Re
}

@Transactional(readOnly = true)
public PaginatedResult<ProjectCompact> getUserStarred(final String userName, final ProjectSortingStrategy sortingStrategy, final RequestPagination pagination) {
public PaginatedResult<ProjectCompact> getUserStarred(final String userName, final RequestPagination pagination) {
this.getUserRequired(userName, this.usersDAO::getUser, User.class);
final boolean canSeeHidden = this.getGlobalPermissions().has(Permission.SeeHidden);
final List<ProjectCompact> projects = this.usersApiDAO.getUserStarred(userName, canSeeHidden, this.getHangarUserId(), sortingStrategy.getSql(), pagination.getLimit(), pagination.getOffset());
final List<ProjectCompact> projects = this.usersApiDAO.getUserStarred(userName, canSeeHidden, this.getHangarUserId(), pagination);
projects.forEach(p -> p.setAvatarUrl(this.avatarService.getProjectAvatarUrl(p.getId(), p.getNamespace().getOwner())));
final long count = this.usersApiDAO.getUserStarredCount(userName, canSeeHidden, this.getHangarUserId());
return new PaginatedResult<>(new Pagination(count, pagination), projects);
}

@Transactional(readOnly = true)
public PaginatedResult<ProjectCompact> getUserWatching(final String userName, final ProjectSortingStrategy sortingStrategy, final RequestPagination pagination) {
public PaginatedResult<ProjectCompact> getUserWatching(final String userName, final RequestPagination pagination) {
this.getUserRequired(userName, this.usersDAO::getUser, User.class);
final boolean canSeeHidden = this.getGlobalPermissions().has(Permission.SeeHidden);
final List<ProjectCompact> projects = this.usersApiDAO.getUserWatching(userName, canSeeHidden, this.getHangarUserId(), sortingStrategy.getSql(), pagination.getLimit(), pagination.getOffset());
final List<ProjectCompact> projects = this.usersApiDAO.getUserWatching(userName, canSeeHidden, this.getHangarUserId(), pagination);
projects.forEach(p -> p.setAvatarUrl(this.avatarService.getProjectAvatarUrl(p.getId(), p.getNamespace().getOwner())));
final long count = this.usersApiDAO.getUserWatchingCount(userName, canSeeHidden, this.getHangarUserId());
return new PaginatedResult<>(new Pagination(count, pagination), projects);
Expand Down
56 changes: 56 additions & 0 deletions backend/src/test/java/io/papermc/hangar/OpenApiTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.papermc.hangar;

import io.papermc.hangar.controller.api.v1.helper.ControllerTest;
import io.swagger.parser.OpenAPIParser;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.parser.core.models.ParseOptions;
import io.swagger.v3.parser.core.models.SwaggerParseResult;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.openapitools.codegen.validation.ValidationResult;
import org.openapitools.codegen.validations.oas.OpenApiEvaluator;
import org.openapitools.codegen.validations.oas.RuleConfiguration;
import org.springframework.util.StreamUtils;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

class OpenApiTest extends ControllerTest {

@Test
void testPublicOpenApi() throws Exception {
String response = this.mockMvc.perform(get("/v3/api-docs/public"))
.andExpect(status().is(200))
.andReturn().getResponse().getContentAsString();

ParseOptions options = new ParseOptions();
options.setResolve(true);
SwaggerParseResult result = new OpenAPIParser().readContents(response, null, options);
OpenAPI specification = result.getOpenAPI();

RuleConfiguration ruleConfiguration = new RuleConfiguration();
ruleConfiguration.setEnableRecommendations(true);
OpenApiEvaluator evaluator = new OpenApiEvaluator(ruleConfiguration);
ValidationResult validationResult = evaluator.validate(specification);

// check validation errors
assertThat(result.getMessages()).isEmpty();
assertThat(validationResult.getErrors()).isEmpty();
assertThat(validationResult.getWarnings()).isEmpty();

// check that sorters and filters got added
PathItem projects = specification.getPaths().get("/api/v1/projects");
assertThat(projects).isNotNull();
List<Parameter> parameters = projects.getGet().getParameters();
assertThat(parameters).isNotNull();
assertThat(parameters.stream().anyMatch(p -> "sort".equals(p.getName()))).isTrue();
assertThat(parameters.stream().anyMatch(p -> "category".equals(p.getName()))).isTrue();
assertThat(parameters.stream().anyMatch(p -> "pagination".equals(p.getName()))).isTrue();
assertThat(parameters.stream().anyMatch(p -> "owner".equals(p.getName()))).isTrue();
}
}

0 comments on commit c11b607

Please sign in to comment.