From 0a5e742023c7ee832c2b52de93e1c27cf76c34f4 Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Fri, 4 Dec 2015 12:09:25 +0100 Subject: [PATCH] SONAR-6900 New WS api/qualitygates/project_status quality gate details of an analysis --- .../server/component/ws/ResourcesWs.java | 23 ++- .../server/computation/ws/ActivityAction.java | 1 - .../server/computation/ws/CeWsModule.java | 2 +- .../computation/ws/ComponentAction.java | 1 - .../server/computation/ws/TaskAction.java | 10 +- .../server/computation/ws/TaskFormatter.java | 3 + .../platformlevel/PlatformLevel4.java | 2 + .../qualitygate/ws/ProjectStatusAction.java | 125 +++++++++++++ .../ws/QualityGateDetailsFormatter.java | 164 +++++++++++++++++ .../server/user/AbstractUserSession.java | 13 ++ .../server/user/ThreadLocalUserSession.java | 6 + .../org/sonar/server/user/UserSession.java | 7 + .../computation/ws/activity-example.json | 1 + .../computation/ws/component-example.json | 1 + .../server/computation/ws/task-example.json | 1 + .../ws/project_status-example.json | 85 +++++++++ .../computation/ws/ActivityActionTest.java | 2 + .../computation/ws/ComponentActionTest.java | 7 +- .../server/computation/ws/TaskActionTest.java | 17 +- .../computation/ws/TaskFormatterTest.java | 3 +- .../ws/ProjectStatusActionTest.java | 167 ++++++++++++++++++ .../ws/QualityGateDetailsFormatterTest.java | 147 +++++++++++++++ .../sonar/server/tester/UserSessionRule.java | 6 + .../ProjectStatusActionTest/measure_data.json | 66 +++++++ .../quality_gate_details.json | 50 ++++++ .../1004_add_ce_activity_snapshot_id.rb | 2 +- .../java/org/sonar/db/ce/CeActivityDto.java | 33 ++-- .../main/java/org/sonar/db/ce/CeQueueDto.java | 24 ++- .../org/sonar/db/ce/CeActivityMapper.xml | 2 +- .../java/org/sonar/db/ce/CeQueueTesting.java | 42 +++++ .../org/sonar/db/measure/MeasureTesting.java | 40 +++++ .../org/sonarqube/ws/client/HttpWsClient.java | 8 + .../org/sonarqube/ws/client/WsClient.java | 3 + .../qualitygate/ProjectStatusWsRequest.java | 34 ++++ .../qualitygate/QualityGatesService.java | 39 ++++ .../ws/client/qualitygate/package-info.java | 26 +++ sonar-ws/src/main/protobuf/ws-ce.proto | 17 +- .../src/main/protobuf/ws-qualitygates.proto | 69 ++++++++ 38 files changed, 1187 insertions(+), 62 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ProjectStatusAction.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java create mode 100644 server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/project_status-example.json create mode 100644 server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ProjectStatusActionTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest.java create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/qualitygate/ws/ProjectStatusActionTest/measure_data.json create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest/quality_gate_details.json create mode 100644 sonar-db/src/test/java/org/sonar/db/ce/CeQueueTesting.java create mode 100644 sonar-db/src/test/java/org/sonar/db/measure/MeasureTesting.java create mode 100644 sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/ProjectStatusWsRequest.java create mode 100644 sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/QualityGatesService.java create mode 100644 sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/package-info.java create mode 100644 sonar-ws/src/main/protobuf/ws-qualitygates.proto diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ResourcesWs.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ResourcesWs.java index 03f16a80f70..5ee2466181f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ResourcesWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ResourcesWs.java @@ -26,9 +26,6 @@ import org.sonar.api.server.ws.WebService; public class ResourcesWs implements WebService { - private static final String FALSE = "false"; - private static final String TRUE = "true"; - @Override public void define(Context context) { NewController controller = context.createController("api/resources") @@ -92,8 +89,8 @@ public class ResourcesWs implements WebService { action.createParam("verbose") .setDescription("Add some data to response") - .setDefaultValue(FALSE) - .setPossibleValues(TRUE, FALSE); + .setBooleanPossibleValues() + .setDefaultValue(String.valueOf(false)); action.createParam("limit") .setDescription("Limit the number of results. Only used if one metric, and only one, is set") @@ -101,19 +98,19 @@ public class ResourcesWs implements WebService { action.createParam("includetrends") .setDescription("Include period variations in response: add nodes <p*> for period variations") - .setDefaultValue(FALSE) - .setPossibleValues(TRUE, FALSE); + .setDefaultValue(String.valueOf(false)) + .setBooleanPossibleValues(); action.createParam("includealerts") .setDescription("Include alerts data: add nodes <alert> (ERROR, WARN, OK) and <alert_text>") - .setDefaultValue(FALSE) - .setPossibleValues(TRUE, FALSE); + .setBooleanPossibleValues() + .setDefaultValue(String.valueOf(false)); action.createParam("rules") .setDescription("Filter on rules: setting it to true will return rules id and rule name for measure having such info " + "(such as 'blocker_violations', 'critical_violations', ..., 'new_blocker_violations', ...). Possible values: true | false | list of rule ids") - .setDefaultValue(FALSE) - .setExampleValue(TRUE); + .setBooleanPossibleValues() + .setDefaultValue(String.valueOf(true)); RailsHandler.addFormatParam(action); } @@ -133,8 +130,8 @@ public class ResourcesWs implements WebService { action.createParam("display_key") .setDescription("Return the resource key instead of the resource id") - .setDefaultValue(FALSE) - .setPossibleValues(TRUE, FALSE); + .setBooleanPossibleValues() + .setDefaultValue(String.valueOf(false)); action.createParam("q") .setDescription("Comma-separated list of qualifiers") diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ActivityAction.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ActivityAction.java index 6decd092133..e7b483eed85 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ActivityAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ActivityAction.java @@ -74,7 +74,6 @@ public class ActivityAction implements CeWsAction { @Override public void define(WebService.NewController controller) { WebService.NewAction action = controller.createAction("activity") - .setInternal(true) .setDescription(format("Search for past task executions. Requires the system administration permission, " + "or project administration permission if %s is set.", PARAM_COMPONENT_UUID)) .setResponseExample(getClass().getResource("activity-example.json")) diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsModule.java index 09b9e10599b..5db2f82871f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsModule.java @@ -25,11 +25,11 @@ public class CeWsModule extends Module { @Override protected void configureModule() { add( + CeWs.class, ActivityAction.class, CancelAction.class, CancelAllAction.class, QueueAction.class, - CeWs.class, IsQueueEmptyWs.class, LogsAction.class, ComponentAction.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ComponentAction.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ComponentAction.java index 07b70fd22e0..24822f358a6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ComponentAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ComponentAction.java @@ -55,7 +55,6 @@ public class ComponentAction implements CeWsAction { WebService.NewAction action = controller.createAction("component") .setDescription("Get the pending tasks, in-progress tasks and the last executed task of a given component " + "(usually a project). Requires the administration permission on the component.") - .setInternal(true) .setSince("5.2") .setResponseExample(getClass().getResource("component-example.json")) .setHandler(this); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/TaskAction.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/TaskAction.java index d3c3eb68974..e79851156dd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/TaskAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/TaskAction.java @@ -20,6 +20,8 @@ package org.sonar.server.computation.ws; import com.google.common.base.Optional; +import com.google.common.collect.ImmutableSet; +import java.util.Set; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; @@ -39,6 +41,7 @@ public class TaskAction implements CeWsAction { public static final String ACTION = "task"; public static final String PARAM_TASK_UUID = "id"; + private static final Set AUTHORIZED_PERMISSIONS = ImmutableSet.of(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.SYSTEM_ADMIN); private final DbClient dbClient; private final TaskFormatter wsTaskFormatter; @@ -55,7 +58,6 @@ public class TaskAction implements CeWsAction { WebService.NewAction action = controller.createAction(ACTION) .setDescription("Give Compute Engine task details such as type, status, duration and associated component.
" + "Requires 'Administer System' or 'Execute Analysis' permission.") - .setInternal(true) .setResponseExample(getClass().getResource("task-example.json")) .setSince("5.2") .setHandler(this); @@ -69,11 +71,7 @@ public class TaskAction implements CeWsAction { @Override public void handle(Request wsRequest, Response wsResponse) throws Exception { - if (!userSession.hasGlobalPermission(GlobalPermissions.SYSTEM_ADMIN) - // WS can be used at the end of an analysis to implement a build breaker - && !userSession.hasGlobalPermission(GlobalPermissions.SCAN_EXECUTION)) { - userSession.checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN); - } + userSession.checkAnyGlobalPermissions(AUTHORIZED_PERMISSIONS); String taskUuid = wsRequest.mandatoryParam(PARAM_TASK_UUID); DbSession dbSession = dbClient.openSession(false); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/TaskFormatter.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/TaskFormatter.java index 1346e6e654d..471c9646e30 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/TaskFormatter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/TaskFormatter.java @@ -115,6 +115,9 @@ public class TaskFormatter { builder.setComponentId(dto.getComponentUuid()); buildComponent(builder, componentCache.get(dto.getComponentUuid())); } + if (dto.getSnapshotId() != null) { + builder.setAnalysisId(String.valueOf(dto.getSnapshotId())); + } if (dto.getSubmitterLogin() != null) { builder.setSubmitterLogin(dto.getSubmitterLogin()); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index 53b153bb349..25e8a70c500 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -206,6 +206,7 @@ import org.sonar.server.qualitygate.ws.CreateConditionAction; import org.sonar.server.qualitygate.ws.DeleteConditionAction; import org.sonar.server.qualitygate.ws.DeselectAction; import org.sonar.server.qualitygate.ws.DestroyAction; +import org.sonar.server.qualitygate.ws.ProjectStatusAction; import org.sonar.server.qualitygate.ws.QGatesWs; import org.sonar.server.qualitygate.ws.SelectAction; import org.sonar.server.qualitygate.ws.SetAsDefaultAction; @@ -507,6 +508,7 @@ public class PlatformLevel4 extends PlatformLevel { DeleteConditionAction.class, UpdateConditionAction.class, org.sonar.server.qualitygate.ws.AppAction.class, + ProjectStatusAction.class, QGatesWs.class, // web services 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 new file mode 100644 index 00000000000..fb7e92c9a47 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ProjectStatusAction.java @@ -0,0 +1,125 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.util.Collections; +import java.util.List; +import javax.annotation.CheckForNull; +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.permission.GlobalPermissions; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.component.SnapshotDto; +import org.sonar.db.measure.MeasureDto; +import org.sonar.server.user.UserSession; +import org.sonarqube.ws.WsQualityGates.ProjectStatusWsResponse; +import org.sonarqube.ws.client.qualitygate.ProjectStatusWsRequest; + +import static com.google.common.collect.Sets.newHashSet; +import static org.sonar.server.ws.WsUtils.checkFound; +import static org.sonar.server.ws.WsUtils.writeProtobuf; + +public class ProjectStatusAction implements QGateWsAction { + private final DbClient dbClient; + private final UserSession userSession; + + public ProjectStatusAction(DbClient dbClient, UserSession userSession) { + this.dbClient = dbClient; + this.userSession = userSession; + } + + @Override + public void define(WebService.NewController controller) { + WebService.NewAction action = controller.createAction("project_status") + .setDescription("Quality gate status for a given Compute Engine task.
" + + "Returns a http code 404 if the analysis associated with the task is not found or does not exist.
" + + "Requires 'Administer System' or 'Execute Analysis' permission.") + .setResponseExample(getClass().getResource("project_status-example.json")) + .setSince("5.3") + .setHandler(this); + + action.createParam("analysisId") + .setDescription("Analysis id") + .setRequired(true) + .setExampleValue("2963"); + } + + @Override + public void handle(Request request, Response response) throws Exception { + ProjectStatusWsResponse projectStatusWsResponse = doHandle(toProjectStatusWsRequest(request)); + writeProtobuf(projectStatusWsResponse, request, response); + } + + private ProjectStatusWsResponse doHandle(ProjectStatusWsRequest request) { + checkScanOrAdminPermission(); + + DbSession dbSession = dbClient.openSession(false); + try { + String snapshotId = request.getAnalysisId(); + SnapshotDto snapshotDto = getSnapshot(dbSession, snapshotId); + String measureData = getQualityGateDetailsMeasureData(dbSession, snapshotDto); + + return ProjectStatusWsResponse.newBuilder() + .setProjectStatus(new QualityGateDetailsFormatter(measureData, snapshotDto).format()) + .build(); + } finally { + dbClient.closeSession(dbSession); + } + } + + private SnapshotDto getSnapshot(DbSession dbSession, String snapshotIdFromRequest) { + Long snapshotId = null; + try { + snapshotId = Long.parseLong(snapshotIdFromRequest); + } catch (NumberFormatException e) { + // checks String is a long + } + + SnapshotDto snapshotDto = null; + if (snapshotId != null) { + snapshotDto = dbClient.snapshotDao().selectById(dbSession, snapshotId); + } + + 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(), + Collections.singleton(CoreMetrics.QUALITY_GATE_DETAILS_KEY), dbSession); + + return measures.isEmpty() + ? null + : measures.get(0).getData(); + } + + private static ProjectStatusWsRequest toProjectStatusWsRequest(Request request) { + return new ProjectStatusWsRequest() + .setAnalysisId(request.mandatoryParam("analysisId")); + } + + private void checkScanOrAdminPermission() { + userSession.checkAnyGlobalPermissions(newHashSet(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.SYSTEM_ADMIN)); + } +} 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 new file mode 100644 index 00000000000..0450cbc9f80 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java @@ -0,0 +1,164 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.qualitygate.ws; + +import com.google.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; + +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 ProjectStatusWsResponse.ProjectStatus.Builder projectStatusBuilder; + + public QualityGateDetailsFormatter(@Nullable String measureData, SnapshotDto snapshot) { + this.measureData = measureData; + this.snapshot = snapshot; + this.projectStatusBuilder = ProjectStatusWsResponse.ProjectStatus.newBuilder(); + } + + public ProjectStatusWsResponse.ProjectStatus format() { + if (isNullOrEmpty(measureData)) { + return newResponseWithoutQualityGateDetails(); + } + + JsonParser parser = new JsonParser(); + JsonObject json = parser.parse(measureData).getAsJsonObject(); + + ProjectStatusWsResponse.Status qualityGateStatus = measureLevelToQualityGateStatus(json.get("level").getAsString()); + projectStatusBuilder.setStatus(qualityGateStatus); + + formatConditions(json.getAsJsonArray("conditions")); + formatPeriods(); + + return projectStatusBuilder.build(); + } + + private void formatPeriods() { + ProjectStatusWsResponse.Period.Builder periodBuilder = ProjectStatusWsResponse.Period.newBuilder(); + for (int i = 1; i <= 5; i++) { + periodBuilder.clear(); + boolean doesPeriodExist = false; + + if (!isNullOrEmpty(snapshot.getPeriodMode(i))) { + doesPeriodExist = true; + periodBuilder.setIndex(i); + periodBuilder.setMode(snapshot.getPeriodMode(i)); + if (snapshot.getPeriodDate(i) != null) { + periodBuilder.setDate(formatDateTime(snapshot.getPeriodDate(i))); + } + if (!isNullOrEmpty(snapshot.getPeriodModeParameter(i))) { + periodBuilder.setParameter(snapshot.getPeriodModeParameter(i)); + } + } + + if (doesPeriodExist) { + projectStatusBuilder.addPeriods(periodBuilder); + } + } + } + + private void formatConditions(@Nullable JsonArray jsonConditions) { + if (jsonConditions == null) { + return; + } + + for (JsonElement jsonCondition : jsonConditions) { + formatCondition(jsonCondition.getAsJsonObject()); + } + } + + 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())); + } + + JsonElement metric = jsonCondition.get("metric"); + if (metric != null && !isNullOrEmpty(metric.getAsString())) { + conditionBuilder.setMetricKey(metric.getAsString()); + } + + JsonElement op = jsonCondition.get("op"); + if (op != null && !isNullOrEmpty(op.getAsString())) { + String stringOp = op.getAsString(); + ProjectStatusWsResponse.Comparator comparator = measureOpToQualityGateComparator(stringOp); + conditionBuilder.setComparator(comparator); + } + + 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()); + } + + JsonElement error = jsonCondition.get("error"); + if (error != null && !isNullOrEmpty(error.getAsString())) { + conditionBuilder.setErrorThreshold(error.getAsString()); + } + + JsonElement actual = jsonCondition.get("actual"); + if (actual != null && !isNullOrEmpty(actual.getAsString())) { + conditionBuilder.setActualValue(actual.getAsString()); + } + + projectStatusBuilder.addConditions(conditionBuilder); + } + + private static ProjectStatusWsResponse.Status measureLevelToQualityGateStatus(String measureLevel) { + for (ProjectStatusWsResponse.Status status : ProjectStatusWsResponse.Status.values()) { + if (status.name().equals(measureLevel)) { + return status; + } + } + + throw new IllegalStateException(String.format("Unknown quality gate status '%s'", measureLevel)); + } + + private static ProjectStatusWsResponse.Comparator measureOpToQualityGateComparator(String measureOp) { + for (ProjectStatusWsResponse.Comparator comparator : ProjectStatusWsResponse.Comparator.values()) { + if (comparator.name().equals(measureOp)) { + return comparator; + } + } + + throw new IllegalStateException(String.format("Unknown quality gate comparator '%s'", measureOp)); + } + + private static ProjectStatusWsResponse.ProjectStatus newResponseWithoutQualityGateDetails() { + return ProjectStatusWsResponse.ProjectStatus.newBuilder().setStatus(ProjectStatusWsResponse.Status.NONE).build(); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java b/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java index e8e6c831c4b..99b43978e7d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java @@ -24,6 +24,7 @@ import com.google.common.base.Strings; import com.google.common.collect.HashMultimap; import com.google.common.collect.Sets; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -140,6 +141,18 @@ public abstract class AbstractUserSession impleme return this; } + @Override + public UserSession checkAnyGlobalPermissions(Collection globalPermissionsToTest) { + List userGlobalPermissions = globalPermissions(); + for (String userGlobalPermission : userGlobalPermissions) { + if (globalPermissionsToTest.contains(userGlobalPermission)) { + return this; + } + } + + throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE); + } + @Override public boolean hasGlobalPermission(String globalPermission) { return globalPermissions().contains(globalPermission); diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java b/server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java index eb03462876e..62011a7a252 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java @@ -20,6 +20,7 @@ package org.sonar.server.user; import com.google.common.base.Objects; +import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.Set; @@ -97,6 +98,11 @@ public class ThreadLocalUserSession implements UserSession { return get().checkGlobalPermission(globalPermission, errorMessage); } + @Override + public UserSession checkAnyGlobalPermissions(Collection globalPermissions) { + return get().checkAnyGlobalPermissions(globalPermissions); + } + @Override public boolean hasGlobalPermission(String globalPermission) { return get().hasGlobalPermission(globalPermission); diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java b/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java index 64a7b3f6d0e..001c26d75c3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java @@ -19,6 +19,7 @@ */ package org.sonar.server.user; +import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.Set; @@ -57,6 +58,12 @@ public interface UserSession { */ UserSession checkGlobalPermission(String globalPermission, @Nullable String errorMessage); + /** + * Ensures that user implies any of the specified global permissions, otherwise throws a {@link org.sonar.server.exceptions.ForbiddenException} with + * the specified error message. + */ + UserSession checkAnyGlobalPermissions(Collection globalPermissions); + /** * Does the user have the given permission ? */ diff --git a/server/sonar-server/src/main/resources/org/sonar/server/computation/ws/activity-example.json b/server/sonar-server/src/main/resources/org/sonar/server/computation/ws/activity-example.json index de5018a0661..93d300ba1d7 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/computation/ws/activity-example.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/computation/ws/activity-example.json @@ -12,6 +12,7 @@ "componentKey": "project_1", "componentName": "Project One", "componentQualifier": "TRK", + "analysisId": "123456", "status": "SUCCESS", "submittedAt": "2015-08-13T23:34:59+0200", "submitterLogin": "john", diff --git a/server/sonar-server/src/main/resources/org/sonar/server/computation/ws/component-example.json b/server/sonar-server/src/main/resources/org/sonar/server/computation/ws/component-example.json index 93ead161166..6adb3f0d7aa 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/computation/ws/component-example.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/computation/ws/component-example.json @@ -19,6 +19,7 @@ "componentKey": "com.github.kevinsawicki:http-request-parent", "componentName": "HttpRequest", "componentQualifier": "TRK", + "analysisId": "123456", "status": "SUCCESS", "submittedAt": "2015-09-21T19:25:49+0200", "startedAt": "2015-09-21T19:25:57+0200", diff --git a/server/sonar-server/src/main/resources/org/sonar/server/computation/ws/task-example.json b/server/sonar-server/src/main/resources/org/sonar/server/computation/ws/task-example.json index 5f02d9072bd..089e277fb62 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/computation/ws/task-example.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/computation/ws/task-example.json @@ -6,6 +6,7 @@ "componentKey": "project_1", "componentName": "Project One", "componentQualifier": "TRK", + "analysisId": "123456", "status": "SUCCESS", "submittedAt": "2015-10-02T11:32:15+0200", "startedAt": "2015-10-02T11:32:16+0200", diff --git a/server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/project_status-example.json b/server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/project_status-example.json new file mode 100644 index 00000000000..cf46493da0f --- /dev/null +++ b/server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/project_status-example.json @@ -0,0 +1,85 @@ +{ + "projectStatus": { + "status": "ERROR", + "conditions": [ + { + "status": "ERROR", + "metricKey": "new_coverage", + "comparator": "LT", + "periodIndex": 1, + "errorThreshold": "85", + "actualValue": "82.50562381034781" + }, + { + "status": "ERROR", + "metricKey": "new_blocker_violations", + "comparator": "GT", + "periodIndex": 1, + "errorThreshold": "0", + "actualValue": "14" + }, + { + "status": "ERROR", + "metricKey": "new_critical_violations", + "comparator": "GT", + "periodIndex": 1, + "errorThreshold": "0", + "actualValue": "1" + }, + { + "status": "OK", + "metricKey": "new_sqale_debt_ratio", + "comparator": "GT", + "periodIndex": 2, + "errorThreshold": "5", + "actualValue": "0.6562109862671661" + }, + { + "status": "OK", + "metricKey": "reopened_issues", + "comparator": "GT", + "periodIndex": 3, + "warningThreshold": "0", + "actualValue": "0" + }, + { + "status": "WARN", + "metricKey": "open_issues", + "comparator": "GT", + "periodIndex": 3, + "warningThreshold": "0", + "actualValue": "17" + }, + { + "status": "OK", + "metricKey": "skipped_tests", + "comparator": "GT", + "periodIndex": 5, + "warningThreshold": "0", + "actualValue": "0" + } + ], + "periods": [ + { + "index": 1, + "mode": "last_period", + "date": "2000-04-27T00:45:23+0200" + }, + { + "index": 2, + "mode": "last_version", + "date": "2000-04-27T00:45:23+0200", + "parameter": "2015-12-07" + }, + { + "index": 3, + "mode": "last_analysis" + }, + { + "index": 5, + "mode": "last_30_days", + "parameter": "2015-11-07" + } + ] + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/ActivityActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/ActivityActionTest.java index 6a0d284415d..a3f97031f12 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/ActivityActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/ActivityActionTest.java @@ -95,6 +95,7 @@ public class ActivityActionTest { assertThat(activityResponse.getTasks(0).getId()).isEqualTo("T2"); assertThat(activityResponse.getTasks(0).getStatus()).isEqualTo(WsCe.TaskStatus.FAILED); assertThat(activityResponse.getTasks(0).getComponentId()).isEqualTo("PROJECT_2"); + assertThat(activityResponse.getTasks(0).getAnalysisId()).isEqualTo("123456"); assertThat(activityResponse.getTasks(0).getExecutionTimeMs()).isEqualTo(500L); assertThat(activityResponse.getTasks(0).getLogs()).isFalse(); assertThat(activityResponse.getTasks(1).getId()).isEqualTo("T1"); @@ -237,6 +238,7 @@ public class ActivityActionTest { CeActivityDto activityDto = new CeActivityDto(queueDto); activityDto.setStatus(status); activityDto.setExecutionTimeMs(500L); + activityDto.setSnapshotId(123_456L); dbTester.getDbClient().ceActivityDao().insert(dbTester.getSession(), activityDto); dbTester.commit(); return activityDto; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/ComponentActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/ComponentActionTest.java index 15c2dffa707..73c397f8b4b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/ComponentActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/ComponentActionTest.java @@ -71,7 +71,7 @@ public class ComponentActionTest { .setMediaType(MediaTypes.PROTOBUF) .execute(); - WsCe.ProjectResponse response = Protobuf.read(wsResponse.getInputStream(), WsCe.ProjectResponse.PARSER); + WsCe.ProjectResponse response = Protobuf.read(wsResponse.getInputStream(), WsCe.ProjectResponse.parser()); assertThat(response.getQueueCount()).isEqualTo(0); assertThat(response.hasCurrent()).isFalse(); } @@ -90,7 +90,7 @@ public class ComponentActionTest { .setMediaType(MediaTypes.PROTOBUF) .execute(); - WsCe.ProjectResponse response = Protobuf.read(wsResponse.getInputStream(), WsCe.ProjectResponse.PARSER); + WsCe.ProjectResponse response = Protobuf.read(wsResponse.getInputStream(), WsCe.ProjectResponse.parser()); assertThat(response.getQueueCount()).isEqualTo(2); assertThat(response.getQueue(0).getId()).isEqualTo("T4"); assertThat(response.getQueue(1).getId()).isEqualTo("T5"); @@ -113,7 +113,7 @@ public class ComponentActionTest { .setMediaType(MediaTypes.PROTOBUF) .execute(); - WsCe.ProjectResponse response = Protobuf.read(wsResponse.getInputStream(), WsCe.ProjectResponse.PARSER); + WsCe.ProjectResponse response = Protobuf.read(wsResponse.getInputStream(), WsCe.ProjectResponse.parser()); assertThat(response.getQueueCount()).isEqualTo(0); // T3 is the latest task executed on PROJECT_1 ignoring Canceled ones assertThat(response.hasCurrent()).isTrue(); @@ -139,6 +139,7 @@ public class ComponentActionTest { CeActivityDto activityDto = new CeActivityDto(queueDto); activityDto.setStatus(status); activityDto.setExecutionTimeMs(500L); + activityDto.setSnapshotId(123_456L); dbTester.getDbClient().ceActivityDao().insert(dbTester.getSession(), activityDto); dbTester.getSession().commit(); return activityDto; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/TaskActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/TaskActionTest.java index a67dcbd65df..4bb1ee8da09 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/TaskActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/TaskActionTest.java @@ -111,6 +111,7 @@ public class TaskActionTest { CeActivityDto activityDto = new CeActivityDto(queueDto); activityDto.setStatus(CeActivityDto.Status.FAILED); activityDto.setExecutionTimeMs(500L); + activityDto.setSnapshotId(123_456L); dbTester.getDbClient().ceActivityDao().insert(dbTester.getSession(), activityDto); dbTester.commit(); @@ -120,13 +121,15 @@ public class TaskActionTest { .execute(); WsCe.TaskResponse taskResponse = Protobuf.read(wsResponse.getInputStream(), WsCe.TaskResponse.PARSER); - assertThat(taskResponse.getTask().getId()).isEqualTo("TASK_1"); - assertThat(taskResponse.getTask().getStatus()).isEqualTo(WsCe.TaskStatus.FAILED); - assertThat(taskResponse.getTask().getComponentId()).isEqualTo(project.uuid()); - assertThat(taskResponse.getTask().getComponentKey()).isEqualTo(project.key()); - assertThat(taskResponse.getTask().getComponentName()).isEqualTo(project.name()); - assertThat(taskResponse.getTask().getExecutionTimeMs()).isEqualTo(500L); - assertThat(taskResponse.getTask().getLogs()).isFalse(); + WsCe.Task task = taskResponse.getTask(); + assertThat(task.getId()).isEqualTo("TASK_1"); + assertThat(task.getStatus()).isEqualTo(WsCe.TaskStatus.FAILED); + assertThat(task.getComponentId()).isEqualTo(project.uuid()); + assertThat(task.getComponentKey()).isEqualTo(project.key()); + assertThat(task.getComponentName()).isEqualTo(project.name()); + assertThat(task.getAnalysisId()).isEqualTo("123456"); + assertThat(task.getExecutionTimeMs()).isEqualTo(500L); + assertThat(task.getLogs()).isFalse(); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/TaskFormatterTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/TaskFormatterTest.java index cca32b55a95..97465abc6fd 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/TaskFormatterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/TaskFormatterTest.java @@ -30,7 +30,6 @@ import org.mockito.Mockito; import org.sonar.api.resources.Qualifiers; import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.System2; -import org.sonar.api.utils.internal.TestSystem2; import org.sonar.db.DbTester; import org.sonar.db.ce.CeActivityDto; import org.sonar.db.ce.CeQueueDto; @@ -177,6 +176,7 @@ public class TaskFormatterTest { assertThat(wsTask.getLogs()).isFalse(); assertThat(wsTask.getSubmittedAt()).isEqualTo(DateUtils.formatDateTime(new Date(1_450_000_000_000L))); assertThat(wsTask.getExecutionTimeMs()).isEqualTo(500L); + assertThat(wsTask.getAnalysisId()).isEqualTo("123456"); assertThat(wsTask.getLogs()).isFalse(); } @@ -209,6 +209,7 @@ public class TaskFormatterTest { CeActivityDto activityDto = new CeActivityDto(queueDto); activityDto.setStatus(status); activityDto.setExecutionTimeMs(500L); + activityDto.setSnapshotId(123_456L); return activityDto; } } 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 new file mode 100644 index 00000000000..a11313e030d --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ProjectStatusActionTest.java @@ -0,0 +1,167 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.qualitygate.ws; + +import com.google.common.base.Throwables; +import java.io.IOException; +import org.apache.commons.io.IOUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.ExpectedException; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.utils.System2; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.component.SnapshotDto; +import org.sonar.db.measure.MeasureDto; +import org.sonar.db.metric.MetricDto; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.ws.WsActionTester; +import org.sonar.test.DbTests; +import org.sonarqube.ws.MediaTypes; +import org.sonarqube.ws.WsQualityGates.ProjectStatusWsResponse; +import org.sonarqube.ws.WsQualityGates.ProjectStatusWsResponse.Status; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.db.component.ComponentTesting.newProjectDto; +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; + +@Category(DbTests.class) +public class ProjectStatusActionTest { + private static final String ANALYSIS_ID = "task-uuid"; + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + @Rule + public DbTester db = DbTester.create(System2.INSTANCE); + + WsActionTester ws; + DbClient dbClient; + DbSession dbSession; + + @Before + public void setUp() { + dbClient = db.getDbClient(); + dbSession = db.getSession(); + + ws = new WsActionTester(new ProjectStatusAction(dbClient, userSession)); + userSession.setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN); + } + + @Test + public void json_example() throws IOException { + ComponentDto project = newProjectDto("project-uuid"); + dbClient.componentDao().insert(dbSession, 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(); + + String response = ws.newRequest() + .setParam("analysisId", snapshot.getId().toString()) + .execute().getInput(); + + assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json")); + } + + @Test + public void fail_if_no_snapshot_id_found() { + 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() { + ComponentDto project = newProjectDto("project-uuid"); + dbClient.componentDao().insert(dbSession, project); + SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newSnapshotForProject(project)); + dbSession.commit(); + + ProjectStatusWsResponse result = newRequest(snapshot.getId().toString()); + + assertThat(result.getProjectStatus().getStatus()).isEqualTo(Status.NONE); + assertThat(result.getProjectStatus().getConditionsCount()).isEqualTo(0); + } + + @Test + public void return_undefined_status_if_measure_data_is_not_well_formatted() { + userSession.setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); + ComponentDto project = newProjectDto("project-uuid"); + dbClient.componentDao().insert(dbSession, project); + SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, newSnapshotForProject(project)); + 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); + dbSession.commit(); + + ProjectStatusWsResponse result = newRequest(String.valueOf(snapshot.getId())); + + assertThat(result.getProjectStatus().getStatus()).isEqualTo(Status.NONE); + assertThat(result.getProjectStatus().getConditionsCount()).isEqualTo(0); + } + + @Test + public void fail_if_insufficient_privileges() { + userSession.setGlobalPermissions(GlobalPermissions.PREVIEW_EXECUTION); + expectedException.expect(ForbiddenException.class); + + newRequest(ANALYSIS_ID); + } + + private ProjectStatusWsResponse newRequest(String taskId) { + try { + return ProjectStatusWsResponse.parseFrom( + ws.newRequest() + .setParam("analysisId", taskId) + .setMediaType(MediaTypes.PROTOBUF) + .execute().getInputStream()); + } catch (IOException e) { + throw Throwables.propagate(e); + } + } +} 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 new file mode 100644 index 00000000000..59488690f8a --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest.java @@ -0,0 +1,147 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.List; +import org.apache.commons.io.IOUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.db.component.SnapshotDto; +import org.sonarqube.ws.WsQualityGates.ProjectStatusWsResponse; +import org.sonarqube.ws.WsQualityGates.ProjectStatusWsResponse.ProjectStatus; + +import static org.assertj.core.api.Assertions.assertThat; + +public class QualityGateDetailsFormatterTest { + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + QualityGateDetailsFormatter underTest; + + @Test + public void map_level_conditions_and_periods() throws IOException { + String measureData = IOUtils.toString(getClass().getResource("QualityGateDetailsFormatterTest/quality_gate_details.json")); + SnapshotDto snapshot = new SnapshotDto() + .setPeriodMode(1, "last_period") + .setPeriodDate(1, 1449490731764L) + .setPeriodMode(2, "last_version") + .setPeriodParam(2, "2015-12-07") + .setPeriodDate(2, 1449404331764L) + .setPeriodMode(3, "last_analysis") + .setPeriodMode(5, "last_30_days") + .setPeriodParam(5, "2015-11-07"); + underTest = new QualityGateDetailsFormatter(measureData, snapshot); + + ProjectStatus result = underTest.format(); + + assertThat(result.getStatus()).isEqualTo(ProjectStatusWsResponse.Status.ERROR); + // check conditions + assertThat(result.getConditionsCount()).isEqualTo(5); + List conditions = result.getConditionsList(); + assertThat(conditions).extracting("status").containsExactly( + ProjectStatusWsResponse.Status.ERROR, + ProjectStatusWsResponse.Status.ERROR, + ProjectStatusWsResponse.Status.OK, + ProjectStatusWsResponse.Status.OK, + ProjectStatusWsResponse.Status.WARN); + assertThat(conditions).extracting("metricKey").containsExactly("new_coverage", "new_blocker_violations", "new_critical_violations", "new_sqale_debt_ratio", + "new_sqale_debt_ratio"); + assertThat(conditions).extracting("comparator").containsExactly( + ProjectStatusWsResponse.Comparator.LT, + ProjectStatusWsResponse.Comparator.GT, + ProjectStatusWsResponse.Comparator.NE, + ProjectStatusWsResponse.Comparator.EQ, + ProjectStatusWsResponse.Comparator.LT); + assertThat(conditions).extracting("periodIndex").containsExactly(1, 2, 3, 4, 5); + assertThat(conditions).extracting("warningThreshold").containsOnly("80", ""); + assertThat(conditions).extracting("errorThreshold").containsOnly("85", "0", "5"); + assertThat(conditions).extracting("actualValue").containsExactly("82.2985024398452", "1", "0", "0.5670339761248853", "0.5670339761248853"); + + // check periods + assertThat(result.getPeriodsCount()).isEqualTo(4); + List periods = result.getPeriodsList(); + 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" + + " \"level\": \"UNKNOWN\",\n" + + " \"conditions\": [\n" + + " {\n" + + " \"metric\": \"new_coverage\",\n" + + " \"op\": \"LT\",\n" + + " \"period\": 1,\n" + + " \"warning\": \"80\",\n" + + " \"error\": \"85\",\n" + + " \"actual\": \"82.2985024398452\",\n" + + " \"level\": \"ERROR\"\n" + + " }\n" + + " ]\n" + + "}"; + underTest = new QualityGateDetailsFormatter(measureData, new SnapshotDto()); + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Unknown quality gate status 'UNKNOWN'"); + + underTest.format(); + } + + @Test + public void fail_when_measure_op_is_unknown() { + String measureData = "{\n" + + " \"level\": \"ERROR\",\n" + + " \"conditions\": [\n" + + " {\n" + + " \"metric\": \"new_coverage\",\n" + + " \"op\": \"UNKNOWN\",\n" + + " \"period\": 1,\n" + + " \"warning\": \"80\",\n" + + " \"error\": \"85\",\n" + + " \"actual\": \"82.2985024398452\",\n" + + " \"level\": \"ERROR\"\n" + + " }\n" + + " ]\n" + + "}"; + underTest = new QualityGateDetailsFormatter(measureData, new SnapshotDto()); + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Unknown quality gate comparator 'UNKNOWN'"); + + underTest.format(); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/tester/UserSessionRule.java b/server/sonar-server/src/test/java/org/sonar/server/tester/UserSessionRule.java index 4a35ad5473e..9cac2c7efcc 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/tester/UserSessionRule.java +++ b/server/sonar-server/src/test/java/org/sonar/server/tester/UserSessionRule.java @@ -20,6 +20,7 @@ package org.sonar.server.tester; import com.google.common.base.Preconditions; +import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.Set; @@ -288,6 +289,11 @@ public class UserSessionRule implements TestRule, UserSession { return currentUserSession.checkGlobalPermission(globalPermission, errorMessage); } + @Override + public UserSession checkAnyGlobalPermissions(Collection globalPermissions) { + return currentUserSession.checkAnyGlobalPermissions(globalPermissions); + } + @Override public boolean hasGlobalPermission(String globalPermission) { return currentUserSession.hasGlobalPermission(globalPermission); diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualitygate/ws/ProjectStatusActionTest/measure_data.json b/server/sonar-server/src/test/resources/org/sonar/server/qualitygate/ws/ProjectStatusActionTest/measure_data.json new file mode 100644 index 00000000000..679e0795c0e --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/qualitygate/ws/ProjectStatusActionTest/measure_data.json @@ -0,0 +1,66 @@ +{ + "level": "ERROR", + "conditions": [ + { + "metric": "new_coverage", + "op": "LT", + "period": 1, + "error": "85", + "actual": "82.50562381034781", + "level": "ERROR" + }, + { + "metric": "new_blocker_violations", + "op": "GT", + "period": 1, + "warning": "", + "error": "0", + "actual": "14", + "level": "ERROR" + }, + { + "metric": "new_critical_violations", + "op": "GT", + "period": 1, + "warning": "", + "error": "0", + "actual": "1", + "level": "ERROR" + }, + { + "metric": "new_sqale_debt_ratio", + "op": "GT", + "period": 2, + "warning": "", + "error": "5", + "actual": "0.6562109862671661", + "level": "OK" + }, + { + "metric": "reopened_issues", + "op": "GT", + "period": 3, + "warning": "0", + "actual": "0", + "level": "OK" + }, + { + "metric": "open_issues", + "op": "GT", + "period": 3, + "warning": "0", + "error": "", + "actual": "17", + "level": "WARN" + }, + { + "metric": "skipped_tests", + "op": "GT", + "period": 5, + "warning": "0", + "error": "", + "actual": "0", + "level": "OK" + } + ] +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest/quality_gate_details.json b/server/sonar-server/src/test/resources/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest/quality_gate_details.json new file mode 100644 index 00000000000..4122aa76dd2 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest/quality_gate_details.json @@ -0,0 +1,50 @@ +{ + "level": "ERROR", + "conditions": [ + { + "metric": "new_coverage", + "op": "LT", + "period": 1, + "warning": "80", + "error": "85", + "actual": "82.2985024398452", + "level": "ERROR" + }, + { + "metric": "new_blocker_violations", + "op": "GT", + "period": 2, + "warning": "", + "error": "0", + "actual": "1", + "level": "ERROR" + }, + { + "metric": "new_critical_violations", + "op": "NE", + "period": 3, + "warning": "", + "error": "0", + "actual": "0", + "level": "OK" + }, + { + "metric": "new_sqale_debt_ratio", + "op": "EQ", + "period": 4, + "warning": "", + "error": "5", + "actual": "0.5670339761248853", + "level": "OK" + }, + { + "metric": "new_sqale_debt_ratio", + "op": "LT", + "period": 5, + "warning": "", + "error": "5", + "actual": "0.5670339761248853", + "level": "WARN" + } + ] +} diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1004_add_ce_activity_snapshot_id.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1004_add_ce_activity_snapshot_id.rb index b2e67caa934..9f41f740617 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1004_add_ce_activity_snapshot_id.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1004_add_ce_activity_snapshot_id.rb @@ -25,7 +25,7 @@ class AddCeActivitySnapshotId < ActiveRecord::Migration def self.up - add_column 'ce_activity', :snapshot_id, :integer, :null => true + add_column 'ce_activity', :snapshot_id, :big_integer, :null => true end end diff --git a/sonar-db/src/main/java/org/sonar/db/ce/CeActivityDto.java b/sonar-db/src/main/java/org/sonar/db/ce/CeActivityDto.java index db945aa9986..4119a12d4d9 100644 --- a/sonar-db/src/main/java/org/sonar/db/ce/CeActivityDto.java +++ b/sonar-db/src/main/java/org/sonar/db/ce/CeActivityDto.java @@ -66,17 +66,19 @@ public class CeActivityDto { return uuid; } - public void setUuid(String s) { + public CeActivityDto setUuid(String s) { checkArgument(s.length() <= 40, "Value is too long for column CE_ACTIVITY.UUID: %s", s); this.uuid = s; + return this; } public String getTaskType() { return taskType; } - public void setTaskType(String s) { + public CeActivityDto setTaskType(String s) { this.taskType = s; + return this; } @CheckForNull @@ -84,25 +86,28 @@ public class CeActivityDto { return componentUuid; } - public void setComponentUuid(@Nullable String s) { + public CeActivityDto setComponentUuid(@Nullable String s) { checkArgument(s == null || s.length() <= 40, "Value is too long for column CE_ACTIVITY.COMPONENT_UUID: %s", s); this.componentUuid = s; + return this; } public Status getStatus() { return status; } - public void setStatus(Status s) { + public CeActivityDto setStatus(Status s) { this.status = s; + return this; } public boolean getIsLast() { return isLast; } - void setIsLast(boolean b) { + CeActivityDto setIsLast(boolean b) { this.isLast = b; + return this; } public String getIsLastKey() { @@ -118,8 +123,9 @@ public class CeActivityDto { return submittedAt; } - public void setSubmittedAt(long submittedAt) { + public CeActivityDto setSubmittedAt(long submittedAt) { this.submittedAt = submittedAt; + return this; } @CheckForNull @@ -127,8 +133,9 @@ public class CeActivityDto { return startedAt; } - public void setStartedAt(@Nullable Long l) { + public CeActivityDto setStartedAt(@Nullable Long l) { this.startedAt = l; + return this; } @CheckForNull @@ -136,24 +143,27 @@ public class CeActivityDto { return executedAt; } - public void setExecutedAt(@Nullable Long l) { + public CeActivityDto setExecutedAt(@Nullable Long l) { this.executedAt = l; + return this; } public long getCreatedAt() { return createdAt; } - public void setCreatedAt(long l) { + public CeActivityDto setCreatedAt(long l) { this.createdAt = l; + return this; } public long getUpdatedAt() { return updatedAt; } - public void setUpdatedAt(long l) { + public CeActivityDto setUpdatedAt(long l) { this.updatedAt = l; + return this; } @CheckForNull @@ -161,9 +171,10 @@ public class CeActivityDto { return executionTimeMs; } - public void setExecutionTimeMs(@Nullable Long l) { + public CeActivityDto setExecutionTimeMs(@Nullable Long l) { checkArgument(l == null || l >= 0, "Execution time must be positive: %s", l); this.executionTimeMs = l; + return this; } @CheckForNull diff --git a/sonar-db/src/main/java/org/sonar/db/ce/CeQueueDto.java b/sonar-db/src/main/java/org/sonar/db/ce/CeQueueDto.java index 3031029c0bd..524a93bd713 100644 --- a/sonar-db/src/main/java/org/sonar/db/ce/CeQueueDto.java +++ b/sonar-db/src/main/java/org/sonar/db/ce/CeQueueDto.java @@ -44,9 +44,10 @@ public class CeQueueDto { return uuid; } - public void setUuid(String s) { + public CeQueueDto setUuid(String s) { checkArgument(s.length() <= 40, "Value of UUID is too long: %s", s); this.uuid = s; + return this; } @CheckForNull @@ -54,26 +55,29 @@ public class CeQueueDto { return componentUuid; } - public void setComponentUuid(@Nullable String s) { + public CeQueueDto setComponentUuid(@Nullable String s) { checkArgument(s == null || s.length() <= 40, "Value of component UUID is too long: %s", s); this.componentUuid = s; + return this; } public Status getStatus() { return status; } - public void setStatus(Status s) { + public CeQueueDto setStatus(Status s) { this.status = s; + return this; } public String getTaskType() { return taskType; } - public void setTaskType(String s) { + public CeQueueDto setTaskType(String s) { checkArgument(s.length() <= 15, "Value of task type is too long: %s", s); this.taskType = s; + return this; } @CheckForNull @@ -81,9 +85,10 @@ public class CeQueueDto { return submitterLogin; } - public void setSubmitterLogin(@Nullable String s) { + public CeQueueDto setSubmitterLogin(@Nullable String s) { checkArgument(s == null || s.length() <= 255, "Value of submitter login is too long: %s", s); this.submitterLogin = s; + return this; } @CheckForNull @@ -91,24 +96,27 @@ public class CeQueueDto { return startedAt; } - public void setStartedAt(@Nullable Long l) { + public CeQueueDto setStartedAt(@Nullable Long l) { this.startedAt = l; + return this; } public long getCreatedAt() { return createdAt; } - public void setCreatedAt(long l) { + public CeQueueDto setCreatedAt(long l) { this.createdAt = l; + return this; } public long getUpdatedAt() { return updatedAt; } - public void setUpdatedAt(long l) { + public CeQueueDto setUpdatedAt(long l) { this.updatedAt = l; + return this; } @Override diff --git a/sonar-db/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml b/sonar-db/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml index 1050b096954..c19c97a0800 100644 --- a/sonar-db/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml @@ -95,7 +95,7 @@ values ( #{uuid,jdbcType=VARCHAR}, #{componentUuid,jdbcType=VARCHAR}, - #{snapshotId,jdbcType=INTEGER}, + #{snapshotId,jdbcType=BIGINT}, #{status,jdbcType=VARCHAR}, #{taskType,jdbcType=VARCHAR}, #{isLast,jdbcType=BOOLEAN}, diff --git a/sonar-db/src/test/java/org/sonar/db/ce/CeQueueTesting.java b/sonar-db/src/test/java/org/sonar/db/ce/CeQueueTesting.java new file mode 100644 index 00000000000..bb46b3e6923 --- /dev/null +++ b/sonar-db/src/test/java/org/sonar/db/ce/CeQueueTesting.java @@ -0,0 +1,42 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.db.ce; + +import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; +import static org.apache.commons.lang.math.RandomUtils.nextLong; + +public class CeQueueTesting { + private CeQueueTesting() { + // static methods only + } + + public static CeQueueDto newCeQueueDto(String uuid) { + return new CeQueueDto() + .setUuid(uuid) + .setComponentUuid(randomAlphanumeric(40)) + .setStatus(CeQueueDto.Status.PENDING) + .setTaskType(CeTaskTypes.REPORT) + .setSubmitterLogin(randomAlphanumeric(255)) + .setCreatedAt(nextLong()) + .setUpdatedAt(nextLong()) + .setStartedAt(nextLong()); + } +} diff --git a/sonar-db/src/test/java/org/sonar/db/measure/MeasureTesting.java b/sonar-db/src/test/java/org/sonar/db/measure/MeasureTesting.java new file mode 100644 index 00000000000..229d487c2d8 --- /dev/null +++ b/sonar-db/src/test/java/org/sonar/db/measure/MeasureTesting.java @@ -0,0 +1,40 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.db.measure; + +import org.sonar.db.metric.MetricDto; + +import static org.apache.commons.lang.math.RandomUtils.nextInt; + +public class MeasureTesting { + private MeasureTesting() { + // static methods only + } + + public static MeasureDto newMeasureDto(MetricDto metricDto, long snapshotId) { + return new MeasureDto() + .setMetricId(metricDto.getId()) + .setMetricKey(metricDto.getKey()) + .setSnapshotId((long) nextInt()) + .setComponentId((long) nextInt()) + .setSnapshotId(snapshotId); + } +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/HttpWsClient.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/HttpWsClient.java index d172f4bbf88..0435aed90a8 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/HttpWsClient.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/HttpWsClient.java @@ -23,6 +23,7 @@ package org.sonarqube.ws.client; import org.sonarqube.ws.client.component.ComponentsService; import org.sonarqube.ws.client.issue.IssuesService; import org.sonarqube.ws.client.permission.PermissionsService; +import org.sonarqube.ws.client.qualitygate.QualityGatesService; import org.sonarqube.ws.client.qualityprofile.QualityProfilesService; import org.sonarqube.ws.client.usertoken.UserTokensService; @@ -38,6 +39,7 @@ public class HttpWsClient implements WsClient { private final QualityProfilesService qualityProfilesService; private final IssuesService issuesService; private final UserTokensService userTokensService; + private final QualityGatesService qualityGatesService; private final WsConnector wsConnector; public HttpWsClient(WsConnector wsConnector) { @@ -47,6 +49,7 @@ public class HttpWsClient implements WsClient { this.qualityProfilesService = new QualityProfilesService(wsConnector); this.issuesService = new IssuesService(wsConnector); this.userTokensService = new UserTokensService(wsConnector); + this.qualityGatesService = new QualityGatesService(wsConnector); } @Override @@ -78,4 +81,9 @@ public class HttpWsClient implements WsClient { public UserTokensService userTokens() { return userTokensService; } + + @Override + public QualityGatesService qualityGates() { + return qualityGatesService; + } } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java index 3f980d7c591..67e088c46be 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java @@ -22,6 +22,7 @@ package org.sonarqube.ws.client; import org.sonarqube.ws.client.component.ComponentsService; import org.sonarqube.ws.client.issue.IssuesService; import org.sonarqube.ws.client.permission.PermissionsService; +import org.sonarqube.ws.client.qualitygate.QualityGatesService; import org.sonarqube.ws.client.qualityprofile.QualityProfilesService; import org.sonarqube.ws.client.usertoken.UserTokensService; @@ -39,5 +40,7 @@ public interface WsClient { UserTokensService userTokens(); + QualityGatesService qualityGates(); + WsConnector wsConnector(); } 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 new file mode 100644 index 00000000000..b9d435ff065 --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/ProjectStatusWsRequest.java @@ -0,0 +1,34 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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 ProjectStatusWsRequest { + private String taskId; + + public String getAnalysisId() { + return taskId; + } + + public ProjectStatusWsRequest setAnalysisId(String taskId) { + this.taskId = taskId; + 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 new file mode 100644 index 00000000000..404619e1814 --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/QualityGatesService.java @@ -0,0 +1,39 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.sonarqube.ws.WsQualityGates.ProjectStatusWsResponse; +import org.sonarqube.ws.client.BaseService; +import org.sonarqube.ws.client.GetRequest; +import org.sonarqube.ws.client.WsConnector; + +public class QualityGatesService extends BaseService { + + public QualityGatesService(WsConnector wsConnector) { + super(wsConnector, "api/qualitygates"); + } + + public ProjectStatusWsResponse projectStatus(ProjectStatusWsRequest request) { + return call(new GetRequest(path("project_status")) + .setParam("analysisId", request.getAnalysisId()), + ProjectStatusWsResponse.parser()); + } +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/package-info.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/package-info.java new file mode 100644 index 00000000000..084636cef47 --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/package-info.java @@ -0,0 +1,26 @@ + +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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. + */ + +@ParametersAreNonnullByDefault +package org.sonarqube.ws.client.qualitygate; + +import javax.annotation.ParametersAreNonnullByDefault; + diff --git a/sonar-ws/src/main/protobuf/ws-ce.proto b/sonar-ws/src/main/protobuf/ws-ce.proto index b6d930a8f02..9c27b02f4c9 100644 --- a/sonar-ws/src/main/protobuf/ws-ce.proto +++ b/sonar-ws/src/main/protobuf/ws-ce.proto @@ -61,14 +61,15 @@ message Task { optional string componentKey = 4; optional string componentName = 5; optional string componentQualifier = 6; - optional TaskStatus status = 7; - optional string submittedAt = 8; - optional string submitterLogin = 9; - optional string startedAt = 10; - optional string executedAt = 11; - optional bool isLastExecuted = 12; - optional int64 executionTimeMs = 13; - optional bool logs = 14; + optional string analysisId = 7; + optional TaskStatus status = 8; + optional string submittedAt = 9; + optional string submitterLogin = 10; + optional string startedAt = 11; + optional string executedAt = 12; + optional bool isLastExecuted = 13; + optional int64 executionTimeMs = 14; + optional bool logs = 15; } enum TaskStatus { diff --git a/sonar-ws/src/main/protobuf/ws-qualitygates.proto b/sonar-ws/src/main/protobuf/ws-qualitygates.proto new file mode 100644 index 00000000000..f4669419d86 --- /dev/null +++ b/sonar-ws/src/main/protobuf/ws-qualitygates.proto @@ -0,0 +1,69 @@ +// SonarQube, open source software quality management tool. +// Copyright (C) 2008-2015 SonarSource +// mailto:contact AT sonarsource DOT com +// +// SonarQube 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. +// +// SonarQube 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. + +syntax = "proto2"; + +package sonarqube.ws.qualitygate; + +import "ws-commons.proto"; + +option java_package = "org.sonarqube.ws"; +option java_outer_classname = "WsQualityGates"; +option optimize_for = SPEED; + +// GET api/qualitygates/project_status +message ProjectStatusWsResponse { + optional ProjectStatus projectStatus = 1; + + message ProjectStatus { + optional Status status = 1; + repeated Condition conditions = 2; + repeated Period periods = 3; + } + + message Condition { + optional Status status = 1; + optional string metricKey = 2; + optional Comparator comparator = 3; + optional int32 periodIndex = 4; + optional string warningThreshold = 5; + optional string errorThreshold = 6; + optional string actualValue = 7; + } + + message Period { + optional int32 index = 1; + optional string mode = 2; + optional string date = 3; + optional string parameter = 4; + } + + enum Status { + OK = 1; + WARN = 2; + ERROR = 3; + NONE = 4; + } + + enum Comparator { + GT = 1; + LT = 2; + EQ = 3; + NE = 4; + } +} -- 2.39.5