aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-server
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2017-02-16 16:55:13 +0100
committerJulien Lancelot <julien.lancelot@sonarsource.com>2017-02-17 09:27:54 +0100
commitc00a059069b34fc14716a5fb40f6eaa5a2cddfe3 (patch)
treecc0f2a5fe2a2440f5b5fbfcdbcbd5a0031cb970f /server/sonar-server
parentb4b1940277e877c853fbe2b32696c3cb1d50f816 (diff)
downloadsonarqube-c00a059069b34fc14716a5fb40f6eaa5a2cddfe3.tar.gz
sonarqube-c00a059069b34fc14716a5fb40f6eaa5a2cddfe3.zip
SONAR-8804 Create api/projects/search
Diffstat (limited to 'server/sonar-server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWsModule.java3
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java153
-rw-r--r--server/sonar-server/src/main/resources/org/sonar/server/project/ws/search-example.json23
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsModuleTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java292
5 files changed, 471 insertions, 2 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWsModule.java
index ee421e8213c..d3c00cf99ed 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWsModule.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWsModule.java
@@ -34,6 +34,7 @@ public class ProjectsWsModule extends Module {
GhostsAction.class,
ProvisionedAction.class,
SearchMyProjectsAction.class,
- SearchMyProjectsDataLoader.class);
+ SearchMyProjectsDataLoader.class,
+ SearchAction.class);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java
new file mode 100644
index 00000000000..1d7aa790a68
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java
@@ -0,0 +1,153 @@
+/*
+ * 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.project.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.server.ws.WebService.Param;
+import org.sonar.api.utils.Paging;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentQuery;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.server.organization.DefaultOrganizationProvider;
+import org.sonar.server.user.UserSession;
+import org.sonarqube.ws.WsProjects.SearchWsResponse;
+import org.sonarqube.ws.client.project.SearchWsRequest;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Optional.ofNullable;
+import static org.sonar.api.resources.Qualifiers.PROJECT;
+import static org.sonar.api.resources.Qualifiers.VIEW;
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+import static org.sonarqube.ws.WsProjects.SearchWsResponse.Component;
+import static org.sonarqube.ws.WsProjects.SearchWsResponse.newBuilder;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_SEARCH;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ORGANIZATION;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_QUALIFIERS;
+
+public class SearchAction implements ProjectsWsAction {
+
+ private final DbClient dbClient;
+ private final UserSession userSession;
+ private final DefaultOrganizationProvider defaultOrganizationProvider;
+ private final ProjectsWsSupport support;
+
+ public SearchAction(DbClient dbClient, UserSession userSession, DefaultOrganizationProvider defaultOrganizationProvider, ProjectsWsSupport support) {
+ this.dbClient = dbClient;
+ this.userSession = userSession;
+ this.defaultOrganizationProvider = defaultOrganizationProvider;
+ this.support = support;
+ }
+
+ @Override
+ public void define(WebService.NewController context) {
+ WebService.NewAction action = context.createAction(ACTION_SEARCH)
+ .setSince("6.3")
+ .setDescription("Search for projects or views.<br>" +
+ "Requires 'System Administrator' permission")
+ .setInternal(true)
+ .addPagingParams(100)
+ .addSearchQuery("sona", "component names", "component keys")
+ .setResponseExample(getClass().getResource("search-example.json"))
+ .setHandler(this);
+ action.createParam(PARAM_QUALIFIERS)
+ .setDescription("Comma-separated list of component qualifiers. Filter the results with the specified qualifiers")
+ .setPossibleValues(PROJECT, VIEW)
+ .setDefaultValue(PROJECT);
+ support.addOrganizationParam(action);
+ }
+
+ @Override
+ public void handle(Request wsRequest, Response wsResponse) throws Exception {
+ SearchWsResponse searchWsResponse = doHandle(toSearchWsRequest(wsRequest));
+ writeProtobuf(searchWsResponse, wsRequest, wsResponse);
+ }
+
+ private static SearchWsRequest toSearchWsRequest(Request request) {
+ return SearchWsRequest.builder()
+ .setOrganization(request.param(PARAM_ORGANIZATION))
+ .setQualifiers(request.mandatoryParamAsStrings(PARAM_QUALIFIERS))
+ .setQuery(request.param(Param.TEXT_QUERY))
+ .setPage(request.mandatoryParamAsInt(Param.PAGE))
+ .setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE)).build();
+ }
+
+ private SearchWsResponse doHandle(SearchWsRequest request) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ OrganizationDto organization = support.getOrganization(dbSession, ofNullable(request.getOrganization()).orElseGet(defaultOrganizationProvider.get()::getKey));
+ userSession.checkOrganizationPermission(organization.getUuid(), SYSTEM_ADMIN);
+
+ ComponentQuery query = buildQuery(request);
+ Paging paging = buildPaging(dbSession, request, organization, query);
+ List<ComponentDto> components = dbClient.componentDao().selectByQuery(dbSession, organization.getUuid(), query, paging.offset(), paging.pageSize());
+ return buildResponse(components, organization, paging);
+ }
+ }
+
+ private static ComponentQuery buildQuery(SearchWsRequest request) {
+ List<String> qualifiers = request.getQualifiers();
+ return ComponentQuery.builder()
+ .setNameOrKeyQuery(request.getQuery())
+ .setQualifiers(qualifiers.toArray(new String[qualifiers.size()]))
+ .build();
+ }
+
+ private Paging buildPaging(DbSession dbSession, SearchWsRequest request, OrganizationDto organization, ComponentQuery query) {
+ int total = dbClient.componentDao().countByQuery(dbSession, organization.getUuid(), query);
+ return Paging.forPageIndex(request.getPage())
+ .withPageSize(request.getPageSize())
+ .andTotal(total);
+ }
+
+ private static SearchWsResponse buildResponse(List<ComponentDto> components, OrganizationDto organization, Paging paging) {
+ SearchWsResponse.Builder responseBuilder = newBuilder();
+ responseBuilder.getPagingBuilder()
+ .setPageIndex(paging.pageIndex())
+ .setPageSize(paging.pageSize())
+ .setTotal(paging.total())
+ .build();
+
+ components.stream()
+ .map(dto -> dtoToProject(organization, dto))
+ .forEach(responseBuilder::addComponents);
+ return responseBuilder.build();
+ }
+
+ private static Component dtoToProject(OrganizationDto organization, ComponentDto dto) {
+ checkArgument(
+ organization.getUuid().equals(dto.getOrganizationUuid()),
+ "No Organization found for uuid '%s'",
+ dto.getOrganizationUuid());
+
+ Component.Builder builder = Component.newBuilder()
+ .setOrganization(organization.getKey())
+ .setId(dto.uuid())
+ .setKey(dto.key())
+ .setName(dto.name())
+ .setQualifier(dto.qualifier());
+ return builder.build();
+ }
+
+}
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/project/ws/search-example.json b/server/sonar-server/src/main/resources/org/sonar/server/project/ws/search-example.json
new file mode 100644
index 00000000000..fca854523c0
--- /dev/null
+++ b/server/sonar-server/src/main/resources/org/sonar/server/project/ws/search-example.json
@@ -0,0 +1,23 @@
+{
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 2
+ },
+ "components": [
+ {
+ "organization": "my-org-1",
+ "id": "project-uuid-1",
+ "key": "project-key-1",
+ "name": "Project Name 1",
+ "qualifier": "TRK"
+ },
+ {
+ "organization": "my-org-1",
+ "id": "project-uuid-2",
+ "key": "project-key-2",
+ "name": "Project Name 1",
+ "qualifier": "TRK"
+ }
+ ]
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsModuleTest.java
index e6c03e41ebf..abd0abd5105 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsModuleTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsModuleTest.java
@@ -30,6 +30,6 @@ public class ProjectsWsModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new ProjectsWsModule().configure(container);
- assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 10);
+ assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 11);
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java
new file mode 100644
index 00000000000..9b62ee686fd
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java
@@ -0,0 +1,292 @@
+/*
+ * 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.project.ws;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Throwables;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.assertj.core.api.Assertions;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.organization.DefaultOrganizationProvider;
+import org.sonar.server.organization.TestDefaultOrganizationProvider;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.WsActionTester;
+import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.WsProjects.SearchWsResponse;
+import org.sonarqube.ws.WsProjects.SearchWsResponse.Component;
+import org.sonarqube.ws.client.component.ComponentsWsParameters;
+import org.sonarqube.ws.client.project.SearchWsRequest;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.api.server.ws.WebService.Param.PAGE;
+import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
+import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY;
+import static org.sonar.core.permission.GlobalPermissions.QUALITY_PROFILE_ADMIN;
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
+import static org.sonar.core.util.Protobuf.setNullable;
+import static org.sonar.db.component.ComponentTesting.newDirectory;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.component.ComponentTesting.newModuleDto;
+import static org.sonar.db.component.ComponentTesting.newProjectDto;
+import static org.sonar.db.component.ComponentTesting.newView;
+import static org.sonar.test.JsonAssert.assertJson;
+import static org.sonarqube.ws.MediaTypes.PROTOBUF;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ORGANIZATION;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_QUALIFIERS;
+
+public class SearchActionTest {
+
+ private static final String PROJECT_KEY_1 = "project1";
+ private static final String PROJECT_KEY_2 = "project2";
+ private static final String PROJECT_KEY_3 = "project3";
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ @Rule
+ public DbTester db = DbTester.create();
+
+ private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
+
+ private WsActionTester ws = new WsActionTester(new SearchAction(db.getDbClient(), userSession, defaultOrganizationProvider, new ProjectsWsSupport(db.getDbClient())));
+
+ @Test
+ public void search_by_key_query() throws IOException {
+ userSession.addOrganizationPermission(db.getDefaultOrganization(), SYSTEM_ADMIN);
+ db.components().insertComponents(
+ newProjectDto(db.getDefaultOrganization()).setKey("project-_%-key"),
+ newProjectDto(db.getDefaultOrganization()).setKey("project-key-without-escaped-characters"));
+
+ SearchWsResponse response = call(SearchWsRequest.builder().setQuery("project-_%-key").build());
+
+ assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly("project-_%-key");
+ }
+
+ @Test
+ public void search_projects_when_no_qualifier_set() throws IOException {
+ userSession.addOrganizationPermission(db.getDefaultOrganization(), SYSTEM_ADMIN);
+ db.components().insertComponents(
+ newProjectDto(db.getDefaultOrganization()).setKey(PROJECT_KEY_1),
+ newView(db.getDefaultOrganization()));
+
+ SearchWsResponse response = call(SearchWsRequest.builder().build());
+
+ assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly(PROJECT_KEY_1);
+ }
+
+ @Test
+ public void search_projects() throws IOException {
+ userSession.addOrganizationPermission(db.getDefaultOrganization(), SYSTEM_ADMIN);
+ ComponentDto project = newProjectDto(db.getDefaultOrganization()).setKey(PROJECT_KEY_1);
+ ComponentDto module = newModuleDto(project);
+ ComponentDto directory = newDirectory(module, "dir");
+ ComponentDto file = newFileDto(directory);
+ db.components().insertComponents(
+ project, module, directory, file,
+ newProjectDto(db.getDefaultOrganization()).setKey(PROJECT_KEY_2),
+ newView(db.getDefaultOrganization()));
+
+ SearchWsResponse response = call(SearchWsRequest.builder().setQualifiers(singletonList("TRK")).build());
+
+ assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly(PROJECT_KEY_1, PROJECT_KEY_2);
+ }
+
+ @Test
+ public void search_views() throws IOException {
+ userSession.addOrganizationPermission(db.getDefaultOrganization(), SYSTEM_ADMIN);
+ db.components().insertComponents(
+ newProjectDto(db.getDefaultOrganization()).setKey(PROJECT_KEY_1),
+ newView(db.getDefaultOrganization()).setKey("view1"));
+
+ SearchWsResponse response = call(SearchWsRequest.builder().setQualifiers(singletonList("VW")).build());
+
+ assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly("view1");
+ }
+
+ @Test
+ public void search_projects_and_views() throws IOException {
+ userSession.addOrganizationPermission(db.getDefaultOrganization(), SYSTEM_ADMIN);
+ db.components().insertComponents(
+ newProjectDto(db.getDefaultOrganization()).setKey(PROJECT_KEY_1),
+ newView(db.getDefaultOrganization()).setKey("view1"));
+
+ SearchWsResponse response = call(SearchWsRequest.builder().setQualifiers(asList("TRK", "VW")).build());
+
+ assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly(PROJECT_KEY_1, "view1");
+ }
+
+ @Test
+ public void search_on_default_organization_when_no_organization_set() throws IOException {
+ userSession.addOrganizationPermission(db.getDefaultOrganization(), SYSTEM_ADMIN);
+ OrganizationDto otherOrganization = db.organizations().insert();
+ db.components().insertComponents(
+ newProjectDto(db.getDefaultOrganization()).setKey(PROJECT_KEY_1),
+ newProjectDto(db.getDefaultOrganization()).setKey(PROJECT_KEY_2),
+ newProjectDto(otherOrganization).setKey(PROJECT_KEY_3));
+
+ SearchWsResponse response = call(SearchWsRequest.builder().build());
+
+ assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly(PROJECT_KEY_1, PROJECT_KEY_2);
+ }
+
+ @Test
+ public void search_for_projects_on_given_organization() throws IOException {
+ OrganizationDto organization1 = db.organizations().insert();
+ OrganizationDto organization2 = db.organizations().insert();
+ userSession.addOrganizationPermission(organization1, SYSTEM_ADMIN);
+ ComponentDto project1 = newProjectDto(organization1);
+ ComponentDto project2 = newProjectDto(organization1);
+ ComponentDto project3 = newProjectDto(organization2);
+ db.components().insertComponents(project1, project2, project3);
+
+ SearchWsResponse response = call(SearchWsRequest.builder().setOrganization(organization1.getKey()).build());
+
+ assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly(project1.key(), project2.key());
+ }
+
+ @Test
+ public void result_is_paginated() throws IOException {
+ userSession.addOrganizationPermission(db.getDefaultOrganization(), SYSTEM_ADMIN);
+ List<ComponentDto> componentDtoList = new ArrayList<>();
+ for (int i = 1; i <= 9; i++) {
+ componentDtoList.add(newProjectDto(db.getDefaultOrganization(), "project-uuid-" + i).setKey("project-key-" + i).setName("Project Name " + i));
+ }
+ db.components().insertComponents(componentDtoList.toArray(new ComponentDto[] {}));
+
+ SearchWsResponse response = call(SearchWsRequest.builder().setPage(2).setPageSize(3).build());
+
+ assertThat(response.getComponentsList()).extracting(Component::getKey).containsExactly("project-key-4", "project-key-5", "project-key-6");
+ }
+
+ @Test
+ public void fail_when_not_system_admin() throws Exception {
+ userSession.addOrganizationPermission(db.getDefaultOrganization(), QUALITY_PROFILE_ADMIN);
+ expectedException.expect(ForbiddenException.class);
+
+ call(SearchWsRequest.builder().build());
+ }
+
+ @Test
+ public void fail_on_unknown_organization() throws Exception {
+ expectedException.expect(NotFoundException.class);
+
+ call(SearchWsRequest.builder().setOrganization("unknown").build());
+ }
+
+ @Test
+ public void fail_on_invalid_qualifier() throws Exception {
+ userSession.addOrganizationPermission(db.getDefaultOrganization(), QUALITY_PROFILE_ADMIN);
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Value of parameter 'qualifiers' (BRC) must be one of: [TRK, VW]");
+
+ call(SearchWsRequest.builder().setQualifiers(singletonList("BRC")).build());
+ }
+
+ @Test
+ public void verify_define() {
+ WebService.Action action = ws.getDef();
+ assertThat(action.key()).isEqualTo("search");
+ assertThat(action.isPost()).isFalse();
+ assertThat(action.description()).isEqualTo("Search for projects or views.<br>Requires 'System Administrator' permission");
+ assertThat(action.isInternal()).isTrue();
+ assertThat(action.since()).isEqualTo("6.3");
+ assertThat(action.handler()).isEqualTo(ws.getDef().handler());
+ assertThat(action.params()).hasSize(5);
+ assertThat(action.responseExample()).isEqualTo(getClass().getResource("search-example.json"));
+
+ WebService.Param organization = action.param("organization");
+ Assertions.assertThat(organization.description()).isEqualTo("The key of the organization");
+ Assertions.assertThat(organization.isInternal()).isTrue();
+ Assertions.assertThat(organization.isRequired()).isFalse();
+ Assertions.assertThat(organization.since()).isEqualTo("6.3");
+
+ WebService.Param qParam = action.param("q");
+ assertThat(qParam.isRequired()).isFalse();
+ assertThat(qParam.description()).isEqualTo("Limit search to component names or component keys that contain the supplied string.");
+
+ WebService.Param qualifierParam = action.param("qualifiers");
+ assertThat(qualifierParam.isRequired()).isFalse();
+ assertThat(qualifierParam.description()).isEqualTo("Comma-separated list of component qualifiers. Filter the results with the specified qualifiers");
+ assertThat(qualifierParam.possibleValues()).containsOnly("TRK", "VW");
+ assertThat(qualifierParam.defaultValue()).isEqualTo("TRK");
+
+ WebService.Param pParam = action.param("p");
+ assertThat(pParam.isRequired()).isFalse();
+ assertThat(pParam.defaultValue()).isEqualTo("1");
+ assertThat(pParam.description()).isEqualTo("1-based page number");
+
+ WebService.Param psParam = action.param("ps");
+ assertThat(psParam.isRequired()).isFalse();
+ assertThat(psParam.defaultValue()).isEqualTo("100");
+ assertThat(psParam.description()).isEqualTo("Page size. Must be greater than 0.");
+ }
+
+ @Test
+ public void verify_response_example() throws URISyntaxException, IOException {
+ OrganizationDto organizationDto = db.organizations().insertForKey("my-org-1");
+ userSession.addOrganizationPermission(organizationDto, SYSTEM_ADMIN);
+ db.components().insertComponents(
+ newProjectDto(organizationDto, "project-uuid-1").setName("Project Name 1").setKey("project-key-1"),
+ newProjectDto(organizationDto, "project-uuid-2").setName("Project Name 1").setKey("project-key-2"));
+
+ String response = ws.newRequest()
+ .setMediaType(MediaTypes.JSON)
+ .setParam(PARAM_ORGANIZATION, organizationDto.getKey())
+ .execute().getInput();
+ assertJson(response).isSimilarTo(ws.getDef().responseExampleAsString());
+ }
+
+ private SearchWsResponse call(SearchWsRequest wsRequest) {
+ TestRequest request = ws.newRequest()
+ .setMediaType(PROTOBUF);
+ setNullable(wsRequest.getOrganization(), organization -> request.setParam(PARAM_ORGANIZATION, organization));
+ List<String> qualifiers = wsRequest.getQualifiers();
+ if (!qualifiers.isEmpty()) {
+ request.setParam(ComponentsWsParameters.PARAM_QUALIFIERS, Joiner.on(",").join(qualifiers));
+ }
+ setNullable(wsRequest.getQuery(), query -> request.setParam(TEXT_QUERY, query));
+ setNullable(wsRequest.getPage(), page -> request.setParam(PAGE, String.valueOf(page)));
+ setNullable(wsRequest.getPageSize(), pageSize -> request.setParam(PAGE_SIZE, String.valueOf(pageSize)));
+ try {
+ return SearchWsResponse.parseFrom(request.execute().getInputStream());
+ } catch (IOException e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+}