]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22017 Add versionEOL to GlobalAction
authorOrlovAlexander <35396155+OrlovAlexander85@users.noreply.github.com>
Tue, 9 Apr 2024 11:35:47 +0000 (13:35 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 10 Apr 2024 20:02:56 +0000 (20:02 +0000)
server/sonar-webserver-webapi/src/main/java/org/sonar/server/ui/ws/GlobalAction.java
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/ui/ws/global-example.json
server/sonar-webserver-webapi/src/test/java/org/sonar/server/ui/ws/GlobalActionTest.java
sonar-plugin-api-impl/build.gradle
sonar-plugin-api-impl/src/main/java/org/sonar/api/internal/MetadataLoader.java
sonar-plugin-api-impl/src/test/java/org/sonar/api/internal/MetadataLoaderTest.java

index aa7ab0590a2c9b68fc0a36c5acffd5e508ef80b3..5ac651b5469c21ac5b93fcfa02b3d73252efefb6 100644 (file)
@@ -28,9 +28,11 @@ import org.sonar.api.config.Configuration;
 import org.sonar.api.platform.Server;
 import org.sonar.api.resources.ResourceType;
 import org.sonar.api.resources.ResourceTypes;
+import org.sonar.api.server.ws.Change;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService.NewController;
+import org.sonar.api.utils.System2;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.api.web.page.Page;
 import org.sonar.core.documentation.DocumentationLinkGenerator;
@@ -48,6 +50,7 @@ import org.sonar.server.user.UserSession;
 
 import static org.sonar.api.CoreProperties.DEVELOPER_AGGREGATED_INFO_DISABLED;
 import static org.sonar.api.CoreProperties.RATING_GRID;
+import static org.sonar.api.internal.MetadataLoader.loadSqVersionEol;
 import static org.sonar.core.config.WebConstants.SONAR_LF_ENABLE_GRAVATAR;
 import static org.sonar.core.config.WebConstants.SONAR_LF_GRAVATAR_SERVER_URL;
 import static org.sonar.core.config.WebConstants.SONAR_LF_LOGO_URL;
@@ -80,9 +83,9 @@ public class GlobalAction implements NavigationWsAction, Startable {
   private final DocumentationLinkGenerator documentationLinkGenerator;
 
   public GlobalAction(PageRepository pageRepository, Configuration config, ResourceTypes resourceTypes, Server server,
-                      NodeInformation nodeInformation, DbClient dbClient, UserSession userSession, PlatformEditionProvider editionProvider,
-                      WebAnalyticsLoader webAnalyticsLoader, IssueIndexSyncProgressChecker issueIndexSyncChecker,
-                      DefaultAdminCredentialsVerifier defaultAdminCredentialsVerifier, DocumentationLinkGenerator documentationLinkGenerator) {
+    NodeInformation nodeInformation, DbClient dbClient, UserSession userSession, PlatformEditionProvider editionProvider,
+    WebAnalyticsLoader webAnalyticsLoader, IssueIndexSyncProgressChecker issueIndexSyncChecker,
+    DefaultAdminCredentialsVerifier defaultAdminCredentialsVerifier, DocumentationLinkGenerator documentationLinkGenerator) {
     this.pageRepository = pageRepository;
     this.config = config;
     this.resourceTypes = resourceTypes;
@@ -115,7 +118,8 @@ public class GlobalAction implements NavigationWsAction, Startable {
       .setHandler(this)
       .setInternal(true)
       .setResponseExample(getClass().getResource("global-example.json"))
-      .setSince("5.2");
+      .setSince("5.2")
+      .setChangelog(new Change("10.5", "Field 'versionEOL' added, to indicate the end of support of installed version."));
   }
 
   @Override
@@ -128,6 +132,7 @@ public class GlobalAction implements NavigationWsAction, Startable {
       writeDeprecatedLogoProperties(json);
       writeQualifiers(json);
       writeVersion(json);
+      writeVersionEol(json);
       writeDatabaseProduction(json);
       writeInstanceUsesDefaultAdminCredentials(json);
       editionProvider.get().ifPresent(e -> json.prop("edition", e.name().toLowerCase(Locale.ENGLISH)));
@@ -179,6 +184,10 @@ public class GlobalAction implements NavigationWsAction, Startable {
     json.prop("version", displayVersion);
   }
 
+  private void writeVersionEol(JsonWriter json) {
+    json.prop("versionEOL", loadSqVersionEol(System2.INSTANCE));
+  }
+
   private void writeDatabaseProduction(JsonWriter json) {
     json.prop("productionDatabase", !dbClient.getDatabase().getDialect().getId().equals(H2.ID));
   }
index d4fe234374145d27c279ea3756245ea02601e93b..c3105e83b1b553917a7b80e50a832664733d3a46 100644 (file)
@@ -24,6 +24,7 @@
     "POL"
   ],
   "version": "6.2",
+  "versionEOL": "2025-01-01",
   "productionDatabase": true,
   "canAdmin": false,
   "standalone": true,
index eebd0f7aab83ca8dbfad3f11e82f844a85aa350d..9faee23bd61d725cbd3fb2fbe2321e6551a195b1 100644 (file)
  */
 package org.sonar.server.ui.ws;
 
+import java.util.Objects;
 import java.util.Optional;
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.mockito.MockedStatic;
 import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.internal.MetadataLoader;
 import org.sonar.api.platform.Server;
 import org.sonar.api.resources.ResourceType;
 import org.sonar.api.resources.ResourceTypeTree;
@@ -51,13 +54,14 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
 import static org.mockito.Mockito.when;
 import static org.sonar.test.JsonAssert.assertJson;
 
-public class GlobalActionTest {
+class GlobalActionTest {
 
-  @Rule
-  public UserSessionRule userSession = UserSessionRule.standalone();
+  @RegisterExtension
+  private final UserSessionRule userSession = UserSessionRule.standalone();
 
   private final MapSettings settings = new MapSettings();
 
@@ -73,7 +77,7 @@ public class GlobalActionTest {
   private WsActionTester ws;
 
   @Test
-  public void empty_call() {
+  void empty_call() {
     init();
 
     assertJson(call()).isSimilarTo("{" +
@@ -84,8 +88,8 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void return_qualifiers() {
-    init(new Page[] {}, new ResourceTypeTree[] {
+  void return_qualifiers() {
+    init(new Page[]{}, new ResourceTypeTree[]{
       ResourceTypeTree.builder()
         .addType(ResourceType.builder("POL").build())
         .addType(ResourceType.builder("LOP").build())
@@ -104,7 +108,7 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void return_settings() {
+  void return_settings() {
     settings.setProperty("sonar.lf.logoUrl", "http://example.com/my-custom-logo.png");
     settings.setProperty("sonar.lf.logoWidthPx", 135);
     settings.setProperty("sonar.lf.gravatarServerUrl", "https://secure.gravatar.com/avatar/{EMAIL_MD5}.jpg?s={SIZE}&d=identicon");
@@ -130,7 +134,7 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void return_developer_info_disabled_setting() {
+  void return_developer_info_disabled_setting() {
     init();
     settings.setProperty("sonar.developerAggregatedInfo.disabled", true);
 
@@ -142,7 +146,7 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void return_deprecated_logo_settings() {
+  void return_deprecated_logo_settings() {
     init();
     settings.setProperty("sonar.lf.logoUrl", "http://example.com/my-custom-logo.png");
     settings.setProperty("sonar.lf.logoWidthPx", 135);
@@ -158,8 +162,8 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void the_returned_global_pages_do_not_include_administration_pages() {
-    init(createPages(), new ResourceTypeTree[] {});
+  void the_returned_global_pages_do_not_include_administration_pages() {
+    init(createPages(), new ResourceTypeTree[]{});
 
     assertJson(call()).isSimilarTo("{" +
       "  \"globalPages\": [" +
@@ -176,7 +180,7 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void return_sonarqube_version() {
+  void return_sonarqube_version() {
     init();
     when(server.getVersion()).thenReturn("6.2");
 
@@ -186,7 +190,16 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void functional_version_when_4_digits() {
+  void execute_shouldReturnVersionEol() {
+    init();
+    try (MockedStatic<MetadataLoader> mocked = mockStatic(MetadataLoader.class)) {
+      mocked.when(() -> MetadataLoader.loadSqVersionEol(any())).thenReturn("2025-01-01");
+      assertThat(call()).contains("\"versionEOL\":\"2025-01-01\"");
+    }
+  }
+
+  @Test
+  void functional_version_when_4_digits() {
     init();
     when(server.getVersion()).thenReturn("6.3.1.1234");
 
@@ -196,7 +209,7 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void functional_version_when_third_digit_is_0() {
+  void functional_version_when_third_digit_is_0() {
     init();
     when(server.getVersion()).thenReturn("6.3.0.1234");
 
@@ -206,7 +219,7 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void return_if_production_database_or_not() {
+  void return_if_production_database_or_not() {
     init();
     when(dbClient.getDatabase().getDialect()).thenReturn(new PostgreSql());
 
@@ -216,7 +229,7 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void return_need_issue_sync() {
+  void return_need_issue_sync() {
     init();
     when(indexSyncProgressChecker.isIssueSyncInProgress(any())).thenReturn(true);
     assertJson(call()).isSimilarTo("{\"needIssueSync\": true}");
@@ -226,7 +239,7 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void instance_uses_default_admin_credentials() {
+  void instance_uses_default_admin_credentials() {
     init();
 
     when(defaultAdminCredentialsVerifier.hasDefaultCredentialUser()).thenReturn(true);
@@ -242,7 +255,7 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void standalone_flag() {
+  void standalone_flag() {
     init();
     userSession.logIn().setSystemAdministrator();
     when(nodeInformation.isStandalone()).thenReturn(true);
@@ -251,7 +264,7 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void not_standalone_flag() {
+  void not_standalone_flag() {
     init();
     userSession.logIn().setSystemAdministrator();
     when(nodeInformation.isStandalone()).thenReturn(false);
@@ -260,14 +273,14 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void test_example_response() {
+  void test_example_response() {
     settings.setProperty("sonar.lf.logoUrl", "http://example.com/my-custom-logo.png");
     settings.setProperty("sonar.lf.logoWidthPx", 135);
     settings.setProperty("sonar.lf.gravatarServerUrl", "http://some-server.tld/logo.png");
     settings.setProperty("sonar.lf.enableGravatar", true);
     settings.setProperty("sonar.updatecenter.activate", false);
     settings.setProperty("sonar.technicalDebt.ratingGrid", "0.05,0.1,0.2,0.5");
-    init(createPages(), new ResourceTypeTree[] {
+    init(createPages(), new ResourceTypeTree[]{
       ResourceTypeTree.builder()
         .addType(ResourceType.builder("POL").build())
         .addType(ResourceType.builder("LOP").build())
@@ -285,12 +298,18 @@ public class GlobalActionTest {
     when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.COMMUNITY));
     when(documentationLinkGenerator.getDocumentationLink(null)).thenReturn("http://docs.example.com/10.0");
 
-    String result = call();
-    assertJson(result).isSimilarTo(ws.getDef().responseExampleAsString());
+
+    try (MockedStatic<MetadataLoader> mocked = mockStatic(MetadataLoader.class)) {
+      mocked.when(() -> MetadataLoader.loadSqVersionEol(any())).thenReturn("2025-01-01");
+
+      String result = call();
+
+      assertJson(result).isSimilarTo(Objects.requireNonNull(ws.getDef().responseExampleAsString()));
+    }
   }
 
   @Test
-  public void edition_is_not_returned_if_not_defined() {
+  void edition_is_not_returned_if_not_defined() {
     init();
     when(editionProvider.get()).thenReturn(Optional.empty());
 
@@ -299,7 +318,7 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void edition_is_returned_if_defined() {
+  void edition_is_returned_if_defined() {
     init();
     when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.DEVELOPER));
 
@@ -308,7 +327,7 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void web_analytics_js_path_is_not_returned_if_not_defined() {
+  void web_analytics_js_path_is_not_returned_if_not_defined() {
     init();
     when(webAnalyticsLoader.getUrlPathToJs()).thenReturn(Optional.empty());
 
@@ -317,7 +336,7 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void web_analytics_js_path_is_returned_if_defined() {
+  void web_analytics_js_path_is_returned_if_defined() {
     init();
     String path = "static/googleanalytics/analytics.js";
     when(webAnalyticsLoader.getUrlPathToJs()).thenReturn(Optional.of(path));
@@ -327,7 +346,7 @@ public class GlobalActionTest {
   }
 
   @Test
-  public void call_shouldReturnDocumentationUrl() {
+  void call_shouldReturnDocumentationUrl() {
     init();
     String url = "https://docs.sonarsource.com/sonarqube/10.0";
     when(documentationLinkGenerator.getDocumentationLink(null)).thenReturn(url);
@@ -337,7 +356,7 @@ public class GlobalActionTest {
   }
 
   private void init() {
-    init(new org.sonar.api.web.page.Page[] {}, new ResourceTypeTree[] {});
+    init(new org.sonar.api.web.page.Page[]{}, new ResourceTypeTree[]{});
   }
 
   private void init(org.sonar.api.web.page.Page[] pages, ResourceTypeTree[] resourceTypeTrees) {
@@ -348,7 +367,7 @@ public class GlobalActionTest {
     when(pluginRepository.getPluginInfo(any())).thenReturn(new PluginInfo("unused").setVersion(Version.create("1.0")));
     CoreExtensionRepository coreExtensionRepository = mock(CoreExtensionRepository.class);
     when(coreExtensionRepository.isInstalled(any())).thenReturn(false);
-    PageRepository pageRepository = new PageRepository(pluginRepository, coreExtensionRepository, new PageDefinition[] {context -> {
+    PageRepository pageRepository = new PageRepository(pluginRepository, coreExtensionRepository, new PageDefinition[]{context -> {
       for (Page page : pages) {
         context.addPage(page);
       }
@@ -370,6 +389,6 @@ public class GlobalActionTest {
     Page anotherPage = Page.builder("another_plugin/page").setName("My Another Page").build();
     Page adminPage = Page.builder("my_plugin/admin_page").setName("Admin Page").setAdmin(true).build();
 
-    return new Page[] {page, anotherPage, adminPage};
+    return new Page[]{page, anotherPage, adminPage};
   }
 }
index 8b7216073edc22f9e6adc515a402277090bab755..8dc960886ce36bd178e1dc3fc46d1ff2a34784aa 100644 (file)
@@ -21,9 +21,14 @@ dependencies {
   testImplementation 'com.google.guava:guava'
   testImplementation 'com.tngtech.java:junit-dataprovider'
   testImplementation 'junit:junit'
+  testImplementation 'org.junit.jupiter:junit-jupiter-api'
   testImplementation 'org.assertj:assertj-core'
   testImplementation 'org.mockito:mockito-core'
+  testImplementation 'org.mockito:mockito-junit-jupiter'
   testImplementation 'org.sonarsource.api.plugin:sonar-plugin-api-test-fixtures'
+
+  testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
+  testRuntimeOnly 'org.junit.vintage:junit-vintage-engine'
 }
 
 import org.apache.tools.ant.filters.ReplaceTokens
@@ -47,3 +52,7 @@ publishing {
     }
   }
 }
+test {
+  // Enabling the JUnit Platform (see https://github.com/junit-team/junit5-samples/tree/master/junit5-migration-gradle)
+  useJUnitPlatform()
+}
index ad8e12b610a4fa8bc7a9abcbbd10b79fd6528890..64e6203fbd2dd9d05f60c31dcc944f12288e0a53 100644 (file)
@@ -28,6 +28,7 @@ import org.sonar.api.SonarEdition;
 import org.sonar.api.utils.System2;
 import org.sonar.api.utils.Version;
 
+import static java.lang.String.format;
 import static org.apache.commons.lang3.StringUtils.trimToEmpty;
 
 /**
@@ -40,6 +41,8 @@ public class MetadataLoader {
   private static final String SQ_VERSION_FILE_PATH = "/sq-version.txt";
   private static final String SONAR_API_VERSION_FILE_PATH = "/sonar-api-version.txt";
   private static final String EDITION_FILE_PATH = "/sonar-edition.txt";
+  private static final String SQ_VERSION_EOL_FILE_PATH = "/sq-version-eol.txt";
+  public static final String CAN_NOT_LOAD_FROM_CLASSPATH = "Can not load %s from classpath";
 
   private MetadataLoader() {
     // only static methods
@@ -48,18 +51,25 @@ public class MetadataLoader {
   public static Version loadApiVersion(System2 system) {
     return getVersion(system, SONAR_API_VERSION_FILE_PATH);
   }
+
   public static Version loadSQVersion(System2 system) {
     return getVersion(system, SQ_VERSION_FILE_PATH);
   }
 
+  public static String loadSqVersionEol(System2 system) {
+    return getParamFromFile(system, SQ_VERSION_EOL_FILE_PATH);
+  }
+
   private static Version getVersion(System2 system, String versionFilePath) {
-    URL url = system.getResource(versionFilePath);
+    return Version.parse(getParamFromFile(system, versionFilePath));
+  }
 
-    try (Scanner scanner = new Scanner(url.openStream(), StandardCharsets.UTF_8.name())) {
-      String versionInFile = scanner.nextLine();
-      return Version.parse(versionInFile);
+  private static String getParamFromFile(System2 system, String filePath) {
+    URL url = system.getResource(filePath);
+    try (Scanner scanner = new Scanner(url.openStream(), StandardCharsets.UTF_8)) {
+      return scanner.nextLine();
     } catch (IOException e) {
-      throw new IllegalStateException("Can not load " + versionFilePath + " from classpath ", e);
+      throw new IllegalStateException(format(CAN_NOT_LOAD_FROM_CLASSPATH, filePath), e);
     }
   }
 
@@ -68,11 +78,11 @@ public class MetadataLoader {
     if (url == null) {
       return SonarEdition.COMMUNITY;
     }
-    try (Scanner scanner = new Scanner(url.openStream(), StandardCharsets.UTF_8.name())) {
+    try (Scanner scanner = new Scanner(url.openStream(), StandardCharsets.UTF_8)) {
       String editionInFile = scanner.nextLine();
       return parseEdition(editionInFile);
     } catch (IOException e) {
-      throw new IllegalStateException("Can not load " + EDITION_FILE_PATH + " from classpath", e);
+      throw new IllegalStateException(format(CAN_NOT_LOAD_FROM_CLASSPATH, EDITION_FILE_PATH), e);
     }
   }
 
@@ -81,7 +91,7 @@ public class MetadataLoader {
     try {
       return SonarEdition.valueOf(str);
     } catch (IllegalArgumentException e) {
-      throw new IllegalStateException(String.format("Invalid edition found in '%s': '%s'", EDITION_FILE_PATH, str));
+      throw new IllegalStateException(format("Invalid edition found in '%s': '%s'", EDITION_FILE_PATH, str));
     }
   }
 }
index 3ee4ec5df9a59fe5b69a4cfc1347b70da78b3826..017bfca8777b75d6bf2e8559ec25ab462039c072 100644 (file)
@@ -21,7 +21,7 @@ package org.sonar.api.internal;
 
 import java.io.File;
 import java.net.MalformedURLException;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 import org.sonar.api.SonarEdition;
 import org.sonar.api.utils.System2;
 import org.sonar.api.utils.Version;
@@ -32,45 +32,45 @@ import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-public class MetadataLoaderTest {
+class MetadataLoaderTest {
   private final System2 system = mock(System2.class);
 
   @Test
-  public void load_api_version_from_file_in_classpath() {
+  void load_api_version_from_file_in_classpath() {
     Version version = MetadataLoader.loadApiVersion(System2.INSTANCE);
     assertThat(version).isNotNull();
     assertThat(version.major()).isGreaterThanOrEqualTo(5);
   }
 
   @Test
-  public void load_sq_version_from_file_in_classpath() {
+  void load_sq_version_from_file_in_classpath() {
     Version version = MetadataLoader.loadSQVersion(System2.INSTANCE);
     assertThat(version).isNotNull();
     assertThat(version.major()).isGreaterThanOrEqualTo(5);
   }
 
   @Test
-  public void load_edition_from_file_in_classpath() {
+  void load_edition_from_file_in_classpath() {
     SonarEdition edition = MetadataLoader.loadEdition(System2.INSTANCE);
     assertThat(edition).isNotNull();
   }
 
   @Test
-  public void load_edition_defaults_to_community_if_file_not_found() throws MalformedURLException {
+  void load_edition_defaults_to_community_if_file_not_found() throws MalformedURLException {
     when(system.getResource(anyString())).thenReturn(new File("target/unknown").toURI().toURL());
     SonarEdition edition = MetadataLoader.loadEdition(System2.INSTANCE);
     assertThat(edition).isEqualTo(SonarEdition.COMMUNITY);
   }
 
   @Test
-  public void throw_ISE_if_edition_is_invalid() {
+  void throw_ISE_if_edition_is_invalid() {
     assertThatThrownBy(() -> MetadataLoader.parseEdition("trash"))
       .isInstanceOf(IllegalStateException.class)
       .hasMessage("Invalid edition found in '/sonar-edition.txt': 'TRASH'");
   }
 
   @Test
-  public void throw_ISE_if_fail_to_load_version() throws Exception {
+  void throw_ISE_if_fail_to_load_version() throws Exception {
     when(system.getResource(anyString())).thenReturn(new File("target/unknown").toURI().toURL());
 
     assertThatThrownBy(() -> MetadataLoader.loadApiVersion(system))
@@ -78,4 +78,17 @@ public class MetadataLoaderTest {
       .hasMessageContaining("Can not load /sonar-api-version.txt from classpath");
   }
 
+  @Test
+  void loadSqVersionEol_shouldLoadCorrectEol() {
+    String eol = MetadataLoader.loadSqVersionEol(System2.INSTANCE);
+    assertThat(eol).isNotNull();
+  }
+
+  @Test
+  void loadSqVersionEol_whenFileNotFound_shouldThrowException() throws MalformedURLException {
+    when(system.getResource(anyString())).thenReturn(new File("target/unknown").toURI().toURL());
+    assertThatThrownBy(() -> MetadataLoader.loadSqVersionEol(system))
+      .isInstanceOf(IllegalStateException.class)
+      .hasMessageContaining("Can not load /sq-version-eol.txt from classpath");
+  }
 }