From 42f412faa49bf0794fdf098f7df5d755e49af466 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 9 May 2017 16:06:08 +0200 Subject: [PATCH] improve rest api documentation of Authentication, ChangePassword, Cipher, Group, Key and Plugin resource --- .../resources/AuthenticationResource.java | 104 +++++--------- .../resources/ChangePasswordResource.java | 19 +-- .../api/rest/resources/CipherResource.java | 13 +- .../scm/api/rest/resources/GroupResource.java | 110 ++++++--------- .../scm/api/rest/resources/KeyResource.java | 14 +- .../api/rest/resources/PluginResource.java | 127 +++++++++--------- 6 files changed, 159 insertions(+), 228 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java index c0cc127c1b..dd3c780cb1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java @@ -41,6 +41,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import com.google.inject.Inject; import com.google.inject.Singleton; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; import org.apache.shiro.SecurityUtils; @@ -78,11 +80,9 @@ import sonia.scm.util.HttpUtil; import java.util.Collection; import java.util.Collections; import java.util.List; -import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; import javax.ws.rs.DefaultValue; import javax.ws.rs.FormParam; @@ -100,7 +100,8 @@ import javax.xml.bind.annotation.XmlRootElement; import sonia.scm.security.XsrfCookies; /** - * + * Authentication related RESTful Web Service endpoint. + * * @author Sebastian Sdorra */ @Singleton @@ -152,15 +153,8 @@ public class AuthenticationResource //~--- methods -------------------------------------------------------------- /** - * Authenticate a user and return the state of the application.
- *
- * - * + * Authenticate a user and return the state of the application. + * * @param request the current http request * @param username the username for the authentication * @param password the password for the authentication @@ -171,6 +165,12 @@ public class AuthenticationResource @POST @Path("login") @TypeHint(ScmState.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 400, condition = "bad request, required parameter is missing"), + @ResponseCode(code = 401, condition = "unauthorized, the specified username or password is wrong"), + @ResponseCode(code = 500, condition = "internal server error") + }) public Response authenticate(@Context HttpServletRequest request, @FormParam("username") String username, @FormParam("password") String password, @FormParam("rememberMe") @@ -243,13 +243,7 @@ public class AuthenticationResource } /** - * Logout the current user. Returns the current state of the application, - * if public access is enabled.
- *
- * + * Logout the current user. Returns the current state of the application, if public access is enabled. * * @param request the current http request * @param response the current http response @@ -259,6 +253,10 @@ public class AuthenticationResource @GET @Path("logout") @TypeHint(ScmState.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 500, condition = "internal server error") + }) public Response logout(@Context HttpServletRequest request, @Context HttpServletResponse response) { @@ -287,16 +285,8 @@ public class AuthenticationResource //~--- get methods ---------------------------------------------------------- /** - * This method is an alias of the - * {@link #getState(javax.servlet.http.HttpServletRequest)} method. - * The only difference between the methods, - * is that this one could not be used with basic authentication.
- *
- * + * This method is an alias of the {@link #getState(HttpServletRequest)} method. + * The only difference between the methods, is that this one could not be used with basic authentication. * * @param request the current http request * @@ -305,19 +295,18 @@ public class AuthenticationResource @GET @Path("state") @TypeHint(ScmState.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 401, condition = "unauthorized, user is not authenticated and public access is disabled"), + @ResponseCode(code = 500, condition = "internal server error") + }) public Response getCurrentState(@Context HttpServletRequest request) { return getState(request); } /** - * Returns the current state of the application.
- *
- * + * Returns the current state of the application. * * @param request the current http request * @@ -325,6 +314,11 @@ public class AuthenticationResource */ @GET @TypeHint(ScmState.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 401, condition = "unauthorized, user is not authenticated and public access is disabled"), + @ResponseCode(code = 500, condition = "internal server error") + }) public Response getState(@Context HttpServletRequest request) { Response response; @@ -361,28 +355,12 @@ public class AuthenticationResource //~--- methods -------------------------------------------------------------- - /** - * Method description - * - * - * @return - */ private ScmState createAnonymousState() { return createState(SCMContext.ANONYMOUS, Collections.EMPTY_LIST, Collections.EMPTY_LIST, Collections.EMPTY_LIST); } - /** - * Method description - * - * - * @param securityContext - * - * @param subject - * - * @return - */ private ScmState createState(Subject subject) { PrincipalCollection collection = subject.getPrincipals(); @@ -410,17 +388,6 @@ public class AuthenticationResource return createState(user, groups.getCollection(), builder.build(), ap); } - /** - * Method description - * - * - * @param user - * @param groups - * @param assignedPermissions - * @param availablePermissions - * - * @return - */ private ScmState createState(User user, Collection groups, List assignedPermissions, List availablePermissions) @@ -431,17 +398,6 @@ public class AuthenticationResource availablePermissions); } - /** - * Method description - * - * - * @param request - * @param ex - * @param status - * @param failure - * - * @return - */ private Response handleFailedAuthentication(HttpServletRequest request, AuthenticationException ex, Response.Status status, WUIAuthenticationFailure failure) diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java index c2ac16eed6..52b8b02c37 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java @@ -36,6 +36,8 @@ package sonia.scm.api.rest.resources; //~--- non-JDK imports -------------------------------------------------------- import com.google.inject.Inject; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; import org.apache.shiro.SecurityUtils; @@ -66,7 +68,8 @@ import javax.ws.rs.core.Response; import sonia.scm.security.Role; /** - * + * Resource to change the password of the authenticated user. + * * @author Sebastian Sdorra */ @Path("action/change-password") @@ -98,14 +101,7 @@ public class ChangePasswordResource //~--- methods -------------------------------------------------------------- /** - * Changes the password of the current user.
- *
- * Status codes: - *
    - *
  • 200 success
  • - *
  • 400 bad request, the old password is not correct
  • - *
  • 500 internal server error
  • - *
+ * Changes the password of the current user. * * @param oldPassword old password of the current user * @param newPassword new password for the current user @@ -117,6 +113,11 @@ public class ChangePasswordResource */ @POST @TypeHint(RestActionResult.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 400, condition = "bad request, the old password is not correct"), + @ResponseCode(code = 500, condition = "internal server error") + }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) public Response changePassword(@FormParam("old-password") String oldPassword, @FormParam("new-password") String newPassword) diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/CipherResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/CipherResource.java index 27de46ce03..bbfdb363c0 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/CipherResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/CipherResource.java @@ -35,6 +35,8 @@ package sonia.scm.api.rest.resources; import com.google.common.base.Preconditions; import com.google.common.base.Strings; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; import org.apache.shiro.SecurityUtils; @@ -60,12 +62,7 @@ public class CipherResource /** * Encrypts the request body and returns an encrypted string. This method can - * only executed with administration privileges.
- *
- *
    - *
  • 200 success
  • - *
  • 500 internal server error
  • - *
+ * only executed with administration privileges. * * @param value value to encrypt * @@ -73,6 +70,10 @@ public class CipherResource */ @POST @Path("encrypt") + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 500, condition = "internal server error") + }) @Produces(MediaType.TEXT_PLAIN) public String encrypt(String value) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupResource.java index afee3eb7e8..808aaa498b 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupResource.java @@ -37,6 +37,9 @@ package sonia.scm.api.rest.resources; import com.google.inject.Inject; import com.google.inject.Singleton; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.ResponseHeader; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; import org.apache.shiro.SecurityUtils; @@ -68,7 +71,8 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; /** - * + * RESTful Web Service Resource to manage groups and their members. + * * @author Sebastian Sdorra */ @Path("groups") @@ -99,15 +103,7 @@ public class GroupResource //~--- methods -------------------------------------------------------------- /** - * Creates a new group.
- * This method requires admin privileges.
- *
- * Status codes: - *
    - *
  • 201 create success
  • - *
  • 403 forbidden, the current user has no admin privileges
  • - *
  • 500 internal server error
  • - *
+ * Creates a new group. Note: This method requires admin privileges. * * @param uriInfo current uri informations * @param group the group to be created @@ -115,6 +111,14 @@ public class GroupResource * @return */ @POST + @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 = 500, condition = "internal server error") + }) + @TypeHint(TypeHint.NO_CONTENT.class) @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Override public Response create(@Context UriInfo uriInfo, Group group) @@ -123,15 +127,7 @@ public class GroupResource } /** - * Deletes a group.
- * This method requires admin privileges.
- *
- * Status codes: - *
    - *
  • 201 delete success
  • - *
  • 403 forbidden, the current user has no admin privileges
  • - *
  • 500 internal server error
  • - *
+ * Deletes a group. Note: This method requires admin privileges. * * @param name the name of the group to delete. * @@ -139,6 +135,12 @@ public class GroupResource */ @DELETE @Path("{id}") + @StatusCodes({ + @ResponseCode(code = 204, condition = "delete success"), + @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), + @ResponseCode(code = 500, condition = "internal server error") + }) + @TypeHint(TypeHint.NO_CONTENT.class) @Override public Response delete(@PathParam("id") String name) { @@ -146,15 +148,7 @@ public class GroupResource } /** - * Modifies the given group.
- * This method requires admin privileges.
- *
- * Status codes: - *
    - *
  • 201 update successful
  • - *
  • 403 forbidden, the current user has no admin privileges
  • - *
  • 500 internal server error
  • - *
+ * Modifies the given group. Note: This method requires admin privileges. * * @param uriInfo current uri informations * @param name name of the group to be modified @@ -164,6 +158,12 @@ public class GroupResource */ @PUT @Path("{id}") + @StatusCodes({ + @ResponseCode(code = 204, condition = "update success"), + @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), + @ResponseCode(code = 500, condition = "internal server error") + }) + @TypeHint(TypeHint.NO_CONTENT.class) @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Override public Response update(@Context UriInfo uriInfo, @@ -175,16 +175,7 @@ public class GroupResource //~--- get methods ---------------------------------------------------------- /** - * Returns a group.
- * This method requires admin privileges.
- *
- * Status codes: - *
    - *
  • 200 get successful
  • - *
  • 403 forbidden, the current user has no admin privileges
  • - *
  • 404 not found, no group with the specified id/name available
  • - *
  • 500 internal server error
  • - *
+ * Fetches a group by its name or id. Note: This method requires admin privileges. * * @param request the current request * @param id the id/name of the group @@ -194,6 +185,12 @@ public class GroupResource @GET @Path("{id}") @TypeHint(Group.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), + @ResponseCode(code = 404, condition = "not found, no group with the specified id/name available"), + @ResponseCode(code = 500, condition = "internal server error") + }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Override public Response get(@Context Request request, @PathParam("id") String id) @@ -213,15 +210,7 @@ public class GroupResource } /** - * Returns all groups.
- * This method requires admin privileges.
- *
- * Status codes: - *
    - *
  • 200 get successful
  • - *
  • 403 forbidden, the current user has no admin privileges
  • - *
  • 500 internal server error
  • - *
+ * Returns all groups. Note: This method requires admin privileges. * * @param request the current request * @param start the start value for paging @@ -234,6 +223,11 @@ public class GroupResource @GET @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @TypeHint(Group[].class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), + @ResponseCode(code = 500, condition = "internal server error") + }) @Override public Response getAll(@Context Request request, @DefaultValue("0") @QueryParam("start") int start, @DefaultValue("-1") @@ -246,14 +240,6 @@ public class GroupResource //~--- methods -------------------------------------------------------------- - /** - * Method description - * - * - * @param items - * - * @return - */ @Override protected GenericEntity> createGenericEntity( Collection items) @@ -264,26 +250,12 @@ public class GroupResource //~--- get methods ---------------------------------------------------------- - /** - * Method description - * - * - * @param group - * - * @return - */ @Override protected String getId(Group group) { return group.getName(); } - /** - * Method description - * - * - * @return - */ @Override protected String getPathPart() { diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/KeyResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/KeyResource.java index 7668a04b71..6a4c56a643 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/KeyResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/KeyResource.java @@ -34,6 +34,8 @@ package sonia.scm.api.rest.resources; //~--- non-JDK imports -------------------------------------------------------- import com.google.inject.Inject; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; import org.apache.shiro.SecurityUtils; @@ -72,17 +74,15 @@ public class KeyResource //~--- methods -------------------------------------------------------------- /** - * Generates a unique key. This method can only executed with administration - * privileges.
- *
- *
    - *
  • 200 success
  • - *
  • 500 internal server error
  • - *
+ * Generates a unique key. Note: This method can only executed with administration privileges. * * @return unique key */ @GET + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 500, condition = "internal server error") + }) @Produces(MediaType.TEXT_PLAIN) public String generateKey() { diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/PluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/PluginResource.java index f6980e844d..0fd7518795 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/PluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/PluginResource.java @@ -53,6 +53,9 @@ import sonia.scm.plugin.PluginInformationComparator; //~--- JDK imports ------------------------------------------------------------ import com.sun.jersey.multipart.FormDataParam; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; +import com.webcohesion.enunciate.metadata.rs.TypeHint; import java.io.IOException; import java.io.InputStream; @@ -73,7 +76,8 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; /** - * + * RESTful Web Service Endpoint to manage plugins. + * * @author Sebastian Sdorra */ @Singleton @@ -104,13 +108,7 @@ public class PluginResource //~--- methods -------------------------------------------------------------- /** - * Installs a plugin from a package.
- *
- *
    - *
  • 200 success
  • - *
  • 412 precondition failed
  • - *
  • 500 internal server error
  • - *
+ * Installs a plugin from a package. * * @param uploadedInputStream * @return @@ -119,6 +117,11 @@ public class PluginResource */ @POST @Path("install-package") + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 412, condition = "precondition failed"), + @ResponseCode(code = 500, condition = "internal server error") + }) @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) public Response install( @@ -150,35 +153,30 @@ public class PluginResource } /** - * Installs a plugin.
- *
- *
    - *
  • 200 success
  • - *
  • 500 internal server error
  • - *
+ * Installs a plugin. * * @param id id of the plugin to be installed * * @return */ @POST + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 500, condition = "internal server error") + }) + @TypeHint(TypeHint.NO_CONTENT.class) @Path("install/{id}") public Response install(@PathParam("id") String id) { pluginManager.install(id); + // TODO should return 204 no content return Response.ok().build(); } /** * Installs a plugin from a package. This method is a workaround for ExtJS - * file upload, which requires text/html as content-type.
- *
- *
    - *
  • 200 success
  • - *
  • 412 precondition failed
  • - *
  • 500 internal server error
  • - *
+ * file upload, which requires text/html as content-type. * * @param uploadedInputStream * @return @@ -187,6 +185,11 @@ public class PluginResource */ @POST @Path("install-package.html") + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 412, condition = "precondition failed"), + @ResponseCode(code = 500, condition = "internal server error") + }) @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.TEXT_HTML) public Response installFromUI( @@ -197,60 +200,62 @@ public class PluginResource } /** - * Uninstalls a plugin.
- *
- *
    - *
  • 200 success
  • - *
  • 500 internal server error
  • - *
+ * Uninstalls a plugin. * * @param id id of the plugin to be uninstalled * * @return */ @POST + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 500, condition = "internal server error") + }) @Path("uninstall/{id}") public Response uninstall(@PathParam("id") String id) { pluginManager.uninstall(id); + // TODO should return 204 content + // consider to do a uninstall with a delete return Response.ok().build(); } /** - * Updates a plugin.
- *
- *
    - *
  • 200 success
  • - *
  • 500 internal server error
  • - *
+ * Updates a plugin. * * @param id id of the plugin to be updated * * @return */ @POST + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 500, condition = "internal server error") + }) @Path("update/{id}") public Response update(@PathParam("id") String id) { pluginManager.update(id); + // TODO should return 204 content + // consider to do an update with a put + return Response.ok().build(); } //~--- get methods ---------------------------------------------------------- /** - * Returns all plugins.
- *
- *
    - *
  • 200 success
  • - *
  • 500 internal server error
  • - *
+ * Returns all plugins. * * @return all plugins */ @GET + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 500, condition = "internal server error") + }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) public Collection getAll() { @@ -258,17 +263,16 @@ public class PluginResource } /** - * Returns all available plugins.
- *
- *
    - *
  • 200 success
  • - *
  • 500 internal server error
  • - *
+ * Returns all available plugins. * * @return all available plugins */ @GET @Path("available") + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 500, condition = "internal server error") + }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) public Collection getAvailable() { @@ -276,17 +280,16 @@ public class PluginResource } /** - * Returns all plugins which are available for update.
- *
- *
    - *
  • 200 success
  • - *
  • 500 internal server error
  • - *
+ * Returns all plugins which are available for update. * * @return all plugins which are available for update */ @GET @Path("updates") + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 500, condition = "internal server error") + }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) public Collection getAvailableUpdates() { @@ -294,17 +297,16 @@ public class PluginResource } /** - * Returns all installed plugins.
- *
- *
    - *
  • 200 success
  • - *
  • 500 internal server error
  • - *
+ * Returns all installed plugins. * * @return all installed plugins */ @GET @Path("installed") + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 500, condition = "internal server error") + }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) public Collection getInstalled() { @@ -312,17 +314,16 @@ public class PluginResource } /** - * Returns all plugins for the overview.
- *
- *
    - *
  • 200 success
  • - *
  • 500 internal server error
  • - *
+ * Returns all plugins for the overview. * * @return all plugins for the overview */ @GET @Path("overview") + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 500, condition = "internal server error") + }) @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) public Collection getOverview() {