}
@Test
- public void generate_keys_when_using_branch() {
+ public void generate_keys_when_using_new_branch() {
Branch branch = mock(Branch.class);
when(branch.getName()).thenReturn("origin/feature");
when(branch.isMain()).thenReturn(false);
verifyComponent(FILE_1_REF, "generated", REPORT_MODULE_KEY + ":" + REPORT_FILE_KEY_1, null);
}
+ @Test
+ public void generate_keys_when_using_existing_branch() {
+ ComponentDto projectDto = dbTester.components().insertMainBranch();
+ ComponentDto branchDto = dbTester.components().insertProjectBranch(projectDto);
+ Branch branch = mock(Branch.class);
+ when(branch.getName()).thenReturn(branchDto.getBranch());
+ when(branch.isMain()).thenReturn(false);
+ when(branch.isLegacyFeature()).thenReturn(false);
+ when(branch.generateKey(any(), any())).thenReturn(branchDto.getDbKey());
+ analysisMetadataHolder.setRootComponentRef(ROOT_REF)
+ .setAnalysisDate(ANALYSIS_DATE)
+ .setProject(Project.from(projectDto))
+ .setBranch(branch);
+ BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
+ reportReader.putComponent(componentWithKey(ROOT_REF, PROJECT, branchDto.getKey()));
+
+ underTest.execute();
+
+ verifyComponent(ROOT_REF, branchDto.getDbKey(), branchDto.getKey(), branchDto.uuid());
+ }
+
@Test
public void generate_keys_when_using_main_branch() {
Branch branch = new DefaultBranchImpl();
);
assertThat(picoContainer.getParent().getParent().getComponentAdapters()).hasSize(
CONTAINER_ITSELF
- + 14 // MigrationConfigurationModule
- + 19 // level 2
+ + 17 // MigrationConfigurationModule
+ + 17 // level 2
);
assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize(
COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import javax.annotation.CheckForNull;
return executeLargeInputs(allKeys, subKeys -> mapper(session).selectByKeysAndBranch(subKeys, branch));
}
+ /**
+ * Return list of components that will will mix main and branch components.
+ * Please note that a project can only appear once in the list, it's not possible to ask for many branches on same project with this method.
+ */
+ public List<ComponentDto> selectByKeysAndBranches(DbSession session, Map<String, String> branchesByKey) {
+ List<String> dbKeys = branchesByKey.entrySet().stream().map(entry -> generateBranchKey(entry.getKey(), entry.getValue())).collect(toList());
+ return executeLargeInputs(dbKeys, subKeys -> mapper(session).selectByDbKeys(subKeys));
+ }
+
public List<ComponentDto> selectByKeysAndPullRequest(DbSession session, Collection<String> keys, String pullRequestId) {
List<String> dbKeys = keys.stream().map(k -> generatePullRequestKey(k, pullRequestId)).collect(toList());
List<String> allKeys = Stream.of(keys, dbKeys).flatMap(Collection::stream).collect(toList());
List<ComponentDto> selectByKeys(@Param("keys") Collection<String> keys);
+ List<ComponentDto> selectByDbKeys(@Param("dbKeys") Collection<String> dbKeys);
+
List<ComponentDto> selectByKeysAndBranch(@Param("keys") Collection<String> keys, @Param("branch") String branch);
List<ComponentDto> selectByIds(@Param("ids") Collection<Long> ids);
</foreach>
</select>
+ <select id="selectByDbKeys" parameterType="String" resultType="Component">
+ select
+ <include refid="componentColumns"/>
+ from projects p
+ where
+ p.enabled=${_true}
+ and p.kee in
+ <foreach collection="dbKeys" open="(" close=")" item="key" separator=",">
+ #{key,jdbcType=VARCHAR}
+ </foreach>
+ </select>
+
<select id="selectByKeysAndBranch" parameterType="String" resultType="Component">
SELECT
<include refid="componentColumns"/>
package org.sonar.db.component;
import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import static org.assertj.core.api.Assertions.entry;
import static org.assertj.core.api.Assertions.tuple;
import static org.assertj.guava.api.Assertions.assertThat;
+import static org.sonar.api.resources.Qualifiers.APP;
import static org.sonar.db.component.ComponentTesting.newDirectory;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newModuleDto;
assertThat(underTest.selectByKeysAndBranch(dbSession, singletonList(branch.getKey()), "master")).extracting(ComponentDto::uuid).containsExactlyInAnyOrder(project.uuid());
}
+ @Test
+ public void select_by_keys_and_branches() {
+ ComponentDto project = db.components().insertMainBranch();
+ ComponentDto projectBranch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
+ ComponentDto application = db.components().insertMainBranch(a -> a.setQualifier(APP));
+ ComponentDto applicationBranch = db.components().insertProjectBranch(application, b -> b.setKey("my_branch"));
+
+ assertThat(underTest.selectByKeysAndBranches(db.getSession(), ImmutableMap.of(
+ projectBranch.getKey(), projectBranch.getBranch(),
+ applicationBranch.getKey(), applicationBranch.getBranch())))
+ .extracting(ComponentDto::getKey, ComponentDto::getBranch)
+ .containsExactlyInAnyOrder(
+ tuple(projectBranch.getKey(), "my_branch"),
+ tuple(applicationBranch.getKey(), "my_branch"));
+ assertThat(underTest.selectByKeysAndBranches(db.getSession(), ImmutableMap.of(
+ projectBranch.getKey(), "unknown",
+ "unknown", projectBranch.getBranch())))
+ .extracting(ComponentDto::getDbKey)
+ .isEmpty();
+ assertThat(underTest.selectByKeysAndBranches(db.getSession(), Collections.emptyMap())).isEmpty();
+ }
+
@Test
public void get_by_ids() {
ComponentDto project1 = db.components().insertPrivateProject();
import static com.google.common.base.Preconditions.checkNotNull;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.sonar.db.component.BranchType.PULL_REQUEST;
+import static org.sonar.db.component.ComponentDto.BRANCH_KEY_SEPARATOR;
+import static org.sonar.db.component.ComponentDto.PULL_REQUEST_SEPARATOR;
+import static org.sonar.db.component.ComponentDto.UUID_PATH_OF_ROOT;
import static org.sonar.db.component.ComponentDto.UUID_PATH_SEPARATOR;
+import static org.sonar.db.component.ComponentDto.formatUuidPathFromParent;
+import static org.sonar.db.component.ComponentDto.generateBranchKey;
+import static org.sonar.db.component.ComponentDto.generatePullRequestKey;
public class ComponentTesting {
private static String generateKey(String key, ComponentDto parentModuleOrProject) {
String branch = parentModuleOrProject.getBranch();
if (branch != null) {
- return ComponentDto.generateBranchKey(key, branch);
+ return generateBranchKey(key, branch);
}
String pullRequest = parentModuleOrProject.getPullRequest();
if (pullRequest != null) {
- return ComponentDto.generatePullRequestKey(key, pullRequest);
+ return generatePullRequestKey(key, pullRequest);
}
return key;
return new ComponentDto()
.setOrganizationUuid(organizationUuid)
.setUuid(uuid)
- .setUuidPath(ComponentDto.UUID_PATH_OF_ROOT)
+ .setUuidPath(UUID_PATH_OF_ROOT)
.setProjectUuid(uuid)
.setModuleUuidPath(UUID_PATH_SEPARATOR + uuid + UUID_PATH_SEPARATOR)
.setRootUuid(uuid)
return new ComponentDto()
.setOrganizationUuid(parent.getOrganizationUuid())
.setUuid(uuid)
- .setUuidPath(ComponentDto.formatUuidPathFromParent(parent))
+ .setUuidPath(formatUuidPathFromParent(parent))
.setProjectUuid(moduleOrProject.projectUuid())
.setRootUuid(moduleOrProject.uuid())
.setModuleUuid(moduleOrProject.uuid())
}
public static ComponentDto newProjectBranch(ComponentDto project, BranchDto branchDto) {
- checkArgument(project.qualifier().equals(Qualifiers.PROJECT));
+ checkArgument(project.qualifier().equals(Qualifiers.PROJECT) || project.qualifier().equals(Qualifiers.APP));
checkArgument(project.getMainBranchProjectUuid() == null);
String branchName = branchDto.getKey();
- String branchSeparator = branchDto.getBranchType() == PULL_REQUEST ? ":PULL_REQUEST:" : ":BRANCH:";
+ String branchSeparator = branchDto.getBranchType() == PULL_REQUEST ? PULL_REQUEST_SEPARATOR : BRANCH_KEY_SEPARATOR;
String uuid = branchDto.getUuid();
return new ComponentDto()
.setUuid(uuid)
.setOrganizationUuid(project.getOrganizationUuid())
- .setUuidPath(ComponentDto.UUID_PATH_OF_ROOT)
+ .setUuidPath(UUID_PATH_OF_ROOT)
.setProjectUuid(uuid)
.setModuleUuidPath(UUID_PATH_SEPARATOR + uuid + UUID_PATH_SEPARATOR)
.setRootUuid(uuid)
import org.sonar.server.platform.db.migration.version.v70.DbVersion70;
import org.sonar.server.platform.db.migration.version.v71.DbVersion71;
import org.sonar.server.platform.db.migration.version.v72.DbVersion72;
+import org.sonar.server.platform.db.migration.version.v73.DbVersion73;
public class MigrationConfigurationModule extends Module {
@Override
DbVersion70.class,
DbVersion71.class,
DbVersion72.class,
+ DbVersion73.class,
// migration steps
MigrationStepRegistryImpl.class,
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v73;
+
+import org.sonar.server.platform.db.migration.step.MigrationStepRegistry;
+import org.sonar.server.platform.db.migration.version.DbVersion;
+
+public class DbVersion73 implements DbVersion {
+
+ @Override
+ public void addSteps(MigrationStepRegistry registry) {
+ registry
+ .add(2200, "Populate PROJECT_BRANCHES with existing main application branches", PopulateMainApplicationBranches.class)
+ ;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v73;
+
+import java.sql.SQLException;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+
+public class PopulateMainApplicationBranches extends DataChange {
+
+ private static final String MAIN_BRANCH_NAME = "master";
+
+ private final System2 system2;
+
+ public PopulateMainApplicationBranches(Database db, System2 system2) {
+ super(db);
+ this.system2 = system2;
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ long now = system2.now();
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("SELECT uuid FROM projects p "
+ + "WHERE p.scope='PRJ' AND p.qualifier='APP' AND p.main_branch_project_uuid IS NULL "
+ + "AND NOT EXISTS (SELECT uuid FROM project_branches b WHERE b.uuid = p.uuid)");
+ massUpdate.update("INSERT INTO project_branches (uuid, project_uuid, kee, branch_type, key_type, "
+ + "merge_branch_uuid, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
+ massUpdate.rowPluralName("applications");
+ massUpdate.execute((row, update) -> {
+ String uuid = row.getString(1);
+ update.setString(1, uuid);
+ update.setString(2, uuid);
+ update.setString(3, MAIN_BRANCH_NAME);
+ update.setString(4, "LONG");
+ update.setString(5, "BRANCH");
+ update.setString(6, null);
+ update.setLong(7, now);
+ update.setLong(8, now);
+ return true;
+ });
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.platform.db.migration.version.v73;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
assertThat(container.getPicoContainer().getComponentAdapters())
.hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER
// DbVersion classes
- + 13
+ + 14
// Others
+ 3);
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v73;
+
+import org.junit.Test;
+
+import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMinimumMigrationNumber;
+
+public class DbVersion73Test {
+
+ private DbVersion73 underTest = new DbVersion73();
+
+ @Test
+ public void migrationNumber_starts_at_2200() {
+ verifyMinimumMigrationNumber(underTest, 2200);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v73;
+
+import java.sql.SQLException;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import org.assertj.core.groups.Tuple;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.internal.TestSystem2;
+import org.sonar.core.util.Uuids;
+import org.sonar.db.CoreDbTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+
+public class PopulateMainApplicationBranchesTest {
+
+ private final static long PAST = 10_000_000_000L;
+ private final static long NOW = 50_000_000_000L;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(PopulateMainApplicationBranchesTest.class, "schema.sql");
+
+ private System2 system2 = new TestSystem2().setNow(NOW);
+
+ private PopulateMainApplicationBranches underTest = new PopulateMainApplicationBranches(db.database(), system2);
+
+ @Test
+ public void migrate() throws SQLException {
+ String project = insertApplication();
+
+ underTest.execute();
+
+ assertProjectBranches(tuple("master", project, project, "LONG", NOW, NOW));
+ }
+
+ @Test
+ public void does_nothing_on_non_applications() throws SQLException {
+ insertComponent(null, "BRC");
+ insertComponent(null, "VW");
+
+ underTest.execute();
+
+ assertThat(db.countRowsOfTable("project_branches")).isZero();
+ }
+
+ @Test
+ public void does_nothing_on_empty_table() throws SQLException {
+ underTest.execute();
+
+ assertThat(db.countRowsOfTable("project_branches")).isZero();
+ }
+
+ @Test
+ public void does_nothing_if_already_migrated() throws SQLException {
+ String application = insertApplication();
+ insertMainBranch(application);
+
+ underTest.execute();
+
+ assertProjectBranches(tuple("master", application, application, "LONG", PAST, PAST));
+ }
+
+ private void assertProjectBranches(Tuple... expectedTuples) {
+ assertThat(db.select("SELECT KEE, UUID, PROJECT_UUID, BRANCH_TYPE, CREATED_AT, UPDATED_AT FROM PROJECT_BRANCHES")
+ .stream()
+ .map(row -> new Tuple(row.get("KEE"), row.get("UUID"), row.get("PROJECT_UUID"), row.get("BRANCH_TYPE"), row.get("CREATED_AT"), row.get("UPDATED_AT")))
+ .collect(Collectors.toList()))
+ .containsExactlyInAnyOrder(expectedTuples);
+ }
+
+ private String insertApplication() {
+ return insertComponent(null, "APP");
+ }
+
+ private String insertComponent(@Nullable String mainBranchUuid, String qualifier) {
+ String uuid = Uuids.createFast();
+ db.executeInsert("PROJECTS",
+ "ORGANIZATION_UUID", "default-org",
+ "KEE", uuid + "-key",
+ "UUID", uuid,
+ "PROJECT_UUID", uuid,
+ "MAIN_BRANCH_PROJECT_UUID", mainBranchUuid,
+ "UUID_PATH", ".",
+ "ROOT_UUID", uuid,
+ "PRIVATE", "true",
+ "SCOPE", "PRJ",
+ "QUALIFIER", qualifier);
+ return uuid;
+ }
+
+ private void insertMainBranch(String uuid) {
+ db.executeInsert("PROJECT_BRANCHES",
+ "UUID", uuid,
+ "PROJECT_UUID", uuid,
+ "KEE", "master",
+ "KEY_TYPE", "BRANCH",
+ "BRANCH_TYPE", "LONG",
+ "CREATED_AT", PAST,
+ "UPDATED_AT", PAST);
+ }
+}
--- /dev/null
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
+ "KEE" VARCHAR(400),
+ "UUID" VARCHAR(50) NOT NULL,
+ "UUID_PATH" VARCHAR(1500) NOT NULL,
+ "ROOT_UUID" VARCHAR(50) NOT NULL,
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(1500),
+ "MAIN_BRANCH_PROJECT_UUID" VARCHAR(50),
+ "NAME" VARCHAR(2000),
+ "DESCRIPTION" VARCHAR(2000),
+ "PRIVATE" BOOLEAN NOT NULL,
+ "TAGS" VARCHAR(500),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_COMPONENT_UUID" VARCHAR(50),
+ "LONG_NAME" VARCHAR(2000),
+ "DEVELOPER_UUID" VARCHAR(50),
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT,
+ "B_CHANGED" BOOLEAN,
+ "B_COPY_COMPONENT_UUID" VARCHAR(50),
+ "B_DESCRIPTION" VARCHAR(2000),
+ "B_ENABLED" BOOLEAN,
+ "B_UUID_PATH" VARCHAR(1500),
+ "B_LANGUAGE" VARCHAR(20),
+ "B_LONG_NAME" VARCHAR(500),
+ "B_MODULE_UUID" VARCHAR(50),
+ "B_MODULE_UUID_PATH" VARCHAR(1500),
+ "B_NAME" VARCHAR(500),
+ "B_PATH" VARCHAR(2000),
+ "B_QUALIFIER" VARCHAR(10)
+);
+CREATE INDEX "PROJECTS_ORGANIZATION" ON "PROJECTS" ("ORGANIZATION_UUID");
+CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE");
+CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID");
+CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID");
+CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID");
+CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID");
+CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER");
+
+CREATE TABLE "PROJECT_BRANCHES" (
+ "UUID" VARCHAR(50) NOT NULL,
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "KEE" VARCHAR(255) NOT NULL,
+ "KEY_TYPE" VARCHAR(12) NOT NULL,
+ "BRANCH_TYPE" VARCHAR(12),
+ "MERGE_BRANCH_UUID" VARCHAR(50),
+ "PULL_REQUEST_BINARY" BLOB,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL,
+
+ CONSTRAINT "PK_PROJECT_BRANCHES" PRIMARY KEY ("UUID")
+);
+CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE_KEY_TYPE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE", "KEY_TYPE");
\ No newline at end of file
*/
package org.sonar.server.branch.ws;
+import com.google.common.collect.ImmutableSet;
import com.google.common.io.Resources;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
import java.util.function.Function;
import javax.annotation.Nullable;
+import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
-import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.BranchDto;
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.APP;
import static org.sonar.api.resources.Qualifiers.PROJECT;
import static org.sonar.api.utils.DateUtils.formatDateTime;
import static org.sonar.api.web.UserRole.USER;
public class ListAction implements BranchWsAction {
+ private static final Set<String> ALLOWED_QUALIFIERS = ImmutableSet.of(PROJECT, APP);
+
private final DbClient dbClient;
private final UserSession userSession;
private final ComponentFinder componentFinder;
.setDescription("List the branches of a project.<br/>" +
"Requires 'Browse' or 'Execute analysis' rights on the specified project.")
.setResponseExample(Resources.getResource(getClass(), "list-example.json"))
+ .setChangelog(new Change("7.2", "Application can be used on this web service"))
.setHandler(this);
addProjectParam(action);
try (DbSession dbSession = dbClient.openSession(false)) {
ComponentDto project = componentFinder.getByKey(dbSession, projectKey);
checkPermission(project);
- checkArgument(project.isEnabled() && PROJECT.equals(project.qualifier()), "Invalid project key");
+ checkArgument(ALLOWED_QUALIFIERS.contains(project.qualifier()), "Invalid project");
Collection<BranchDto> branches = dbClient.branchDao().selectByComponent(dbSession, project).stream()
.filter(b -> b.getBranchType() == SHORT || b.getBranchType() == LONG)
- .collect(MoreCollectors.toList());
+ .collect(toList());
List<String> branchUuids = branches.stream().map(BranchDto::getUuid).collect(toList());
Map<String, BranchDto> mergeBranchesByUuid = dbClient.branchDao()
*/
package org.sonar.server.component;
+import com.google.common.collect.ImmutableSet;
import java.util.Date;
import java.util.List;
import java.util.Locale;
+import java.util.Set;
import javax.annotation.Nullable;
import org.sonar.api.i18n.I18n;
import org.sonar.api.resources.Qualifiers;
public class ComponentUpdater {
+ private static final Set<String> MAIN_BRANCH_QUALIFIERS = ImmutableSet.of(Qualifiers.PROJECT, Qualifiers.APP);
+
private final DbClient dbClient;
private final I18n i18n;
private final System2 system2;
checkKeyFormat(newComponent.qualifier(), newComponent.key());
ComponentDto componentDto = createRootComponent(dbSession, newComponent);
if (isRootProject(componentDto)) {
- createBranch(dbSession, componentDto.uuid());
+ createMainBranch(dbSession, componentDto.uuid());
}
removeDuplicatedProjects(dbSession, componentDto.getDbKey());
handlePermissionTemplate(dbSession, componentDto, newComponent.getOrganizationUuid(), userId);
}
private static boolean isRootProject(ComponentDto componentDto) {
- return Scopes.PROJECT.equals(componentDto.scope()) && Qualifiers.PROJECT.equals(componentDto.qualifier());
+ return Scopes.PROJECT.equals(componentDto.scope())
+ && MAIN_BRANCH_QUALIFIERS.contains(componentDto.qualifier());
}
- private BranchDto createBranch(DbSession session, String componentUuid) {
+ private BranchDto createMainBranch(DbSession session, String componentUuid) {
BranchDto branch = new BranchDto()
.setBranchType(BranchType.LONG)
.setUuid(componentUuid)
import static com.google.common.base.Preconditions.checkState;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
this.sorting.addDefault(IssueIndexDefinition.FIELD_ISSUE_KEY);
}
+ public SearchResponse search(IssueQuery query, SearchOptions options) {
+ SearchRequestBuilder requestBuilder = client.prepareSearch(INDEX_TYPE_ISSUE);
+
+ configureSorting(query, requestBuilder);
+ configurePagination(options, requestBuilder);
+ configureRouting(query, options, requestBuilder);
+
+ QueryBuilder esQuery = matchAllQuery();
+ BoolQueryBuilder esFilter = boolQuery();
+ Map<String, QueryBuilder> filters = createFilters(query);
+ for (QueryBuilder filter : filters.values()) {
+ if (filter != null) {
+ esFilter.must(filter);
+ }
+ }
+ if (esFilter.hasClauses()) {
+ requestBuilder.setQuery(boolQuery().must(esQuery).filter(esFilter));
+ } else {
+ requestBuilder.setQuery(esQuery);
+ }
+
+ configureStickyFacets(query, options, filters, esQuery, requestBuilder);
+ requestBuilder.setFetchSource(false);
+ return requestBuilder.get();
+ }
+
/**
* Optimization - do not send ES request to all shards when scope is restricted
* to a set of projects. Because project UUID is used for routing, the request
esSearch.setFrom(options.getOffset()).setSize(options.getLimit());
}
+ private Map<String, QueryBuilder> createFilters(IssueQuery query) {
+ Map<String, QueryBuilder> filters = new HashMap<>();
+ filters.put("__authorization", createAuthorizationFilter(query.checkAuthorization()));
+
+ // Issue is assigned Filter
+ if (BooleanUtils.isTrue(query.assigned())) {
+ filters.put(IS_ASSIGNED_FILTER, existsQuery(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID));
+ } else if (BooleanUtils.isFalse(query.assigned())) {
+ filters.put(IS_ASSIGNED_FILTER, boolQuery().mustNot(existsQuery(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID)));
+ }
+
+ // Issue is Resolved Filter
+ String isResolved = "__isResolved";
+ if (BooleanUtils.isTrue(query.resolved())) {
+ filters.put(isResolved, existsQuery(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION));
+ } else if (BooleanUtils.isFalse(query.resolved())) {
+ filters.put(isResolved, boolQuery().mustNot(existsQuery(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION)));
+ }
+
+ // Field Filters
+ filters.put(IssueIndexDefinition.FIELD_ISSUE_KEY, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_KEY, query.issueKeys()));
+ filters.put(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID, query.assignees()));
+ filters.put(IssueIndexDefinition.FIELD_ISSUE_LANGUAGE, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_LANGUAGE, query.languages()));
+ filters.put(IssueIndexDefinition.FIELD_ISSUE_TAGS, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_TAGS, query.tags()));
+ filters.put(IssueIndexDefinition.FIELD_ISSUE_TYPE, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_TYPE, query.types()));
+ filters.put(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION, query.resolutions()));
+ filters.put(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, query.authors()));
+ filters.put(IssueIndexDefinition.FIELD_ISSUE_RULE_ID, createTermsFilter(
+ IssueIndexDefinition.FIELD_ISSUE_RULE_ID,
+ query.rules().stream().map(RuleDefinitionDto::getId).collect(Collectors.toList())));
+ filters.put(IssueIndexDefinition.FIELD_ISSUE_SEVERITY, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_SEVERITY, query.severities()));
+ filters.put(IssueIndexDefinition.FIELD_ISSUE_STATUS, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_STATUS, query.statuses()));
+ filters.put(IssueIndexDefinition.FIELD_ISSUE_ORGANIZATION_UUID, createTermFilter(IssueIndexDefinition.FIELD_ISSUE_ORGANIZATION_UUID, query.organizationUuid()));
+
+ addComponentRelatedFilters(query, filters);
+
+ addDatesFilter(filters, query);
+ addCreatedAfterByProjectsFilter(filters, query);
+ return filters;
+ }
+
private static void addComponentRelatedFilters(IssueQuery query, Map<String, QueryBuilder> filters) {
- QueryBuilder viewFilter = createViewFilter(query.viewUuids());
+ addCommonComponentRelatedFilters(query, filters);
+ if (query.viewUuids().isEmpty()) {
+ addBranchComponentRelatedFilters(query, filters);
+ } else {
+ addViewRelatedFilters(query, filters);
+ }
+ }
+
+ private static void addCommonComponentRelatedFilters(IssueQuery query, Map<String, QueryBuilder> filters) {
QueryBuilder componentFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.componentUuids());
QueryBuilder projectFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, query.projectUuids());
QueryBuilder moduleRootFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_PATH, query.moduleRootUuids());
QueryBuilder moduleFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, query.moduleUuids());
QueryBuilder directoryFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, query.directories());
QueryBuilder fileFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.fileUuids());
- QueryBuilder branchFilter = createTermFilter(IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID, query.branchUuid());
- filters.put("__is_main_branch", createTermFilter(IssueIndexDefinition.FIELD_ISSUE_IS_MAIN_BRANCH, Boolean.toString(query.isMainBranch())));
if (BooleanUtils.isTrue(query.onComponentOnly())) {
filters.put(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, componentFilter);
} else {
- filters.put("__view", viewFilter);
filters.put(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, projectFilter);
- filters.put(IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID, branchFilter);
filters.put("__module", moduleRootFilter);
filters.put(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, moduleFilter);
filters.put(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, directoryFilter);
- if (fileFilter != null) {
- filters.put(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, fileFilter);
- } else {
- filters.put(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, componentFilter);
- }
+ filters.put(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, fileFilter != null ? fileFilter : componentFilter);
+ }
+ }
+
+ private static void addBranchComponentRelatedFilters(IssueQuery query, Map<String, QueryBuilder> filters) {
+ if (BooleanUtils.isTrue(query.onComponentOnly())) {
+ return;
+ }
+ QueryBuilder branchFilter = createTermFilter(IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID, query.branchUuid());
+ filters.put("__is_main_branch", createTermFilter(IssueIndexDefinition.FIELD_ISSUE_IS_MAIN_BRANCH, Boolean.toString(query.isMainBranch())));
+ filters.put(IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID, branchFilter);
+ }
+
+ private static void addViewRelatedFilters(IssueQuery query, Map<String, QueryBuilder> filters) {
+ if (BooleanUtils.isTrue(query.onComponentOnly())) {
+ return;
+ }
+ Collection<String> viewUuids = query.viewUuids();
+ String branchUuid = query.branchUuid();
+ boolean onApplicationBranch = branchUuid != null && !viewUuids.isEmpty();
+ if (onApplicationBranch) {
+ filters.put("__view", createViewFilter(singletonList(query.branchUuid())));
+ } else {
+ filters.put("__view", createViewFilter(viewUuids));
}
}
return value == null ? null : termQuery(field, value);
}
- public SearchResponse search(IssueQuery query, SearchOptions options) {
- SearchRequestBuilder requestBuilder = client.prepareSearch(INDEX_TYPE_ISSUE);
-
- configureSorting(query, requestBuilder);
- configurePagination(options, requestBuilder);
- configureRouting(query, options, requestBuilder);
-
- QueryBuilder esQuery = matchAllQuery();
- BoolQueryBuilder esFilter = boolQuery();
- Map<String, QueryBuilder> filters = createFilters(query);
- for (QueryBuilder filter : filters.values()) {
- if (filter != null) {
- esFilter.must(filter);
- }
- }
- if (esFilter.hasClauses()) {
- requestBuilder.setQuery(boolQuery().must(esQuery).filter(esFilter));
- } else {
- requestBuilder.setQuery(esQuery);
- }
-
- configureStickyFacets(query, options, filters, esQuery, requestBuilder);
- requestBuilder.setFetchSource(false);
- return requestBuilder.get();
- }
-
private void configureSorting(IssueQuery query, SearchRequestBuilder esRequest) {
createSortBuilders(query).forEach(esRequest::addSort);
}
return sorting.fillDefault();
}
- private Map<String, QueryBuilder> createFilters(IssueQuery query) {
- Map<String, QueryBuilder> filters = new HashMap<>();
- filters.put("__authorization", createAuthorizationFilter(query.checkAuthorization()));
-
- // Issue is assigned Filter
- if (BooleanUtils.isTrue(query.assigned())) {
- filters.put(IS_ASSIGNED_FILTER, existsQuery(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID));
- } else if (BooleanUtils.isFalse(query.assigned())) {
- filters.put(IS_ASSIGNED_FILTER, boolQuery().mustNot(existsQuery(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID)));
- }
-
- // Issue is Resolved Filter
- String isResolved = "__isResolved";
- if (BooleanUtils.isTrue(query.resolved())) {
- filters.put(isResolved, existsQuery(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION));
- } else if (BooleanUtils.isFalse(query.resolved())) {
- filters.put(isResolved, boolQuery().mustNot(existsQuery(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION)));
- }
-
- // Field Filters
- filters.put(IssueIndexDefinition.FIELD_ISSUE_KEY, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_KEY, query.issueKeys()));
- filters.put(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID, query.assignees()));
-
- addComponentRelatedFilters(query, filters);
-
- filters.put(IssueIndexDefinition.FIELD_ISSUE_LANGUAGE, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_LANGUAGE, query.languages()));
- filters.put(IssueIndexDefinition.FIELD_ISSUE_TAGS, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_TAGS, query.tags()));
- filters.put(IssueIndexDefinition.FIELD_ISSUE_TYPE, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_TYPE, query.types()));
- filters.put(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION, query.resolutions()));
- filters.put(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, query.authors()));
- filters.put(IssueIndexDefinition.FIELD_ISSUE_RULE_ID, createTermsFilter(
- IssueIndexDefinition.FIELD_ISSUE_RULE_ID,
- query.rules().stream().map(RuleDefinitionDto::getId).collect(Collectors.toList())));
- filters.put(IssueIndexDefinition.FIELD_ISSUE_SEVERITY, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_SEVERITY, query.severities()));
- filters.put(IssueIndexDefinition.FIELD_ISSUE_STATUS, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_STATUS, query.statuses()));
- filters.put(IssueIndexDefinition.FIELD_ISSUE_ORGANIZATION_UUID, createTermFilter(IssueIndexDefinition.FIELD_ISSUE_ORGANIZATION_UUID, query.organizationUuid()));
-
- addDatesFilter(filters, query);
- addCreatedAfterByProjectsFilter(filters, query);
- return filters;
- }
-
private QueryBuilder createAuthorizationFilter(boolean checkAuthorization) {
if (checkAuthorization) {
return authorizationTypeSupport.createQueryFilter();
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;
tester.newRequest().execute();
}
- public void fail_branch_does_not_exist() {
+ @Test
+ public void fail_if_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("project", project.getDbKey())
.setParam("branch", "branch1")
.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()
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.api.web.UserRole.USER;
import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
-import static org.sonar.db.component.BranchType.LONG;
import static org.sonar.db.component.BranchType.SHORT;
import static org.sonar.db.component.SnapshotTesting.newAnalysis;
import static org.sonar.test.JsonAssert.assertJson;
ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey("sonarqube"));
ComponentDto longLivingBranch = db.components()
- .insertProjectBranch(project, b -> b.setKey("feature/bar").setBranchType(LONG));
+ .insertProjectBranch(project, b -> b.setKey("feature/bar").setBranchType(org.sonar.db.component.BranchType.LONG));
db.getDbClient().snapshotDao().insert(db.getSession(),
newAnalysis(longLivingBranch).setLast(true).setCreatedAt(parseDateTime("2017-04-01T01:15:42+0100").getTime()));
db.measures().insertLiveMeasure(longLivingBranch, qualityGateStatus, m -> m.setData("OK"));
ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey("sonarqube"));
ComponentDto longLivingBranch = db.components()
- .insertProjectBranch(project, b -> b.setKey("feature/bar").setBranchType(LONG));
+ .insertProjectBranch(project, b -> b.setKey("feature/bar").setBranchType(org.sonar.db.component.BranchType.LONG));
db.getDbClient().snapshotDao().insert(db.getSession(),
newAnalysis(longLivingBranch).setLast(true).setCreatedAt(parseDateTime("2017-04-01T01:15:42+0100").getTime()));
db.measures().insertLiveMeasure(longLivingBranch, qualityGateStatus, m -> m.setData("OK"));
ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(USER, project);
ComponentDto longLivingBranch = db.components().insertProjectBranch(project,
- b -> b.setKey("long").setBranchType(LONG));
+ b -> b.setKey("long").setBranchType(org.sonar.db.component.BranchType.LONG));
ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
b -> b.setKey("short").setBranchType(SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
ComponentDto shortLivingBranchOnMaster = db.components().insertProjectBranch(project,
public void status_on_long_living_branch() {
ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(USER, project);
- ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(LONG));
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(org.sonar.db.component.BranchType.LONG));
db.measures().insertLiveMeasure(branch, qualityGateStatus, m -> m.setData("OK"));
ListWsResponse response = ws.newRequest()
public void status_on_short_living_branches() {
ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(USER, project);
- ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(LONG));
+ ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(org.sonar.db.component.BranchType.LONG));
ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
b -> b.setKey("short").setBranchType(SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
db.measures().insertLiveMeasure(shortLivingBranch, qualityGateStatus, m -> m.setData("OK"));
public void status_on_short_living_branch_with_no_issue() {
ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(USER, project);
- ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(LONG));
+ ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(org.sonar.db.component.BranchType.LONG));
db.components().insertProjectBranch(project, b -> b.setBranchType(SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
issueIndexer.indexOnStartup(emptySet());
permissionIndexerTester.allowOnlyAnyone(project);
ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(USER, project);
ComponentDto shortLivingBranch1 = db.components().insertProjectBranch(project, b -> b.setBranchType(SHORT).setMergeBranchUuid(project.uuid()));
- ComponentDto longLivingBranch2 = db.components().insertProjectBranch(project, b -> b.setBranchType(LONG));
+ ComponentDto longLivingBranch2 = db.components().insertProjectBranch(project, b -> b.setBranchType(org.sonar.db.component.BranchType.LONG));
ComponentDto shortLivingBranch2 = db.components().insertProjectBranch(project, b -> b.setBranchType(SHORT).setMergeBranchUuid(longLivingBranch2.uuid()));
db.getDbClient().snapshotDao().insert(db.getSession(),
newAnalysis(longLivingBranch2).setCreatedAt(lastAnalysisLongLivingBranch));
tuple(BranchType.SHORT, true, lastAnalysisShortLivingBranch));
}
+ @Test
+ public void application_branches() {
+ ComponentDto application = db.components().insertPrivateApplication(db.getDefaultOrganization());
+ db.components().insertProjectBranch(application, b -> b.setKey("feature/bar"));
+ db.components().insertProjectBranch(application, b -> b.setKey("feature/foo"));
+ userSession.logIn().addProjectPermission(USER, application);
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", application.getDbKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getBranchesList())
+ .extracting(Branch::getName, Branch::getType)
+ .containsExactlyInAnyOrder(
+ tuple("feature/foo", BranchType.LONG),
+ tuple("feature/bar", BranchType.LONG));
+ }
+
@Test
public void fail_when_using_branch_db_key() throws Exception {
OrganizationDto organization = db.organizations().insert();
userSession.logIn().addProjectPermission(USER, project);
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Invalid project key");
+ expectedException.expectMessage("Invalid project");
ws.newRequest()
.setParam("project", file.getDbKey())
}
@Test
- public void create_project_with_branch() {
+ public void create_project_with_deprecated_branch() {
ComponentDto project = underTest.create(db.getSession(),
NewComponent.newComponentBuilder()
.setKey(DEFAULT_PROJECT_KEY)
}
@Test
- public void persist_and_index_when_creating_view() {
+ public void create_view() {
NewComponent view = NewComponent.newComponentBuilder()
.setKey("view-key")
.setName("view-name")
}
@Test
- public void persist_and_index_when_creating_application() {
- NewComponent view = NewComponent.newComponentBuilder()
+ public void create_application() {
+ NewComponent application = NewComponent.newComponentBuilder()
.setKey("app-key")
.setName("app-name")
.setQualifier(APP)
.setOrganizationUuid(db.getDefaultOrganization().getUuid())
.build();
- ComponentDto returned = underTest.create(db.getSession(), view, null);
+ ComponentDto returned = underTest.create(db.getSession(), application, null);
ComponentDto loaded = db.getDbClient().componentDao().selectOrFailByUuid(db.getSession(), returned.uuid());
assertThat(loaded.getDbKey()).isEqualTo("app-key");
assertThat(loaded.qualifier()).isEqualTo("APP");
assertThat(projectIndexers.hasBeenCalled(loaded.uuid(), ProjectIndexer.Cause.PROJECT_CREATION)).isTrue();
Optional<BranchDto> branch = db.getDbClient().branchDao().selectByUuid(db.getSession(), returned.uuid());
- assertThat(branch).isNotPresent();
- }
-
- @Test
- public void create_application() {
- NewComponent view = NewComponent.newComponentBuilder()
- .setKey("app-key")
- .setName("app-name")
- .setQualifier(APP)
- .setOrganizationUuid(db.getDefaultOrganization().getUuid())
- .build();
-
- ComponentDto returned = underTest.create(db.getSession(), view, null);
-
- ComponentDto loaded = db.getDbClient().componentDao().selectByKey(db.getSession(), returned.getDbKey()).get();
- assertThat(loaded.getDbKey()).isEqualTo("app-key");
- assertThat(loaded.name()).isEqualTo("app-name");
- assertThat(loaded.qualifier()).isEqualTo("APP");
- assertThat(projectIndexers.hasBeenCalled(loaded.uuid(), ProjectIndexer.Cause.PROJECT_CREATION)).isTrue();
+ assertThat(branch).isPresent();
+ assertThat(branch.get().getKey()).isEqualTo(BranchDto.DEFAULT_MAIN_BRANCH_NAME);
+ assertThat(branch.get().getMergeBranchUuid()).isNull();
+ assertThat(branch.get().getBranchType()).isEqualTo(BranchType.LONG);
+ assertThat(branch.get().getUuid()).isEqualTo(returned.uuid());
+ assertThat(branch.get().getProjectUuid()).isEqualTo(returned.uuid());
}
@Test
import static org.assertj.core.api.Assertions.tuple;
import static org.junit.rules.ExpectedException.none;
import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
+import static org.sonar.api.resources.Qualifiers.APP;
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;
}
@Test
- public void filter_by_views() {
- OrganizationDto organizationDto = newOrganizationDto();
- ComponentDto project1 = newPrivateProjectDto(organizationDto);
- ComponentDto file1 = newFileDto(project1, null);
- ComponentDto project2 = newPrivateProjectDto(organizationDto);
- indexIssues(
- // Project1 has 2 issues (one on a file and one on the project itself)
- newDoc("I1", project1),
- newDoc("I2", file1),
- // Project2 has 1 issue
- newDoc("I3", project2));
+ public void filter_by_portfolios() {
+ ComponentDto portfolio1 = db.components().insertPrivateApplication(db.getDefaultOrganization());
+ ComponentDto portfolio2 = db.components().insertPrivateApplication(db.getDefaultOrganization());
+ ComponentDto project1 = db.components().insertPrivateProject();
+ ComponentDto file = db.components().insertComponent(newFileDto(project1));
+ ComponentDto project2 = db.components().insertPrivateProject();
- // The view1 is containing 2 issues from project1
- String view1 = "ABCD";
- indexView(view1, asList(project1.uuid()));
+ IssueDoc issueOnProject1 = newDoc(project1);
+ IssueDoc issueOnFile = newDoc(file);
+ IssueDoc issueOnProject2 = newDoc(project2);
- // The view2 is containing 1 issue from project2
- String view2 = "CDEF";
- indexView(view2, asList(project2.uuid()));
+ indexIssues(issueOnProject1, issueOnFile, issueOnProject2);
+ indexView(portfolio1.uuid(), singletonList(project1.uuid()));
+ indexView(portfolio2.uuid(), singletonList(project2.uuid()));
- assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(singletonList(view1)), "I1", "I2");
- assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(singletonList(view2)), "I3");
- assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(asList(view1, view2)), "I1", "I2", "I3");
+ assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(singletonList(portfolio1.uuid())), issueOnProject1.key(), issueOnFile.key());
+ assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(singletonList(portfolio2.uuid())), issueOnProject2.key());
+ assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(asList(portfolio1.uuid(), portfolio2.uuid())), issueOnProject1.key(), issueOnFile.key(), issueOnProject2.key());
+ assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(singletonList(portfolio1.uuid())).projectUuids(singletonList(project1.uuid())), issueOnProject1.key(),
+ issueOnFile.key());
+ assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(singletonList(portfolio1.uuid())).fileUuids(singletonList(file.uuid())), issueOnFile.key());
assertThatSearchReturnsEmpty(IssueQuery.builder().viewUuids(singletonList("unknown")));
}
@Test
- public void filter_by_views_not_having_projects() {
+ public void filter_by_portfolios_not_having_projects() {
OrganizationDto organizationDto = newOrganizationDto();
ComponentDto project1 = newPrivateProjectDto(organizationDto);
ComponentDto file1 = newFileDto(project1, null);
assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(singletonList(view1)));
}
- @Test
- public void filter_by_created_after_by_projects() {
- Date now = new Date();
- OrganizationDto organizationDto = newOrganizationDto();
- ComponentDto project1 = newPrivateProjectDto(organizationDto);
- IssueDoc project1Issue1 = newDoc(project1).setFuncCreationDate(addDays(now, -10));
- IssueDoc project1Issue2 = newDoc(project1).setFuncCreationDate(addDays(now, -20));
- ComponentDto project2 = newPrivateProjectDto(organizationDto);
- IssueDoc project2Issue1 = newDoc(project2).setFuncCreationDate(addDays(now, -15));
- IssueDoc project2Issue2 = newDoc(project2).setFuncCreationDate(addDays(now, -30));
- indexIssues(project1Issue1, project1Issue2, project2Issue1, project2Issue2);
-
- // Search for issues of project 1 having less than 15 days
- assertThatSearchReturnsOnly(IssueQuery.builder()
- .createdAfterByProjectUuids(ImmutableMap.of(project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -15), true))),
- project1Issue1.key());
-
- // Search for issues of project 1 having less than 14 days and project 2 having less then 25 days
- assertThatSearchReturnsOnly(IssueQuery.builder()
- .createdAfterByProjectUuids(ImmutableMap.of(
- project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -14), true),
- project2.uuid(), new IssueQuery.PeriodStart(addDays(now, -25), true))),
- project1Issue1.key(), project2Issue1.key());
-
- // Search for issues of project 1 having less than 30 days
- assertThatSearchReturnsOnly(IssueQuery.builder()
- .createdAfterByProjectUuids(ImmutableMap.of(
- project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -30), true))),
- project1Issue1.key(), project1Issue2.key());
-
- // Search for issues of project 1 and project 2 having less than 5 days
- assertThatSearchReturnsOnly(IssueQuery.builder()
- .createdAfterByProjectUuids(ImmutableMap.of(
- project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -5), true),
- project2.uuid(), new IssueQuery.PeriodStart(addDays(now, -5), true))));
- }
-
@Test
public void filter_one_issue_by_project_and_branch() {
ComponentDto project = db.components().insertPrivateProject();
assertThatSearchReturnsOnly(IssueQuery.builder(), projectIssue.key());
}
+ @Test
+ public void filter_by_application() {
+ ComponentDto application1 = db.components().insertPrivateApplication(db.getDefaultOrganization());
+ ComponentDto application2 = db.components().insertPrivateApplication(db.getDefaultOrganization());
+ ComponentDto project1 = db.components().insertPrivateProject();
+ ComponentDto file = db.components().insertComponent(newFileDto(project1));
+ ComponentDto project2 = db.components().insertPrivateProject();
+
+ IssueDoc issueOnProject1 = newDoc(project1);
+ IssueDoc issueOnFile = newDoc(file);
+ IssueDoc issueOnProject2 = newDoc(project2);
+
+ indexIssues(issueOnProject1, issueOnFile, issueOnProject2);
+ indexView(application1.uuid(), singletonList(project1.uuid()));
+ indexView(application2.uuid(), singletonList(project2.uuid()));
+
+ assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(singletonList(application1.uuid())), issueOnProject1.key(), issueOnFile.key());
+ assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(singletonList(application2.uuid())), issueOnProject2.key());
+ assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(asList(application1.uuid(), application2.uuid())), issueOnProject1.key(), issueOnFile.key(), issueOnProject2.key());
+ assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(singletonList(application1.uuid())).projectUuids(singletonList(project1.uuid())), issueOnProject1.key(),
+ issueOnFile.key());
+ assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(singletonList(application1.uuid())).fileUuids(singletonList(file.uuid())), issueOnFile.key());
+ assertThatSearchReturnsEmpty(IssueQuery.builder().viewUuids(singletonList("unknown")));
+ }
+
+ @Test
+ public void filter_by_application_and_branch() {
+ ComponentDto application = db.components().insertMainBranch(c -> c.setQualifier(APP));
+ ComponentDto branch1 = db.components().insertProjectBranch(application);
+ ComponentDto branch2 = db.components().insertProjectBranch(application);
+ ComponentDto project1 = db.components().insertPrivateProject();
+ ComponentDto file = db.components().insertComponent(newFileDto(project1));
+ ComponentDto project2 = db.components().insertPrivateProject();
+
+ IssueDoc issueOnProject1 = newDoc(project1);
+ IssueDoc issueOnFile = newDoc(file);
+ IssueDoc issueOnProject2 = newDoc(project2);
+ indexIssues(issueOnProject1, issueOnFile, issueOnProject2);
+
+ indexView(branch1.uuid(), singletonList(project1.uuid()));
+ indexView(branch2.uuid(), singletonList(project2.uuid()));
+
+ assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(singletonList(branch1.uuid())).branchUuid(branch1.uuid()).mainBranch(false), issueOnProject1.key(),
+ issueOnFile.key());
+ assertThatSearchReturnsOnly(
+ IssueQuery.builder().viewUuids(singletonList(branch1.uuid())).projectUuids(singletonList(project1.uuid())).branchUuid(branch1.uuid()).mainBranch(false),
+ issueOnProject1.key(), issueOnFile.key());
+ assertThatSearchReturnsOnly(IssueQuery.builder().viewUuids(singletonList(branch1.uuid())).fileUuids(singletonList(file.uuid())).branchUuid(branch1.uuid()).mainBranch(false),
+ issueOnFile.key());
+ assertThatSearchReturnsEmpty(IssueQuery.builder().branchUuid("unknown"));
+ }
+
+ @Test
+ public void filter_by_created_after_by_projects() {
+ Date now = new Date();
+ OrganizationDto organizationDto = newOrganizationDto();
+ ComponentDto project1 = newPrivateProjectDto(organizationDto);
+ IssueDoc project1Issue1 = newDoc(project1).setFuncCreationDate(addDays(now, -10));
+ IssueDoc project1Issue2 = newDoc(project1).setFuncCreationDate(addDays(now, -20));
+ ComponentDto project2 = newPrivateProjectDto(organizationDto);
+ IssueDoc project2Issue1 = newDoc(project2).setFuncCreationDate(addDays(now, -15));
+ IssueDoc project2Issue2 = newDoc(project2).setFuncCreationDate(addDays(now, -30));
+ indexIssues(project1Issue1, project1Issue2, project2Issue1, project2Issue2);
+
+ // Search for issues of project 1 having less than 15 days
+ assertThatSearchReturnsOnly(IssueQuery.builder()
+ .createdAfterByProjectUuids(ImmutableMap.of(project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -15), true))),
+ project1Issue1.key());
+
+ // Search for issues of project 1 having less than 14 days and project 2 having less then 25 days
+ assertThatSearchReturnsOnly(IssueQuery.builder()
+ .createdAfterByProjectUuids(ImmutableMap.of(
+ project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -14), true),
+ project2.uuid(), new IssueQuery.PeriodStart(addDays(now, -25), true))),
+ project1Issue1.key(), project2Issue1.key());
+
+ // Search for issues of project 1 having less than 30 days
+ assertThatSearchReturnsOnly(IssueQuery.builder()
+ .createdAfterByProjectUuids(ImmutableMap.of(
+ project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -30), true))),
+ project1Issue1.key(), project1Issue2.key());
+
+ // Search for issues of project 1 and project 2 having less than 5 days
+ assertThatSearchReturnsOnly(IssueQuery.builder()
+ .createdAfterByProjectUuids(ImmutableMap.of(
+ project1.uuid(), new IssueQuery.PeriodStart(addDays(now, -5), true),
+ project2.uuid(), new IssueQuery.PeriodStart(addDays(now, -5), true))));
+ }
+
@Test
public void filter_by_severities() {
ComponentDto project = newPrivateProjectDto(newOrganizationDto());
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.Durations;
import org.sonar.api.utils.System2;
-import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
+import static org.sonar.api.resources.Qualifiers.APP;
import static org.sonar.api.utils.DateUtils.addDays;
import static org.sonar.api.utils.DateUtils.parseDateTime;
+import static org.sonar.api.web.UserRole.USER;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
import static org.sonar.db.component.BranchType.PULL_REQUEST;
@Test
public void search_by_application_key() {
- ComponentDto project1 = db.components().insertPublicProject();
- ComponentDto project2 = db.components().insertPublicProject();
- ComponentDto application = db.components().insertApplication(db.getDefaultOrganization());
- db.components().insertComponents(newProjectCopy("PC1", project1, application));
- db.components().insertComponents(newProjectCopy("PC2", project2, application));
+ ComponentDto application = db.components().insertPrivateApplication(db.getDefaultOrganization());
+ ComponentDto project1 = db.components().insertPrivateProject();
+ ComponentDto project2 = db.components().insertPrivateProject();
+ db.components().insertComponents(newProjectCopy(project1, application));
+ db.components().insertComponents(newProjectCopy(project2, application));
RuleDefinitionDto rule = db.rules().insert();
IssueDto issue1 = db.issues().insert(rule, project1, project1);
IssueDto issue2 = db.issues().insert(rule, project2, project2);
allowAnyoneOnProjects(project1, project2, application);
+ userSession.addProjectPermission(USER, application);
indexIssuesAndViews();
SearchWsResponse result = ws.newRequest()
.containsExactlyInAnyOrder(issue1.getKey(), issue2.getKey());
}
+ @Test
+ public void search_by_application_key_and_branch() {
+ ComponentDto application = db.components().insertMainBranch(c -> c.setQualifier(APP));
+ ComponentDto applicationBranch = db.components().insertProjectBranch(application);
+ ComponentDto project1 = db.components().insertPrivateProject();
+ ComponentDto project2 = db.components().insertPrivateProject();
+ db.components().insertComponents(newProjectCopy(project1, applicationBranch));
+ db.components().insertComponents(newProjectCopy(project2, applicationBranch));
+ RuleDefinitionDto rule = db.rules().insert();
+ IssueDto issue1 = db.issues().insert(rule, project1, project1);
+ IssueDto issue2 = db.issues().insert(rule, project2, project2);
+ allowAnyoneOnProjects(project1, project2, application);
+ userSession.addProjectPermission(USER, application);
+ indexIssuesAndViews();
+
+ SearchWsResponse result = ws.newRequest()
+ .setParam(PARAM_COMPONENT_KEYS, applicationBranch.getKey())
+ .setParam(PARAM_BRANCH, applicationBranch.getBranch())
+ .executeProtobuf(SearchWsResponse.class);
+
+ assertThat(result.getIssuesList()).extracting(Issue::getKey)
+ .containsExactlyInAnyOrder(issue1.getKey(), issue2.getKey());
+ }
+
@Test
public void ignore_application_without_browse_permission() {
ComponentDto project = db.components().insertPublicProject();
public void search_by_branch() {
RuleDefinitionDto rule = db.rules().insert();
ComponentDto project = db.components().insertPrivateProject();
- userSession.addProjectPermission(UserRole.USER, project);
+ userSession.addProjectPermission(USER, project);
ComponentDto projectFile = db.components().insertComponent(newFileDto(project));
IssueDto projectIssue = db.issues().insertIssue(newIssue(rule, project, projectFile));
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(SHORT));
public void search_by_pull_request() {
RuleDefinitionDto rule = db.rules().insert();
ComponentDto project = db.components().insertPrivateProject();
- userSession.addProjectPermission(UserRole.USER, project);
+ userSession.addProjectPermission(USER, project);
ComponentDto projectFile = db.components().insertComponent(newFileDto(project));
IssueDto projectIssue = db.issues().insertIssue(newIssue(rule, project, projectFile));
ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(PULL_REQUEST));
public void search_using_main_branch_name() {
RuleDefinitionDto rule = db.rules().insert();
ComponentDto project = db.components().insertMainBranch();
- userSession.addProjectPermission(UserRole.USER, project);
+ userSession.addProjectPermission(USER, project);
ComponentDto projectFile = db.components().insertComponent(newFileDto(project));
IssueDto projectIssue = db.issues().insertIssue(newIssue(rule, project, projectFile));
allowAnyoneOnProjects(project);
public void does_not_return_branch_issues_when_using_db_key() {
RuleDefinitionDto rule = db.rules().insert();
ComponentDto project = db.components().insertPrivateProject();
- userSession.addProjectPermission(UserRole.USER, project);
+ userSession.addProjectPermission(USER, project);
ComponentDto projectFile = db.components().insertComponent(newFileDto(project));
IssueDto projectIssue = db.issues().insertIssue(newIssue(rule, project, projectFile));
ComponentDto branch = db.components().insertProjectBranch(project);
fetchBranches = (
component: Component
): Promise<{ branchLike?: BranchLike; branchLikes: BranchLike[] }> => {
+ const application = component.breadcrumbs.find(({ qualifier }) => qualifier === 'APP');
+ if (application) {
+ return getBranches(application.key).then(branchLikes => {
+ return {
+ branchLike: this.getCurrentBranchLike(branchLikes),
+ branchLikes
+ };
+ });
+ }
const project = component.breadcrumbs.find(({ qualifier }) => qualifier === 'TRK');
return project
? Promise.all([getBranches(project.key), getPullRequests(project.key)]).then(
import LockIcon from '../../components/icons-components/LockIcon';
import QualifierIcon from '../../components/icons-components/QualifierIcon';
import Rating from '../../components/ui/Rating';
+import BranchIcon from '../../components/icons-components/BranchIcon';
+import LongLivingBranchIcon from '../../components/icons-components/LongLivingBranchIcon';
+import PullRequestIcon from '../../components/icons-components/PullRequestIcon';
+import ActionsDropdown, { ActionsDropdownItem } from '../../components/controls/ActionsDropdown';
const exposeLibraries = () => {
const global = window as any;
global.SonarMeasures = measures;
global.SonarRequest = { ...request, throwGlobalError, addGlobalSuccessMessage };
global.SonarComponents = {
+ ActionsDropdown,
+ ActionsDropdownItem,
AlertErrorIcon,
AlertSuccessIcon,
AlertWarnIcon,
+ BranchIcon,
Button,
Checkbox,
CheckIcon,
Level,
ListFooter,
LockIcon,
+ LongLivingBranchIcon,
Modal,
+ PullRequestIcon,
QualifierIcon,
Rating,
ReloadButton,