From cbd86e8e5c584ce84560fed226758cffa93b927e Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Wed, 19 Feb 2020 16:51:20 +0100 Subject: [PATCH] Update rest resource annotations --- .../api/v2/resources/BranchRootResource.java | 123 ++++++++++----- .../v2/resources/ChangesetRootResource.java | 100 ++++++------ .../scm/api/v2/resources/GroupResource.java | 2 +- .../v2/resources/IncomingRootResource.java | 112 ++++++++++---- .../resources/ModificationsRootResource.java | 36 +++-- .../resources/NamespaceStrategyResource.java | 2 + .../RepositoryCollectionResource.java | 54 ++++--- .../RepositoryPermissionRootResource.java | 144 +++++++++++++----- .../api/v2/resources/RepositoryResource.java | 39 +++-- .../RepositoryRoleCollectionResource.java | 54 ++++--- .../v2/resources/RepositoryRoleResource.java | 74 +++++---- .../resources/RepositoryRoleRootResource.java | 6 + .../RepositoryTypeCollectionResource.java | 28 +++- .../v2/resources/RepositoryTypeResource.java | 31 ++-- .../v2/resources/RepositoryVerbResource.java | 26 +++- .../api/v2/resources/SourceRootResource.java | 10 +- .../scm/api/v2/resources/TagRootResource.java | 68 ++++++--- .../api/v2/resources/UIPluginResource.java | 57 +++++-- .../scm/api/v2/resources/UserResource.java | 6 +- 19 files changed, 653 insertions(+), 319 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java index 9e7353b4b7..1a41508e51 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java @@ -1,11 +1,12 @@ package sonia.scm.api.v2.resources; import com.google.common.base.Strings; -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 io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import sonia.scm.PageResult; import sonia.scm.repository.Branch; import sonia.scm.repository.Branches; @@ -69,15 +70,27 @@ public class BranchRootResource { @GET @Path("{branch}") @Produces(VndMediaType.BRANCH) - @TypeHint(BranchDto.class) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "branches not supported for given repository"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the branch"), - @ResponseCode(code = 404, condition = "not found, no branch with the specified name for the repository available or repository not found"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Operation(summary = "Get single branch", description = "Returns a branch for a repository.", tags = "Repository") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.BRANCH, + schema = @Schema(implementation = BranchDto.class) + ) + ) + @ApiResponse(responseCode = "400", description = "branches not supported for given repository") + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the branch") + @ApiResponse(responseCode = "404", description = "not found, no branch with the specified name for the repository available or repository not found") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + ) + ) public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("branch") String branchName) throws IOException { NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) { @@ -95,17 +108,29 @@ public class BranchRootResource { } } - @Path("{branch}/changesets/") @GET - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the changeset"), - @ResponseCode(code = 404, condition = "not found, no changesets available in the repository"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Path("{branch}/changesets/") @Produces(VndMediaType.CHANGESET_COLLECTION) - @TypeHint(CollectionDto.class) + @Operation(summary = "Collection of changesets", description = "Returns a collection of changesets for specific branch.", tags = "Repository") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.CHANGESET_COLLECTION, + schema = @Schema(implementation = CollectionDto.class) + ) + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the changeset") + @ApiResponse(responseCode = "404", description = "not found, no changesets available in the repository") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + ) + ) public Response history(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("branch") String branchName, @@ -143,14 +168,18 @@ public class BranchRootResource { @POST @Path("") @Consumes(VndMediaType.BRANCH_REQUEST) - @StatusCodes({ - @ResponseCode(code = 201, condition = "create success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"push\" privilege"), - @ResponseCode(code = 409, condition = "conflict, a user with this name already exists"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) + @Operation(summary = "Create branch", description = "Creates a new branch.", tags = "Repository") + @ApiResponse(responseCode = "201", description = "create success") + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"push\" privilege") + @ApiResponse(responseCode = "409", description = "conflict, a branch with this name already exists") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) @ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created branch")) public Response create(@PathParam("namespace") String namespace, @PathParam("name") String name, @@ -195,15 +224,33 @@ public class BranchRootResource { @GET @Path("") @Produces(VndMediaType.BRANCH_COLLECTION) - @TypeHint(CollectionDto.class) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "branches not supported for given repository"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"read repository\" privilege"), - @ResponseCode(code = 404, condition = "not found, no repository found for the given namespace and name"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Operation(summary = "List of branches", description = "Returns all branches for a repository.", tags = "Repository") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.BRANCH_COLLECTION, + schema = @Schema(implementation = CollectionDto.class) + ) + ) + @ApiResponse(responseCode = "400", description = "branches not supported for given repository") + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"read repository\" privilege") + @ApiResponse( + responseCode = "404", + description = "not found, no repository found for the given namespace and name", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + ) + ) + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name) throws IOException { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Branches branches = repositoryService.getBranchesCommand().getBranches(); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java index 3921839b76..1b1bc7b0a1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java @@ -1,11 +1,9 @@ package sonia.scm.api.v2.resources; -import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import sonia.scm.PageResult; import sonia.scm.repository.Changeset; @@ -45,58 +43,10 @@ public class ChangesetRootResource { this.changesetToChangesetDtoMapper = changesetToChangesetDtoMapper; } - @GET - @Path("{id}") - @Produces(VndMediaType.CHANGESET) - @Operation(summary = "Collection of changesets", description = "Returns a collection of changesets.", tags = "Repository") - @ApiResponse( - responseCode = "200", - description = "success", - content = @Content( - mediaType = VndMediaType.CHANGESET, - schema = @Schema(implementation = ChangesetDto.class) - ) - ) - @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") - @ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the changeset") - @ApiResponse( - responseCode = "404", - description = "not found, no changeset with the specified id is available in the repository", - content = @Content( - mediaType = VndMediaType.ERROR_TYPE, - schema = @Schema(implementation = ErrorDto.class) - )) - @ApiResponse( - responseCode = "500", - description = "internal server error", - content = @Content( - mediaType = VndMediaType.ERROR_TYPE, - schema = @Schema(implementation = ErrorDto.class) - )) - public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("id") String id) throws IOException { - try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { - Repository repository = repositoryService.getRepository(); - RepositoryPermissions.read(repository).check(); - ChangesetPagingResult changesets = repositoryService.getLogCommand() - .setStartChangeset(id) - .setEndChangeset(id) - .getChangesets(); - if (changesets != null && changesets.getChangesets() != null && !changesets.getChangesets().isEmpty()) { - Optional changeset = changesets.getChangesets().stream().filter(ch -> ch.getId().equals(id)).findFirst(); - if (!changeset.isPresent()) { - return Response.status(Response.Status.NOT_FOUND).build(); - } - return Response.ok(changesetToChangesetDtoMapper.map(changeset.get(), repository)).build(); - } else { - return Response.status(Response.Status.NOT_FOUND).build(); - } - } - } - @GET @Path("") @Produces(VndMediaType.CHANGESET_COLLECTION) - @Operation(summary = "Specific changeset", description = "Returns a specific changeset.", tags = "Repository") + @Operation(summary = "Collection of changesets", description = "Returns a collection of changesets.", tags = "Repository") @ApiResponse( responseCode = "200", description = "success", @@ -143,4 +93,52 @@ public class ChangesetRootResource { } } } + + @GET + @Path("{id}") + @Produces(VndMediaType.CHANGESET) + @Operation(summary = "Specific changeset", description = "Returns a specific changeset.", tags = "Repository") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.CHANGESET, + schema = @Schema(implementation = ChangesetDto.class) + ) + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the changeset") + @ApiResponse( + responseCode = "404", + description = "not found, no changeset with the specified id is available in the repository", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("id") String id) throws IOException { + try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { + Repository repository = repositoryService.getRepository(); + RepositoryPermissions.read(repository).check(); + ChangesetPagingResult changesets = repositoryService.getLogCommand() + .setStartChangeset(id) + .setEndChangeset(id) + .getChangesets(); + if (changesets != null && changesets.getChangesets() != null && !changesets.getChangesets().isEmpty()) { + Optional changeset = changesets.getChangesets().stream().filter(ch -> ch.getId().equals(id)).findFirst(); + if (!changeset.isPresent()) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + return Response.ok(changesetToChangesetDtoMapper.map(changeset.get(), repository)).build(); + } else { + return Response.status(Response.Status.NOT_FOUND).build(); + } + } + } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java index 510657c797..0e54d6855d 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java @@ -78,7 +78,7 @@ public class GroupResource { */ @DELETE @Path("") - @Operation(summary = "Delete group", description = "Deletes a group.", tags = "Group") + @Operation(summary = "Delete group", description = "Deletes the group with the given id.", tags = "Group") @ApiResponse(responseCode = "204", description = "delete success or nothing to delete") @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") @ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the group") diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IncomingRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IncomingRootResource.java index 1062e60c90..b07f52ecd8 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IncomingRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IncomingRootResource.java @@ -1,9 +1,10 @@ package sonia.scm.api.v2.resources; 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 io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import sonia.scm.PageResult; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; @@ -81,17 +82,34 @@ public class IncomingRootResource { * @return * @throws Exception */ - @Path("{source}/{target}/changesets") @GET - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the changeset"), - @ResponseCode(code = 404, condition = "not found, no changesets available in the repository"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Path("{source}/{target}/changesets") @Produces(VndMediaType.CHANGESET_COLLECTION) - @TypeHint(CollectionDto.class) + @Operation(summary = "Incoming changesets", description = "Get the incoming changesets from source to target", tags = "Repository") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.CHANGESET_COLLECTION, + schema = @Schema(implementation = CollectionDto.class) + ) + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the changeset") + @ApiResponse( + responseCode = "404", + description = "not found, no changesets available in the repository", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response incomingChangesets(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("source") String source, @@ -117,18 +135,34 @@ public class IncomingRootResource { } } - - @Path("{source}/{target}/diff") @GET - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the changeset"), - @ResponseCode(code = 404, condition = "not found, no changesets available in the repository"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Path("{source}/{target}/diff") @Produces(VndMediaType.DIFF) - @TypeHint(CollectionDto.class) + @Operation(summary = "Incoming diff", description = "Get the incoming diff from source to target", tags = "Repository") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.DIFF, + schema = @Schema(implementation = CollectionDto.class) + ) + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the changeset") + @ApiResponse( + responseCode = "404", + description = "not found, no changesets available in the repository", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response incomingDiff(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("source") String source, @@ -155,14 +189,32 @@ public class IncomingRootResource { @GET @Path("{source}/{target}/diff/parsed") @Produces(VndMediaType.DIFF_PARSED) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "Bad Request"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the diff"), - @ResponseCode(code = 404, condition = "not found, source or target branch for the repository not available or repository not found"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Operation(summary = "Incoming parsed diff", description = "Get the incoming parsed diff from source to target", tags = "Repository") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.DIFF_PARSED, + schema = @Schema(implementation = CollectionDto.class) + ) + ) + @ApiResponse(responseCode = "400", description = "bad request") + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the diff") + @ApiResponse( + responseCode = "404", + description = "not found, source or target branch for the repository not available or repository not found", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response incomingDiffParsed(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("source") String source, diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ModificationsRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ModificationsRootResource.java index eaf165cda1..50cdc0c617 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ModificationsRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ModificationsRootResource.java @@ -1,8 +1,9 @@ package sonia.scm.api.v2.resources; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; -import com.webcohesion.enunciate.metadata.rs.TypeHint; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import sonia.scm.repository.Modifications; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.api.RepositoryService; @@ -33,16 +34,27 @@ public class ModificationsRootResource { * file modifications are for example: Modified, Added or Removed. */ @GET - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the modifications"), - @ResponseCode(code = 404, condition = "not found, no changeset with the specified id is available in the repository"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces(VndMediaType.MODIFICATIONS) - @TypeHint(ModificationsDto.class) @Path("{revision}") + @Produces(VndMediaType.MODIFICATIONS) + @Operation(summary = "File modifications", description = "Get the file modifications related to a revision.", tags = "Repository") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.MODIFICATIONS, + schema = @Schema(implementation = ModificationsDto.class) + ) + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the modifications") + @ApiResponse( + responseCode = "404", + description = "not found, no changeset with the specified id is available in the repository", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + @ApiResponse(responseCode = "500", description = "internal server error") public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws IOException { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Modifications modifications = repositoryService.getModificationsCommand() diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/NamespaceStrategyResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/NamespaceStrategyResource.java index 7b87c612d7..f1d0a107ca 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/NamespaceStrategyResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/NamespaceStrategyResource.java @@ -1,6 +1,7 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.Links; +import io.swagger.v3.oas.annotations.Operation; import sonia.scm.repository.NamespaceStrategy; import sonia.scm.web.VndMediaType; @@ -42,6 +43,7 @@ public class NamespaceStrategyResource { @GET @Path("") @Produces(VndMediaType.NAMESPACE_STRATEGIES) + @Operation(summary = "List of namespace strategies", description = "Returns all available namespace strategies and the current selected.", tags = "Repository") public NamespaceStrategiesDto get(@Context UriInfo uriInfo) { String currentStrategy = strategyAsString(namespaceStrategyProvider.get()); List availableStrategies = collectStrategyNames(); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java index d27b598646..ed12dab1ee 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java @@ -1,10 +1,11 @@ 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 io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import org.apache.shiro.SecurityUtils; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryInitializer; @@ -63,13 +64,26 @@ public class RepositoryCollectionResource { @GET @Path("") @Produces(VndMediaType.REPOSITORY_COLLECTION) - @TypeHint(CollectionDto.class) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"repository\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Operation(summary = "List of repositories", description = "Returns all repositories for a given page number with a given page size.", tags = "Repository") + + + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.REPOSITORY_COLLECTION, + schema = @Schema(implementation = CollectionDto.class) + ) + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"repository\" privilege") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response getAll(@DefaultValue("0") @QueryParam("page") int page, @DefaultValue("" + DEFAULT_PAGE_SIZE) @QueryParam("pageSize") int pageSize, @QueryParam("sortBy") String sortBy, @@ -92,14 +106,18 @@ public class RepositoryCollectionResource { @POST @Path("") @Consumes(VndMediaType.REPOSITORY) - @StatusCodes({ - @ResponseCode(code = 201, condition = "create success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"repository\" privilege"), - @ResponseCode(code = 409, condition = "conflict, a repository with this name already exists"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) + @Operation(summary = "Create repository", description = "Creates a new repository.", tags = "Repository") + @ApiResponse(responseCode = "201", description = "create success") + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"repository\" privilege") + @ApiResponse(responseCode = "409", description = "conflict, a repository with this name already exists") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) @ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created repository")) public Response create(@Valid RepositoryDto repository, @QueryParam("initialize") boolean initialize) { AtomicReference reference = new AtomicReference<>(); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResource.java index b87a4911a5..1cebbba3c7 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResource.java @@ -1,9 +1,10 @@ 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.StatusCodes; -import com.webcohesion.enunciate.metadata.rs.TypeHint; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.extern.slf4j.Slf4j; import sonia.scm.AlreadyExistsException; import sonia.scm.NotFoundException; @@ -64,17 +65,30 @@ public class RepositoryPermissionRootResource { * @return a web response with the status code 201 and the url to GET the added permission */ @POST - @StatusCodes({ - @ResponseCode(code = 201, condition = "creates", additionalHeaders = { - @ResponseHeader(name = "Location", description = "uri of the created permission") - }), - @ResponseCode(code = 500, condition = "internal server error"), - @ResponseCode(code = 404, condition = "not found"), - @ResponseCode(code = 409, condition = "conflict") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Consumes(VndMediaType.REPOSITORY_PERMISSION) @Path("") + @Consumes(VndMediaType.REPOSITORY_PERMISSION) + @Operation(summary = "Create repository-specific permission", description = "Adds a new permission to the user or group managed by the repository.", tags = {"Repository", "Permissions"}) + @ApiResponse( + responseCode = "201", + description = "creates", + headers = @Header(name = "Location", description = "uri of the created permission") + ) + @ApiResponse( + responseCode = "404", + description = "not found", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + @ApiResponse(responseCode = "409", description = "conflict") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + ) + ) public Response create(@PathParam("namespace") String namespace, @PathParam("name") String name, @Valid RepositoryPermissionDto permission) { log.info("try to add new permission: {}", permission); Repository repository = load(namespace, name); @@ -95,14 +109,32 @@ public class RepositoryPermissionRootResource { * @throws NotFoundException if the repository does not exists */ @GET - @StatusCodes({ - @ResponseCode(code = 200, condition = "ok"), - @ResponseCode(code = 404, condition = "not found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces(VndMediaType.REPOSITORY_PERMISSION) - @TypeHint(RepositoryPermissionDto.class) @Path("{permission-name}") + @Produces(VndMediaType.REPOSITORY_PERMISSION) + @Operation(summary = "Get single repository-specific permission", description = "Get the searched permission with permission name related to a repository.", tags = {"Repository", "Permissions"}) + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.REPOSITORY_PERMISSION, + schema = @Schema(implementation = RepositoryPermissionDto.class) + ) + ) + @ApiResponse( + responseCode = "404", + description = "not found", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + ) + ) public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("permission-name") String permissionName) { Repository repository = load(namespace, name); RepositoryPermissions.permissionRead(repository).check(); @@ -125,14 +157,32 @@ public class RepositoryPermissionRootResource { * @throws NotFoundException if the repository does not exists */ @GET - @StatusCodes({ - @ResponseCode(code = 200, condition = "ok"), - @ResponseCode(code = 404, condition = "not found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces(VndMediaType.REPOSITORY_PERMISSION) - @TypeHint(RepositoryPermissionDto.class) @Path("") + @Produces(VndMediaType.REPOSITORY_PERMISSION) + @Operation(summary = "List of repository-specific permissions", description = "Get all permissions related to a repository.", tags = {"Repository", "Permissions"}) + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.REPOSITORY_PERMISSION, + schema = @Schema(implementation = RepositoryPermissionDto.class) + ) + ) + @ApiResponse( + responseCode = "404", + description = "not found", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + ) + ) public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name) { Repository repository = load(namespace, name); RepositoryPermissions.permissionRead(repository).check(); @@ -148,14 +198,19 @@ public class RepositoryPermissionRootResource { * @return a web response with the status code 204 */ @PUT - @StatusCodes({ - @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Consumes(VndMediaType.REPOSITORY_PERMISSION) @Path("{permission-name}") + @Consumes(VndMediaType.REPOSITORY_PERMISSION) + @Operation(summary = "Update repository-specific permission", description = "Update a permission to the user or group managed by the repository.", tags = {"Repository", "Permissions"}) + @ApiResponse(responseCode = "204", description = "update success") + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + ) + ) public Response update(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("permission-name") String permissionName, @@ -194,14 +249,19 @@ public class RepositoryPermissionRootResource { * @return a web response with the status code 204 */ @DELETE - @StatusCodes({ - @ResponseCode(code = 204, condition = "delete success or nothing to delete"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) @Path("{permission-name}") + @Operation(summary = "Delete repository-specific permission", description = "Delete a permission with the given name.", tags = {"Repository", "Permissions"}) + @ApiResponse(responseCode = "204", description = "delete success or nothing to delete") + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + ) + ) public Response delete(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("permission-name") String permissionName) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java index 8eb1c204a7..ed29ac9b52 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java @@ -1,8 +1,5 @@ package sonia.scm.api.v2.resources; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; -import com.webcohesion.enunciate.metadata.rs.TypeHint; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -91,7 +88,7 @@ public class RepositoryResource { @GET @Path("") @Produces(VndMediaType.REPOSITORY) - @Operation(summary = "Returns a single repository", description = "Returns the repository for the given namespace and name.", tags = "Repository") + @Operation(summary = "Get single repository", description = "Returns the repository for the given namespace and name.", tags = "Repository") @ApiResponse( responseCode = "200", description = "success", @@ -139,13 +136,11 @@ public class RepositoryResource { */ @DELETE @Path("") - @StatusCodes({ - @ResponseCode(code = 204, condition = "delete success or nothing to delete"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"repository\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) + @Operation(summary = "Delete repository", description = "Deletes the repository with the given namespace and name.", tags = "Repository") + @ApiResponse(responseCode = "204", description = "delete success or nothing to delete") + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"repository\" privilege") + @ApiResponse(responseCode = "500", description = "internal server error") public Response delete(@PathParam("namespace") String namespace, @PathParam("name") String name) { return adapter.delete(loadBy(namespace, name)); } @@ -162,15 +157,19 @@ public class RepositoryResource { @PUT @Path("") @Consumes(VndMediaType.REPOSITORY) - @StatusCodes({ - @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 400, condition = "invalid body, e.g. illegal change of namespace or name"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"repository\" privilege"), - @ResponseCode(code = 404, condition = "not found, no repository with the specified namespace and name available"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) + @Operation(summary = "Update repository", description = "Modifies the repository for the given namespace and name.", tags = "Repository") + @ApiResponse(responseCode = "204", description = "update success") + @ApiResponse(responseCode = "400", description = "invalid body, e.g. illegal change of namespace or name") + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"repository\" privilege") + @ApiResponse( + responseCode = "404", + description = "not found, no repository with the specified namespace and name available", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + @ApiResponse(responseCode = "500", description = "internal server error") public Response update(@PathParam("namespace") String namespace, @PathParam("name") String name, @Valid RepositoryDto repository) { return adapter.update( loadBy(namespace, name), diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRoleCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRoleCollectionResource.java index 99d8672eec..60f39f774f 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRoleCollectionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRoleCollectionResource.java @@ -1,10 +1,11 @@ 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 io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import sonia.scm.repository.RepositoryRole; import sonia.scm.repository.RepositoryRoleManager; import sonia.scm.web.VndMediaType; @@ -51,14 +52,25 @@ public class RepositoryRoleCollectionResource { @GET @Path("") @Produces(VndMediaType.REPOSITORY_ROLE_COLLECTION) - @TypeHint(CollectionDto.class) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "\"sortBy\" field unknown"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current repositoryRole does not have the \"repositoryRole\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Operation(summary = "List of repository roles", description = "Returns all repository roles for a given page number with a given page size.", tags = "Repository role") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.REPOSITORY_ROLE_COLLECTION, + schema = @Schema(implementation = CollectionDto.class) + ) + ) + @ApiResponse(responseCode = "400", description = "\"sortBy\" field unknown") + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current repositoryRole does not have the \"repositoryRole\" privilege") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response getAll(@DefaultValue("0") @QueryParam("page") int page, @DefaultValue("" + DEFAULT_PAGE_SIZE) @QueryParam("pageSize") int pageSize, @QueryParam("sortBy") String sortBy, @@ -79,14 +91,18 @@ public class RepositoryRoleCollectionResource { @POST @Path("") @Consumes(VndMediaType.REPOSITORY_ROLE) - @StatusCodes({ - @ResponseCode(code = 201, condition = "create success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"repositoryRole\" privilege"), - @ResponseCode(code = 409, condition = "conflict, a repository role with this name already exists"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) + @Operation(summary = "Create repository role", description = "Creates a new repository role.", tags = "Repository role") + @ApiResponse(responseCode = "201", description = "create success") + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"repositoryRole\" privilege") + @ApiResponse(responseCode = "409", description = "conflict, a repository role with this name already exists") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) @ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created repositoryRole")) public Response create(@Valid RepositoryRoleDto repositoryRole) { return adapter.create(repositoryRole, () -> dtoToRepositoryRoleMapper.map(repositoryRole), u -> resourceLinks.repositoryRole().self(u.getName())); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRoleResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRoleResource.java index 3a85fb5377..7f38eaa0c2 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRoleResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRoleResource.java @@ -1,8 +1,9 @@ package sonia.scm.api.v2.resources; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; -import com.webcohesion.enunciate.metadata.rs.TypeHint; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import sonia.scm.repository.RepositoryRole; import sonia.scm.repository.RepositoryRoleManager; import sonia.scm.web.VndMediaType; @@ -45,14 +46,31 @@ public class RepositoryRoleResource { @GET @Path("") @Produces(VndMediaType.REPOSITORY_ROLE) - @TypeHint(RepositoryRoleDto.class) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the repository role"), - @ResponseCode(code = 404, condition = "not found, no repository role with the specified name available"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Operation(summary = "Get single repository role", description = "Returns the repository role for the given name.", tags = "Repository role") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.REPOSITORY_ROLE, + schema = @Schema(implementation = RepositoryRoleDto.class) + ) + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the repository role") + @ApiResponse( + responseCode = "404", + description = "not found, no repository role with the specified name available", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response get(@PathParam("name") String name) { return adapter.get(name, repositoryRoleToDtoMapper::map); } @@ -66,13 +84,11 @@ public class RepositoryRoleResource { */ @DELETE @Path("") - @StatusCodes({ - @ResponseCode(code = 204, condition = "delete success or nothing to delete"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"repositoryRole\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) + @Operation(summary = "Delete repository role", description = "Deletes the repository role with the given name.", tags = "Repository role") + @ApiResponse(responseCode = "204", description = "delete success or nothing to delete") + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"repositoryRole\" privilege") + @ApiResponse(responseCode = "500", description = "internal server error") public Response delete(@PathParam("name") String name) { return adapter.delete(name); } @@ -88,15 +104,19 @@ public class RepositoryRoleResource { @PUT @Path("") @Consumes(VndMediaType.REPOSITORY_ROLE) - @StatusCodes({ - @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 400, condition = "invalid body, e.g. illegal change of repository role name"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"repositoryRole\" privilege"), - @ResponseCode(code = 404, condition = "not found, no repository role with the specified name available"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) + @Operation(summary = "Update repository role", description = "Modifies the repository role for the given name.", tags = "Repository role") + @ApiResponse(responseCode = "204", description = "update success") + @ApiResponse(responseCode = "400", description = "invalid body, e.g. illegal change of repository role name") + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"repositoryRole\" privilege") + @ApiResponse( + responseCode = "404", + description = "not found, no repository role with the specified name available", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + @ApiResponse(responseCode = "500", description = "internal server error") public Response update(@PathParam("name") String name, @Valid RepositoryRoleDto repositoryRole) { return adapter.update(name, existing -> dtoToRepositoryRoleMapper.map(repositoryRole)); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRoleRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRoleRootResource.java index 44e3a10fba..8d47963778 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRoleRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryRoleRootResource.java @@ -1,5 +1,8 @@ package sonia.scm.api.v2.resources; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.tags.Tag; + import javax.inject.Inject; import javax.inject.Provider; import javax.ws.rs.Path; @@ -7,6 +10,9 @@ import javax.ws.rs.Path; /** * RESTful web service resource to manage repository roles. */ +@OpenAPIDefinition(tags = { + @Tag(name = "Repository role", description = "Repository role related endpoints") +}) @Path(RepositoryRoleRootResource.REPOSITORY_ROLES_PATH_V2) public class RepositoryRoleRootResource { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryTypeCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryTypeCollectionResource.java index 4f9e76a939..8f33381497 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryTypeCollectionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryTypeCollectionResource.java @@ -1,8 +1,10 @@ package sonia.scm.api.v2.resources; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; import de.otto.edison.hal.HalRepresentation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import sonia.scm.repository.RepositoryManager; import sonia.scm.web.VndMediaType; @@ -24,11 +26,25 @@ public class RepositoryTypeCollectionResource { @GET @Path("") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) @Produces(VndMediaType.REPOSITORY_TYPE_COLLECTION) + @Operation(summary = "List of repository types", description = "Returns all repository types.", tags = "Repository") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.REPOSITORY_TYPE_COLLECTION, + schema = @Schema(implementation = HalRepresentation.class) + ) + ) + @ApiResponse(responseCode = "400", description = "\"sortBy\" field unknown") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + ) + ) public HalRepresentation getAll() { return mapper.map(repositoryManager.getConfiguredTypes()); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryTypeResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryTypeResource.java index 3a47fdf6c6..fa1c5b66d3 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryTypeResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryTypeResource.java @@ -1,8 +1,9 @@ package sonia.scm.api.v2.resources; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; -import com.webcohesion.enunciate.metadata.rs.TypeHint; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import sonia.scm.repository.RepositoryManager; import sonia.scm.repository.RepositoryType; import sonia.scm.web.VndMediaType; @@ -35,12 +36,24 @@ public class RepositoryTypeResource { @GET @Path("") @Produces(VndMediaType.REPOSITORY_TYPE) - @TypeHint(RepositoryTypeDto.class) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 404, condition = "not found, no repository type with the specified name available"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Operation(summary = "Get single repository type", description = "Returns the specified repository type for the given name.", tags = "Repository") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.REPOSITORY_TYPE, + schema = @Schema(implementation = RepositoryTypeDto.class) + ) + ) + @ApiResponse(responseCode = "404", description = "not found, no repository type with the specified name available") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + ) + ) public Response get(@PathParam("name") String name) { for (RepositoryType type : repositoryManager.getConfiguredTypes()) { if (name.equalsIgnoreCase(type.getName())) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryVerbResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryVerbResource.java index 4d4de067e5..0fde9d2911 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryVerbResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryVerbResource.java @@ -1,8 +1,10 @@ package sonia.scm.api.v2.resources; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; import de.otto.edison.hal.Links; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import sonia.scm.security.RepositoryPermissionProvider; import sonia.scm.web.VndMediaType; @@ -30,11 +32,23 @@ public class RepositoryVerbResource { @GET @Path("") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) @Produces(VndMediaType.REPOSITORY_VERB_COLLECTION) + @Operation(summary = "List of repository verbs", description = "Returns all repository-specific permissions.", hidden = true) + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.REPOSITORY_VERB_COLLECTION, + schema = @Schema(implementation = RepositoryVerbsDto.class) + ) + ) + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public RepositoryVerbsDto getAll() { return new RepositoryVerbsDto( Links.linkingTo().self(resourceLinks.repositoryVerbs().self()).build(), diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java index 758afd7660..dfd7dc8b5b 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java @@ -1,5 +1,6 @@ package sonia.scm.api.v2.resources; +import io.swagger.v3.oas.annotations.Operation; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.api.BrowseCommandBuilder; @@ -32,22 +33,25 @@ public class SourceRootResource { } @GET - @Produces(VndMediaType.SOURCE) @Path("") + @Produces(VndMediaType.SOURCE) + @Operation(summary = "List of sources", description = "Returns all sources for repository head.", tags = "Repository") public Response getAllWithoutRevision(@PathParam("namespace") String namespace, @PathParam("name") String name) throws IOException { return getSource(namespace, name, "/", null); } @GET - @Produces(VndMediaType.SOURCE) @Path("{revision}") + @Produces(VndMediaType.SOURCE) + @Operation(summary = "List of sources by revision", description = "Returns all sources for the given revision.", tags = "Repository") public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws IOException { return getSource(namespace, name, "/", revision); } @GET - @Produces(VndMediaType.SOURCE) @Path("{revision}/{path: .*}") + @Produces(VndMediaType.SOURCE) + @Operation(summary = "List of sources by revision in path", description = "Returns all sources for the given revision in a specific path.", tags = "Repository") public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) throws IOException { return getSource(namespace, name, path, revision); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java index 7acd59e3e1..9294128fee 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java @@ -1,8 +1,9 @@ package sonia.scm.api.v2.resources; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; -import com.webcohesion.enunciate.metadata.rs.TypeHint; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import sonia.scm.NotFoundException; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; @@ -39,17 +40,27 @@ public class TagRootResource { this.tagToTagDtoMapper = tagToTagDtoMapper; } - @GET @Path("") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the tags"), - @ResponseCode(code = 500, condition = "internal server error") - }) @Produces(VndMediaType.TAG_COLLECTION) - @TypeHint(CollectionDto.class) + @Operation(summary = "List of tags", description = "Returns the tags for the given namespace and name.", tags = "Repository") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.TAG_COLLECTION, + schema = @Schema(implementation = CollectionDto.class) + ) + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the tags") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name) throws IOException { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Tags tags = getTags(repositoryService); @@ -65,16 +76,33 @@ public class TagRootResource { @GET - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the tags"), - @ResponseCode(code = 404, condition = "not found, no tag available in the repository"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces(VndMediaType.TAG) - @TypeHint(TagDto.class) @Path("{tagName}") + @Produces(VndMediaType.TAG) + @Operation(summary = "Get tag", description = "Returns the tag for the given name.", tags = "Repository") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.TAG, + schema = @Schema(implementation = TagDto.class) + ) + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the tags") + @ApiResponse( + responseCode = "404", + description = "not found, no tag with the specified name available in the repository", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("tagName") String tagName) throws IOException { NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java index 1c779653a0..4efb0c6446 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java @@ -1,8 +1,9 @@ package sonia.scm.api.v2.resources; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; -import com.webcohesion.enunciate.metadata.rs.TypeHint; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.InstalledPlugin; import sonia.scm.security.AllowAnonymousAccess; @@ -39,12 +40,23 @@ public class UIPluginResource { */ @GET @Path("") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(CollectionDto.class) @Produces(VndMediaType.UI_PLUGIN_COLLECTION) + @Operation(summary = "Collection of ui plugin bundles", description = "Returns a collection of installed plugins and their ui bundles.", hidden = true) + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.UI_PLUGIN_COLLECTION, + schema = @Schema(implementation = CollectionDto.class) + ) + ) + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response getInstalledPlugins() { List plugins = pluginLoader.getInstalledPlugins() .stream() @@ -63,13 +75,30 @@ public class UIPluginResource { */ @GET @Path("{id}") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 404, condition = "not found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(UIPluginDto.class) @Produces(VndMediaType.UI_PLUGIN) + @Operation(summary = "Get single ui plugin bundle", description = "Returns the installed plugin with the given id.", hidden = true) + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = VndMediaType.UI_PLUGIN, + schema = @Schema(implementation = UIPluginDto.class) + ) + ) + @ApiResponse( + responseCode = "404", + description = "not found", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response getInstalledPlugin(@PathParam("id") String id) { Optional uiPluginDto = pluginLoader.getInstalledPlugins() .stream() 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 016eb27430..e4dfe6e633 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 @@ -54,7 +54,7 @@ public class UserResource { @GET @Path("") @Produces(VndMediaType.USER) - @Operation(summary = "Returns a single user", description = "Returns the user for the given id.", tags = "User") + @Operation(summary = "Get single user", description = "Returns the user for the given id.", tags = "User") @ApiResponse( responseCode = "200", description = "success", @@ -92,7 +92,7 @@ public class UserResource { */ @DELETE @Path("") - @Operation(summary = "Deletes a user", description = "Deletes the user for the given id.", tags = "User") + @Operation(summary = "Delete user", description = "Deletes the user with the given id.", tags = "User") @ApiResponse(responseCode = "204", description = "delete success or nothing to delete") @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"user\" privilege") @@ -113,7 +113,7 @@ public class UserResource { @PUT @Path("") @Consumes(VndMediaType.USER) - @Operation(summary = "Modifies a user", description = "Modifies the user for the given id.", tags = "User") + @Operation(summary = "Update user", description = "Modifies the user for the given id.", tags = "User") @ApiResponse(responseCode = "204", description = "update success") @ApiResponse(responseCode = "400", description = "invalid body, e.g. illegal change of id/user name") @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")