diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f40686a7b..a715533bac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Added +- Added footer extension points for links and avatar +- Create OpenAPI specification during build - Extension point entries with supplied extensionName are sorted ascending +### Changed +- New footer design + ### Fixed - Modification for mercurial repositories with enabled XSRF protection +### Removed +- Enunciate rest documentation + ## 2.0.0-rc4 - 2020-02-14 ### Added - Support for Java versions > 8 diff --git a/Jenkinsfile b/Jenkinsfile index 43fece6567..8218808237 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -29,7 +29,7 @@ node('docker') { } stage('Build') { - mvn 'clean install -Pdoc -DskipTests' + mvn 'clean install -DskipTests' } stage('Unit Test') { @@ -67,7 +67,6 @@ node('docker') { stage('Archive') { archiveArtifacts 'scm-webapp/target/scm-webapp.war' archiveArtifacts 'scm-server/target/scm-server-app.*' - archiveArtifacts 'scm-webapp/target/scm-webapp-restdocs.zip' } stage('Docker') { diff --git a/pom.xml b/pom.xml index 522e440efb..41bd917705 100644 --- a/pom.xml +++ b/pom.xml @@ -184,12 +184,6 @@ true - - com.webcohesion.enunciate - enunciate-core-annotations - ${enunciate.version} - - org.mapstruct mapstruct-jdk8 @@ -266,6 +260,12 @@ ${jaxrs.version} + + io.swagger.core.v3 + swagger-annotations + 2.1.1 + + com.fasterxml.jackson.core jackson-core @@ -447,12 +447,6 @@ 2.3 - - com.webcohesion.enunciate - enunciate-maven-plugin - ${enunciate.version} - - sonia.scm.maven smp-maven-plugin @@ -465,6 +459,12 @@ 2.8.2 + + io.openapitools.swagger + swagger-maven-plugin + 2.1.2 + + @@ -831,7 +831,6 @@ 2.1.1 4.4.1.Final 1.19.4 - 2.11.1 2.10.0 4.0 2.3.0 diff --git a/scm-core/pom.xml b/scm-core/pom.xml index 563ac0f40f..ec3b884fd6 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -137,12 +137,6 @@ provided - - - com.webcohesion.enunciate - enunciate-core-annotations - - diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java similarity index 100% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index e6b2929bd2..57999aa7d0 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -61,6 +61,13 @@ provided + + + io.swagger.core.v3 + swagger-annotations + provided + + @@ -136,100 +143,37 @@ + + io.openapitools.swagger + swagger-maven-plugin + + + sonia.scm.api.v2.resources + + ${basedir}/target/classes/META-INF/scm + openapi + JSON,YAML + true + + + SCM-Manager Plugin REST-API + ${project.version} + + http://www.opensource.org/licenses/bsd-license.php + BSD + + + + + + + + generate + + + + - - - plugin-doc - - - - - - org.apache.maven.plugins - maven-resources-plugin - - - copy-enunciate-configuration - compile - - copy-resources - - - ${project.build.directory} - - - src/main/doc - true - - **/enunciate.xml - - - - - - - - - - com.webcohesion.enunciate - enunciate-maven-plugin - - - - docs - - compile - - - - ${project.build.directory}/enunciate.xml - ${project.build.directory} - restdocs - - - - com.webcohesion.enunciate - enunciate-top - ${enunciate.version} - - - com.webcohesion.enunciate - enunciate-swagger - - - - - com.webcohesion.enunciate - enunciate-lombok - ${enunciate.version} - - - - - - org.apache.maven.plugins - maven-assembly-plugin - - - src/main/doc/assembly.xml - - - - - package - - single - - - - - - - - - - - diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigResource.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigResource.java index 7cda4bc9d3..098396098f 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigResource.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigResource.java @@ -1,12 +1,16 @@ 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.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 sonia.scm.config.ConfigurationPermissions; import sonia.scm.repository.GitConfig; import sonia.scm.repository.GitRepositoryHandler; import sonia.scm.web.GitVndMediaType; +import sonia.scm.web.VndMediaType; import javax.inject.Inject; import javax.inject.Provider; @@ -14,13 +18,15 @@ import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; -import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Response; /** * RESTful Web Service Resource to manage the configuration of the git plugin. */ +@OpenAPIDefinition(tags = { + @Tag(name = "Git", description = "Configuration for the git repository type") +}) @Path(GitConfigResource.GIT_CONFIG_PATH_V2) public class GitConfigResource { @@ -45,13 +51,24 @@ public class GitConfigResource { @GET @Path("") @Produces(GitVndMediaType.GIT_CONFIG) - @TypeHint(GitConfigDto.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 \"configuration:read:git\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Operation(summary = "Git configuration", description = "Returns the global git configuration.", tags = "Git") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = GitVndMediaType.GIT_CONFIG, + schema = @Schema(implementation = GitConfigDto.class) + ) + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:read:git\" privilege") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response get() { GitConfig config = repositoryHandler.getConfig(); @@ -74,13 +91,20 @@ public class GitConfigResource { @PUT @Path("") @Consumes(GitVndMediaType.GIT_CONFIG) - @StatusCodes({ - @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"configuration:write:git\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) + @Operation(summary = "Modify git configuration", description = "Modifies the global git configuration.", tags = "Git") + @ApiResponse( + responseCode = "204", + description = "update success" + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:write:git\" privilege") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response update(GitConfigDto configDto) { GitConfig config = dtoToConfigMapper.map(configDto); @@ -94,7 +118,7 @@ public class GitConfigResource { } @Path("{namespace}/{name}") - public GitRepositoryConfigResource getRepositoryConfig(@PathParam("namespace") String namespace, @PathParam("name") String name) { + public GitRepositoryConfigResource getRepositoryConfig() { return gitRepositoryConfigResource.get(); } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitRepositoryConfigResource.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitRepositoryConfigResource.java index 175caf8840..af7eb23c63 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitRepositoryConfigResource.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitRepositoryConfigResource.java @@ -1,7 +1,9 @@ package sonia.scm.api.v2.resources; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; +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.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.repository.GitRepositoryConfig; @@ -11,6 +13,7 @@ import sonia.scm.repository.RepositoryManager; import sonia.scm.repository.RepositoryPermissions; import sonia.scm.store.ConfigurationStore; import sonia.scm.web.GitVndMediaType; +import sonia.scm.web.VndMediaType; import javax.inject.Inject; import javax.ws.rs.Consumes; @@ -42,13 +45,31 @@ public class GitRepositoryConfigResource { @GET @Path("/") @Produces(GitVndMediaType.GIT_REPOSITORY_CONFIG) - @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 config"), - @ResponseCode(code = 404, condition = "not found, no repository with the specified namespace and name available"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Operation(summary = "Git repository configuration", description = "Returns the repository related git configuration.", tags = "Git") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = GitVndMediaType.GIT_REPOSITORY_CONFIG, + schema = @Schema(implementation = GitRepositoryConfigDto.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 config") + @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", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response getRepositoryConfig(@PathParam("namespace") String namespace, @PathParam("name") String name) { Repository repository = getRepository(namespace, name); RepositoryPermissions.read(repository).check(); @@ -61,13 +82,27 @@ public class GitRepositoryConfigResource { @PUT @Path("/") @Consumes(GitVndMediaType.GIT_REPOSITORY_CONFIG) - @StatusCodes({ - @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the privilege to change this repositories config"), - @ResponseCode(code = 404, condition = "not found, no repository with the specified namespace and name available/name available"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Operation(summary = "Modifies git repository configuration", description = "Modifies the repository related git configuration.", tags = "Git") + @ApiResponse( + responseCode = "204", + description = "update success" + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the privilege to change this repositories config") + @ApiResponse( + responseCode = "404", + description = "not found, no repository with the specified namespace and name available/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 setRepositoryConfig(@PathParam("namespace") String namespace, @PathParam("name") String name, GitRepositoryConfigDto dto) { Repository repository = getRepository(namespace, name); RepositoryPermissions.custom("git", repository).check(); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResource.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResource.java index b265f2929d..3507c41da3 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResource.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResource.java @@ -1,13 +1,15 @@ 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.config.ConfigurationPermissions; import sonia.scm.repository.HgConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.web.HgVndMediaType; +import sonia.scm.web.VndMediaType; import javax.ws.rs.Consumes; import javax.ws.rs.PUT; @@ -31,13 +33,20 @@ public class HgConfigAutoConfigurationResource { */ @PUT @Path("") - @StatusCodes({ - @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"configuration:write:hg\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) + @Operation(summary = "Sets hg configuration and installs hg binary", description = "Sets the default mercurial config and installs the mercurial binary.", tags = "Mercurial") + @ApiResponse( + responseCode = "204", + description = "update success" + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:write:hg\" privilege") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response autoConfiguration() { return autoConfiguration(null); } @@ -50,13 +59,20 @@ public class HgConfigAutoConfigurationResource { @PUT @Path("") @Consumes(HgVndMediaType.CONFIG) - @StatusCodes({ - @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"configuration:write:hg\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) + @Operation(summary = "Modifies hg configuration and installs hg binary", description = "Modifies the mercurial config and installs the mercurial binary.", tags = "Mercurial") + @ApiResponse( + responseCode = "204", + description = "update success" + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:write:hg\" privilege") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response autoConfiguration(HgConfigDto configDto) { HgConfig config; diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInstallationsResource.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInstallationsResource.java index 8842d07569..795d0c87a6 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInstallationsResource.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInstallationsResource.java @@ -1,13 +1,15 @@ 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 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.config.ConfigurationPermissions; import sonia.scm.installer.HgInstallerFactory; import sonia.scm.repository.HgConfig; import sonia.scm.web.HgVndMediaType; +import sonia.scm.web.VndMediaType; import javax.inject.Inject; import javax.ws.rs.GET; @@ -31,13 +33,24 @@ public class HgConfigInstallationsResource { @GET @Path(PATH_HG) @Produces(HgVndMediaType.INSTALLATIONS) - @TypeHint(HalRepresentation.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 \"configuration:read:hg\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Operation(summary = "Hg installations", description = "Returns the mercurial installations.", tags = "Mercurial") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = HgVndMediaType.INSTALLATIONS, + schema = @Schema(implementation = HgConfigInstallationsDto.class) + ) + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:read:hg\" privilege") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public HalRepresentation getHgInstallations() { ConfigurationPermissions.read(HgConfig.PERMISSION).check(); @@ -52,13 +65,24 @@ public class HgConfigInstallationsResource { @GET @Path(PATH_PYTHON) @Produces(HgVndMediaType.INSTALLATIONS) - @TypeHint(HalRepresentation.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 \"configuration:read:hg\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Operation(summary = "Python installations", description = "Returns the python installations.", tags = "Mercurial") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = HgVndMediaType.INSTALLATIONS, + schema = @Schema(implementation = HgConfigInstallationsDto.class) + ) + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:read:hg\" privilege") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public HalRepresentation getPythonInstallations() { ConfigurationPermissions.read(HgConfig.PERMISSION).check(); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigPackageResource.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigPackageResource.java index 88a7de7ea0..2e185f152d 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigPackageResource.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigPackageResource.java @@ -1,9 +1,10 @@ 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 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.SCMContext; import sonia.scm.config.ConfigurationPermissions; import sonia.scm.installer.HgInstallerFactory; @@ -13,6 +14,7 @@ import sonia.scm.net.ahc.AdvancedHttpClient; import sonia.scm.repository.HgConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.web.HgVndMediaType; +import sonia.scm.web.VndMediaType; import javax.inject.Inject; import javax.ws.rs.GET; @@ -44,13 +46,20 @@ public class HgConfigPackageResource { @GET @Path("") @Produces(HgVndMediaType.PACKAGES) - @StatusCodes({ - @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"configuration:read:hg\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(HalRepresentation.class) + @Operation(summary = "Hg configuration packages", description = "Returns all mercurial packages.", tags = "Mercurial") + @ApiResponse( + responseCode = "204", + description = "update success" + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:read:hg\" privilege") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public HalRepresentation getPackages() { ConfigurationPermissions.read(HgConfig.PERMISSION).check(); @@ -65,14 +74,27 @@ public class HgConfigPackageResource { */ @PUT @Path("{pkgId}") - @StatusCodes({ - @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"configuration:write:hg\" privilege"), - @ResponseCode(code = 404, condition = "no package found for id"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) + @Operation(summary = "Modifies hg configuration package", description = "Installs a mercurial package.", tags = "Mercurial") + @ApiResponse( + responseCode = "204", + description = "update success" + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:write:hg\" privilege") + @ApiResponse( + responseCode = "404", + description = "no package found for id", + 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 installPackage(@PathParam("pkgId") String pkgId) { Response response; @@ -82,7 +104,7 @@ public class HgConfigPackageResource { if (pkg != null) { if (HgInstallerFactory.createInstaller() - .installPackage(client, handler, SCMContext.getContext().getBaseDirectory(), pkg)) { + .installPackage(client, handler, SCMContext.getContext().getBaseDirectory(), pkg)) { response = Response.noContent().build(); } else { response = Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigResource.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigResource.java index e6a8f01238..be534f4345 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigResource.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigResource.java @@ -1,12 +1,16 @@ 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.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 sonia.scm.config.ConfigurationPermissions; import sonia.scm.repository.HgConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.web.HgVndMediaType; +import sonia.scm.web.VndMediaType; import javax.inject.Inject; import javax.inject.Provider; @@ -20,11 +24,13 @@ import javax.ws.rs.core.Response; /** * RESTful Web Service Resource to manage the configuration of the hg plugin. */ +@OpenAPIDefinition(tags = { + @Tag(name = "Mercurial", description = "Configuration for the mercurial repository type") +}) @Path(HgConfigResource.HG_CONFIG_PATH_V2) public class HgConfigResource { static final String HG_CONFIG_PATH_V2 = "v2/config/hg"; - private final HgConfigDtoToHgConfigMapper dtoToConfigMapper; private final HgConfigToHgConfigDtoMapper configToDtoMapper; private final HgRepositoryHandler repositoryHandler; @@ -51,13 +57,24 @@ public class HgConfigResource { @GET @Path("") @Produces(HgVndMediaType.CONFIG) - @TypeHint(HgConfigDto.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 \"configuration:read:hg\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Operation(summary = "Hg configuration", description = "Returns the global mercurial configuration.", tags = "Mercurial") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = HgVndMediaType.CONFIG, + schema = @Schema(implementation = HgConfigDto.class) + ) + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:read:hg\" privilege") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response get() { ConfigurationPermissions.read(HgConfig.PERMISSION).check(); @@ -80,13 +97,20 @@ public class HgConfigResource { @PUT @Path("") @Consumes(HgVndMediaType.CONFIG) - @StatusCodes({ - @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"configuration:write:hg\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) + @Operation(summary = "Modify hg configuration", description = "Modifies the global mercurial configuration.", tags = "Mercurial") + @ApiResponse( + responseCode = "204", + description = "update success" + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:write:hg\" privilege") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response update(HgConfigDto configDto) { HgConfig config = dtoToConfigMapper.map(configDto); diff --git a/scm-plugins/scm-legacy-plugin/src/main/java/sonia/scm/legacy/LegacyRepositoryService.java b/scm-plugins/scm-legacy-plugin/src/main/java/sonia/scm/legacy/LegacyRepositoryService.java index d6b923a927..282a802e2e 100644 --- a/scm-plugins/scm-legacy-plugin/src/main/java/sonia/scm/legacy/LegacyRepositoryService.java +++ b/scm-plugins/scm-legacy-plugin/src/main/java/sonia/scm/legacy/LegacyRepositoryService.java @@ -1,8 +1,6 @@ package sonia.scm.legacy; import com.google.inject.Inject; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; import sonia.scm.NotFoundException; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryManager; @@ -26,12 +24,6 @@ public class LegacyRepositoryService { @GET @Path("{id}") @Produces(MediaType.APPLICATION_JSON) - @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:read:global\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) public NamespaceAndNameDto getNameAndNamespaceForRepositoryId(@PathParam("id") String repositoryId) { Repository repo = repositoryManager.get(repositoryId); if (repo == null) { diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigResource.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigResource.java index b12785dca9..9ff13ffb46 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigResource.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigResource.java @@ -1,12 +1,16 @@ 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.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 sonia.scm.config.ConfigurationPermissions; import sonia.scm.repository.SvnConfig; import sonia.scm.repository.SvnRepositoryHandler; import sonia.scm.web.SvnVndMediaType; +import sonia.scm.web.VndMediaType; import javax.inject.Inject; import javax.ws.rs.Consumes; @@ -19,6 +23,9 @@ import javax.ws.rs.core.Response; /** * RESTful Web Service Resource to manage the configuration of the svn plugin. */ +@OpenAPIDefinition(tags = { + @Tag(name = "Subversion", description = "Configuration for the subversion repository type") +}) @Path(SvnConfigResource.SVN_CONFIG_PATH_V2) public class SvnConfigResource { @@ -41,13 +48,24 @@ public class SvnConfigResource { @GET @Path("") @Produces(SvnVndMediaType.SVN_CONFIG) - @TypeHint(SvnConfigDto.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 \"configuration:read:svn\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) + @Operation(summary = "Svn configuration", description = "Returns the global subversion configuration.", tags = "Subversion") + @ApiResponse( + responseCode = "200", + description = "success", + content = @Content( + mediaType = SvnVndMediaType.SVN_CONFIG, + schema = @Schema(implementation = SvnConfigDto.class) + ) + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:read:svn\" privilege") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response get() { SvnConfig config = repositoryHandler.getConfig(); @@ -70,13 +88,20 @@ public class SvnConfigResource { @PUT @Path("") @Consumes(SvnVndMediaType.SVN_CONFIG) - @StatusCodes({ - @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), - @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"configuration:write:svn\" privilege"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) + @Operation(summary = "Modify svn configuration", description = "Modifies the global subversion configuration.", tags = "Subversion") + @ApiResponse( + responseCode = "204", + description = "update success" + ) + @ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials") + @ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:write:svn\" privilege") + @ApiResponse( + responseCode = "500", + description = "internal server error", + content = @Content( + mediaType = VndMediaType.ERROR_TYPE, + schema = @Schema(implementation = ErrorDto.class) + )) public Response update(SvnConfigDto configDto) { SvnConfig config = dtoToConfigMapper.map(configDto); diff --git a/scm-ui/ui-components/src/__resources__/hitchhiker.png b/scm-ui/ui-components/src/__resources__/hitchhiker.png new file mode 100644 index 0000000000..71632a3a51 Binary files /dev/null and b/scm-ui/ui-components/src/__resources__/hitchhiker.png differ diff --git a/scm-ui/ui-components/src/__resources__/marvin.jpg b/scm-ui/ui-components/src/__resources__/marvin.jpg new file mode 100644 index 0000000000..a98f6b09cd Binary files /dev/null and b/scm-ui/ui-components/src/__resources__/marvin.jpg differ diff --git a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap index 0ec9bb0c76..790c2cdc94 100644 --- a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap +++ b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap @@ -484,7 +484,7 @@ exports[`Storyshots DateFromNow Default 1`] = ` exports[`Storyshots Diff Binaries 1`] = `