Browse Source

Store issues locations in DB and return in WS

tags/5.2-RC1
Simon Brandhof 8 years ago
parent
commit
12324c45ba

+ 1
- 4
server/sonar-server/src/main/java/org/sonar/server/computation/issue/BaseIssuesLoader.java View File

@@ -19,10 +19,8 @@
*/
package org.sonar.server.computation.issue;

import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
@@ -60,8 +58,7 @@ public class BaseIssuesLoader {
DbSession session = dbClient.openSession(false);
final List<DefaultIssue> result = new ArrayList<>();
try {
Map<String, String> params = ImmutableMap.of("componentUuid", componentUuid);
session.select(IssueMapper.class.getName() + ".selectNonClosedByComponentUuid", params, new ResultHandler() {
session.getMapper(IssueMapper.class).selectNonClosedByComponentUuid(componentUuid, new ResultHandler() {
@Override
public void handleResult(ResultContext resultContext) {
DefaultIssue issue = ((IssueDto) resultContext.getResultObject()).toDefaultIssue();

+ 46
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java View File

@@ -20,7 +20,6 @@
package org.sonar.server.computation.issue;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -34,6 +33,8 @@ import org.sonar.core.issue.tracking.Input;
import org.sonar.core.issue.tracking.LazyInput;
import org.sonar.core.issue.tracking.LineHashSequence;
import org.sonar.core.util.CloseableIterator;
import org.sonar.db.protobuf.DbCommons;
import org.sonar.db.protobuf.DbIssues;
import org.sonar.server.computation.batch.BatchReportReader;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.TreeRootHolder;
@@ -127,6 +128,22 @@ public class TrackerRawInputFactory {
if (reportIssue.hasAttributes()) {
issue.setAttributes(KeyValueFormat.parse(reportIssue.getAttributes()));
}
DbIssues.Locations.Builder dbLocationsBuilder = DbIssues.Locations.newBuilder();
if (reportIssue.hasPrimaryLocation()) {
BatchReport.IssueLocation location = reportIssue.getPrimaryLocation();
dbLocationsBuilder.setPrimary(convertLocation(location));
}
for (BatchReport.IssueLocation location : reportIssue.getAdditionalLocationList()) {
dbLocationsBuilder.addSecondary(convertLocation(location));
}
for (BatchReport.ExecutionFlow flow : reportIssue.getExecutionFlowList()) {
DbIssues.ExecutionFlow.Builder dbFlowBuilder = DbIssues.ExecutionFlow.newBuilder();
for (BatchReport.IssueLocation location : flow.getLocationList()) {
dbFlowBuilder.addLocations(convertLocation(location));
}
dbLocationsBuilder.addExecutionFlow(dbFlowBuilder);
}
issue.setLocations(dbLocationsBuilder.build());
return issue;
}

@@ -139,5 +156,33 @@ public class TrackerRawInputFactory {
issue.setProjectKey(treeRootHolder.getRoot().getKey());
return issue;
}

private DbIssues.Location convertLocation(BatchReport.IssueLocation source) {
DbIssues.Location.Builder target = DbIssues.Location.newBuilder();
if (source.hasComponentRef() && source.getComponentRef() != component.getRef()) {
target.setComponentId(treeRootHolder.getComponentByRef(source.getComponentRef()).getUuid());
}
if (source.hasMsg()) {
target.setMsg(source.getMsg());
}
if (source.hasTextRange()) {
BatchReport.TextRange sourceRange = source.getTextRange();
DbCommons.TextRange.Builder targetRange = DbCommons.TextRange.newBuilder();
if (sourceRange.hasStartLine()) {
targetRange.setStartLine(sourceRange.getStartLine());
}
if (sourceRange.hasStartOffset()) {
targetRange.setStartOffset(sourceRange.getStartOffset());
}
if (sourceRange.hasEndLine()) {
targetRange.setEndLine(sourceRange.getEndLine());
}
if (sourceRange.hasEndOffset()) {
targetRange.setEndOffset(sourceRange.getEndOffset());
}
target.setTextRange(targetRange);
}
return target.build();
}
}
}

+ 56
- 4
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java View File

@@ -40,6 +40,8 @@ import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.ActionPlanDto;
import org.sonar.db.issue.IssueChangeDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.protobuf.DbCommons;
import org.sonar.db.protobuf.DbIssues;
import org.sonar.db.rule.RuleDto;
import org.sonar.db.user.UserDto;
import org.sonar.markdown.Markdown;
@@ -165,6 +167,7 @@ public class SearchResponseFormat {
issueBuilder.setStatus(nullToEmpty(dto.getStatus()));
issueBuilder.setActionPlan(nullToEmpty(dto.getActionPlanKey()));
issueBuilder.setMessage(nullToEmpty(dto.getMessage()));
issueBuilder.setTagsPresentIfEmpty(true);
issueBuilder.addAllTags(dto.getTags());
Long debt = dto.getDebt();
if (debt != null) {
@@ -174,6 +177,7 @@ public class SearchResponseFormat {
if (line != null) {
issueBuilder.setLine(line);
}
completeIssueLocations(dto, issueBuilder);
issueBuilder.setAuthor(nullToEmpty(dto.getAuthorLogin()));
Date date = dto.getIssueCreationDate();
if (date != null) {
@@ -189,7 +193,55 @@ public class SearchResponseFormat {
}
}

private void formatIssueTransitions(SearchResponseData data, Issues.Issue.Builder issueBuilder, IssueDto dto) {
private void completeIssueLocations(IssueDto dto, Issues.Issue.Builder issueBuilder) {
DbIssues.Locations locations = dto.parseLocations();
if (locations != null) {
if (locations.hasPrimary()) {
DbIssues.Location primary = locations.getPrimary();
issueBuilder.setLocation(convertLocation(primary));
}
for (DbIssues.Location secondary : locations.getSecondaryList()) {
issueBuilder.addSecondaryLocations(convertLocation(secondary));
}
for (DbIssues.ExecutionFlow flow : locations.getExecutionFlowList()) {
Issues.ExecutionFlow.Builder targetFlow = Issues.ExecutionFlow.newBuilder();
for (DbIssues.Location flowLocation : flow.getLocationsList()) {
targetFlow.addLocations(convertLocation(flowLocation));
}
issueBuilder.addExecutionFlows(targetFlow);
}
}
}

private static Issues.Location convertLocation(DbIssues.Location source) {
Issues.Location.Builder target = Issues.Location.newBuilder();
if (source.hasComponentId()) {
target.setComponentId(source.getComponentId());
}
if (source.hasMsg()) {
target.setMsg(source.getMsg());
}
if (source.hasTextRange()) {
DbCommons.TextRange sourceRange = source.getTextRange();
Common.TextRange.Builder targetRange = Common.TextRange.newBuilder();
if (sourceRange.hasStartLine()) {
targetRange.setStartLine(sourceRange.getStartLine());
}
if (sourceRange.hasStartOffset()) {
targetRange.setStartOffset(sourceRange.getStartOffset());
}
if (sourceRange.hasEndLine()) {
targetRange.setEndLine(sourceRange.getEndLine());
}
if (sourceRange.hasEndOffset()) {
targetRange.setEndOffset(sourceRange.getEndOffset());
}
target.setTextRange(targetRange);
}
return target.build();
}

private static void formatIssueTransitions(SearchResponseData data, Issues.Issue.Builder issueBuilder, IssueDto dto) {
issueBuilder.setTransitionsPresentIfEmpty(true);
List<Transition> transitions = data.getTransitionsForIssueKey(dto.getKey());
if (transitions != null) {
@@ -199,7 +251,7 @@ public class SearchResponseFormat {
}
}

private void formatIssueActions(SearchResponseData data, Issues.Issue.Builder issueBuilder, IssueDto dto) {
private static void formatIssueActions(SearchResponseData data, Issues.Issue.Builder issueBuilder, IssueDto dto) {
issueBuilder.setActionsPresentIfEmpty(true);
List<String> actions = data.getActionsForIssueKey(dto.getKey());
if (actions != null) {
@@ -207,7 +259,7 @@ public class SearchResponseFormat {
}
}

private void formatIssueComments(SearchResponseData data, Issues.Issue.Builder issueBuilder, IssueDto dto) {
private static void formatIssueComments(SearchResponseData data, Issues.Issue.Builder issueBuilder, IssueDto dto) {
issueBuilder.setCommentsPresentIfEmpty(true);
List<IssueChangeDto> comments = data.getCommentsForIssueKey(dto.getKey());
if (comments != null) {
@@ -241,7 +293,7 @@ public class SearchResponseFormat {
return result;
}

private List<Issues.Component> formatComponents(SearchResponseData data) {
private static List<Issues.Component> formatComponents(SearchResponseData data) {
List<Issues.Component> result = new ArrayList<>();
Collection<ComponentDto> components = data.getComponents();
if (components != null) {

+ 23
- 4
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java View File

@@ -35,6 +35,7 @@ import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.IssueChangeDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.protobuf.DbIssues;
import org.sonar.server.es.Facets;
import org.sonar.server.issue.ActionService;
import org.sonar.server.issue.IssueCommentService;
@@ -181,6 +182,28 @@ public class SearchResponseLoader {
add(RULES, issue.getRuleKey());
add(USERS, issue.getReporter());
add(USERS, issue.getAssignee());
collectIssueLocations(issue);
}
}

private void collectIssueLocations(IssueDto issue) {
DbIssues.Locations locations = issue.parseLocations();
if (locations != null) {
if (locations.hasPrimary() && locations.getPrimary().hasComponentId()) {
componentUuids.add(locations.getPrimary().getComponentId());
}
for (DbIssues.Location location : locations.getSecondaryList()) {
if (location.hasComponentId()) {
componentUuids.add(location.getComponentId());
}
}
for (DbIssues.ExecutionFlow flow : locations.getExecutionFlowList()) {
for (DbIssues.Location location : flow.getLocationsList()) {
if (location.hasComponentId()) {
componentUuids.add(location.getComponentId());
}
}
}
}
}

@@ -190,10 +213,6 @@ public class SearchResponseLoader {
}
}

public void addComponentUuid(String uuid) {
this.componentUuids.add(uuid);
}

public void addComponentUuids(@Nullable Collection<String> uuids) {
if (uuids != null) {
this.componentUuids.addAll(uuids);

+ 325
- 325
sonar-db/src/main/gen-java/org/sonar/db/protobuf/DbIssues.java
File diff suppressed because it is too large
View File


+ 4
- 1
sonar-db/src/main/java/org/sonar/db/issue/IssueDto.java View File

@@ -103,6 +103,7 @@ public final class IssueDto implements Serializable {
return new IssueDto()
.setKee(issue.key())
.setLine(issue.line())
.setLocations((DbIssues.Locations) issue.getLocations())
.setMessage(issue.message())
.setEffortToFix(issue.effortToFix())
.setDebt(issue.debtInMinutes())
@@ -150,6 +151,7 @@ public final class IssueDto implements Serializable {
return new IssueDto()
.setKee(issue.key())
.setLine(issue.line())
.setLocations((DbIssues.Locations) issue.getLocations())
.setMessage(issue.message())
.setEffortToFix(issue.effortToFix())
.setDebt(issue.debtInMinutes())
@@ -665,7 +667,7 @@ public final class IssueDto implements Serializable {
return null;
}

public IssueDto setLocations(byte[] locations) {
public IssueDto setLocations(@Nullable byte[] locations) {
this.locations = locations;
return this;
}
@@ -715,6 +717,7 @@ public final class IssueDto implements Serializable {
issue.setCloseDate(longToDate(issueCloseDate));
issue.setUpdateDate(longToDate(issueUpdateDate));
issue.setSelectedAt(selectedAt);
issue.setLocations(parseLocations());
return issue;
}
}

+ 3
- 1
sonar-db/src/main/java/org/sonar/db/issue/IssueMapper.java View File

@@ -21,12 +21,14 @@ package org.sonar.db.issue;

import java.util.List;
import java.util.Set;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.ResultHandler;

public interface IssueMapper {

IssueDto selectByKey(String key);

List<IssueDto> selectNonClosedByComponentUuid(String componentUuid);
void selectNonClosedByComponentUuid(@Param("componentUuid") String componentUuid, ResultHandler resultHandler);

Set<String> selectComponentUuidsOfOpenIssuesForProjectUuid(String projectUuid);


+ 2
- 2
sonar-db/src/main/protobuf/db-issues.proto View File

@@ -32,8 +32,8 @@ option optimize_for = SPEED;

message Locations {
optional Location primary = 1;
repeated Location secondaries = 2;
repeated ExecutionFlow execution_flows = 3;
repeated Location secondary = 2;
repeated ExecutionFlow execution_flow = 3;
}

message ExecutionFlow {

+ 6
- 1
sonar-db/src/main/resources/org/sonar/db/issue/IssueMapper.xml View File

@@ -13,6 +13,7 @@
i.manual_severity as manualSeverity,
i.message as message,
i.line as line,
i.locations as locations,
i.effort_to_fix as effortToFix,
i.technical_debt as debt,
i.status as status,
@@ -67,12 +68,13 @@

<insert id="insert" parameterType="Issue" useGeneratedKeys="false" keyProperty="id">
INSERT INTO issues (kee, rule_id, action_plan_key, severity, manual_severity,
message, line, effort_to_fix, technical_debt, status, tags,
message, line, locations, effort_to_fix, technical_debt, status, tags,
resolution, checksum, reporter, assignee, author_login, issue_attributes, issue_creation_date, issue_update_date,
issue_close_date, created_at, updated_at, component_uuid, project_uuid)
VALUES (#{kee,jdbcType=VARCHAR}, #{ruleId,jdbcType=INTEGER}, #{actionPlanKey,jdbcType=VARCHAR},
#{severity,jdbcType=VARCHAR},
#{manualSeverity,jdbcType=BOOLEAN}, #{message,jdbcType=VARCHAR}, #{line,jdbcType=INTEGER},
#{locations,jdbcType=BLOB},
#{effortToFix,jdbcType=DOUBLE}, #{debt,jdbcType=INTEGER}, #{status,jdbcType=VARCHAR},
#{tagsString,jdbcType=VARCHAR}, #{resolution,jdbcType=VARCHAR}, #{checksum,jdbcType=VARCHAR},
#{reporter,jdbcType=VARCHAR}, #{assignee,jdbcType=VARCHAR}, #{authorLogin,jdbcType=VARCHAR},
@@ -92,6 +94,7 @@
manual_severity=#{manualSeverity,jdbcType=BOOLEAN},
message=#{message,jdbcType=VARCHAR},
line=#{line,jdbcType=INTEGER},
locations=#{locations,jdbcType=BLOB},
effort_to_fix=#{effortToFix,jdbcType=DOUBLE},
technical_debt=#{debt,jdbcType=INTEGER},
status=#{status,jdbcType=VARCHAR},
@@ -120,6 +123,7 @@
manual_severity=#{manualSeverity,jdbcType=BOOLEAN},
message=#{message,jdbcType=VARCHAR},
line=#{line,jdbcType=INTEGER},
locations=#{locations,jdbcType=BLOB},
effort_to_fix=#{effortToFix,jdbcType=DOUBLE},
technical_debt=#{debt,jdbcType=INTEGER},
status=#{status,jdbcType=VARCHAR},
@@ -172,6 +176,7 @@
i.manual_severity as manualSeverity,
i.message as message,
i.line as line,
i.locations as locations,
i.effort_to_fix as effortToFix,
i.technical_debt as debt,
i.status as status,

+ 3
- 1
sonar-db/src/test/java/org/sonar/db/issue/IssueDaoTest.java View File

@@ -29,9 +29,9 @@ import org.junit.rules.ExpectedException;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.System2;
import org.sonar.db.DbTester;
import org.sonar.db.RowNotFoundException;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.rule.RuleTesting;
import org.sonar.db.RowNotFoundException;
import org.sonar.test.DbTests;

import static java.util.Arrays.asList;
@@ -126,6 +126,8 @@ public class IssueDaoTest {
assertThat(issue.getRule()).isEqualTo("AvoidCycle");
assertThat(issue.getComponentKey()).isEqualTo("Action.java");
assertThat(issue.getProjectKey()).isEqualTo("struts");
assertThat(issue.getLocations()).isNull();
assertThat(issue.parseLocations()).isNull();
}

@Test

+ 921
- 141
sonar-ws/src/main/gen-java/org/sonarqube/ws/Common.java
File diff suppressed because it is too large
View File


+ 3845
- 1583
sonar-ws/src/main/gen-java/org/sonarqube/ws/Issues.java
File diff suppressed because it is too large
View File


sonar-ws/src/main/protobuf/ws-common.proto → sonar-ws/src/main/protobuf/ws-commons.proto View File

@@ -18,7 +18,7 @@

syntax = "proto2";

package sonarqube.ws;
package sonarqube.ws.commons;

option java_package = "org.sonarqube.ws";
option java_outer_classname = "Common";
@@ -71,3 +71,18 @@ message User {
optional string email = 3;
optional bool active = 4;
}

// Lines start at 1 and line offsets start at 0
message TextRange {
// Start line. Should never be absent
optional int32 start_line = 1;

// End line (inclusive). Absent means it is same as start line
optional int32 end_line = 2;

// If absent it means range starts at the first offset of start line
optional int32 start_offset = 3;

// If absent it means range ends at the last offset of end line
optional int32 end_offset = 4;
}

+ 43
- 30
sonar-ws/src/main/protobuf/ws-issues.proto View File

@@ -20,7 +20,7 @@ syntax = "proto2";

package sonarqube.ws.issues;

import "ws-common.proto";
import "ws-commons.proto";

option java_package = "org.sonarqube.ws";
option java_outer_classname = "Issues";
@@ -31,7 +31,7 @@ message Search {
optional int64 total = 1;
optional int64 p = 2;
optional int32 ps = 3;
optional Paging paging = 4;
optional sonarqube.ws.commons.Paging paging = 4;

// Total amount of debt, only when the facet "total" is enabled
optional int64 debtTotal = 5;
@@ -39,23 +39,23 @@ message Search {
repeated Issue issues = 6;
repeated Component components = 7;
optional bool rulesPresentIfEmpty = 8;
repeated Rule rules = 9;
repeated sonarqube.ws.commons.Rule rules = 9;
optional bool usersPresentIfEmpty = 10;
repeated User users = 11;
repeated sonarqube.ws.commons.User users = 11;
optional bool actionPlansPresentIfEmpty = 12;
repeated ActionPlan actionPlans = 13;
optional bool languagesPresentIfEmpty = 14;
repeated Language languages = 15;
optional bool facetsPresentIfEmpty = 16;
repeated Facet facets = 17;
repeated sonarqube.ws.commons.Facet facets = 17;
}

// Response of most of POST/issues/{operation}, for instance assign, add_comment and set_severity
message Operation {
optional Issue issue = 1;
repeated Component components = 2;
repeated Rule rules = 3;
repeated User users = 4;
repeated sonarqube.ws.commons.Rule rules = 3;
repeated sonarqube.ws.commons.User users = 4;
repeated ActionPlan actionPlans = 5;
}

@@ -63,41 +63,54 @@ message Operation {
message Issue {
optional string key = 1;
optional string rule = 2;
optional Severity severity = 3;
optional sonarqube.ws.commons.Severity severity = 3;
optional string component = 4;
optional int64 componentId = 5;
optional string project = 6;
optional string subProject = 7;
optional int32 line = 8;
optional string resolution = 9;
optional string status = 10;
optional string message = 11;
optional string debt = 12;
optional string assignee = 13;
optional string reporter = 14;
optional Location location = 9;
repeated Location secondaryLocations = 10;
repeated ExecutionFlow executionFlows = 11;
optional string resolution = 12;
optional string status = 13;
optional string message = 14;
optional string debt = 15;
optional string assignee = 16;
optional string reporter = 17;

// SCM login of the committer who introduced the issue
optional string author = 15;
optional string author = 18;

optional string actionPlan = 16;
optional string actionPlanName = 17;
optional string attr = 18;
repeated string tags = 19;
optional string actionPlan = 19;
optional bool tagsPresentIfEmpty = 20;
repeated string tags = 21;

// the transitions allowed for the requesting user.
optional bool transitionsPresentIfEmpty = 20;
repeated string transitions = 21;
optional bool transitionsPresentIfEmpty = 22;
repeated string transitions = 23;

// the actions allowed for the requesting user.
optional bool actionsPresentIfEmpty = 22;
repeated string actions = 23;

optional bool commentsPresentIfEmpty = 24;
repeated Comment comments = 25;
optional string creationDate = 26;
optional string updateDate = 27;
optional string fUpdateAge = 28;
optional string closeDate = 29;
optional bool actionsPresentIfEmpty = 24;
repeated string actions = 25;

optional bool commentsPresentIfEmpty = 26;
repeated Comment comments = 27;
optional string creationDate = 28;
optional string updateDate = 29;
optional string fUpdateAge = 30;
optional string closeDate = 31;
}

message ExecutionFlow {
repeated Location locations = 1;
}

message Location {
optional string component_id = 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 text_range = 2;
optional string msg = 3;
}

message Comment {

Loading…
Cancel
Save