@@ -27,6 +27,7 @@ import org.sonar.api.web.UserRole; | |||
import org.sonar.core.util.stream.MoreCollectors; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.organization.OrganizationDto; | |||
import org.sonar.db.qualitygate.ProjectQgateAssociation; | |||
import org.sonar.db.qualitygate.ProjectQgateAssociationDao; | |||
import org.sonar.db.qualitygate.ProjectQgateAssociationDto; | |||
@@ -52,21 +53,19 @@ public class QgateProjectFinder { | |||
this.associationDao = dbClient.projectQgateAssociationDao(); | |||
} | |||
public Association find(ProjectQgateAssociationQuery query) { | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
getQualityGateId(dbSession, query.gateId()); | |||
List<ProjectQgateAssociationDto> projects = associationDao.selectProjects(dbSession, query); | |||
List<ProjectQgateAssociationDto> authorizedProjects = keepAuthorizedProjects(dbSession, projects); | |||
public Association find(DbSession dbSession, OrganizationDto organization, ProjectQgateAssociationQuery query) { | |||
getQualityGateId(dbSession, organization, query.gateId()); | |||
List<ProjectQgateAssociationDto> projects = associationDao.selectProjects(dbSession, query); | |||
List<ProjectQgateAssociationDto> authorizedProjects = keepAuthorizedProjects(dbSession, projects); | |||
Paging paging = forPageIndex(query.pageIndex()) | |||
.withPageSize(query.pageSize()) | |||
.andTotal(authorizedProjects.size()); | |||
return new Association(toProjectAssociations(getPaginatedProjects(authorizedProjects, paging)), paging.hasNextPage()); | |||
} | |||
Paging paging = forPageIndex(query.pageIndex()) | |||
.withPageSize(query.pageSize()) | |||
.andTotal(authorizedProjects.size()); | |||
return new Association(toProjectAssociations(getPaginatedProjects(authorizedProjects, paging)), paging.hasNextPage()); | |||
} | |||
private Long getQualityGateId(DbSession dbSession, String gateId) { | |||
return checkFound(qualitygateDao.selectById(dbSession, Long.valueOf(gateId)), "Quality gate '" + gateId + "' does not exists.").getId(); | |||
private Long getQualityGateId(DbSession dbSession, OrganizationDto organization, String gateId) { | |||
return checkFound(qualitygateDao.selectByOrganizationAndId(dbSession, organization, Long.valueOf(gateId)), "Quality gate '" + gateId + "' does not exists.").getId(); | |||
} | |||
private static List<ProjectQgateAssociationDto> getPaginatedProjects(List<ProjectQgateAssociationDto> projects, Paging paging) { |
@@ -23,18 +23,32 @@ import com.google.common.io.Resources; | |||
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.text.JsonWriter; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.organization.OrganizationDto; | |||
import org.sonar.db.qualitygate.ProjectQgateAssociation; | |||
import org.sonar.db.qualitygate.ProjectQgateAssociationQuery; | |||
import org.sonar.server.qualitygate.QgateProjectFinder; | |||
import org.sonarqube.ws.Qualitygates; | |||
import static org.sonar.api.server.ws.WebService.Param.SELECTED; | |||
import static org.sonar.db.qualitygate.ProjectQgateAssociationQuery.ANY; | |||
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_GATE_ID; | |||
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PAGE; | |||
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PAGE_SIZE; | |||
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_QUERY; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
public class SearchAction implements QualityGatesWsAction { | |||
private final DbClient dbClient; | |||
private final QgateProjectFinder projectFinder; | |||
private final QualityGatesWsSupport wsSupport; | |||
public SearchAction(QgateProjectFinder projectFinder) { | |||
public SearchAction(DbClient dbClient, QgateProjectFinder projectFinder, QualityGatesWsSupport wsSupport) { | |||
this.dbClient = dbClient; | |||
this.projectFinder = projectFinder; | |||
this.wsSupport = wsSupport; | |||
} | |||
@Override | |||
@@ -43,47 +57,60 @@ public class SearchAction implements QualityGatesWsAction { | |||
.setDescription("Search for projects associated (or not) to a quality gate.<br/>" + | |||
"Only authorized projects for current user will be returned.") | |||
.setSince("4.3") | |||
.setResponseExample(Resources.getResource(this.getClass(), "example-search.json")) | |||
.setResponseExample(Resources.getResource(this.getClass(), "search-example.json")) | |||
.setHandler(this); | |||
action.createParam(QualityGatesWsParameters.PARAM_GATE_ID) | |||
action.createParam(PARAM_GATE_ID) | |||
.setDescription("Quality Gate ID") | |||
.setRequired(true) | |||
.setExampleValue("1"); | |||
action.createParam(QualityGatesWsParameters.PARAM_QUERY) | |||
action.createParam(PARAM_QUERY) | |||
.setDescription("To search for projects containing this string. If this parameter is set, \"selected\" is set to \"all\".") | |||
.setExampleValue("abc"); | |||
action.addSelectionModeParam(); | |||
action.createParam(QualityGatesWsParameters.PARAM_PAGE) | |||
action.createParam(PARAM_PAGE) | |||
.setDescription("Page number") | |||
.setDefaultValue("1") | |||
.setExampleValue("2"); | |||
action.createParam(QualityGatesWsParameters.PARAM_PAGE_SIZE) | |||
action.createParam(PARAM_PAGE_SIZE) | |||
.setDescription("Page size") | |||
.setExampleValue("10"); | |||
wsSupport.createOrganizationParam(action); | |||
} | |||
@Override | |||
public void handle(Request request, Response response) { | |||
QgateProjectFinder.Association associations = projectFinder.find(ProjectQgateAssociationQuery.builder() | |||
.gateId(request.mandatoryParam(QualityGatesWsParameters.PARAM_GATE_ID)) | |||
.membership(request.param(QualityGatesWsParameters.PARAM_QUERY) == null ? request.param(Param.SELECTED) : ProjectQgateAssociationQuery.ANY) | |||
.projectSearch(request.param(QualityGatesWsParameters.PARAM_QUERY)) | |||
.pageIndex(request.paramAsInt(QualityGatesWsParameters.PARAM_PAGE)) | |||
.pageSize(request.paramAsInt(QualityGatesWsParameters.PARAM_PAGE_SIZE)) | |||
.build()); | |||
try (JsonWriter writer = response.newJsonWriter()) { | |||
writer.beginObject().prop("more", associations.hasMoreResults()); | |||
writer.name("results").beginArray(); | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
OrganizationDto organization = wsSupport.getOrganization(dbSession, request); | |||
QgateProjectFinder.Association associations = projectFinder.find(dbSession, organization, | |||
ProjectQgateAssociationQuery.builder() | |||
.gateId(request.mandatoryParam(PARAM_GATE_ID)) | |||
.membership(request.param(PARAM_QUERY) == null ? request.param(SELECTED) : ANY) | |||
.projectSearch(request.param(PARAM_QUERY)) | |||
.pageIndex(request.paramAsInt(PARAM_PAGE)) | |||
.pageSize(request.paramAsInt(PARAM_PAGE_SIZE)) | |||
.build()); | |||
Qualitygates.SearchResponse.Builder createResponse = Qualitygates.SearchResponse.newBuilder() | |||
.setMore(associations.hasMoreResults()); | |||
for (ProjectQgateAssociation project : associations.projects()) { | |||
writer.beginObject().prop("id", project.id()).prop("name", project.name()).prop(Param.SELECTED, project.isMember()).endObject(); | |||
createResponse.addResultsBuilder() | |||
.setId(project.id()) | |||
.setName(project.name()) | |||
.setSelected(project.isMember()); | |||
} | |||
writer.endArray().endObject().close(); | |||
writeProtobuf(createResponse.build(), request, response); | |||
} | |||
} | |||
} |
@@ -23,19 +23,16 @@ import com.google.common.base.Function; | |||
import java.util.List; | |||
import java.util.Map; | |||
import javax.annotation.Nonnull; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.api.web.UserRole; | |||
import org.sonar.core.util.Uuids; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.component.ComponentDbTester; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.component.ComponentTesting; | |||
import org.sonar.db.organization.OrganizationDto; | |||
import org.sonar.db.property.PropertyDto; | |||
import org.sonar.db.qualitygate.ProjectQgateAssociation; | |||
@@ -48,6 +45,9 @@ import org.sonar.server.tester.UserSessionRule; | |||
import static com.google.common.collect.FluentIterable.from; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonar.api.web.UserRole.USER; | |||
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; | |||
import static org.sonar.db.component.ComponentTesting.newPublicProjectDto; | |||
import static org.sonar.db.qualitygate.ProjectQgateAssociationQuery.IN; | |||
import static org.sonar.db.qualitygate.ProjectQgateAssociationQuery.OUT; | |||
import static org.sonar.db.qualitygate.ProjectQgateAssociationQuery.builder; | |||
@@ -67,19 +67,16 @@ public class QgateProjectFinderTest { | |||
private DbClient dbClient = dbTester.getDbClient(); | |||
private DbSession dbSession = dbTester.getSession(); | |||
private ComponentDbTester componentDbTester = new ComponentDbTester(dbTester); | |||
private QualityGateDto qGate; | |||
private QgateProjectFinder underTest = new QgateProjectFinder(dbClient, userSession); | |||
@Before | |||
public void setUp() throws Exception { | |||
qGate = new QualityGateDto().setName("Default Quality Gate").setUuid(Uuids.createFast()); | |||
dbClient.qualityGateDao().insert(dbSession, qGate); | |||
dbTester.commit(); | |||
} | |||
@Test | |||
public void return_empty_association() { | |||
Association result = underTest.find( | |||
OrganizationDto organization = dbTester.organizations().insert(); | |||
QualityGateDto qGate = dbTester.qualityGates().insertQualityGate(organization, | |||
qualityGateDto -> qualityGateDto.setName("Default Quality Gate").setUuid(Uuids.createFast())); | |||
dbTester.commit(); | |||
Association result = underTest.find(dbSession, organization, | |||
builder() | |||
.gateId(Long.toString(qGate.getId())) | |||
.build()); | |||
@@ -89,12 +86,17 @@ public class QgateProjectFinderTest { | |||
@Test | |||
public void return_all_projects() { | |||
OrganizationDto org = dbTester.organizations().insert(); | |||
ComponentDto associatedProject = insertProject(ComponentTesting.newPublicProjectDto(org)); | |||
ComponentDto unassociatedProject = insertProject(ComponentTesting.newPublicProjectDto(org)); | |||
associateProjectToQualitGate(associatedProject.getId()); | |||
OrganizationDto organization = dbTester.organizations().insert(); | |||
QualityGateDto qGate = dbTester.qualityGates().insertQualityGate(organization, | |||
qualityGateDto -> qualityGateDto.setName("Default Quality Gate").setUuid(Uuids.createFast())); | |||
dbTester.commit(); | |||
ComponentDto unassociatedProject = newPublicProjectDto(organization); | |||
dbTester.components().insertComponent(unassociatedProject); | |||
ComponentDto associatedProject = newPublicProjectDto(organization); | |||
dbTester.components().insertComponent(associatedProject); | |||
associateProjectToQualitGate(associatedProject, qGate); | |||
Association result = underTest.find( | |||
Association result = underTest.find(dbSession, organization, | |||
builder() | |||
.gateId(Long.toString(qGate.getId())) | |||
.build()); | |||
@@ -108,12 +110,18 @@ public class QgateProjectFinderTest { | |||
@Test | |||
public void return_only_associated_project() { | |||
OrganizationDto org = dbTester.organizations().insert(); | |||
ComponentDto associatedProject = insertProject(ComponentTesting.newPublicProjectDto(org)); | |||
insertProject(ComponentTesting.newPublicProjectDto(org)); | |||
associateProjectToQualitGate(associatedProject.getId()); | |||
OrganizationDto organization = dbTester.organizations().insert(); | |||
QualityGateDto qGate = dbTester.qualityGates().insertQualityGate(organization, | |||
qualityGateDto -> qualityGateDto.setName("Default Quality Gate").setUuid(Uuids.createFast())); | |||
dbTester.commit(); | |||
ComponentDto project1 = newPublicProjectDto(organization); | |||
dbTester.components().insertComponent(project1); | |||
ComponentDto associatedProject = project1; | |||
ComponentDto project = newPublicProjectDto(organization); | |||
dbTester.components().insertComponent(project); | |||
associateProjectToQualitGate(associatedProject, qGate); | |||
Association result = underTest.find( | |||
Association result = underTest.find(dbSession, organization, | |||
builder() | |||
.membership(IN) | |||
.gateId(Long.toString(qGate.getId())) | |||
@@ -126,12 +134,17 @@ public class QgateProjectFinderTest { | |||
@Test | |||
public void return_only_unassociated_project() { | |||
OrganizationDto org = dbTester.organizations().insert(); | |||
ComponentDto associatedProject = insertProject(ComponentTesting.newPublicProjectDto(org)); | |||
ComponentDto unassociatedProject = insertProject(ComponentTesting.newPublicProjectDto(org)); | |||
associateProjectToQualitGate(associatedProject.getId()); | |||
OrganizationDto organization = dbTester.organizations().insert(); | |||
QualityGateDto qGate = dbTester.qualityGates().insertQualityGate(organization, | |||
qualityGateDto -> qualityGateDto.setName("Default Quality Gate").setUuid(Uuids.createFast())); | |||
dbTester.commit(); | |||
ComponentDto unAssociatedProject = newPublicProjectDto(organization); | |||
dbTester.components().insertComponent(unAssociatedProject); | |||
ComponentDto associatedProject = newPublicProjectDto(organization); | |||
dbTester.components().insertComponent(associatedProject); | |||
associateProjectToQualitGate(associatedProject, qGate); | |||
Association result = underTest.find( | |||
Association result = underTest.find(dbSession, organization, | |||
builder() | |||
.membership(OUT) | |||
.gateId(Long.toString(qGate.getId())) | |||
@@ -139,53 +152,65 @@ public class QgateProjectFinderTest { | |||
Map<Long, ProjectQgateAssociation> projectsById = projectsById(result.projects()); | |||
assertThat(projectsById).hasSize(1); | |||
verifyProject(projectsById.get(unassociatedProject.getId()), false, unassociatedProject.name()); | |||
verifyProject(projectsById.get(unAssociatedProject.getId()), false, unAssociatedProject.name()); | |||
} | |||
@Test | |||
public void return_only_authorized_projects() { | |||
OrganizationDto organization = dbTester.organizations().insert(); | |||
QualityGateDto qGate = dbTester.qualityGates().insertQualityGate(organization, | |||
qualityGateDto -> qualityGateDto.setName("Default Quality Gate").setUuid(Uuids.createFast())); | |||
dbTester.commit(); | |||
UserDto user = dbTester.users().insertUser("a_login"); | |||
OrganizationDto organizationDto = dbTester.organizations().insert(); | |||
ComponentDto project1 = componentDbTester.insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto)); | |||
componentDbTester.insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto)); | |||
ComponentDto project = componentDbTester.insertComponent(newPrivateProjectDto(organization)); | |||
componentDbTester.insertComponent(newPrivateProjectDto(organization)); | |||
// User can only see project 1 | |||
dbTester.users().insertProjectPermissionOnUser(user, UserRole.USER, project1); | |||
dbTester.users().insertProjectPermissionOnUser(user, USER, project); | |||
userSession.logIn(user.getLogin()).setUserId(user.getId()); | |||
Association result = underTest.find( | |||
Association result = underTest.find(dbSession, organization, | |||
builder() | |||
.gateId(Long.toString(qGate.getId())) | |||
.build()); | |||
verifyProjects(result, project1.getId()); | |||
verifyProjects(result, project.getId()); | |||
} | |||
@Test | |||
public void do_not_verify_permissions_if_user_is_root() { | |||
OrganizationDto org = dbTester.organizations().insert(); | |||
ComponentDto project = componentDbTester.insertPrivateProject(org); | |||
OrganizationDto organization = dbTester.organizations().insert(); | |||
QualityGateDto qGate = dbTester.qualityGates().insertQualityGate(organization, | |||
qualityGateDto -> qualityGateDto.setName("Default Quality Gate").setUuid(Uuids.createFast())); | |||
dbTester.commit(); | |||
ComponentDto project = componentDbTester.insertPrivateProject(organization); | |||
ProjectQgateAssociationQuery query = builder() | |||
.gateId(Long.toString(qGate.getId())) | |||
.build(); | |||
userSession.logIn().setNonRoot(); | |||
verifyProjects(underTest.find(query)); | |||
verifyProjects(underTest.find(dbSession, organization, query)); | |||
userSession.logIn().setRoot(); | |||
verifyProjects(underTest.find(query), project.getId()); | |||
verifyProjects(underTest.find(dbSession, organization, query), project.getId()); | |||
} | |||
@Test | |||
public void test_paging() throws Exception { | |||
OrganizationDto org = dbTester.organizations().insert(); | |||
ComponentDto project1 = insertProject(ComponentTesting.newPublicProjectDto(org).setName("Project 1")); | |||
ComponentDto project2 = insertProject(ComponentTesting.newPublicProjectDto(org).setName("Project 2")); | |||
ComponentDto project3 = insertProject(ComponentTesting.newPublicProjectDto(org).setName("Project 3")); | |||
associateProjectToQualitGate(project1.getId()); | |||
OrganizationDto organization = dbTester.organizations().insert(); | |||
QualityGateDto qGate = dbTester.qualityGates().insertQualityGate(organization, | |||
qualityGateDto -> qualityGateDto.setName("Default Quality Gate").setUuid(Uuids.createFast())); | |||
dbTester.commit(); | |||
ComponentDto project1 = newPublicProjectDto(organization).setName("Project 1"); | |||
dbTester.components().insertComponent(project1); | |||
associateProjectToQualitGate(project1, qGate); | |||
ComponentDto project2 = newPublicProjectDto(organization).setName("Project 2"); | |||
dbTester.components().insertComponent(project2); | |||
ComponentDto project3 = newPublicProjectDto(organization).setName("Project 3"); | |||
dbTester.components().insertComponent(project3); | |||
// Return partial result on first page | |||
verifyPaging(underTest.find( | |||
verifyPaging(underTest.find(dbSession, organization, | |||
builder().gateId(Long.toString(qGate.getId())) | |||
.pageIndex(1) | |||
.pageSize(1) | |||
@@ -193,7 +218,7 @@ public class QgateProjectFinderTest { | |||
true, project1.getId()); | |||
// Return partial result on second page | |||
verifyPaging(underTest.find( | |||
verifyPaging(underTest.find(dbSession, organization, | |||
builder().gateId(Long.toString(qGate.getId())) | |||
.pageIndex(2) | |||
.pageSize(1) | |||
@@ -201,7 +226,7 @@ public class QgateProjectFinderTest { | |||
true, project2.getId()); | |||
// Return partial result on first page | |||
verifyPaging(underTest.find( | |||
verifyPaging(underTest.find(dbSession, organization, | |||
builder().gateId(Long.toString(qGate.getId())) | |||
.pageIndex(1) | |||
.pageSize(2) | |||
@@ -209,7 +234,7 @@ public class QgateProjectFinderTest { | |||
true, project1.getId(), project2.getId()); | |||
// Return all result on first page | |||
verifyPaging(underTest.find( | |||
verifyPaging(underTest.find(dbSession, organization, | |||
builder().gateId(Long.toString(qGate.getId())) | |||
.pageIndex(1) | |||
.pageSize(3) | |||
@@ -217,7 +242,7 @@ public class QgateProjectFinderTest { | |||
false, project1.getId(), project2.getId(), project3.getId()); | |||
// Return no result as page index is off limit | |||
verifyPaging(underTest.find( | |||
verifyPaging(underTest.find(dbSession, organization, | |||
builder().gateId(Long.toString(qGate.getId())) | |||
.pageIndex(3) | |||
.pageSize(3) | |||
@@ -228,7 +253,7 @@ public class QgateProjectFinderTest { | |||
@Test | |||
public void fail_on_unknown_quality_gate() { | |||
expectedException.expect(NotFoundException.class); | |||
underTest.find(builder().gateId("123").build()); | |||
underTest.find(dbSession, dbTester.organizations().insert(), builder().gateId("123").build()); | |||
} | |||
private void verifyProject(ProjectQgateAssociation project, boolean expectedMembership, String expectedName) { | |||
@@ -245,19 +270,14 @@ public class QgateProjectFinderTest { | |||
assertThat(association.projects()).extracting("id").containsOnly(expectedProjectIds); | |||
} | |||
private void associateProjectToQualitGate(long projectId) { | |||
private void associateProjectToQualitGate(ComponentDto component, QualityGateDto qualityGate) { | |||
dbClient.propertiesDao().saveProperty( | |||
new PropertyDto().setKey(SONAR_QUALITYGATE_PROPERTY) | |||
.setResourceId(projectId) | |||
.setValue(Long.toString(qGate.getId()))); | |||
.setResourceId(component.getId()) | |||
.setValue(Long.toString(qualityGate.getId()))); | |||
dbTester.commit(); | |||
} | |||
private ComponentDto insertProject(ComponentDto project) { | |||
dbTester.components().insertComponent(project); | |||
return project; | |||
} | |||
private static Map<Long, ProjectQgateAssociation> projectsById(List<ProjectQgateAssociation> projects) { | |||
return from(projects).uniqueIndex(ProjectToId.INSTANCE); | |||
} |
@@ -1,148 +0,0 @@ | |||
/* | |||
* 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.qualitygate.ws; | |||
import com.google.common.collect.ImmutableList; | |||
import java.util.List; | |||
import org.junit.Before; | |||
import org.junit.Ignore; | |||
import org.junit.Test; | |||
import org.junit.runner.RunWith; | |||
import org.mockito.ArgumentCaptor; | |||
import org.mockito.Mock; | |||
import org.mockito.runners.MockitoJUnitRunner; | |||
import org.sonar.api.server.ws.Change; | |||
import org.sonar.api.server.ws.WebService.Action; | |||
import org.sonar.api.server.ws.WebService.Controller; | |||
import org.sonar.db.qualitygate.ProjectQgateAssociation; | |||
import org.sonar.db.qualitygate.ProjectQgateAssociationQuery; | |||
import org.sonar.server.qualitygate.QgateProjectFinder; | |||
import org.sonar.server.qualitygate.QgateProjectFinder.Association; | |||
import org.sonar.server.qualitygate.QualityGates; | |||
import org.sonar.server.ws.RemovedWebServiceHandler; | |||
import org.sonar.server.ws.WsTester; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.tuple; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.when; | |||
// TODO split testcases in action tests | |||
// TODO restore | |||
@RunWith(MockitoJUnitRunner.class) | |||
@Ignore | |||
public class QualityGatesWsTest { | |||
@Mock | |||
private QualityGates qGates; | |||
@Mock | |||
private QgateProjectFinder projectFinder; | |||
WsTester tester; | |||
@Before | |||
public void setUp() { | |||
tester = new WsTester(new QualityGatesWs( | |||
new SearchAction(projectFinder), | |||
new CreateAction(null, null, null, null), | |||
new SetAsDefaultAction(qGates))); | |||
} | |||
@Test | |||
public void define_ws() { | |||
Controller controller = tester.controller("api/qualitygates"); | |||
assertThat(controller).isNotNull(); | |||
assertThat(controller.path()).isEqualTo("api/qualitygates"); | |||
assertThat(controller.description()).isNotEmpty(); | |||
assertThat(controller.actions()).hasSize(6); | |||
Action setDefault = controller.action("set_as_default"); | |||
assertThat(setDefault).isNotNull(); | |||
assertThat(setDefault.handler()).isNotNull(); | |||
assertThat(setDefault.since()).isEqualTo("4.3"); | |||
assertThat(setDefault.isPost()).isTrue(); | |||
assertThat(setDefault.param("id")).isNotNull(); | |||
assertThat(setDefault.isInternal()).isFalse(); | |||
Action unsetDefault = controller.action("unset_default"); | |||
assertThat(unsetDefault).isNotNull(); | |||
assertThat(unsetDefault.handler()).isNotNull(); | |||
assertThat(unsetDefault.since()).isEqualTo("4.3"); | |||
assertThat(unsetDefault.deprecatedSince()).isEqualTo("7.0"); | |||
assertThat(unsetDefault.changelog()) | |||
.extracting(Change::getVersion, Change::getDescription) | |||
.containsOnly( | |||
tuple("7.0", "Unset a quality gate is no more authorized")); | |||
assertThat(unsetDefault.isPost()).isTrue(); | |||
assertThat(unsetDefault.handler()).isEqualTo(RemovedWebServiceHandler.INSTANCE); | |||
assertThat(unsetDefault.responseExample()).isEqualTo(RemovedWebServiceHandler.INSTANCE.getResponseExample()); | |||
assertThat(unsetDefault.isInternal()).isFalse(); | |||
} | |||
@Test | |||
public void search_with_query() throws Exception { | |||
long gateId = 12345L; | |||
Association assoc = mock(Association.class); | |||
when(assoc.hasMoreResults()).thenReturn(true); | |||
List<ProjectQgateAssociation> projects = ImmutableList.of( | |||
new ProjectQgateAssociation().setId(42L).setName("Project One").setMember(false), | |||
new ProjectQgateAssociation().setId(24L).setName("Project Two").setMember(true)); | |||
when(assoc.projects()).thenReturn(projects); | |||
when(projectFinder.find(any(ProjectQgateAssociationQuery.class))).thenReturn(assoc); | |||
tester.newGetRequest("api/qualitygates", "search") | |||
.setParam("gateId", Long.toString(gateId)) | |||
.setParam("query", "Project") | |||
.execute() | |||
.assertJson("{\"more\":true,\"results\":[" | |||
+ "{\"id\":42,\"name\":\"Project One\",\"selected\":false}," | |||
+ "{\"id\":24,\"name\":\"Project Two\",\"selected\":true}" | |||
+ "]}"); | |||
ArgumentCaptor<ProjectQgateAssociationQuery> queryCaptor = ArgumentCaptor.forClass(ProjectQgateAssociationQuery.class); | |||
verify(projectFinder).find(queryCaptor.capture()); | |||
ProjectQgateAssociationQuery query = queryCaptor.getValue(); | |||
assertThat(query.membership()).isEqualTo(ProjectQgateAssociationQuery.ANY); | |||
} | |||
@Test | |||
public void search_nominal() throws Exception { | |||
long gateId = 12345L; | |||
Association assoc = mock(Association.class); | |||
when(assoc.hasMoreResults()).thenReturn(true); | |||
List<ProjectQgateAssociation> projects = ImmutableList.of( | |||
new ProjectQgateAssociation().setId(24L).setName("Project Two").setMember(true)); | |||
when(assoc.projects()).thenReturn(projects); | |||
when(projectFinder.find(any(ProjectQgateAssociationQuery.class))).thenReturn(assoc); | |||
tester.newGetRequest("api/qualitygates", "search") | |||
.setParam("gateId", Long.toString(gateId)) | |||
.execute() | |||
.assertJson("{\"more\":true,\"results\":[" | |||
+ "{\"id\":24,\"name\":\"Project Two\",\"selected\":true}" | |||
+ "]}"); | |||
ArgumentCaptor<ProjectQgateAssociationQuery> queryCaptor = ArgumentCaptor.forClass(ProjectQgateAssociationQuery.class); | |||
verify(projectFinder).find(queryCaptor.capture()); | |||
ProjectQgateAssociationQuery query = queryCaptor.getValue(); | |||
assertThat(query.membership()).isEqualTo(ProjectQgateAssociationQuery.IN); | |||
} | |||
} |
@@ -0,0 +1,177 @@ | |||
/* | |||
* 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.qualitygate.ws; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.organization.OrganizationDto; | |||
import org.sonar.db.property.PropertyDto; | |||
import org.sonar.db.qualitygate.QualityGateDto; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
import org.sonar.server.organization.TestDefaultOrganizationProvider; | |||
import org.sonar.server.qualitygate.QgateProjectFinder; | |||
import org.sonar.server.tester.UserSessionRule; | |||
import org.sonar.server.ws.WsActionTester; | |||
import org.sonarqube.ws.Qualitygates.SearchResponse; | |||
import static java.lang.String.format; | |||
import static java.lang.String.valueOf; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.AssertionsForClassTypes.tuple; | |||
import static org.sonar.api.utils.System2.INSTANCE; | |||
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_GATES; | |||
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_GATE_ID; | |||
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_ORGANIZATION; | |||
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PAGE; | |||
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PAGE_SIZE; | |||
public class SearchActionTest { | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
@Rule | |||
public UserSessionRule userSession = UserSessionRule.standalone(); | |||
@Rule | |||
public DbTester db = DbTester.create(INSTANCE); | |||
private DbClient dbClient = db.getDbClient(); | |||
private DbSession dbSession = db.getSession(); | |||
private TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); | |||
private QgateProjectFinder projectFinder = new QgateProjectFinder(dbClient, userSession); | |||
private QualityGatesWsSupport wsSupport = new QualityGatesWsSupport(dbClient, userSession, defaultOrganizationProvider); | |||
private SearchAction underTest = new SearchAction(dbClient, projectFinder, wsSupport); | |||
private WsActionTester ws = new WsActionTester(underTest); | |||
@Test | |||
public void definition() { | |||
WebService.Action action = ws.getDef(); | |||
assertThat(action).isNotNull(); | |||
assertThat(action.isInternal()).isFalse(); | |||
assertThat(action.isPost()).isFalse(); | |||
assertThat(action.responseExampleAsString()).isNotEmpty(); | |||
assertThat(action.params()) | |||
.extracting(WebService.Param::key, WebService.Param::isRequired) | |||
.containsExactlyInAnyOrder( | |||
tuple("gateId", true), | |||
tuple("query", false), | |||
tuple("organization", false), | |||
tuple("selected", false), | |||
tuple("page", false), | |||
tuple("pageSize", false)); | |||
} | |||
@Test | |||
public void search_projects_of_a_quality_gate_from_an_organization() { | |||
OrganizationDto organization = db.organizations().insert(); | |||
ComponentDto project = db.components().insertPublicProject(organization); | |||
QualityGateDto qualityGate = db.qualityGates().insertQualityGate(organization); | |||
associateProjectToQualityGate(project, qualityGate); | |||
userSession.addPermission(ADMINISTER_QUALITY_GATES, organization); | |||
SearchResponse response = ws.newRequest() | |||
.setParam(PARAM_GATE_ID, valueOf(qualityGate.getId())) | |||
.setParam(PARAM_ORGANIZATION, valueOf(organization.getKey())) | |||
.executeProtobuf(SearchResponse.class); | |||
assertThat(response).isNotNull(); | |||
assertThat(response.getMore()).isFalse(); | |||
assertThat(response.getResultsCount()).isEqualTo(1); | |||
assertThat(response.getResults(0).getId()).isEqualTo(project.getId()); | |||
assertThat(response.getResults(0).getName().substring(4)).isEqualTo(project.getKey().substring(3)); | |||
} | |||
@Test | |||
public void search_on_default_organization_when_none_is_provided() { | |||
OrganizationDto defaultOrganization = db.getDefaultOrganization(); | |||
ComponentDto project = db.components().insertPublicProject(defaultOrganization); | |||
QualityGateDto qualityGate = db.qualityGates().insertQualityGate(defaultOrganization); | |||
associateProjectToQualityGate(project, qualityGate); | |||
userSession.addPermission(ADMINISTER_QUALITY_GATES, defaultOrganization); | |||
SearchResponse response = ws.newRequest() | |||
.setParam(PARAM_GATE_ID, valueOf(qualityGate.getId())) | |||
.executeProtobuf(SearchResponse.class); | |||
assertThat(response).isNotNull(); | |||
assertThat(response.getMore()).isFalse(); | |||
assertThat(response.getResultsCount()).isEqualTo(1); | |||
assertThat(response.getResults(0).getId()).isEqualTo(project.getId()); | |||
assertThat(response.getResults(0).getName().substring(4)).isEqualTo(project.getKey().substring(3)); | |||
} | |||
@Test | |||
public void fail_when_quality_gates_does_not_belong_to_organization() { | |||
OrganizationDto organization = db.organizations().insert(); | |||
OrganizationDto otherOrganization = db.organizations().insert(); | |||
QualityGateDto qualityGate = db.qualityGates().insertQualityGate(otherOrganization); | |||
expectedException.expect(NotFoundException.class); | |||
expectedException.expectMessage(format("Quality gate '%s' does not exists.", qualityGate.getId())); | |||
ws.newRequest() | |||
.setParam(PARAM_GATE_ID, valueOf(qualityGate.getId())) | |||
.setParam(PARAM_ORGANIZATION, valueOf(organization.getKey())) | |||
.executeProtobuf(SearchResponse.class); | |||
} | |||
@Test | |||
public void more_is_true_when_not_all_project_fit_in_page_size() { | |||
OrganizationDto organization = db.organizations().insert(); | |||
QualityGateDto qualityGate = db.qualityGates().insertQualityGate(organization); | |||
for (int i = 0; i < 20; i++) { | |||
ComponentDto project1 = db.components().insertPublicProject(); | |||
associateProjectToQualityGate(project1, qualityGate); | |||
} | |||
userSession.addPermission(ADMINISTER_QUALITY_GATES, organization); | |||
SearchResponse response = ws.newRequest() | |||
.setParam(PARAM_GATE_ID, valueOf(qualityGate.getId())) | |||
.setParam(PARAM_ORGANIZATION, valueOf(organization.getKey())) | |||
.setParam(PARAM_PAGE_SIZE, valueOf(10)) | |||
.setParam(PARAM_PAGE, valueOf(1)) | |||
.executeProtobuf(SearchResponse.class); | |||
assertThat(response).isNotNull(); | |||
assertThat(response.getMore()).isTrue(); | |||
assertThat(response.getResultsCount()).isEqualTo(10); | |||
} | |||
private void associateProjectToQualityGate(ComponentDto componentDto, QualityGateDto qualityGateDto) { | |||
dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto() | |||
.setKey("sonar.qualitygate") | |||
.setResourceId(componentDto.getId()) | |||
.setValue(valueOf(qualityGateDto.getId()))); | |||
db.commit(); | |||
} | |||
} |
@@ -136,6 +136,18 @@ message ShowWsResponse { | |||
} | |||
} | |||
// GET api/qualitygates/search | |||
message SearchResponse { | |||
optional bool more = 1; | |||
repeated Result results = 2; | |||
message Result { | |||
optional int64 id = 1; | |||
optional string name = 2; | |||
optional bool selected = 3; | |||
} | |||
} | |||
// GET api/qualitygates/list | |||
message ListWsResponse { | |||
repeated QualityGate qualitygates = 1; |