return newCondition;
}
+ public QualityGateConditionDto updateCondition(DbSession dbSession, long condId, String metricKey, String operator,
+ @Nullable String warningThreshold, @Nullable String errorThreshold, @Nullable Integer period) {
+ QualityGateConditionDto condition = getNonNullCondition(dbSession, condId);
+ MetricDto metric = getNonNullMetric(dbSession, metricKey);
+ validateCondition(metric, operator, warningThreshold, errorThreshold, period);
+ checkConditionDoesNotAlreadyExistOnSameMetricAndPeriod(getConditions(dbSession, condition.getQualityGateId(), condition.getId()), metric, period);
+
+ condition
+ .setMetricId(metric.getId())
+ .setMetricKey(metric.getKey())
+ .setOperator(operator)
+ .setWarningThreshold(warningThreshold)
+ .setErrorThreshold(errorThreshold)
+ .setPeriod(period);
+ dbClient.gateConditionDao().update(condition, dbSession);
+ return condition;
+ }
+
private QualityGateDto getNonNullQgate(DbSession dbSession, long id) {
QualityGateDto qGate = dbClient.qualityGateDao().selectById(dbSession, id);
if (qGate == null) {
return metric;
}
+ private QualityGateConditionDto getNonNullCondition(DbSession dbSession, long id) {
+ QualityGateConditionDto condition = dbClient.gateConditionDao().selectById(id, dbSession);
+ if (condition == null) {
+ throw new NotFoundException("There is no condition with id=" + id);
+ }
+ return condition;
+ }
+
private Collection<QualityGateConditionDto> getConditions(DbSession dbSession, long qGateId, @Nullable Long conditionId) {
Collection<QualityGateConditionDto> conditions = dbClient.gateConditionDao().selectForQualityGate(qGateId, dbSession);
if (conditionId == null) {
*/
package org.sonar.server.qualitygate;
-import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import java.util.Collection;
-import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
-import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import org.apache.commons.lang.BooleanUtils;
-import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
-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.api.web.UserRole;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.server.user.UserSession;
import org.sonar.server.util.Validation;
-import static java.lang.String.format;
-
/**
* Methods from this class should be moved to {@link QualityGateUpdater} and to new classes QualityGateFinder / QualityGateConditionsUpdater / etc.
* in order to have classes with clearer responsibilities and more easily testable (without having to use too much mocks)
}
}
- public QualityGateConditionDto updateCondition(long condId, String metricKey, String operator,
- @Nullable String warningThreshold, @Nullable String errorThreshold, @Nullable Integer period) {
- checkPermission();
- QualityGateConditionDto condition = getNonNullCondition(condId);
- Metric metric = getNonNullMetric(metricKey);
- validateCondition(metric, operator, warningThreshold, errorThreshold, period);
- checkConditionDoesNotAlreadyExistOnSameMetricAndPeriod(getConditions(condition.getQualityGateId(), condition.getId()), metric, period);
- condition
- .setMetricId(metric.getId())
- .setMetricKey(metric.getKey())
- .setOperator(operator)
- .setWarningThreshold(warningThreshold)
- .setErrorThreshold(errorThreshold)
- .setPeriod(period);
- conditionDao.update(condition);
- return condition;
- }
-
public Collection<QualityGateConditionDto> listConditions(long qGateId) {
Collection<QualityGateConditionDto> conditionsForGate = conditionDao.selectForQualityGate(qGateId);
for (QualityGateConditionDto condition : conditionsForGate) {
}
}
- private Collection<QualityGateConditionDto> getConditions(long qGateId, @Nullable Long conditionId) {
- Collection<QualityGateConditionDto> conditions = conditionDao.selectForQualityGate(qGateId);
- if (conditionId == null) {
- return conditions;
- }
- return conditionDao.selectForQualityGate(qGateId).stream()
- .filter(condition -> condition.getId() != conditionId)
- .collect(Collectors.toList());
- }
-
- private static void validateCondition(Metric metric, String operator, @Nullable String warningThreshold, @Nullable String errorThreshold, @Nullable Integer period) {
- Errors errors = new Errors();
- validateMetric(metric, errors);
- checkOperator(metric, operator, errors);
- checkThresholds(warningThreshold, errorThreshold, errors);
- checkPeriod(metric, period, errors);
- if (!errors.isEmpty()) {
- throw new BadRequestException(errors);
- }
- }
-
- private static void checkConditionDoesNotAlreadyExistOnSameMetricAndPeriod(Collection<QualityGateConditionDto> conditions, Metric metric, @Nullable final Integer period) {
- if (conditions.isEmpty()) {
- return;
- }
-
- boolean conditionExists = conditions.stream().anyMatch(new MatchMetricAndPeriod(metric.getId(), period)::apply);
- if (conditionExists) {
- String errorMessage = period == null
- ? format("Condition on metric '%s' already exists.", metric.getName())
- : format("Condition on metric '%s' over leak period already exists.", metric.getName());
- throw new BadRequestException(errorMessage);
- }
- }
-
- private static void checkPeriod(Metric metric, @Nullable Integer period, Errors errors) {
- if (period == null) {
- errors.check(!metric.getKey().startsWith("new_"), "A period must be selected for differential metrics.");
- } else {
- errors.check(period == 1, "The only valid quality gate period is 1, the leak period.");
- }
- }
-
- private static void checkThresholds(@Nullable String warningThreshold, @Nullable String errorThreshold, Errors errors) {
- errors.check(warningThreshold != null || errorThreshold != null, "At least one threshold (warning, error) must be set.");
- }
-
- private static void checkOperator(Metric metric, String operator, Errors errors) {
- errors
- .check(QualityGateConditionDto.isOperatorAllowed(operator, metric.getType()), format("Operator %s is not allowed for metric type %s.", operator, metric.getType()));
- }
-
- private static void validateMetric(Metric metric, Errors errors) {
- errors.check(isAlertable(metric), format("Metric '%s' cannot be used to define a condition.", metric.getKey()));
- }
-
- private static boolean isAlertable(Metric metric) {
- return isAvailableForInit(metric) && BooleanUtils.isFalse(metric.isHidden());
- }
-
- private static boolean isAvailableForInit(Metric metric) {
- return !metric.isDataType() && !CoreMetrics.ALERT_STATUS.equals(metric) && ValueType.RATING != metric.getType();
- }
-
private boolean isDefault(QualityGateDto qGate) {
return qGate.getId().equals(getDefaultId());
}
return qGate;
}
- private Metric getNonNullMetric(String metricKey) {
- Metric metric = metricFinder.findByKey(metricKey);
- if (metric == null) {
- throw new NotFoundException("There is no metric with key=" + metricKey);
- }
- return metric;
- }
-
private QualityGateConditionDto getNonNullCondition(long id) {
QualityGateConditionDto condition = conditionDao.selectById(id);
if (condition == null) {
throw new ForbiddenException("Insufficient privileges");
}
}
-
- private static class MatchMetricAndPeriod implements Predicate<QualityGateConditionDto> {
- private final int metricId;
- @CheckForNull
- private final Integer period;
-
- public MatchMetricAndPeriod(int metricId, @Nullable Integer period) {
- this.metricId = metricId;
- this.period = period;
- }
-
- @Override
- public boolean apply(@Nonnull QualityGateConditionDto input) {
- return input.getMetricId() == metricId &&
- ObjectUtils.equals(input.getPeriod(), period);
- }
- }
}
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
-import org.sonar.server.qualitygate.QualityGates;
-import org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.qualitygate.QualityGateConditionDto;
+import org.sonar.server.qualitygate.QualityGateConditionsUpdater;
+import org.sonar.server.user.UserSession;
+import org.sonarqube.ws.WsQualityGates.UpdateConditionWsResponse;
+import org.sonarqube.ws.client.qualitygate.UpdateConditionRequest;
+
+import static org.sonar.core.permission.GlobalPermissions.QUALITY_GATE_ADMIN;
+import static org.sonar.server.qualitygate.ws.QualityGatesWs.addConditionParams;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.ACTION_UPDATE_CONDITION;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ERROR;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ID;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_METRIC;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_OPERATOR;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PERIOD;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_WARNING;
public class UpdateConditionAction implements QualityGatesWsAction {
- private final QualityGates qualityGates;
+ private final UserSession userSession;
+ private final DbClient dbClient;
+ private final QualityGateConditionsUpdater qualityGateConditionsUpdater;
- public UpdateConditionAction(QualityGates qualityGates) {
- this.qualityGates = qualityGates;
+ public UpdateConditionAction(UserSession userSession, DbClient dbClient, QualityGateConditionsUpdater qualityGateConditionsUpdater) {
+ this.userSession = userSession;
+ this.dbClient = dbClient;
+ this.qualityGateConditionsUpdater = qualityGateConditionsUpdater;
}
@Override
public void define(WebService.NewController controller) {
- WebService.NewAction createCondition = controller.createAction("update_condition")
+ WebService.NewAction createCondition = controller.createAction(ACTION_UPDATE_CONDITION)
.setDescription("Update a condition attached to a quality gate. Require Administer Quality Gates permission")
.setPost(true)
.setSince("4.3")
.setHandler(this);
createCondition
- .createParam(QualityGatesWsParameters.PARAM_ID)
+ .createParam(PARAM_ID)
.setDescription("Condition ID")
.setRequired(true)
.setExampleValue("10");
- QualityGatesWs.addConditionParams(createCondition);
+ addConditionParams(createCondition);
}
@Override
public void handle(Request request, Response response) {
- QualityGatesWs.writeQualityGateCondition(
- qualityGates.updateCondition(
- QualityGatesWs.parseId(request, QualityGatesWsParameters.PARAM_ID),
- request.mandatoryParam(QualityGatesWsParameters.PARAM_METRIC),
- request.mandatoryParam(QualityGatesWsParameters.PARAM_OPERATOR),
- request.param(QualityGatesWsParameters.PARAM_WARNING),
- request.param(QualityGatesWsParameters.PARAM_ERROR),
- request.paramAsInt(QualityGatesWsParameters.PARAM_PERIOD)
- ), response.newJsonWriter()
- ).close();
+ userSession.checkPermission(QUALITY_GATE_ADMIN);
+
+ DbSession dbSession = dbClient.openSession(false);
+ try {
+ writeProtobuf(doHandle(toWsRequest(request), dbSession), request, response);
+ dbSession.commit();
+ } finally {
+ dbClient.closeSession(dbSession);
+ }
+ }
+
+ private UpdateConditionWsResponse doHandle(UpdateConditionRequest request, DbSession dbSession) {
+ QualityGateConditionDto condition = qualityGateConditionsUpdater.updateCondition(dbSession, request.getConditionId(), request.getMetricKey(), request.getOperator(),
+ request.getWarning(), request.getError(), request.getPeriod());
+ UpdateConditionWsResponse.Builder response = UpdateConditionWsResponse.newBuilder()
+ .setId(condition.getId())
+ .setMetric(condition.getMetricKey())
+ .setOp(condition.getOperator());
+ String warning = condition.getWarningThreshold();
+ if (warning != null) {
+ response.setWarning(warning);
+ }
+ String error = condition.getErrorThreshold();
+ if (error != null) {
+ response.setError(error);
+ }
+ Integer period = condition.getPeriod();
+ if (period != null) {
+ response.setPeriod(period);
+ }
+ return response.build();
+ }
+
+ private static UpdateConditionRequest toWsRequest(Request request) {
+ return UpdateConditionRequest.builder()
+ .setConditionId(request.mandatoryParamAsInt(PARAM_ID))
+ .setMetricKey(request.mandatoryParam(PARAM_METRIC))
+ .setOperator(request.mandatoryParam(PARAM_OPERATOR))
+ .setWarning(request.param(PARAM_WARNING))
+ .setError(request.param(PARAM_ERROR))
+ .setPeriod(request.paramAsInt(PARAM_PERIOD))
+ .build();
}
}
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@Before
public void setUp() throws Exception {
qualityGateDto = qualityGateDbTester.insertQualityGate();
-
dbClient.metricDao().insert(dbSession, coverageMetricDto);
dbSession.commit();
}
public void create_warning_condition_without_period() {
QualityGateConditionDto result = underTest.createCondition(dbSession, qualityGateDto.getId(), "coverage", "LT", "90", null, null);
- assertThat(result.getQualityGateId()).isEqualTo(qualityGateDto.getId());
- assertThat(result.getMetricId()).isEqualTo(coverageMetricDto.getId().longValue());
- assertThat(result.getOperator()).isEqualTo("LT");
- assertThat(result.getWarningThreshold()).isEqualTo("90");
- assertThat(result.getErrorThreshold()).isNull();
- assertThat(result.getPeriod()).isNull();
- assertThat(dbClient.gateConditionDao().selectById(result.getId(), dbSession)).isNotNull();
+ verifyCondition(result, coverageMetricDto.getId(), "LT", "90", null, null);
}
@Test
QualityGateConditionDto result = underTest.createCondition(dbSession, qualityGateDto.getId(), "new_coverage", "LT", null, "80", 1);
- assertThat(result.getQualityGateId()).isEqualTo(qualityGateDto.getId());
- assertThat(result.getMetricId()).isEqualTo(metricDto.getId().longValue());
- assertThat(result.getMetricKey()).isEqualTo("new_coverage");
- assertThat(result.getOperator()).isEqualTo("LT");
- assertThat(result.getWarningThreshold()).isNull();
- assertThat(result.getErrorThreshold()).isEqualTo("80");
- assertThat(result.getPeriod()).isEqualTo(1);
- assertThat(dbClient.gateConditionDao().selectById(result.getId(), dbSession)).isNotNull();
+ verifyCondition(result, metricDto.getId(), "LT", null, "80", 1);
}
@Test
@Test
public void fail_to_create_condition_when_condition_on_same_metric_and_on_leak_period_already_exist() throws Exception {
dbClient.gateConditionDao().insert(new QualityGateConditionDto()
- .setQualityGateId(qualityGateDto.getId())
- .setMetricId(coverageMetricDto.getId())
- .setPeriod(1),
+ .setQualityGateId(qualityGateDto.getId())
+ .setMetricId(coverageMetricDto.getId())
+ .setPeriod(1),
dbSession);
expectedException.expect(BadRequestException.class);
underTest.createCondition(dbSession, qualityGateDto.getId(), "coverage", "EQ", null, "90", 6);
}
+ @Test
+ public void update_condition() {
+ QualityGateConditionDto condition = insertCondition(coverageMetricDto.getId(), "LT", null, "80", null);
+
+ QualityGateConditionDto result = underTest.updateCondition(dbSession, condition.getId(), "coverage", "GT", "60", null, 1);
+
+ verifyCondition(result, coverageMetricDto.getId(), "GT", "60", null, 1);
+ }
+
+ @Test
+ public void update_condition_over_leak_period() {
+ QualityGateConditionDto condition = insertCondition(coverageMetricDto.getId(), "GT", "80", null, 1);
+
+ QualityGateConditionDto result = underTest.updateCondition(dbSession, condition.getId(), "coverage", "LT", null, "80", null);
+
+ verifyCondition(result, coverageMetricDto.getId(), "LT", null, "80", null);
+ }
+
+ @Test
+ @UseDataProvider("invalid_metrics")
+ public void fail_to_update_condition_on_invalid_metric(String metricKey, Metric.ValueType valueType, boolean hidden) {
+ MetricDto metricDto = dbClient.metricDao().insert(dbSession, newMetricDto()
+ .setKey(metricKey)
+ .setValueType(valueType.name())
+ .setHidden(hidden));
+ QualityGateConditionDto condition = insertCondition(metricDto.getId(), "LT", null, "80", null);
+ dbSession.commit();
+
+ expectedException.expect(BadRequestException.class);
+ expectedException.expectMessage("Metric '" + metricKey + "' cannot be used to define a condition.");
+ underTest.updateCondition(dbSession, condition.getId(), metricDto.getKey(), "GT", "60", null, 1);
+ }
+
+ @Test
+ public void fail_to_update_condition_when_condition_on_same_metric_already_exist() throws Exception {
+ QualityGateConditionDto conditionNotOnLeakPeriod = insertCondition(coverageMetricDto.getId(), "GT", "80", null, null);
+ QualityGateConditionDto conditionOnLeakPeriod = insertCondition(coverageMetricDto.getId(), "GT", "80", null, 1);
+
+ expectedException.expect(BadRequestException.class);
+ expectedException.expectMessage("Condition on metric 'Coverage' over leak period already exists.");
+ // Update condition not on leak period to be on leak period => will fail as this condition already exist
+ underTest.updateCondition(dbSession, conditionNotOnLeakPeriod.getId(), coverageMetricDto.getKey(), "GT", "80", null, 1);
+ }
+
@DataProvider
public static Object[][] invalid_metrics() {
return new Object[][] {
};
}
+ private QualityGateConditionDto insertCondition(long metricId, String operator, @Nullable String warning, @Nullable String error,
+ @Nullable Integer period) {
+ QualityGateConditionDto qualityGateConditionDto = new QualityGateConditionDto().setQualityGateId(qualityGateDto.getId())
+ .setMetricId(metricId)
+ .setOperator(operator)
+ .setWarningThreshold(warning)
+ .setErrorThreshold(error)
+ .setPeriod(period);
+ dbClient.gateConditionDao().insert(qualityGateConditionDto, dbSession);
+ dbSession.commit();
+ return qualityGateConditionDto;
+ }
+
+ private void verifyCondition(QualityGateConditionDto dto, int metricId, String operator, @Nullable String warning, @Nullable String error, @Nullable Integer period) {
+ QualityGateConditionDto reloaded = dbClient.gateConditionDao().selectById(dto.getId(), dbSession);
+ assertThat(reloaded.getQualityGateId()).isEqualTo(qualityGateDto.getId());
+ assertThat(reloaded.getMetricId()).isEqualTo(metricId);
+ assertThat(reloaded.getOperator()).isEqualTo(operator);
+ assertThat(reloaded.getWarningThreshold()).isEqualTo(warning);
+ assertThat(reloaded.getErrorThreshold()).isEqualTo(error);
+ assertThat(reloaded.getPeriod()).isEqualTo(period);
+
+ assertThat(dto.getQualityGateId()).isEqualTo(qualityGateDto.getId());
+ assertThat(dto.getMetricId()).isEqualTo(metricId);
+ assertThat(dto.getOperator()).isEqualTo(operator);
+ assertThat(dto.getWarningThreshold()).isEqualTo(warning);
+ assertThat(dto.getErrorThreshold()).isEqualTo(error);
+ assertThat(dto.getPeriod()).isEqualTo(period);
+ }
+
}
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.user.UserSession;
-import static java.util.Arrays.asList;
-import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
assertThat(underTest.getDefault()).isNull();
}
- @Test
- public void should_update_condition() {
- String metricKey = "new_coverage";
- String operator = "LT";
- String errorThreshold = "80";
- addMetric(metricKey, "New Coverage");
-
- QualityGateConditionDto condition = insertQualityGateConditionDto(newCondition(metricKey, METRIC_ID));
- when(conditionDao.selectForQualityGate(QUALITY_GATE_ID)).thenReturn(singletonList(condition));
-
- assertThat(underTest.updateCondition(condition.getId(), metricKey, operator, null, errorThreshold, 1)).isEqualTo(condition);
- verify(conditionDao).update(condition);
- }
-
- @Test
- public void fail_to_update_condition_when_condition_on_same_metric_already_exist() throws Exception {
- String metricKey = "coverage";
- addMetric(metricKey, "Coverage");
- when(dao.selectById(QUALITY_GATE_ID)).thenReturn(new QualityGateDto().setId(QUALITY_GATE_ID));
-
- QualityGateConditionDto conditionNotOnLeakPeriod = insertQualityGateConditionDto(newCondition(metricKey, METRIC_ID)).setPeriod(0);
- QualityGateConditionDto conditionOnLeakPeriod = insertQualityGateConditionDto(newCondition(metricKey, METRIC_ID)).setPeriod(1);
- when(conditionDao.selectForQualityGate(QUALITY_GATE_ID)).thenReturn(asList(conditionNotOnLeakPeriod, conditionOnLeakPeriod));
-
- expectedException.expect(BadRequestException.class);
- expectedException.expectMessage("Condition on metric 'Coverage' over leak period already exists.");
-
- // Update condition not on leak period to be on leak period => will fail as this condition already exist
- underTest.updateCondition(conditionNotOnLeakPeriod.getId(), metricKey, "LT", null, "60", 1);
- }
-
@Test
public void should_list_conditions() {
long qGateId = QUALITY_GATE_ID;
new ListAction(qGates), new ShowAction(qGates), new SearchAction(projectFinder),
new CreateAction(null, null, null), new CopyAction(qGates), new DestroyAction(qGates), new RenameAction(qGates),
new SetAsDefaultAction(qGates), new UnsetDefaultAction(qGates),
- new CreateConditionAction(null, null, null), new UpdateConditionAction(qGates), new DeleteConditionAction(qGates),
+ new CreateConditionAction(null, null, null), new UpdateConditionAction(null, null, null), new DeleteConditionAction(qGates),
selectAction, new DeselectAction(qGates, mock(DbClient.class), mock(ComponentFinder.class)), new AppAction(null, null)));
}
tester.newGetRequest("api/qualitygates", "show").setParam("id", "12345").setParam("name", "Polop").execute();
}
- @Test
- public void update_condition_nominal() throws Exception {
- long condId = 12345L;
- String metricKey = "coverage";
- String operator = "LT";
- String warningThreshold = "80";
- String errorThreshold = "75";
- when(qGates.updateCondition(condId, metricKey, operator, warningThreshold, errorThreshold, null))
- .thenReturn(new QualityGateConditionDto().setId(condId).setMetricId(10).setMetricKey(metricKey)
- .setOperator(operator).setWarningThreshold(warningThreshold).setErrorThreshold(errorThreshold));
- tester.newPostRequest("api/qualitygates", "update_condition")
- .setParam("id", Long.toString(condId))
- .setParam("metric", metricKey)
- .setParam("op", operator)
- .setParam("warning", warningThreshold)
- .setParam("error", errorThreshold)
- .execute()
- .assertJson("{\"id\":12345,\"metric\":\"coverage\",\"op\":\"LT\",\"warning\":\"80\",\"error\":\"75\"}");
- }
-
@Test
public void delete_condition_nominal() throws Exception {
long condId = 12345L;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.metric.MetricDto;
+import org.sonar.db.qualitygate.QualityGateConditionDto;
+import org.sonar.db.qualitygate.QualityGateDbTester;
+import org.sonar.db.qualitygate.QualityGateDto;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.qualitygate.QualityGateConditionsUpdater;
+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.WsQualityGates.CreateConditionWsResponse;
+
+import static org.assertj.core.api.Java6Assertions.assertThat;
+import static org.sonar.core.permission.GlobalPermissions.QUALITY_GATE_ADMIN;
+import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
+import static org.sonar.db.metric.MetricTesting.newMetricDto;
+import static org.sonar.server.computation.task.projectanalysis.metric.Metric.MetricType.PERCENT;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ERROR;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ID;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_METRIC;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_OPERATOR;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PERIOD;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_WARNING;
+
+public class UpdateConditionActionTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+
+ DbClient dbClient = db.getDbClient();
+ DbSession dbSession = db.getSession();
+ QualityGateDbTester qualityGateDbTester = new QualityGateDbTester(db);
+
+ UpdateConditionAction underTest = new UpdateConditionAction(userSession, dbClient, new QualityGateConditionsUpdater(dbClient));
+
+ QualityGateDto qualityGateDto;
+ QualityGateConditionDto conditionDto;
+
+ MetricDto coverageMetricDto = newMetricDto()
+ .setKey("coverage")
+ .setShortName("Coverage")
+ .setValueType(PERCENT.name())
+ .setHidden(false);
+
+ WsActionTester ws = new WsActionTester(underTest);
+
+ @Before
+ public void setUp() throws Exception {
+ MetricDto metricDto = dbClient.metricDao().insert(dbSession, coverageMetricDto);
+ qualityGateDto = qualityGateDbTester.insertQualityGate();
+ conditionDto = new QualityGateConditionDto().setQualityGateId(qualityGateDto.getId())
+ .setMetricId(metricDto.getId())
+ .setOperator("GT")
+ .setWarningThreshold(null)
+ .setErrorThreshold("80")
+ .setPeriod(1);
+ dbClient.gateConditionDao().insert(conditionDto, dbSession);
+ dbSession.commit();
+ }
+
+ @Test
+ public void update_warning_condition() throws Exception {
+ setUserAsQualityGateAdmin();
+
+ CreateConditionWsResponse response = executeRequest(conditionDto.getId(), coverageMetricDto.getKey(), "LT", "90", null, null);
+
+ assertCondition(response, "LT", "90", null, null);
+ }
+
+ @Test
+ public void update_error_condition() throws Exception {
+ setUserAsQualityGateAdmin();
+
+ CreateConditionWsResponse response = executeRequest(conditionDto.getId(), coverageMetricDto.getKey(), "LT", null, "90", null);
+
+ assertCondition(response, "LT", null, "90", null);
+ }
+
+ @Test
+ public void update_condition_over_leak_period() throws Exception {
+ setUserAsQualityGateAdmin();
+
+ CreateConditionWsResponse response = executeRequest(conditionDto.getId(), coverageMetricDto.getKey(), "LT", null, "90", 1);
+
+ assertCondition(response, "LT", null, "90", 1);
+ }
+
+ @Test
+ public void fail_when_not_quality_gate_admin() throws Exception {
+ setUserAsNotQualityGateAdmin();
+
+ expectedException.expect(ForbiddenException.class);
+ executeRequest(conditionDto.getId(), coverageMetricDto.getKey(), "LT", "90", null, null);
+ }
+
+ @Test
+ public void test_ws_definition() {
+ WebService.Action action = ws.getDef();
+ assertThat(action).isNotNull();
+ assertThat(action.isInternal()).isFalse();
+ assertThat(action.isPost()).isTrue();
+ assertThat(action.responseExampleAsString()).isNull();
+ assertThat(action.params()).hasSize(6);
+ }
+
+ private void assertCondition(CreateConditionWsResponse response, String operator, @Nullable String warning, @Nullable String error, @Nullable Integer period) {
+ List<QualityGateConditionDto> conditionDtoList = new ArrayList<>(dbClient.gateConditionDao().selectForQualityGate(qualityGateDto.getId(), dbSession));
+ assertThat(conditionDtoList).hasSize(1);
+ QualityGateConditionDto qualityGateConditionDto = conditionDtoList.get(0);
+ assertThat(qualityGateConditionDto.getQualityGateId()).isEqualTo(qualityGateDto.getId());
+ assertThat(qualityGateConditionDto.getMetricId()).isEqualTo(coverageMetricDto.getId().longValue());
+ assertThat(qualityGateConditionDto.getOperator()).isEqualTo(operator);
+ assertThat(qualityGateConditionDto.getWarningThreshold()).isEqualTo(warning);
+ assertThat(qualityGateConditionDto.getErrorThreshold()).isEqualTo(error);
+ assertThat(qualityGateConditionDto.getPeriod()).isEqualTo(period);
+
+ assertThat(response.getId()).isEqualTo(qualityGateConditionDto.getId());
+ assertThat(response.getMetric()).isEqualTo(coverageMetricDto.getKey());
+ assertThat(response.getOp()).isEqualTo(operator);
+ if (warning != null) {
+ assertThat(response.getWarning()).isEqualTo(warning);
+ } else {
+ assertThat(response.hasWarning()).isFalse();
+ }
+ if (error != null) {
+ assertThat(response.getError()).isEqualTo(error);
+ } else {
+ assertThat(response.hasError()).isFalse();
+ }
+ if (period != null) {
+ assertThat(response.getPeriod()).isEqualTo(period);
+ } else {
+ assertThat(response.hasPeriod()).isFalse();
+ }
+ }
+
+ private CreateConditionWsResponse executeRequest(long conditionId, String metricKey, String operator, @Nullable String warning, @Nullable String error,
+ @Nullable Integer period) {
+ TestRequest request = ws.newRequest()
+ .setMediaType(MediaTypes.PROTOBUF)
+ .setParam(PARAM_ID, Long.toString(conditionId))
+ .setParam(PARAM_METRIC, metricKey)
+ .setParam(PARAM_OPERATOR, operator);
+ if (warning != null) {
+ request.setParam(PARAM_WARNING, warning);
+ }
+ if (error != null) {
+ request.setParam(PARAM_ERROR, error);
+ }
+ if (period != null) {
+ request.setParam(PARAM_PERIOD, Integer.toString(period));
+ }
+ try {
+ return CreateConditionWsResponse.parseFrom(request.execute().getInputStream());
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private void setUserAsQualityGateAdmin() {
+ userSession.login("project-admin").setGlobalPermissions(QUALITY_GATE_ADMIN);
+ }
+
+ private void setUserAsNotQualityGateAdmin() {
+ userSession.login("not-admin").setGlobalPermissions(SCAN_EXECUTION);
+ }
+}
import org.sonarqube.ws.WsQualityGates.CreateConditionWsResponse;
import org.sonarqube.ws.WsQualityGates.CreateWsResponse;
import org.sonarqube.ws.WsQualityGates.ProjectStatusWsResponse;
+import org.sonarqube.ws.WsQualityGates.UpdateConditionWsResponse;
import org.sonarqube.ws.client.BaseService;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.PostRequest;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.ACTION_CREATE_CONDITION;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.ACTION_PROJECT_STATUS;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.ACTION_SELECT;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.ACTION_UPDATE_CONDITION;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.CONTROLLER_QUALITY_GATES;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ANALYSIS_ID;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ERROR;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_GATE_ID;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ID;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_METRIC;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_NAME;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_OPERATOR;
.setParam(PARAM_PERIOD, request.getPeriod()),
CreateConditionWsResponse.parser());
}
+
+ public UpdateConditionWsResponse updateCondition(UpdateConditionRequest request) {
+ return call(new PostRequest(path(ACTION_UPDATE_CONDITION))
+ .setParam(PARAM_ID, request.getConditionId())
+ .setParam(PARAM_METRIC, request.getMetricKey())
+ .setParam(PARAM_OPERATOR, request.getOperator())
+ .setParam(PARAM_WARNING, request.getWarning())
+ .setParam(PARAM_ERROR, request.getError())
+ .setParam(PARAM_PERIOD, request.getPeriod()),
+ UpdateConditionWsResponse.parser());
+ }
}
public static final String ACTION_SELECT = "select";
public static final String ACTION_CREATE = "create";
public static final String ACTION_CREATE_CONDITION = "create_condition";
+ public static final String ACTION_UPDATE_CONDITION = "update_condition";
public static final String PARAM_ANALYSIS_ID = "analysisId";
public static final String PARAM_PROJECT_ID = "projectId";
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.qualitygate;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Strings.isNullOrEmpty;
+
+public class UpdateConditionRequest {
+
+ private final long conditionId;
+ private final String metricKey;
+ private final String operator;
+ private final String warning;
+ private final String error;
+ private final Integer period;
+
+ public UpdateConditionRequest(Builder builder) {
+ this.conditionId = builder.conditionId;
+ this.metricKey = builder.metricKey;
+ this.operator = builder.operator;
+ this.warning = builder.warning;
+ this.error = builder.error;
+ this.period = builder.period;
+ }
+
+ public long getConditionId() {
+ return conditionId;
+ }
+
+ public String getMetricKey() {
+ return metricKey;
+ }
+
+ public String getOperator() {
+ return operator;
+ }
+
+ @CheckForNull
+ public String getWarning() {
+ return warning;
+ }
+
+ @CheckForNull
+ public String getError() {
+ return error;
+ }
+
+ @CheckForNull
+ public Integer getPeriod() {
+ return period;
+ }
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private long conditionId;
+ private String metricKey;
+ private String operator;
+ private String warning;
+ private String error;
+ private Integer period;
+
+ private Builder() {
+ // enforce factory method use
+ }
+
+ public Builder setConditionId(long conditionId) {
+ this.conditionId = conditionId;
+ return this;
+ }
+
+ public Builder setMetricKey(String metricKey) {
+ this.metricKey = metricKey;
+ return this;
+ }
+
+ public Builder setOperator(String operator) {
+ this.operator = operator;
+ return this;
+ }
+
+ public Builder setWarning(@Nullable String warning) {
+ this.warning = warning;
+ return this;
+ }
+
+ public Builder setError(@Nullable String error) {
+ this.error = error;
+ return this;
+ }
+
+ public Builder setPeriod(@Nullable Integer period) {
+ this.period = period;
+ return this;
+ }
+
+ public UpdateConditionRequest build() {
+ checkArgument(conditionId > 0, "Condition id is mandatory and must not be empty");
+ checkArgument(!isNullOrEmpty(metricKey), "Metric key is mandatory and must not be empty");
+ checkArgument(!isNullOrEmpty(operator), "Operator is mandatory and must not be empty");
+ return new UpdateConditionRequest(this);
+ }
+ }
+}
optional int32 period = 6;
}
+// POST api/qualitygates/update_condition
+message UpdateConditionWsResponse {
+ optional int64 id = 1;
+ optional string metric = 2;
+ optional string op = 3;
+ optional string warning = 4;
+ optional string error = 5;
+ optional int32 period = 6;
+}
+
import org.sonarqube.ws.WsQualityGates.CreateConditionWsResponse;
import org.sonarqube.ws.WsQualityGates.CreateWsResponse;
import org.sonarqube.ws.WsQualityGates.ProjectStatusWsResponse;
+import org.sonarqube.ws.WsQualityGates.UpdateConditionWsResponse;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.PostRequest;
import org.sonarqube.ws.client.ServiceTester;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ANALYSIS_ID;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ERROR;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_GATE_ID;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ID;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_METRIC;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_NAME;
import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_OPERATOR;
.hasParam(PARAM_PERIOD, 1)
.andNoOtherParam();
}
+
+ @Test
+ public void update_condition() {
+ underTest.updateCondition(UpdateConditionRequest.builder()
+ .setConditionId(10)
+ .setMetricKey("metric")
+ .setOperator("LT")
+ .setWarning("warning")
+ .setError("error")
+ .setPeriod(1)
+ .build());
+
+ PostRequest request = serviceTester.getPostRequest();
+
+ assertThat(serviceTester.getPostParser()).isSameAs(UpdateConditionWsResponse.parser());
+ serviceTester.assertThat(request)
+ .hasPath("update_condition")
+ .hasParam(PARAM_ID, 10)
+ .hasParam(PARAM_METRIC, "metric")
+ .hasParam(PARAM_OPERATOR, "LT")
+ .hasParam(PARAM_WARNING, "warning")
+ .hasParam(PARAM_ERROR, "error")
+ .hasParam(PARAM_PERIOD, 1)
+ .andNoOtherParam();
+ }
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.qualitygate;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class UpdateConditionRequestTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ UpdateConditionRequest.Builder underTest = UpdateConditionRequest.builder();
+
+ @Test
+ public void create_condition_request() {
+ UpdateConditionRequest result = underTest
+ .setConditionId(10)
+ .setMetricKey("metric")
+ .setOperator("LT")
+ .setWarning("warning")
+ .setError("error")
+ .setPeriod(1)
+ .build();
+
+ assertThat(result.getConditionId()).isEqualTo(10);
+ assertThat(result.getMetricKey()).isEqualTo("metric");
+ assertThat(result.getOperator()).isEqualTo("LT");
+ assertThat(result.getWarning()).isEqualTo("warning");
+ assertThat(result.getError()).isEqualTo("error");
+ assertThat(result.getPeriod()).isEqualTo(1);
+ }
+
+ @Test
+ public void fail_when_no_quality_gate() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Condition id is mandatory and must not be empty");
+
+ underTest
+ .setMetricKey("metric")
+ .setOperator("LT")
+ .setWarning("warning")
+ .build();
+ }
+
+ @Test
+ public void fail_when_no_metric() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Metric key is mandatory and must not be empty");
+
+ underTest
+ .setConditionId(10)
+ .setOperator("LT")
+ .setWarning("warning")
+ .build();
+ }
+
+ @Test
+ public void fail_when_no_operator() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Operator is mandatory and must not be empty");
+
+ underTest
+ .setConditionId(10)
+ .setMetricKey("metric")
+ .setWarning("warning")
+ .build();
+ }
+
+}