From 3b9222096f56858f89867f2739a0807e68c83ae8 Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Thu, 22 Mar 2018 15:02:11 +0100 Subject: [PATCH] SONAR-10489 Support cross file locations in WS --- .../server/issue/ws/SearchResponseFormat.java | 21 ++-- .../server/issue/ws/SearchActionTest.java | 116 ++++++++++++++---- sonar-ws/src/main/protobuf/ws-issues.proto | 1 + 3 files changed, 106 insertions(+), 32 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java index a59cd7f5eb2..ff6dea2e454 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java @@ -19,7 +19,6 @@ */ package org.sonar.server.issue.ws; -import com.google.common.base.Strings; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -186,14 +185,14 @@ public class SearchResponseFormat { } setNullable(dto.getLine(), issueBuilder::setLine); setNullable(emptyToNull(dto.getChecksum()), issueBuilder::setHash); - completeIssueLocations(dto, issueBuilder); + completeIssueLocations(dto, issueBuilder, data); issueBuilder.setAuthor(nullToEmpty(dto.getAuthorLogin())); setNullable(dto.getIssueCreationDate(), issueBuilder::setCreationDate, DateUtils::formatDateTime); setNullable(dto.getIssueUpdateDate(), issueBuilder::setUpdateDate, DateUtils::formatDateTime); setNullable(dto.getIssueCloseDate(), issueBuilder::setCloseDate, DateUtils::formatDateTime); } - private static void completeIssueLocations(IssueDto dto, Issue.Builder issueBuilder) { + private static void completeIssueLocations(IssueDto dto, Issue.Builder issueBuilder, SearchResponseData data) { DbIssues.Locations locations = dto.parseLocations(); if (locations == null) { return; @@ -205,13 +204,13 @@ public class SearchResponseFormat { for (DbIssues.Flow flow : locations.getFlowList()) { Flow.Builder targetFlow = Flow.newBuilder(); for (DbIssues.Location flowLocation : flow.getLocationList()) { - targetFlow.addLocations(convertLocation(flowLocation)); + targetFlow.addLocations(convertLocation(issueBuilder, flowLocation, data)); } issueBuilder.addFlows(targetFlow); } } - private static Location convertLocation(DbIssues.Location source) { + private static Location convertLocation(Issue.Builder issueBuilder, DbIssues.Location source, SearchResponseData data) { Location.Builder target = Location.newBuilder(); if (source.hasMsg()) { target.setMsg(source.getMsg()); @@ -221,6 +220,11 @@ public class SearchResponseFormat { Common.TextRange.Builder targetRange = convertTextRange(sourceRange); target.setTextRange(targetRange); } + if (source.hasComponentId()) { + setNullable(data.getComponentByUuid(source.getComponentId()), c -> target.setComponent(c.getKey())); + } else { + target.setComponent(issueBuilder.getComponent()); + } return target.build(); } @@ -311,12 +315,7 @@ public class SearchResponseFormat { .setEnabled(dto.isEnabled()); setNullable(dto.getBranch(), builder::setBranch); setNullable(dto.getPullRequest(), builder::setPullRequest); - String path = dto.path(); - // path is not applicable to the components that are not files. - // Value must not be "" in this case. - if (!Strings.isNullOrEmpty(path)) { - builder.setPath(path); - } + setNullable(emptyToNull(dto.path()), builder::setPath); result.add(builder.build()); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java index b8283b57c8b..ab8291ecce7 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java @@ -22,6 +22,7 @@ package org.sonar.server.issue.ws; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import java.time.Clock; +import java.util.Arrays; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -46,6 +47,8 @@ import org.sonar.db.organization.OrganizationDao; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.organization.OrganizationTesting; import org.sonar.db.permission.GroupPermissionDto; +import org.sonar.db.protobuf.DbCommons; +import org.sonar.db.protobuf.DbIssues; import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleTesting; import org.sonar.db.user.UserDto; @@ -68,10 +71,13 @@ import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestResponse; import org.sonar.server.ws.WsActionTester; import org.sonar.server.ws.WsResponseCommonFormat; +import org.sonarqube.ws.Issues; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.groups.Tuple.tuple; import static org.sonar.api.web.UserRole.ISSUE_ADMIN; +import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BRANCH; import static org.sonarqube.ws.client.issue.IssuesWsParameters.DEPRECATED_FACET_MODE_DEBT; import static org.sonarqube.ws.client.issue.IssuesWsParameters.FACET_MODE_EFFORT; @@ -155,7 +161,7 @@ public class SearchActionTest { "Portfolios are not supported. If this parameter is set, 'projects' must not be set."); } - //SONAR-10217 + // SONAR-10217 @Test public void empty_search_with_unknown_branch() { TestResponse result = ws.newRequest() @@ -184,7 +190,7 @@ public class SearchActionTest { ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization2, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); IssueDto issue = IssueTesting.newDto(newRule(), file, project) .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2") .setEffort(10L) @@ -206,6 +212,74 @@ public class SearchActionTest { ws.newRequest().execute().assertJson(this.getClass(), "response_contains_all_fields_except_additional_fields.json"); } + @Test + public void issue_with_cross_file_locations() { + UserDto simon = db.users().insertUser(); + UserDto fabrice = db.users().insertUser(); + + ComponentDto project = db.components().insertPublicProject(otherOrganization2); + indexPermissions(); + ComponentDto file = db.components().insertComponent(newFileDto(project)); + ComponentDto anotherFile = db.components().insertComponent(newFileDto(project)); + DbIssues.Locations.Builder locations = DbIssues.Locations.newBuilder().addFlow(DbIssues.Flow.newBuilder().addAllLocation(Arrays.asList( + DbIssues.Location.newBuilder() + .setComponentId(file.uuid()) + .setMsg("FLOW MESSAGE") + .setTextRange(DbCommons.TextRange.newBuilder() + .setStartLine(1) + .setEndLine(1) + .setStartOffset(0) + .setEndOffset(12) + .build()) + .build(), + DbIssues.Location.newBuilder() + .setComponentId(anotherFile.uuid()) + .setMsg("ANOTHER FLOW MESSAGE") + .setTextRange(DbCommons.TextRange.newBuilder() + .setStartLine(1) + .setEndLine(1) + .setStartOffset(0) + .setEndOffset(12) + .build()) + .build(), + DbIssues.Location.newBuilder() + // .setComponentId(no component id set) + .setMsg("FLOW MESSAGE WITHOUT FILE UUID") + .setTextRange(DbCommons.TextRange.newBuilder() + .setStartLine(1) + .setEndLine(1) + .setStartOffset(0) + .setEndOffset(12) + .build()) + .build()))); + IssueDto issue = IssueTesting.newDto(newRule(), file, project) + .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2") + .setEffort(10L) + .setLine(42) + .setChecksum("a227e508d6646b55a086ee11d63b21e9") + .setMessage("the message") + .setStatus(Issue.STATUS_RESOLVED) + .setResolution(Issue.RESOLUTION_FIXED) + .setSeverity("MAJOR") + .setAuthorLogin(fabrice.getLogin()) + .setAssignee(simon.getLogin()) + .setTags(asList("bug", "owasp")) + .setLocations(locations.build()) + .setIssueCreationDate(DateUtils.parseDateTime("2014-09-04T00:00:00+0100")) + .setIssueUpdateDate(DateUtils.parseDateTime("2017-12-04T00:00:00+0100")); + db.issues().insertIssue(issue); + issueIndexer.indexOnStartup(issueIndexer.getIndexTypes()); + + Issues.SearchWsResponse result = ws.newRequest().executeProtobuf(Issues.SearchWsResponse.class); + + assertThat(result.getIssuesCount()).isEqualTo(1); + assertThat(result.getIssues(0).getFlows(0).getLocationsList()).extracting(Issues.Location::getComponent, Issues.Location::getMsg) + .containsExactly( + tuple(file.getKey(), "FLOW MESSAGE"), + tuple(anotherFile.getKey(), "ANOTHER FLOW MESSAGE"), + tuple(file.getKey(), "FLOW MESSAGE WITHOUT FILE UUID")); + } + @Test public void issue_with_comments() { dbClient.userDao().insert(session, new UserDto().setLogin("john").setName("John")); @@ -213,7 +287,7 @@ public class SearchActionTest { ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization2, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); IssueDto issue = IssueTesting.newDto(newRule(), file, project) .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2"); dbClient.issueDao().insert(session, issue); @@ -249,7 +323,7 @@ public class SearchActionTest { ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization1, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); IssueDto issue = IssueTesting.newDto(newRule(), file, project) .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2"); dbClient.issueDao().insert(session, issue); @@ -283,7 +357,7 @@ public class SearchActionTest { dbClient.userDao().insert(session, new UserDto().setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com")); ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization2, "PROJECT_ID").setDbKey("PROJECT_KEY").setLanguage("java")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY").setLanguage("js")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY").setLanguage("js")); IssueDto issue = IssueTesting.newDto(newRule(), file, project) .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2") @@ -306,7 +380,7 @@ public class SearchActionTest { ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization1, "PROJECT_ID").setDbKey("PROJECT_KEY").setLanguage("java")); grantPermissionToAnyone(project, ISSUE_ADMIN); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY").setLanguage("js")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY").setLanguage("js")); IssueDto issue = IssueTesting.newDto(newRule(), file, project) .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2") @@ -327,7 +401,7 @@ public class SearchActionTest { public void search_by_rule_key() { RuleDto rule = newRule(); ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization1, "PROJECT_ID").setDbKey("PROJECT_KEY").setLanguage("java")); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY").setLanguage("java")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY").setLanguage("java")); IssueDto issue = IssueTesting.newIssue(rule.getDefinition(), project, file); dbClient.issueDao().insert(session, issue); @@ -350,7 +424,7 @@ public class SearchActionTest { RuleDto rule = newRule(); ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization2, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto removedFile = insertComponent(ComponentTesting.newFileDto(project, null).setUuid("REMOVED_FILE_ID") + ComponentDto removedFile = insertComponent(newFileDto(project, null).setUuid("REMOVED_FILE_ID") .setDbKey("REMOVED_FILE_KEY") .setEnabled(false)); @@ -375,7 +449,7 @@ public class SearchActionTest { RuleDto rule = newRule(); ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization2, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); for (int i = 0; i < SearchOptions.MAX_LIMIT + 1; i++) { IssueDto issue = IssueTesting.newDto(rule, file, project); dbClient.issueDao().insert(session, issue); @@ -392,7 +466,7 @@ public class SearchActionTest { ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization1, "PROJECT_ID").setDbKey("ProjectHavingModule")); indexPermissions(); ComponentDto module = insertComponent(ComponentTesting.newModuleDto(project).setDbKey("ModuleHavingFile")); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(module, null, "BCDE").setDbKey("FileLinkedToModule")); + ComponentDto file = insertComponent(newFileDto(module, null, "BCDE").setDbKey("FileLinkedToModule")); IssueDto issue = IssueTesting.newDto(newRule(), file, project); dbClient.issueDao().insert(session, issue); session.commit(); @@ -406,7 +480,7 @@ public class SearchActionTest { public void display_facets() { ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization1, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); IssueDto issue = IssueTesting.newDto(newRule(), file, project) .setIssueCreationDate(DateUtils.parseDate("2014-09-04")) .setIssueUpdateDate(DateUtils.parseDate("2017-12-04")) @@ -431,7 +505,7 @@ public class SearchActionTest { public void display_facets_in_effort_mode() { ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization2, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); IssueDto issue = IssueTesting.newDto(newRule(), file, project) .setIssueCreationDate(DateUtils.parseDate("2014-09-04")) .setIssueUpdateDate(DateUtils.parseDate("2017-12-04")) @@ -457,7 +531,7 @@ public class SearchActionTest { public void display_zero_valued_facets_for_selected_items() { ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization1, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); IssueDto issue = IssueTesting.newDto(newRule(), file, project) .setIssueCreationDate(DateUtils.parseDate("2014-09-04")) .setIssueUpdateDate(DateUtils.parseDate("2017-12-04")) @@ -499,7 +573,7 @@ public class SearchActionTest { ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(defaultOrganization, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); RuleDto rule = newRule(); IssueDto issue1 = IssueTesting.newDto(rule, file, project) .setIssueCreationDate(DateUtils.parseDate("2014-09-04")) @@ -543,7 +617,7 @@ public class SearchActionTest { ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization1, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); RuleDto rule = newRule(); IssueDto issue1 = IssueTesting.newDto(rule, file, project) .setStatus("OPEN") @@ -573,7 +647,7 @@ public class SearchActionTest { ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization2, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); RuleDto rule = newRule(); IssueDto issue1 = IssueTesting.newDto(rule, file, project) .setIssueCreationDate(DateUtils.parseDate("2014-09-04")) @@ -616,7 +690,7 @@ public class SearchActionTest { RuleDto rule = newRule(); ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization2, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); dbClient.issueDao().insert(session, IssueTesting.newDto(rule, file, project) .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac1") .setIssueUpdateDate(DateUtils.parseDateTime("2014-11-02T00:00:00+0100"))); @@ -646,7 +720,7 @@ public class SearchActionTest { RuleDto rule = newRule(); ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization1, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); for (int i = 0; i < 12; i++) { IssueDto issue = IssueTesting.newDto(rule, file, project); dbClient.issueDao().insert(session, issue); @@ -666,7 +740,7 @@ public class SearchActionTest { RuleDto rule = newRule(); ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization2, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); for (int i = 0; i < 12; i++) { IssueDto issue = IssueTesting.newDto(rule, file, project); dbClient.issueDao().insert(session, issue); @@ -686,7 +760,7 @@ public class SearchActionTest { RuleDto rule = newRule(); ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(defaultOrganization, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); for (int i = 0; i < 12; i++) { IssueDto issue = IssueTesting.newDto(rule, file, project); dbClient.issueDao().insert(session, issue); @@ -712,7 +786,7 @@ public class SearchActionTest { public void display_deprecated_debt_fields() { ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization1, "PROJECT_ID").setDbKey("PROJECT_KEY")); indexPermissions(); - ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); + ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY")); IssueDto issue = IssueTesting.newDto(newRule(), file, project) .setIssueCreationDate(DateUtils.parseDate("2014-09-04")) .setIssueUpdateDate(DateUtils.parseDate("2017-12-04")) diff --git a/sonar-ws/src/main/protobuf/ws-issues.proto b/sonar-ws/src/main/protobuf/ws-issues.proto index 125f6e4c216..93b99eb4a3c 100644 --- a/sonar-ws/src/main/protobuf/ws-issues.proto +++ b/sonar-ws/src/main/protobuf/ws-issues.proto @@ -172,6 +172,7 @@ message Flow { } message Location { + optional string component = 4; optional string unusedComponentId = 1; // Only when component is a file. Can be empty for a file if this is an issue global to the file. optional sonarqube.ws.commons.TextRange textRange = 2; -- 2.39.5