aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--it/it-tests/src/test/java/it/duplication/DuplicationsTest.java74
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsWsAction.java27
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java39
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowResponseBuilder.java (renamed from server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsJsonWriter.java)120
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java4
-rw-r--r--server/sonar-server/src/main/resources/org/sonar/server/duplication/ws/show-example.json (renamed from server/sonar-server/src/main/resources/org/sonar/server/duplication/ws/example-show.json)28
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsWsTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java49
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowResponseBuilderTest.java (renamed from server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsJsonWriterTest.java)71
-rw-r--r--sonar-ws/src/main/protobuf/ws-duplications.proto54
10 files changed, 294 insertions, 174 deletions
diff --git a/it/it-tests/src/test/java/it/duplication/DuplicationsTest.java b/it/it-tests/src/test/java/it/duplication/DuplicationsTest.java
index ea55e0eb24b..6eddcee5915 100644
--- a/it/it-tests/src/test/java/it/duplication/DuplicationsTest.java
+++ b/it/it-tests/src/test/java/it/duplication/DuplicationsTest.java
@@ -28,14 +28,19 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.WsClient;
+import org.sonarqube.ws.client.WsResponse;
import org.sonarqube.ws.client.issue.SearchWsRequest;
import util.ItUtils;
import util.issue.IssueRule;
+import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.skyscreamer.jsonassert.JSONAssert.assertEquals;
import static util.ItUtils.getMeasuresAsDoubleByMetricKey;
+import static util.ItUtils.newAdminWsClient;
import static util.ItUtils.runProjectAnalysis;
import static util.ItUtils.setServerProperty;
@@ -51,6 +56,8 @@ public class DuplicationsTest {
@ClassRule
public static final IssueRule issueRule = IssueRule.from(orchestrator);
+ private static WsClient adminWsClient;
+
@BeforeClass
public static void analyzeProjects() {
orchestrator.resetData();
@@ -62,6 +69,8 @@ public class DuplicationsTest {
// Set minimum tokens to a big value in order to not get duplications
setServerProperty(orchestrator, "sonar.cpd.xoo.minimumTokens", "1000");
analyzeProject(WITHOUT_ENOUGH_TOKENS);
+
+ adminWsClient = newAdminWsClient(orchestrator);
}
@AfterClass
@@ -69,6 +78,37 @@ public class DuplicationsTest {
setServerProperty(orchestrator, "sonar.cpd.xoo.minimumTokens", null);
}
+ private static Map<String, Double> getMeasures(String key) {
+ return getMeasuresAsDoubleByMetricKey(orchestrator, key, "duplicated_lines", "duplicated_blocks", "duplicated_files", "duplicated_lines_density");
+ }
+
+ private static void verifyDuplicationMeasures(String componentKey, int duplicatedBlocks, int duplicatedLines, int duplicatedFiles, double duplicatedLinesDensity) {
+ Map<String, Double> measures = getMeasures(componentKey);
+ assertThat(measures.get("duplicated_blocks").intValue()).isEqualTo(duplicatedBlocks);
+ assertThat(measures.get("duplicated_lines").intValue()).isEqualTo(duplicatedLines);
+ assertThat(measures.get("duplicated_files").intValue()).isEqualTo(duplicatedFiles);
+ assertThat(measures.get("duplicated_lines_density")).isEqualTo(duplicatedLinesDensity);
+ }
+
+ private static void analyzeProject(String projectKey, String... additionalProperties) {
+ orchestrator.getServer().provisionProject(projectKey, projectKey);
+ orchestrator.getServer().associateProjectToQualityProfile(projectKey, "xoo", "xoo-duplication-profile");
+
+ runProjectAnalysis(orchestrator, "duplications/file-duplications",
+ ObjectArrays.concat(
+ new String[] {
+ "sonar.projectKey", projectKey,
+ "sonar.projectName", projectKey
+ },
+ additionalProperties, String.class));
+ }
+
+ private static void verifyWsResultOnDuplicateFile(String fileKey, String ws, String expectedFilePath) throws Exception {
+ String duplication = orchestrator.getServer().adminWsClient().get(ws, "key", fileKey);
+ assertEquals(IOUtils.toString(CrossProjectDuplicationsTest.class.getResourceAsStream("/duplication/DuplicationsTest/" + expectedFilePath), "UTF-8"), duplication,
+ false);
+ }
+
@Test
public void duplicated_lines_within_same_file() {
verifyDuplicationMeasures(DUPLICATIONS + ":src/main/xoo/duplicated_lines_within_same_file/DuplicatedLinesInSameFile.xoo",
@@ -137,35 +177,13 @@ public class DuplicationsTest {
"api/duplications/show", "duplications_show-expected.json");
}
- private static Map<String, Double> getMeasures(String key) {
- return getMeasuresAsDoubleByMetricKey(orchestrator, key, "duplicated_lines", "duplicated_blocks", "duplicated_files", "duplicated_lines_density");
- }
-
- private static void verifyDuplicationMeasures(String componentKey, int duplicatedBlocks, int duplicatedLines, int duplicatedFiles, double duplicatedLinesDensity) {
- Map<String, Double> measures = getMeasures(componentKey);
- assertThat(measures.get("duplicated_blocks").intValue()).isEqualTo(duplicatedBlocks);
- assertThat(measures.get("duplicated_lines").intValue()).isEqualTo(duplicatedLines);
- assertThat(measures.get("duplicated_files").intValue()).isEqualTo(duplicatedFiles);
- assertThat(measures.get("duplicated_lines_density")).isEqualTo(duplicatedLinesDensity);
- }
-
- private static void analyzeProject(String projectKey, String... additionalProperties) {
- orchestrator.getServer().provisionProject(projectKey, projectKey);
- orchestrator.getServer().associateProjectToQualityProfile(projectKey, "xoo", "xoo-duplication-profile");
-
- runProjectAnalysis(orchestrator, "duplications/file-duplications",
- ObjectArrays.concat(
- new String[] {
- "sonar.projectKey", projectKey,
- "sonar.projectName", projectKey
- },
- additionalProperties, String.class));
- }
+ // SONAR-9441
+ @Test
+ public void fail_properly_when_no_parameter() {
+ WsResponse result = adminWsClient.wsConnector().call(new GetRequest("api/duplications/show"));
- private static void verifyWsResultOnDuplicateFile(String fileKey, String ws, String expectedFilePath) throws Exception {
- String duplication = orchestrator.getServer().adminWsClient().get(ws, "key", fileKey);
- assertEquals(IOUtils.toString(CrossProjectDuplicationsTest.class.getResourceAsStream("/duplication/DuplicationsTest/" + expectedFilePath), "UTF-8"), duplication,
- false);
+ assertThat(result.code()).isEqualTo(HTTP_BAD_REQUEST);
+ assertThat(result.content()).contains("Either 'uuid' or 'key' must be provided, not both");
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsWsAction.java
new file mode 100644
index 00000000000..8c41d92d71b
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsWsAction.java
@@ -0,0 +1,27 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.duplication.ws;
+
+import org.sonar.server.ws.WsAction;
+
+public interface DuplicationsWsAction extends WsAction {
+ // marker interface
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java
index 7e18c3df19e..31e5fa771a0 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java
@@ -19,16 +19,13 @@
*/
package org.sonar.server.duplication.ws;
-import com.google.common.io.Resources;
import java.util.List;
-import java.util.Optional;
import javax.annotation.CheckForNull;
import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request;
-import org.sonar.api.server.ws.RequestHandler;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
-import org.sonar.api.utils.text.JsonWriter;
import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
@@ -39,30 +36,34 @@ import org.sonar.server.component.ComponentFinder;
import org.sonar.server.user.UserSession;
import static org.sonar.server.component.ComponentFinder.ParamNames.UUID_AND_KEY;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
-public class ShowAction implements RequestHandler {
+public class ShowAction implements DuplicationsWsAction {
private final DbClient dbClient;
private final DuplicationsParser parser;
- private final DuplicationsJsonWriter duplicationsJsonWriter;
+ private final ShowResponseBuilder responseBuilder;
private final UserSession userSession;
private final ComponentFinder componentFinder;
- public ShowAction(DbClient dbClient, DuplicationsParser parser,
- DuplicationsJsonWriter duplicationsJsonWriter, UserSession userSession, ComponentFinder componentFinder) {
+ public ShowAction(DbClient dbClient, DuplicationsParser parser, ShowResponseBuilder responseBuilder, UserSession userSession, ComponentFinder componentFinder) {
this.dbClient = dbClient;
this.parser = parser;
- this.duplicationsJsonWriter = duplicationsJsonWriter;
+ this.responseBuilder = responseBuilder;
this.userSession = userSession;
this.componentFinder = componentFinder;
}
- void define(WebService.NewController controller) {
+ @Override
+ public void define(WebService.NewController controller) {
WebService.NewAction action = controller.createAction("show")
.setDescription("Get duplications. Require Browse permission on file's project")
.setSince("4.4")
.setHandler(this)
- .setResponseExample(Resources.getResource(this.getClass(), "example-show.json"));
+ .setResponseExample(getClass().getResource("show-example.json"));
+
+ action.setChangelog(
+ new Change("6.5", "The fields 'uuid', 'projectUuid', 'subProjectUuid' are deprecated in the response."));
action
.createParam("key")
@@ -71,21 +72,20 @@ public class ShowAction implements RequestHandler {
action
.createParam("uuid")
- .setDescription("File UUID")
+ .setDeprecatedSince("6.5")
+ .setDescription("File ID. If provided, 'key' must not be provided.")
.setExampleValue("584a89f2-8037-4f7b-b82c-8b45d2d63fb2");
}
@Override
public void handle(Request request, Response response) {
- try (DbSession dbSession = dbClient.openSession(false);
- JsonWriter json = response.newJsonWriter()) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
ComponentDto component = componentFinder.getByUuidOrKey(dbSession, request.param("uuid"), request.param("key"), UUID_AND_KEY);
userSession.checkComponentPermission(UserRole.CODEVIEWER, component);
- json.beginObject();
String duplications = findDataFromComponent(dbSession, component);
List<DuplicationsParser.Block> blocks = parser.parse(component, duplications, dbSession);
- duplicationsJsonWriter.write(blocks, json, dbSession);
- json.endObject();
+
+ writeProtobuf(responseBuilder.build(blocks, dbSession), request, response);
}
}
@@ -95,7 +95,8 @@ public class ShowAction implements RequestHandler {
.setComponentUuid(component.uuid())
.setMetricKey(CoreMetrics.DUPLICATIONS_DATA_KEY)
.build();
- Optional<MeasureDto> measure = dbClient.measureDao().selectSingle(dbSession, query);
- return measure.isPresent() ? measure.get().getData() : null;
+ return dbClient.measureDao().selectSingle(dbSession, query)
+ .map(MeasureDto::getData)
+ .orElse(null);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsJsonWriter.java b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowResponseBuilder.java
index 4dc6513260e..9e61f607405 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsJsonWriter.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowResponseBuilder.java
@@ -24,46 +24,52 @@ import com.google.common.base.Optional;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
-import org.sonar.api.server.ServerSide;
-import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDao;
import org.sonar.db.component.ComponentDto;
+import org.sonarqube.ws.WsDuplications;
+import org.sonarqube.ws.WsDuplications.Block;
+import org.sonarqube.ws.WsDuplications.ShowResponse;
import static com.google.common.collect.Maps.newHashMap;
+import static org.sonar.core.util.Protobuf.setNullable;
-@ServerSide
-public class DuplicationsJsonWriter {
+public class ShowResponseBuilder {
private final ComponentDao componentDao;
- public DuplicationsJsonWriter(ComponentDao componentDao) {
- this.componentDao = componentDao;
+ public ShowResponseBuilder(DbClient dbClient) {
+ this.componentDao = dbClient.componentDao();
}
@VisibleForTesting
- void write(List<DuplicationsParser.Block> blocks, JsonWriter json, DbSession session) {
+ ShowResponseBuilder(ComponentDao componentDao) {
+ this.componentDao = componentDao;
+ }
+
+ ShowResponse build(List<DuplicationsParser.Block> blocks, DbSession session) {
+ ShowResponse.Builder response = ShowResponse.newBuilder();
Map<String, String> refByComponentKey = newHashMap();
- json.name("duplications").beginArray();
- writeDuplications(blocks, refByComponentKey, json);
- json.endArray();
+ blocks.stream()
+ .map(block -> toWsDuplication(block, refByComponentKey))
+ .forEach(response::addDuplications);
+
+ writeFiles(response, refByComponentKey, session);
- json.name("files").beginObject();
- writeFiles(refByComponentKey, json, session);
- json.endObject();
+ return response.build();
}
- private static void writeDuplications(List<DuplicationsParser.Block> blocks, Map<String, String> refByComponentKey, JsonWriter json) {
- for (DuplicationsParser.Block block : blocks) {
- json.beginObject().name("blocks").beginArray();
- for (DuplicationsParser.Duplication duplication : block.getDuplications()) {
- writeDuplication(refByComponentKey, duplication, json);
- }
- json.endArray().endObject();
- }
+ private static WsDuplications.Duplication.Builder toWsDuplication(DuplicationsParser.Block block, Map<String, String> refByComponentKey) {
+ WsDuplications.Duplication.Builder wsDuplication = WsDuplications.Duplication.newBuilder();
+ block.getDuplications().stream()
+ .map(d -> toWsBlock(refByComponentKey, d))
+ .forEach(wsDuplication::addBlocks);
+
+ return wsDuplication;
}
- private static void writeDuplication(Map<String, String> refByComponentKey, DuplicationsParser.Duplication duplication, JsonWriter json) {
+ private static Block.Builder toWsBlock(Map<String, String> refByComponentKey, DuplicationsParser.Duplication duplication) {
String ref = null;
ComponentDto componentDto = duplication.file();
if (componentDto != null) {
@@ -75,52 +81,52 @@ public class DuplicationsJsonWriter {
}
}
- json.beginObject();
- json.prop("from", duplication.from());
- json.prop("size", duplication.size());
- json.prop("_ref", ref);
- json.endObject();
+ Block.Builder block = Block.newBuilder();
+ block.setFrom(duplication.from());
+ block.setSize(duplication.size());
+ setNullable(ref, block::setRef);
+
+ return block;
}
- private void writeFiles(Map<String, String> refByComponentKey, JsonWriter json, DbSession session) {
+ private static WsDuplications.File toWsFile(ComponentDto file, @Nullable ComponentDto project, @Nullable ComponentDto subProject) {
+ WsDuplications.File.Builder wsFile = WsDuplications.File.newBuilder();
+ wsFile.setKey(file.key());
+ wsFile.setUuid(file.uuid());
+ wsFile.setName(file.longName());
+
+ if (project != null) {
+ wsFile.setProject(project.key());
+ wsFile.setProjectUuid(project.uuid());
+ wsFile.setProjectName(project.longName());
+
+ // Do not return sub project if sub project and project are the same
+ boolean displaySubProject = subProject != null && !subProject.uuid().equals(project.uuid());
+ if (displaySubProject) {
+ wsFile.setSubProject(subProject.key());
+ wsFile.setSubProjectUuid(subProject.uuid());
+ wsFile.setSubProjectName(subProject.longName());
+ }
+ }
+
+ return wsFile.build();
+ }
+
+ private void writeFiles(ShowResponse.Builder response, Map<String, String> refByComponentKey, DbSession session) {
Map<String, ComponentDto> projectsByUuid = newHashMap();
- Map<String, ComponentDto> parentProjectsByUuid = newHashMap();
+ Map<String, ComponentDto> parentModulesByUuid = newHashMap();
+ Map<String, WsDuplications.File> filesByRef = response.getMutableFiles();
+
for (Map.Entry<String, String> entry : refByComponentKey.entrySet()) {
String componentKey = entry.getKey();
String ref = entry.getValue();
Optional<ComponentDto> fileOptional = componentDao.selectByKey(session, componentKey);
if (fileOptional.isPresent()) {
ComponentDto file = fileOptional.get();
- json.name(ref).beginObject();
- addFile(json, file);
ComponentDto project = getProject(file.projectUuid(), projectsByUuid, session);
- ComponentDto parentProject = getParentProject(file.getRootUuid(), parentProjectsByUuid, session);
- addProject(json, project, parentProject);
-
- json.endObject();
- }
- }
- }
-
- private static void addFile(JsonWriter json, ComponentDto file) {
- json.prop("key", file.key());
- json.prop("uuid", file.uuid());
- json.prop("name", file.longName());
- }
-
- private static void addProject(JsonWriter json, @Nullable ComponentDto project, @Nullable ComponentDto subProject) {
- if (project != null) {
- json.prop("project", project.key());
- json.prop("projectUuid", project.uuid());
- json.prop("projectName", project.longName());
-
- // Do not return sub project if sub project and project are the same
- boolean displaySubProject = subProject != null && !subProject.uuid().equals(project.uuid());
- if (displaySubProject) {
- json.prop("subProject", subProject.key());
- json.prop("subProjectUuid", subProject.uuid());
- json.prop("subProjectName", subProject.longName());
+ ComponentDto parentModule = getParentProject(file.getRootUuid(), parentModulesByUuid, session);
+ filesByRef.put(ref, toWsFile(file, project, parentModule));
}
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
index 45722f00c6a..abf786f89c0 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
@@ -47,7 +47,7 @@ import org.sonar.server.component.ws.ComponentsWsModule;
import org.sonar.server.debt.DebtModelPluginRepository;
import org.sonar.server.debt.DebtModelXMLExporter;
import org.sonar.server.debt.DebtRulesXMLImporter;
-import org.sonar.server.duplication.ws.DuplicationsJsonWriter;
+import org.sonar.server.duplication.ws.ShowResponseBuilder;
import org.sonar.server.duplication.ws.DuplicationsParser;
import org.sonar.server.duplication.ws.DuplicationsWs;
import org.sonar.server.email.ws.EmailsWsModule;
@@ -430,7 +430,7 @@ public class PlatformLevel4 extends PlatformLevel {
// Duplications
DuplicationsParser.class,
DuplicationsWs.class,
- DuplicationsJsonWriter.class,
+ ShowResponseBuilder.class,
org.sonar.server.duplication.ws.ShowAction.class,
// text
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/duplication/ws/example-show.json b/server/sonar-server/src/main/resources/org/sonar/server/duplication/ws/show-example.json
index 16be68f375b..64f3fb78903 100644
--- a/server/sonar-server/src/main/resources/org/sonar/server/duplication/ws/example-show.json
+++ b/server/sonar-server/src/main/resources/org/sonar/server/duplication/ws/show-example.json
@@ -3,33 +3,47 @@
{
"blocks": [
{
- "from": 94, "size": 101, "_ref": "1"
+ "from": 94,
+ "size": 101,
+ "_ref": "1"
},
{
- "from": 83, "size": 101, "_ref": "2"
+ "from": 83,
+ "size": 101,
+ "_ref": "2"
}
]
},
{
"blocks": [
{
- "from": 38, "size": 40, "_ref": "1"
+ "from": 38,
+ "size": 40,
+ "_ref": "1"
},
{
- "from": 29, "size": 39, "_ref": "2"
+ "from": 29,
+ "size": 39,
+ "_ref": "2"
}
]
},
{
"blocks": [
{
- "from": 148, "size": 24, "_ref": "1"
+ "from": 148,
+ "size": 24,
+ "_ref": "1"
},
{
- "from": 137, "size": 24, "_ref": "2"
+ "from": 137,
+ "size": 24,
+ "_ref": "2"
},
{
- "from": 137, "size": 24, "_ref": "3"
+ "from": 137,
+ "size": 24,
+ "_ref": "3"
}
]
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsWsTest.java
index 6dff1f318f3..8cf850d2547 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsWsTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsWsTest.java
@@ -35,7 +35,7 @@ public class DuplicationsWsTest {
public UserSessionRule userSessionRule = UserSessionRule.standalone();
WsTester tester = new WsTester(new DuplicationsWs(
- new ShowAction(mock(DbClient.class), mock(DuplicationsParser.class), mock(DuplicationsJsonWriter.class), userSessionRule,
+ new ShowAction(mock(DbClient.class), mock(DuplicationsParser.class), mock(ShowResponseBuilder.class), userSessionRule,
mock(ComponentFinder.class))));
@Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java
index 2300f6e3ff2..64774e7cc8f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java
@@ -35,43 +35,46 @@ import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.startup.RegisterMetrics;
import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.ws.WsTester;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.TestResponse;
+import org.sonar.server.ws.WsActionTester;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.SnapshotTesting.newAnalysis;
import static org.sonar.db.measure.MeasureTesting.newMeasureDto;
+import static org.sonar.test.JsonAssert.assertJson;
public class ShowActionTest {
+ private static MetricDto dataMetric = RegisterMetrics.MetricToDto.INSTANCE.apply(CoreMetrics.DUPLICATIONS_DATA);
+
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Rule
public UserSessionRule userSessionRule = UserSessionRule.standalone();
+
@Rule
public DbTester db = DbTester.create();
-
private DuplicationsParser parser = new DuplicationsParser(db.getDbClient().componentDao());
- private DuplicationsJsonWriter duplicationsJsonWriter = new DuplicationsJsonWriter(db.getDbClient().componentDao());
- private WsTester tester;
- private MetricDto dataMetric = RegisterMetrics.MetricToDto.INSTANCE.apply(CoreMetrics.DUPLICATIONS_DATA);
+ private ShowResponseBuilder showResponseBuilder = new ShowResponseBuilder(db.getDbClient());
+
+ private WsActionTester ws = new WsActionTester(new ShowAction(db.getDbClient(), parser, showResponseBuilder, userSessionRule, TestComponentFinder.from(db)));
@Before
public void setUp() {
- tester = new WsTester(new DuplicationsWs(new ShowAction(db.getDbClient(), parser, duplicationsJsonWriter, userSessionRule, TestComponentFinder.from(db))));
-
db.getDbClient().metricDao().insert(db.getSession(), dataMetric);
db.commit();
}
@Test
public void get_duplications_by_file_key() throws Exception {
- WsTester.TestRequest request = newBaseRequest();
+ TestRequest request = newBaseRequest();
verifyCallToFileWithDuplications(file -> request.setParam("key", file.key()));
}
@Test
public void get_duplications_by_file_id() throws Exception {
- WsTester.TestRequest request = newBaseRequest();
+ TestRequest request = newBaseRequest();
verifyCallToFileWithDuplications(file -> request.setParam("uuid", file.uuid()));
}
@@ -83,23 +86,23 @@ public class ShowActionTest {
userSessionRule.addProjectPermission(UserRole.CODEVIEWER, project);
- WsTester.Result result = newBaseRequest().setParam("key", file.key()).execute();
+ TestResponse result = newBaseRequest().setParam("key", file.key()).execute();
- result.assertJson("{\n" +
+ assertJson(result.getInput()).isSimilarTo("{\n" +
" \"duplications\": [],\n" +
" \"files\": {}\n" +
"}");
}
@Test
- public void return_404_if_file_does_not_exist() throws Exception {
+ public void fail_if_file_does_not_exist() throws Exception {
expectedException.expect(NotFoundException.class);
newBaseRequest().setParam("key", "missing").execute();
}
@Test
- public void return_403_if_user_is_not_allowed_to_access_project() throws Exception {
+ public void fail_if_user_is_not_allowed_to_access_project() throws Exception {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
@@ -108,11 +111,19 @@ public class ShowActionTest {
newBaseRequest().setParam("key", file.key()).execute();
}
- private WsTester.TestRequest newBaseRequest() {
- return tester.newGetRequest("api/duplications", "show");
+ @Test
+ public void fail_if_no_parameter_provided() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Either 'uuid' or 'key' must be provided, not both");
+
+ newBaseRequest().execute();
+ }
+
+ private TestRequest newBaseRequest() {
+ return ws.newRequest();
}
- private void verifyCallToFileWithDuplications(Function<ComponentDto, WsTester.TestRequest> requestFactory) throws Exception {
+ private void verifyCallToFileWithDuplications(Function<ComponentDto, TestRequest> requestFactory) throws Exception {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project).setKey("foo.js"));
SnapshotDto snapshot = db.components().insertSnapshot(newAnalysis(project));
@@ -127,10 +138,10 @@ public class ShowActionTest {
userSessionRule.addProjectPermission(UserRole.CODEVIEWER, project);
- WsTester.TestRequest request = requestFactory.apply(file);
- WsTester.Result result = request.execute();
+ TestRequest request = requestFactory.apply(file);
+ TestResponse result = request.execute();
- result.assertJson("{\"duplications\":[" +
+ assertJson(result.getInput()).isSimilarTo("{\"duplications\":[" +
"{\"blocks\":[{\"from\":20,\"size\":5,\"_ref\":\"1\"},{\"from\":31,\"size\":5,\"_ref\":\"1\"}]}]," +
"\"files\":{\"1\":{\"key\":\"foo.js\",\"uuid\":\"" + file.uuid() + "\"}}}");
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsJsonWriterTest.java b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowResponseBuilderTest.java
index d51dcc4397c..8dfc52367bf 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsJsonWriterTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowResponseBuilderTest.java
@@ -19,67 +19,63 @@
*/
package org.sonar.server.duplication.ws;
-import com.google.common.base.Optional;
import java.io.StringWriter;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.core.util.ProtobufJsonFormat;
import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDao;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.organization.OrganizationTesting;
import org.sonar.test.JsonAssert;
import static com.google.common.collect.Lists.newArrayList;
-import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
-@RunWith(MockitoJUnitRunner.class)
-public class DuplicationsJsonWriterTest {
+public class ShowResponseBuilderTest {
- @Mock
- ComponentDao componentDao;
+ @Rule
+ public DbTester db = DbTester.create();
+ private DbSession dbSession = db.getSession();
+ private ComponentDao componentDao = spy(db.getDbClient().componentDao());
- @Mock
- DbSession session;
+ private ComponentDto project;
+ private OrganizationDto organization = OrganizationTesting.newOrganizationDto();
- DuplicationsJsonWriter writer;
-
- ComponentDto project;
+ private ShowResponseBuilder underTest = new ShowResponseBuilder(componentDao);
@Before
public void setUp() {
- project = ComponentTesting.newPrivateProjectDto(OrganizationTesting.newOrganizationDto())
- .setId(1L)
+ project = newPrivateProjectDto(organization)
.setName("SonarQube")
.setLongName("SonarQube")
.setKey("org.codehaus.sonar:sonar");
-
- writer = new DuplicationsJsonWriter(componentDao);
+ db.components().insertComponent(project);
}
@Test
public void write_duplications() {
String key1 = "org.codehaus.sonar:sonar-ws-client:src/main/java/org/sonar/wsclient/services/PropertyDeleteQuery.java";
- ComponentDto file1 = ComponentTesting.newFileDto(project, null).setId(10L).setKey(key1).setLongName("PropertyDeleteQuery").setRootUuid("uuid_5");
+ ComponentDto file1 = ComponentTesting.newFileDto(project, null).setKey(key1).setLongName("PropertyDeleteQuery").setRootUuid("uuid_5");
String key2 = "org.codehaus.sonar:sonar-ws-client:src/main/java/org/sonar/wsclient/services/PropertyUpdateQuery.java";
- ComponentDto file2 = ComponentTesting.newFileDto(project, null).setId(11L).setQualifier("FIL").setKey(key2).setLongName("PropertyUpdateQuery").setRootUuid("uuid_5");
+ ComponentDto file2 = ComponentTesting.newFileDto(project, null).setQualifier("FIL").setKey(key2).setLongName("PropertyUpdateQuery").setRootUuid("uuid_5");
+ ComponentDto project2 = db.components().insertPrivateProject(organization, p -> p.setUuid("uuid_5").setKey("org.codehaus.sonar:sonar-ws-client")
+ .setLongName("SonarQube :: Web Service Client"));
- when(componentDao.selectByKey(session, key1)).thenReturn(Optional.of(file1));
- when(componentDao.selectByKey(session, key2)).thenReturn(Optional.of(file2));
- when(componentDao.selectByUuid(session, "uuid_5")).thenReturn(Optional.of(
- new ComponentDto().setUuid("uuid_5").setKey("org.codehaus.sonar:sonar-ws-client").setLongName("SonarQube :: Web Service Client")));
- when(componentDao.selectByUuid(session, project.uuid())).thenReturn(Optional.of(project));
+ db.components().insertComponent(file1);
+ db.components().insertComponent(file2);
List<DuplicationsParser.Block> blocks = newArrayList();
blocks.add(new DuplicationsParser.Block(newArrayList(
@@ -121,10 +117,10 @@ public class DuplicationsJsonWriterTest {
" }" +
"}");
- verify(componentDao, times(2)).selectByKey(eq(session), anyString());
+ verify(componentDao, times(2)).selectByKey(eq(dbSession), anyString());
// Verify call to dao is cached when searching for project / sub project
- verify(componentDao, times(1)).selectByUuid(eq(session), eq(project.uuid()));
- verify(componentDao, times(1)).selectByUuid(eq(session), eq("uuid_5"));
+ verify(componentDao, times(1)).selectByUuid(eq(dbSession), eq(project.uuid()));
+ verify(componentDao, times(1)).selectByUuid(eq(dbSession), eq("uuid_5"));
}
@Test
@@ -134,10 +130,8 @@ public class DuplicationsJsonWriterTest {
String key2 = "org.codehaus.sonar:sonar-ws-client:src/main/java/org/sonar/wsclient/services/PropertyUpdateQuery.java";
ComponentDto file2 = ComponentTesting.newFileDto(project, null).setId(11L).setKey(key2).setLongName("PropertyUpdateQuery");
- when(componentDao.selectByKey(session, key1)).thenReturn(Optional.of(file1));
- when(componentDao.selectByKey(session, key2)).thenReturn(Optional.of(file2));
- when(componentDao.selectById(eq(session), anyLong())).thenReturn(Optional.<ComponentDto>absent());
- when(componentDao.selectByUuid(session, project.uuid())).thenReturn(Optional.of(project));
+ db.components().insertComponent(file1);
+ db.components().insertComponent(file2);
List<DuplicationsParser.Block> blocks = newArrayList();
blocks.add(new DuplicationsParser.Block(newArrayList(
@@ -180,10 +174,7 @@ public class DuplicationsJsonWriterTest {
public void write_duplications_with_a_removed_component() {
String key1 = "org.codehaus.sonar:sonar-ws-client:src/main/java/org/sonar/wsclient/services/PropertyDeleteQuery.java";
ComponentDto file1 = ComponentTesting.newFileDto(project, null).setId(10L).setKey(key1).setLongName("PropertyDeleteQuery");
-
- when(componentDao.selectByKey(session, key1)).thenReturn(Optional.of(file1));
- when(componentDao.selectByUuid(session, project.uuid())).thenReturn(Optional.of(project));
- when(componentDao.selectById(eq(session), anyLong())).thenReturn(Optional.<ComponentDto>absent());
+ db.components().insertComponent(file1);
List<DuplicationsParser.Block> blocks = newArrayList();
@@ -220,15 +211,13 @@ public class DuplicationsJsonWriterTest {
@Test
public void write_nothing_when_no_data() {
- test(Collections.<DuplicationsParser.Block>emptyList(), "{\"duplications\": [], \"files\": {}}");
+ test(Collections.emptyList(), "{\"duplications\": [], \"files\": {}}");
}
private void test(List<DuplicationsParser.Block> blocks, String expected) {
StringWriter output = new StringWriter();
JsonWriter jsonWriter = JsonWriter.of(output);
- jsonWriter.beginObject();
- writer.write(blocks, jsonWriter, session);
- jsonWriter.endObject();
+ ProtobufJsonFormat.write(underTest.build(blocks, dbSession), jsonWriter);
JsonAssert.assertJson(output.toString()).isSimilarTo(expected);
}
diff --git a/sonar-ws/src/main/protobuf/ws-duplications.proto b/sonar-ws/src/main/protobuf/ws-duplications.proto
new file mode 100644
index 00000000000..f8b292ec4e8
--- /dev/null
+++ b/sonar-ws/src/main/protobuf/ws-duplications.proto
@@ -0,0 +1,54 @@
+// SonarQube, open source software quality management tool.
+// Copyright (C) 2008-2016 SonarSource
+// mailto:contact AT sonarsource DOT com
+//
+// SonarQube 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.
+//
+// SonarQube 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.
+
+syntax = "proto3";
+
+package sonarqube.ws.duplication;
+
+option java_package = "org.sonarqube.ws";
+option java_outer_classname = "WsDuplications";
+option optimize_for = SPEED;
+
+// WS api/duplications/show
+message ShowResponse {
+ repeated Duplication duplications = 1;
+ map<string,File> files = 2;
+
+}
+
+message Duplication {
+ repeated Block blocks = 1;
+}
+
+message Block {
+ int32 from = 1;
+ int32 size = 2;
+ string _ref = 3;
+}
+
+message File {
+ string key = 1;
+ string name = 2;
+ string uuid = 3;
+ string project = 4;
+ string projectUuid = 5;
+ string projectName = 6;
+ string subProject = 7;
+ string subProjectUuid = 8;
+ string subProjectName = 9;
+}