From b299606f88df98c958f0d06c0a8bee84bb3322bb Mon Sep 17 00:00:00 2001 From: Johannes Schnatterer Date: Mon, 25 Jun 2018 17:56:03 +0200 Subject: [PATCH] Polishing during review. Extends REST API docs, straightens naming, cleans up dependencies, clears warnings, does a bit of formatting, etc. --- scm-webapp/pom.xml | 20 ++++------- .../api/v2/FieldContainerResponseFilter.java | 2 +- .../java/sonia/scm/api/v2/JsonFilters.java | 4 +-- .../api/v2/JsonMarshallingResponseFilter.java | 2 +- ...ies.java => ResponseFilterPriorities.java} | 7 ++-- .../v2/resources/GroupToGroupDtoMapper.java | 6 ++-- .../scm/api/v2/resources/LinkBuilder.java | 2 +- .../sonia/scm/api/v2/resources/MemberDto.java | 5 ++- .../v2/resources/UserCollectionResource.java | 35 +++++++++++-------- .../resources/UserCollectionToDtoMapper.java | 6 ++-- .../sonia/scm/api/v2/resources/UserDto.java | 5 ++- .../api/v2/resources/UserDtoToUserMapper.java | 6 ++-- .../scm/api/v2/resources/UserResource.java | 31 ++++++++++++++-- .../api/v2/resources/UserRootResource.java | 5 ++- .../api/v2/resources/UserToUserDtoMapper.java | 6 ++-- .../v2/resources/UserRootResourceTest.java | 3 +- 16 files changed, 86 insertions(+), 59 deletions(-) rename scm-webapp/src/main/java/sonia/scm/api/v2/{FilterPriorities.java => ResponseFilterPriorities.java} (67%) diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index b6ce69c9ba..28b5ae16a2 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -405,19 +405,6 @@ provided - - - - javax.validation - validation-api - 2.0.1.Final - - - - org.hibernate - hibernate-validator - 6.0.10.Final - @@ -871,7 +858,7 @@ com.webcohesion.enunciate enunciate-top - 2.9.1 + ${enunciate.version} com.webcohesion.enunciate @@ -879,6 +866,11 @@ + + com.webcohesion.enunciate + enunciate-lombok + ${enunciate.version} + diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/FieldContainerResponseFilter.java b/scm-webapp/src/main/java/sonia/scm/api/v2/FieldContainerResponseFilter.java index f66621e367..21e32dc96f 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/FieldContainerResponseFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/FieldContainerResponseFilter.java @@ -24,7 +24,7 @@ import static java.util.Optional.ofNullable; * the {@link JsonNode} tree.

*/ @Provider -@Priority(FilterPriorities.FIELD_FILTER) +@Priority(ResponseFilterPriorities.FIELD_FILTER) public class FieldContainerResponseFilter implements ContainerResponseFilter { private static final String PARAMETER_FIELDS = "fields"; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/JsonFilters.java b/scm-webapp/src/main/java/sonia/scm/api/v2/JsonFilters.java index 149825b047..74221ab2ac 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/JsonFilters.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/JsonFilters.java @@ -11,12 +11,12 @@ import java.util.Map; import static java.util.Arrays.stream; -public final class JsonFilters { +final class JsonFilters { private JsonFilters() { } - public static void filterByFields(JsonNode root, Collection filterExpressions) { + static void filterByFields(JsonNode root, Collection filterExpressions) { createJsonFilterNode(filterExpressions).filterNode(root); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/JsonMarshallingResponseFilter.java b/scm-webapp/src/main/java/sonia/scm/api/v2/JsonMarshallingResponseFilter.java index a1e571aa34..883ead6109 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/JsonMarshallingResponseFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/JsonMarshallingResponseFilter.java @@ -19,7 +19,7 @@ import java.util.Set; * and processes all registered plugins for the {@link JsonEnricher} extension point. */ @Provider -@Priority(FilterPriorities.JSON_MARSHALLING) +@Priority(ResponseFilterPriorities.JSON_MARSHALLING) public class JsonMarshallingResponseFilter implements ContainerResponseFilter { private final ObjectMapper objectMapper; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/FilterPriorities.java b/scm-webapp/src/main/java/sonia/scm/api/v2/ResponseFilterPriorities.java similarity index 67% rename from scm-webapp/src/main/java/sonia/scm/api/v2/FilterPriorities.java rename to scm-webapp/src/main/java/sonia/scm/api/v2/ResponseFilterPriorities.java index 96ed8f0891..2ad02838ed 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/FilterPriorities.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/ResponseFilterPriorities.java @@ -5,16 +5,17 @@ import javax.ws.rs.Priorities; /** * A collection of filter priorities used by custom {@link javax.ws.rs.container.ContainerResponseFilter}s. + * Higher number means earlier execution in the response filter chain. */ -final class FilterPriorities { +final class ResponseFilterPriorities { /** * Other filters depend on already marshalled {@link com.fasterxml.jackson.databind.JsonNode} trees. Thus, JSON * marshalling has to happen before those filters */ static final int JSON_MARSHALLING = Priorities.USER + 1000; - static final int FIELD_FILTER = JSON_MARSHALLING - 1000; + static final int FIELD_FILTER = Priorities.USER; - private FilterPriorities() { + private ResponseFilterPriorities() { } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java index a9adab6f2c..57008f6313 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java @@ -16,10 +16,8 @@ import static de.otto.edison.hal.Links.linkingTo; import static sonia.scm.api.v2.resources.ResourceLinks.group; import static sonia.scm.api.v2.resources.ResourceLinks.user; -/** - * Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection. - */ -@java.lang.SuppressWarnings("squid:S3306") +// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection. +@SuppressWarnings("squid:S3306") @Mapper public abstract class GroupToGroupDtoMapper extends BaseMapper { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkBuilder.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkBuilder.java index 0a893e11e1..a659d49004 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkBuilder.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkBuilder.java @@ -9,7 +9,7 @@ import java.util.Arrays; /** * This class is used to create links for JAX-RS resources. Create a new instance specifying all resource classes used - * to process the request. Than for each of these classes call builder.method(...).parameters(...) for each + * to process the request. Then for each of these classes call builder.method(...).parameters(...) for each * of these classes consecutively. The builder itself is immutable, so that each instance is reusable and you get a new * builder for each method. * diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MemberDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MemberDto.java index 3b0d4f0a40..fcd7b8e8ad 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MemberDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MemberDto.java @@ -4,13 +4,16 @@ import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -@Data @NoArgsConstructor @AllArgsConstructor +@NoArgsConstructor @AllArgsConstructor +@Data @EqualsAndHashCode(callSuper = false) public class MemberDto extends HalRepresentation { private String name; @Override + @SuppressWarnings("squid:S1185") // We want to have this method available in this package protected HalRepresentation add(Links links) { return super.add(links); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java index e336ef302d..e52e546a30 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java @@ -2,6 +2,7 @@ package sonia.scm.api.v2.resources; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.ResponseHeader; +import com.webcohesion.enunciate.metadata.rs.ResponseHeaders; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; import sonia.scm.PageResult; @@ -31,12 +32,14 @@ import static sonia.scm.api.v2.resources.ResourceLinks.user; @Produces(VndMediaType.USER_COLLECTION) public class UserCollectionResource extends AbstractManagerResource { - public static final int DEFAULT_PAGE_SIZE = 10; + + private static final int DEFAULT_PAGE_SIZE = 10; private final UserDtoToUserMapper dtoToUserMapper; private final UserCollectionToDtoMapper userCollectionToDtoMapper; @Inject - public UserCollectionResource(UserManager manager, UserDtoToUserMapper dtoToUserMapper, UserCollectionToDtoMapper userCollectionToDtoMapper) { + public UserCollectionResource(UserManager manager, UserDtoToUserMapper dtoToUserMapper, + UserCollectionToDtoMapper userCollectionToDtoMapper) { super(manager); this.dtoToUserMapper = dtoToUserMapper; this.userCollectionToDtoMapper = userCollectionToDtoMapper; @@ -44,50 +47,52 @@ public class UserCollectionResource extends AbstractManagerResourceNote: This method requires admin privileges. + * + * Note: This method requires "user" privileges. * * @param request the current request * @param page the number of the requested page * @param pageSize the page size (default page size is {@value DEFAULT_PAGE_SIZE}) - * @param sortby sort parameter - * @param desc sort direction desc or aesc - * @return + * @param sortBy sort parameter + * @param desc sort direction desc or asc */ @GET @Path("") @TypeHint(UserDto[].class) @StatusCodes({ @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), + @ResponseCode(code = 403, condition = "forbidden, the current user does not have the \"user\" privilege"), @ResponseCode(code = 500, condition = "internal server error") }) @Override public Response getAll(@Context Request request, @DefaultValue("0") @QueryParam("page") int page, @DefaultValue("" + DEFAULT_PAGE_SIZE) @QueryParam("pageSize") int pageSize, - @QueryParam("sortby") String sortby, - @DefaultValue("false") - @QueryParam("desc") boolean desc) { - PageResult pageResult = fetchPage(sortby, desc, page, pageSize); + @QueryParam("sortby") String sortBy, + @DefaultValue("false") @QueryParam("desc") boolean desc) { + + PageResult pageResult = fetchPage(sortBy, desc, page, pageSize); return Response.ok(userCollectionToDtoMapper.map(page, pageSize, pageResult)).build(); } /** * Creates a new user. + * + * Note: This method requires "user" privileges. + * * @param userDto The user to be created. * @return A response with the link to the new user (if created successfully). */ @POST @Path("") @StatusCodes({ - @ResponseCode(code = 201, condition = "create success", additionalHeaders = { - @ResponseHeader(name = "Location", description = "uri to the created group") - }), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), + @ResponseCode(code = 201, condition = "create success"), + @ResponseCode(code = 403, condition = "forbidden, the current user does not have the \"user\" privilege"), @ResponseCode(code = 500, condition = "internal server error") }) @TypeHint(TypeHint.NO_CONTENT.class) + @ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created user")) public Response create(@Context UriInfo uriInfo, UserDto userDto) throws IOException, UserException { if (userDto == null) { return Response.status(400).build(); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionToDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionToDtoMapper.java index b609771e69..34f26332f1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionToDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionToDtoMapper.java @@ -7,10 +7,8 @@ import javax.inject.Inject; import static sonia.scm.api.v2.resources.ResourceLinks.userCollection; -/** - * Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection. - */ -@java.lang.SuppressWarnings("squid:S3306") +// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection. +@SuppressWarnings("squid:S3306") public class UserCollectionToDtoMapper extends BasicCollectionToDtoMapper { private final UriInfoStore uriInfoStore; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDto.java index df538592c5..c79df01570 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDto.java @@ -5,12 +5,14 @@ import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import java.time.Instant; import java.util.Optional; -@Data @AllArgsConstructor @NoArgsConstructor +@AllArgsConstructor @NoArgsConstructor +@Data @EqualsAndHashCode(callSuper = false) public class UserDto extends HalRepresentation { private boolean active; private boolean admin; @@ -24,6 +26,7 @@ public class UserDto extends HalRepresentation { private String type; @Override + @SuppressWarnings("squid:S1185") // We want to have this method available in this package protected HalRepresentation add(Links links) { return super.add(links); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDtoToUserMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDtoToUserMapper.java index b2c9830274..29620e6742 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDtoToUserMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDtoToUserMapper.java @@ -11,10 +11,8 @@ import javax.inject.Inject; import static sonia.scm.api.rest.resources.UserResource.DUMMY_PASSWORT; -/** - * Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection. - */ -@java.lang.SuppressWarnings("squid:S3306") +// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection. +@SuppressWarnings("squid:S3306") @Mapper public abstract class UserDtoToUserMapper { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java index 4ee9dbd6b1..6a8845d17e 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java @@ -26,6 +26,7 @@ import java.util.Collection; @Produces(VndMediaType.USER) public class UserResource extends AbstractManagerResource { + private final UserDtoToUserMapper dtoToUserMapper; private final UserToUserDtoMapper userToDtoMapper; @@ -36,6 +37,15 @@ public class UserResource extends AbstractManagerResource { this.userToDtoMapper = userToDtoMapper; } + /** + * Returns a user. + * + * Note: This method requires "user" privileges. + * + * @param request the current request + * @param id the id/name of the user + * + */ @GET @Path("") @TypeHint(UserDto.class) @@ -54,11 +64,19 @@ public class UserResource extends AbstractManagerResource { return Response.ok(userDto).build(); } + /** + * Modifies the given user. + * + * Note: This method requires "user" privileges. + * + * @param name name of the user to be modified + * @param userDto user object to modify + */ @PUT @Path("") @StatusCodes({ @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), + @ResponseCode(code = 403, condition = "forbidden, the current user does not have the \"user\" privilege"), @ResponseCode(code = 500, condition = "internal server error") }) @TypeHint(TypeHint.NO_CONTENT.class) @@ -69,14 +87,23 @@ public class UserResource extends AbstractManagerResource { return update(name, user); } + /** + * Deletes a user. + * + * Note: This method requires "user" privileges. + * + * @param name the name of the user to delete. + * + */ @DELETE @Path("") @StatusCodes({ @ResponseCode(code = 204, condition = "delete success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), + @ResponseCode(code = 403, condition = "forbidden, the current user does not have the \"user\" privilege"), @ResponseCode(code = 500, condition = "internal server error") }) @TypeHint(TypeHint.NO_CONTENT.class) + @Override public Response delete(@PathParam("id") String name) { return super.delete(name); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserRootResource.java index 7f3972dce2..779f6323e1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserRootResource.java @@ -4,10 +4,13 @@ import javax.inject.Inject; import javax.inject.Provider; import javax.ws.rs.Path; +/** + * RESTful Web Service Resource to manage users. + */ @Path(UserRootResource.USERS_PATH_V2) public class UserRootResource { - public static final String USERS_PATH_V2 = "v2/users/"; + static final String USERS_PATH_V2 = "v2/users/"; private final Provider userCollectionResource; private final Provider userResource; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java index 3c9a03d9bb..194c44acaa 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java @@ -14,10 +14,8 @@ import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; import static sonia.scm.api.v2.resources.ResourceLinks.user; -/** - * Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection. - */ -@java.lang.SuppressWarnings("squid:S3306") +// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection. +@SuppressWarnings("squid:S3306") @Mapper public abstract class UserToUserDtoMapper extends BaseMapper { diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java index 605d7b0db2..9e732e9212 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java @@ -80,7 +80,8 @@ public class UserRootResourceTest { UserCollectionResource userCollectionResource = new UserCollectionResource(userManager, dtoToUserMapper, userCollectionToDtoMapper); UserResource userResource = new UserResource(dtoToUserMapper, userToDtoMapper, userManager); - UserRootResource userRootResource = new UserRootResource(MockProvider.of(userCollectionResource), MockProvider.of(userResource)); + UserRootResource userRootResource = new UserRootResource(MockProvider.of(userCollectionResource), + MockProvider.of(userResource)); dispatcher.getRegistry().addSingletonResource(userRootResource); when(uriInfo.getBaseUri()).thenReturn(URI.create("/"));