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;
@ClassRule
public static final IssueRule issueRule = IssueRule.from(orchestrator);
+ private static WsClient adminWsClient;
+
@BeforeClass
public static void analyzeProjects() {
orchestrator.resetData();
// 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
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",
"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");
}
}
+++ /dev/null
-/*
- * 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 com.google.common.annotations.VisibleForTesting;
-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.DbSession;
-import org.sonar.db.component.ComponentDao;
-import org.sonar.db.component.ComponentDto;
-
-import static com.google.common.collect.Maps.newHashMap;
-
-@ServerSide
-public class DuplicationsJsonWriter {
-
- private final ComponentDao componentDao;
-
- public DuplicationsJsonWriter(ComponentDao componentDao) {
- this.componentDao = componentDao;
- }
-
- @VisibleForTesting
- void write(List<DuplicationsParser.Block> blocks, JsonWriter json, DbSession session) {
- Map<String, String> refByComponentKey = newHashMap();
- json.name("duplications").beginArray();
- writeDuplications(blocks, refByComponentKey, json);
- json.endArray();
-
- json.name("files").beginObject();
- writeFiles(refByComponentKey, json, session);
- json.endObject();
- }
-
- 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 void writeDuplication(Map<String, String> refByComponentKey, DuplicationsParser.Duplication duplication, JsonWriter json) {
- String ref = null;
- ComponentDto componentDto = duplication.file();
- if (componentDto != null) {
- String componentKey = componentDto.key();
- ref = refByComponentKey.get(componentKey);
- if (ref == null) {
- ref = Integer.toString(refByComponentKey.size() + 1);
- refByComponentKey.put(componentKey, ref);
- }
- }
-
- json.beginObject();
- json.prop("from", duplication.from());
- json.prop("size", duplication.size());
- json.prop("_ref", ref);
- json.endObject();
- }
-
- private void writeFiles(Map<String, String> refByComponentKey, JsonWriter json, DbSession session) {
- Map<String, ComponentDto> projectsByUuid = newHashMap();
- Map<String, ComponentDto> parentProjectsByUuid = newHashMap();
- 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());
- }
- }
- }
-
- private ComponentDto getProject(String projectUuid, Map<String, ComponentDto> projectsByUuid, DbSession session) {
- ComponentDto project = projectsByUuid.get(projectUuid);
- if (project == null) {
- Optional<ComponentDto> projectOptional = componentDao.selectByUuid(session, projectUuid);
- if (projectOptional.isPresent()) {
- project = projectOptional.get();
- projectsByUuid.put(project.uuid(), project);
- }
- }
- return project;
- }
-
- private ComponentDto getParentProject(String rootUuid, Map<String, ComponentDto> subProjectsByUuid, DbSession session) {
- ComponentDto project = subProjectsByUuid.get(rootUuid);
- if (project == null) {
- Optional<ComponentDto> projectOptional = componentDao.selectByUuid(session, rootUuid);
- if (projectOptional.isPresent()) {
- project = projectOptional.get();
- subProjectsByUuid.put(project.uuid(), project);
- }
- }
- return project;
- }
-
-}
--- /dev/null
+/*
+ * 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
+}
*/
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;
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")
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);
}
}
.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);
}
}
--- /dev/null
+/*
+ * 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 com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nullable;
+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;
+
+public class ShowResponseBuilder {
+
+ private final ComponentDao componentDao;
+
+ public ShowResponseBuilder(DbClient dbClient) {
+ this.componentDao = dbClient.componentDao();
+ }
+
+ @VisibleForTesting
+ ShowResponseBuilder(ComponentDao componentDao) {
+ this.componentDao = componentDao;
+ }
+
+ ShowResponse build(List<DuplicationsParser.Block> blocks, DbSession session) {
+ ShowResponse.Builder response = ShowResponse.newBuilder();
+ Map<String, String> refByComponentKey = newHashMap();
+ blocks.stream()
+ .map(block -> toWsDuplication(block, refByComponentKey))
+ .forEach(response::addDuplications);
+
+ writeFiles(response, refByComponentKey, session);
+
+ return response.build();
+ }
+
+ 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 Block.Builder toWsBlock(Map<String, String> refByComponentKey, DuplicationsParser.Duplication duplication) {
+ String ref = null;
+ ComponentDto componentDto = duplication.file();
+ if (componentDto != null) {
+ String componentKey = componentDto.key();
+ ref = refByComponentKey.get(componentKey);
+ if (ref == null) {
+ ref = Integer.toString(refByComponentKey.size() + 1);
+ refByComponentKey.put(componentKey, ref);
+ }
+ }
+
+ Block.Builder block = Block.newBuilder();
+ block.setFrom(duplication.from());
+ block.setSize(duplication.size());
+ setNullable(ref, block::setRef);
+
+ return block;
+ }
+
+ 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> 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();
+
+ ComponentDto project = getProject(file.projectUuid(), projectsByUuid, session);
+ ComponentDto parentModule = getParentProject(file.getRootUuid(), parentModulesByUuid, session);
+ filesByRef.put(ref, toWsFile(file, project, parentModule));
+ }
+ }
+ }
+
+ private ComponentDto getProject(String projectUuid, Map<String, ComponentDto> projectsByUuid, DbSession session) {
+ ComponentDto project = projectsByUuid.get(projectUuid);
+ if (project == null) {
+ Optional<ComponentDto> projectOptional = componentDao.selectByUuid(session, projectUuid);
+ if (projectOptional.isPresent()) {
+ project = projectOptional.get();
+ projectsByUuid.put(project.uuid(), project);
+ }
+ }
+ return project;
+ }
+
+ private ComponentDto getParentProject(String rootUuid, Map<String, ComponentDto> subProjectsByUuid, DbSession session) {
+ ComponentDto project = subProjectsByUuid.get(rootUuid);
+ if (project == null) {
+ Optional<ComponentDto> projectOptional = componentDao.selectByUuid(session, rootUuid);
+ if (projectOptional.isPresent()) {
+ project = projectOptional.get();
+ subProjectsByUuid.put(project.uuid(), project);
+ }
+ }
+ return project;
+ }
+
+}
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;
// Duplications
DuplicationsParser.class,
DuplicationsWs.class,
- DuplicationsJsonWriter.class,
+ ShowResponseBuilder.class,
org.sonar.server.duplication.ws.ShowAction.class,
// text
+++ /dev/null
-{
- "duplications": [
- {
- "blocks": [
- {
- "from": 94, "size": 101, "_ref": "1"
- },
- {
- "from": 83, "size": 101, "_ref": "2"
- }
- ]
- },
- {
- "blocks": [
- {
- "from": 38, "size": 40, "_ref": "1"
- },
- {
- "from": 29, "size": 39, "_ref": "2"
- }
- ]
- },
- {
- "blocks": [
- {
- "from": 148, "size": 24, "_ref": "1"
- },
- {
- "from": 137, "size": 24, "_ref": "2"
- },
- {
- "from": 137, "size": 24, "_ref": "3"
- }
- ]
- }
- ],
- "files": {
- "1": {
- "key": "org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/utils/command/CommandExecutor.java",
- "name": "CommandExecutor",
- "projectName": "SonarQube"
- },
- "2": {
- "key": "com.sonarsource.orchestrator:sonar-orchestrator:src/main/java/com/sonar/orchestrator/util/CommandExecutor.java",
- "name": "CommandExecutor",
- "projectName": "SonarSource :: Orchestrator"
- },
- "3": {
- "key": "org.codehaus.sonar.runner:sonar-runner-api:src/main/java/org/sonar/runner/api/CommandExecutor.java",
- "name": "CommandExecutor",
- "projectName": "SonarSource Runner"
- }
- }
-}
--- /dev/null
+{
+ "duplications": [
+ {
+ "blocks": [
+ {
+ "from": 94,
+ "size": 101,
+ "_ref": "1"
+ },
+ {
+ "from": 83,
+ "size": 101,
+ "_ref": "2"
+ }
+ ]
+ },
+ {
+ "blocks": [
+ {
+ "from": 38,
+ "size": 40,
+ "_ref": "1"
+ },
+ {
+ "from": 29,
+ "size": 39,
+ "_ref": "2"
+ }
+ ]
+ },
+ {
+ "blocks": [
+ {
+ "from": 148,
+ "size": 24,
+ "_ref": "1"
+ },
+ {
+ "from": 137,
+ "size": 24,
+ "_ref": "2"
+ },
+ {
+ "from": 137,
+ "size": 24,
+ "_ref": "3"
+ }
+ ]
+ }
+ ],
+ "files": {
+ "1": {
+ "key": "org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/utils/command/CommandExecutor.java",
+ "name": "CommandExecutor",
+ "projectName": "SonarQube"
+ },
+ "2": {
+ "key": "com.sonarsource.orchestrator:sonar-orchestrator:src/main/java/com/sonar/orchestrator/util/CommandExecutor.java",
+ "name": "CommandExecutor",
+ "projectName": "SonarSource :: Orchestrator"
+ },
+ "3": {
+ "key": "org.codehaus.sonar.runner:sonar-runner-api:src/main/java/org/sonar/runner/api/CommandExecutor.java",
+ "name": "CommandExecutor",
+ "projectName": "SonarSource Runner"
+ }
+ }
+}
+++ /dev/null
-/*
- * 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 com.google.common.base.Optional;
-import java.io.StringWriter;
-import java.util.Collections;
-import java.util.List;
-import org.junit.Before;
-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.db.DbSession;
-import org.sonar.db.component.ComponentDao;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentTesting;
-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.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@RunWith(MockitoJUnitRunner.class)
-public class DuplicationsJsonWriterTest {
-
- @Mock
- ComponentDao componentDao;
-
- @Mock
- DbSession session;
-
- DuplicationsJsonWriter writer;
-
- ComponentDto project;
-
- @Before
- public void setUp() {
- project = ComponentTesting.newPrivateProjectDto(OrganizationTesting.newOrganizationDto())
- .setId(1L)
- .setName("SonarQube")
- .setLongName("SonarQube")
- .setKey("org.codehaus.sonar:sonar");
-
- writer = new DuplicationsJsonWriter(componentDao);
- }
-
- @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");
- 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");
-
- 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));
-
- List<DuplicationsParser.Block> blocks = newArrayList();
- blocks.add(new DuplicationsParser.Block(newArrayList(
- new DuplicationsParser.Duplication(file1, 57, 12),
- new DuplicationsParser.Duplication(file2, 73, 12)
- )));
-
- test(blocks,
- "{\n" +
- " \"duplications\": [\n" +
- " {\n" +
- " \"blocks\": [\n" +
- " {\n" +
- " \"from\": 57, \"size\": 12, \"_ref\": \"1\"\n" +
- " },\n" +
- " {\n" +
- " \"from\": 73, \"size\": 12, \"_ref\": \"2\"\n" +
- " }\n" +
- " ]\n" +
- " }," +
- " ],\n" +
- " \"files\": {\n" +
- " \"1\": {\n" +
- " \"key\": \"org.codehaus.sonar:sonar-ws-client:src/main/java/org/sonar/wsclient/services/PropertyDeleteQuery.java\",\n" +
- " \"name\": \"PropertyDeleteQuery\",\n" +
- " \"project\": \"org.codehaus.sonar:sonar\",\n" +
- " \"projectName\": \"SonarQube\",\n" +
- " \"subProject\": \"org.codehaus.sonar:sonar-ws-client\",\n" +
- " \"subProjectName\": \"SonarQube :: Web Service Client\"\n" +
- " },\n" +
- " \"2\": {\n" +
- " \"key\": \"org.codehaus.sonar:sonar-ws-client:src/main/java/org/sonar/wsclient/services/PropertyUpdateQuery.java\",\n" +
- " \"name\": \"PropertyUpdateQuery\",\n" +
- " \"project\": \"org.codehaus.sonar:sonar\",\n" +
- " \"projectName\": \"SonarQube\",\n" +
- " \"subProject\": \"org.codehaus.sonar:sonar-ws-client\",\n" +
- " \"subProjectName\": \"SonarQube :: Web Service Client\"\n" +
- " }\n" +
- " }" +
- "}");
-
- verify(componentDao, times(2)).selectByKey(eq(session), 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"));
- }
-
- @Test
- public void write_duplications_without_sub_project() {
- 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");
- 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));
-
- List<DuplicationsParser.Block> blocks = newArrayList();
- blocks.add(new DuplicationsParser.Block(newArrayList(
- new DuplicationsParser.Duplication(file1, 57, 12),
- new DuplicationsParser.Duplication(file2, 73, 12)
- )));
-
- test(blocks,
- "{\n" +
- " \"duplications\": [\n" +
- " {\n" +
- " \"blocks\": [\n" +
- " {\n" +
- " \"from\": 57, \"size\": 12, \"_ref\": \"1\"\n" +
- " },\n" +
- " {\n" +
- " \"from\": 73, \"size\": 12, \"_ref\": \"2\"\n" +
- " }\n" +
- " ]\n" +
- " }," +
- " ],\n" +
- " \"files\": {\n" +
- " \"1\": {\n" +
- " \"key\": \"org.codehaus.sonar:sonar-ws-client:src/main/java/org/sonar/wsclient/services/PropertyDeleteQuery.java\",\n" +
- " \"name\": \"PropertyDeleteQuery\",\n" +
- " \"project\": \"org.codehaus.sonar:sonar\",\n" +
- " \"projectName\": \"SonarQube\"\n" +
- " },\n" +
- " \"2\": {\n" +
- " \"key\": \"org.codehaus.sonar:sonar-ws-client:src/main/java/org/sonar/wsclient/services/PropertyUpdateQuery.java\",\n" +
- " \"name\": \"PropertyUpdateQuery\",\n" +
- " \"project\": \"org.codehaus.sonar:sonar\",\n" +
- " \"projectName\": \"SonarQube\"\n" +
- " }\n" +
- " }" +
- "}");
- }
-
- @Test
- 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());
-
- List<DuplicationsParser.Block> blocks = newArrayList();
-
- blocks.add(new DuplicationsParser.Block(newArrayList(
- new DuplicationsParser.Duplication(file1, 57, 12),
- // Duplication on a removed file
- new DuplicationsParser.Duplication(null, 73, 12)
- )));
-
- test(blocks,
- "{\n" +
- " \"duplications\": [\n" +
- " {\n" +
- " \"blocks\": [\n" +
- " {\n" +
- " \"from\": 57, \"size\": 12, \"_ref\": \"1\"\n" +
- " },\n" +
- " {\n" +
- " \"from\": 73, \"size\": 12\n" +
- " }\n" +
- " ]\n" +
- " }," +
- " ],\n" +
- " \"files\": {\n" +
- " \"1\": {\n" +
- " \"key\": \"org.codehaus.sonar:sonar-ws-client:src/main/java/org/sonar/wsclient/services/PropertyDeleteQuery.java\",\n" +
- " \"name\": \"PropertyDeleteQuery\",\n" +
- " \"project\": \"org.codehaus.sonar:sonar\",\n" +
- " \"projectName\": \"SonarQube\"\n" +
- " }\n" +
- " }" +
- "}");
- }
-
- @Test
- public void write_nothing_when_no_data() {
- test(Collections.<DuplicationsParser.Block>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();
- JsonAssert.assertJson(output.toString()).isSimilarTo(expected);
- }
-
-}
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
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()));
}
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));
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));
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() + "\"}}}");
}
--- /dev/null
+/*
+ * 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 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.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.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.sonar.db.component.ComponentTesting.newPrivateProjectDto;
+
+public class ShowResponseBuilderTest {
+
+ @Rule
+ public DbTester db = DbTester.create();
+ private DbSession dbSession = db.getSession();
+ private ComponentDao componentDao = spy(db.getDbClient().componentDao());
+
+ private ComponentDto project;
+ private OrganizationDto organization = OrganizationTesting.newOrganizationDto();
+
+ private ShowResponseBuilder underTest = new ShowResponseBuilder(componentDao);
+
+ @Before
+ public void setUp() {
+ project = newPrivateProjectDto(organization)
+ .setName("SonarQube")
+ .setLongName("SonarQube")
+ .setKey("org.codehaus.sonar:sonar");
+ 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).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).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"));
+
+ db.components().insertComponent(file1);
+ db.components().insertComponent(file2);
+
+ List<DuplicationsParser.Block> blocks = newArrayList();
+ blocks.add(new DuplicationsParser.Block(newArrayList(
+ new DuplicationsParser.Duplication(file1, 57, 12),
+ new DuplicationsParser.Duplication(file2, 73, 12)
+ )));
+
+ test(blocks,
+ "{\n" +
+ " \"duplications\": [\n" +
+ " {\n" +
+ " \"blocks\": [\n" +
+ " {\n" +
+ " \"from\": 57, \"size\": 12, \"_ref\": \"1\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"from\": 73, \"size\": 12, \"_ref\": \"2\"\n" +
+ " }\n" +
+ " ]\n" +
+ " }," +
+ " ],\n" +
+ " \"files\": {\n" +
+ " \"1\": {\n" +
+ " \"key\": \"org.codehaus.sonar:sonar-ws-client:src/main/java/org/sonar/wsclient/services/PropertyDeleteQuery.java\",\n" +
+ " \"name\": \"PropertyDeleteQuery\",\n" +
+ " \"project\": \"org.codehaus.sonar:sonar\",\n" +
+ " \"projectName\": \"SonarQube\",\n" +
+ " \"subProject\": \"org.codehaus.sonar:sonar-ws-client\",\n" +
+ " \"subProjectName\": \"SonarQube :: Web Service Client\"\n" +
+ " },\n" +
+ " \"2\": {\n" +
+ " \"key\": \"org.codehaus.sonar:sonar-ws-client:src/main/java/org/sonar/wsclient/services/PropertyUpdateQuery.java\",\n" +
+ " \"name\": \"PropertyUpdateQuery\",\n" +
+ " \"project\": \"org.codehaus.sonar:sonar\",\n" +
+ " \"projectName\": \"SonarQube\",\n" +
+ " \"subProject\": \"org.codehaus.sonar:sonar-ws-client\",\n" +
+ " \"subProjectName\": \"SonarQube :: Web Service Client\"\n" +
+ " }\n" +
+ " }" +
+ "}");
+
+ 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(dbSession), eq(project.uuid()));
+ verify(componentDao, times(1)).selectByUuid(eq(dbSession), eq("uuid_5"));
+ }
+
+ @Test
+ public void write_duplications_without_sub_project() {
+ 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");
+ 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");
+
+ db.components().insertComponent(file1);
+ db.components().insertComponent(file2);
+
+ List<DuplicationsParser.Block> blocks = newArrayList();
+ blocks.add(new DuplicationsParser.Block(newArrayList(
+ new DuplicationsParser.Duplication(file1, 57, 12),
+ new DuplicationsParser.Duplication(file2, 73, 12)
+ )));
+
+ test(blocks,
+ "{\n" +
+ " \"duplications\": [\n" +
+ " {\n" +
+ " \"blocks\": [\n" +
+ " {\n" +
+ " \"from\": 57, \"size\": 12, \"_ref\": \"1\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"from\": 73, \"size\": 12, \"_ref\": \"2\"\n" +
+ " }\n" +
+ " ]\n" +
+ " }," +
+ " ],\n" +
+ " \"files\": {\n" +
+ " \"1\": {\n" +
+ " \"key\": \"org.codehaus.sonar:sonar-ws-client:src/main/java/org/sonar/wsclient/services/PropertyDeleteQuery.java\",\n" +
+ " \"name\": \"PropertyDeleteQuery\",\n" +
+ " \"project\": \"org.codehaus.sonar:sonar\",\n" +
+ " \"projectName\": \"SonarQube\"\n" +
+ " },\n" +
+ " \"2\": {\n" +
+ " \"key\": \"org.codehaus.sonar:sonar-ws-client:src/main/java/org/sonar/wsclient/services/PropertyUpdateQuery.java\",\n" +
+ " \"name\": \"PropertyUpdateQuery\",\n" +
+ " \"project\": \"org.codehaus.sonar:sonar\",\n" +
+ " \"projectName\": \"SonarQube\"\n" +
+ " }\n" +
+ " }" +
+ "}");
+ }
+
+ @Test
+ 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");
+ db.components().insertComponent(file1);
+
+ List<DuplicationsParser.Block> blocks = newArrayList();
+
+ blocks.add(new DuplicationsParser.Block(newArrayList(
+ new DuplicationsParser.Duplication(file1, 57, 12),
+ // Duplication on a removed file
+ new DuplicationsParser.Duplication(null, 73, 12)
+ )));
+
+ test(blocks,
+ "{\n" +
+ " \"duplications\": [\n" +
+ " {\n" +
+ " \"blocks\": [\n" +
+ " {\n" +
+ " \"from\": 57, \"size\": 12, \"_ref\": \"1\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"from\": 73, \"size\": 12\n" +
+ " }\n" +
+ " ]\n" +
+ " }," +
+ " ],\n" +
+ " \"files\": {\n" +
+ " \"1\": {\n" +
+ " \"key\": \"org.codehaus.sonar:sonar-ws-client:src/main/java/org/sonar/wsclient/services/PropertyDeleteQuery.java\",\n" +
+ " \"name\": \"PropertyDeleteQuery\",\n" +
+ " \"project\": \"org.codehaus.sonar:sonar\",\n" +
+ " \"projectName\": \"SonarQube\"\n" +
+ " }\n" +
+ " }" +
+ "}");
+ }
+
+ @Test
+ public void write_nothing_when_no_data() {
+ test(Collections.emptyList(), "{\"duplications\": [], \"files\": {}}");
+ }
+
+ private void test(List<DuplicationsParser.Block> blocks, String expected) {
+ StringWriter output = new StringWriter();
+ JsonWriter jsonWriter = JsonWriter.of(output);
+ ProtobufJsonFormat.write(underTest.build(blocks, dbSession), jsonWriter);
+ JsonAssert.assertJson(output.toString()).isSimilarTo(expected);
+ }
+
+}
--- /dev/null
+// 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;
+}