@@ -0,0 +1,77 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 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.hotspot.ws; | |||
import org.sonar.api.server.ws.Request; | |||
import org.sonar.api.server.ws.Response; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.api.web.UserRole; | |||
import org.sonar.core.util.Uuids; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.issue.IssueDto; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
import org.sonar.server.user.UserSession; | |||
import static java.lang.String.format; | |||
public class ShowAction implements HotspotsWsAction { | |||
private static final String PARAM_HOTSPOT_KEY = "hotspot"; | |||
private final DbClient dbClient; | |||
private final UserSession userSession; | |||
public ShowAction(DbClient dbClient, UserSession userSession) { | |||
this.dbClient = dbClient; | |||
this.userSession = userSession; | |||
} | |||
@Override | |||
public void define(WebService.NewController controller) { | |||
WebService.NewAction action = controller | |||
.createAction("show") | |||
.setHandler(this) | |||
.setDescription("Provides the details of a Security Hotpot.") | |||
.setSince("8.1"); | |||
action.createParam(PARAM_HOTSPOT_KEY) | |||
.setDescription("Key of the Security Hotspot") | |||
.setExampleValue(Uuids.UUID_EXAMPLE_03) | |||
.setRequired(true); | |||
// FIXME add response example and test it | |||
// action.setResponseExample() | |||
} | |||
@Override | |||
public void handle(Request request, Response response) throws Exception { | |||
String hotspotKey = request.mandatoryParam(PARAM_HOTSPOT_KEY); | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
IssueDto hotspot = dbClient.issueDao().selectByKey(dbSession, hotspotKey) | |||
.orElseThrow(() -> new NotFoundException(format("Hotspot with key '%s' does not exist", hotspotKey))); | |||
ComponentDto project = dbClient.componentDao().selectByUuid(dbSession, hotspot.getProjectUuid()) | |||
.orElseThrow(() -> new NotFoundException(format("Project with uuid '%s' does not exist", hotspot.getProjectUuid()))); | |||
userSession.checkComponentPermission(UserRole.USER, project); | |||
} | |||
} | |||
} |
@@ -39,6 +39,7 @@ 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.protobuf.DbIssues.Location; | |||
import org.sonar.db.rule.RuleDefinitionDto; | |||
import org.sonar.db.user.UserDto; | |||
import org.sonar.markdown.Markdown; | |||
@@ -51,9 +52,7 @@ import org.sonarqube.ws.Issues.Actions; | |||
import org.sonarqube.ws.Issues.Comment; | |||
import org.sonarqube.ws.Issues.Comments; | |||
import org.sonarqube.ws.Issues.Component; | |||
import org.sonarqube.ws.Issues.Flow; | |||
import org.sonarqube.ws.Issues.Issue; | |||
import org.sonarqube.ws.Issues.Location; | |||
import org.sonarqube.ws.Issues.Operation; | |||
import org.sonarqube.ws.Issues.SearchWsResponse; | |||
import org.sonarqube.ws.Issues.Transitions; | |||
@@ -230,16 +229,16 @@ public class SearchResponseFormat { | |||
issueBuilder.setTextRange(convertTextRange(textRange)); | |||
} | |||
for (DbIssues.Flow flow : locations.getFlowList()) { | |||
Flow.Builder targetFlow = Flow.newBuilder(); | |||
for (DbIssues.Location flowLocation : flow.getLocationList()) { | |||
Common.Flow.Builder targetFlow = Common.Flow.newBuilder(); | |||
for (Location flowLocation : flow.getLocationList()) { | |||
targetFlow.addLocations(convertLocation(issueBuilder, flowLocation, data)); | |||
} | |||
issueBuilder.addFlows(targetFlow); | |||
} | |||
} | |||
private static Location convertLocation(Issue.Builder issueBuilder, DbIssues.Location source, SearchResponseData data) { | |||
Location.Builder target = Location.newBuilder(); | |||
private static Common.Location convertLocation(Issue.Builder issueBuilder, Location source, SearchResponseData data) { | |||
Common.Location.Builder target = Common.Location.newBuilder(); | |||
if (source.hasMsg()) { | |||
target.setMsg(source.getMsg()); | |||
} |
@@ -71,7 +71,6 @@ import org.sonar.server.ws.TestResponse; | |||
import org.sonar.server.ws.WsActionTester; | |||
import org.sonarqube.ws.Common; | |||
import org.sonarqube.ws.Common.Severity; | |||
import org.sonarqube.ws.Issues; | |||
import org.sonarqube.ws.Issues.Issue; | |||
import org.sonarqube.ws.Issues.SearchWsResponse; | |||
@@ -253,7 +252,7 @@ public class SearchActionTest { | |||
SearchWsResponse result = ws.newRequest().executeProtobuf(SearchWsResponse.class); | |||
assertThat(result.getIssuesCount()).isEqualTo(1); | |||
assertThat(result.getIssues(0).getFlows(0).getLocationsList()).extracting(Issues.Location::getComponent, Issues.Location::getMsg) | |||
assertThat(result.getIssues(0).getFlows(0).getLocationsList()).extracting(Common.Location::getComponent, Common.Location::getMsg) | |||
.containsExactlyInAnyOrder( | |||
tuple(file.getKey(), "FLOW MESSAGE"), | |||
tuple(anotherFile.getKey(), "ANOTHER FLOW MESSAGE"), |
@@ -93,6 +93,18 @@ message TextRange { | |||
optional int32 endOffset = 4; | |||
} | |||
message Flow { | |||
repeated Location locations = 1; | |||
} | |||
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; | |||
optional string msg = 3; | |||
} | |||
message Metric { | |||
optional string key = 1; | |||
optional string name = 2; |
@@ -112,7 +112,7 @@ message Issue { | |||
optional int32 line = 8; | |||
optional string hash = 31; | |||
optional sonarqube.ws.commons.TextRange textRange = 9; | |||
repeated Flow flows = 10; | |||
repeated sonarqube.ws.commons.Flow flows = 10; | |||
optional string resolution = 11; | |||
optional string status = 12; | |||
optional string message = 13; | |||
@@ -164,18 +164,6 @@ message Actions { | |||
repeated string actions = 1; | |||
} | |||
message Flow { | |||
repeated Location locations = 1; | |||
} | |||
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; | |||
optional string msg = 3; | |||
} | |||
message Comment { | |||
optional string key = 1; | |||
optional string login = 2; |