aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentFinder.java5
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ScannerCache.java52
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/package-info.java23
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ClearAction.java62
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/GetAction.java129
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ScannerCacheWs.java45
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ScannerCacheWsAction.java30
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ScannerCacheWsModule.java33
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/package-info.java23
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/queue/CeQueueCleanerTest.java3
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/ClearActionTest.java93
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/GetActionTest.java158
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/ScannerCacheWsModuleTest.java35
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/ScannerCacheWsTest.java56
-rw-r--r--server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java6
15 files changed, 747 insertions, 6 deletions
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentFinder.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentFinder.java
index 8c4a77febfa..5a26580fdbb 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentFinder.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentFinder.java
@@ -126,11 +126,6 @@ public class ComponentFinder {
.orElseThrow(() -> new IllegalStateException(String.format("Can't find main branch for project '%s'", projectDto.getKey())));
}
- public BranchDto getMainBranch(DbSession dbSession, ComponentDto projectDto) {
- return dbClient.branchDao().selectByUuid(dbSession, projectDto.uuid())
- .orElseThrow(() -> new IllegalStateException(String.format("Can't find main branch for project '%s'", projectDto.getKey())));
- }
-
private static void checkByUuidOrKey(@Nullable String componentUuid, @Nullable String componentKey, ParamNames parameterNames) {
checkArgument(componentUuid != null ^ componentKey != null, MSG_COMPONENT_ID_OR_KEY_TEMPLATE, parameterNames.getUuidParam(), parameterNames.getKeyParam());
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ScannerCache.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ScannerCache.java
new file mode 100644
index 00000000000..dc59028dffa
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ScannerCache.java
@@ -0,0 +1,52 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.scannercache;
+
+import javax.annotation.CheckForNull;
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbInputStream;
+import org.sonar.db.DbSession;
+import org.sonar.db.scannercache.ScannerCacheDao;
+
+@ServerSide
+public class ScannerCache {
+ private final DbClient dbClient;
+ private final ScannerCacheDao dao;
+
+ public ScannerCache(DbClient dbClient, ScannerCacheDao dao) {
+ this.dbClient = dbClient;
+ this.dao = dao;
+ }
+
+ @CheckForNull
+ public DbInputStream get(String branchUuid) {
+ try (DbSession session = dbClient.openSession(false)) {
+ return dao.selectData(session, branchUuid);
+ }
+ }
+
+ public void clear() {
+ try (DbSession session = dbClient.openSession(false)) {
+ dao.removeAll(session);
+ session.commit();
+ }
+ }
+}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/package-info.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/package-info.java
new file mode 100644
index 00000000000..fdd26060e29
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.scannercache;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ClearAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ClearAction.java
new file mode 100644
index 00000000000..44794a1cf7c
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ClearAction.java
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.scannercache.ws;
+
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.db.permission.GlobalPermission;
+import org.sonar.server.scannercache.ScannerCache;
+import org.sonar.server.user.UserSession;
+
+import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
+
+public class ClearAction implements ScannerCacheWsAction {
+ private final UserSession userSession;
+ private final ScannerCache cache;
+
+ public ClearAction(UserSession userSession, ScannerCache cache) {
+ this.userSession = userSession;
+ this.cache = cache;
+ }
+
+ @Override
+ public void define(WebService.NewController context) {
+ context.createAction("clear")
+ .setInternal(true)
+ .setPost(true)
+ .setDescription("Clear the scanner's cached data for all projects and branches. Requires global administration permission. ")
+ .setSince("9.4")
+ .setHandler(this);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ checkPermission();
+ cache.clear();
+ response.noContent();
+ }
+
+ private void checkPermission() {
+ if (!userSession.hasPermission(GlobalPermission.ADMINISTER)) {
+ throw insufficientPrivilegesException();
+ }
+ }
+}
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
new file mode 100644
index 00000000000..5db206245a8
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/GetAction.java
@@ -0,0 +1,129 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.scannercache.ws;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.zip.GZIPInputStream;
+import org.apache.commons.io.IOUtils;
+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.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.scannercache.ScannerCache;
+import org.sonar.server.user.UserSession;
+
+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;
+
+public class GetAction implements ScannerCacheWsAction {
+ private static final String PROJECT = "project";
+ private static final String BRANCH = "branch";
+
+ private final DbClient dbClient;
+ private final UserSession userSession;
+ private final ComponentFinder componentFinder;
+ private final ScannerCache cache;
+
+ public GetAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder, ScannerCache cache) {
+ this.dbClient = dbClient;
+ this.userSession = userSession;
+ this.componentFinder = componentFinder;
+ this.cache = cache;
+ }
+
+ @Override
+ public void define(WebService.NewController context) {
+ WebService.NewAction action = context.createAction("get")
+ .setInternal(true)
+ .setDescription("Get the scanner's cached data for a branch. Requires scan permission on the project. "
+ + "Data is returned gzipped if the corresponding 'Accept-Encoding' header is set in the request.")
+ .setSince("9.4")
+ .setHandler(this);
+
+ action.createParam(PROJECT)
+ .setDescription("Project key")
+ .setExampleValue(KEY_PROJECT_EXAMPLE_001)
+ .setRequired(true);
+
+ action.createParam(BRANCH)
+ .setDescription("Branch key. If not provided, main branch will be used.")
+ .setExampleValue(KEY_BRANCH_EXAMPLE_001)
+ .setRequired(false);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ String projectKey = request.mandatoryParam(PROJECT);
+ String branchKey = request.param(BRANCH);
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ ComponentDto component = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, projectKey, branchKey, null);
+ checkPermission(component);
+
+ try (DbInputStream dbInputStream = cache.get(component.uuid())) {
+ if (dbInputStream == null) {
+ throw new NotFoundException("No cache for given branch");
+ }
+
+ boolean compressed = requestedCompressedData(request);
+ try (OutputStream output = response.stream().output()) {
+ if (compressed) {
+ response.setHeader("Content-Encoding", "gzip");
+ // data is stored compressed
+ IOUtils.copy(dbInputStream, output);
+ } else {
+ try (InputStream uncompressedInput = new GZIPInputStream(dbInputStream)) {
+ IOUtils.copy(uncompressedInput, output);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static boolean requestedCompressedData(Request request) {
+ String encoding = request.getHeaders().get("Accept-Encoding");
+ if (encoding == null) {
+ return false;
+ }
+ return Arrays.stream(encoding.split(","))
+ .map(String::trim)
+ .anyMatch("gzip"::equals);
+ }
+
+ private void checkPermission(ComponentDto project) {
+ if (userSession.hasComponentPermission(UserRole.SCAN, project) ||
+ userSession.hasComponentPermission(UserRole.ADMIN, project) ||
+ userSession.hasPermission(SCAN)) {
+ return;
+ }
+ throw insufficientPrivilegesException();
+ }
+}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ScannerCacheWs.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ScannerCacheWs.java
new file mode 100644
index 00000000000..d69e26bfcc4
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ScannerCacheWs.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.scannercache.ws;
+
+import org.sonar.api.server.ws.WebService;
+
+public class ScannerCacheWs implements WebService {
+
+ private final ScannerCacheWsAction[] actions;
+
+ public ScannerCacheWs(ScannerCacheWsAction... actions) {
+ this.actions = actions;
+ }
+
+ @Override
+ public void define(Context context) {
+ NewController controller = context
+ .createController("api/scanner_cache")
+ .setSince("9.4")
+ .setDescription("Access the scanner cache");
+
+ for (ScannerCacheWsAction action : actions) {
+ action.define(controller);
+ }
+
+ controller.done();
+ }
+}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ScannerCacheWsAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ScannerCacheWsAction.java
new file mode 100644
index 00000000000..0da2162a7c9
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ScannerCacheWsAction.java
@@ -0,0 +1,30 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.scannercache.ws;
+
+import org.sonar.server.ws.WsAction;
+
+/**
+ * Marker interface for coding rule related actions
+ *
+ */
+interface ScannerCacheWsAction extends WsAction {
+ // Marker interface
+}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ScannerCacheWsModule.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ScannerCacheWsModule.java
new file mode 100644
index 00000000000..f3d14e98a5a
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ScannerCacheWsModule.java
@@ -0,0 +1,33 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.scannercache.ws;
+
+import org.sonar.core.platform.Module;
+
+public class ScannerCacheWsModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(
+ ScannerCacheWs.class,
+ GetAction.class,
+ ClearAction.class
+ );
+ }
+}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/package-info.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/package-info.java
new file mode 100644
index 00000000000..9147763cb27
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.scannercache.ws;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/queue/CeQueueCleanerTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/queue/CeQueueCleanerTest.java
index 3fd68497843..89af15825a4 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/queue/CeQueueCleanerTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/queue/CeQueueCleanerTest.java
@@ -27,6 +27,7 @@ import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.platform.ServerUpgradeStatus;
import org.sonar.api.utils.System2;
import org.sonar.ce.queue.CeQueue;
+import org.sonar.db.DbInputStream;
import org.sonar.db.DbTester;
import org.sonar.db.ce.CeQueueDto;
import org.sonar.db.ce.CeTaskInputDao;
@@ -89,7 +90,7 @@ public class CeQueueCleanerTest {
runCleaner();
CeTaskInputDao dataDao = dbTester.getDbClient().ceTaskInputDao();
- Optional<CeTaskInputDao.DataStream> task1Data = dataDao.selectData(dbTester.getSession(), "TASK_1");
+ Optional<DbInputStream> task1Data = dataDao.selectData(dbTester.getSession(), "TASK_1");
assertThat(task1Data).isPresent();
task1Data.get().close();
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/ClearActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/ClearActionTest.java
new file mode 100644
index 00000000000..5bd61e4aafe
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/ClearActionTest.java
@@ -0,0 +1,93 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.scannercache.ws;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import org.apache.commons.io.IOUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbInputStream;
+import org.sonar.db.DbTester;
+import org.sonar.db.permission.GlobalPermission;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.db.scannercache.ScannerCacheDao;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.scannercache.ScannerCache;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.TestResponse;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.sonar.api.web.UserRole.SCAN;
+
+public class ClearActionTest {
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private final ScannerCacheDao dao = new ScannerCacheDao();
+ private final ScannerCache cache = new ScannerCache(dbTester.getDbClient(), dao);
+ private final ClearAction ws = new ClearAction(userSession, cache);
+ private final WsActionTester wsTester = new WsActionTester(ws);
+
+ @Test
+ public void should_clear_all_entries() throws IOException {
+ ProjectDto project1 = dbTester.components().insertPrivateProjectDto();
+ ProjectDto project2 = dbTester.components().insertPrivateProjectDto();
+
+ dao.insert(dbTester.getSession(), project1.getUuid(), stringToInputStream("test data"));
+ dao.insert(dbTester.getSession(), project2.getUuid(), stringToInputStream("test data"));
+
+ assertThat(dataStreamToString(dao.selectData(dbTester.getSession(), project1.getUuid()))).isEqualTo("test data");
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER);
+ TestResponse response = wsTester.newRequest().execute();
+
+ response.assertNoContent();
+ assertThat(dbTester.countRowsOfTable("scanner_cache")).isZero();
+ }
+
+ @Test
+ public void fail_if_not_global_admin() throws IOException {
+ ProjectDto project = dbTester.components().insertPrivateProjectDto();
+ dao.insert(dbTester.getSession(), "branch1", stringToInputStream("test data"));
+ assertThat(dataStreamToString(dao.selectData(dbTester.getSession(), "branch1"))).isEqualTo("test data");
+ userSession.logIn().addProjectPermission(SCAN, project);
+ TestRequest request = wsTester.newRequest();
+
+ assertThatThrownBy(request::execute).isInstanceOf(ForbiddenException.class);
+ }
+
+ private static String dataStreamToString(DbInputStream dbInputStream) throws IOException {
+ try (DbInputStream ds = dbInputStream) {
+ return IOUtils.toString(ds, StandardCharsets.UTF_8);
+ }
+ }
+
+ private static InputStream stringToInputStream(String str) {
+ return new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8));
+ }
+}
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
new file mode 100644
index 00000000000..7135bf77b67
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/GetActionTest.java
@@ -0,0 +1,158 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.scannercache.ws;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+import org.apache.commons.io.IOUtils;
+import org.junit.Rule;
+import org.junit.Test;
+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.project.ProjectDto;
+import org.sonar.db.scannercache.ScannerCacheDao;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.scannercache.ScannerCache;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.TestResponse;
+import org.sonar.server.ws.WsActionTester;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.sonar.api.web.UserRole.SCAN;
+
+public class GetActionTest {
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private final ScannerCacheDao dao = new ScannerCacheDao();
+ private final ScannerCache cache = new ScannerCache(dbTester.getDbClient(), dao);
+ private final ComponentFinder finder = new ComponentFinder(dbTester.getDbClient(), null);
+ private final GetAction ws = new GetAction(dbTester.getDbClient(), userSession, finder, cache);
+ private final WsActionTester wsTester = new WsActionTester(ws);
+
+ @Test
+ public void get_data_for_project() throws IOException {
+ ProjectDto project1 = dbTester.components().insertPrivateProjectDto();
+ BranchDto branch = dbTester.components().insertProjectBranch(project1);
+ ProjectDto project2 = dbTester.components().insertPrivateProjectDto();
+
+ dao.insert(dbTester.getSession(), project1.getUuid(), stringToCompressedInputStream("test data1"));
+ dao.insert(dbTester.getSession(), branch.getUuid(), stringToCompressedInputStream("test data2"));
+ dao.insert(dbTester.getSession(), project2.getUuid(), stringToCompressedInputStream("test data3"));
+
+ userSession.logIn().addProjectPermission(SCAN, project1);
+ TestResponse response = wsTester.newRequest()
+ .setParam("project", project1.getKey())
+ .setHeader("Accept-Encoding", "gzip")
+ .execute();
+
+ assertThat(compressedInputStreamToString(response.getInputStream())).isEqualTo("test data1");
+ assertThat(response.getHeader("Content-Encoding")).isEqualTo("gzip");
+ }
+
+ @Test
+ public void get_uncompressed_data_for_project() throws IOException {
+ ProjectDto project1 = dbTester.components().insertPrivateProjectDto();
+
+ dao.insert(dbTester.getSession(), project1.getUuid(), stringToCompressedInputStream("test data1"));
+
+ userSession.logIn().addProjectPermission(SCAN, project1);
+
+ TestResponse response = wsTester.newRequest()
+ .setParam("project", project1.getKey())
+ .execute();
+
+ assertThat(response.getHeader("Content-Encoding")).isNull();
+ assertThat(response.getInput()).isEqualTo("test data1");
+ }
+
+ @Test
+ public void get_data_for_branch() throws IOException {
+ ProjectDto project1 = dbTester.components().insertPrivateProjectDto();
+ BranchDto branch = dbTester.components().insertProjectBranch(project1);
+
+ 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("branch", 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()
+ .setParam("project", "project1");
+ assertThatThrownBy(request::execute).isInstanceOf(NotFoundException.class);
+ }
+
+ @Test
+ public void return_not_found_if_cache_not_found() {
+ ProjectDto project1 = dbTester.components().insertPrivateProjectDto();
+
+ userSession.logIn().addProjectPermission(SCAN, project1);
+ TestRequest request = wsTester
+ .newRequest()
+ .setParam("project", "project1");
+ assertThatThrownBy(request::execute).isInstanceOf(NotFoundException.class);
+ }
+
+ @Test
+ public void fail_if_no_permissions() {
+ ProjectDto project = dbTester.components().insertPrivateProjectDto();
+ userSession.logIn().addProjectPermission(UserRole.CODEVIEWER, project);
+ TestRequest request = wsTester
+ .newRequest()
+ .setParam("project", project.getKey());
+
+ assertThatThrownBy(request::execute).isInstanceOf(ForbiddenException.class);
+ }
+
+ private static String compressedInputStreamToString(InputStream inputStream) throws IOException {
+ return IOUtils.toString(new GZIPInputStream(inputStream), UTF_8);
+ }
+
+ private static InputStream stringToCompressedInputStream(String str) throws IOException {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) {
+ IOUtils.write(str.getBytes(UTF_8), gzipOutputStream);
+ }
+ return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
+ }
+}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/ScannerCacheWsModuleTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/ScannerCacheWsModuleTest.java
new file mode 100644
index 00000000000..f17381e8876
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/ScannerCacheWsModuleTest.java
@@ -0,0 +1,35 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.scannercache.ws;
+
+import org.junit.Test;
+import org.sonar.core.platform.ListContainer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ScannerCacheWsModuleTest {
+ @Test
+ public void verify_count_of_added_components() {
+ ListContainer container = new ListContainer();
+ new ScannerCacheWsModule().configure(container);
+ assertThat(container.getAddedObjects()).hasSize(3);
+ }
+
+}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/ScannerCacheWsTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/ScannerCacheWsTest.java
new file mode 100644
index 00000000000..e48b4e5321a
--- /dev/null
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/ScannerCacheWsTest.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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.scannercache.ws;
+
+import org.junit.Test;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ScannerCacheWsTest {
+ @Test
+ public void define_ws() {
+ ScannerCacheWsAction action = new FakeAction();
+ ScannerCacheWs underTest = new ScannerCacheWs(action);
+ WebService.Context context = new WebService.Context();
+
+ underTest.define(context);
+
+ WebService.Controller controller = context.controller("api/scanner_cache");
+ assertThat(controller).isNotNull();
+ assertThat(controller.since()).isEqualTo("9.4");
+ assertThat(controller.description()).isNotEmpty();
+ assertThat(controller.actions()).hasSize(1);
+ }
+
+ private static class FakeAction implements ScannerCacheWsAction {
+ @Override
+ public void define(WebService.NewController newController) {
+ newController.createAction("fake").setHandler(this);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+
+ }
+ }
+}
diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
index cded3afbfc0..1d678796df5 100644
--- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
+++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
@@ -221,6 +221,8 @@ import org.sonar.server.rule.ws.RuleQueryFactory;
import org.sonar.server.rule.ws.RuleWsSupport;
import org.sonar.server.rule.ws.RulesWs;
import org.sonar.server.rule.ws.TagsAction;
+import org.sonar.server.scannercache.ScannerCache;
+import org.sonar.server.scannercache.ws.ScannerCacheWsModule;
import org.sonar.server.setting.ProjectConfigurationLoaderImpl;
import org.sonar.server.setting.SettingsChangeNotifier;
import org.sonar.server.setting.ws.SettingsWsModule;
@@ -523,6 +525,10 @@ public class PlatformLevel4 extends PlatformLevel {
CancelAllAction.class,
PluginsWs.class,
+ // Scanner Cache
+ ScannerCache.class,
+ new ScannerCacheWsModule(),
+
// ALM integrations
TimeoutConfigurationImpl.class,
CredentialsEncoderHelper.class,