Also take the opportunity to move business from QualityGates to ShowActiontags/7.0-RC1
@@ -19,6 +19,8 @@ | |||
*/ | |||
package org.sonar.db.qualitygate; | |||
import java.util.Arrays; | |||
import java.util.function.Consumer; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.DbTester; | |||
@@ -72,12 +74,14 @@ public class QualityGateDbTester { | |||
db.commit(); | |||
} | |||
public QualityGateConditionDto addCondition(QualityGateDto qualityGate, MetricDto metric) { | |||
@SafeVarargs | |||
public final QualityGateConditionDto addCondition(QualityGateDto qualityGate, MetricDto metric, Consumer<QualityGateConditionDto>... dtoPopulators) { | |||
QualityGateConditionDto condition = new QualityGateConditionDto().setQualityGateId(qualityGate.getId()) | |||
.setMetricId(metric.getId()) | |||
.setOperator("GT") | |||
.setWarningThreshold(randomNumeric(10)) | |||
.setErrorThreshold(randomNumeric(10)); | |||
Arrays.stream(dtoPopulators).forEach(dtoPopulator -> dtoPopulator.accept(condition)); | |||
dbClient.gateConditionDao().insert(condition, dbSession); | |||
db.commit(); | |||
return condition; |
@@ -21,14 +21,15 @@ package org.sonar.server.qualitygate; | |||
import java.util.Optional; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.property.PropertyDto; | |||
import org.sonar.db.qualitygate.QualityGateDto; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
import static org.apache.commons.lang.StringUtils.isBlank; | |||
import static org.sonar.server.qualitygate.QualityGates.SONAR_QUALITYGATE_PROPERTY; | |||
import static org.sonar.server.ws.WsUtils.checkFound; | |||
public class QualityGateFinder { | |||
@@ -47,7 +48,7 @@ public class QualityGateFinder { | |||
public Optional<QualityGateData> getQualityGate(DbSession dbSession, long componentId) { | |||
Optional<Long> qualityGateId = dbClient.projectQgateAssociationDao().selectQGateIdByComponentId(dbSession, componentId); | |||
if (qualityGateId.isPresent()) { | |||
return Optional.of(new QualityGateData(selectOrFailById(dbSession, qualityGateId.get()), false)); | |||
return Optional.of(new QualityGateData(getById(dbSession, qualityGateId.get()), false)); | |||
} else { | |||
QualityGateDto defaultQualityGate = getDefault(dbSession); | |||
if (defaultQualityGate == null) { | |||
@@ -57,21 +58,27 @@ public class QualityGateFinder { | |||
} | |||
} | |||
public QualityGateDto getById(DbSession dbSession, long qualityGateId) { | |||
return checkFound(dbClient.qualityGateDao().selectById(dbSession, qualityGateId), "No quality gate has been found for id %s", qualityGateId); | |||
} | |||
public QualityGateDto getByNameOrId(DbSession dbSession, @Nullable String name, @Nullable Long id) { | |||
if (name != null) { | |||
return checkFound(dbClient.qualityGateDao().selectByName(dbSession, name), "No quality gate has been found for name %s", name); | |||
} | |||
if (id != null) { | |||
return getById(dbSession, id); | |||
} | |||
throw new IllegalArgumentException("No parameter has been set to identify a quality gate"); | |||
} | |||
@CheckForNull | |||
private QualityGateDto getDefault(DbSession dbSession) { | |||
Long defaultId = getDefaultId(dbSession); | |||
if (defaultId == null) { | |||
return null; | |||
} | |||
return selectOrFailById(dbSession, defaultId); | |||
} | |||
private QualityGateDto selectOrFailById(DbSession dbSession, long qualityGateId) { | |||
QualityGateDto qualityGateDto = dbClient.qualityGateDao().selectById(dbSession, qualityGateId); | |||
if (qualityGateDto == null) { | |||
throw new NotFoundException(String.format("No quality gate has been found for id %s", qualityGateId)); | |||
} | |||
return qualityGateDto; | |||
return getById(dbSession, defaultId); | |||
} | |||
@CheckForNull | |||
@@ -101,4 +108,5 @@ public class QualityGateFinder { | |||
return isDefault; | |||
} | |||
} | |||
} |
@@ -26,8 +26,6 @@ import java.util.List; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.measures.Metric; | |||
import org.sonar.api.measures.MetricFinder; | |||
import org.sonar.api.web.UserRole; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
@@ -47,11 +45,10 @@ import static java.lang.String.format; | |||
import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException; | |||
import static org.sonar.server.util.Validation.CANT_BE_EMPTY_MESSAGE; | |||
import static org.sonar.server.util.Validation.IS_ALREADY_USED_MESSAGE; | |||
import static org.sonar.server.ws.WsUtils.checkFound; | |||
import static org.sonar.server.ws.WsUtils.checkRequest; | |||
/** | |||
* Methods from this class should be moved to {@link QualityGateUpdater} and to new classes QualityGateFinder / QualityGateConditionsUpdater / etc. | |||
* Methods from this class should be moved to {@link QualityGateUpdater} and to classes QualityGateFinder / QualityGateConditionsUpdater / etc. | |||
* in order to have classes with clearer responsibilities and more easily testable (without having to use too much mocks) | |||
*/ | |||
public class QualityGates { | |||
@@ -61,29 +58,19 @@ public class QualityGates { | |||
private final DbClient dbClient; | |||
private final QualityGateDao dao; | |||
private final QualityGateConditionDao conditionDao; | |||
private final MetricFinder metricFinder; | |||
private final PropertiesDao propertiesDao; | |||
private final UserSession userSession; | |||
private final DefaultOrganizationProvider organizationProvider; | |||
public QualityGates(DbClient dbClient, MetricFinder metricFinder, UserSession userSession, DefaultOrganizationProvider organizationProvider) { | |||
public QualityGates(DbClient dbClient, UserSession userSession, DefaultOrganizationProvider organizationProvider) { | |||
this.dbClient = dbClient; | |||
this.dao = dbClient.qualityGateDao(); | |||
this.conditionDao = dbClient.gateConditionDao(); | |||
this.metricFinder = metricFinder; | |||
this.propertiesDao = dbClient.propertiesDao(); | |||
this.userSession = userSession; | |||
this.organizationProvider = organizationProvider; | |||
} | |||
public QualityGateDto get(Long qGateId) { | |||
return getNonNullQgate(qGateId); | |||
} | |||
public QualityGateDto get(String qGateName) { | |||
return getNonNullQgate(qGateName); | |||
} | |||
public QualityGateDto rename(long idToRename, String name) { | |||
checkIsQualityGateAdministrator(); | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
@@ -161,20 +148,6 @@ public class QualityGates { | |||
} | |||
} | |||
public Collection<QualityGateConditionDto> listConditions(long qGateId) { | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
Collection<QualityGateConditionDto> conditionsForGate = conditionDao.selectForQualityGate(dbSession, qGateId); | |||
for (QualityGateConditionDto condition : conditionsForGate) { | |||
Metric metric = metricFinder.findById((int) condition.getMetricId()); | |||
if (metric == null) { | |||
throw new IllegalStateException("Could not find metric with id " + condition.getMetricId()); | |||
} | |||
condition.setMetricKey(metric.getKey()); | |||
} | |||
return conditionsForGate; | |||
} | |||
} | |||
public void dissociateProject(DbSession dbSession, ComponentDto project) { | |||
checkProjectAdmin(project); | |||
propertiesDao.deleteProjectProperty(SONAR_QUALITYGATE_PROPERTY, project.getId(), dbSession); | |||
@@ -207,12 +180,6 @@ public class QualityGates { | |||
return qGate; | |||
} | |||
private QualityGateDto getNonNullQgate(String name) { | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
return checkFound(dao.selectByName(dbSession, name), "There is no quality gate with name=%s", name); | |||
} | |||
} | |||
private void validateQualityGate(DbSession dbSession, @Nullable Long updatingQgateId, @Nullable String name) { | |||
List<String> errors = new ArrayList<>(); | |||
if (Strings.isNullOrEmpty(name)) { |
@@ -105,22 +105,4 @@ public class QualityGatesWs implements WebService { | |||
.endObject(); | |||
} | |||
static JsonWriter writeQualityGateCondition(QualityGateConditionDto condition, JsonWriter writer) { | |||
writer.beginObject() | |||
.prop(PARAM_ID, condition.getId()) | |||
.prop(PARAM_METRIC, condition.getMetricKey()) | |||
.prop(PARAM_OPERATOR, condition.getOperator()); | |||
if (condition.getWarningThreshold() != null) { | |||
writer.prop(PARAM_WARNING, condition.getWarningThreshold()); | |||
} | |||
if (condition.getErrorThreshold() != null) { | |||
writer.prop(PARAM_ERROR, condition.getErrorThreshold()); | |||
} | |||
if (condition.getPeriod() != null) { | |||
writer.prop(PARAM_PERIOD, condition.getPeriod()); | |||
} | |||
writer.endObject(); | |||
return writer; | |||
} | |||
} |
@@ -21,25 +21,39 @@ package org.sonar.server.qualitygate.ws; | |||
import com.google.common.io.Resources; | |||
import java.util.Collection; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import java.util.function.Function; | |||
import javax.annotation.Nullable; | |||
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.utils.text.JsonWriter; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.metric.MetricDto; | |||
import org.sonar.db.qualitygate.QualityGateConditionDto; | |||
import org.sonar.db.qualitygate.QualityGateDto; | |||
import org.sonar.server.qualitygate.QualityGates; | |||
import org.sonar.server.qualitygate.QualityGateFinder; | |||
import org.sonarqube.ws.Qualitygates.ShowWsResponse; | |||
import static org.sonar.server.ws.WsUtils.checkRequest; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static com.google.common.base.Preconditions.checkState; | |||
import static org.sonar.core.util.Protobuf.setNullable; | |||
import static org.sonar.core.util.stream.MoreCollectors.toList; | |||
import static org.sonar.core.util.stream.MoreCollectors.toSet; | |||
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex; | |||
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_ID; | |||
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_NAME; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
public class ShowAction implements QualityGatesWsAction { | |||
private final QualityGates qualityGates; | |||
private final DbClient dbClient; | |||
private final QualityGateFinder qualityGateFinder; | |||
public ShowAction(QualityGates qualityGates) { | |||
this.qualityGates = qualityGates; | |||
public ShowAction(DbClient dbClient, QualityGateFinder qualityGateFinder) { | |||
this.dbClient = dbClient; | |||
this.qualityGateFinder = qualityGateFinder; | |||
} | |||
@Override | |||
@@ -47,7 +61,7 @@ public class ShowAction implements QualityGatesWsAction { | |||
WebService.NewAction action = controller.createAction("show") | |||
.setDescription("Display the details of a quality gate") | |||
.setSince("4.3") | |||
.setResponseExample(Resources.getResource(this.getClass(), "example-show.json")) | |||
.setResponseExample(Resources.getResource(this.getClass(), "show-example.json")) | |||
.setHandler(this); | |||
action.createParam(PARAM_ID) | |||
@@ -61,30 +75,56 @@ public class ShowAction implements QualityGatesWsAction { | |||
@Override | |||
public void handle(Request request, Response response) { | |||
Long qGateId = request.paramAsLong(PARAM_ID); | |||
String qGateName = request.param(PARAM_NAME); | |||
checkOneOfIdOrNamePresent(qGateId, qGateName); | |||
Long id = request.paramAsLong(PARAM_ID); | |||
String name = request.param(PARAM_NAME); | |||
checkOneOfIdOrNamePresent(id, name); | |||
QualityGateDto qGate = qGateId == null ? qualityGates.get(qGateName) : qualityGates.get(qGateId); | |||
qGateId = qGate.getId(); | |||
try (JsonWriter writer = response.newJsonWriter()) { | |||
writer.beginObject() | |||
.prop(PARAM_ID, qGate.getId()) | |||
.prop(PARAM_NAME, qGate.getName()); | |||
Collection<QualityGateConditionDto> conditions = qualityGates.listConditions(qGateId); | |||
if (!conditions.isEmpty()) { | |||
writer.name("conditions").beginArray(); | |||
for (QualityGateConditionDto condition : conditions) { | |||
QualityGatesWs.writeQualityGateCondition(condition, writer); | |||
} | |||
writer.endArray(); | |||
} | |||
writer.endObject().close(); | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
QualityGateDto qualityGate = qualityGateFinder.getByNameOrId(dbSession, name, id); | |||
Collection<QualityGateConditionDto> conditions = getConditions(dbSession, qualityGate); | |||
Map<Integer, MetricDto> metricsById = getMetricsById(dbSession, conditions); | |||
writeProtobuf(buildResponse(qualityGate, conditions, metricsById), request, response); | |||
} | |||
} | |||
public Collection<QualityGateConditionDto> getConditions(DbSession dbSession, QualityGateDto qualityGate) { | |||
return dbClient.gateConditionDao().selectForQualityGate(dbSession, qualityGate.getId()); | |||
} | |||
private Map<Integer, MetricDto> getMetricsById(DbSession dbSession, Collection<QualityGateConditionDto> conditions) { | |||
Set<Integer> metricIds = conditions.stream().map(c -> (int) c.getMetricId()).collect(toSet()); | |||
return dbClient.metricDao().selectByIds(dbSession, metricIds).stream() | |||
.filter(MetricDto::isEnabled) | |||
.collect(uniqueIndex(MetricDto::getId)); | |||
} | |||
private static ShowWsResponse buildResponse(QualityGateDto qualityGate, Collection<QualityGateConditionDto> conditions, Map<Integer, MetricDto> metricsById) { | |||
return ShowWsResponse.newBuilder() | |||
.setId(qualityGate.getId()) | |||
.setName(qualityGate.getName()) | |||
.addAllConditions(conditions.stream() | |||
.map(toWsCondition(metricsById)) | |||
.collect(toList())) | |||
.build(); | |||
} | |||
private static Function<QualityGateConditionDto, ShowWsResponse.Condition> toWsCondition(Map<Integer, MetricDto> metricsById) { | |||
return condition -> { | |||
int metricId = (int) condition.getMetricId(); | |||
MetricDto metric = metricsById.get(metricId); | |||
checkState(metric != null, "Could not find metric with id %s", metricId); | |||
ShowWsResponse.Condition.Builder builder = ShowWsResponse.Condition.newBuilder() | |||
.setId(condition.getId()) | |||
.setMetric(metric.getKey()) | |||
.setOp(condition.getOperator()); | |||
setNullable(condition.getPeriod(), builder::setPeriod); | |||
setNullable(condition.getErrorThreshold(), builder::setError); | |||
setNullable(condition.getWarningThreshold(), builder::setWarning); | |||
return builder.build(); | |||
}; | |||
} | |||
private static void checkOneOfIdOrNamePresent(@Nullable Long qGateId, @Nullable String qGateName) { | |||
checkRequest(qGateId == null ^ qGateName == null, "Either '%s' or '%s' must be provided", PARAM_ID, PARAM_NAME); | |||
checkArgument(qGateId == null ^ qGateName == null, "Either '%s' or '%s' must be provided", PARAM_ID, PARAM_NAME); | |||
} | |||
} |
@@ -11,7 +11,8 @@ | |||
{ | |||
"id": 10, | |||
"metric": "critical_violations", | |||
"op": "GT", | |||
"period": 1, | |||
"op": "LT", | |||
"warning": "0" | |||
} | |||
] |
@@ -27,7 +27,6 @@ import org.sonar.api.utils.System2; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.component.ComponentTesting; | |||
import org.sonar.db.qualitygate.QualityGateDto; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
@@ -39,16 +38,16 @@ public class QualityGateFinderTest { | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
@Rule | |||
public DbTester dbTester = DbTester.create(System2.INSTANCE); | |||
public DbTester db = DbTester.create(System2.INSTANCE); | |||
private DbSession dbSession = dbTester.getSession(); | |||
private DbSession dbSession = db.getSession(); | |||
private QualityGateFinder underTest = new QualityGateFinder(dbTester.getDbClient()); | |||
private QualityGateFinder underTest = new QualityGateFinder(db.getDbClient()); | |||
@Test | |||
public void return_default_quality_gate_for_project() { | |||
ComponentDto project = dbTester.components().insertComponent(ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization())); | |||
QualityGateDto dbQualityGate = dbTester.qualityGates().createDefaultQualityGate("Sonar way"); | |||
ComponentDto project = db.components().insertPrivateProject(); | |||
QualityGateDto dbQualityGate = db.qualityGates().createDefaultQualityGate("Sonar way"); | |||
Optional<QualityGateFinder.QualityGateData> result = underTest.getQualityGate(dbSession, project.getId()); | |||
@@ -59,10 +58,10 @@ public class QualityGateFinderTest { | |||
@Test | |||
public void return_project_quality_gate_over_default() { | |||
ComponentDto project = dbTester.components().insertComponent(ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert())); | |||
dbTester.qualityGates().createDefaultQualityGate("Sonar way"); | |||
QualityGateDto dbQualityGate = dbTester.qualityGates().insertQualityGate("My team QG"); | |||
dbTester.qualityGates().associateProjectToQualityGate(project, dbQualityGate); | |||
ComponentDto project = db.components().insertPrivateProject(); | |||
db.qualityGates().createDefaultQualityGate("Sonar way"); | |||
QualityGateDto dbQualityGate = db.qualityGates().insertQualityGate("My team QG"); | |||
db.qualityGates().associateProjectToQualityGate(project, dbQualityGate); | |||
Optional<QualityGateFinder.QualityGateData> result = underTest.getQualityGate(dbSession, project.getId()); | |||
@@ -73,7 +72,7 @@ public class QualityGateFinderTest { | |||
@Test | |||
public void return_nothing_when_no_default_qgate_and_no_qgate_defined_for_project() { | |||
ComponentDto project = dbTester.components().insertComponent(ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization())); | |||
ComponentDto project = db.components().insertPrivateProject(); | |||
Optional<QualityGateFinder.QualityGateData> result = underTest.getQualityGate(dbSession, project.getId()); | |||
@@ -82,9 +81,9 @@ public class QualityGateFinderTest { | |||
@Test | |||
public void fail_when_default_qgate_defined_in_properties_does_not_exists() throws Exception { | |||
ComponentDto project = dbTester.components().insertComponent(ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert())); | |||
QualityGateDto dbQualityGate = dbTester.qualityGates().createDefaultQualityGate("Sonar way"); | |||
dbTester.getDbClient().qualityGateDao().delete(dbQualityGate, dbSession); | |||
ComponentDto project = db.components().insertPrivateProject(); | |||
QualityGateDto dbQualityGate = db.qualityGates().createDefaultQualityGate("Sonar way"); | |||
db.getDbClient().qualityGateDao().delete(dbQualityGate, dbSession); | |||
expectedException.expect(NotFoundException.class); | |||
underTest.getQualityGate(dbSession, project.getId()); | |||
@@ -92,12 +91,44 @@ public class QualityGateFinderTest { | |||
@Test | |||
public void fail_when_project_qgate_defined_in_properties_does_not_exists() throws Exception { | |||
ComponentDto project = dbTester.components().insertComponent(ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization())); | |||
QualityGateDto dbQualityGate = dbTester.qualityGates().insertQualityGate("My team QG"); | |||
dbTester.qualityGates().associateProjectToQualityGate(project, dbQualityGate); | |||
dbTester.getDbClient().qualityGateDao().delete(dbQualityGate, dbSession); | |||
ComponentDto project = db.components().insertPrivateProject(); | |||
QualityGateDto dbQualityGate = db.qualityGates().insertQualityGate("My team QG"); | |||
db.qualityGates().associateProjectToQualityGate(project, dbQualityGate); | |||
db.getDbClient().qualityGateDao().delete(dbQualityGate, dbSession); | |||
expectedException.expect(NotFoundException.class); | |||
underTest.getQualityGate(dbSession, project.getId()); | |||
} | |||
@Test | |||
public void get_by_name_or_id() { | |||
QualityGateDto qualityGate = db.qualityGates().insertQualityGate(); | |||
assertThat(underTest.getByNameOrId(db.getSession(), qualityGate.getName(), null)).isNotNull(); | |||
assertThat(underTest.getByNameOrId(db.getSession(), null, qualityGate.getId())).isNotNull(); | |||
} | |||
@Test | |||
public void fail_get_by_name_or_id_when_name_matches_nothing() { | |||
expectedException.expect(NotFoundException.class); | |||
expectedException.expectMessage("No quality gate has been found for name UNKNOWN"); | |||
underTest.getByNameOrId(db.getSession(), "UNKNOWN", null); | |||
} | |||
@Test | |||
public void fail_get_by_name_or_id_when_id_matches_nothing() { | |||
expectedException.expect(NotFoundException.class); | |||
expectedException.expectMessage("No quality gate has been found for id 123"); | |||
underTest.getByNameOrId(db.getSession(), null, 123L); | |||
} | |||
@Test | |||
public void fail_get_by_name_or_id_when_parameters_are_null() { | |||
expectedException.expect(IllegalArgumentException.class); | |||
expectedException.expectMessage("No parameter has been set to identify a quality gate"); | |||
underTest.getByNameOrId(db.getSession(), null, null); | |||
} | |||
} |
@@ -22,7 +22,6 @@ package org.sonar.server.qualitygate; | |||
import com.google.common.collect.ImmutableList; | |||
import com.google.common.collect.Lists; | |||
import java.util.Collection; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
@@ -30,10 +29,6 @@ import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.mockito.ArgumentCaptor; | |||
import org.mockito.Mockito; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.api.measures.Metric; | |||
import org.sonar.api.measures.Metric.ValueType; | |||
import org.sonar.api.measures.MetricFinder; | |||
import org.sonar.core.util.Uuids; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
@@ -80,7 +75,6 @@ public class QualityGatesTest { | |||
private QualityGateConditionDao conditionDao = mock(QualityGateConditionDao.class); | |||
private PropertiesDao propertiesDao = mock(PropertiesDao.class); | |||
private ComponentDao componentDao = mock(ComponentDao.class); | |||
private MetricFinder metricFinder = mock(MetricFinder.class); | |||
private QualityGates underTest; | |||
@Before | |||
@@ -94,7 +88,7 @@ public class QualityGatesTest { | |||
when(componentDao.selectOrFailById(eq(dbSession), anyLong())).thenReturn( | |||
newPrivateProjectDto(OrganizationTesting.newOrganizationDto(), PROJECT_UUID).setId(1L).setDbKey(PROJECT_KEY)); | |||
underTest = new QualityGates(dbClient, metricFinder, userSession, organizationProvider); | |||
underTest = new QualityGates(dbClient, userSession, organizationProvider); | |||
userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_GATES, organizationProvider.get().getUuid()); | |||
} | |||
@@ -106,31 +100,6 @@ public class QualityGatesTest { | |||
assertThat(underTest.list()).isEqualTo(allQgates); | |||
} | |||
@Test | |||
public void should_get_qgate_by_id() { | |||
long id = QUALITY_GATE_ID; | |||
final String name = "Golden"; | |||
QualityGateDto existing = new QualityGateDto().setId(id).setName(name); | |||
when(dao.selectById(dbSession, id)).thenReturn(existing); | |||
assertThat(underTest.get(id)).isEqualTo(existing); | |||
verify(dao).selectById(dbSession, id); | |||
} | |||
@Test | |||
public void should_get_qgate_by_name() { | |||
long id = QUALITY_GATE_ID; | |||
final String name = "Golden"; | |||
QualityGateDto existing = new QualityGateDto().setId(id).setName(name); | |||
when(dao.selectByName(dbSession, name)).thenReturn(existing); | |||
assertThat(underTest.get(name)).isEqualTo(existing); | |||
verify(dao).selectByName(dbSession, name); | |||
} | |||
@Test(expected = NotFoundException.class) | |||
public void should_fail_to_find_qgate_by_name() { | |||
underTest.get("Does not exist"); | |||
} | |||
@Test | |||
public void should_rename_qgate() { | |||
long id = QUALITY_GATE_ID; | |||
@@ -246,45 +215,6 @@ public class QualityGatesTest { | |||
assertThat(underTest.getDefault()).isNull(); | |||
} | |||
@Test | |||
public void should_list_conditions() { | |||
long qGateId = QUALITY_GATE_ID; | |||
long metric1Id = 1L; | |||
String metric1Key = "polop"; | |||
long metric2Id = 2L; | |||
String metric2Key = "palap"; | |||
QualityGateConditionDto cond1 = new QualityGateConditionDto().setMetricId(metric1Id); | |||
QualityGateConditionDto cond2 = new QualityGateConditionDto().setMetricId(metric2Id); | |||
Collection<QualityGateConditionDto> conditions = ImmutableList.of(cond1, cond2); | |||
when(conditionDao.selectForQualityGate(dbSession, qGateId)).thenReturn(conditions); | |||
Metric metric1 = mock(Metric.class); | |||
when(metric1.getKey()).thenReturn(metric1Key); | |||
when(metricFinder.findById((int) metric1Id)).thenReturn(metric1); | |||
Metric metric2 = mock(Metric.class); | |||
when(metric2.getKey()).thenReturn(metric2Key); | |||
when(metricFinder.findById((int) metric2Id)).thenReturn(metric2); | |||
assertThat(underTest.listConditions(qGateId)).isEqualTo(conditions); | |||
Iterator<QualityGateConditionDto> iterator = conditions.iterator(); | |||
assertThat(iterator.next().getMetricKey()).isEqualTo(metric1Key); | |||
assertThat(iterator.next().getMetricKey()).isEqualTo(metric2Key); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void should_do_a_sanity_check_when_listing_conditions() { | |||
long qGateId = QUALITY_GATE_ID; | |||
long metric1Id = 1L; | |||
String metric1Key = "polop"; | |||
long metric2Id = 2L; | |||
QualityGateConditionDto cond1 = new QualityGateConditionDto().setMetricId(metric1Id); | |||
QualityGateConditionDto cond2 = new QualityGateConditionDto().setMetricId(metric2Id); | |||
Collection<QualityGateConditionDto> conditions = ImmutableList.of(cond1, cond2); | |||
when(conditionDao.selectForQualityGate(dbSession, qGateId)).thenReturn(conditions); | |||
Metric metric1 = mock(Metric.class); | |||
when(metric1.getKey()).thenReturn(metric1Key); | |||
when(metricFinder.findById((int) metric1Id)).thenReturn(metric1); | |||
underTest.listConditions(qGateId); | |||
} | |||
@Test | |||
public void should_copy_qgate() { | |||
String name = "Atlantis"; | |||
@@ -310,21 +240,4 @@ public class QualityGatesTest { | |||
verify(conditionDao, times(conditions.size())).insert(any(QualityGateConditionDto.class), eq(dbSession)); | |||
} | |||
@Test | |||
public void should_list_gate_metrics() { | |||
Metric dataMetric = mock(Metric.class); | |||
when(dataMetric.isDataType()).thenReturn(true); | |||
Metric hiddenMetric = mock(Metric.class); | |||
when(hiddenMetric.isHidden()).thenReturn(true); | |||
Metric nullHiddenMetric = mock(Metric.class); | |||
when(nullHiddenMetric.isHidden()).thenReturn(null); | |||
Metric alertMetric = CoreMetrics.ALERT_STATUS; | |||
Metric ratingMetric = mock(Metric.class); | |||
when(ratingMetric.getType()).thenReturn(ValueType.RATING); | |||
Metric classicMetric = mock(Metric.class); | |||
when(classicMetric.getType()).thenReturn(ValueType.BOOL); | |||
when(metricFinder.findAll()).thenReturn(ImmutableList.of( | |||
dataMetric, hiddenMetric, nullHiddenMetric, alertMetric, ratingMetric, classicMetric)); | |||
} | |||
} |
@@ -23,7 +23,6 @@ import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.api.measures.MetricFinder; | |||
import org.sonar.api.server.ws.Change; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.api.utils.System2; | |||
@@ -47,7 +46,6 @@ import org.sonar.server.ws.WsActionTester; | |||
import static java.lang.String.format; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.tuple; | |||
import static org.mockito.Mockito.mock; | |||
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES; | |||
import static org.sonar.server.qualitygate.QualityGates.SONAR_QUALITYGATE_PROPERTY; | |||
@@ -63,7 +61,7 @@ public class DeselectActionTest { | |||
private DbClient dbClient = db.getDbClient(); | |||
private DbSession dbSession = db.getSession(); | |||
private TestDefaultOrganizationProvider organizationProvider = TestDefaultOrganizationProvider.from(db); | |||
private QualityGates qualityGates = new QualityGates(dbClient, mock(MetricFinder.class), userSession, organizationProvider); | |||
private QualityGates qualityGates = new QualityGates(dbClient, userSession, organizationProvider); | |||
private WsActionTester ws; | |||
private ComponentDto project; | |||
private QualityGateDto gate; | |||
@@ -87,8 +85,7 @@ public class DeselectActionTest { | |||
assertThat(def.isPost()).isTrue(); | |||
assertThat(def.since()).isEqualTo("4.3"); | |||
assertThat(def.changelog()).extracting(Change::getVersion, Change::getDescription).containsExactly( | |||
tuple("6.6", "The parameter 'gateId' was removed") | |||
); | |||
tuple("6.6", "The parameter 'gateId' was removed")); | |||
assertThat(def.params()).extracting(WebService.Param::key) | |||
.containsExactlyInAnyOrder("projectId", "projectKey"); |
@@ -33,7 +33,6 @@ import org.sonar.api.server.ws.WebService.Controller; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.qualitygate.ProjectQgateAssociation; | |||
import org.sonar.db.qualitygate.ProjectQgateAssociationQuery; | |||
import org.sonar.db.qualitygate.QualityGateConditionDto; | |||
import org.sonar.db.qualitygate.QualityGateDto; | |||
import org.sonar.server.component.ComponentFinder; | |||
import org.sonar.server.exceptions.BadRequestException; | |||
@@ -70,7 +69,6 @@ public class QualityGatesWsTest { | |||
tester = new WsTester(new QualityGatesWs( | |||
new ListAction(qGates), | |||
new ShowAction(qGates), | |||
new SearchAction(projectFinder), | |||
new CreateAction(null, null, null, null), | |||
new CopyAction(qGates), | |||
@@ -90,7 +88,7 @@ public class QualityGatesWsTest { | |||
assertThat(controller).isNotNull(); | |||
assertThat(controller.path()).isEqualTo("api/qualitygates"); | |||
assertThat(controller.description()).isNotEmpty(); | |||
assertThat(controller.actions()).hasSize(15); | |||
assertThat(controller.actions()).hasSize(14); | |||
Action list = controller.action("list"); | |||
assertThat(list).isNotNull(); | |||
@@ -99,14 +97,6 @@ public class QualityGatesWsTest { | |||
assertThat(list.isPost()).isFalse(); | |||
assertThat(list.isInternal()).isFalse(); | |||
Action show = controller.action("show"); | |||
assertThat(show).isNotNull(); | |||
assertThat(show.handler()).isNotNull(); | |||
assertThat(show.since()).isEqualTo("4.3"); | |||
assertThat(show.isPost()).isFalse(); | |||
assertThat(show.param("id")).isNotNull(); | |||
assertThat(show.isInternal()).isFalse(); | |||
Action create = controller.action("create"); | |||
assertThat(create).isNotNull(); | |||
assertThat(create.handler()).isNotNull(); | |||
@@ -258,53 +248,6 @@ public class QualityGatesWsTest { | |||
"{\"qualitygates\":[{\"id\":42,\"name\":\"Golden\"},{\"id\":43,\"name\":\"Star\"},{\"id\":666,\"name\":\"Ninth\"}],\"default\":42}"); | |||
} | |||
@Test | |||
public void show_empty() throws Exception { | |||
long gateId = 12345L; | |||
when(qGates.get(gateId)).thenReturn(new QualityGateDto().setId(gateId).setName("Golden")); | |||
tester.newGetRequest("api/qualitygates", "show").setParam("id", Long.toString(gateId)).execute().assertJson( | |||
"{\"id\":12345,\"name\":\"Golden\"}"); | |||
} | |||
@Test | |||
public void show_by_id_nominal() throws Exception { | |||
long gateId = 12345L; | |||
when(qGates.get(gateId)).thenReturn(new QualityGateDto().setId(gateId).setName("Golden")); | |||
when(qGates.listConditions(gateId)).thenReturn(ImmutableList.of( | |||
new QualityGateConditionDto().setId(1L).setMetricKey("ncloc").setOperator("GT").setErrorThreshold("10000"), | |||
new QualityGateConditionDto().setId(2L).setMetricKey("new_coverage").setOperator("LT").setWarningThreshold("90").setPeriod(3))); | |||
tester.newGetRequest("api/qualitygates", "show").setParam("id", Long.toString(gateId)).execute().assertJson( | |||
"{\"id\":12345,\"name\":\"Golden\",\"conditions\":[" | |||
+ "{\"id\":1,\"metric\":\"ncloc\",\"op\":\"GT\",\"error\":\"10000\"}," | |||
+ "{\"id\":2,\"metric\":\"new_coverage\",\"op\":\"LT\",\"warning\":\"90\",\"period\":3}" | |||
+ "]}"); | |||
} | |||
@Test | |||
public void show_by_name_nominal() throws Exception { | |||
long qGateId = 12345L; | |||
String gateName = "Golden"; | |||
when(qGates.get(gateName)).thenReturn(new QualityGateDto().setId(qGateId).setName(gateName)); | |||
when(qGates.listConditions(qGateId)).thenReturn(ImmutableList.of( | |||
new QualityGateConditionDto().setId(1L).setMetricKey("ncloc").setOperator("GT").setErrorThreshold("10000"), | |||
new QualityGateConditionDto().setId(2L).setMetricKey("new_coverage").setOperator("LT").setWarningThreshold("90").setPeriod(3))); | |||
tester.newGetRequest("api/qualitygates", "show").setParam("name", gateName).execute().assertJson( | |||
"{\"id\":12345,\"name\":\"Golden\",\"conditions\":[" | |||
+ "{\"id\":1,\"metric\":\"ncloc\",\"op\":\"GT\",\"error\":\"10000\"}," | |||
+ "{\"id\":2,\"metric\":\"new_coverage\",\"op\":\"LT\",\"warning\":\"90\",\"period\":3}" | |||
+ "]}"); | |||
} | |||
@Test(expected = BadRequestException.class) | |||
public void show_without_parameters() throws Exception { | |||
tester.newGetRequest("api/qualitygates", "show").execute(); | |||
} | |||
@Test(expected = BadRequestException.class) | |||
public void show_with_both_parameters() throws Exception { | |||
tester.newGetRequest("api/qualitygates", "show").setParam("id", "12345").setParam("name", "Polop").execute(); | |||
} | |||
@Test | |||
public void search_with_query() throws Exception { | |||
long gateId = 12345L; |
@@ -0,0 +1,160 @@ | |||
/* | |||
* 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.api.server.ws.WebService.Param; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.metric.MetricDto; | |||
import org.sonar.db.qualitygate.QualityGateConditionDto; | |||
import org.sonar.db.qualitygate.QualityGateDto; | |||
import org.sonar.server.qualitygate.QualityGateFinder; | |||
import org.sonar.server.tester.UserSessionRule; | |||
import org.sonar.server.ws.WsActionTester; | |||
import org.sonarqube.ws.Qualitygates.ShowWsResponse; | |||
import org.sonarqube.ws.Qualitygates.ShowWsResponse.Condition; | |||
import static java.lang.String.format; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.AssertionsForClassTypes.tuple; | |||
import static org.sonar.test.JsonAssert.assertJson; | |||
public class ShowActionTest { | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
@Rule | |||
public UserSessionRule userSession = UserSessionRule.standalone(); | |||
@Rule | |||
public DbTester db = DbTester.create(System2.INSTANCE); | |||
private WsActionTester ws = new WsActionTester(new ShowAction(db.getDbClient(), new QualityGateFinder(db.getDbClient()))); | |||
@Test | |||
public void verify_definition() { | |||
WebService.Action action = ws.getDef(); | |||
assertThat(action.since()).isEqualTo("4.3"); | |||
assertThat(action.changelog()).isEmpty(); | |||
assertThat(action.params()) | |||
.extracting(Param::key, Param::isRequired) | |||
.containsExactlyInAnyOrder(tuple("id", false), tuple("name", false)); | |||
} | |||
@Test | |||
public void json_example() { | |||
QualityGateDto qualityGate = db.qualityGates().insertQualityGate("My Quality Gate"); | |||
MetricDto blockerViolationsMetric = db.measures().insertMetric(m -> m.setKey("blocker_violations")); | |||
MetricDto criticalViolationsMetric = db.measures().insertMetric(m -> m.setKey("critical_violations")); | |||
db.qualityGates().addCondition(qualityGate, blockerViolationsMetric, c -> c.setOperator("GT").setPeriod(null).setErrorThreshold("0").setWarningThreshold(null)); | |||
db.qualityGates().addCondition(qualityGate, criticalViolationsMetric, c -> c.setOperator("LT").setPeriod(1).setErrorThreshold(null).setWarningThreshold("0")); | |||
String response = ws.newRequest() | |||
.setParam("name", qualityGate.getName()) | |||
.execute() | |||
.getInput(); | |||
assertJson(response).ignoreFields("id") | |||
.isSimilarTo(getClass().getResource("show-example.json")); | |||
} | |||
@Test | |||
public void show() { | |||
QualityGateDto qualityGate = db.qualityGates().insertQualityGate(); | |||
MetricDto metric = db.measures().insertMetric(); | |||
QualityGateConditionDto condition1 = db.qualityGates().addCondition(qualityGate, metric, c -> c.setOperator("GT").setPeriod(null)); | |||
QualityGateConditionDto condition2 = db.qualityGates().addCondition(qualityGate, metric, c -> c.setOperator("LT").setPeriod(1)); | |||
ShowWsResponse response = ws.newRequest().setParam("name", qualityGate.getName()) | |||
.executeProtobuf(ShowWsResponse.class); | |||
assertThat(response.getId()).isEqualTo(qualityGate.getId()); | |||
assertThat(response.getName()).isEqualTo(qualityGate.getName()); | |||
assertThat(response.getConditionsList()).hasSize(2); | |||
assertThat(response.getConditionsList()) | |||
.extracting(Condition::getId, Condition::getMetric, Condition::hasPeriod, Condition::getPeriod, Condition::getOp, Condition::getError, Condition::getWarning) | |||
.containsExactlyInAnyOrder( | |||
tuple(condition1.getId(), metric.getKey(), false, 0, "GT", condition1.getErrorThreshold(), condition1.getWarningThreshold()), | |||
tuple(condition2.getId(), metric.getKey(), true, 1, "LT", condition2.getErrorThreshold(), condition2.getWarningThreshold())); | |||
} | |||
@Test | |||
public void show_by_id() { | |||
QualityGateDto qualityGate = db.qualityGates().insertQualityGate(); | |||
ShowWsResponse response = ws.newRequest().setParam("id", qualityGate.getId().toString()) | |||
.executeProtobuf(ShowWsResponse.class); | |||
assertThat(response.getId()).isEqualTo(qualityGate.getId()); | |||
assertThat(response.getName()).isEqualTo(qualityGate.getName()); | |||
} | |||
@Test | |||
public void no_condition() { | |||
QualityGateDto qualityGate = db.qualityGates().insertQualityGate(); | |||
ShowWsResponse response = ws.newRequest().setParam("name", qualityGate.getName()) | |||
.executeProtobuf(ShowWsResponse.class); | |||
assertThat(response.getId()).isEqualTo(qualityGate.getId()); | |||
assertThat(response.getName()).isEqualTo(qualityGate.getName()); | |||
assertThat(response.getConditionsList()).isEmpty(); | |||
} | |||
@Test | |||
public void fail_when_no_name_or_id() { | |||
expectedException.expect(IllegalArgumentException.class); | |||
expectedException.expectMessage("Either 'id' or 'name' must be provided"); | |||
ws.newRequest().execute(); | |||
} | |||
@Test | |||
public void fail_when_both_name_or_id() { | |||
QualityGateDto qualityGate = db.qualityGates().insertQualityGate(); | |||
expectedException.expect(IllegalArgumentException.class); | |||
expectedException.expectMessage("Either 'id' or 'name' must be provided"); | |||
ws.newRequest() | |||
.setParam("name", qualityGate.getName()) | |||
.setParam("id", qualityGate.getId().toString()) | |||
.execute(); | |||
} | |||
@Test | |||
public void fail_when_condition_is_on_disabled_metric() { | |||
QualityGateDto qualityGate = db.qualityGates().insertQualityGate(); | |||
MetricDto metric = db.measures().insertMetric(); | |||
db.qualityGates().addCondition(qualityGate, metric); | |||
db.getDbClient().metricDao().disableCustomByKey(db.getSession(), metric.getKey()); | |||
db.commit(); | |||
expectedException.expect(IllegalStateException.class); | |||
expectedException.expectMessage(format("Could not find metric with id %s", metric.getId())); | |||
ws.newRequest() | |||
.setParam("name", qualityGate.getName()) | |||
.execute(); | |||
} | |||
} |
@@ -118,5 +118,21 @@ message UpdateConditionResponse { | |||
optional int32 period = 6; | |||
} | |||
// GET api/qualitygates/show | |||
message ShowWsResponse { | |||
optional int64 id = 1; | |||
optional string name = 2; | |||
repeated Condition conditions = 3; | |||
message Condition { | |||
optional int64 id = 1; | |||
optional string metric = 2; | |||
optional int32 period = 3; | |||
optional string op = 4; | |||
optional string warning = 5; | |||
optional string error = 6; | |||
} | |||
} | |||