From a210c65f91fab1542d11f11a4156734a08deebc2 Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Fri, 29 Jan 2016 19:13:49 +0100 Subject: [PATCH] SONAR-7152 WS api/qualitygates/project_status search by project id or key --- .../qualitygate/ws/ProjectStatusAction.java | 109 ++++++++++++--- .../ws/QualityGateDetailsFormatter.java | 88 ++++++++----- .../ws/ProjectStatusActionTest.java | 124 +++++++++++++----- .../ws/QualityGateDetailsFormatterTest.java | 24 ++-- .../qualitygate/ProjectStatusWsRequest.java | 34 ++++- .../qualitygate/QualityGatesService.java | 8 +- .../qualitygate/QualityGatesWsParameters.java | 32 +++++ 7 files changed, 318 insertions(+), 101 deletions(-) create mode 100644 sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/QualityGatesWsParameters.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ProjectStatusAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ProjectStatusAction.java index 2ad6a9c451b..416fb42496b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ProjectStatusAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ProjectStatusAction.java @@ -21,30 +21,42 @@ package org.sonar.server.qualitygate.ws; import com.google.common.base.Function; import com.google.common.base.Joiner; +import com.google.common.base.Optional; import com.google.common.collect.Lists; import java.util.Arrays; import java.util.Collections; import java.util.List; -import javax.annotation.CheckForNull; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; +import org.sonar.core.util.Uuids; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.SnapshotDto; import org.sonar.db.measure.MeasureDto; +import org.sonar.server.component.ComponentFinder; +import org.sonar.server.component.ComponentFinder.ParamNames; +import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.user.UserSession; +import org.sonar.server.ws.KeyExamples; import org.sonarqube.ws.WsQualityGates.ProjectStatusWsResponse; import org.sonarqube.ws.client.qualitygate.ProjectStatusWsRequest; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Strings.isNullOrEmpty; import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION; import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN; import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException; import static org.sonar.server.ws.WsUtils.checkFound; +import static org.sonar.server.ws.WsUtils.checkRequest; import static org.sonar.server.ws.WsUtils.writeProtobuf; +import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ANALYSIS_ID; +import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PROJECT_ID; +import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PROJECT_KEY; public class ProjectStatusAction implements QGateWsAction { private static final String QG_STATUSES_ONE_LINE = Joiner.on(", ") @@ -55,30 +67,44 @@ public class ProjectStatusAction implements QGateWsAction { return input.toString(); } })); + private static final String MSG_ONE_PARAMETER_ONLY = String.format("One (and only one) of the following parameters must be provided '%s', '%s', '%s'", + PARAM_ANALYSIS_ID, PARAM_PROJECT_ID, PARAM_PROJECT_KEY); private final DbClient dbClient; + private final ComponentFinder componentFinder; private final UserSession userSession; - public ProjectStatusAction(DbClient dbClient, UserSession userSession) { + public ProjectStatusAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession) { this.dbClient = dbClient; + this.componentFinder = componentFinder; this.userSession = userSession; } @Override public void define(WebService.NewController controller) { WebService.NewAction action = controller.createAction("project_status") - .setDescription(String.format("Quality gate status for a given Compute Engine task.
" + - "The different statuses returned are: %s. The %s status is returned when there is no Quality Gate associated with the analysis.
" + - "Returns a http code 404 if the analysis associated with the task is not found or does not exist.
" + + .setDescription(String.format("Get the quality gate status of a project or a Compute Engine task.
" + + MSG_ONE_PARAMETER_ONLY + "
" + + "The different statuses returned are: %s. The %s status is returned when there is no quality gate associated with the analysis.
" + + "Returns an HTTP code 404 if the analysis associated with the task is not found or does not exist.
" + "Requires 'Administer System' or 'Execute Analysis' permission.", QG_STATUSES_ONE_LINE, ProjectStatusWsResponse.Status.NONE)) .setResponseExample(getClass().getResource("project_status-example.json")) .setSince("5.3") .setHandler(this); - action.createParam("analysisId") + action.createParam(PARAM_ANALYSIS_ID) .setDescription("Analysis id") - .setRequired(true) .setExampleValue("2963"); + + action.createParam(PARAM_PROJECT_ID) + .setSince("5.4") + .setDescription("Project id") + .setExampleValue(Uuids.UUID_EXAMPLE_01); + + action.createParam(PARAM_PROJECT_KEY) + .setSince("5.4") + .setDescription("Project key") + .setExampleValue(KeyExamples.KEY_PROJECT_EXAMPLE_001); } @Override @@ -90,20 +116,42 @@ public class ProjectStatusAction implements QGateWsAction { private ProjectStatusWsResponse doHandle(ProjectStatusWsRequest request) { DbSession dbSession = dbClient.openSession(false); try { - String snapshotId = request.getAnalysisId(); - SnapshotDto snapshotDto = getSnapshot(dbSession, snapshotId); - ComponentDto projectDto = dbClient.componentDao().selectOrFailById(dbSession, snapshotDto.getComponentId()); - checkPermission(projectDto.uuid()); - String measureData = getQualityGateDetailsMeasureData(dbSession, snapshotDto); + ProjectAndSnapshot projectAndSnapshot = getProjectAndSnapshot(dbSession, request); + checkPermission(projectAndSnapshot.project.uuid()); + Optional measureData = getQualityGateDetailsMeasureData(dbSession, projectAndSnapshot.snapshotDto); return ProjectStatusWsResponse.newBuilder() - .setProjectStatus(new QualityGateDetailsFormatter(measureData, snapshotDto).format()) + .setProjectStatus(new QualityGateDetailsFormatter(measureData, projectAndSnapshot.snapshotDto).format()) .build(); } finally { dbClient.closeSession(dbSession); } } + private ProjectAndSnapshot getProjectAndSnapshot(DbSession dbSession, ProjectStatusWsRequest request) { + String snapshotId = request.getAnalysisId(); + if (!isNullOrEmpty(request.getAnalysisId())) { + return getSnapshotThenProject(dbSession, snapshotId); + } else if (!isNullOrEmpty(request.getProjectId()) ^ !isNullOrEmpty(request.getProjectKey())) { + return getProjectThenSnapshot(dbSession, request); + } + + throw new BadRequestException(MSG_ONE_PARAMETER_ONLY); + } + + private ProjectAndSnapshot getProjectThenSnapshot(DbSession dbSession, ProjectStatusWsRequest request) { + ComponentDto projectDto = componentFinder.getByUuidOrKey(dbSession, request.getProjectId(), request.getProjectKey(), ParamNames.PROJECT_ID_AND_KEY); + SnapshotDto snapshotDto = dbClient.snapshotDao().selectLastSnapshotByComponentId(dbSession, projectDto.getId()); + checkState(snapshotDto != null, "Last analysis of project '%s' not found", projectDto.getKey()); + return new ProjectAndSnapshot(projectDto, snapshotDto); + } + + private ProjectAndSnapshot getSnapshotThenProject(DbSession dbSession, String snapshotId) { + SnapshotDto snapshotDto = getSnapshot(dbSession, snapshotId); + ComponentDto projectDto = dbClient.componentDao().selectOrFailById(dbSession, snapshotDto.getComponentId()); + return new ProjectAndSnapshot(projectDto, snapshotDto); + } + private SnapshotDto getSnapshot(DbSession dbSession, String snapshotIdFromRequest) { Long snapshotId = null; try { @@ -120,19 +168,30 @@ public class ProjectStatusAction implements QGateWsAction { return checkFound(snapshotDto, "Analysis with id '%s' is not found", snapshotIdFromRequest); } - @CheckForNull - private String getQualityGateDetailsMeasureData(DbSession dbSession, SnapshotDto snapshotDto) { - List measures = dbClient.measureDao().selectBySnapshotIdAndMetricKeys(snapshotDto.getId(), + private Optional getQualityGateDetailsMeasureData(DbSession dbSession, Optional snapshotDto) { + if (!snapshotDto.isPresent()) { + return Optional.absent(); + } + + List measures = dbClient.measureDao().selectBySnapshotIdAndMetricKeys(snapshotDto.get().getId(), Collections.singleton(CoreMetrics.QUALITY_GATE_DETAILS_KEY), dbSession); return measures.isEmpty() - ? null - : measures.get(0).getData(); + ? Optional.absent() + : Optional.fromNullable(measures.get(0).getData()); } private static ProjectStatusWsRequest toProjectStatusWsRequest(Request request) { - return new ProjectStatusWsRequest() - .setAnalysisId(request.mandatoryParam("analysisId")); + ProjectStatusWsRequest projectStatusWsRequest = new ProjectStatusWsRequest() + .setAnalysisId(request.param(PARAM_ANALYSIS_ID)) + .setProjectId(request.param(PARAM_PROJECT_ID)) + .setProjectKey(request.param(PARAM_PROJECT_KEY)); + checkRequest( + !isNullOrEmpty(projectStatusWsRequest.getAnalysisId()) + ^ !isNullOrEmpty(projectStatusWsRequest.getProjectId()) + ^ !isNullOrEmpty(projectStatusWsRequest.getProjectKey()), + MSG_ONE_PARAMETER_ONLY); + return projectStatusWsRequest; } private void checkPermission(String projectUuid) { @@ -141,4 +200,14 @@ public class ProjectStatusAction implements QGateWsAction { throw insufficientPrivilegesException(); } } + + private static class ProjectAndSnapshot { + private final ComponentDto project; + private final Optional snapshotDto; + + private ProjectAndSnapshot(ComponentDto project, @Nullable SnapshotDto snapshotDto) { + this.project = project; + this.snapshotDto = Optional.fromNullable(snapshotDto); + } + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java index 9ed8f3c1313..21888dbb6fd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java @@ -19,11 +19,11 @@ */ package org.sonar.server.qualitygate.ws; +import com.google.common.base.Optional; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonar.db.component.SnapshotDto; import org.sonarqube.ws.WsQualityGates.ProjectStatusWsResponse; @@ -32,24 +32,23 @@ import static com.google.common.base.Strings.isNullOrEmpty; import static org.sonar.api.utils.DateUtils.formatDateTime; public class QualityGateDetailsFormatter { - @CheckForNull - private final String measureData; - private final SnapshotDto snapshot; + private final Optional optionalMeasureData; + private final Optional optionalSnapshot; private final ProjectStatusWsResponse.ProjectStatus.Builder projectStatusBuilder; - public QualityGateDetailsFormatter(@Nullable String measureData, SnapshotDto snapshot) { - this.measureData = measureData; - this.snapshot = snapshot; + public QualityGateDetailsFormatter(Optional measureData, Optional snapshot) { + this.optionalMeasureData = measureData; + this.optionalSnapshot = snapshot; this.projectStatusBuilder = ProjectStatusWsResponse.ProjectStatus.newBuilder(); } public ProjectStatusWsResponse.ProjectStatus format() { - if (isNullOrEmpty(measureData)) { + if (!optionalMeasureData.isPresent()) { return newResponseWithoutQualityGateDetails(); } JsonParser parser = new JsonParser(); - JsonObject json = parser.parse(measureData).getAsJsonObject(); + JsonObject json = parser.parse(optionalMeasureData.get()).getAsJsonObject(); ProjectStatusWsResponse.Status qualityGateStatus = measureLevelToQualityGateStatus(json.get("level").getAsString()); projectStatusBuilder.setStatus(qualityGateStatus); @@ -61,11 +60,16 @@ public class QualityGateDetailsFormatter { } private void formatPeriods() { + if (!optionalSnapshot.isPresent()) { + return; + } + ProjectStatusWsResponse.Period.Builder periodBuilder = ProjectStatusWsResponse.Period.newBuilder(); for (int i = 1; i <= 5; i++) { periodBuilder.clear(); boolean doesPeriodExist = false; + SnapshotDto snapshot = this.optionalSnapshot.get(); if (!isNullOrEmpty(snapshot.getPeriodMode(i))) { doesPeriodExist = true; periodBuilder.setIndex(i); @@ -97,44 +101,66 @@ public class QualityGateDetailsFormatter { private void formatCondition(JsonObject jsonCondition) { ProjectStatusWsResponse.Condition.Builder conditionBuilder = ProjectStatusWsResponse.Condition.newBuilder(); - JsonElement measureLevel = jsonCondition.get("level"); - if (measureLevel != null && !isNullOrEmpty(measureLevel.getAsString())) { - conditionBuilder.setStatus(measureLevelToQualityGateStatus(measureLevel.getAsString())); + formatConditionLevel(conditionBuilder, jsonCondition); + formatConditionMetric(conditionBuilder, jsonCondition); + formatConditionOperation(conditionBuilder, jsonCondition); + formatConditionPeriod(conditionBuilder, jsonCondition); + formatConditionWarningThreshold(conditionBuilder, jsonCondition); + formatConditionErrorThreshold(conditionBuilder, jsonCondition); + formatConditionActual(conditionBuilder, jsonCondition); + + projectStatusBuilder.addConditions(conditionBuilder); + } + + private static void formatConditionActual(ProjectStatusWsResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) { + JsonElement actual = jsonCondition.get("actual"); + if (actual != null && !isNullOrEmpty(actual.getAsString())) { + conditionBuilder.setActualValue(actual.getAsString()); } + } - JsonElement metric = jsonCondition.get("metric"); - if (metric != null && !isNullOrEmpty(metric.getAsString())) { - conditionBuilder.setMetricKey(metric.getAsString()); + private static void formatConditionErrorThreshold(ProjectStatusWsResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) { + JsonElement error = jsonCondition.get("error"); + if (error != null && !isNullOrEmpty(error.getAsString())) { + conditionBuilder.setErrorThreshold(error.getAsString()); } + } - JsonElement op = jsonCondition.get("op"); - if (op != null && !isNullOrEmpty(op.getAsString())) { - String stringOp = op.getAsString(); - ProjectStatusWsResponse.Comparator comparator = measureOpToQualityGateComparator(stringOp); - conditionBuilder.setComparator(comparator); + private static void formatConditionWarningThreshold(ProjectStatusWsResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) { + JsonElement warning = jsonCondition.get("warning"); + if (warning != null && !isNullOrEmpty(warning.getAsString())) { + conditionBuilder.setWarningThreshold(warning.getAsString()); } + } + private static void formatConditionPeriod(ProjectStatusWsResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) { JsonElement periodIndex = jsonCondition.get("period"); if (periodIndex != null && !isNullOrEmpty(periodIndex.getAsString())) { conditionBuilder.setPeriodIndex(periodIndex.getAsInt()); } + } - JsonElement warning = jsonCondition.get("warning"); - if (warning != null && !isNullOrEmpty(warning.getAsString())) { - conditionBuilder.setWarningThreshold(warning.getAsString()); + private static void formatConditionOperation(ProjectStatusWsResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) { + JsonElement op = jsonCondition.get("op"); + if (op != null && !isNullOrEmpty(op.getAsString())) { + String stringOp = op.getAsString(); + ProjectStatusWsResponse.Comparator comparator = measureOpToQualityGateComparator(stringOp); + conditionBuilder.setComparator(comparator); } + } - JsonElement error = jsonCondition.get("error"); - if (error != null && !isNullOrEmpty(error.getAsString())) { - conditionBuilder.setErrorThreshold(error.getAsString()); + private static void formatConditionMetric(ProjectStatusWsResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) { + JsonElement metric = jsonCondition.get("metric"); + if (metric != null && !isNullOrEmpty(metric.getAsString())) { + conditionBuilder.setMetricKey(metric.getAsString()); } + } - JsonElement actual = jsonCondition.get("actual"); - if (actual != null && !isNullOrEmpty(actual.getAsString())) { - conditionBuilder.setActualValue(actual.getAsString()); + private static void formatConditionLevel(ProjectStatusWsResponse.Condition.Builder conditionBuilder, JsonObject jsonCondition) { + JsonElement measureLevel = jsonCondition.get("level"); + if (measureLevel != null && !isNullOrEmpty(measureLevel.getAsString())) { + conditionBuilder.setStatus(measureLevelToQualityGateStatus(measureLevel.getAsString())); } - - projectStatusBuilder.addConditions(conditionBuilder); } private static ProjectStatusWsResponse.Status measureLevelToQualityGateStatus(String measureLevel) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ProjectStatusActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ProjectStatusActionTest.java index f50387016d0..093d1c6749c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ProjectStatusActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ProjectStatusActionTest.java @@ -34,8 +34,9 @@ import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.SnapshotDto; -import org.sonar.db.measure.MeasureDto; import org.sonar.db.metric.MetricDto; +import org.sonar.server.component.ComponentFinder; +import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.tester.UserSessionRule; @@ -54,6 +55,9 @@ import static org.sonar.db.component.SnapshotTesting.newSnapshotForProject; import static org.sonar.db.measure.MeasureTesting.newMeasureDto; import static org.sonar.db.metric.MetricTesting.newMetricDto; import static org.sonar.test.JsonAssert.assertJson; +import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ANALYSIS_ID; +import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PROJECT_ID; +import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PROJECT_KEY; @Category(DbTests.class) public class ProjectStatusActionTest { @@ -74,7 +78,7 @@ public class ProjectStatusActionTest { dbClient = db.getDbClient(); dbSession = db.getSession(); - ws = new WsActionTester(new ProjectStatusAction(dbClient, userSession)); + ws = new WsActionTester(new ProjectStatusAction(dbClient, new ComponentFinder(dbClient), userSession)); } @Test @@ -108,61 +112,79 @@ public class ProjectStatusActionTest { } @Test - public void fail_if_no_snapshot_id_found() { - userSession.login("john").setGlobalPermissions(SYSTEM_ADMIN); - - expectedException.expect(NotFoundException.class); - expectedException.expectMessage("Analysis with id 'task-uuid' is not found"); - - newRequest(ANALYSIS_ID); - } - - @Test - public void return_undefined_status_if_measure_is_not_found() { + public void return_status_by_project_id() throws IOException { userSession.login("john").setGlobalPermissions(SYSTEM_ADMIN); ComponentDto project = newProjectDto("project-uuid"); dbClient.componentDao().insert(dbSession, project); - SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newSnapshotForProject(project)); + SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newSnapshotForProject(project) + .setPeriodMode(1, "last_period") + .setPeriodDate(1, 956789123456L) + .setPeriodMode(2, "last_version") + .setPeriodParam(2, "2015-12-07") + .setPeriodDate(2, 956789123987L) + .setPeriodMode(3, "last_analysis") + .setPeriodMode(5, "last_30_days") + .setPeriodParam(5, "2015-11-07")); + MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDto() + .setEnabled(true) + .setKey(CoreMetrics.QUALITY_GATE_DETAILS_KEY)); + dbClient.measureDao().insert(dbSession, + newMeasureDto(metric, snapshot.getId()) + .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionTest/measure_data.json")))); dbSession.commit(); - ProjectStatusWsResponse result = newRequest(snapshot.getId().toString()); + String response = ws.newRequest() + .setParam(PARAM_PROJECT_ID, "project-uuid") + .execute().getInput(); - assertThat(result.getProjectStatus().getStatus()).isEqualTo(Status.NONE); - assertThat(result.getProjectStatus().getConditionsCount()).isEqualTo(0); + assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json")); } @Test - public void return_undefined_status_if_measure_data_is_not_well_formatted() { - userSession.login("john").setGlobalPermissions(SCAN_EXECUTION); + public void return_status_by_project_key() throws IOException { + userSession.login("john").setGlobalPermissions(SYSTEM_ADMIN); - ComponentDto project = newProjectDto("project-uuid"); + ComponentDto project = newProjectDto("project-uuid") + .setKey("project-key"); dbClient.componentDao().insert(dbSession, project); - SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newSnapshotForProject(project)); + SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newSnapshotForProject(project) + .setPeriodMode(1, "last_period") + .setPeriodDate(1, 956789123456L) + .setPeriodMode(2, "last_version") + .setPeriodParam(2, "2015-12-07") + .setPeriodDate(2, 956789123987L) + .setPeriodMode(3, "last_analysis") + .setPeriodMode(5, "last_30_days") + .setPeriodParam(5, "2015-11-07")); MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDto() .setEnabled(true) .setKey(CoreMetrics.QUALITY_GATE_DETAILS_KEY)); - MeasureDto measure = newMeasureDto(metric, snapshot.getId()).setData(""); - dbClient.measureDao().insert(dbSession, measure); + dbClient.measureDao().insert(dbSession, + newMeasureDto(metric, snapshot.getId()) + .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionTest/measure_data.json")))); dbSession.commit(); - ProjectStatusWsResponse result = newRequest(String.valueOf(snapshot.getId())); + String response = ws.newRequest() + .setParam(PARAM_PROJECT_KEY, "project-key") + .execute().getInput(); - assertThat(result.getProjectStatus().getStatus()).isEqualTo(Status.NONE); - assertThat(result.getProjectStatus().getConditionsCount()).isEqualTo(0); + assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json")); } @Test - public void fail_if_insufficient_privileges() { - userSession.login("john").setGlobalPermissions(PREVIEW_EXECUTION); + public void return_undefined_status_if_measure_is_not_found() { + userSession.login("john").setGlobalPermissions(SYSTEM_ADMIN); ComponentDto project = newProjectDto("project-uuid"); dbClient.componentDao().insert(dbSession, project); SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newSnapshotForProject(project)); dbSession.commit(); - expectedException.expect(ForbiddenException.class); - newRequest(snapshot.getId().toString()); + ProjectStatusWsResponse result = newRequest(snapshot.getId().toString()); + + assertThat(result.getProjectStatus().getStatus()).isEqualTo(Status.NONE); + assertThat(result.getProjectStatus().getConditionsCount()).isEqualTo(0); } @Test @@ -201,6 +223,48 @@ public class ProjectStatusActionTest { newRequest(snapshot.getId().toString()); } + @Test + public void fail_if_no_snapshot_id_found() { + userSession.login("john").setGlobalPermissions(SYSTEM_ADMIN); + + expectedException.expect(NotFoundException.class); + expectedException.expectMessage("Analysis with id 'task-uuid' is not found"); + + newRequest(ANALYSIS_ID); + } + + @Test + public void fail_if_insufficient_privileges() { + userSession.login("john").setGlobalPermissions(PREVIEW_EXECUTION); + + ComponentDto project = newProjectDto("project-uuid"); + dbClient.componentDao().insert(dbSession, project); + SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newSnapshotForProject(project)); + dbSession.commit(); + + expectedException.expect(ForbiddenException.class); + newRequest(snapshot.getId().toString()); + } + + @Test + public void fail_if_project_id_and_ce_task_id_provided() { + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("One (and only one) of the following parameters must be provided 'analysisId', 'projectId', 'projectKey'"); + + ws.newRequest() + .setParam(PARAM_ANALYSIS_ID, "analysis-id") + .setParam(PARAM_PROJECT_ID, "project-uuid") + .execute().getInput(); + } + + @Test + public void fail_if_no_parameter_provided() { + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("One (and only one) of the following parameters must be provided 'analysisId', 'projectId', 'projectKey'"); + + ws.newRequest().execute().getInput(); + } + private ProjectStatusWsResponse newRequest(String taskId) { try { return ProjectStatusWsResponse.parseFrom( diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest.java index b421b5df532..19355575e30 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest.java @@ -19,8 +19,10 @@ */ package org.sonar.server.qualitygate.ws; +import com.google.common.base.Optional; import java.io.IOException; import java.util.List; +import javax.annotation.Nullable; import org.apache.commons.io.IOUtils; import org.junit.Rule; import org.junit.Test; @@ -49,7 +51,7 @@ public class QualityGateDetailsFormatterTest { .setPeriodMode(3, "last_analysis") .setPeriodMode(5, "last_30_days") .setPeriodParam(5, "2015-11-07"); - underTest = new QualityGateDetailsFormatter(measureData, snapshot); + underTest = newQualityGateDetailsFormatter(measureData, snapshot); ProjectStatus result = underTest.format(); @@ -82,22 +84,10 @@ public class QualityGateDetailsFormatterTest { assertThat(periods).extracting("index").containsExactly(1, 2, 3, 5); assertThat(periods).extracting("mode").containsExactly("last_period", "last_version", "last_analysis", "last_30_days"); assertThat(periods).extracting("parameter").containsExactly("", "2015-12-07", "", "2015-11-07"); - System.out.println(System.currentTimeMillis()); assertThat(periods.get(0).getDate()).startsWith("2015-12-07T"); assertThat(periods.get(1).getDate()).startsWith("2015-12-06T"); } - @Test - public void undefined_quality_gate_when_ill_formatted_measure_data() { - underTest = new QualityGateDetailsFormatter("", new SnapshotDto()); - - ProjectStatus result = underTest.format(); - - assertThat(result.getStatus()).isEqualTo(ProjectStatusWsResponse.Status.NONE); - assertThat(result.getPeriodsCount()).isEqualTo(0); - assertThat(result.getConditionsCount()).isEqualTo(0); - } - @Test public void fail_when_measure_level_is_unknown() { String measureData = "{\n" + @@ -114,7 +104,7 @@ public class QualityGateDetailsFormatterTest { " }\n" + " ]\n" + "}"; - underTest = new QualityGateDetailsFormatter(measureData, new SnapshotDto()); + underTest = newQualityGateDetailsFormatter(measureData, new SnapshotDto()); expectedException.expect(IllegalStateException.class); expectedException.expectMessage("Unknown quality gate status 'UNKNOWN'"); @@ -137,10 +127,14 @@ public class QualityGateDetailsFormatterTest { " }\n" + " ]\n" + "}"; - underTest = new QualityGateDetailsFormatter(measureData, new SnapshotDto()); + underTest = newQualityGateDetailsFormatter(measureData, new SnapshotDto()); expectedException.expect(IllegalStateException.class); expectedException.expectMessage("Unknown quality gate comparator 'UNKNOWN'"); underTest.format(); } + + private static QualityGateDetailsFormatter newQualityGateDetailsFormatter(@Nullable String measureData, @Nullable SnapshotDto snapshotDto) { + return new QualityGateDetailsFormatter(Optional.fromNullable(measureData), Optional.fromNullable(snapshotDto)); + } } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/ProjectStatusWsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/ProjectStatusWsRequest.java index e5bf09d8e3c..328efa5bb58 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/ProjectStatusWsRequest.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/ProjectStatusWsRequest.java @@ -19,15 +19,41 @@ */ package org.sonarqube.ws.client.qualitygate; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + public class ProjectStatusWsRequest { - private String taskId; + private String analysisId; + private String projectId; + private String projectKey; + @CheckForNull public String getAnalysisId() { - return taskId; + return analysisId; + } + + public ProjectStatusWsRequest setAnalysisId(@Nullable String analysisId) { + this.analysisId = analysisId; + return this; + } + + @CheckForNull + public String getProjectId() { + return projectId; + } + + public ProjectStatusWsRequest setProjectId(@Nullable String projectId) { + this.projectId = projectId; + return this; + } + + @CheckForNull + public String getProjectKey() { + return projectKey; } - public ProjectStatusWsRequest setAnalysisId(String taskId) { - this.taskId = taskId; + public ProjectStatusWsRequest setProjectKey(@Nullable String projectKey) { + this.projectKey = projectKey; return this; } } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/QualityGatesService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/QualityGatesService.java index 6142b9aa167..bd00874f57a 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/QualityGatesService.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/QualityGatesService.java @@ -24,6 +24,10 @@ import org.sonarqube.ws.client.BaseService; import org.sonarqube.ws.client.GetRequest; import org.sonarqube.ws.client.WsConnector; +import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_ANALYSIS_ID; +import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PROJECT_ID; +import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PROJECT_KEY; + public class QualityGatesService extends BaseService { public QualityGatesService(WsConnector wsConnector) { @@ -32,7 +36,9 @@ public class QualityGatesService extends BaseService { public ProjectStatusWsResponse projectStatus(ProjectStatusWsRequest request) { return call(new GetRequest(path("project_status")) - .setParam("analysisId", request.getAnalysisId()), + .setParam(PARAM_ANALYSIS_ID, request.getAnalysisId()) + .setParam(PARAM_PROJECT_ID, request.getProjectId()) + .setParam(PARAM_PROJECT_KEY, request.getProjectKey()), ProjectStatusWsResponse.parser()); } } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/QualityGatesWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/QualityGatesWsParameters.java new file mode 100644 index 00000000000..5ba885b3dd3 --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/QualityGatesWsParameters.java @@ -0,0 +1,32 @@ +/* + * 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; + +public class QualityGatesWsParameters { + public static final String PARAM_ANALYSIS_ID = "analysisId"; + public static final String PARAM_PROJECT_ID = "projectId"; + public static final String PARAM_PROJECT_KEY = "projectKey"; + + private QualityGatesWsParameters() { + // prevent instantiation + } + +} -- 2.39.5