]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12366 Create WS to read/write New Code Periods
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Fri, 9 Aug 2019 12:56:38 +0000 (07:56 -0500)
committerSonarTech <sonartech@sonarsource.com>
Tue, 24 Sep 2019 18:21:13 +0000 (20:21 +0200)
server/sonar-db-dao/src/main/java/org/sonar/db/newcodeperiod/NewCodePeriodDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/newcodeperiod/NewCodePeriodMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/newcodeperiod/NewCodePeriodMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/newcodeperiod/NewCodePeriodDaoTest.java
server/sonar-server/src/main/java/org/sonar/server/setting/ws/DeleteNewCodePeriodAction.java
server/sonar-server/src/test/java/org/sonar/server/setting/ws/DeleteNewCodePeriodActionTest.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/projectanalysis/ws/UnsetBaselineAction.java

index 0cb308b69912e8b1d573d1b3dde10460961b8d4b..66290c83d1c4e887f9b6dbb9bb4fc7199f0d0a22 100644 (file)
 package org.sonar.db.newcodeperiod;
 
 import java.util.Optional;
+import javax.annotation.Nullable;
 import org.sonar.api.utils.System2;
 import org.sonar.core.util.UuidFactory;
 import org.sonar.db.Dao;
 import org.sonar.db.DbSession;
 
 import static java.util.Objects.requireNonNull;
+import static org.sonar.api.utils.Preconditions.checkArgument;
 
 public class NewCodePeriodDao implements Dao {
 
@@ -85,10 +87,13 @@ public class NewCodePeriodDao implements Dao {
     return mapper(dbSession).countByProjectAnalysis(projectAnalysisUuid) > 0;
   }
 
-  public void deleteByBranch(DbSession dbSession, String projectUuid, String branchUuid) {
-    requireNonNull(projectUuid, "Project uuid must be specified.");
-    requireNonNull(branchUuid, "Branch uuid must be specified.");
-    mapper(dbSession).deleteByBranch(projectUuid, branchUuid);
+  /**
+   * Deletes an entry. It can be the global setting or a specific project or branch setting.
+   * Note that deleting project's setting doesn't delete the settings of the branches belonging to that project.
+   */
+  public void delete(DbSession dbSession, @Nullable String projectUuid, @Nullable String branchUuid) {
+    checkArgument(branchUuid == null || projectUuid != null, "branchUuid must be null if projectUuid is null");
+    mapper(dbSession).delete(projectUuid, branchUuid);
   }
 
   private static NewCodePeriodMapper mapper(DbSession session) {
index ab5548deea4c0721fa9e3efbae3f5758de724266..e692ab95d48ca3c7c94b8ac61a636b030198f354 100644 (file)
@@ -34,7 +34,7 @@ public interface NewCodePeriodMapper {
 
   NewCodePeriodDto selectByProject(String projectUuid);
 
-  void deleteByBranch(@Param("projectUuid") String projectUuid, @Param("branchUuid") String branchUuid);
+  void delete(@Param("projectUuid") String projectUuid, @Param("branchUuid") String branchUuid);
 
   NewCodePeriodDto selectByBranch(@Param("projectUuid") String projectUuid, @Param("branchUuid") String branchUuid);
 
index 8ec1e59cf75114a8774254290132ccf9cec533dd..7c7463a963098a801d5fa9043081ff9dab2a7409 100644 (file)
     AND ncp.branch_uuid=#{branchUuid, jdbcType=VARCHAR}
   </select>
 
-  <update id="deleteByBranch" parameterType="map">
+  <update id="delete" parameterType="map">
     DELETE
     FROM new_code_periods
     WHERE
-    project_uuid=#{projectUuid, jdbcType=VARCHAR}
-    AND branch_uuid=#{branchUuid, jdbcType=VARCHAR}
+    <choose>
+      <when test="projectUuid != null">
+        project_uuid=#{projectUuid, jdbcType=VARCHAR}
+      </when>
+      <otherwise>
+        project_uuid IS NULL
+      </otherwise>
+    </choose>
+    AND
+    <choose>
+      <when test="branchUuid != null">
+        branch_uuid=#{branchUuid, jdbcType=VARCHAR}
+      </when>
+      <otherwise>
+        branch_uuid IS NULL
+      </otherwise>
+    </choose>
   </update>
 
   <select id="countByProjectAnalysis" parameterType="map" resultType="java.lang.Long">
index c9e07d6addb8b3fc8e6f335eeb08dcdd6f64ae5c..ad2bf33d26f1630009c534f2f1fef434c37a2019 100644 (file)
@@ -269,7 +269,34 @@ public class NewCodePeriodDaoTest {
       .setType(NewCodePeriodType.SPECIFIC_ANALYSIS)
       .setValue("analysis-uuid"));
 
-    underTest.deleteByBranch(dbSession, "proj-uuid", "branch-uuid");
+    underTest.delete(dbSession, "proj-uuid", "branch-uuid");
+    db.commit();
+    assertNewCodePeriodRowCount(0);
+  }
+
+  @Test
+  public void delete_by_project_uuid() {
+    when(uuidFactory.create()).thenReturn(NEW_CODE_PERIOD_UUID);
+
+    underTest.insert(dbSession, new NewCodePeriodDto()
+      .setProjectUuid("proj-uuid")
+      .setType(NewCodePeriodType.SPECIFIC_ANALYSIS)
+      .setValue("analysis-uuid"));
+
+    underTest.delete(dbSession, "proj-uuid", null);
+    db.commit();
+    assertNewCodePeriodRowCount(0);
+  }
+
+  @Test
+  public void delete_global() {
+    when(uuidFactory.create()).thenReturn(NEW_CODE_PERIOD_UUID);
+
+    underTest.insert(dbSession, new NewCodePeriodDto()
+      .setType(NewCodePeriodType.SPECIFIC_ANALYSIS)
+      .setValue("analysis-uuid"));
+
+    underTest.delete(dbSession, null, null);
     db.commit();
     assertNewCodePeriodRowCount(0);
   }
index 1d8ae8a353f00ff54dbb9398400a11138469ce99..4c2950e7ba012861120856001de30b66271fd10c 100644 (file)
@@ -1,3 +1,22 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.setting.ws;
 
 import com.google.common.collect.ImmutableSet;
@@ -92,7 +111,7 @@ public class DeleteNewCodePeriodAction implements SettingsWsAction {
         userSession.checkIsSystemAdministrator();
       }
 
-      newCodePeriodDao.deleteByBranch(dbSession, projectUuid, branchUuid);
+      newCodePeriodDao.delete(dbSession, projectUuid, branchUuid);
       dbSession.commit();
     }
   }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/setting/ws/DeleteNewCodePeriodActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/setting/ws/DeleteNewCodePeriodActionTest.java
new file mode 100644 (file)
index 0000000..a04c216
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.setting.ws;
+
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.newcodeperiod.NewCodePeriodDao;
+import org.sonar.db.newcodeperiod.NewCodePeriodType;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.component.TestComponentFinder;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+
+public class DeleteNewCodePeriodActionTest {
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone();
+  @Rule
+  public DbTester db = DbTester.create(System2.INSTANCE);
+
+  private ComponentDbTester componentDb = new ComponentDbTester(db);
+  private DbClient dbClient = db.getDbClient();
+  private DbSession dbSession = db.getSession();
+  private ComponentFinder componentFinder = TestComponentFinder.from(db);
+  private NewCodePeriodDao dao = new NewCodePeriodDao(System2.INSTANCE, UuidFactoryFast.getInstance());
+
+  private DeleteNewCodePeriodAction underTest = new DeleteNewCodePeriodAction(dbClient, userSession, componentFinder, dao);
+  private WsActionTester ws = new WsActionTester(underTest);
+
+  @Test
+  public void test_definition() {
+    WebService.Action definition = ws.getDef();
+
+    assertThat(definition.key()).isEqualTo("delete_new_code_period");
+    assertThat(definition.isInternal()).isFalse();
+    assertThat(definition.since()).isEqualTo("8.0");
+    assertThat(definition.isPost()).isFalse();
+
+    assertThat(definition.params()).extracting(WebService.Param::key).containsOnly("project", "branch");
+    assertThat(definition.param("project").isRequired()).isFalse();
+    assertThat(definition.param("branch").isRequired()).isFalse();
+  }
+
+  // validation of project/branch
+  @Test
+  public void throw_IAE_if_branch_is_specified_without_project() {
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("If branch key is specified, project key needs to be specified too");
+
+    ws.newRequest()
+      .setParam("branch", "branch")
+      .execute();
+  }
+
+  @Test
+  public void throw_NFE_if_project_not_found() {
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage("Component key 'unknown' not found");
+
+    ws.newRequest()
+      .setParam("type", "previous_version")
+      .setParam("project", "unknown")
+      .execute();
+  }
+
+  @Test
+  public void throw_NFE_if_branch_not_found() {
+    ComponentDto project = componentDb.insertMainBranch();
+    logInAsProjectAdministrator(project);
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage("Component '" + project.getKey() + "' on branch 'unknown' not found");
+
+    ws.newRequest()
+      .setParam("project", project.getKey())
+      .setParam("type", "previous_version")
+      .setParam("branch", "unknown")
+      .execute();
+  }
+
+  @Test
+  public void throw_IAE_if_branch_is_a_SLB() {
+    ComponentDto project = componentDb.insertMainBranch();
+    ComponentDto branch = componentDb.insertProjectBranch(project, b -> b.setKey("branch").setBranchType(BranchType.SHORT));
+    logInAsProjectAdministrator(project);
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Not a long-living branch: 'branch'");
+
+    ws.newRequest()
+      .setParam("project", project.getKey())
+      .setParam("type", "previous_version")
+      .setParam("branch", "branch")
+      .execute();
+  }
+
+  // permission
+  @Test
+  public void throw_NFE_if_no_project_permission() {
+    ComponentDto project = componentDb.insertMainBranch();
+    expectedException.expect(ForbiddenException.class);
+    expectedException.expectMessage("Insufficient privileges");
+
+    ws.newRequest()
+      .setParam("project", project.getKey())
+      .setParam("type", "previous_version")
+      .execute();
+  }
+
+  @Test
+  public void throw_NFE_if_no_system_permission() {
+    expectedException.expect(ForbiddenException.class);
+    expectedException.expectMessage("Insufficient privileges");
+
+    ws.newRequest()
+      .setParam("type", "previous_version")
+      .execute();
+  }
+
+  // success cases
+  @Test
+  public void delete_global_period() {
+    logInAsSystemAdministrator();
+    ws.newRequest()
+      .execute();
+
+    assertTableEmpty();
+  }
+
+  @Test
+  public void delete_project_period() {
+    ComponentDto project = componentDb.insertMainBranch();
+    logInAsProjectAdministrator(project);
+    ws.newRequest()
+      .setParam("project", project.getKey())
+      .execute();
+
+    assertTableEmpty();
+  }
+
+  @Test
+  public void delete_project_period_twice() {
+    ComponentDto project = componentDb.insertMainBranch();
+    db.newCodePeriods().insert(project.uuid(), null, NewCodePeriodType.SPECIFIC_ANALYSIS, "uuid");
+
+    logInAsProjectAdministrator(project);
+    ws.newRequest()
+      .setParam("project", project.getKey())
+      .execute();
+    assertTableContainsOnly(project.uuid(), null, NewCodePeriodType.PREVIOUS_VERSION, null);
+
+    ws.newRequest()
+      .setParam("project", project.getKey())
+      .execute();
+
+    assertTableEmpty();
+  }
+
+  @Test
+  public void delete_branch_period() {
+    ComponentDto project = componentDb.insertMainBranch();
+    ComponentDto branch = componentDb.insertProjectBranch(project, b -> b.setKey("branch"));
+
+    db.newCodePeriods().insert(project.uuid(), null, NewCodePeriodType.SPECIFIC_ANALYSIS, "uuid1");
+    db.newCodePeriods().insert(project.uuid(), branch.uuid(), NewCodePeriodType.SPECIFIC_ANALYSIS, "uuid2");
+
+    logInAsProjectAdministrator(project);
+
+    ws.newRequest()
+      .setParam("project", project.getKey())
+      .setParam("branch", "branch")
+      .execute();
+
+    assertTableContainsOnly(project.uuid(), null, NewCodePeriodType.SPECIFIC_ANALYSIS, "uuid1");
+  }
+
+  private void assertTableEmpty() {
+    assertThat(db.countRowsOfTable(dbSession, "new_code_periods")).isEqualTo(0);
+  }
+
+  private void assertTableContainsOnly(@Nullable String projectUuid, @Nullable String branchUuid, NewCodePeriodType type, @Nullable String value) {
+    assertThat(db.countRowsOfTable(dbSession, "new_code_periods")).isEqualTo(1);
+    assertThat(db.selectFirst(dbSession, "select project_uuid, branch_uuid, type, value from new_code_periods"))
+      .containsOnly(entry("PROJECT_UUID", projectUuid), entry("BRANCH_UUID", branchUuid), entry("TYPE", type.name()), entry("VALUE", value));
+  }
+
+  private void logInAsProjectAdministrator(ComponentDto project) {
+    userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+  }
+
+  private void logInAsSystemAdministrator() {
+    userSession.logIn().setSystemAdministrator();
+  }
+}
index 4fa37823ddd3187575e68a527af5d4d5ee1e1cc5..441fe67adfc749e11347389d7826cd4947fe8ee7 100644 (file)
@@ -92,7 +92,7 @@ public class UnsetBaselineAction implements ProjectAnalysesWsAction {
 
       String projectUuid = projectBranch.getMainBranchProjectUuid() != null ? projectBranch.getMainBranchProjectUuid() : projectBranch.uuid();
       String branchUuid = projectBranch.uuid();
-      dbClient.newCodePeriodDao().deleteByBranch(dbSession, projectUuid, branchUuid);
+      dbClient.newCodePeriodDao().delete(dbSession, projectUuid, branchUuid);
       dbSession.commit();
     }
   }