@@ -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()); | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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; |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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 | |||
} |
@@ -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 | |||
); | |||
} | |||
} |
@@ -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; |
@@ -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(); | |||
@@ -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)); | |||
} | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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 { | |||
} | |||
} | |||
} |
@@ -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, |