]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17443 clear cache of a project or a branch
authorSteve Marion <steve.marion@sonarsource.com>
Wed, 12 Oct 2022 12:36:08 +0000 (14:36 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 14 Oct 2022 20:03:03 +0000 (20:03 +0000)
Add optional parameters to clear cache WS to allow specifying a project and a branch to clear specifically.

server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ScannerCache.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/scannercache/ws/ClearAction.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/ClearActionTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/scannercache/ws/GetActionTest.java

index e38f4600b8539841d1c053f4b3dae43dd39bf22b..6ec48769ab849b84d71fa4a8c3662166196c899a 100644 (file)
  */
 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
@@ -43,9 +54,48 @@ public class ScannerCache {
     }
   }
 
+  /**
+   * 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();
     }
   }
index 79fbd3bb182cadf2a3123c2fb7de0e626a4a67a5..9212e3fa830ab8d08fe081624169aca4fa436c16 100644 (file)
@@ -26,9 +26,12 @@ import org.sonar.db.permission.GlobalPermission;
 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;
 
@@ -39,21 +42,60 @@ public class ClearAction implements AnalysisCacheWsAction {
 
   @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();
index 50c9c54492668d9c2725e7810caa0e818a5a59da..cecbc107882699ceef6b56077bd1484fbacce01a 100644 (file)
@@ -29,7 +29,11 @@ import org.junit.Test;
 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;
@@ -50,7 +54,9 @@ public class ClearActionTest {
   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);
 
@@ -70,11 +76,69 @@ public class ClearActionTest {
     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();
 
index 4bfca1570f7e8343ce3ae878cf5966eeb9090d27..12db408623176f0d9c217a72b16b03fd74134b40 100644 (file)
@@ -31,8 +31,10 @@ 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.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;
@@ -56,7 +58,9 @@ public class GetActionTest {
   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);