]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-18484 Documentation for SonarQube Web API V2 (OpenApi/Swagger)
authorAurelien Poscia <aurelien.poscia@sonarsource.com>
Wed, 15 Feb 2023 10:48:25 +0000 (11:48 +0100)
committersonartech <sonartech@sonarsource.com>
Tue, 21 Feb 2023 12:02:56 +0000 (12:02 +0000)
build.gradle
server/sonar-web/public/WEB-INF/app-content.xml [deleted file]
server/sonar-webserver-webapi-v2/build.gradle
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/LogComponent.java [deleted file]
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/common/RestResponseEntityExceptionHandler.java
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/CommonWebConfig.java
server/sonar-webserver-webapi-v2/src/main/resources/springdoc.properties [new file with mode: 0644]
server/sonar-webserver-webapi/build.gradle
server/sonar-webserver/src/main/java/org/sonar/server/platform/PlatformImpl.java
sonar-application/build.gradle

index c08ad9fc4df406004015271c2e97e932cf6c27f9..83e41aa32f3cd10b068a9d4b932c67dedfbd8aa0 100644 (file)
@@ -158,6 +158,7 @@ subprojects {
 
   ext {
     protobufVersion = '3.21.12'
+    springVersion = '5.3.23'
   }
 
   sonar {
@@ -329,6 +330,7 @@ subprojects {
       dependency('org.mockito:mockito-core:5.0.0') {
         exclude 'org.hamcrest:hamcrest-core'
       }
+      dependency "org.springframework:spring-test:${springVersion}"
       dependency 'org.mybatis:mybatis:3.5.11'
       dependencySet(group: 'org.slf4j', version: '2.0.6') {
         entry 'jcl-over-slf4j'
@@ -341,9 +343,13 @@ subprojects {
       dependency 'org.simpleframework:simple:5.1.6'
       dependency 'org.sonarsource.orchestrator:sonar-orchestrator:3.40.0.183'
       dependency 'org.sonarsource.update-center:sonar-update-center-common:1.29.0.1000'
-      dependency('org.springframework:spring-context:5.3.23') {
+      dependency("org.springframework:spring-context:${springVersion}") {
         exclude 'commons-logging:commons-logging'
       }
+      dependency ("org.springframework:spring-webmvc:${springVersion}") {
+          exclude 'commons-logging:commons-logging'
+      }
+      dependency 'org.springdoc:springdoc-openapi-ui:1.6.14'
       dependency 'org.subethamail:subethasmtp:3.1.7'
       dependency 'org.yaml:snakeyaml:1.33'
 
diff --git a/server/sonar-web/public/WEB-INF/app-content.xml b/server/sonar-web/public/WEB-INF/app-content.xml
deleted file mode 100644 (file)
index 6593bb2..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xmlns:context="http://www.springframework.org/schema/context"
-       xmlns:mvc="http://www.springframework.org/schema/mvc"
-       xsi:schemaLocation="http://www.springframework.org/schema/beans
-
-    http://www.springframework.org/schema/beans/spring-beans.xsd
-    http://www.springframework.org/schema/context
-    http://www.springframework.org/schema/context/spring-context.xsd
-    http://www.springframework.org/schema/mvc
-    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
-
-  <context:annotation-config/>
-  <context:component-scan base-package="org.sonar.server.v2"/>
-  <mvc:annotation-driven />
-</beans>
index 0c41cadea535cb48dfa0bb8297c9a5ff876c9fc7..6366dad07d7cbfd237363536e7d5876c969865a1 100644 (file)
@@ -6,14 +6,16 @@ sonarqube {
 
 dependencies {
     // please keep the list grouped by configuration and ordered by name
-    api 'org.springframework:spring-webmvc:5.3.23'
+    api 'org.springdoc:springdoc-openapi-ui'
+    api 'org.springframework:spring-webmvc'
 
     api project(':server:sonar-db-dao')
     // We are not suppose to have a v1 dependency. The ideal would be to have another common module between webapi and webapi-v2 but that needs a lot of refactoring.
     api project(':server:sonar-webserver-webapi')
 
+
     testImplementation 'org.mockito:mockito-core'
-    testImplementation 'org.springframework:spring-test:5.3.23'
+    testImplementation 'org.springframework:spring-test'
 
     testImplementation testFixtures(project(':server:sonar-server-common'))
 
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/LogComponent.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/LogComponent.java
deleted file mode 100644 (file)
index 0fdaf56..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.v2;
-
-import java.util.List;
-import java.util.stream.Collectors;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationListener;
-import org.springframework.context.event.ContextRefreshedEvent;
-import org.springframework.stereotype.Component;
-import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
-import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
-
-@Component
-public class LogComponent implements ApplicationListener<ContextRefreshedEvent> {
-
-  private static final Logger LOGGER = Loggers.get(LogComponent.class);
-  @Override
-  public void onApplicationEvent(ContextRefreshedEvent event) {
-    ApplicationContext applicationContext = event.getApplicationContext();
-    List<Integer> isUseless = applicationContext.getBeansOfType(RequestMappingHandlerMapping.class).values().stream()
-      .map(AbstractHandlerMethodMapping::getHandlerMethods)
-      .map(d->{
-        d.forEach((e, c)-> LOGGER.info("Registered endpoint: "+e.getName()+" "+e.getDirectPaths()+" "+e));
-        return 1;
-      })
-      .collect(Collectors.toList());
-  }
-}
index 170914c5ec2be8b4b6865cefb04d8f145d81d4bc..d62cc5c6996ca92686b1b5404600d579d91d3af9 100644 (file)
 package org.sonar.server.v2.common;
 
 import java.util.Optional;
+import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.ServerException;
+import org.sonar.server.exceptions.UnauthorizedException;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
 import org.springframework.web.bind.annotation.RestControllerAdvice;
 
 @RestControllerAdvice
 public class RestResponseEntityExceptionHandler {
 
+  @ExceptionHandler(IllegalStateException.class)
+  @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+  protected ResponseEntity<Object> handleIllegalStateException(IllegalStateException illegalStateException) {
+    return new ResponseEntity<>(illegalStateException.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
+  }
+
+  @ExceptionHandler(ForbiddenException.class)
+  @ResponseStatus(HttpStatus.FORBIDDEN)
+  protected ResponseEntity<Object> handleForbiddenException(ForbiddenException forbiddenException) {
+    return handleServerException(forbiddenException);
+  }
+
+  @ExceptionHandler(UnauthorizedException.class)
+  @ResponseStatus(HttpStatus.UNAUTHORIZED)
+  protected ResponseEntity<Object> handleUnauthorizedException(UnauthorizedException unauthorizedException) {
+    return handleServerException(unauthorizedException);
+  }
+
+
   @ExceptionHandler(ServerException.class)
   protected ResponseEntity<Object> handleServerException(ServerException serverException) {
     return new ResponseEntity<>(serverException.getMessage(), Optional.ofNullable(HttpStatus.resolve(serverException.httpCode())).orElse(HttpStatus.INTERNAL_SERVER_ERROR));
   }
-
 }
index 23a1fdc678a0bb29b700c61c0de59af670416805..34c77693a300d0815a7f16776a239325e3cb0293 100644 (file)
  */
 package org.sonar.server.v2.config;
 
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
 import org.sonar.server.v2.common.RestResponseEntityExceptionHandler;
 import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
 import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 
 @Configuration
 @EnableWebMvc
+@ComponentScan(basePackages = {"org.springdoc"})
+@PropertySource("classpath:springdoc.properties")
 public class CommonWebConfig {
 
   @Bean
@@ -33,4 +39,15 @@ public class CommonWebConfig {
     return new RestResponseEntityExceptionHandler();
   }
 
+  @Bean
+  public OpenAPI customOpenAPI() {
+    return new OpenAPI()
+      .info(
+        new Info()
+          .title("SonarQube Web API")
+          .version("0.0.1 alpha")
+          .description("Documentation of SonarQube Web API")
+      );
+  }
+
 }
diff --git a/server/sonar-webserver-webapi-v2/src/main/resources/springdoc.properties b/server/sonar-webserver-webapi-v2/src/main/resources/springdoc.properties
new file mode 100644 (file)
index 0000000..1f51a43
--- /dev/null
@@ -0,0 +1 @@
+springdoc.api-docs.path=/api-docs
index b891af08f70a58ab46c088f26b5f4d662d8dd18f..2dcc84a9f29d15d21be458705eb597bb2a62b85e 100644 (file)
@@ -13,12 +13,6 @@ dependencies {
   api 'io.prometheus:simpleclient_common'
   api 'io.prometheus:simpleclient_servlet'
 
-  api 'org.springframework:spring-webmvc:5.3.23'
-  api 'org.springframework:spring-web:5.3.23'
-  api 'org.springframework:spring-context:5.3.23'
-  api 'org.springframework:spring-core:5.3.23'
-  testImplementation 'org.springframework:spring-test:5.3.23'
-
   api project(':server:sonar-ce-common')
   api project(':server:sonar-ce-task')
   api project(':server:sonar-db-dao')
index a89de3719d8c47bec3dd3de72acf169b4230593f..5cfd89446b9db43b869f8691efac892676bb9eae 100644 (file)
@@ -31,7 +31,6 @@ import org.sonar.api.utils.log.Loggers;
 import org.sonar.api.utils.log.Profiler;
 import org.sonar.core.platform.ExtensionContainer;
 import org.sonar.core.platform.SpringComponentContainer;
-import org.sonar.server.platform.web.ApiV2Servlet;
 import org.sonar.server.app.ProcessCommandWrapper;
 import org.sonar.server.platform.db.migration.version.DatabaseVersion;
 import org.sonar.server.platform.platformlevel.PlatformLevel;
@@ -41,7 +40,7 @@ import org.sonar.server.platform.platformlevel.PlatformLevel3;
 import org.sonar.server.platform.platformlevel.PlatformLevel4;
 import org.sonar.server.platform.platformlevel.PlatformLevelSafeMode;
 import org.sonar.server.platform.platformlevel.PlatformLevelStartup;
-import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+import org.sonar.server.platform.web.ApiV2Servlet;
 
 import static org.sonar.process.ProcessId.WEB_SERVER;
 
@@ -63,12 +62,11 @@ public class PlatformImpl implements Platform {
   private PlatformLevel level3 = null;
   private PlatformLevel level4 = null;
   private PlatformLevel currentLevel = null;
-  private AnnotationConfigWebApplicationContext springMvcContext = null;
   private boolean dbConnected = false;
   private boolean started = false;
   private final List<Object> level4AddedComponents = new ArrayList<>();
   private final Profiler profiler = Profiler.createIfTrace(Loggers.get(PlatformImpl.class));
-  private ApiV2Servlet servlet;
+  private ApiV2Servlet servlet = null;
 
   public static PlatformImpl getInstance() {
     return INSTANCE;
index 94defd62f61d86197621a4fb1c69ed4dbd99ff57..6a9d0c34e756077b1f3fa57f51dc92894a218694 100644 (file)
@@ -43,7 +43,7 @@ jar.enabled = false
 shadowJar {
   archiveBaseName = 'sonar-application'
   archiveClassifier = null
-  mergeServiceFiles('META-INF/spring.*')
+  mergeServiceFiles()
   manifest {
     attributes('Main-Class': 'org.sonar.application.App')
   }
@@ -309,7 +309,7 @@ task zip(type: Zip, dependsOn: [configurations.compileClasspath, downloadElastic
 // Check the size of the archive
 zip.doLast {
   def minLength = 320000000
-  def maxLength = 345000000
+  def maxLength = 355000000
 
   def length = archiveFile.get().asFile.length()
   if (length < minLength)