--- /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.branch.ws;
+
+import org.sonar.server.ws.WsAction;
+
+public interface BranchWsAction extends WsAction {
+ // marker interface
+}
--- /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.branch.ws;
+
+import org.sonar.core.platform.Module;
+
+public class BranchWsModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(
+ ListAction.class,
+ DeleteAction.class,
+ RenameAction.class,
+ BranchesWs.class);
+ }
+}
--- /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.branch.ws;
+
+import java.util.Arrays;
+import org.sonar.api.server.ws.WebService;
+
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+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;
+
+public class BranchesWs implements WebService {
+ private final BranchWsAction[] actions;
+
+ public BranchesWs(BranchWsAction... actions) {
+ this.actions = actions;
+ }
+
+ @Override
+ public void define(Context context) {
+ NewController controller = context.createController(CONTROLLER)
+ .setSince("6.6")
+ .setDescription("Manage branch (only available when the Branch plugin is installed)");
+ Arrays.stream(actions).forEach(action -> action.define(controller));
+ controller.done();
+ }
+
+ static void addProjectParam(NewAction action) {
+ action
+ .createParam(PARAM_PROJECT)
+ .setDescription("Project key")
+ .setExampleValue(KEY_PROJECT_EXAMPLE_001)
+ .setRequired(true);
+ }
+
+ static void addBranchParam(NewAction action) {
+ action
+ .createParam(PARAM_BRANCH)
+ .setDescription("Name of the branch")
+ .setExampleValue("branch1")
+ .setRequired(true);
+ }
+
+}
--- /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.branch.ws;
+
+import com.google.common.io.Resources;
+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.ComponentDto;
+import org.sonar.server.component.ComponentCleanerService;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.user.UserSession;
+
+import static org.sonar.server.branch.ws.BranchesWs.addBranchParam;
+import static org.sonar.server.branch.ws.BranchesWs.addProjectParam;
+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;
+
+public class DeleteAction implements BranchWsAction {
+ private final DbClient dbClient;
+ private final UserSession userSession;
+ private final ComponentCleanerService componentCleanerService;
+ private final ComponentFinder componentFinder;
+
+ public DeleteAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, ComponentCleanerService componentCleanerService) {
+ this.dbClient = dbClient;
+ this.componentFinder = componentFinder;
+ this.userSession = userSession;
+ this.componentCleanerService = componentCleanerService;
+ }
+
+ @Override
+ public void define(NewController context) {
+ WebService.NewAction action = context.createAction(ACTION_DELETE)
+ .setSince("6.6")
+ .setDescription("Delete a non-main branch of a project.<br/>" +
+ "Requires 'Administer' rights on the specified project.")
+ .setResponseExample(Resources.getResource(getClass(), "list-example.json"))
+ .setPost(true)
+ .setHandler(this);
+
+ addProjectParam(action);
+ addBranchParam(action);
+ }
+
+ @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);
+
+ BranchDto branch = checkFoundWithOptional(
+ dbClient.branchDao().selectByKey(dbSession, project.uuid(), branchKey),
+ "Branch '%s' not found for project '%s'", branchKey, projectKey);
+
+ if (branch.isMain()) {
+ throw new IllegalArgumentException("Only non-main branches can be deleted");
+ }
+ ComponentDto branchComponent = componentFinder.getByKeyAndBranch(dbSession, projectKey, branchKey);
+ componentCleanerService.deleteBranch(dbSession, branchComponent);
+ response.noContent();
+ }
+ }
+
+ private void checkPermission(ComponentDto 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.branch.ws;
+
+import com.google.common.io.Resources;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+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.web.UserRole;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.measure.MeasureDto;
+import org.sonar.db.metric.MetricDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.issue.index.BranchStatistics;
+import org.sonar.server.issue.index.IssueIndex;
+import org.sonar.server.user.UserSession;
+import org.sonar.server.ws.WsUtils;
+import org.sonarqube.ws.Common;
+import org.sonarqube.ws.WsBranches;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Collections.singletonList;
+import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
+import static org.sonar.api.resources.Qualifiers.PROJECT;
+import static org.sonar.api.utils.DateUtils.formatDateTime;
+import static org.sonar.core.util.Protobuf.setNullable;
+import static org.sonar.core.util.stream.MoreCollectors.toList;
+import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
+import static org.sonar.db.component.BranchType.LONG;
+import static org.sonar.db.component.BranchType.SHORT;
+import static org.sonar.server.branch.ws.BranchesWs.addProjectParam;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.ACTION_LIST;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_PROJECT;
+
+public class ListAction implements BranchWsAction {
+
+ private final DbClient dbClient;
+ private final UserSession userSession;
+ private final ComponentFinder componentFinder;
+ private final IssueIndex issueIndex;
+
+ public ListAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder, IssueIndex issueIndex) {
+ this.dbClient = dbClient;
+ this.userSession = userSession;
+ this.componentFinder = componentFinder;
+ this.issueIndex = issueIndex;
+ }
+
+ @Override
+ public void define(WebService.NewController context) {
+ WebService.NewAction action = context.createAction(ACTION_LIST)
+ .setSince("6.6")
+ .setDescription("List the branches of a project.<br/>" +
+ "Requires 'Administer' rights on the specified project.")
+ .setResponseExample(Resources.getResource(getClass(), "list-example.json"))
+ .setHandler(this);
+
+ addProjectParam(action);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ String projectKey = request.mandatoryParam(PARAM_PROJECT);
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ ComponentDto project = componentFinder.getByKey(dbSession, projectKey);
+ userSession.checkComponentPermission(UserRole.USER, project);
+ checkArgument(project.isEnabled() && PROJECT.equals(project.qualifier()), "Invalid project key");
+
+ Collection<BranchDto> branches = dbClient.branchDao().selectByComponent(dbSession, project);
+ MetricDto qualityGateMetric = dbClient.metricDao().selectOrFailByKey(dbSession, ALERT_STATUS_KEY);
+ Map<String, BranchDto> mergeBranchesByUuid = dbClient.branchDao()
+ .selectByUuids(dbSession, branches.stream().map(BranchDto::getMergeBranchUuid).filter(Objects::nonNull).collect(toList()))
+ .stream().collect(uniqueIndex(BranchDto::getUuid));
+ Map<String, MeasureDto> qualityGateMeasuresByComponentUuids = dbClient.measureDao()
+ .selectByComponentsAndMetrics(dbSession, branches.stream().map(BranchDto::getUuid).collect(toList()), singletonList(qualityGateMetric.getId()))
+ .stream().collect(uniqueIndex(MeasureDto::getComponentUuid));
+ Map<String, BranchStatistics> branchStatisticsByBranchUuid = issueIndex.searchBranchStatistics(project.uuid(), branches.stream()
+ .filter(b -> b.getBranchType().equals(SHORT))
+ .map(BranchDto::getUuid).collect(toList()))
+ .stream().collect(uniqueIndex(BranchStatistics::getBranchUuid, Function.identity()));
+ Map<String, String> analysisDateByBranchUuid = dbClient.snapshotDao()
+ .selectLastAnalysesByRootComponentUuids(dbSession, branches.stream().map(BranchDto::getUuid).collect(Collectors.toList()))
+ .stream().collect(uniqueIndex(SnapshotDto::getComponentUuid, s -> formatDateTime(s.getCreatedAt())));
+
+ WsBranches.ListWsResponse.Builder protobufResponse = WsBranches.ListWsResponse.newBuilder();
+ branches.stream()
+ .forEach(b -> addBranch(protobufResponse, b, mergeBranchesByUuid, qualityGateMeasuresByComponentUuids.get(b.getUuid()), branchStatisticsByBranchUuid.get(b.getUuid()),
+ analysisDateByBranchUuid.get(b.getUuid())));
+ WsUtils.writeProtobuf(protobufResponse.build(), request, response);
+ }
+ }
+
+ private static void addBranch(WsBranches.ListWsResponse.Builder response, BranchDto branch, Map<String, BranchDto> mergeBranchesByUuid, @Nullable MeasureDto qualityGateMeasure,
+ BranchStatistics branchStatistics, @Nullable String analysisDate) {
+ WsBranches.Branch.Builder builder = toBranchBuilder(branch, Optional.ofNullable(mergeBranchesByUuid.get(branch.getMergeBranchUuid())));
+ setBranchStatus(builder, branch, qualityGateMeasure, branchStatistics);
+ if (analysisDate != null) {
+ builder.setAnalysisDate(analysisDate);
+ }
+ response.addBranches(builder);
+ }
+
+ private static WsBranches.Branch.Builder toBranchBuilder(BranchDto branch, Optional<BranchDto> mergeBranch) {
+ WsBranches.Branch.Builder builder = WsBranches.Branch.newBuilder();
+ String branchKey = branch.getKey();
+ setNullable(branchKey, builder::setName);
+ builder.setIsMain(branch.isMain());
+ builder.setType(Common.BranchType.valueOf(branch.getBranchType().name()));
+ if (branch.getBranchType().equals(SHORT)) {
+ if (mergeBranch.isPresent()) {
+ String mergeBranchKey = mergeBranch.get().getKey();
+ builder.setMergeBranch(mergeBranchKey);
+ } else {
+ builder.setIsOrphan(true);
+ }
+ }
+ return builder;
+ }
+
+ private static void setBranchStatus(WsBranches.Branch.Builder builder, BranchDto branch, @Nullable MeasureDto qualityGateMeasure, @Nullable BranchStatistics branchStatistics) {
+ WsBranches.Branch.Status.Builder statusBuilder = WsBranches.Branch.Status.newBuilder();
+ if (branch.getBranchType() == LONG && qualityGateMeasure != null) {
+ statusBuilder.setQualityGateStatus(qualityGateMeasure.getData());
+ }
+ if (branch.getBranchType() == BranchType.SHORT) {
+ statusBuilder.setBugs(branchStatistics == null ? 0L : branchStatistics.getBugs());
+ statusBuilder.setVulnerabilities(branchStatistics == null ? 0L : branchStatistics.getVulnerabilities());
+ statusBuilder.setCodeSmells(branchStatistics == null ? 0L : branchStatistics.getCodeSmells());
+ }
+ builder.setStatus(statusBuilder);
+ }
+}
--- /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.branch.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.ComponentDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.user.UserSession;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.sonar.server.branch.ws.BranchesWs.addProjectParam;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.ACTION_RENAME;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_NAME;
+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.")
+ .setPost(true)
+ .setHandler(this);
+
+ addProjectParam(action);
+ action
+ .createParam(PARAM_NAME)
+ .setDescription("New name of the main branch")
+ .setExampleValue("branch1")
+ .setRequired(true);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ userSession.checkLoggedIn();
+ String projectKey = request.mandatoryParam(PARAM_PROJECT);
+ String newBranchName = request.mandatoryParam(PARAM_NAME);
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ ComponentDto project = componentFinder.getRootComponentByUuidOrKey(dbSession, null, projectKey);
+ checkPermission(project);
+
+ Optional<BranchDto> existingBranch = dbClient.branchDao().selectByKey(dbSession, project.uuid(), newBranchName);
+ checkArgument(!existingBranch.filter(b -> !b.isMain()).isPresent(),
+ "Impossible to update branch name: a branch with name \"%s\" already exists in the project.", newBranchName);
+
+ dbClient.branchDao().updateMainBranchName(dbSession, project.uuid(), newBranchName);
+ dbSession.commit();
+ response.noContent();
+ }
+ }
+
+ private void checkPermission(ComponentDto 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.branch.ws;
+
+import javax.annotation.ParametersAreNonnullByDefault;
import org.sonar.server.authentication.LogOAuthWarning;
import org.sonar.server.batch.BatchWsModule;
import org.sonar.server.branch.BranchFeatureProxyImpl;
+import org.sonar.server.branch.ws.BranchWsModule;
import org.sonar.server.ce.ws.CeWsModule;
import org.sonar.server.component.ComponentCleanerService;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.plugins.ws.UpdatesAction;
import org.sonar.server.project.ws.ProjectsWsModule;
import org.sonar.server.projectanalysis.ProjectAnalysisModule;
-import org.sonar.server.projectbranch.ws.BranchWsModule;
import org.sonar.server.projectlink.ws.ProjectLinksModule;
import org.sonar.server.projecttag.ws.ProjectTagsWsModule;
import org.sonar.server.property.InternalPropertiesImpl;
+++ /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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.server.projectbranch;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /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 org.sonar.server.ws.WsAction;
-
-public interface BranchWsAction extends WsAction {
- // marker interface
-}
+++ /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 org.sonar.core.platform.Module;
-
-public class BranchWsModule extends Module {
- @Override
- protected void configureModule() {
- add(
- ListAction.class,
- DeleteAction.class,
- RenameAction.class,
- BranchesWs.class);
- }
-}
+++ /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.Arrays;
-import org.sonar.api.server.ws.WebService;
-
-import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
-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;
-
-public class BranchesWs implements WebService {
- private final BranchWsAction[] actions;
-
- public BranchesWs(BranchWsAction... actions) {
- this.actions = actions;
- }
-
- @Override
- public void define(Context context) {
- NewController controller = context.createController(CONTROLLER)
- .setSince("6.6")
- .setDescription("Manage branch (only available when the Branch plugin is installed)");
- Arrays.stream(actions).forEach(action -> action.define(controller));
- controller.done();
- }
-
- static void addProjectParam(NewAction action) {
- action
- .createParam(PARAM_PROJECT)
- .setDescription("Project key")
- .setExampleValue(KEY_PROJECT_EXAMPLE_001)
- .setRequired(true);
- }
-
- static void addBranchParam(NewAction action) {
- action
- .createParam(PARAM_BRANCH)
- .setDescription("Name of the branch")
- .setExampleValue("branch1")
- .setRequired(true);
- }
-
-}
+++ /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 com.google.common.io.Resources;
-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.ComponentDto;
-import org.sonar.server.component.ComponentCleanerService;
-import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.user.UserSession;
-
-import static org.sonar.server.projectbranch.ws.BranchesWs.addBranchParam;
-import static org.sonar.server.projectbranch.ws.BranchesWs.addProjectParam;
-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;
-
-public class DeleteAction implements BranchWsAction {
- private final DbClient dbClient;
- private final UserSession userSession;
- private final ComponentCleanerService componentCleanerService;
- private final ComponentFinder componentFinder;
-
- public DeleteAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, ComponentCleanerService componentCleanerService) {
- this.dbClient = dbClient;
- this.componentFinder = componentFinder;
- this.userSession = userSession;
- this.componentCleanerService = componentCleanerService;
- }
-
- @Override
- public void define(NewController context) {
- WebService.NewAction action = context.createAction(ACTION_DELETE)
- .setSince("6.6")
- .setDescription("Delete a non-main branch of a project.<br/>" +
- "Requires 'Administer' rights on the specified project.")
- .setResponseExample(Resources.getResource(getClass(), "list-example.json"))
- .setPost(true)
- .setHandler(this);
-
- addProjectParam(action);
- addBranchParam(action);
- }
-
- @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);
-
- BranchDto branch = checkFoundWithOptional(
- dbClient.branchDao().selectByKey(dbSession, project.uuid(), branchKey),
- "Branch '%s' not found for project '%s'", branchKey, projectKey);
-
- if (branch.isMain()) {
- throw new IllegalArgumentException("Only non-main branches can be deleted");
- }
- ComponentDto branchComponent = componentFinder.getByKeyAndBranch(dbSession, projectKey, branchKey);
- componentCleanerService.deleteBranch(dbSession, branchComponent);
- response.noContent();
- }
- }
-
- private void checkPermission(ComponentDto 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 com.google.common.io.Resources;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import javax.annotation.Nullable;
-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.web.UserRole;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.component.BranchDto;
-import org.sonar.db.component.BranchType;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.SnapshotDto;
-import org.sonar.db.measure.MeasureDto;
-import org.sonar.db.metric.MetricDto;
-import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.issue.index.BranchStatistics;
-import org.sonar.server.issue.index.IssueIndex;
-import org.sonar.server.user.UserSession;
-import org.sonar.server.ws.WsUtils;
-import org.sonarqube.ws.Common;
-import org.sonarqube.ws.WsBranches;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.Collections.singletonList;
-import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
-import static org.sonar.api.resources.Qualifiers.PROJECT;
-import static org.sonar.api.utils.DateUtils.formatDateTime;
-import static org.sonar.core.util.Protobuf.setNullable;
-import static org.sonar.core.util.stream.MoreCollectors.toList;
-import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
-import static org.sonar.db.component.BranchType.LONG;
-import static org.sonar.db.component.BranchType.SHORT;
-import static org.sonar.server.projectbranch.ws.BranchesWs.addProjectParam;
-import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.ACTION_LIST;
-import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_PROJECT;
-
-public class ListAction implements BranchWsAction {
-
- private final DbClient dbClient;
- private final UserSession userSession;
- private final ComponentFinder componentFinder;
- private final IssueIndex issueIndex;
-
- public ListAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder, IssueIndex issueIndex) {
- this.dbClient = dbClient;
- this.userSession = userSession;
- this.componentFinder = componentFinder;
- this.issueIndex = issueIndex;
- }
-
- @Override
- public void define(WebService.NewController context) {
- WebService.NewAction action = context.createAction(ACTION_LIST)
- .setSince("6.6")
- .setDescription("List the branches of a project.<br/>" +
- "Requires 'Administer' rights on the specified project.")
- .setResponseExample(Resources.getResource(getClass(), "list-example.json"))
- .setHandler(this);
-
- addProjectParam(action);
- }
-
- @Override
- public void handle(Request request, Response response) throws Exception {
- String projectKey = request.mandatoryParam(PARAM_PROJECT);
-
- try (DbSession dbSession = dbClient.openSession(false)) {
- ComponentDto project = componentFinder.getByKey(dbSession, projectKey);
- userSession.checkComponentPermission(UserRole.USER, project);
- checkArgument(project.isEnabled() && PROJECT.equals(project.qualifier()), "Invalid project key");
-
- Collection<BranchDto> branches = dbClient.branchDao().selectByComponent(dbSession, project);
- MetricDto qualityGateMetric = dbClient.metricDao().selectOrFailByKey(dbSession, ALERT_STATUS_KEY);
- Map<String, BranchDto> mergeBranchesByUuid = dbClient.branchDao()
- .selectByUuids(dbSession, branches.stream().map(BranchDto::getMergeBranchUuid).filter(Objects::nonNull).collect(toList()))
- .stream().collect(uniqueIndex(BranchDto::getUuid));
- Map<String, MeasureDto> qualityGateMeasuresByComponentUuids = dbClient.measureDao()
- .selectByComponentsAndMetrics(dbSession, branches.stream().map(BranchDto::getUuid).collect(toList()), singletonList(qualityGateMetric.getId()))
- .stream().collect(uniqueIndex(MeasureDto::getComponentUuid));
- Map<String, BranchStatistics> branchStatisticsByBranchUuid = issueIndex.searchBranchStatistics(project.uuid(), branches.stream()
- .filter(b -> b.getBranchType().equals(SHORT))
- .map(BranchDto::getUuid).collect(toList()))
- .stream().collect(uniqueIndex(BranchStatistics::getBranchUuid, Function.identity()));
- Map<String, String> analysisDateByBranchUuid = dbClient.snapshotDao()
- .selectLastAnalysesByRootComponentUuids(dbSession, branches.stream().map(BranchDto::getUuid).collect(Collectors.toList()))
- .stream().collect(uniqueIndex(SnapshotDto::getComponentUuid, s -> formatDateTime(s.getCreatedAt())));
-
- WsBranches.ListWsResponse.Builder protobufResponse = WsBranches.ListWsResponse.newBuilder();
- branches.stream()
- .forEach(b -> addBranch(protobufResponse, b, mergeBranchesByUuid, qualityGateMeasuresByComponentUuids.get(b.getUuid()), branchStatisticsByBranchUuid.get(b.getUuid()),
- analysisDateByBranchUuid.get(b.getUuid())));
- WsUtils.writeProtobuf(protobufResponse.build(), request, response);
- }
- }
-
- private static void addBranch(WsBranches.ListWsResponse.Builder response, BranchDto branch, Map<String, BranchDto> mergeBranchesByUuid, @Nullable MeasureDto qualityGateMeasure,
- BranchStatistics branchStatistics, @Nullable String analysisDate) {
- WsBranches.Branch.Builder builder = toBranchBuilder(branch, Optional.ofNullable(mergeBranchesByUuid.get(branch.getMergeBranchUuid())));
- setBranchStatus(builder, branch, qualityGateMeasure, branchStatistics);
- if (analysisDate != null) {
- builder.setAnalysisDate(analysisDate);
- }
- response.addBranches(builder);
- }
-
- private static WsBranches.Branch.Builder toBranchBuilder(BranchDto branch, Optional<BranchDto> mergeBranch) {
- WsBranches.Branch.Builder builder = WsBranches.Branch.newBuilder();
- String branchKey = branch.getKey();
- setNullable(branchKey, builder::setName);
- builder.setIsMain(branch.isMain());
- builder.setType(Common.BranchType.valueOf(branch.getBranchType().name()));
- if (branch.getBranchType().equals(SHORT)) {
- if (mergeBranch.isPresent()) {
- String mergeBranchKey = mergeBranch.get().getKey();
- builder.setMergeBranch(mergeBranchKey);
- } else {
- builder.setIsOrphan(true);
- }
- }
- return builder;
- }
-
- private static void setBranchStatus(WsBranches.Branch.Builder builder, BranchDto branch, @Nullable MeasureDto qualityGateMeasure, @Nullable BranchStatistics branchStatistics) {
- WsBranches.Branch.Status.Builder statusBuilder = WsBranches.Branch.Status.newBuilder();
- if (branch.getBranchType() == LONG && qualityGateMeasure != null) {
- statusBuilder.setQualityGateStatus(qualityGateMeasure.getData());
- }
- if (branch.getBranchType() == BranchType.SHORT) {
- statusBuilder.setBugs(branchStatistics == null ? 0L : branchStatistics.getBugs());
- statusBuilder.setVulnerabilities(branchStatistics == null ? 0L : branchStatistics.getVulnerabilities());
- statusBuilder.setCodeSmells(branchStatistics == null ? 0L : branchStatistics.getCodeSmells());
- }
- builder.setStatus(statusBuilder);
- }
-}
+++ /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.ComponentDto;
-import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.user.UserSession;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static org.sonar.server.projectbranch.ws.BranchesWs.addProjectParam;
-import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.ACTION_RENAME;
-import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_NAME;
-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.")
- .setPost(true)
- .setHandler(this);
-
- addProjectParam(action);
- action
- .createParam(PARAM_NAME)
- .setDescription("New name of the main branch")
- .setExampleValue("branch1")
- .setRequired(true);
- }
-
- @Override
- public void handle(Request request, Response response) throws Exception {
- userSession.checkLoggedIn();
- String projectKey = request.mandatoryParam(PARAM_PROJECT);
- String newBranchName = request.mandatoryParam(PARAM_NAME);
-
- try (DbSession dbSession = dbClient.openSession(false)) {
- ComponentDto project = componentFinder.getRootComponentByUuidOrKey(dbSession, null, projectKey);
- checkPermission(project);
-
- Optional<BranchDto> existingBranch = dbClient.branchDao().selectByKey(dbSession, project.uuid(), newBranchName);
- checkArgument(!existingBranch.filter(b -> !b.isMain()).isPresent(),
- "Impossible to update branch name: a branch with name \"%s\" already exists in the project.", newBranchName);
-
- dbClient.branchDao().updateMainBranchName(dbSession, project.uuid(), newBranchName);
- dbSession.commit();
- response.noContent();
- }
- }
-
- private void checkPermission(ComponentDto 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.server.projectbranch.ws;
-
-import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+{
+ "branches": [
+ {
+ "name": "feature/bar",
+ "isMain": false,
+ "type": "LONG",
+ "analysisDate": "2017-04-01T01:15:42+0100"
+ },
+ {
+ "name": "feature/foo",
+ "isMain": false,
+ "type": "SHORT",
+ "mergeBranch": "feature/bar",
+ "analysisDate": "2017-04-03T13:37:00+0100"
+ }
+ ]
+}
+++ /dev/null
-{
- "branches": [
- {
- "name": "feature/bar",
- "isMain": false,
- "type": "LONG",
- "analysisDate": "2017-04-01T01:15:42+0100"
- },
- {
- "name": "feature/foo",
- "isMain": false,
- "type": "SHORT",
- "mergeBranch": "feature/bar",
- "analysisDate": "2017-04-03T13:37:00+0100"
- }
- ]
-}
--- /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.branch.ws;
+
+import org.junit.Test;
+import org.sonar.core.platform.ComponentContainer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.core.platform.ComponentContainer.COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER;
+
+public class BranchWsModuleTest {
+ @Test
+ 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);
+ }
+}
--- /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.branch.ws;
+
+import org.junit.Test;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BranchesWsTest {
+
+ @Test
+ public void define_ws() {
+ BranchesWs underTest = new BranchesWs(new BranchWsAction() {
+ @Override
+ public void define(WebService.NewController context) {
+ context.createAction("foo").setHandler(this);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+
+ }
+ });
+
+ WebService.Context context = new WebService.Context();
+ underTest.define(context);
+
+ assertThat(context.controller("api/project_branches").action("foo")).isNotNull();
+ }
+
+}
--- /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.branch.ws;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.server.component.ComponentCleanerService;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.component.TestComponentFinder;
+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.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class DeleteActionTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+
+ private ComponentCleanerService componentCleanerService = mock(ComponentCleanerService.class);
+ private ComponentFinder componentFinder = TestComponentFinder.from(db);
+
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ public WsActionTester tester = new WsActionTester(new DeleteAction(db.getDbClient(), componentFinder, userSession, componentCleanerService));
+
+ @Test
+ public void test_definition() {
+ WebService.Action definition = tester.getDef();
+ assertThat(definition.key()).isEqualTo("delete");
+ assertThat(definition.isPost()).isTrue();
+ assertThat(definition.isInternal()).isFalse();
+ 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();
+ }
+
+ public void fail_branch_does_not_exist() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
+ userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage("Branch 'branch1' not found");
+
+ tester.newRequest()
+ .setParam("project", file.getDbKey())
+ .setParam("branch", "branch1")
+ .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();
+ }
+
+ @Test
+ public void fail_if_branch_is_main() {
+ ComponentDto project = db.components().insertMainBranch();
+ db.executeUpdateSql("UPDATE project_branches set KEE = 'main'");
+ userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+
+ // not found because the DB keys don't contain the name
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Only non-main branches can be deleted");
+
+ tester.newRequest()
+ .setParam("project", project.getKey())
+ .setParam("branch", "main")
+ .execute();
+ }
+
+ @Test
+ public void delete_branch() {
+
+ ComponentDto project = db.components().insertMainBranch();
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("branch1"));
+
+ userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+
+ tester.newRequest()
+ .setParam("project", project.getKey())
+ .setParam("branch", "branch1")
+ .execute();
+ verifyDeletedKey(branch.getDbKey());
+ }
+
+ private void verifyDeletedKey(String key) {
+ ArgumentCaptor<ComponentDto> argument = ArgumentCaptor.forClass(ComponentDto.class);
+ verify(componentCleanerService).deleteBranch(any(DbSession.class), argument.capture());
+ assertThat(argument.getValue().getDbKey()).isEqualTo(key);
+ }
+
+}
--- /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.branch.ws;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.resources.ResourceTypes;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.component.ResourceTypesRule;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.component.SnapshotTesting;
+import org.sonar.db.metric.MetricDto;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.issue.index.IssueIndex;
+import org.sonar.server.issue.index.IssueIndexDefinition;
+import org.sonar.server.issue.index.IssueIndexer;
+import org.sonar.server.issue.index.IssueIteratorFactory;
+import org.sonar.server.permission.index.AuthorizationTypeSupport;
+import org.sonar.server.permission.index.PermissionIndexerTester;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.WsActionTester;
+import org.sonarqube.ws.Common;
+import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.WsBranches;
+import org.sonarqube.ws.WsBranches.Branch;
+import org.sonarqube.ws.WsBranches.ListWsResponse;
+
+import static java.lang.String.format;
+import static java.util.Collections.emptySet;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.sonar.api.issue.Issue.RESOLUTION_FALSE_POSITIVE;
+import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
+import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
+import static org.sonar.api.resources.Qualifiers.PROJECT;
+import static org.sonar.api.rules.RuleType.BUG;
+import static org.sonar.api.rules.RuleType.CODE_SMELL;
+import static org.sonar.api.rules.RuleType.VULNERABILITY;
+import static org.sonar.api.utils.DateUtils.dateToLong;
+import static org.sonar.api.utils.DateUtils.parseDateTime;
+import static org.sonar.test.JsonAssert.assertJson;
+import static org.sonarqube.ws.WsBranches.Branch.Status;
+
+public class ListActionTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+ @Rule
+ public EsTester es = new EsTester(new IssueIndexDefinition(new MapSettings().asConfig()));
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ private ResourceTypes resourceTypes = new ResourceTypesRule().setRootQualifiers(PROJECT);
+ private IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()));
+ private IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSession, new AuthorizationTypeSupport(userSession));
+ private PermissionIndexerTester permissionIndexerTester = new PermissionIndexerTester(es, issueIndexer);
+
+ private MetricDto qualityGateStatus;
+
+ public WsActionTester ws = new WsActionTester(new ListAction(db.getDbClient(), userSession, new ComponentFinder(db.getDbClient(), resourceTypes), issueIndex));
+
+ @Before
+ public void setUp() throws Exception {
+ qualityGateStatus = db.measures().insertMetric(m -> m.setKey(ALERT_STATUS_KEY));
+ }
+
+ @Test
+ public void test_definition() {
+ WebService.Action definition = ws.getDef();
+ assertThat(definition.key()).isEqualTo("list");
+ assertThat(definition.isPost()).isFalse();
+ assertThat(definition.isInternal()).isFalse();
+ assertThat(definition.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("project");
+ assertThat(definition.since()).isEqualTo("6.6");
+ }
+
+ @Test
+ public void test_example() {
+ ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey("sonarqube"));
+ ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setKey("feature/bar").setBranchType(BranchType.LONG));
+ ComponentDto shortLivingBranch = db.components().insertProjectBranch(project, b -> b.setKey("feature/foo").setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+
+ db.getDbClient().snapshotDao().insert(db.getSession(), SnapshotTesting.newAnalysis(longLivingBranch).setLast(true).setCreatedAt(DateUtils.parseDateTime("2017-04-01T01:15:42+0100").getTime()));
+ db.getDbClient().snapshotDao().insert(db.getSession(), SnapshotTesting.newAnalysis(shortLivingBranch).setLast(true).setCreatedAt(DateUtils.parseDateTime("2017-04-03T13:37:00+0100").getTime()));
+ db.commit();
+
+ String json = ws.newRequest()
+ .setParam("project", project.getDbKey())
+ .execute()
+ .getInput();
+
+ assertJson(json).isSimilarTo(ws.getDef().responseExampleAsString());
+ }
+
+ @Test
+ public void main_branch() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getDbKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getBranchesList())
+ .extracting(Branch::getName, Branch::getIsMain, Branch::getType)
+ .containsExactlyInAnyOrder(tuple("master", true, Common.BranchType.LONG));
+ }
+
+ @Test
+ public void main_branch_with_specified_name() {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertMainBranch(organization, "head");
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getDbKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getBranchesList())
+ .extracting(Branch::getName, Branch::getIsMain, Branch::getType)
+ .containsExactlyInAnyOrder(tuple("head", true, Common.BranchType.LONG));
+ }
+
+ @Test
+ public void test_project_with_zero_branches() {
+ ComponentDto project = db.components().insertPrivateProject();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+
+ String json = ws.newRequest()
+ .setParam("project", project.getDbKey())
+ .setMediaType(MediaTypes.JSON)
+ .execute()
+ .getInput();
+ assertJson(json).isSimilarTo("{\"branches\": []}");
+ }
+
+ @Test
+ public void test_project_with_branches() {
+ ComponentDto project = db.components().insertMainBranch();
+ db.components().insertProjectBranch(project, b -> b.setKey("feature/bar"));
+ db.components().insertProjectBranch(project, b -> b.setKey("feature/foo"));
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getDbKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getBranchesList())
+ .extracting(Branch::getName, Branch::getType)
+ .containsExactlyInAnyOrder(
+ tuple("master", Common.BranchType.LONG),
+ tuple("feature/foo", Common.BranchType.LONG),
+ tuple("feature/bar", Common.BranchType.LONG));
+ }
+
+ @Test
+ public void short_living_branches() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto longLivingBranch = db.components().insertProjectBranch(project,
+ b -> b.setKey("long").setBranchType(BranchType.LONG));
+ ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
+ b -> b.setKey("short").setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
+ ComponentDto shortLivingBranchOnMaster = db.components().insertProjectBranch(project,
+ b -> b.setKey("short_on_master").setBranchType(BranchType.SHORT).setMergeBranchUuid(project.uuid()));
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getBranchesList())
+ .extracting(Branch::getName, Branch::getType, Branch::getMergeBranch)
+ .containsExactlyInAnyOrder(
+ tuple("master", Common.BranchType.LONG, ""),
+ tuple(longLivingBranch.getBranch(), Common.BranchType.LONG, ""),
+ tuple(shortLivingBranch.getBranch(), Common.BranchType.SHORT, longLivingBranch.getBranch()),
+ tuple(shortLivingBranchOnMaster.getBranch(), Common.BranchType.SHORT, "master"));
+ }
+
+ @Test
+ public void mergeBranch_is_using_default_main_name_when_main_branch_has_no_name() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
+ b -> b.setKey("short").setBranchType(BranchType.SHORT).setMergeBranchUuid(project.uuid()));
+
+ WsBranches.ShowWsResponse response = ws.newRequest()
+ .setParam("project", shortLivingBranch.getKey())
+ .executeProtobuf(WsBranches.ShowWsResponse.class);
+
+ assertThat(response.getBranch())
+ .extracting(Branch::getName, Branch::getType, Branch::getMergeBranch)
+ .containsExactlyInAnyOrder(shortLivingBranch.getBranch(), Common.BranchType.SHORT, "master");
+ }
+
+ @Test
+ public void short_living_branch_on_removed_branch() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
+ b -> b.setKey("short").setBranchType(BranchType.SHORT).setMergeBranchUuid("unknown"));
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getBranchesList())
+ .extracting(Branch::getName, Branch::getType, Branch::hasMergeBranch, Branch::getIsOrphan)
+ .containsExactlyInAnyOrder(
+ tuple("master", Common.BranchType.LONG, false, false),
+ tuple(shortLivingBranch.getBranch(), Common.BranchType.SHORT, false, true));
+ }
+
+ @Test
+ public void status_on_long_living_branch() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
+ SnapshotDto branchAnalysis = db.components().insertSnapshot(branch);
+ db.measures().insertMeasure(branch, branchAnalysis, qualityGateStatus, m -> m.setData("OK"));
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getBranchesList())
+ .extracting(b -> b.getStatus().hasQualityGateStatus(), b -> b.getStatus().getQualityGateStatus())
+ .containsExactlyInAnyOrder(tuple(false, ""), tuple(true, "OK"));
+ }
+
+ @Test
+ public void status_on_short_living_branches() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
+ ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
+ b -> b.setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
+ RuleDefinitionDto rule = db.rules().insert();
+ db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(BUG).setResolution(null));
+ db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(BUG).setResolution(RESOLUTION_FIXED));
+ db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(VULNERABILITY).setResolution(null));
+ db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(VULNERABILITY).setResolution(null));
+ db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(CODE_SMELL).setResolution(null));
+ db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(CODE_SMELL).setResolution(null));
+ db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(CODE_SMELL).setResolution(null));
+ db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(CODE_SMELL).setResolution(RESOLUTION_FALSE_POSITIVE));
+ issueIndexer.indexOnStartup(emptySet());
+ permissionIndexerTester.allowOnlyAnyone(project);
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getBranchesList().stream().map(WsBranches.Branch::getStatus))
+ .extracting(Status::hasBugs, Status::getBugs, Status::hasVulnerabilities, Status::getVulnerabilities, Status::hasCodeSmells, Status::getCodeSmells)
+ .containsExactlyInAnyOrder(
+ tuple(false, 0L, false, 0L, false, 0L),
+ tuple(false, 0L, false, 0L, false, 0L),
+ tuple(true, 1L, true, 2L, true, 3L));
+ }
+
+ @Test
+ public void status_on_short_living_branch_with_no_issue() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
+ db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
+ issueIndexer.indexOnStartup(emptySet());
+ permissionIndexerTester.allowOnlyAnyone(project);
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getBranchesList().stream().filter(b -> b.getType().equals(Common.BranchType.SHORT)).map(WsBranches.Branch::getStatus))
+ .extracting(Status::getBugs, Status::getVulnerabilities, Status::getCodeSmells)
+ .containsExactlyInAnyOrder(tuple(0L, 0L, 0L));
+ }
+
+ @Test
+ public void response_contains_date_of_last_analysis() {
+ Long lastAnalysisLongLivingBranch = dateToLong(parseDateTime("2017-04-01T00:00:00+0100"));
+ Long previousAnalysisShortLivingBranch = dateToLong(parseDateTime("2017-04-02T00:00:00+0100"));
+ Long lastAnalysisShortLivingBranch = dateToLong(parseDateTime("2017-04-03T00:00:00+0100"));
+
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto shortLivingBranch1 = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.SHORT).setMergeBranchUuid(project.uuid()));
+ ComponentDto longLivingBranch2 = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
+ ComponentDto shortLivingBranch2 = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch2.uuid()));
+ db.getDbClient().snapshotDao().insert(db.getSession(),
+ SnapshotTesting.newAnalysis(longLivingBranch2).setCreatedAt(lastAnalysisLongLivingBranch));
+ db.getDbClient().snapshotDao().insert(db.getSession(),
+ SnapshotTesting.newAnalysis(shortLivingBranch2).setCreatedAt(previousAnalysisShortLivingBranch).setLast(false));
+ db.getDbClient().snapshotDao().insert(db.getSession(),
+ SnapshotTesting.newAnalysis(shortLivingBranch2).setCreatedAt(lastAnalysisShortLivingBranch));
+ db.commit();
+ issueIndexer.indexOnStartup(emptySet());
+ permissionIndexerTester.allowOnlyAnyone(project);
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getBranchesList())
+ .extracting(WsBranches.Branch::getType, WsBranches.Branch::hasAnalysisDate, b -> "".equals(b.getAnalysisDate()) ? null : dateToLong(parseDateTime(b.getAnalysisDate())))
+ .containsExactlyInAnyOrder(
+ tuple(Common.BranchType.LONG, false, null),
+ tuple(Common.BranchType.SHORT, false, null),
+ tuple(Common.BranchType.LONG, true, lastAnalysisLongLivingBranch),
+ tuple(Common.BranchType.SHORT, true, lastAnalysisShortLivingBranch)
+ );
+ }
+
+ @Test
+ public void fail_when_using_branch_db_key() throws Exception {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertMainBranch(organization);
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto branch = db.components().insertProjectBranch(project);
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage(format("Component key '%s' not found", branch.getDbKey()));
+
+ ws.newRequest()
+ .setParam("project", branch.getDbKey())
+ .execute();
+ }
+
+ @Test
+ public void fail_if_missing_project_parameter() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'project' parameter is missing");
+
+ ws.newRequest().execute();
+ }
+
+ @Test
+ public void fail_if_not_a_reference_on_project() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Invalid project key");
+
+ ws.newRequest()
+ .setParam("project", file.getDbKey())
+ .execute();
+ }
+
+ @Test
+ public void fail_if_project_does_not_exist() {
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage("Component key 'foo' not found");
+
+ ws.newRequest()
+ .setParam("project", "foo")
+ .execute();
+ }
+
+}
--- /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.branch.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()).isFalse();
+ assertThat(definition.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("project", "name");
+ 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 'name' 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("name", "branch1")
+ .execute();
+ }
+
+ @Test
+ public void 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("name", "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 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("name", "master")
+ .execute();
+
+ tester.newRequest()
+ .setParam("project", project.getKey())
+ .setParam("name", "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("name", "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("name", "branch1")
+ .execute();
+ }
+}
+++ /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 org.junit.Test;
-import org.sonar.core.platform.ComponentContainer;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.core.platform.ComponentContainer.COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER;
-
-public class BranchWsModuleTest {
- @Test
- 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);
- }
-}
+++ /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 org.junit.Test;
-import org.sonar.api.server.ws.Request;
-import org.sonar.api.server.ws.Response;
-import org.sonar.api.server.ws.WebService;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class BranchesWsTest {
-
- @Test
- public void define_ws() {
- BranchesWs underTest = new BranchesWs(new BranchWsAction() {
- @Override
- public void define(WebService.NewController context) {
- context.createAction("foo").setHandler(this);
- }
-
- @Override
- public void handle(Request request, Response response) throws Exception {
-
- }
- });
-
- WebService.Context context = new WebService.Context();
- underTest.define(context);
-
- assertThat(context.controller("api/project_branches").action("foo")).isNotNull();
- }
-
-}
+++ /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 org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.mockito.ArgumentCaptor;
-import org.sonar.api.server.ws.WebService;
-import org.sonar.api.utils.System2;
-import org.sonar.api.web.UserRole;
-import org.sonar.db.DbSession;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentTesting;
-import org.sonar.server.component.ComponentCleanerService;
-import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.component.TestComponentFinder;
-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.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-public class DeleteActionTest {
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- @Rule
- public DbTester db = DbTester.create(System2.INSTANCE);
-
- private ComponentCleanerService componentCleanerService = mock(ComponentCleanerService.class);
- private ComponentFinder componentFinder = TestComponentFinder.from(db);
-
- @Rule
- public UserSessionRule userSession = UserSessionRule.standalone();
-
- public WsActionTester tester = new WsActionTester(new DeleteAction(db.getDbClient(), componentFinder, userSession, componentCleanerService));
-
- @Test
- public void test_definition() {
- WebService.Action definition = tester.getDef();
- assertThat(definition.key()).isEqualTo("delete");
- assertThat(definition.isPost()).isTrue();
- assertThat(definition.isInternal()).isFalse();
- 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();
- }
-
- public void fail_branch_does_not_exist() {
- ComponentDto project = db.components().insertPrivateProject();
- ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
- userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
-
- expectedException.expect(NotFoundException.class);
- expectedException.expectMessage("Branch 'branch1' not found");
-
- tester.newRequest()
- .setParam("project", file.getDbKey())
- .setParam("branch", "branch1")
- .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();
- }
-
- @Test
- public void fail_if_branch_is_main() {
- ComponentDto project = db.components().insertMainBranch();
- db.executeUpdateSql("UPDATE project_branches set KEE = 'main'");
- userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
-
- // not found because the DB keys don't contain the name
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Only non-main branches can be deleted");
-
- tester.newRequest()
- .setParam("project", project.getKey())
- .setParam("branch", "main")
- .execute();
- }
-
- @Test
- public void delete_branch() {
-
- ComponentDto project = db.components().insertMainBranch();
- ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("branch1"));
-
- userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
-
- tester.newRequest()
- .setParam("project", project.getKey())
- .setParam("branch", "branch1")
- .execute();
- verifyDeletedKey(branch.getDbKey());
- }
-
- private void verifyDeletedKey(String key) {
- ArgumentCaptor<ComponentDto> argument = ArgumentCaptor.forClass(ComponentDto.class);
- verify(componentCleanerService).deleteBranch(any(DbSession.class), argument.capture());
- assertThat(argument.getValue().getDbKey()).isEqualTo(key);
- }
-
-}
+++ /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 org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.api.resources.ResourceTypes;
-import org.sonar.api.server.ws.WebService;
-import org.sonar.api.utils.DateUtils;
-import org.sonar.api.utils.System2;
-import org.sonar.api.web.UserRole;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.BranchType;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentTesting;
-import org.sonar.db.component.ResourceTypesRule;
-import org.sonar.db.component.SnapshotDto;
-import org.sonar.db.component.SnapshotTesting;
-import org.sonar.db.metric.MetricDto;
-import org.sonar.db.organization.OrganizationDto;
-import org.sonar.db.rule.RuleDefinitionDto;
-import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.es.EsTester;
-import org.sonar.server.exceptions.NotFoundException;
-import org.sonar.server.issue.index.IssueIndex;
-import org.sonar.server.issue.index.IssueIndexDefinition;
-import org.sonar.server.issue.index.IssueIndexer;
-import org.sonar.server.issue.index.IssueIteratorFactory;
-import org.sonar.server.permission.index.AuthorizationTypeSupport;
-import org.sonar.server.permission.index.PermissionIndexerTester;
-import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.ws.WsActionTester;
-import org.sonarqube.ws.Common;
-import org.sonarqube.ws.MediaTypes;
-import org.sonarqube.ws.WsBranches;
-import org.sonarqube.ws.WsBranches.Branch;
-import org.sonarqube.ws.WsBranches.ListWsResponse;
-
-import static java.lang.String.format;
-import static java.util.Collections.emptySet;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.tuple;
-import static org.sonar.api.issue.Issue.RESOLUTION_FALSE_POSITIVE;
-import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
-import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
-import static org.sonar.api.resources.Qualifiers.PROJECT;
-import static org.sonar.api.rules.RuleType.BUG;
-import static org.sonar.api.rules.RuleType.CODE_SMELL;
-import static org.sonar.api.rules.RuleType.VULNERABILITY;
-import static org.sonar.api.utils.DateUtils.dateToLong;
-import static org.sonar.api.utils.DateUtils.parseDateTime;
-import static org.sonar.test.JsonAssert.assertJson;
-import static org.sonarqube.ws.WsBranches.Branch.Status;
-
-public class ListActionTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
- @Rule
- public DbTester db = DbTester.create(System2.INSTANCE);
- @Rule
- public EsTester es = new EsTester(new IssueIndexDefinition(new MapSettings().asConfig()));
- @Rule
- public UserSessionRule userSession = UserSessionRule.standalone();
-
- private ResourceTypes resourceTypes = new ResourceTypesRule().setRootQualifiers(PROJECT);
- private IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()));
- private IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSession, new AuthorizationTypeSupport(userSession));
- private PermissionIndexerTester permissionIndexerTester = new PermissionIndexerTester(es, issueIndexer);
-
- private MetricDto qualityGateStatus;
-
- public WsActionTester ws = new WsActionTester(new ListAction(db.getDbClient(), userSession, new ComponentFinder(db.getDbClient(), resourceTypes), issueIndex));
-
- @Before
- public void setUp() throws Exception {
- qualityGateStatus = db.measures().insertMetric(m -> m.setKey(ALERT_STATUS_KEY));
- }
-
- @Test
- public void test_definition() {
- WebService.Action definition = ws.getDef();
- assertThat(definition.key()).isEqualTo("list");
- assertThat(definition.isPost()).isFalse();
- assertThat(definition.isInternal()).isFalse();
- assertThat(definition.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("project");
- assertThat(definition.since()).isEqualTo("6.6");
- }
-
- @Test
- public void test_example() {
- ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey("sonarqube"));
- ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setKey("feature/bar").setBranchType(BranchType.LONG));
- ComponentDto shortLivingBranch = db.components().insertProjectBranch(project, b -> b.setKey("feature/foo").setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
- userSession.logIn().addProjectPermission(UserRole.USER, project);
-
- db.getDbClient().snapshotDao().insert(db.getSession(), SnapshotTesting.newAnalysis(longLivingBranch).setLast(true).setCreatedAt(DateUtils.parseDateTime("2017-04-01T01:15:42+0100").getTime()));
- db.getDbClient().snapshotDao().insert(db.getSession(), SnapshotTesting.newAnalysis(shortLivingBranch).setLast(true).setCreatedAt(DateUtils.parseDateTime("2017-04-03T13:37:00+0100").getTime()));
- db.commit();
-
- String json = ws.newRequest()
- .setParam("project", project.getDbKey())
- .execute()
- .getInput();
-
- assertJson(json).isSimilarTo(ws.getDef().responseExampleAsString());
- }
-
- @Test
- public void main_branch() {
- ComponentDto project = db.components().insertMainBranch();
- userSession.logIn().addProjectPermission(UserRole.USER, project);
-
- ListWsResponse response = ws.newRequest()
- .setParam("project", project.getDbKey())
- .executeProtobuf(ListWsResponse.class);
-
- assertThat(response.getBranchesList())
- .extracting(Branch::getName, Branch::getIsMain, Branch::getType)
- .containsExactlyInAnyOrder(tuple("master", true, Common.BranchType.LONG));
- }
-
- @Test
- public void main_branch_with_specified_name() {
- OrganizationDto organization = db.organizations().insert();
- ComponentDto project = db.components().insertMainBranch(organization, "head");
- userSession.logIn().addProjectPermission(UserRole.USER, project);
-
- ListWsResponse response = ws.newRequest()
- .setParam("project", project.getDbKey())
- .executeProtobuf(ListWsResponse.class);
-
- assertThat(response.getBranchesList())
- .extracting(Branch::getName, Branch::getIsMain, Branch::getType)
- .containsExactlyInAnyOrder(tuple("head", true, Common.BranchType.LONG));
- }
-
- @Test
- public void test_project_with_zero_branches() {
- ComponentDto project = db.components().insertPrivateProject();
- userSession.logIn().addProjectPermission(UserRole.USER, project);
-
- String json = ws.newRequest()
- .setParam("project", project.getDbKey())
- .setMediaType(MediaTypes.JSON)
- .execute()
- .getInput();
- assertJson(json).isSimilarTo("{\"branches\": []}");
- }
-
- @Test
- public void test_project_with_branches() {
- ComponentDto project = db.components().insertMainBranch();
- db.components().insertProjectBranch(project, b -> b.setKey("feature/bar"));
- db.components().insertProjectBranch(project, b -> b.setKey("feature/foo"));
- userSession.logIn().addProjectPermission(UserRole.USER, project);
-
- ListWsResponse response = ws.newRequest()
- .setParam("project", project.getDbKey())
- .executeProtobuf(ListWsResponse.class);
-
- assertThat(response.getBranchesList())
- .extracting(Branch::getName, Branch::getType)
- .containsExactlyInAnyOrder(
- tuple("master", Common.BranchType.LONG),
- tuple("feature/foo", Common.BranchType.LONG),
- tuple("feature/bar", Common.BranchType.LONG));
- }
-
- @Test
- public void short_living_branches() {
- ComponentDto project = db.components().insertMainBranch();
- userSession.logIn().addProjectPermission(UserRole.USER, project);
- ComponentDto longLivingBranch = db.components().insertProjectBranch(project,
- b -> b.setKey("long").setBranchType(BranchType.LONG));
- ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
- b -> b.setKey("short").setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
- ComponentDto shortLivingBranchOnMaster = db.components().insertProjectBranch(project,
- b -> b.setKey("short_on_master").setBranchType(BranchType.SHORT).setMergeBranchUuid(project.uuid()));
-
- ListWsResponse response = ws.newRequest()
- .setParam("project", project.getKey())
- .executeProtobuf(ListWsResponse.class);
-
- assertThat(response.getBranchesList())
- .extracting(Branch::getName, Branch::getType, Branch::getMergeBranch)
- .containsExactlyInAnyOrder(
- tuple("master", Common.BranchType.LONG, ""),
- tuple(longLivingBranch.getBranch(), Common.BranchType.LONG, ""),
- tuple(shortLivingBranch.getBranch(), Common.BranchType.SHORT, longLivingBranch.getBranch()),
- tuple(shortLivingBranchOnMaster.getBranch(), Common.BranchType.SHORT, "master"));
- }
-
- @Test
- public void mergeBranch_is_using_default_main_name_when_main_branch_has_no_name() {
- ComponentDto project = db.components().insertMainBranch();
- userSession.logIn().addProjectPermission(UserRole.USER, project);
- ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
- b -> b.setKey("short").setBranchType(BranchType.SHORT).setMergeBranchUuid(project.uuid()));
-
- WsBranches.ShowWsResponse response = ws.newRequest()
- .setParam("project", shortLivingBranch.getKey())
- .executeProtobuf(WsBranches.ShowWsResponse.class);
-
- assertThat(response.getBranch())
- .extracting(Branch::getName, Branch::getType, Branch::getMergeBranch)
- .containsExactlyInAnyOrder(shortLivingBranch.getBranch(), Common.BranchType.SHORT, "master");
- }
-
- @Test
- public void short_living_branch_on_removed_branch() {
- ComponentDto project = db.components().insertMainBranch();
- userSession.logIn().addProjectPermission(UserRole.USER, project);
- ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
- b -> b.setKey("short").setBranchType(BranchType.SHORT).setMergeBranchUuid("unknown"));
-
- ListWsResponse response = ws.newRequest()
- .setParam("project", project.getKey())
- .executeProtobuf(ListWsResponse.class);
-
- assertThat(response.getBranchesList())
- .extracting(Branch::getName, Branch::getType, Branch::hasMergeBranch, Branch::getIsOrphan)
- .containsExactlyInAnyOrder(
- tuple("master", Common.BranchType.LONG, false, false),
- tuple(shortLivingBranch.getBranch(), Common.BranchType.SHORT, false, true));
- }
-
- @Test
- public void status_on_long_living_branch() {
- ComponentDto project = db.components().insertMainBranch();
- userSession.logIn().addProjectPermission(UserRole.USER, project);
- ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
- SnapshotDto branchAnalysis = db.components().insertSnapshot(branch);
- db.measures().insertMeasure(branch, branchAnalysis, qualityGateStatus, m -> m.setData("OK"));
-
- ListWsResponse response = ws.newRequest()
- .setParam("project", project.getKey())
- .executeProtobuf(ListWsResponse.class);
-
- assertThat(response.getBranchesList())
- .extracting(b -> b.getStatus().hasQualityGateStatus(), b -> b.getStatus().getQualityGateStatus())
- .containsExactlyInAnyOrder(tuple(false, ""), tuple(true, "OK"));
- }
-
- @Test
- public void status_on_short_living_branches() {
- ComponentDto project = db.components().insertMainBranch();
- userSession.logIn().addProjectPermission(UserRole.USER, project);
- ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
- ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
- b -> b.setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
- RuleDefinitionDto rule = db.rules().insert();
- db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(BUG).setResolution(null));
- db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(BUG).setResolution(RESOLUTION_FIXED));
- db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(VULNERABILITY).setResolution(null));
- db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(VULNERABILITY).setResolution(null));
- db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(CODE_SMELL).setResolution(null));
- db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(CODE_SMELL).setResolution(null));
- db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(CODE_SMELL).setResolution(null));
- db.issues().insert(rule, shortLivingBranch, shortLivingBranch, i -> i.setType(CODE_SMELL).setResolution(RESOLUTION_FALSE_POSITIVE));
- issueIndexer.indexOnStartup(emptySet());
- permissionIndexerTester.allowOnlyAnyone(project);
-
- ListWsResponse response = ws.newRequest()
- .setParam("project", project.getKey())
- .executeProtobuf(ListWsResponse.class);
-
- assertThat(response.getBranchesList().stream().map(WsBranches.Branch::getStatus))
- .extracting(Status::hasBugs, Status::getBugs, Status::hasVulnerabilities, Status::getVulnerabilities, Status::hasCodeSmells, Status::getCodeSmells)
- .containsExactlyInAnyOrder(
- tuple(false, 0L, false, 0L, false, 0L),
- tuple(false, 0L, false, 0L, false, 0L),
- tuple(true, 1L, true, 2L, true, 3L));
- }
-
- @Test
- public void status_on_short_living_branch_with_no_issue() {
- ComponentDto project = db.components().insertMainBranch();
- userSession.logIn().addProjectPermission(UserRole.USER, project);
- ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
- db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
- issueIndexer.indexOnStartup(emptySet());
- permissionIndexerTester.allowOnlyAnyone(project);
-
- ListWsResponse response = ws.newRequest()
- .setParam("project", project.getKey())
- .executeProtobuf(ListWsResponse.class);
-
- assertThat(response.getBranchesList().stream().filter(b -> b.getType().equals(Common.BranchType.SHORT)).map(WsBranches.Branch::getStatus))
- .extracting(Status::getBugs, Status::getVulnerabilities, Status::getCodeSmells)
- .containsExactlyInAnyOrder(tuple(0L, 0L, 0L));
- }
-
- @Test
- public void response_contains_date_of_last_analysis() {
- Long lastAnalysisLongLivingBranch = dateToLong(parseDateTime("2017-04-01T00:00:00+0100"));
- Long previousAnalysisShortLivingBranch = dateToLong(parseDateTime("2017-04-02T00:00:00+0100"));
- Long lastAnalysisShortLivingBranch = dateToLong(parseDateTime("2017-04-03T00:00:00+0100"));
-
- ComponentDto project = db.components().insertMainBranch();
- userSession.logIn().addProjectPermission(UserRole.USER, project);
- ComponentDto shortLivingBranch1 = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.SHORT).setMergeBranchUuid(project.uuid()));
- ComponentDto longLivingBranch2 = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
- ComponentDto shortLivingBranch2 = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch2.uuid()));
- db.getDbClient().snapshotDao().insert(db.getSession(),
- SnapshotTesting.newAnalysis(longLivingBranch2).setCreatedAt(lastAnalysisLongLivingBranch));
- db.getDbClient().snapshotDao().insert(db.getSession(),
- SnapshotTesting.newAnalysis(shortLivingBranch2).setCreatedAt(previousAnalysisShortLivingBranch).setLast(false));
- db.getDbClient().snapshotDao().insert(db.getSession(),
- SnapshotTesting.newAnalysis(shortLivingBranch2).setCreatedAt(lastAnalysisShortLivingBranch));
- db.commit();
- issueIndexer.indexOnStartup(emptySet());
- permissionIndexerTester.allowOnlyAnyone(project);
-
- ListWsResponse response = ws.newRequest()
- .setParam("project", project.getKey())
- .executeProtobuf(ListWsResponse.class);
-
- assertThat(response.getBranchesList())
- .extracting(WsBranches.Branch::getType, WsBranches.Branch::hasAnalysisDate, b -> "".equals(b.getAnalysisDate()) ? null : dateToLong(parseDateTime(b.getAnalysisDate())))
- .containsExactlyInAnyOrder(
- tuple(Common.BranchType.LONG, false, null),
- tuple(Common.BranchType.SHORT, false, null),
- tuple(Common.BranchType.LONG, true, lastAnalysisLongLivingBranch),
- tuple(Common.BranchType.SHORT, true, lastAnalysisShortLivingBranch)
- );
- }
-
- @Test
- public void fail_when_using_branch_db_key() throws Exception {
- OrganizationDto organization = db.organizations().insert();
- ComponentDto project = db.components().insertMainBranch(organization);
- userSession.logIn().addProjectPermission(UserRole.USER, project);
- ComponentDto branch = db.components().insertProjectBranch(project);
-
- expectedException.expect(NotFoundException.class);
- expectedException.expectMessage(format("Component key '%s' not found", branch.getDbKey()));
-
- ws.newRequest()
- .setParam("project", branch.getDbKey())
- .execute();
- }
-
- @Test
- public void fail_if_missing_project_parameter() {
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("The 'project' parameter is missing");
-
- ws.newRequest().execute();
- }
-
- @Test
- public void fail_if_not_a_reference_on_project() {
- ComponentDto project = db.components().insertPrivateProject();
- ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
- userSession.logIn().addProjectPermission(UserRole.USER, project);
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Invalid project key");
-
- ws.newRequest()
- .setParam("project", file.getDbKey())
- .execute();
- }
-
- @Test
- public void fail_if_project_does_not_exist() {
- expectedException.expect(NotFoundException.class);
- expectedException.expectMessage("Component key 'foo' not found");
-
- ws.newRequest()
- .setParam("project", "foo")
- .execute();
- }
-
-}
+++ /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()).isFalse();
- assertThat(definition.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("project", "name");
- 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 'name' 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("name", "branch1")
- .execute();
- }
-
- @Test
- public void 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("name", "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 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("name", "master")
- .execute();
-
- tester.newRequest()
- .setParam("project", project.getKey())
- .setParam("name", "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("name", "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("name", "branch1")
- .execute();
- }
-}