}
}
+ public int updateMainBranchName(DbSession dbSession, String projectUuid, String newBranchKey) {
+ long now = system2.now();
+ return mapper(dbSession).updateMainBranchName(projectUuid, newBranchKey, now);
+ }
+
public Optional<BranchDto> selectByKey(DbSession dbSession, String projectUuid, BranchKeyType keyType, @Nullable String key) {
String keyInDb = BranchDto.convertKeyToDb(key);
return Optional.ofNullable(mapper(dbSession).selectByKey(projectUuid, keyType, keyInDb));
return Optional.ofNullable(mapper(session).selectByUuid(uuid));
}
-
private static BranchMapper mapper(DbSession dbSession) {
return dbSession.getMapper(BranchMapper.class);
}
int update(@Param("dto") BranchDto dto, @Param("now") long now);
+ int updateMainBranchName(@Param("projectUuid") String projectUuid, @Param("newBranchName") String newBranchName, @Param("now") long now);
+
BranchDto selectByKey(@Param("projectUuid") String projectUuid,
@Param("keyType") BranchKeyType keyType, @Param("key") String key);
)
</insert>
+ <update id="updateMainBranchName" parameterType="map">
+ update project_branches
+ set
+ kee = #{newBranchName, jdbcType=VARCHAR},
+ updated_at = #{now, jdbcType=BIGINT}
+ where
+ uuid = #{projectUuid, jdbcType=VARCHAR}
+ </update>
+
<update id="update" parameterType="map" useGeneratedKeys="false">
update project_branches
set
entry("mergeBranchUuid", null),
entry("pullRequestTitle", null),
entry("createdAt", 1_000L),
- entry("updatedAt", 1_000L)
- );
+ entry("updatedAt", 1_000L));
+ }
+
+ @Test
+ public void update_main_branch_name() {
+ BranchDto dto = new BranchDto();
+ dto.setProjectUuid("U1");
+ dto.setUuid("U1");
+ dto.setBranchType(BranchType.LONG);
+ dto.setKeeType(BranchKeyType.BRANCH);
+ dto.setKey(null);
+ underTest.insert(dbSession, dto);
+
+ BranchDto dto2 = new BranchDto();
+ dto2.setProjectUuid("U2");
+ dto2.setUuid("U2");
+ dto2.setBranchType(BranchType.LONG);
+ dto2.setKeeType(BranchKeyType.BRANCH);
+ dto2.setKey("branch");
+ underTest.insert(dbSession, dto2);
+
+ underTest.updateMainBranchName(dbSession, "U1", "master");
+ BranchDto loaded = underTest.selectByKey(dbSession, "U1", BranchKeyType.BRANCH, "master").get();
+ assertThat(loaded.getMergeBranchUuid()).isNull();
+ assertThat(loaded.getPullRequestTitle()).isNull();
+ assertThat(loaded.getProjectUuid()).isEqualTo("U1");
+ assertThat(loaded.getBranchType()).isEqualTo(BranchType.LONG);
+ assertThat(loaded.getKeeType()).isEqualTo(BranchKeyType.BRANCH);
}
@Test
underTest.insert(dbSession, dto);
Map<String, Object> map = db.selectFirst(dbSession, SELECT_FROM + " where uuid='" + dto.getUuid() + "'");
- assertThat((String)map.get("projectUuid")).contains("a").isEqualTo(dto.getProjectUuid());
- assertThat((String)map.get("uuid")).contains("b").isEqualTo(dto.getUuid());
- assertThat((String)map.get("kee")).contains("c").isEqualTo(dto.getKey());
- assertThat((String)map.get("mergeBranchUuid")).contains("d").isEqualTo(dto.getMergeBranchUuid());
- assertThat((String)map.get("pullRequestTitle")).contains("e").isEqualTo(dto.getPullRequestTitle());
+ assertThat((String) map.get("projectUuid")).contains("a").isEqualTo(dto.getProjectUuid());
+ assertThat((String) map.get("uuid")).contains("b").isEqualTo(dto.getUuid());
+ assertThat((String) map.get("kee")).contains("c").isEqualTo(dto.getKey());
+ assertThat((String) map.get("mergeBranchUuid")).contains("d").isEqualTo(dto.getMergeBranchUuid());
+ assertThat((String) map.get("pullRequestTitle")).contains("e").isEqualTo(dto.getPullRequestTitle());
}
@Test
assertThat(underTest.selectByUuids(db.getSession(), asList(branch1.uuid(), branch2.uuid(), branch3.uuid())))
.extracting(BranchDto::getUuid)
- .containsExactlyInAnyOrder(branch1.uuid(), branch2.uuid(), branch3.uuid());
+ .containsExactlyInAnyOrder(branch1.uuid(), branch2.uuid(), branch3.uuid());
assertThat(underTest.selectByUuids(db.getSession(), singletonList(branch1.uuid())))
.extracting(BranchDto::getUuid)
.containsExactlyInAnyOrder(branch1.uuid());
ListAction.class,
ShowAction.class,
DeleteAction.class,
+ RenameAction.class,
BranchesWs.class);
}
}
import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.ACTION_DELETE;
import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_BRANCH;
-import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_PROJECT;;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_PROJECT;
public class DeleteAction implements BranchWsAction {
private final DbClient dbClient;
}
private void checkPermission(ComponentDto project) {
- userSession.hasComponentPermission(UserRole.ADMIN, project);
+ userSession.checkComponentPermission(UserRole.ADMIN, project);
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.projectbranch.ws;
+
+import java.util.Optional;
+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.server.ws.WebService.NewController;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.BranchKeyType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.user.UserSession;
+
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.ACTION_RENAME;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_BRANCH;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_PROJECT;
+
+public class RenameAction implements BranchWsAction {
+ private final ComponentFinder componentFinder;
+ private final UserSession userSession;
+ private final DbClient dbClient;
+
+ public RenameAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession) {
+ this.dbClient = dbClient;
+ this.componentFinder = componentFinder;
+ this.userSession = userSession;
+ }
+
+ @Override
+ public void define(NewController context) {
+ WebService.NewAction action = context.createAction(ACTION_RENAME)
+ .setSince("6.6")
+ .setDescription("Rename the main branch of a project. <br />"
+ + "Requires 'Administer' permission on the specified project.")
+ .setInternal(true)
+ .setPost(true)
+ .setHandler(this);
+
+ action
+ .createParam(PARAM_PROJECT)
+ .setDescription("Project key")
+ .setExampleValue(KEY_PROJECT_EXAMPLE_001)
+ .setRequired(true);
+ action
+ .createParam(PARAM_BRANCH)
+ .setDescription("New name of the main branch")
+ .setExampleValue("master")
+ .setRequired(true);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ userSession.checkLoggedIn();
+ String projectKey = request.mandatoryParam(PARAM_PROJECT);
+ String branchKey = request.mandatoryParam(PARAM_BRANCH);
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ ComponentDto project = componentFinder.getRootComponentByUuidOrKey(dbSession, null, projectKey);
+ checkPermission(project);
+
+ Optional<BranchDto> branch = dbClient.branchDao().selectByKey(dbSession, project.uuid(), BranchKeyType.BRANCH, branchKey);
+ if (branch.isPresent() && !branch.get().isMain()) {
+ throw new IllegalArgumentException("Impossible to update branch name: a branch with name \"" + branchKey + "\" already exists in the project.");
+ }
+
+ dbClient.branchDao().updateMainBranchName(dbSession, project.uuid(), branchKey);
+ dbSession.commit();
+ response.noContent();
+ }
+ }
+
+ private void checkPermission(ComponentDto project) {
+ userSession.checkComponentPermission(UserRole.ADMIN, project);
+ }
+
+}
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new BranchWsModule().configure(container);
- assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 4);
+ assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 5);
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.projectbranch.ws;
+
+import java.util.Optional;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.resources.ResourceTypes;
+import org.sonar.api.server.ws.WebService;
+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.component.ComponentDto;
+import org.sonar.db.component.ResourceTypesRule;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.api.resources.Qualifiers.PROJECT;
+
+public class RenameActionTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ private ResourceTypes resourceTypes = new ResourceTypesRule().setRootQualifiers(PROJECT);
+ private ComponentFinder componentFinder = new ComponentFinder(db.getDbClient(), resourceTypes);
+ private WsActionTester tester = new WsActionTester(new RenameAction(db.getDbClient(), componentFinder, userSession));
+
+ @Test
+ public void test_definition() {
+ WebService.Action definition = tester.getDef();
+ assertThat(definition.key()).isEqualTo("rename");
+ assertThat(definition.isPost()).isTrue();
+ assertThat(definition.isInternal()).isTrue();
+ assertThat(definition.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("project", "branch");
+ assertThat(definition.since()).isEqualTo("6.6");
+ }
+
+ @Test
+ public void fail_if_missing_project_parameter() {
+ userSession.logIn();
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'project' parameter is missing");
+
+ tester.newRequest().execute();
+ }
+
+ @Test
+ public void fail_if_missing_branch_parameter() {
+ userSession.logIn();
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'branch' parameter is missing");
+
+ tester.newRequest().setParam("project", "projectName").execute();
+ }
+
+ @Test
+ public void fail_if_not_logged_in() {
+ expectedException.expect(UnauthorizedException.class);
+ expectedException.expectMessage("Authentication is required");
+
+ tester.newRequest().execute();
+ }
+
+ @Test
+ public void fail_if_no_administer_permission() {
+ userSession.logIn();
+ ComponentDto project = db.components().insertMainBranch();
+
+ expectedException.expect(ForbiddenException.class);
+ expectedException.expectMessage("Insufficient privileges");
+
+ tester.newRequest()
+ .setParam("project", project.getKey())
+ .setParam("branch", "branch1")
+ .execute();
+ }
+
+ @Test
+ public void successfully_rename() {
+ userSession.logIn();
+ ComponentDto project = db.components().insertMainBranch();
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("branch"));
+ userSession.addProjectPermission(UserRole.ADMIN, project);
+
+ tester.newRequest()
+ .setParam("project", project.getKey())
+ .setParam("branch", "master")
+ .execute();
+
+ assertThat(db.countRowsOfTable("project_branches")).isEqualTo(2);
+ Optional<BranchDto> mainBranch = db.getDbClient().branchDao().selectByUuid(db.getSession(), project.uuid());
+ assertThat(mainBranch.get().getKey()).isEqualTo("master");
+
+ Optional<BranchDto> unchangedBranch = db.getDbClient().branchDao().selectByUuid(db.getSession(), branch.uuid());
+ assertThat(unchangedBranch.get().getKey()).isEqualTo("branch");
+ }
+
+ @Test
+ public void successfully_rename_with_same_name() {
+ userSession.logIn();
+ ComponentDto project = db.components().insertMainBranch();
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("branch"));
+ userSession.addProjectPermission(UserRole.ADMIN, project);
+
+ tester.newRequest()
+ .setParam("project", project.getKey())
+ .setParam("branch", "master")
+ .execute();
+
+ tester.newRequest()
+ .setParam("project", project.getKey())
+ .setParam("branch", "master")
+ .execute();
+
+ assertThat(db.countRowsOfTable("project_branches")).isEqualTo(2);
+ Optional<BranchDto> mainBranch = db.getDbClient().branchDao().selectByUuid(db.getSession(), project.uuid());
+ assertThat(mainBranch.get().getKey()).isEqualTo("master");
+
+ Optional<BranchDto> unchangedBranch = db.getDbClient().branchDao().selectByUuid(db.getSession(), branch.uuid());
+ assertThat(unchangedBranch.get().getKey()).isEqualTo("branch");
+ }
+
+ @Test
+ public void fail_if_name_already_used() {
+ userSession.logIn();
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.addProjectPermission(UserRole.ADMIN, project);
+ db.components().insertProjectBranch(project, b -> b.setKey("branch"));
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Impossible to update branch name: a branch with name \"branch\" already exists");
+
+ tester.newRequest()
+ .setParam("project", project.getKey())
+ .setParam("branch", "branch")
+ .execute();
+ }
+
+ @Test
+ public void fail_if_project_does_not_exist() {
+ userSession.logIn();
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage("Project key 'foo' not found");
+
+ tester.newRequest()
+ .setParam("project", "foo")
+ .setParam("branch", "branch1")
+ .execute();
+ }
+}
public static final String ACTION_LIST = "list";
public static final String ACTION_SHOW = "show";
public static final String ACTION_DELETE = "delete";
+ public static final String ACTION_RENAME = "rename";
// parameters
public static final String PARAM_PROJECT = "project";
import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.ACTION_LIST;
import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.ACTION_SHOW;
import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.ACTION_DELETE;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.ACTION_RENAME;
import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.CONTROLLER;
import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_BRANCH;
import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_PROJECT;
call(post);
}
+ public void rename(String project, String branch) {
+ PostRequest post = new PostRequest(path(ACTION_RENAME))
+ .setParam(PARAM_PROJECT, project)
+ .setParam(PARAM_BRANCH, branch);
+ call(post);
+ }
+
}
.andNoOtherParam();
}
+ @Test
+ public void rename() {
+ underTest.rename("projectKey", "my_branch");
+
+ PostRequest postRequest = serviceTester.getPostRequest();
+ serviceTester.assertThat(postRequest)
+ .hasPath("rename")
+ .hasParam(PARAM_PROJECT, "projectKey")
+ .hasParam(PARAM_BRANCH, "my_branch")
+ .andNoOtherParam();
+ }
+
}