From 71b58bc6abf618c795bc503a73e935a3986d6bf1 Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Tue, 27 Jun 2017 18:47:24 +0200 Subject: [PATCH] SONAR-8918 Do two calls to load QP when sonar.profile is used --- .../DefaultQualityProfileLoader.java | 45 +++++++++++++------ .../DefaultQualityProfileLoaderTest.java | 31 ++++++------- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultQualityProfileLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultQualityProfileLoader.java index 4415c12e341..92fced49ea2 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultQualityProfileLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultQualityProfileLoader.java @@ -21,8 +21,12 @@ package org.sonar.scanner.repository; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.function.BinaryOperator; import javax.annotation.Nullable; import org.apache.commons.io.IOUtils; import org.sonar.api.config.Settings; @@ -32,6 +36,8 @@ import org.sonarqube.ws.QualityProfiles.SearchWsResponse; import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile; import org.sonarqube.ws.client.GetRequest; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; import static org.sonar.scanner.util.ScannerUtils.encodeForUrl; public class DefaultQualityProfileLoader implements QualityProfileLoader { @@ -48,28 +54,37 @@ public class DefaultQualityProfileLoader implements QualityProfileLoader { @Override public List loadDefault(@Nullable String profileName) { StringBuilder url = new StringBuilder(WS_URL + "?defaults=true"); - if (profileName != null) { - url.append("&profileName=").append(encodeForUrl(profileName)); - } - getOrganizationKey().ifPresent(k -> url.append("&organization=").append(encodeForUrl(k))); - return call(url.toString()); + return loadAndOverrideIfNeeded(profileName, url); } @Override public List load(String projectKey, @Nullable String profileName) { StringBuilder url = new StringBuilder(WS_URL + "?projectKey=").append(encodeForUrl(projectKey)); + return loadAndOverrideIfNeeded(profileName, url); + } + + private List loadAndOverrideIfNeeded(@Nullable String profileName, StringBuilder url) { + getOrganizationKey().ifPresent(k -> url.append("&organization=").append(encodeForUrl(k))); + Map result = call(url.toString()); + if (profileName != null) { - url.append("&profileName=").append(encodeForUrl(profileName)); + StringBuilder urlForName = new StringBuilder(WS_URL + "?profileName="); + urlForName.append(encodeForUrl(profileName)); + getOrganizationKey().ifPresent(k -> urlForName.append("&organization=").append(encodeForUrl(k))); + result.putAll(call(urlForName.toString())); } - getOrganizationKey().ifPresent(k -> url.append("&organization=").append(encodeForUrl(k))); - return call(url.toString()); + if (result.isEmpty()) { + throw MessageException.of("No quality profiles have been found, you probably don't have any language plugin installed."); + } + + return new ArrayList<>(result.values()); } private Optional getOrganizationKey() { return Optional.ofNullable(settings.getString("sonar.organization")); } - private List call(String url) { + private Map call(String url) { GetRequest getRequest = new GetRequest(url); InputStream is = wsClient.call(getRequest).contentStream(); SearchWsResponse profiles; @@ -83,10 +98,14 @@ public class DefaultQualityProfileLoader implements QualityProfileLoader { } List profilesList = profiles.getProfilesList(); - if (profilesList == null || profilesList.isEmpty()) { - throw MessageException.of("No quality profiles have been found, you probably don't have any language plugin installed."); - } - return profilesList; + return profilesList.stream() + .collect(toMap(QualityProfile::getLanguage, identity(), throwingMerger(), LinkedHashMap::new)); + } + + private static BinaryOperator throwingMerger() { + return (u, v) -> { + throw new IllegalStateException(String.format("Duplicate key %s", u)); + }; } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultQualityProfileLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultQualityProfileLoaderTest.java index 246425e592c..7a2982e77f1 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultQualityProfileLoaderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultQualityProfileLoaderTest.java @@ -47,9 +47,11 @@ public class DefaultQualityProfileLoaderTest { @Test public void load_gets_profiles_for_specified_project_and_profile_name() throws IOException { - prepareCallWithResults(); + WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?projectKey=foo", createStreamOfProfiles("qp")); + WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?profileName=bar", createStreamOfProfiles("qp")); underTest.load("foo", "bar"); - verifyCalledPath("/api/qualityprofiles/search.protobuf?projectKey=foo&profileName=bar"); + verifyCalledPath("/api/qualityprofiles/search.protobuf?projectKey=foo"); + verifyCalledPath("/api/qualityprofiles/search.protobuf?profileName=bar"); } @Test @@ -61,9 +63,11 @@ public class DefaultQualityProfileLoaderTest { @Test public void load_encodes_url_parameters() throws IOException { - prepareCallWithResults(); + WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?projectKey=foo%232", createStreamOfProfiles("qp")); + WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?profileName=bar%232", createStreamOfProfiles("qp")); underTest.load("foo#2", "bar#2"); - verifyCalledPath("/api/qualityprofiles/search.protobuf?projectKey=foo%232&profileName=bar%232"); + verifyCalledPath("/api/qualityprofiles/search.protobuf?projectKey=foo%232"); + verifyCalledPath("/api/qualityprofiles/search.protobuf?profileName=bar%232"); } @Test @@ -76,9 +80,11 @@ public class DefaultQualityProfileLoaderTest { @Test public void loadDefault_gets_profiles_with_specified_name() throws IOException { - prepareCallWithResults(); + WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?defaults=true", createStreamOfProfiles("qp")); + WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?profileName=foo", createStreamOfProfiles("qp")); underTest.loadDefault("foo"); - verifyCalledPath("/api/qualityprofiles/search.protobuf?defaults=true&profileName=foo"); + verifyCalledPath("/api/qualityprofiles/search.protobuf?defaults=true"); + verifyCalledPath("/api/qualityprofiles/search.protobuf?profileName=foo"); } @Test @@ -88,19 +94,14 @@ public class DefaultQualityProfileLoaderTest { verifyCalledPath("/api/qualityprofiles/search.protobuf?defaults=true"); } - @Test - public void loadDefault_encodes_url_parameters() throws IOException { - prepareCallWithResults(); - underTest.loadDefault("foo#2"); - verifyCalledPath("/api/qualityprofiles/search.protobuf?defaults=true&profileName=foo%232"); - } - @Test public void loadDefault_sets_organization_parameter_if_defined_in_settings() throws IOException { settings.setProperty("sonar.organization", "my-org"); - prepareCallWithResults(); + WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?defaults=true&organization=my-org", createStreamOfProfiles("qp")); + WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?profileName=foo&organization=my-org", createStreamOfProfiles("qp")); underTest.loadDefault("foo"); - verifyCalledPath("/api/qualityprofiles/search.protobuf?defaults=true&profileName=foo&organization=my-org"); + verifyCalledPath("/api/qualityprofiles/search.protobuf?defaults=true&organization=my-org"); + verifyCalledPath("/api/qualityprofiles/search.protobuf?profileName=foo&organization=my-org"); } @Test -- 2.39.5