aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java3
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/GetAction.java14
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/GetActionTest.java32
-rw-r--r--sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java4
-rw-r--r--sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java6
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java4
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheLoader.java31
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java2
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/cache/AnalysisCacheLoaderTest.java78
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/ModuleSensorContextTest.java2
10 files changed, 136 insertions, 40 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java
index 1d6137f5dbd..b7f17b328b8 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java
@@ -47,8 +47,6 @@ public class ReportComputationSteps extends AbstractComputationSteps {
ExecuteStatelessInitExtensionsStep.class,
BuildComponentTreeStep.class,
ValidateProjectStep.class,
-
- PersistScannerAnalysisCacheStep.class,
LoadQualityProfilesStep.class,
// load project related stuffs
@@ -89,6 +87,7 @@ public class ReportComputationSteps extends AbstractComputationSteps {
HandleUnanalyzedLanguagesStep.class,
// Persist data
+ PersistScannerAnalysisCacheStep.class,
PersistComponentsStep.class,
PersistAnalysisStep.class,
PersistAnalysisPropertiesStep.class,
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/GetAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/GetAction.java
index ba95c5e6393..8cc9792e51d 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/GetAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/GetAction.java
@@ -28,8 +28,8 @@ import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.web.UserRole;
-import org.sonar.db.DbInputStream;
import org.sonar.db.DbClient;
+import org.sonar.db.DbInputStream;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.component.ComponentFinder;
@@ -41,10 +41,12 @@ import static org.sonar.db.permission.GlobalPermission.SCAN;
import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
public class GetAction implements AnalysisCacheWsAction {
private static final String PROJECT = "project";
private static final String BRANCH = "branch";
+ private static final String PR = "pullRequest";
private final DbClient dbClient;
private final UserSession userSession;
@@ -76,20 +78,26 @@ public class GetAction implements AnalysisCacheWsAction {
.setDescription("Branch key. If not provided, main branch will be used.")
.setExampleValue(KEY_BRANCH_EXAMPLE_001)
.setRequired(false);
+
+ action.createParam(PR)
+ .setDescription("Pull request id. Not available in the community edition.")
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001)
+ .setRequired(false);
}
@Override
public void handle(Request request, Response response) throws Exception {
String projectKey = request.mandatoryParam(PROJECT);
String branchKey = request.param(BRANCH);
+ String prKey = request.param(PR);
try (DbSession dbSession = dbClient.openSession(false)) {
- ComponentDto component = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, projectKey, branchKey, null);
+ ComponentDto component = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, projectKey, branchKey, prKey);
checkPermission(component);
try (DbInputStream dbInputStream = cache.get(component.uuid())) {
if (dbInputStream == null) {
- throw new NotFoundException("No cache for given branch");
+ throw new NotFoundException("No cache for given branch or pull request");
}
boolean compressed = requestedCompressedData(request);
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/GetActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/GetActionTest.java
index 312df15ca6b..1aec02807f4 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/GetActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/GetActionTest.java
@@ -32,6 +32,7 @@ import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
import org.sonar.db.DbTester;
import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.BranchType;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.scannercache.ScannerAnalysisCacheDao;
import org.sonar.server.component.ComponentFinder;
@@ -115,6 +116,24 @@ public class GetActionTest {
}
@Test
+ public void get_data_for_pr() throws IOException {
+ ProjectDto project1 = dbTester.components().insertPrivateProjectDto();
+ BranchDto branch = dbTester.components().insertProjectBranch(project1, b -> b.setBranchType(BranchType.PULL_REQUEST));
+
+ dao.insert(dbTester.getSession(), project1.getUuid(), stringToCompressedInputStream("test data1"));
+ dao.insert(dbTester.getSession(), branch.getUuid(), stringToCompressedInputStream("test data2"));
+
+ userSession.logIn().addProjectPermission(SCAN, project1);
+ TestResponse response = wsTester.newRequest()
+ .setParam("project", project1.getKey())
+ .setParam("pullRequest", branch.getKey())
+ .setHeader("Accept-Encoding", "gzip")
+ .execute();
+
+ assertThat(compressedInputStreamToString(response.getInputStream())).isEqualTo("test data2");
+ }
+
+ @Test
public void return_not_found_if_project_not_found() {
TestRequest request = wsTester
.newRequest()
@@ -123,6 +142,19 @@ public class GetActionTest {
}
@Test
+ public void return_not_found_if_branch_mixed_with_pr() {
+ ProjectDto project1 = dbTester.components().insertPrivateProjectDto();
+ BranchDto branch = dbTester.components().insertProjectBranch(project1);
+
+ userSession.logIn().addProjectPermission(SCAN, project1);
+ TestRequest request = wsTester.newRequest()
+ .setParam("project", project1.getKey())
+ .setParam("pullRequest", branch.getKey());
+
+ assertThatThrownBy(request::execute).isInstanceOf(NotFoundException.class);
+ }
+
+ @Test
public void return_not_found_if_cache_not_found() {
ProjectDto project1 = dbTester.components().insertPrivateProjectDto();
diff --git a/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java b/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java
index 9cbe24f4eeb..dc5acf17136 100644
--- a/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java
+++ b/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java
@@ -423,12 +423,12 @@ public class SensorContextTester implements SensorContext {
}
@Override
- public ReadCache previousAnalysisCache() {
+ public ReadCache previousCache() {
return readCache;
}
- public void setPreviousAnalysisCache(ReadCache cache) {
+ public void setPreviousCache(ReadCache cache) {
this.readCache = cache;
}
diff --git a/sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java b/sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java
index 12e42d9b3ff..99f28cb066b 100644
--- a/sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java
+++ b/sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java
@@ -87,18 +87,18 @@ public class SensorContextTesterTest {
@Test
public void testPluginCache() {
assertThat(tester.nextCache()).isNull();
- assertThat(tester.previousAnalysisCache()).isNull();
+ assertThat(tester.previousCache()).isNull();
assertThat(tester.isCacheEnabled()).isFalse();
ReadCache readCache = mock(ReadCache.class);
WriteCache writeCache = mock(WriteCache.class);
- tester.setPreviousAnalysisCache(readCache);
+ tester.setPreviousCache(readCache);
tester.setNextCache(writeCache);
tester.setCacheEnabled(true);
assertThat(tester.nextCache()).isEqualTo(writeCache);
- assertThat(tester.previousAnalysisCache()).isEqualTo(readCache);
+ assertThat(tester.previousCache()).isEqualTo(readCache);
assertThat(tester.isCacheEnabled()).isTrue();
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
index da9578d0c98..9f1a282543d 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
@@ -239,13 +239,13 @@ public interface SensorContext {
* @since 9.4
*/
@Beta
- ReadCache previousAnalysisCache();
+ ReadCache previousCache();
/**
* Returns true if caching is enabled.
* This API is experimental and can be changed or dropped at any time.
* @see #nextCache()
- * @see #previousAnalysisCache()
+ * @see #previousCache()
* @since 9.4
*/
@Beta
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheLoader.java
index f582e122079..035d3cf0010 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheLoader.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/AnalysisCacheLoader.java
@@ -21,10 +21,10 @@ package org.sonar.scanner.cache;
import java.io.IOException;
import java.io.InputStream;
-import java.net.HttpURLConnection;
import java.util.Optional;
import java.util.zip.InflaterInputStream;
import org.sonar.api.scanner.fs.InputProject;
+import org.sonar.api.utils.MessageException;
import org.sonar.core.util.Protobuf;
import org.sonar.scanner.bootstrap.DefaultScannerWsClient;
import org.sonar.scanner.protocol.internal.ScannerInternal;
@@ -32,6 +32,7 @@ import org.sonar.scanner.protocol.internal.ScannerInternal.AnalysisCacheMsg;
import org.sonar.scanner.scan.branch.BranchConfiguration;
import org.sonar.scanner.scan.branch.BranchType;
import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.HttpException;
import org.sonarqube.ws.client.WsResponse;
/**
@@ -56,24 +57,26 @@ public class AnalysisCacheLoader {
String url = URL + "?project=" + project.key();
if (branchConfiguration.branchType() == BranchType.BRANCH && branchConfiguration.branchName() != null) {
url = url + "&branch=" + branchConfiguration.branchName();
+ } else if (branchConfiguration.isPullRequest()) {
+ url = url + "&pullRequest=" + branchConfiguration.pullRequestKey();
}
GetRequest request = new GetRequest(url).setHeader(ACCEPT_ENCODING, "gzip");
- try (WsResponse response = wsClient.call(request)) {
- if (response.code() == HttpURLConnection.HTTP_NOT_FOUND) {
- return Optional.empty();
+ try (WsResponse response = wsClient.call(request); InputStream is = response.contentStream()) {
+ Optional<String> contentEncoding = response.header(CONTENT_ENCODING);
+ if (contentEncoding.isPresent() && contentEncoding.get().equals("gzip")) {
+ return Optional.of(decompress(is));
+ } else {
+ return Optional.of(Protobuf.read(is, AnalysisCacheMsg.parser()));
}
- try (InputStream is = response.contentStream()) {
- Optional<String> contentEncoding = response.header(CONTENT_ENCODING);
- if (contentEncoding.isPresent() && contentEncoding.get().equals("gzip")) {
- return Optional.of(decompress(is));
- } else {
- return Optional.of(Protobuf.read(is, AnalysisCacheMsg.parser()));
- }
- } catch (IOException e) {
- throw new IllegalStateException("Failed to download cache", e);
+ } catch (HttpException e) {
+ if (e.code() == 404) {
+ return Optional.empty();
}
+ throw MessageException.of("Failed to download analysis cache: " + DefaultScannerWsClient.createErrorMessage(e));
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to download analysis cache", e);
}
}
@@ -81,7 +84,7 @@ public class AnalysisCacheLoader {
try (InflaterInputStream iis = new InflaterInputStream(is)) {
return Protobuf.read(iis, ScannerInternal.AnalysisCacheMsg.parser());
} catch (IOException e) {
- throw new IllegalStateException("Failed to decompress plugin cache", e);
+ throw new IllegalStateException("Failed to decompress analysis cache", e);
}
}
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java
index c3cd0ac0521..a4cd85157d6 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java
@@ -199,7 +199,7 @@ public class ProjectSensorContext implements SensorContext {
}
@Override
- public ReadCache previousAnalysisCache() {
+ public ReadCache previousCache() {
return readCache;
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/cache/AnalysisCacheLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/cache/AnalysisCacheLoaderTest.java
index 8ccdcafdb2d..1c1a04c7e08 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/cache/AnalysisCacheLoaderTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/cache/AnalysisCacheLoaderTest.java
@@ -29,20 +29,29 @@ import java.util.Optional;
import java.util.zip.DeflaterInputStream;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.sonar.api.scanner.fs.InputProject;
+import org.sonar.api.utils.MessageException;
import org.sonar.scanner.bootstrap.DefaultScannerWsClient;
-import org.sonar.scanner.protocol.internal.ScannerInternal;
import org.sonar.scanner.protocol.internal.ScannerInternal.AnalysisCacheMsg;
import org.sonar.scanner.scan.branch.BranchConfiguration;
+import org.sonar.scanner.scan.branch.BranchType;
+import org.sonarqube.ws.client.HttpException;
+import org.sonarqube.ws.client.WsRequest;
import org.sonarqube.ws.client.WsResponse;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.sonar.scanner.cache.AnalysisCacheLoader.CONTENT_ENCODING;
public class AnalysisCacheLoaderTest {
+ private final static AnalysisCacheMsg MSG = AnalysisCacheMsg.newBuilder()
+ .putMap("key", ByteString.copyFrom("value", StandardCharsets.UTF_8))
+ .build();
private final WsResponse response = mock(WsResponse.class);
private final DefaultScannerWsClient wsClient = mock(DefaultScannerWsClient.class);
private final InputProject project = mock(InputProject.class);
@@ -51,35 +60,75 @@ public class AnalysisCacheLoaderTest {
@Before
public void before() {
+ when(project.key()).thenReturn("myproject");
when(wsClient.call(any())).thenReturn(response);
}
@Test
public void loads_content() throws IOException {
- ScannerInternal.AnalysisCacheMsg expected = ScannerInternal.AnalysisCacheMsg.newBuilder()
- .putMap("key", ByteString.copyFrom("value", StandardCharsets.UTF_8))
- .build();
- setResponse(expected);
+ setResponse(MSG);
AnalysisCacheMsg msg = loader.load().get();
- assertThat(msg).isEqualTo(expected);
+ assertThat(msg).isEqualTo(MSG);
+ assertRequestPath("api/analysis_cache/get?project=myproject");
+ }
+
+ @Test
+ public void loads_content_for_branch() throws IOException {
+ when(branchConfiguration.branchType()).thenReturn(BranchType.BRANCH);
+ when(branchConfiguration.branchName()).thenReturn("name");
+
+ setResponse(MSG);
+ AnalysisCacheMsg msg = loader.load().get();
+
+ assertThat(msg).isEqualTo(MSG);
+ assertRequestPath("api/analysis_cache/get?project=myproject&branch=name");
+ }
+
+ @Test
+ public void loads_content_for_pr() throws IOException {
+ when(branchConfiguration.isPullRequest()).thenReturn(true);
+ when(branchConfiguration.pullRequestKey()).thenReturn("key");
+ setResponse(MSG);
+ AnalysisCacheMsg msg = loader.load().get();
+ assertThat(msg).isEqualTo(MSG);
+ assertRequestPath("api/analysis_cache/get?project=myproject&pullRequest=key");
}
@Test
public void loads_compressed_content() throws IOException {
- AnalysisCacheMsg expected = AnalysisCacheMsg.newBuilder()
- .putMap("key", ByteString.copyFrom("value", StandardCharsets.UTF_8))
- .build();
- setCompressedResponse(expected);
+ setCompressedResponse(MSG);
AnalysisCacheMsg msg = loader.load().get();
- assertThat(msg).isEqualTo(expected);
+ assertThat(msg).isEqualTo(MSG);
}
@Test
public void returns_empty_if_404() {
- when(response.code()).thenReturn(404);
+ when(wsClient.call(any())).thenThrow(new HttpException("url", 404, "content"));
assertThat(loader.load()).isEmpty();
}
+ @Test
+ public void throw_error_if_http_exception_not_404() {
+ when(wsClient.call(any())).thenThrow(new HttpException("url", 401, "content"));
+ assertThatThrownBy(loader::load)
+ .isInstanceOf(MessageException.class)
+ .hasMessage("Failed to download analysis cache: HTTP code 401: content");
+ }
+
+ @Test
+ public void throw_error_if_cant_decompress_content() {
+ setInvalidCompressedResponse();
+ assertThatThrownBy(loader::load)
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Failed to download analysis cache");
+ }
+
+ private void assertRequestPath(String expectedPath) {
+ ArgumentCaptor<WsRequest> requestCaptor = ArgumentCaptor.forClass(WsRequest.class);
+ verify(wsClient).call(requestCaptor.capture());
+ assertThat(requestCaptor.getValue().getPath()).isEqualTo(expectedPath);
+ }
+
private void setResponse(AnalysisCacheMsg msg) throws IOException {
when(response.contentStream()).thenReturn(createInputStream(msg));
}
@@ -89,6 +138,11 @@ public class AnalysisCacheLoaderTest {
when(response.header(CONTENT_ENCODING)).thenReturn(Optional.of("gzip"));
}
+ private void setInvalidCompressedResponse() {
+ when(response.contentStream()).thenReturn(new ByteArrayInputStream(new byte[] {1, 2, 3}));
+ when(response.header(CONTENT_ENCODING)).thenReturn(Optional.of("gzip"));
+ }
+
private InputStream createInputStream(AnalysisCacheMsg analysisCacheMsg) throws IOException {
ByteArrayOutputStream serialized = new ByteArrayOutputStream(analysisCacheMsg.getSerializedSize());
analysisCacheMsg.writeTo(serialized);
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/ModuleSensorContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/ModuleSensorContextTest.java
index 1063283716e..d40c7344bee 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/ModuleSensorContextTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/ModuleSensorContextTest.java
@@ -89,7 +89,7 @@ public class ModuleSensorContextTest {
assertThat(adaptor.canSkipUnchangedFiles()).isFalse();
assertThat(adaptor.nextCache()).isEqualTo(writeCache);
- assertThat(adaptor.previousAnalysisCache()).isEqualTo(readCache);
+ assertThat(adaptor.previousCache()).isEqualTo(readCache);
assertThat(adaptor.newIssue()).isNotNull();
assertThat(adaptor.newExternalIssue()).isNotNull();