diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2017-08-03 15:41:24 +0200 |
---|---|---|
committer | Janos Gyerik <janos.gyerik@sonarsource.com> | 2017-09-12 10:55:10 +0200 |
commit | e31a01aedd82182ca124964325ddf73e32c71f12 (patch) | |
tree | 0c0884578aba70551030460fb6ef255a446f1960 | |
parent | 49309a28357a3056c1a8f481af4c5daae37676f9 (diff) | |
download | sonarqube-e31a01aedd82182ca124964325ddf73e32c71f12.tar.gz sonarqube-e31a01aedd82182ca124964325ddf73e32c71f12.zip |
SONAR-9616 Handle branch in batch/issues
20 files changed, 299 insertions, 99 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDto.java index f3a71679f55..387d91d0ae1 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDto.java @@ -97,16 +97,18 @@ public class BranchDto { return uuid; } - public void setUuid(String s) { + public BranchDto setUuid(String s) { this.uuid = s; + return this; } public String getProjectUuid() { return projectUuid; } - public void setProjectUuid(String s) { + public BranchDto setProjectUuid(String s) { this.projectUuid = s; + return this; } public boolean isMain() { @@ -117,8 +119,9 @@ public class BranchDto { return keeType; } - public void setKeeType(BranchKeyType t) { + public BranchDto setKeeType(BranchKeyType t) { this.keeType = t; + return this; } /** @@ -142,10 +145,11 @@ public class BranchDto { this.kee = s; } - public void setKey(@Nullable String s) { + public BranchDto setKey(@Nullable String s) { checkArgument(s == null || s.length() <= KEE_MAX_LENGTH, "Maximum length of branch name or pull request id is %s: %s", KEE_MAX_LENGTH, s); checkArgument(!NULL_KEY.equals(s), "Branch name is not allowed: %s", s); setKee(convertKeyToDb(s)); + return this; } @Nullable @@ -153,8 +157,9 @@ public class BranchDto { return branchType; } - public void setBranchType(@Nullable BranchType b) { + public BranchDto setBranchType(@Nullable BranchType b) { this.branchType = b; + return this; } @Nullable @@ -162,8 +167,9 @@ public class BranchDto { return mergeBranchUuid; } - public void setMergeBranchUuid(@Nullable String s) { + public BranchDto setMergeBranchUuid(@Nullable String s) { this.mergeBranchUuid = s; + return this; } @Nullable @@ -171,9 +177,10 @@ public class BranchDto { return pullRequestTitle; } - public void setPullRequestTitle(@Nullable String s) { + public BranchDto setPullRequestTitle(@Nullable String s) { checkArgument(s == null || s.length() <= 4000, "Maximum length of pull request title is 4000: %s", s); this.pullRequestTitle = s; + return this; } @Override @@ -198,4 +205,5 @@ public class BranchDto { static String convertKeyFromDb(String s) { return NULL_KEY.equals(s) ? null : s; } + } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java index e5379b9ab91..f12e59a954c 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java @@ -214,6 +214,10 @@ public class ComponentDao implements Dao { return Optional.fromNullable(mapper(session).selectByKey(key)); } + public java.util.Optional<ComponentDto> selectByKeyAndBranch(DbSession session, String key, String branch) { + return java.util.Optional.ofNullable(mapper(session).selectByDbKey(ComponentDto.generateBranchKey(key, branch))); + } + public List<UuidWithProjectUuidDto> selectAllViewsAndSubViews(DbSession session) { return mapper(session).selectUuidsForQualifiers(Qualifiers.APP, Qualifiers.VIEW, Qualifiers.SUBVIEW); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java index 9e3b55062fa..9082e9710f0 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java @@ -31,12 +31,20 @@ import org.apache.commons.lang.builder.ToStringBuilder; import org.sonar.api.resources.Scopes; import static com.google.common.base.Preconditions.checkArgument; +import static java.lang.String.format; import static org.sonar.db.component.ComponentValidator.checkComponentKey; import static org.sonar.db.component.ComponentValidator.checkComponentName; import static org.sonar.db.component.DbTagsReader.readDbTags; public class ComponentDto { + /** + * Separator used to generate the key of the branch + */ + public static final String BRANCH_KEY_SEPARATOR = ":BRANCH:"; + + private static final Splitter BRANCH_KEY_SPLITTER = Splitter.on(BRANCH_KEY_SEPARATOR); + public static final String UUID_PATH_SEPARATOR = "."; public static final String UUID_PATH_OF_ROOT = UUID_PATH_SEPARATOR; private static final Splitter UUID_PATH_SPLITTER = Splitter.on(UUID_PATH_SEPARATOR).omitEmptyStrings(); @@ -108,7 +116,7 @@ public class ComponentDto { /** * On non-main branches only, {@link #uuid} of the main branch that represents - * the project ({@link #qualifier}="TRK"). + * the project ({@link #qualifier}="TRK").x * It is propagated to all the components of the branch. * * Value is null on the main-branch components and on other kinds of components @@ -216,6 +224,22 @@ public class ComponentDto { return this; } + /** + * The key to be displayed to user, doesn't contain information on branches + */ + public String getKey() { + List<String> split = BRANCH_KEY_SPLITTER.splitToList(kee); + return split.size() == 2 ? split.get(0) : kee; + } + + /** + * @return the key of the branch. It will be null on the main branch and when the component is not on a branch + */ + @CheckForNull + public String getBranch(){ + List<String> split = BRANCH_KEY_SPLITTER.splitToList(kee); + return split.size() == 2 ? split.get(1) : null; + } public String scope() { return scope; @@ -503,4 +527,9 @@ public class ComponentDto { copy.createdAt = createdAt; return copy; } + + // TODO Use it in branch plugin + public static String generateBranchKey(String componentKey, String branch) { + return format("%s%s%s", componentKey, BRANCH_KEY_SEPARATOR, branch); + } } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java index 53ebb044c5a..692948c085c 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java @@ -33,6 +33,12 @@ public interface ComponentMapper { @CheckForNull ComponentDto selectByKey(String key); + /** + * This method should be used to get a component by its key without filtering out branches + */ + @CheckForNull + ComponentDto selectByDbKey(String dbKey); + @CheckForNull ComponentDto selectById(long id); diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml index 47b464dd2dd..0409bbd1f62 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml @@ -37,6 +37,14 @@ p.kee=#{key,jdbcType=VARCHAR} </select> + <select id="selectByDbKey" parameterType="String" resultType="Component"> + SELECT + <include refid="componentColumns"/> + FROM projects p + where + p.kee=#{key,jdbcType=VARCHAR} + </select> + <select id="selectComponentsHavingSameKeyOrderedById" parameterType="String" resultType="Component"> select <include refid="componentColumns"/> diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java index 0f66805fdd4..e3f4e89f355 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java @@ -166,7 +166,8 @@ public class ComponentDaoTest { OrganizationDto organization = db.organizations().insert(); ComponentDto project = db.components().insertPrivateProject(organization); ComponentDto directory = db.components().insertComponent(newDirectory(project, "src")); - ComponentDto file = db.components().insertComponent(newFileDto(directory) + ComponentDto file = db.components().insertComponent(newFileDto(project, directory) + .setDbKey("org.struts:struts-core:src/org/struts/RequestContext.java") .setName("RequestContext.java") .setLongName("org.struts.RequestContext") .setLanguage("java") @@ -177,7 +178,7 @@ public class ComponentDaoTest { ComponentDto result = optional.get(); assertThat(result.getOrganizationUuid()).isEqualTo(organization.getUuid()); assertThat(result.uuid()).isEqualTo(file.uuid()); - assertThat(result.getDbKey()).isEqualTo(file.getDbKey()); + assertThat(result.getDbKey()).isEqualTo("org.struts:struts-core:src/org/struts/RequestContext.java"); assertThat(result.path()).isEqualTo("src/RequestContext.java"); assertThat(result.name()).isEqualTo("RequestContext.java"); assertThat(result.longName()).isEqualTo("org.struts.RequestContext"); @@ -190,6 +191,18 @@ public class ComponentDaoTest { } @Test + public void selectByKeyAndBranch() { + ComponentDto project = db.components().insertPrivateProject(); + ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch")); + ComponentDto file = db.components().insertComponent(newFileDto(branch)); + + assertThat(underTest.selectByKeyAndBranch(dbSession, project.getKey(), "my_branch").get().uuid()).isEqualTo(branch.uuid()); + assertThat(underTest.selectByKeyAndBranch(dbSession, file.getKey(), "my_branch").get().uuid()).isEqualTo(file.uuid()); + assertThat(underTest.selectByKeyAndBranch(dbSession, "unknown", "my_branch")).isNotPresent(); + assertThat(underTest.selectByKeyAndBranch(dbSession, file.getKey(), "unknown")).isNotPresent(); + } + + @Test public void selectOrFailByKey_fails_when_component_not_found() { db.components().insertPrivateProject(); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDbTester.java index 916fac22755..9723e0d689f 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDbTester.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDbTester.java @@ -22,6 +22,7 @@ package org.sonar.db.component; import java.util.Arrays; import java.util.function.Consumer; import javax.annotation.Nullable; +import org.sonar.core.util.Uuids; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; @@ -29,8 +30,12 @@ import org.sonar.db.organization.OrganizationDto; import static com.google.common.base.Preconditions.checkState; import static java.util.Arrays.asList; +import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; +import static org.sonar.db.component.BranchKeyType.BRANCH; +import static org.sonar.db.component.BranchType.LONG; import static org.sonar.db.component.ComponentTesting.newApplication; import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; +import static org.sonar.db.component.ComponentTesting.newProjectBranch; import static org.sonar.db.component.ComponentTesting.newPublicProjectDto; import static org.sonar.db.component.ComponentTesting.newSubView; import static org.sonar.db.component.ComponentTesting.newView; @@ -196,9 +201,19 @@ public class ComponentDbTester { db.commit(); } - public ComponentDto insertProjectBranch(ComponentDto project, String branchName) { - ComponentDto branch = ComponentTesting.newProjectBranch(project, branchName); + @SafeVarargs + public final ComponentDto insertProjectBranch(ComponentDto project, Consumer<BranchDto>... dtoPopulators) { + String uuid = Uuids.createFast(); + BranchDto branchDto = new BranchDto() + .setKey(randomAlphanumeric(255)) + .setUuid(uuid) + .setProjectUuid(project.uuid()) + .setKeeType(BRANCH) + .setBranchType(LONG); + Arrays.stream(dtoPopulators).forEach(dtoPopulator -> dtoPopulator.accept(branchDto)); + ComponentDto branch = newProjectBranch(project, branchDto); insertComponent(branch); + dbClient.branchDao().insert(dbSession, branchDto); db.commit(); return branch; } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDtoTest.java index b4e997d24bc..143f2abdf43 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDtoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDtoTest.java @@ -48,6 +48,7 @@ public class ComponentDtoTest { assertThat(componentDto.getId()).isEqualTo(1L); assertThat(componentDto.getDbKey()).isEqualTo("org.struts:struts-core:src/org/struts/RequestContext.java"); assertThat(componentDto.deprecatedKey()).isEqualTo("org.struts:struts-core:src/org/struts/RequestContext.java"); + assertThat(componentDto.getBranch()).isNull(); assertThat(componentDto.name()).isEqualTo("RequestContext.java"); assertThat(componentDto.longName()).isEqualTo("org.struts.RequestContext"); assertThat(componentDto.qualifier()).isEqualTo("FIL"); @@ -103,4 +104,15 @@ public class ComponentDtoTest { ComponentDto nonRoot = new ComponentDto().setUuidPath(".12.34.56."); assertThat(nonRoot.getUuidPathAsList()).containsExactly("12", "34", "56"); } + + @Test + public void test_getKey_and_getBranch() { + ComponentDto underTest = new ComponentDto().setDbKey("my_key:BRANCH:my_branch"); + assertThat(underTest.getKey()).isEqualTo("my_key"); + assertThat(underTest.getBranch()).isEqualTo("my_branch"); + + underTest = new ComponentDto().setDbKey("my_key"); + assertThat(underTest.getKey()).isEqualTo("my_key"); + assertThat(underTest.getBranch()).isNull(); + } } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentTesting.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentTesting.java index 08d0854f7b4..571b29cf47d 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentTesting.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentTesting.java @@ -44,7 +44,7 @@ public class ComponentTesting { String filename = "NAME_" + fileUuid; String path = directory != null ? directory.path() + "/" + filename : module.path() + "/" + filename; return newChildComponent(fileUuid, module, directory == null ? module : directory) - .setDbKey("KEY_" + fileUuid) + .setDbKey(generateKey("FILE_KEY_" + fileUuid, module)) .setName(filename) .setLongName(path) .setScope(Scopes.FILE) @@ -59,8 +59,9 @@ public class ComponentTesting { } public static ComponentDto newDirectory(ComponentDto module, String uuid, String path) { + String key = !path.equals("/") ? module.getKey() + ":" + path : module.getKey() + ":/"; return newChildComponent(uuid, module, module) - .setDbKey(!path.equals("/") ? module.getDbKey() + ":" + path : module.getDbKey() + ":/") + .setDbKey(generateKey(key, module)) .setName(path) .setLongName(path) .setPath(path) @@ -86,7 +87,7 @@ public class ComponentTesting { public static ComponentDto newModuleDto(String uuid, ComponentDto parentModuleOrProject) { return newChildComponent(uuid, parentModuleOrProject, parentModuleOrProject) .setModuleUuidPath(parentModuleOrProject.moduleUuidPath() + uuid + UUID_PATH_SEPARATOR) - .setDbKey("KEY_" + uuid) + .setDbKey(generateKey("MODULE_KEY_" + uuid, parentModuleOrProject)) .setName("NAME_" + uuid) .setLongName("LONG_NAME_" + uuid) .setPath("module") @@ -95,6 +96,11 @@ public class ComponentTesting { .setLanguage(null); } + private static String generateKey(String key, ComponentDto parentModuleOrProject) { + String branch = parentModuleOrProject.getBranch(); + return branch == null ? key : ComponentDto.generateBranchKey(key, branch); + } + public static ComponentDto newModuleDto(ComponentDto subProjectOrProject) { return newModuleDto(Uuids.createFast(), subProjectOrProject); } @@ -194,10 +200,11 @@ public class ComponentTesting { .setPrivate(moduleOrProject.isPrivate()); } - public static ComponentDto newProjectBranch(ComponentDto project, String branchName) { + public static ComponentDto newProjectBranch(ComponentDto project, BranchDto branchDto) { checkArgument(project.qualifier().equals(Qualifiers.PROJECT)); checkArgument(project.getMainBranchProjectUuid() == null); - String uuid = Uuids.createFast(); + String branchName = branchDto.getKey(); + String uuid = branchDto.getUuid(); return new ComponentDto() .setUuid(uuid) .setOrganizationUuid(project.getOrganizationUuid()) @@ -205,7 +212,8 @@ public class ComponentTesting { .setProjectUuid(uuid) .setModuleUuidPath(UUID_PATH_SEPARATOR + uuid + UUID_PATH_SEPARATOR) .setRootUuid(uuid) - .setDbKey(project.getDbKey() + ":BRANCH:" + branchName) + // name of the branch is not mandatory on the main branch + .setDbKey(branchName != null ? project.getDbKey() + ":BRANCH:" + branchName : project.getKey()) .setMainBranchProjectUuid(project.uuid()) .setName(project.name()) .setLongName(project.longName()) diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorTest.java index 321bbcbe8aa..5c475437ed3 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorTest.java @@ -264,8 +264,8 @@ public class ProjectMeasuresIndexerIteratorTest { SnapshotDto projectAnalysis = dbTester.components().insertProjectAndSnapshot(project); insertMeasure(project, projectAnalysis, metric, 10d); - ComponentDto branch = ComponentTesting.newProjectBranch(project, "feature/foo"); - SnapshotDto branchAnalysis = dbTester.components().insertProjectAndSnapshot(branch); + ComponentDto branch = dbTester.components().insertProjectBranch(project, b -> b.setKey("feature/foo")); + SnapshotDto branchAnalysis = dbTester.components().insertSnapshot(branch); insertMeasure(branch, branchAnalysis, metric, 20d); Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/batch/IssuesAction.java b/server/sonar-server/src/main/java/org/sonar/server/batch/IssuesAction.java index bfd19284a95..c1bb50c8612 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/batch/IssuesAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/batch/IssuesAction.java @@ -39,15 +39,18 @@ import org.sonar.server.component.ComponentFinder; import org.sonar.server.user.UserSession; import org.sonarqube.ws.MediaTypes; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Maps.newHashMap; import static java.lang.String.format; import static org.sonar.api.web.UserRole.USER; import static org.sonar.core.util.Protobuf.setNullable; +import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; public class IssuesAction implements BatchWsAction { private static final String PARAM_KEY = "key"; + private static final String PARAM_BRANCH = "branch"; private static final Splitter MODULE_PATH_SPLITTER = Splitter.on('.').trimResults().omitEmptyStrings(); private final DbClient dbClient; @@ -74,15 +77,19 @@ public class IssuesAction implements BatchWsAction { .setRequired(true) .setDescription("Project, module or file key") .setExampleValue(KEY_PROJECT_EXAMPLE_001); + + action + .createParam(PARAM_BRANCH) + .setSince("6.6") + .setDescription("Branch key") + .setExampleValue(KEY_BRANCH_EXAMPLE_001); } @Override public void handle(Request request, Response response) throws Exception { try (DbSession dbSession = dbClient.openSession(false)) { - String componentKey = request.mandatoryParam(PARAM_KEY); - ComponentDto component = componentFinder.getByKey(dbSession, componentKey); + ComponentDto component = loadComponent(dbSession, request); userSession.checkComponentPermission(USER, component); - Map<String, String> keysByUUid = keysByUUid(dbSession, component); ScannerInput.ServerIssue.Builder responseBuilder = ScannerInput.ServerIssue.newBuilder(); @@ -108,7 +115,7 @@ public class IssuesAction implements BatchWsAction { } private static void handleIssue(IssueDto issue, ScannerInput.ServerIssue.Builder issueBuilder, - Map<String, String> keysByUUid, OutputStream out) { + Map<String, String> keysByUUid, OutputStream out) { issueBuilder.setKey(issue.getKey()); String moduleUuid = extractModuleUuid(issue); issueBuilder.setModuleKey(keysByUUid.get(moduleUuid)); @@ -135,7 +142,7 @@ public class IssuesAction implements BatchWsAction { private static String extractModuleUuid(IssueDto issue) { List<String> split = MODULE_PATH_SPLITTER.splitToList(issue.getModuleUuidPath()); - return split.get(split.size()-1); + return split.get(split.size() - 1); } private Map<String, String> keysByUUid(DbSession session, ComponentDto component) { @@ -143,16 +150,23 @@ public class IssuesAction implements BatchWsAction { if (Scopes.PROJECT.equals(component.scope())) { List<ComponentDto> modulesTree = dbClient.componentDao().selectDescendantModules(session, component.uuid()); for (ComponentDto componentDto : modulesTree) { - keysByUUid.put(componentDto.uuid(), componentDto.getDbKey()); + keysByUUid.put(componentDto.uuid(), componentDto.getKey()); } } else { String moduleUuid = component.moduleUuid(); - if (moduleUuid == null) { - throw new IllegalArgumentException(String.format("The component '%s' has no module uuid", component.uuid())); - } + checkArgument(moduleUuid != null, "The component '%s' has no module uuid", component.uuid()); ComponentDto module = dbClient.componentDao().selectOrFailByUuid(session, moduleUuid); - keysByUUid.put(module.uuid(), module.getDbKey()); + keysByUUid.put(module.uuid(), module.getKey()); } return keysByUUid; } + + private ComponentDto loadComponent(DbSession dbSession, Request request) { + String componentKey = request.mandatoryParam(PARAM_KEY); + String branch = request.param(PARAM_BRANCH); + if (branch != null) { + return componentFinder.getByKeyAndBranch(dbSession, componentKey, branch); + } + return componentFinder.getByKey(dbSession, componentKey); + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java index 64200549f4a..ec479bf2e2e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java @@ -143,6 +143,10 @@ public class ComponentFinder { return checkFoundWithOptional(organizationDto, "Organization with uuid '%s' not found", organizationUuid); } + public ComponentDto getByKeyAndBranch(DbSession dbSession, String key, String branch) { + return checkFoundWithOptional(dbClient.componentDao().selectByKeyAndBranch(dbSession, key, branch), "Component '%s' on branch '%s' not found", key, branch); + } + public enum ParamNames { PROJECT_ID_AND_KEY("projectId", "projectKey"), PROJECT_UUID_AND_KEY("projectUuid", "projectKey"), diff --git a/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java b/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java index 93c1513a3e8..fc4a971fee9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java @@ -29,6 +29,8 @@ public class KeyExamples { public static final String KEY_ORG_EXAMPLE_001 = "my-org"; public static final String KEY_ORG_EXAMPLE_002 = "foo-company"; + public static final String KEY_BRANCH_EXAMPLE_001 = "feature/my_branch"; + private KeyExamples() { // prevent instantiation } diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java index d60c60980ac..582f7ea54ee 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java @@ -20,6 +20,8 @@ package org.sonar.server.batch; import java.io.IOException; +import javax.annotation.Nullable; +import org.assertj.core.groups.Tuple; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -37,9 +39,11 @@ import org.sonar.server.component.TestComponentFinder; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.TestResponse; import org.sonar.server.ws.WsActionTester; +import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; import static org.sonar.api.rules.RuleType.BUG; @@ -66,25 +70,24 @@ public class IssuesActionTest { ComponentDto project = db.components().insertPrivateProject(); ComponentDto module = db.components().insertComponent(newModuleDto(project)); ComponentDto file = db.components().insertComponent(newFileDto(module, null).setPath(null)); - IssueDto issue = db.issues().insert(rule, project, file, i -> - i.setSeverity("BLOCKER") - // non-null fields - .setStatus("OPEN") - .setType(BUG) - .setManualSeverity(true) - - // all the nullable fields - .setResolution(null) - .setMessage(null) - .setLine(null) - .setChecksum(null) - .setAssignee(null)); + IssueDto issue = db.issues().insert(rule, project, file, i -> i.setSeverity("BLOCKER") + // non-null fields + .setStatus("OPEN") + .setType(BUG) + .setManualSeverity(true) + + // all the nullable fields + .setResolution(null) + .setMessage(null) + .setLine(null) + .setChecksum(null) + .setAssignee(null)); addPermissionTo(project); - ServerIssue serverIssue = call(project.getDbKey()); + ServerIssue serverIssue = call(project.getKey()); assertThat(serverIssue.getKey()).isEqualTo(issue.getKey()); - assertThat(serverIssue.getModuleKey()).isEqualTo(module.getDbKey()); + assertThat(serverIssue.getModuleKey()).isEqualTo(module.getKey()); assertThat(serverIssue.getRuleRepository()).isEqualTo(rule.getRepositoryKey()); assertThat(serverIssue.getRuleKey()).isEqualTo(rule.getRuleKey()); assertThat(serverIssue.getStatus()).isEqualTo("OPEN"); @@ -106,22 +109,21 @@ public class IssuesActionTest { ComponentDto project = db.components().insertPrivateProject(); ComponentDto module = db.components().insertComponent(newModuleDto(project)); ComponentDto file = db.components().insertComponent(newFileDto(module, null)); - IssueDto issue = db.issues().insert(rule, project, file, i -> - i.setSeverity("BLOCKER") - .setStatus("OPEN") - .setType(BUG) - .setManualSeverity(true) - .setResolution("FIXED") - .setMessage("the message") - .setLine(10) - .setChecksum("ABC") - .setAssignee("foo")); + IssueDto issue = db.issues().insert(rule, project, file, i -> i.setSeverity("BLOCKER") + .setStatus("OPEN") + .setType(BUG) + .setManualSeverity(true) + .setResolution("FIXED") + .setMessage("the message") + .setLine(10) + .setChecksum("ABC") + .setAssignee("foo")); addPermissionTo(project); - ServerIssue serverIssue = call(project.getDbKey()); + ServerIssue serverIssue = call(project.getKey()); assertThat(serverIssue.getKey()).isEqualTo(issue.getKey()); - assertThat(serverIssue.getModuleKey()).isEqualTo(module.getDbKey()); + assertThat(serverIssue.getModuleKey()).isEqualTo(module.getKey()); assertThat(serverIssue.getRuleRepository()).isEqualTo(rule.getRepositoryKey()); assertThat(serverIssue.getRuleKey()).isEqualTo(rule.getRuleKey()); assertThat(serverIssue.getStatus()).isEqualTo("OPEN"); @@ -147,13 +149,13 @@ public class IssuesActionTest { IssueDto issueOnProject = db.issues().insert(rule, project, project, i -> i.setKee("ON_PROJECT")); addPermissionTo(project); - try (CloseableIterator<ServerIssue> result = callStream(project.getDbKey())) { + try (CloseableIterator<ServerIssue> result = callStream(project.getKey(), null)) { assertThat(result) .extracting(ServerIssue::getKey, ServerIssue::getModuleKey) .containsExactlyInAnyOrder( - tuple(issueOnFile.getKey(), module.getDbKey()), - tuple(issueOnModule.getKey(), module.getDbKey()), - tuple(issueOnProject.getKey(), project.getDbKey())); + tuple(issueOnFile.getKey(), module.getKey()), + tuple(issueOnModule.getKey(), module.getKey()), + tuple(issueOnProject.getKey(), project.getKey())); } } @@ -168,12 +170,12 @@ public class IssuesActionTest { IssueDto issueOnProject = db.issues().insert(rule, project, project, i -> i.setKee("ON_PROJECT")); addPermissionTo(project); - try (CloseableIterator<ServerIssue> result = callStream(module.getDbKey())) { + try (CloseableIterator<ServerIssue> result = callStream(module.getKey(), null)) { assertThat(result) .extracting(ServerIssue::getKey, ServerIssue::getModuleKey) .containsExactlyInAnyOrder( - tuple(issueOnFile.getKey(), module.getDbKey()), - tuple(issueOnModule.getKey(), module.getDbKey())); + tuple(issueOnFile.getKey(), module.getKey()), + tuple(issueOnModule.getKey(), module.getKey())); } } @@ -188,15 +190,50 @@ public class IssuesActionTest { IssueDto issueOnProject = db.issues().insert(rule, project, project); addPermissionTo(project); - try (CloseableIterator<ServerIssue> result = callStream(file.getDbKey())) { + try (CloseableIterator<ServerIssue> result = callStream(file.getKey(), null)) { assertThat(result) .extracting(ServerIssue::getKey, ServerIssue::getModuleKey) .containsExactlyInAnyOrder( - tuple(issueOnFile.getKey(), module.getDbKey())); + tuple(issueOnFile.getKey(), module.getKey())); } } @Test + public void return_issues_by_project_and_branch() { + RuleDefinitionDto rule = db.rules().insert(); + ComponentDto project = db.components().insertPrivateProject(); + addPermissionTo(project); + ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch")); + ComponentDto file = db.components().insertComponent(newFileDto(branch)); + IssueDto issueOnFile = db.issues().insert(rule, branch, file); + IssueDto issueOnBranch = db.issues().insert(rule, branch, branch); + + assertResult(project.getKey(), "my_branch", + tuple(issueOnFile.getKey(), branch.getKey()), + tuple(issueOnBranch.getKey(), branch.getKey())); + } + + @Test + public void return_issues_by_module_and_branch() { + RuleDefinitionDto rule = db.rules().insert(); + ComponentDto project = db.components().insertPrivateProject(); + addPermissionTo(project); + ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch")); + ComponentDto module = db.components().insertComponent(newModuleDto(branch)); + ComponentDto subModule = db.components().insertComponent(newModuleDto(module)); + ComponentDto file = db.components().insertComponent(newFileDto(subModule)); + IssueDto issueOnFile = db.issues().insert(rule, branch, file); + IssueDto issueOnSubModule = db.issues().insert(rule, branch, subModule); + IssueDto issueOnModule = db.issues().insert(rule, branch, module); + + assertResult(module.getKey(), "my_branch", + tuple(issueOnFile.getKey(), subModule.getKey()), + tuple(issueOnSubModule.getKey(), subModule.getKey()), + tuple(issueOnModule.getKey(), module.getKey()) + ); + } + + @Test public void fail_if_requested_component_is_a_directory() throws IOException { ComponentDto project = db.components().insertPrivateProject(); ComponentDto directory = db.components().insertComponent(newDirectory(project, "src/main/java")); @@ -205,7 +242,7 @@ public class IssuesActionTest { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Component of scope 'DIR' is not allowed"); - call(directory.getDbKey()); + call(directory.getKey()); } @Test @@ -217,11 +254,11 @@ public class IssuesActionTest { IssueDto issue = db.issues().insert(rule, project, file); addPermissionTo(project); - try (CloseableIterator<ServerIssue> result = callStream(project.getDbKey())) { + try (CloseableIterator<ServerIssue> result = callStream(project.getKey(), null)) { // Module key of removed file should be returned assertThat(result) .extracting(ServerIssue::getKey, ServerIssue::getModuleKey) - .containsExactly(tuple(issue.getKey(), module.getDbKey())); + .containsExactly(tuple(issue.getKey(), module.getKey())); } } @@ -232,7 +269,7 @@ public class IssuesActionTest { expectedException.expect(ForbiddenException.class); - tester.newRequest().setParam("key", file.getDbKey()).execute(); + tester.newRequest().setParam("key", file.getKey()).execute(); } @Test @@ -243,6 +280,21 @@ public class IssuesActionTest { tester.newRequest().setParam("key", "does_not_exist").execute(); } + @Test + public void fail_if_branch_does_not_exist() { + ComponentDto project = db.components().insertPrivateProject(); + db.components().insertProjectBranch(project, b -> b.setKey("my_branch")); + addPermissionTo(project); + + expectedException.expect(NotFoundException.class); + expectedException.expectMessage(format("Component '%s' on branch 'does_not_exist' not found", project.getKey())); + + tester.newRequest() + .setParam("key",project.getKey()) + .setParam("branch", "does_not_exist") + .execute(); + } + private void addPermissionTo(ComponentDto project) { userSessionRule.addProjectPermission(UserRole.USER, project); } @@ -252,8 +304,20 @@ public class IssuesActionTest { return ServerIssue.parseDelimitedFrom(response.getInputStream()); } - private CloseableIterator<ServerIssue> callStream(String componentKey) { - TestResponse response = tester.newRequest().setParam("key", componentKey).execute(); - return Protobuf.readStream(response.getInputStream(), ServerIssue.parser()); + private CloseableIterator<ServerIssue> callStream(String componentKey, @Nullable String branch) { + TestRequest request = tester.newRequest() + .setParam("key", componentKey); + if (branch != null) { + request.setParam("branch", branch); + } + return Protobuf.readStream(request.execute().getInputStream(), ServerIssue.parser()); + } + + private void assertResult(String componentKey, String branch, Tuple... tuples) { + try (CloseableIterator<ServerIssue> result = callStream(componentKey, branch)) { + assertThat(result) + .extracting(ServerIssue::getKey, ServerIssue::getModuleKey) + .containsExactlyInAnyOrder(tuples); + } } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentFinderTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentFinderTest.java index fcccc22fb60..c6dcd86eeaa 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentFinderTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentFinderTest.java @@ -28,8 +28,11 @@ import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; import org.sonar.server.exceptions.NotFoundException; +import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.db.component.ComponentTesting.newDirectory; import static org.sonar.db.component.ComponentTesting.newFileDto; +import static org.sonar.db.component.ComponentTesting.newModuleDto; import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; import static org.sonar.server.component.ComponentFinder.ParamNames.ID_AND_KEY; @@ -108,7 +111,6 @@ public class ComponentFinderTest { ComponentDto project = db.components().insertComponent(newPrivateProjectDto(db.getDefaultOrganization())); db.components().insertComponent(newFileDto(project).setDbKey("file-key").setEnabled(false)); - expectedException.expect(NotFoundException.class); expectedException.expectMessage("Component key 'file-key' not found"); @@ -132,4 +134,30 @@ public class ComponentFinderTest { assertThat(component.getDbKey()).isEqualTo("project-key"); } + + @Test + public void get_by_key_and_branch() { + ComponentDto project = db.components().insertPrivateProject(); + ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch")); + ComponentDto module = db.components().insertComponent(newModuleDto(branch)); + ComponentDto directory = db.components().insertComponent(newDirectory(module, "scr")); + ComponentDto file = db.components().insertComponent(newFileDto(module)); + + assertThat(underTest.getByKeyAndBranch(dbSession, project.getKey(), "my_branch").uuid()).isEqualTo(branch.uuid()); + assertThat(underTest.getByKeyAndBranch(dbSession, module.getKey(), "my_branch").uuid()).isEqualTo(module.uuid()); + assertThat(underTest.getByKeyAndBranch(dbSession, file.getKey(), "my_branch").uuid()).isEqualTo(file.uuid()); + assertThat(underTest.getByKeyAndBranch(dbSession, directory.getKey(), "my_branch").uuid()).isEqualTo(directory.uuid()); + } + + @Test + public void fail_to_get_by_key_and_branch_when_branch_does_not_exist() { + ComponentDto project = db.components().insertPrivateProject(); + ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch")); + ComponentDto file = db.components().insertComponent(newFileDto(branch)); + + expectedException.expect(NotFoundException.class); + expectedException.expectMessage(format("Component '%s' on branch 'other_branch' not found", file.getKey())); + + underTest.getByKeyAndBranch(dbSession, file.getKey(), "other_branch"); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexerTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexerTest.java index 47549914406..74de66dae6b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/index/ComponentIndexerTest.java @@ -86,7 +86,7 @@ public class ComponentIndexerTest { @Test public void indexOnStartup_does_not_index_non_main_branches() { ComponentDto project = db.components().insertPrivateProject(); - ComponentDto branch = db.components().insertProjectBranch(project, "feature/foo"); + ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("feature/foo")); underTest.indexOnStartup(emptySet()); @@ -133,7 +133,7 @@ public class ComponentIndexerTest { @Test public void indexOnAnalysis_does_not_index_non_main_branches() { ComponentDto project = db.components().insertPrivateProject(); - ComponentDto branch = db.components().insertProjectBranch(project, "feature/foo"); + ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("feature/foo")); underTest.indexOnAnalysis(branch.uuid()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java index dd42cc7e85e..5d9e3245848 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java @@ -193,7 +193,6 @@ public class IssueIndexerTest { assertThatEsQueueTableHasSize(0); } - @Test public void index_is_not_updated_when_creating_project() { // it's impossible to already have an issue on a project @@ -452,7 +451,7 @@ public class IssueIndexerTest { public void index_issue_in_non_main_branch() { RuleDefinitionDto rule = db.rules().insert(); ComponentDto project = db.components().insertPrivateProject(organization); - ComponentDto branch = db.components().insertProjectBranch(project, "feature/foo"); + ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("feature/foo")); ComponentDto dir = db.components().insertComponent(ComponentTesting.newDirectory(branch, "src/main/java/foo")); ComponentDto file = db.components().insertComponent(newFileDto(branch, dir, "F1")); IssueDto issue = db.issues().insertIssue(IssueTesting.newIssue(rule, branch, file)); diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexerTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexerTest.java index e8f63c4a87c..7f5d6ca4a9b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexerTest.java @@ -96,7 +96,7 @@ public class ProjectMeasuresIndexerTest { @Test public void indexOnStartup_ignores_non_main_branches() { ComponentDto project = db.components().insertPrivateProject(); - ComponentDto branch = db.components().insertProjectBranch(project, "feature/foo"); + ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("feature/foo")); underTest.indexOnStartup(emptySet()); @@ -202,7 +202,7 @@ public class ProjectMeasuresIndexerTest { @Test public void non_main_branches_are_not_indexed_during_analysis() { ComponentDto project = db.components().insertPrivateProject(); - ComponentDto branch = db.components().insertProjectBranch(project, "feature/foo"); + ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("feature/foo")); underTest.indexOnAnalysis(branch.uuid()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/ListActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/ListActionTest.java index 53f7a58cf60..cf0207382d8 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/ListActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/ListActionTest.java @@ -27,9 +27,6 @@ import org.sonar.api.utils.System2; import org.sonar.api.web.UserRole; import org.sonar.db.DbTester; import org.sonar.db.RowNotFoundException; -import org.sonar.db.component.BranchDto; -import org.sonar.db.component.BranchKeyType; -import org.sonar.db.component.BranchType; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; import org.sonar.server.tester.UserSessionRule; @@ -111,8 +108,8 @@ public class ListActionTest { @Test public void test_project_with_branches() { ComponentDto project = db.components().insertPrivateProject(); - insertBranch(project, "feature/bar"); - insertBranch(project, "feature/foo"); + 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 = tester.newRequest() @@ -128,8 +125,8 @@ public class ListActionTest { @Test public void test_example() { ComponentDto project = db.components().insertPrivateProject(); - insertBranch(project, "feature/bar"); - insertBranch(project, "feature/foo"); + db.components().insertProjectBranch(project, b -> b.setKey("feature/bar")); + db.components().insertProjectBranch(project, b -> b.setKey("feature/foo")); userSession.logIn().addProjectPermission(UserRole.USER, project); String json = tester.newRequest() @@ -140,15 +137,4 @@ public class ListActionTest { assertJson(json).isSimilarTo(tester.getDef().responseExampleAsString()); } - private void insertBranch(ComponentDto project, String branchName) { - ComponentDto branch = db.components().insertProjectBranch(project, branchName); - BranchDto branchDto = new BranchDto(); - branchDto.setUuid(branch.uuid()); - branchDto.setProjectUuid(project.uuid()); - branchDto.setBranchType(BranchType.LONG); - branchDto.setKeeType(BranchKeyType.BRANCH); - branchDto.setKey(branchName); - db.getDbClient().branchDao().insert(db.getSession(), branchDto); - db.commit(); - } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java index 83b0fb4cdf7..b4fff65d1c7 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java @@ -518,7 +518,7 @@ public class ServerUserSessionTest { @Test public void hasComponentPermission_on_branch_checks_permissions_of_its_project() { - ComponentDto branch = db.components().insertProjectBranch(privateProject, "feature/foo"); + ComponentDto branch = db.components().insertProjectBranch(privateProject, b -> b.setKey("feature/foo")); ComponentDto fileInBranch = db.components().insertComponent(newChildComponent("fileUuid", branch, branch)); // permissions are defined on the project, not on the branch |