]> source.dussan.org Git - sonarqube.git/commitdiff
GOV-334 trigger views refresh on api/projects/update_key call
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Thu, 29 Mar 2018 10:30:35 +0000 (12:30 +0200)
committerSonarTech <sonartech@sonarsource.com>
Fri, 6 Apr 2018 18:21:52 +0000 (20:21 +0200)
server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java
server/sonar-server/src/main/java/org/sonar/server/project/ProjectLifeCycleListener.java
server/sonar-server/src/main/java/org/sonar/server/project/ProjectLifeCycleListeners.java
server/sonar-server/src/main/java/org/sonar/server/project/ProjectLifeCycleListenersImpl.java
server/sonar-server/src/main/java/org/sonar/server/project/RekeyedProject.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceTest.java
server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceUpdateKeyTest.java
server/sonar-server/src/test/java/org/sonar/server/project/RekeyedProjectTest.java [new file with mode: 0644]

index a5d047c2d7e68419ced18c512bd298594764ece5..3c21cad71d76dc490ed03c7b71c3ebe0fefe4f29 100644 (file)
@@ -26,6 +26,9 @@ import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.server.es.ProjectIndexer;
 import org.sonar.server.es.ProjectIndexers;
+import org.sonar.server.project.Project;
+import org.sonar.server.project.ProjectLifeCycleListeners;
+import org.sonar.server.project.RekeyedProject;
 import org.sonar.server.user.UserSession;
 
 import static java.util.Collections.singletonList;
@@ -38,23 +41,27 @@ public class ComponentService {
   private final DbClient dbClient;
   private final UserSession userSession;
   private final ProjectIndexers projectIndexers;
+  private final ProjectLifeCycleListeners projectLifeCycleListeners;
 
-  public ComponentService(DbClient dbClient, UserSession userSession, ProjectIndexers projectIndexers) {
+  public ComponentService(DbClient dbClient, UserSession userSession, ProjectIndexers projectIndexers, ProjectLifeCycleListeners projectLifeCycleListeners) {
     this.dbClient = dbClient;
     this.userSession = userSession;
     this.projectIndexers = projectIndexers;
+    this.projectLifeCycleListeners = projectLifeCycleListeners;
   }
 
-  // TODO should be moved to UpdateKeyAction
   public void updateKey(DbSession dbSession, ComponentDto projectOrModule, String newKey) {
     userSession.checkComponentPermission(UserRole.ADMIN, projectOrModule);
     checkIsProjectOrModule(projectOrModule);
     checkProjectOrModuleKeyFormat(newKey);
     dbClient.componentKeyUpdaterDao().updateKey(dbSession, projectOrModule.uuid(), newKey);
     projectIndexers.commitAndIndex(dbSession, singletonList(projectOrModule), ProjectIndexer.Cause.PROJECT_KEY_UPDATE);
+    if (projectOrModule.isRootProject() && projectOrModule.getMainBranchProjectUuid() == null) {
+      Project newProject = new Project(projectOrModule.uuid(), newKey, projectOrModule.name(), projectOrModule.description());
+      projectLifeCycleListeners.onProjectRekeyed(new RekeyedProject(newProject, projectOrModule.getDbKey()));
+    }
   }
 
-  // TODO should be moved to BulkUpdateKeyAction
   public void bulkUpdateKey(DbSession dbSession, ComponentDto projectOrModule, String stringToReplace, String replacementString) {
     dbClient.componentKeyUpdaterDao().bulkUpdateKey(dbSession, projectOrModule.uuid(), stringToReplace, replacementString);
     projectIndexers.commitAndIndex(dbSession, singletonList(projectOrModule), ProjectIndexer.Cause.PROJECT_KEY_UPDATE);
index 16601035f75ccbd0ce312d291b718a561796437c..3c555307c6df56fc8144c7f3bfbe15d3934e07fe 100644 (file)
@@ -28,4 +28,9 @@ public interface ProjectLifeCycleListener {
    * This method is called after the specified projects have been deleted.
    */
   void onProjectsDeleted(Set<Project> projects);
+
+  /**
+   * This method is called after the specified project's key has been modified.
+   */
+  void onProjectRekeyed(RekeyedProject rekeyedProject);
 }
index 352826a2677236f60d91a7ca8d6add47b342f270..c78083edf0bc205219210ed8fbea995e9aa8f842 100644 (file)
@@ -31,4 +31,15 @@ public interface ProjectLifeCycleListeners {
    * them fail with an exception.
    */
   void onProjectsDeleted(Set<Project> projects);
+
+  /**
+   * This method is called after the specified project's key has been changed and will call method
+   * {@link ProjectLifeCycleListener#onProjectRekeyed(RekeyedProject) onProjectRekeyed(RekeyedProject)} of all known
+   * {@link ProjectLifeCycleListener} implementations.
+   * <p>
+   * This method ensures all {@link ProjectLifeCycleListener} implementations are called, even if one or more of
+   * them fail with an exception.
+   */
+  void onProjectRekeyed(RekeyedProject rekeyedProject);
+
 }
index 3e43c0ab7be337a16fb3dc00506f88c011cacbe2..9714ee98d497124b63739ce68e71cbf9d30667d5 100644 (file)
  */
 package org.sonar.server.project;
 
-import com.google.common.base.Preconditions;
 import java.util.Arrays;
 import java.util.Set;
 import java.util.function.Consumer;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 public class ProjectLifeCycleListenersImpl implements ProjectLifeCycleListeners {
   private static final Logger LOG = Loggers.get(ProjectLifeCycleListenersImpl.class);
 
@@ -47,7 +48,7 @@ public class ProjectLifeCycleListenersImpl implements ProjectLifeCycleListeners
 
   @Override
   public void onProjectsDeleted(Set<Project> projects) {
-    Preconditions.checkNotNull(projects, "projects can't be null");
+    checkNotNull(projects, "projects can't be null");
     if (projects.isEmpty()) {
       return;
     }
@@ -56,12 +57,20 @@ public class ProjectLifeCycleListenersImpl implements ProjectLifeCycleListeners
       .forEach(safelyCallListener(listener -> listener.onProjectsDeleted(projects)));
   }
 
+  @Override
+  public void onProjectRekeyed(RekeyedProject rekeyedProject) {
+    checkNotNull(rekeyedProject, "rekeyedProject can't be null");
+
+    Arrays.stream(listeners)
+      .forEach(safelyCallListener(listener -> listener.onProjectRekeyed(rekeyedProject)));
+  }
+
   private static Consumer<ProjectLifeCycleListener> safelyCallListener(Consumer<ProjectLifeCycleListener> task) {
     return listener -> {
       try {
         task.accept(listener);
       } catch (Error | Exception e) {
-        LOG.error("Call to ProjectLifeCycleListener \"{}\" failed", listener.getClass(), e);
+        LOG.error("Call on ProjectLifeCycleListener \"{}\" failed", listener.getClass(), e);
       }
     };
   }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/RekeyedProject.java b/server/sonar-server/src/main/java/org/sonar/server/project/RekeyedProject.java
new file mode 100644 (file)
index 0000000..1f0ce75
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.project;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public final class RekeyedProject {
+  private final Project project;
+  private final String previousKey;
+
+  public RekeyedProject(Project project, String previousKey) {
+    this.project = checkNotNull(project, "project can't be null");
+    this.previousKey = checkNotNull(previousKey, "previousKey can't be null");
+  }
+
+  public Project getProject() {
+    return project;
+  }
+
+  public String getPreviousKey() {
+    return previousKey;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    RekeyedProject that = (RekeyedProject) o;
+    return Objects.equals(project, that.project) &&
+      Objects.equals(previousKey, that.previousKey);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(project, previousKey);
+  }
+
+  @Override
+  public String toString() {
+    return "RekeyedProject{" +
+      "project=" + project +
+      ", previousKey='" + previousKey + '\'' +
+      '}';
+  }
+}
index bd5c5740a3a8df96954009d5ffe29fe658aa7575..420f1c049d436776a19c20f289e85bd88922c0f9 100644 (file)
@@ -30,9 +30,11 @@ import org.sonar.db.component.ComponentDbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTesting;
 import org.sonar.server.es.TestProjectIndexers;
+import org.sonar.server.project.ProjectLifeCycleListeners;
 import org.sonar.server.tester.UserSessionRule;
 
 import static org.assertj.guava.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
 import static org.sonar.db.component.ComponentTesting.newModuleDto;
 
@@ -49,8 +51,9 @@ public class ComponentServiceTest {
   private DbClient dbClient = dbTester.getDbClient();
   private DbSession dbSession = dbTester.getSession();
   private TestProjectIndexers projectIndexers = new TestProjectIndexers();
+  private ProjectLifeCycleListeners projectLifeCycleListeners = mock(ProjectLifeCycleListeners.class);
 
-  private ComponentService underTest = new ComponentService(dbClient, userSession, projectIndexers);
+  private ComponentService underTest = new ComponentService(dbClient, userSession, projectIndexers, projectLifeCycleListeners);
 
   @Test
   public void bulk_update() {
index f8932006235f3f6166a35e17d1869ef93d85ba4d..3883ad250ca605bc60befe03c296bd479f79aaa3 100644 (file)
@@ -34,9 +34,11 @@ import org.sonar.server.es.ProjectIndexer;
 import org.sonar.server.es.TestProjectIndexers;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.project.ProjectLifeCycleListeners;
 import org.sonar.server.tester.UserSessionRule;
 
 import static org.assertj.guava.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
 import static org.sonar.db.component.ComponentTesting.newModuleDto;
 
@@ -55,7 +57,8 @@ public class ComponentServiceUpdateKeyTest {
   private DbClient dbClient = db.getDbClient();
   private DbSession dbSession = db.getSession();
   private TestProjectIndexers projectIndexers = new TestProjectIndexers();
-  private ComponentService underTest = new ComponentService(dbClient, userSession, projectIndexers);
+  private ProjectLifeCycleListeners projectLifeCycleListeners = mock(ProjectLifeCycleListeners.class);
+  private ComponentService underTest = new ComponentService(dbClient, userSession, projectIndexers, projectLifeCycleListeners);
 
   @Test
   public void update_project_key() {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/RekeyedProjectTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/RekeyedProjectTest.java
new file mode 100644 (file)
index 0000000..3e9ddd7
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.project;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RekeyedProjectTest {
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Test
+  public void constructor_throws_NPE_if_project_is_null() {
+    expectedException.expect(NullPointerException.class);
+    expectedException.expectMessage("project can't be null");
+
+    new RekeyedProject(null, randomAlphanumeric(3));
+  }
+
+  @Test
+  public void constructor_throws_NPE_if_previousKey_is_null() {
+    expectedException.expect(NullPointerException.class);
+    expectedException.expectMessage("previousKey can't be null");
+
+    new RekeyedProject(newRandomProject(), null);
+  }
+
+  @Test
+  public void verify_getters() {
+    Project project = newRandomProject();
+    String previousKey = randomAlphanumeric(6);
+    RekeyedProject underTest = new RekeyedProject(project, previousKey);
+
+    assertThat(underTest.getProject()).isSameAs(project);
+    assertThat(underTest.getPreviousKey()).isEqualTo(previousKey);
+  }
+
+  @Test
+  public void equals_is_based_on_project_and_previousKey() {
+    Project project = newRandomProject();
+    String previousKey = randomAlphanumeric(6);
+    RekeyedProject underTest = new RekeyedProject(project, previousKey);
+
+    assertThat(underTest).isEqualTo(underTest);
+    assertThat(underTest).isEqualTo(new RekeyedProject(project, previousKey));
+    assertThat(underTest).isNotEqualTo(new RekeyedProject(project, randomAlphanumeric(11)));
+    assertThat(underTest).isNotEqualTo(new RekeyedProject(newRandomProject(), previousKey));
+    assertThat(underTest).isNotEqualTo(new Object());
+    assertThat(underTest).isNotEqualTo(null);
+  }
+
+  @Test
+  public void hashCode_is_based_on_project_and_previousKey() {
+    Project project = newRandomProject();
+    String previousKey = randomAlphanumeric(6);
+    RekeyedProject underTest = new RekeyedProject(project, previousKey);
+
+    assertThat(underTest.hashCode()).isEqualTo(underTest.hashCode());
+    assertThat(underTest.hashCode()).isEqualTo(new RekeyedProject(project, previousKey).hashCode());
+    assertThat(underTest.hashCode()).isNotEqualTo(new RekeyedProject(project, randomAlphanumeric(11)).hashCode());
+    assertThat(underTest.hashCode()).isNotEqualTo(new RekeyedProject(newRandomProject(), previousKey).hashCode());
+    assertThat(underTest.hashCode()).isNotEqualTo(new Object().hashCode());
+    assertThat(underTest.hashCode()).isNotEqualTo(null);
+  }
+
+  @Test
+  public void verify_toString() {
+    Project project = new Project("A", "B", "C", "D");
+    String previousKey = "E";
+    RekeyedProject underTest = new RekeyedProject(project, previousKey);
+
+    assertThat(underTest.toString()).isEqualTo("RekeyedProject{project=Project{uuid='A', key='B', name='C', description='D'}, previousKey='E'}");
+  }
+
+  private static Project newRandomProject() {
+    return new Project(randomAlphanumeric(3), randomAlphanumeric(4), randomAlphanumeric(5));
+  }
+}