aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--it/it-tests/src/test/java/it/Category4Suite.java6
-rw-r--r--it/it-tests/src/test/java/it/component/ComponentsWsTest.java70
-rw-r--r--it/it-tests/src/test/java/it/component/ProjectSearchTest.java (renamed from it/it-tests/src/test/java/it/componentSearch/ProjectSearchTest.java)2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWsModule.java1
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowAction.java160
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowData.java104
-rw-r--r--server/sonar-server/src/main/resources/org/sonar/server/component/ws/show-example.json25
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsModuleTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/component/ws/ShowActionTest.java169
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java20
-rw-r--r--sonar-db/src/main/java/org/sonar/db/component/ComponentDtoFunctions.java38
-rw-r--r--sonar-db/src/main/java/org/sonar/db/component/SnapshotDao.java13
-rw-r--r--sonar-db/src/main/java/org/sonar/db/component/SnapshotDtoFunctions.java54
-rw-r--r--sonar-db/src/main/java/org/sonar/db/component/SnapshotMapper.java4
-rw-r--r--sonar-db/src/main/resources/org/sonar/db/component/SnapshotMapper.xml11
-rw-r--r--sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java1
-rw-r--r--sonar-db/src/test/java/org/sonar/db/component/SnapshotDaoTest.java18
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java34
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java3
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/component/ShowWsRequest.java49
-rw-r--r--sonar-ws/src/main/protobuf/ws-components.proto7
21 files changed, 763 insertions, 28 deletions
diff --git a/it/it-tests/src/test/java/it/Category4Suite.java b/it/it-tests/src/test/java/it/Category4Suite.java
index c1460fc9218..620f4379cab 100644
--- a/it/it-tests/src/test/java/it/Category4Suite.java
+++ b/it/it-tests/src/test/java/it/Category4Suite.java
@@ -22,7 +22,8 @@ package it;
import com.sonar.orchestrator.Orchestrator;
import it.analysisExclusion.FileExclusionsTest;
import it.analysisExclusion.IssueExclusionsTest;
-import it.componentSearch.ProjectSearchTest;
+import it.component.ComponentsWsTest;
+import it.component.ProjectSearchTest;
import it.dbCleaner.PurgeTest;
import it.duplication.CrossProjectDuplicationsOnRemoveFileTest;
import it.duplication.CrossProjectDuplicationsTest;
@@ -50,8 +51,9 @@ import static util.ItUtils.xooPlugin;
// user
ForceAuthenticationTest.class,
FavouriteTest.class,
- // project search
+ // component search
ProjectSearchTest.class,
+ ComponentsWsTest.class,
// update center
UpdateCenterTest.class,
// analysis exclusion
diff --git a/it/it-tests/src/test/java/it/component/ComponentsWsTest.java b/it/it-tests/src/test/java/it/component/ComponentsWsTest.java
new file mode 100644
index 00000000000..3a518d41f04
--- /dev/null
+++ b/it/it-tests/src/test/java/it/component/ComponentsWsTest.java
@@ -0,0 +1,70 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.
+ */
+
+package it.component;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarRunner;
+import it.Category4Suite;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonarqube.ws.WsComponents;
+import org.sonarqube.ws.client.WsClient;
+import org.sonarqube.ws.client.component.SearchWsRequest;
+import org.sonarqube.ws.client.component.ShowWsRequest;
+import util.ItUtils;
+
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static util.ItUtils.projectDir;
+
+public class ComponentsWsTest {
+ @ClassRule
+ public static final Orchestrator orchestrator = Category4Suite.ORCHESTRATOR;
+ private static final String FILE_KEY = "sample:src/main/xoo/sample/Sample.xoo";
+ WsClient wsClient;
+
+ @Before
+ public void inspectProject() {
+ orchestrator.resetData();
+ orchestrator.executeBuild(SonarRunner.create(projectDir("shared/xoo-sample")));
+
+ wsClient = ItUtils.newAdminWsClient(orchestrator);
+ }
+
+ @Test
+ public void show() {
+ WsComponents.ShowWsResponse response = wsClient.components().show(new ShowWsRequest().setKey(FILE_KEY));
+
+ assertThat(response).isNotNull();
+ assertThat(response.getComponent().getKey()).isEqualTo(FILE_KEY);
+ assertThat(response.getAncestorsList()).isNotEmpty();
+ }
+
+ @Test
+ public void search() {
+ WsComponents.SearchWsResponse response = wsClient.components().search(new SearchWsRequest()
+ .setQualifiers(singletonList("FIL")));
+
+ assertThat(response).isNotNull();
+ assertThat(response.getComponents(0).getKey()).isEqualTo(FILE_KEY);
+ }
+}
diff --git a/it/it-tests/src/test/java/it/componentSearch/ProjectSearchTest.java b/it/it-tests/src/test/java/it/component/ProjectSearchTest.java
index e13068469af..5e5f47fedb7 100644
--- a/it/it-tests/src/test/java/it/componentSearch/ProjectSearchTest.java
+++ b/it/it-tests/src/test/java/it/component/ProjectSearchTest.java
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package it.componentSearch;
+package it.component;
import com.sonar.orchestrator.Orchestrator;
import com.sonar.orchestrator.build.SonarRunner;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWsModule.java
index 2b6896679dc..e9fd3333ada 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWsModule.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWsModule.java
@@ -33,6 +33,7 @@ public class ComponentsWsModule extends Module {
AppAction.class,
SearchAction.class,
TreeAction.class,
+ ShowAction.class,
SearchViewComponentsAction.class);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowAction.java
new file mode 100644
index 00000000000..dfc19ab3c40
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowAction.java
@@ -0,0 +1,160 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.
+ */
+
+package org.sonar.server.component.ws;
+
+import java.util.List;
+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.permission.GlobalPermissions;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.component.ComponentFinder.ParamNames;
+import org.sonar.server.user.UserSession;
+import org.sonarqube.ws.WsComponents;
+import org.sonarqube.ws.WsComponents.ShowWsResponse;
+import org.sonarqube.ws.client.component.ShowWsRequest;
+
+import static com.google.common.base.Objects.firstNonNull;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static java.lang.String.format;
+import static java.util.Collections.emptyList;
+import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
+import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_SHOW;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_ID;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_KEY;
+
+public class ShowAction implements ComponentsWsAction {
+ private final UserSession userSession;
+ private final DbClient dbClient;
+ private final ComponentFinder componentFinder;
+
+ public ShowAction(UserSession userSession, DbClient dbClient, ComponentFinder componentFinder) {
+ this.userSession = userSession;
+ this.dbClient = dbClient;
+ this.componentFinder = componentFinder;
+ }
+
+ @Override
+ public void define(WebService.NewController context) {
+ WebService.NewAction action = context.createAction(ACTION_SHOW)
+ .setDescription(format("Returns a component (file, directory, project, view…) and its ancestors. " +
+ "The ancestors are ordered from the parent to the root project. " +
+ "The '%s' or '%s' must be provided.<br>" +
+ "Requires one of the following permissions:" +
+ "<ul>" +
+ "<li>'Administer System'</li>" +
+ "<li>'Administer' rights on the specified project</li>" +
+ "<li>'Browse' on the specified project</li>" +
+ "</ul>",
+ PARAM_ID, PARAM_KEY))
+ .setResponseExample(getClass().getResource("show-example.json"))
+ .setSince("5.4")
+ .setHandler(this);
+
+ action.createParam(PARAM_ID)
+ .setDescription("Component id")
+ .setExampleValue(UUID_EXAMPLE_01);
+
+ action.createParam(PARAM_KEY)
+ .setDescription("Component key")
+ .setExampleValue("net.java.openjdk:jdk7");
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ ShowWsRequest showWsRequest = toShowWsRequest(request);
+ ShowWsResponse showWsResponse = doHandle(showWsRequest);
+
+ writeProtobuf(showWsResponse, request, response);
+ }
+
+ private ShowWsResponse doHandle(ShowWsRequest request) {
+ DbSession dbSession = dbClient.openSession(false);
+ try {
+ ComponentDto component = getComponentByUuidOrKey(dbSession, request);
+ SnapshotDto lastSnapshot = dbClient.snapshotDao().selectLastSnapshotByComponentId(dbSession, component.getId());
+ List<ComponentDto> orderedAncestors = emptyList();
+ if (lastSnapshot != null) {
+ ShowData.Builder showDataBuilder = ShowData.builder(lastSnapshot);
+ List<SnapshotDto> ancestorsSnapshots = dbClient.snapshotDao().selectByIds(dbSession, showDataBuilder.getOrderedSnapshotIds());
+ showDataBuilder.withAncestorsSnapshots(ancestorsSnapshots);
+ List<ComponentDto> ancestorComponents = dbClient.componentDao().selectByIds(dbSession, showDataBuilder.getOrderedComponentIds());
+ ShowData showData = showDataBuilder.andAncestorComponents(ancestorComponents);
+ orderedAncestors = showData.getComponents();
+ }
+
+ return buildResponse(component, orderedAncestors);
+ } finally {
+ dbClient.closeSession(dbSession);
+ }
+ }
+
+ private static ShowWsResponse buildResponse(ComponentDto component, List<ComponentDto> orderedAncestorComponents) {
+ ShowWsResponse.Builder response = ShowWsResponse.newBuilder();
+ response.setComponent(componentDtoToWsComponent(component));
+
+ for (ComponentDto ancestor : orderedAncestorComponents) {
+ response.addAncestors(componentDtoToWsComponent(ancestor));
+ }
+
+ return response.build();
+ }
+
+ private static WsComponents.Component.Builder componentDtoToWsComponent(ComponentDto dto) {
+ WsComponents.Component.Builder wsComponent = WsComponents.Component.newBuilder()
+ .setId(dto.uuid())
+ .setKey(dto.key())
+ .setName(dto.name())
+ .setQualifier(dto.qualifier());
+ if (!isNullOrEmpty(dto.path())) {
+ wsComponent.setPath(dto.path());
+ }
+ if (!isNullOrEmpty(dto.description())) {
+ wsComponent.setDescription(dto.description());
+ }
+
+ return wsComponent;
+ }
+
+ private ComponentDto getComponentByUuidOrKey(DbSession dbSession, ShowWsRequest request) {
+ ComponentDto component = componentFinder.getByUuidOrKey(dbSession, request.getId(), request.getKey(), ParamNames.ID_AND_KEY);
+ String projectUuid = firstNonNull(component.projectUuid(), component.uuid());
+ if (!userSession.hasGlobalPermission(GlobalPermissions.SYSTEM_ADMIN) &&
+ !userSession.hasProjectPermissionByUuid(UserRole.ADMIN, projectUuid) &&
+ !userSession.hasProjectPermissionByUuid(UserRole.USER, projectUuid)) {
+ throw insufficientPrivilegesException();
+ }
+ return component;
+ }
+
+ private static ShowWsRequest toShowWsRequest(Request request) {
+ return new ShowWsRequest()
+ .setId(request.param(PARAM_ID))
+ .setKey(request.param(PARAM_KEY));
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowData.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowData.java
new file mode 100644
index 00000000000..6e7d447e414
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowData.java
@@ -0,0 +1,104 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.
+ */
+
+package org.sonar.server.component.ws;
+
+import com.google.common.base.Function;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Ordering;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentDtoFunctions;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.component.SnapshotDtoFunctions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+class ShowData {
+ private final List<ComponentDto> components;
+
+ private ShowData(List<ComponentDto> components) {
+ this.components = components;
+ }
+
+ static Builder builder(SnapshotDto snapshot) {
+ return new Builder(snapshot);
+ }
+
+ List<ComponentDto> getComponents() {
+ return components;
+ }
+
+ static class Builder {
+ private Ordering<SnapshotDto> snapshotOrdering;
+ private List<Long> orderedSnapshotIds;
+ private List<Long> orderedComponentIds;
+
+ private Builder(SnapshotDto snapshot) {
+ List<String> orderedSnapshotIdsAsString = Splitter.on(".").omitEmptyStrings().splitToList(snapshot.getPath());
+ orderedSnapshotIds = Lists.transform(orderedSnapshotIdsAsString, StringToLongFunction.INSTANCE);
+ snapshotOrdering = Ordering
+ .explicit(orderedSnapshotIds)
+ .onResultOf(SnapshotDtoFunctions.toId())
+ .reverse();
+ }
+
+ Builder withAncestorsSnapshots(List<SnapshotDto> ancestorsSnapshots) {
+ checkNotNull(snapshotOrdering, "Snapshot must be set before the ancestors");
+ checkState(orderedSnapshotIds.size() == ancestorsSnapshots.size(), "Missing ancestor");
+
+ orderedComponentIds = Lists.transform(
+ snapshotOrdering.immutableSortedCopy(ancestorsSnapshots),
+ SnapshotDtoFunctions.toComponentId());
+
+ return this;
+ }
+
+ ShowData andAncestorComponents(List<ComponentDto> ancestorComponents) {
+ checkNotNull(orderedComponentIds, "Snapshot ancestors must be set before the component ancestors");
+ checkState(orderedComponentIds.size() == ancestorComponents.size(), "Missing ancestor");
+
+ return new ShowData(Ordering
+ .explicit(orderedComponentIds)
+ .onResultOf(ComponentDtoFunctions.toId())
+ .immutableSortedCopy(ancestorComponents));
+ }
+
+ List<Long> getOrderedSnapshotIds() {
+ return orderedSnapshotIds;
+ }
+
+ List<Long> getOrderedComponentIds() {
+ return orderedComponentIds;
+ }
+ }
+
+ private enum StringToLongFunction implements Function<String, Long> {
+ INSTANCE;
+
+ @Override
+ public Long apply(@Nonnull String input) {
+ return Long.parseLong(input);
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/component/ws/show-example.json b/server/sonar-server/src/main/resources/org/sonar/server/component/ws/show-example.json
new file mode 100644
index 00000000000..72a9988f652
--- /dev/null
+++ b/server/sonar-server/src/main/resources/org/sonar/server/component/ws/show-example.json
@@ -0,0 +1,25 @@
+{
+ "component": {
+ "id": "AVIF-FffA3Ax6PH2efPD",
+ "key": "com.sonarsource:java-markdown:src/main/java/com/sonarsource/markdown/impl/Rule.java",
+ "name": "Rule.java",
+ "qualifier": "FIL",
+ "path": "src/main/java/com/sonarsource/markdown/impl/Rule.java"
+ },
+ "ancestors": [
+ {
+ "id": "AVIF-FfgA3Ax6PH2efPF",
+ "key": "com.sonarsource:java-markdown:src/main/java/com/sonarsource/markdown/impl",
+ "name": "src/main/java/com/sonarsource/markdown/impl",
+ "qualifier": "DIR",
+ "path": "src/main/java/com/sonarsource/markdown/impl"
+ },
+ {
+ "id": "AVIF98jgA3Ax6PH2efOW",
+ "key": "com.sonarsource:java-markdown",
+ "name": "Java Markdown",
+ "description": "Java Markdown Project",
+ "qualifier": "TRK"
+ }
+ ]
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsModuleTest.java
index 5e854bb1f5f..dd31885a80d 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsModuleTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsModuleTest.java
@@ -30,6 +30,6 @@ public class ComponentsWsModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new ComponentsWsModule().configure(container);
- assertThat(container.size()).isEqualTo(7 + 2);
+ assertThat(container.size()).isEqualTo(8 + 2);
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ShowActionTest.java
new file mode 100644
index 00000000000..5e9500f4919
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ShowActionTest.java
@@ -0,0 +1,169 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.
+ */
+
+package org.sonar.server.component.ws;
+
+import com.google.common.base.Throwables;
+import java.io.IOException;
+import java.io.InputStream;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.WsActionTester;
+import org.sonar.test.DbTests;
+import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.WsComponents.ShowWsResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.component.ComponentTesting.newDirectory;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.component.ComponentTesting.newProjectDto;
+import static org.sonar.test.JsonAssert.assertJson;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_ID;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_KEY;
+
+@Category(DbTests.class)
+public class ShowActionTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone()
+ .setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+ ComponentDbTester componentDb = new ComponentDbTester(db);
+
+ WsActionTester ws = new WsActionTester(new ShowAction(userSession, db.getDbClient(), new ComponentFinder(db.getDbClient())));
+
+ @Test
+ public void json_example() throws IOException {
+ insertJsonExampleComponentsAndSnapshots();
+
+ String response = ws.newRequest()
+ .setParam("id", "AVIF-FffA3Ax6PH2efPD")
+ .execute()
+ .getInput();
+
+ assertJson(response).isSimilarTo(getClass().getResource("show-example.json"));
+ }
+
+ @Test
+ public void show_by_key_with_project_permission() {
+ userSession.anonymous().login().addProjectUuidPermissions(UserRole.ADMIN, "project-uuid");
+ componentDb.insertProjectAndSnapshot(newProjectDto("project-uuid").setKey("project-key"));
+
+ ShowWsResponse response = newRequest(null, "project-key");
+
+ assertThat(response.getAncestorsCount()).isEqualTo(0);
+ assertThat(response.getComponent().getKey()).isEqualTo("project-key");
+ }
+
+ @Test
+ public void show_with_browse_permission() {
+ userSession.anonymous().addProjectUuidPermissions(UserRole.USER, "project-uuid");
+ componentDb.insertProjectAndSnapshot(newProjectDto("project-uuid"));
+
+ ShowWsResponse response = newRequest("project-uuid", null);
+
+ assertThat(response.getComponent().getId()).isEqualTo("project-uuid");
+ }
+
+ @Test
+ public void show_provided_project() {
+ componentDb.insertComponent(newProjectDto("project-uuid"));
+
+ ShowWsResponse response = newRequest("project-uuid", null);
+
+ assertThat(response.getComponent().getId()).isEqualTo("project-uuid");
+ }
+
+ @Test
+ public void fail_if_not_enough_privilege() {
+ userSession.anonymous().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN);
+ expectedException.expect(ForbiddenException.class);
+ componentDb.insertProjectAndSnapshot(newProjectDto("project-uuid"));
+
+ newRequest("project-uuid", null);
+ }
+
+ @Test
+ public void fail_if_component_does_not_exist() {
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage("Component id 'unknown-uuid' not found");
+
+ newRequest("unknown-uuid", null);
+ }
+
+ private ShowWsResponse newRequest(@Nullable String uuid, @Nullable String key) {
+ TestRequest request = ws.newRequest()
+ .setMediaType(MediaTypes.PROTOBUF);
+
+ if (uuid != null) {
+ request.setParam(PARAM_ID, uuid);
+ }
+ if (key != null) {
+ request.setParam(PARAM_KEY, key);
+ }
+
+ try(InputStream responseStream = request.execute().getInputStream()) {
+ return ShowWsResponse.parseFrom(responseStream);
+ } catch (IOException e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+ private void insertJsonExampleComponentsAndSnapshots() {
+ ComponentDto project = newProjectDto("AVIF98jgA3Ax6PH2efOW")
+ .setKey("com.sonarsource:java-markdown")
+ .setName("Java Markdown")
+ .setDescription("Java Markdown Project")
+ .setQualifier(Qualifiers.PROJECT);
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ ComponentDto directory = newDirectory(project, "AVIF-FfgA3Ax6PH2efPF", "src/main/java/com/sonarsource/markdown/impl")
+ .setKey("com.sonarsource:java-markdown:src/main/java/com/sonarsource/markdown/impl")
+ .setName("src/main/java/com/sonarsource/markdown/impl")
+ .setQualifier(Qualifiers.DIRECTORY);
+ SnapshotDto directorySnapshot = componentDb.insertComponentAndSnapshot(
+ directory,
+ projectSnapshot);
+ componentDb.insertComponentAndSnapshot(
+ newFileDto(directory, "AVIF-FffA3Ax6PH2efPD")
+ .setKey("com.sonarsource:java-markdown:src/main/java/com/sonarsource/markdown/impl/Rule.java")
+ .setName("Rule.java")
+ .setPath("src/main/java/com/sonarsource/markdown/impl/Rule.java")
+ .setQualifier(Qualifiers.FILE),
+ directorySnapshot);
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java
index cffa000bc2f..dc0cf55afe5 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java
@@ -28,6 +28,7 @@ import com.google.gson.JsonParser;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
+import javax.annotation.CheckForNull;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Rule;
@@ -361,15 +362,15 @@ public class TreeActionTest {
for (JsonElement componentAsJsonElement : components) {
JsonObject componentAsJsonObject = componentAsJsonElement.getAsJsonObject();
componentDb.insertComponentAndSnapshot(new ComponentDto()
- .setUuid(getJsonField(componentAsJsonObject, "id"))
- .setKey(getJsonField(componentAsJsonObject, "key"))
- .setName(getJsonField(componentAsJsonObject, "name"))
- .setPath(getJsonField(componentAsJsonObject, "path"))
- .setProjectUuid(project.projectUuid())
- .setQualifier(getJsonField(componentAsJsonObject, "qualifier"))
- .setDescription(getJsonField(componentAsJsonObject, "description"))
- .setEnabled(true)
- .setCreatedAt(now),
+ .setUuid(getJsonField(componentAsJsonObject, "id"))
+ .setKey(getJsonField(componentAsJsonObject, "key"))
+ .setName(getJsonField(componentAsJsonObject, "name"))
+ .setPath(getJsonField(componentAsJsonObject, "path"))
+ .setProjectUuid(project.projectUuid())
+ .setQualifier(getJsonField(componentAsJsonObject, "qualifier"))
+ .setDescription(getJsonField(componentAsJsonObject, "description"))
+ .setEnabled(true)
+ .setCreatedAt(now),
projectSnapshot);
}
db.commit();
@@ -377,6 +378,7 @@ public class TreeActionTest {
return project;
}
+ @CheckForNull
private static String getJsonField(JsonObject jsonObject, String field) {
JsonElement jsonElement = jsonObject.get(field);
return jsonElement == null ? null : jsonElement.getAsString();
diff --git a/sonar-db/src/main/java/org/sonar/db/component/ComponentDtoFunctions.java b/sonar-db/src/main/java/org/sonar/db/component/ComponentDtoFunctions.java
index c75518ebf86..9116cc9eac0 100644
--- a/sonar-db/src/main/java/org/sonar/db/component/ComponentDtoFunctions.java
+++ b/sonar-db/src/main/java/org/sonar/db/component/ComponentDtoFunctions.java
@@ -34,6 +34,32 @@ public final class ComponentDtoFunctions {
return ToKey.INSTANCE;
}
+ public static Function<ComponentDto, Long> toId() {
+ return ToId.INSTANCE;
+ }
+
+ public static Function<ComponentDto, String> toProjectUuid() {
+ return ToProjectUuid.INSTANCE;
+ }
+
+ public static Function<ComponentDto, String> toUuid() {
+ return ToUuid.INSTANCE;
+ }
+
+ public static Function<ComponentDto, Long> toCopyResourceId() {
+ return ToCopyResourceId.INSTANCE;
+ }
+
+ private enum ToId implements Function<ComponentDto, Long> {
+ INSTANCE;
+
+ @Override
+ public Long apply(@Nonnull ComponentDto input) {
+ return input.getId();
+ }
+ }
+
+
private enum ToKey implements Function<ComponentDto, String> {
INSTANCE;
@@ -43,10 +69,6 @@ public final class ComponentDtoFunctions {
}
}
- public static Function<ComponentDto, String> toProjectUuid() {
- return ToProjectUuid.INSTANCE;
- }
-
private enum ToProjectUuid implements Function<ComponentDto, String> {
INSTANCE;
@@ -56,10 +78,6 @@ public final class ComponentDtoFunctions {
}
}
- public static Function<ComponentDto, String> toUuid() {
- return ToUuid.INSTANCE;
- }
-
private enum ToUuid implements Function<ComponentDto, String> {
INSTANCE;
@@ -69,10 +87,6 @@ public final class ComponentDtoFunctions {
}
}
- public static Function<ComponentDto, Long> toCopyResourceId() {
- return ToCopyResourceId.INSTANCE;
- }
-
private enum ToCopyResourceId implements Function<ComponentDto, Long> {
INSTANCE;
diff --git a/sonar-db/src/main/java/org/sonar/db/component/SnapshotDao.java b/sonar-db/src/main/java/org/sonar/db/component/SnapshotDao.java
index 4ef0cea7fa4..fc3dead424b 100644
--- a/sonar-db/src/main/java/org/sonar/db/component/SnapshotDao.java
+++ b/sonar-db/src/main/java/org/sonar/db/component/SnapshotDao.java
@@ -20,11 +20,13 @@
package org.sonar.db.component;
+import com.google.common.base.Function;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import java.util.Collection;
import java.util.List;
import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.ibatis.session.RowBounds;
import org.sonar.api.resources.Scopes;
@@ -34,6 +36,7 @@ import org.sonar.db.RowNotFoundException;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.FluentIterable.from;
+import static org.sonar.db.DatabaseUtils.executeLargeInputs;
public class SnapshotDao implements Dao {
@@ -54,6 +57,16 @@ public class SnapshotDao implements Dao {
return value;
}
+ public List<SnapshotDto> selectByIds(final DbSession dbSession, List<Long> snapshotIds) {
+ return executeLargeInputs(snapshotIds, new Function<List<Long>, List<SnapshotDto>>() {
+ @Nonnull
+ @Override
+ public List<SnapshotDto> apply(@Nonnull List<Long> input) {
+ return mapper(dbSession).selectByIds(input);
+ }
+ });
+ }
+
@CheckForNull
public SnapshotDto selectLastSnapshotByComponentId(DbSession session, long componentId) {
return mapper(session).selectLastSnapshot(componentId);
diff --git a/sonar-db/src/main/java/org/sonar/db/component/SnapshotDtoFunctions.java b/sonar-db/src/main/java/org/sonar/db/component/SnapshotDtoFunctions.java
new file mode 100644
index 00000000000..db46706b184
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/component/SnapshotDtoFunctions.java
@@ -0,0 +1,54 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.
+ */
+
+package org.sonar.db.component;
+
+import com.google.common.base.Function;
+import javax.annotation.Nonnull;
+
+public class SnapshotDtoFunctions {
+ public static Function<SnapshotDto, Long> toId() {
+ return ToId.INSTANCE;
+ }
+
+ public static Function<SnapshotDto, Long> toComponentId() {
+ return ToComponentId.INSTANCE;
+ }
+
+ private enum ToId implements Function<SnapshotDto, Long> {
+ INSTANCE;
+
+ @Override
+ public Long apply(@Nonnull SnapshotDto input) {
+ return input.getId();
+ }
+ }
+
+ private enum ToComponentId implements Function<SnapshotDto, Long> {
+ INSTANCE;
+
+ @Override
+ public Long apply(@Nonnull SnapshotDto input) {
+ return input.getComponentId();
+ }
+ }
+
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/component/SnapshotMapper.java b/sonar-db/src/main/java/org/sonar/db/component/SnapshotMapper.java
index 043390bdf13..b6a43bd6377 100644
--- a/sonar-db/src/main/java/org/sonar/db/component/SnapshotMapper.java
+++ b/sonar-db/src/main/java/org/sonar/db/component/SnapshotMapper.java
@@ -30,6 +30,8 @@ public interface SnapshotMapper {
@CheckForNull
SnapshotDto selectByKey(long id);
+ List<SnapshotDto> selectByIds(@Param("ids") List<Long> ids);
+
void insert(SnapshotDto snapshot);
@CheckForNull
@@ -53,7 +55,7 @@ public interface SnapshotMapper {
int updateSnapshotAndChildrenLastFlag(@Param(value = "root") Long rootId, @Param(value = "pathRootId") Long pathRootId,
@Param(value = "path") String path, @Param(value = "isLast") boolean isLast);
-
+
List<ViewsSnapshotDto> selectSnapshotBefore(@Param("componentId") long componentId, @Param("date") long date);
ViewsSnapshotDto selectLatestSnapshot(@Param("componentId") long componentId);
diff --git a/sonar-db/src/main/resources/org/sonar/db/component/SnapshotMapper.xml b/sonar-db/src/main/resources/org/sonar/db/component/SnapshotMapper.xml
index a8531b0a407..1c408c558e2 100644
--- a/sonar-db/src/main/resources/org/sonar/db/component/SnapshotMapper.xml
+++ b/sonar-db/src/main/resources/org/sonar/db/component/SnapshotMapper.xml
@@ -49,6 +49,17 @@
</where>
</select>
+ <select id="selectByIds" parameterType="Long" resultType="Snapshot">
+ SELECT
+ <include refid="snapshotColumns"/>
+ FROM snapshots s
+ WHERE
+ s.id in
+ <foreach collection="ids" item="id" separator="," open="(" close=")">
+ #{id}
+ </foreach>
+ </select>
+
<select id="selectLastSnapshot" resultType="Snapshot">
select
<include refid="snapshotColumns"/>
diff --git a/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java b/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java
index 74932cfe65e..523bf5873e7 100644
--- a/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java
+++ b/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java
@@ -65,6 +65,7 @@ public class ComponentDbTester {
public ComponentDto insertComponent(ComponentDto component) {
dbClient.componentDao().insert(dbSession, component);
+ db.commit();
return component;
}
diff --git a/sonar-db/src/test/java/org/sonar/db/component/SnapshotDaoTest.java b/sonar-db/src/test/java/org/sonar/db/component/SnapshotDaoTest.java
index 2f8639db024..6d3f406fa0a 100644
--- a/sonar-db/src/test/java/org/sonar/db/component/SnapshotDaoTest.java
+++ b/sonar-db/src/test/java/org/sonar/db/component/SnapshotDaoTest.java
@@ -30,11 +30,14 @@ import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Scopes;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.test.DbTests;
+import static com.google.common.collect.Lists.newArrayList;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.component.ComponentTesting.newProjectDto;
import static org.sonar.db.component.SnapshotQuery.SORT_FIELD.BY_DATE;
import static org.sonar.db.component.SnapshotQuery.SORT_ORDER.ASC;
import static org.sonar.db.component.SnapshotQuery.SORT_ORDER.DESC;
@@ -48,7 +51,8 @@ public class SnapshotDaoTest {
@Rule
public DbTester db = DbTester.create(System2.INSTANCE);
-
+ ComponentDbTester componentDb = new ComponentDbTester(db);
+ DbClient dbClient = db.getDbClient();
DbSession dbSession = db.getSession();
SnapshotDao underTest = db.getDbClient().snapshotDao();
@@ -96,6 +100,18 @@ public class SnapshotDaoTest {
}
@Test
+ public void select_by_ids() {
+ SnapshotDto snapshot1 = componentDb.insertProjectAndSnapshot(newProjectDto());
+ SnapshotDto snapshot2 = componentDb.insertProjectAndSnapshot(newProjectDto());
+ SnapshotDto snapshot3 = componentDb.insertProjectAndSnapshot(newProjectDto());
+
+ List<SnapshotDto> result = underTest.selectByIds(dbSession, newArrayList(snapshot1.getId(), snapshot2.getId(), snapshot3.getId(), 42L));
+
+ assertThat(result).hasSize(3);
+ assertThat(result).extracting("id").containsOnly(snapshot1.getId(), snapshot2.getId(), snapshot3.getId());
+ }
+
+ @Test
public void lastSnapshot_returns_null_when_no_last_snapshot() {
SnapshotDto snapshot = underTest.selectLastSnapshotByComponentId(db.getSession(), 123L);
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java
index fcee519bdb0..2630839acf4 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java
@@ -20,11 +20,23 @@
package org.sonarqube.ws.client.component;
+import com.google.common.base.Joiner;
import org.sonarqube.ws.WsComponents.SearchWsResponse;
+import org.sonarqube.ws.WsComponents.ShowWsResponse;
+import org.sonarqube.ws.WsComponents.TreeWsResponse;
import org.sonarqube.ws.client.BaseService;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.WsConnector;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_SHOW;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_TREE;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BASE_COMPONENT_ID;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BASE_COMPONENT_KEY;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_ID;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_KEY;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_QUALIFIERS;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_STRATEGY;
+
public class ComponentsService extends BaseService {
public ComponentsService(WsConnector wsConnector) {
@@ -33,10 +45,30 @@ public class ComponentsService extends BaseService {
public SearchWsResponse search(SearchWsRequest request) {
GetRequest get = new GetRequest(path("search"))
- .setParam("qualifiers", request.getQualifiers())
+ .setParam("qualifiers", Joiner.on(",").join(request.getQualifiers()))
.setParam("p", request.getPage())
.setParam("ps", request.getPageSize())
.setParam("q", request.getQuery());
return call(get, SearchWsResponse.parser());
}
+
+ public TreeWsResponse tree(TreeWsRequest request) {
+ GetRequest get = new GetRequest(path(ACTION_TREE))
+ .setParam(PARAM_BASE_COMPONENT_ID, request.getBaseComponentId())
+ .setParam(PARAM_BASE_COMPONENT_KEY, request.getBaseComponentKey())
+ .setParam(PARAM_QUALIFIERS, request.getQualifiers())
+ .setParam(PARAM_STRATEGY, request.getStrategy())
+ .setParam("p", request.getPage())
+ .setParam("ps", request.getPageSize())
+ .setParam("q", request.getQuery())
+ .setParam("s", request.getSort());
+ return call(get, TreeWsResponse.parser());
+ }
+
+ public ShowWsResponse show(ShowWsRequest request) {
+ GetRequest get = new GetRequest(path(ACTION_SHOW))
+ .setParam(PARAM_ID, request.getId())
+ .setParam(PARAM_KEY, request.getKey());
+ return call(get, ShowWsResponse.parser());
+ }
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java
index b9739c4480e..02d6fd688bc 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java
@@ -27,6 +27,7 @@ public class ComponentsWsParameters {
//actions
public static final String ACTION_TREE = "tree";
+ public static final String ACTION_SHOW = "show";
// parameters
public static final String PARAM_QUALIFIERS = "qualifiers";
@@ -34,4 +35,6 @@ public class ComponentsWsParameters {
public static final String PARAM_BASE_COMPONENT_ID = "baseComponentId";
public static final String PARAM_BASE_COMPONENT_KEY = "baseComponentKey";
public static final String PARAM_STRATEGY = "strategy";
+ public static final String PARAM_ID = "id";
+ public static final String PARAM_KEY = "key";
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ShowWsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ShowWsRequest.java
new file mode 100644
index 00000000000..37a7c77f040
--- /dev/null
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ShowWsRequest.java
@@ -0,0 +1,49 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.
+ */
+
+package org.sonarqube.ws.client.component;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+public class ShowWsRequest {
+ @CheckForNull
+ private String id;
+ @CheckForNull
+ private String key;
+
+ public String getId() {
+ return id;
+ }
+
+ public ShowWsRequest setId(@Nullable String id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public ShowWsRequest setKey(@Nullable String key) {
+ this.key = key;
+ return this;
+ }
+}
diff --git a/sonar-ws/src/main/protobuf/ws-components.proto b/sonar-ws/src/main/protobuf/ws-components.proto
index 03ded8c9bb7..d9e86b0e0e4 100644
--- a/sonar-ws/src/main/protobuf/ws-components.proto
+++ b/sonar-ws/src/main/protobuf/ws-components.proto
@@ -39,6 +39,13 @@ message TreeWsResponse {
repeated Component components = 3;
}
+// WS api/components/show
+message ShowWsResponse {
+ optional sonarqube.ws.commons.Paging paging = 1;
+ optional Component component = 2;
+ repeated Component ancestors = 3;
+}
+
message Component {
optional string id = 1;
optional string key = 2;