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("/"));