Add optional parameters to clear cache WS to allow specifying a project and a branch to clear specifically.
*/
package org.sonar.server.scannercache;
+import java.util.Optional;
+import java.util.Set;
import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
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.component.BranchDao;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.project.ProjectDao;
+import org.sonar.db.project.ProjectDto;
import org.sonar.db.scannercache.ScannerAnalysisCacheDao;
@ServerSide
public class ScannerCache {
private final DbClient dbClient;
private final ScannerAnalysisCacheDao dao;
+ private final ProjectDao projectDao;
+ private final BranchDao branchDao;
- public ScannerCache(DbClient dbClient, ScannerAnalysisCacheDao dao) {
+ public ScannerCache(DbClient dbClient, ScannerAnalysisCacheDao dao, ProjectDao projectDao, BranchDao branchDao) {
this.dbClient = dbClient;
this.dao = dao;
+ this.projectDao = projectDao;
+ this.branchDao = branchDao;
}
@CheckForNull
}
}
+ /**
+ * clear all the cache.
+ */
public void clear() {
- try (DbSession session = dbClient.openSession(false)) {
- dao.removeAll(session);
+ doClear(null, null);
+ }
+
+ /**
+ * clear the project cache, that is the cache of all the branches of the project.
+ *
+ * @param projectKey the key of the project.
+ */
+ public void clearProject(String projectKey) {
+ doClear(projectKey, null);
+ }
+
+ /**
+ * clear the branch cache, that is the cache of this branch only.
+ *
+ * @param projectKey the key of the project.
+ * @param branchKey the key of the specific branch.
+ */
+ public void clearBranch(String projectKey, String branchKey) {
+ doClear(projectKey, branchKey);
+ }
+
+ private void doClear(@Nullable final String projectKey, @Nullable final String branchKey) {
+ try (final DbSession session = dbClient.openSession(true)) {
+ if (projectKey == null) {
+ dao.removeAll(session);
+ } else {
+ Optional<ProjectDto> projectDto = projectDao.selectProjectByKey(session, projectKey);
+ projectDto.stream().flatMap(pDto -> {
+ if (branchKey == null) {
+ return branchDao.selectByProject(session, pDto).stream();
+ } else {
+ return branchDao.selectByKeys(session, pDto.getUuid(), Set.of(branchKey)).stream();
+ }
+ })
+ .map(BranchDto::getUuid)
+ .forEach(bUuid -> dao.remove(session, bUuid));
+ }
session.commit();
}
}
import org.sonar.server.scannercache.ScannerCache;
import org.sonar.server.user.UserSession;
+import static com.google.common.base.Preconditions.checkArgument;
import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
public class ClearAction implements AnalysisCacheWsAction {
+ public static final String PARAM_PROJECT_KEY = "project";
+ public static final String PARAM_BRANCH_KEY = "branch";
private final UserSession userSession;
private final ScannerCache cache;
@Override
public void define(WebService.NewController context) {
- context.createAction("clear")
+ WebService.NewAction clearDefinition = context.createAction("clear")
.setInternal(true)
.setPost(true)
- .setDescription("Clear the scanner's cached data for all projects and branches. Requires global administration permission. ")
+ .setDescription("Clear all or part of the scanner's cached data. Requires global administration permission.")
.setSince("9.4")
.setHandler(this);
+ clearDefinition.createParam(PARAM_PROJECT_KEY)
+ .setRequired(false)
+ .setSince("9.7")
+ .setDescription("Filter which project's cached data will be cleared with the provided key.")
+ .setExampleValue("org.sonarsource.sonarqube:sonarqube-private");
+ clearDefinition.createParam(PARAM_BRANCH_KEY)
+ .setRequired(false)
+ .setSince("9.7")
+ .setDescription("Filter which project's branch's cached data will be cleared with the provided key. '" + PARAM_PROJECT_KEY + "' parameter must be set.")
+ .setExampleValue("6468");
+ }
+
+ private static class ClearRequestDto {
+ private final String projectKey;
+ private final String branchKey;
+
+ private ClearRequestDto(Request request) {
+ this.projectKey = request.param(PARAM_PROJECT_KEY);
+ this.branchKey = request.param(PARAM_BRANCH_KEY);
+ }
}
@Override
public void handle(Request request, Response response) throws Exception {
checkPermission();
- cache.clear();
+ ClearRequestDto params = new ClearRequestDto(request);
+ validateParams(params);
+ if (params.projectKey != null) {
+ if (params.branchKey != null) {
+ cache.clearBranch(params.projectKey, params.branchKey);
+ } else {
+ cache.clearProject(params.projectKey);
+ }
+ } else {
+ cache.clear();
+ }
response.noContent();
}
+ private static void validateParams(ClearRequestDto params) {
+ if (params.branchKey != null) {
+ checkArgument(
+ params.projectKey != null,
+ "{} needs to be specified when {} is present",
+ PARAM_PROJECT_KEY, PARAM_BRANCH_KEY);
+ }
+ }
+
private void checkPermission() {
if (!userSession.hasPermission(GlobalPermission.ADMINISTER)) {
throw insufficientPrivilegesException();
import org.sonar.api.utils.System2;
import org.sonar.db.DbInputStream;
import org.sonar.db.DbTester;
+import org.sonar.db.audit.NoOpAuditPersister;
+import org.sonar.db.component.BranchDao;
+import org.sonar.db.component.BranchDto;
import org.sonar.db.permission.GlobalPermission;
+import org.sonar.db.project.ProjectDao;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.scannercache.ScannerAnalysisCacheDao;
import org.sonar.server.exceptions.ForbiddenException;
public DbTester dbTester = DbTester.create(System2.INSTANCE);
private final ScannerAnalysisCacheDao dao = new ScannerAnalysisCacheDao();
- private final ScannerCache cache = new ScannerCache(dbTester.getDbClient(), dao);
+ private final ProjectDao projectDao = new ProjectDao(System2.INSTANCE, new NoOpAuditPersister());
+ private final BranchDao branchDao = new BranchDao(System2.INSTANCE);
+ private final ScannerCache cache = new ScannerCache(dbTester.getDbClient(), dao, projectDao, branchDao);
private final ClearAction ws = new ClearAction(userSession, cache);
private final WsActionTester wsTester = new WsActionTester(ws);
assertThat(dbTester.countRowsOfTable("scanner_analysis_cache")).isZero();
}
+ @Test
+ public void should_clear_project_cache() {
+ ProjectDto project1 = dbTester.components().insertPrivateProjectDto(cDto -> cDto.setKey("p1"));
+ BranchDto branch11 = dbTester.components().insertProjectBranch(project1, cdto -> cdto.setKey("b11"));
+ BranchDto branch12 = dbTester.components().insertProjectBranch(project1, cdto -> cdto.setKey("b12"));
+ ProjectDto project2 = dbTester.components().insertPrivateProjectDto(cDto -> cDto.setKey("p2"));
+ BranchDto branch21 = dbTester.components().insertProjectBranch(project2, cdto -> cdto.setKey("b21"));
+
+ dao.insert(dbTester.getSession(), branch11.getUuid(), stringToInputStream("test data"));
+ dao.insert(dbTester.getSession(), branch12.getUuid(), stringToInputStream("test data"));
+ dao.insert(dbTester.getSession(), branch21.getUuid(), stringToInputStream("test data"));
+
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER);
+ TestResponse response = wsTester.newRequest()
+ .setParam(ClearAction.PARAM_PROJECT_KEY, "p1")
+ .execute();
+
+ response.assertNoContent();
+ assertThat(cache.get(branch11.getUuid())).isNull();
+ assertThat(cache.get(branch12.getUuid())).isNull();
+ assertThat(cache.get(branch21.getUuid())).isNotNull();
+ }
+
+ @Test
+ public void should_clear_branch_cache() {
+ ProjectDto project1 = dbTester.components().insertPrivateProjectDto(cDto -> cDto.setKey("p1"));
+ BranchDto branch11 = dbTester.components().insertProjectBranch(project1, cdto -> cdto.setKey("b11"));
+ BranchDto branch12 = dbTester.components().insertProjectBranch(project1, cdto -> cdto.setKey("b12"));
+ ProjectDto project2 = dbTester.components().insertPrivateProjectDto(cDto -> cDto.setKey("p2"));
+ BranchDto branch21 = dbTester.components().insertProjectBranch(project2, cdto -> cdto.setKey("b21"));
+
+ dao.insert(dbTester.getSession(), branch11.getUuid(), stringToInputStream("test data"));
+ dao.insert(dbTester.getSession(), branch12.getUuid(), stringToInputStream("test data"));
+ dao.insert(dbTester.getSession(), branch21.getUuid(), stringToInputStream("test data"));
+
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER);
+ TestResponse response = wsTester.newRequest()
+ .setParam(ClearAction.PARAM_PROJECT_KEY, "p1")
+ .setParam(ClearAction.PARAM_BRANCH_KEY, "b11")
+ .execute();
+
+ response.assertNoContent();
+ assertThat(cache.get(branch11.getUuid())).isNull();
+ assertThat(cache.get(branch12.getUuid())).isNotNull();
+ assertThat(cache.get(branch21.getUuid())).isNotNull();
+ }
+
+ @Test
+ public void should_fail_on_missing_project_key() {
+ userSession.logIn().addPermission(GlobalPermission.ADMINISTER);
+ TestRequest failingRequest = wsTester.newRequest().setParam(ClearAction.PARAM_BRANCH_KEY, "b11");
+
+ assertThatThrownBy(failingRequest::execute, "missing project key param with branch present should throw exception")
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
@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");
+ DbInputStream branchDataIs = dao.selectData(dbTester.getSession(), "branch1");
+ assertThat(branchDataIs).isNotNull();
+ assertThat(dataStreamToString(branchDataIs)).isEqualTo("test data");
userSession.logIn().addProjectPermission(SCAN, project);
TestRequest request = wsTester.newRequest();
import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
import org.sonar.db.DbTester;
+import org.sonar.db.audit.NoOpAuditPersister;
+import org.sonar.db.component.BranchDao;
import org.sonar.db.component.BranchDto;
-import org.sonar.db.component.BranchType;
+import org.sonar.db.project.ProjectDao;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.scannercache.ScannerAnalysisCacheDao;
import org.sonar.server.component.ComponentFinder;
public DbTester dbTester = DbTester.create(System2.INSTANCE);
private final ScannerAnalysisCacheDao dao = new ScannerAnalysisCacheDao();
- private final ScannerCache cache = new ScannerCache(dbTester.getDbClient(), dao);
+ private final ProjectDao projectDao = new ProjectDao(System2.INSTANCE, new NoOpAuditPersister());
+ private final BranchDao branchDao = new BranchDao(System2.INSTANCE);
+ private final ScannerCache cache = new ScannerCache(dbTester.getDbClient(), dao, projectDao, branchDao);
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);