Browse Source

SONAR-8804 Create api/projects/search

tags/6.3.0.18587
Julien Lancelot 7 years ago
parent
commit
c00a059069

+ 2
- 1
server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWsModule.java View File

@@ -34,6 +34,7 @@ public class ProjectsWsModule extends Module {
GhostsAction.class,
ProvisionedAction.class,
SearchMyProjectsAction.class,
SearchMyProjectsDataLoader.class);
SearchMyProjectsDataLoader.class,
SearchAction.class);
}
}

+ 153
- 0
server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java View File

@@ -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();
}

}

+ 23
- 0
server/sonar-server/src/main/resources/org/sonar/server/project/ws/search-example.json View File

@@ -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"
}
]
}

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsModuleTest.java View File

@@ -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);
}
}

+ 292
- 0
server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java View File

@@ -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);
}
}

}

+ 18
- 1
sonar-ws/src/main/java/org/sonarqube/ws/client/project/ProjectsService.java View File

@@ -19,16 +19,23 @@
*/
package org.sonarqube.ws.client.project;

import com.google.common.base.Joiner;
import org.sonarqube.ws.WsProjects;
import org.sonarqube.ws.WsProjects.CreateWsResponse;
import org.sonarqube.ws.client.BaseService;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.PostRequest;
import org.sonarqube.ws.client.WsConnector;

import static org.sonar.api.server.ws.WebService.Param.*;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_CREATE;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_SEARCH;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.CONTROLLER;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_BRANCH;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NAME;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ORGANIZATION;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_QUALIFIERS;

/**
* Maps web service {@code api/projects}.
@@ -47,7 +54,7 @@ public class ProjectsService extends BaseService {
*/
public CreateWsResponse create(CreateRequest project) {
PostRequest request = new PostRequest(path(ACTION_CREATE))
.setParam("organization", project.getOrganization())
.setParam(PARAM_ORGANIZATION, project.getOrganization())
.setParam(PARAM_PROJECT, project.getKey())
.setParam(PARAM_NAME, project.getName())
.setParam(PARAM_BRANCH, project.getBranch());
@@ -62,4 +69,14 @@ public class ProjectsService extends BaseService {
.setParam("id", request.getId())
.setParam("key", request.getKey()));
}

public WsProjects.SearchWsResponse search(SearchWsRequest request) {
GetRequest get = new GetRequest(path(ACTION_SEARCH))
.setParam(PARAM_ORGANIZATION, request.getOrganization())
.setParam(PARAM_QUALIFIERS, Joiner.on(",").join(request.getQualifiers()))
.setParam(TEXT_QUERY, request.getQuery())
.setParam(PAGE, request.getPage())
.setParam(PAGE_SIZE, request.getPageSize());
return call(get, WsProjects.SearchWsResponse.parser());
}
}

+ 3
- 0
sonar-ws/src/main/java/org/sonarqube/ws/client/project/ProjectsWsParameters.java View File

@@ -25,10 +25,13 @@ public class ProjectsWsParameters {

public static final String ACTION_CREATE = "create";
public static final String ACTION_INDEX = "index";
public static final String ACTION_SEARCH = "search";

public static final String PARAM_PROJECT = "project";
public static final String PARAM_NAME = "name";
public static final String PARAM_BRANCH = "branch";
public static final String PARAM_ORGANIZATION = "organization";
public static final String PARAM_QUALIFIERS = "qualifiers";

private ProjectsWsParameters() {
// static utils only

+ 109
- 0
sonar-ws/src/main/java/org/sonarqube/ws/client/project/SearchWsRequest.java View File

@@ -0,0 +1,109 @@
/*
* 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.sonarqube.ws.client.project;

import java.util.ArrayList;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

import static java.util.Objects.requireNonNull;

public class SearchWsRequest {
private final String organization;
private final String query;
private final List<String> qualifiers;
private final Integer page;
private final Integer pageSize;

public SearchWsRequest(Builder builder) {
this.organization = builder.organization;
this.query = builder.query;
this.qualifiers = builder.qualifiers;
this.page = builder.page;
this.pageSize = builder.pageSize;
}

@CheckForNull
public String getOrganization() {
return organization;
}

public List<String> getQualifiers() {
return qualifiers;
}

@CheckForNull
public Integer getPage() {
return page;
}

@CheckForNull
public Integer getPageSize() {
return pageSize;
}

@CheckForNull
public String getQuery() {
return query;
}

public static Builder builder() {
return new Builder();
}

public static class Builder {
private String organization;
private List<String> qualifiers = new ArrayList<>();
private Integer page;
private Integer pageSize;
private String query;

public Builder setOrganization(@Nullable String organization) {
this.organization = organization;
return this;
}

public Builder setQualifiers(List<String> qualifiers) {
this.qualifiers = requireNonNull(qualifiers, "Qualifiers cannot be null");
return this;
}

public Builder setPage(@Nullable Integer page) {
this.page = page;
return this;
}

public Builder setPageSize(@Nullable Integer pageSize) {
this.pageSize = pageSize;
return this;
}

public Builder setQuery(@Nullable String query) {
this.query = query;
return this;
}

public SearchWsRequest build() {
return new SearchWsRequest(this);
}
}

}

+ 14
- 0
sonar-ws/src/main/protobuf/ws-projects.proto View File

@@ -57,3 +57,17 @@ message CreateWsResponse {
}
}

// WS api/projects/search
message SearchWsResponse {
optional sonarqube.ws.commons.Paging paging = 1;
repeated Component components = 2;

message Component {
optional string organization = 1;
optional string id = 2;
optional string key = 3;
optional string name = 4;
optional string qualifier = 5;
}
}


+ 23
- 0
sonar-ws/src/test/java/org/sonarqube/ws/client/project/ProjectsServiceTest.java View File

@@ -25,9 +25,12 @@ import org.sonarqube.ws.WsProjects;
import org.sonarqube.ws.client.ServiceTester;
import org.sonarqube.ws.client.WsConnector;

import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.MapEntry.entry;
import static org.mockito.Mockito.mock;
import static org.sonar.api.server.ws.WebService.Param.PAGE;
import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;

public class ProjectsServiceTest {

@@ -96,4 +99,24 @@ public class ProjectsServiceTest {
assertThat(serviceTester.getPostRequest().getPath()).isEqualTo("api/projects/delete");
assertThat(serviceTester.getPostRequest().getParams()).containsOnly(entry("key", "project_key"));
}

@Test
public void search() {
underTest.search(SearchWsRequest.builder()
.setOrganization("default")
.setQuery("project")
.setQualifiers(asList("TRK", "VW"))
.setPage(3)
.setPageSize(10)
.build());

serviceTester.assertThat(serviceTester.getGetRequest())
.hasPath("search")
.hasParam("organization", "default")
.hasParam("q", "project")
.hasParam("qualifiers", "TRK,VW")
.hasParam(PAGE, 3)
.hasParam(PAGE_SIZE, 10)
.andNoOtherParam();
}
}

Loading…
Cancel
Save