]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6900 New WS api/qualitygates/project_status quality gate details of an analysis 680/head
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Fri, 4 Dec 2015 11:09:25 +0000 (12:09 +0100)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Mon, 7 Dec 2015 23:27:39 +0000 (00:27 +0100)
38 files changed:
server/sonar-server/src/main/java/org/sonar/server/component/ws/ResourcesWs.java
server/sonar-server/src/main/java/org/sonar/server/computation/ws/ActivityAction.java
server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsModule.java
server/sonar-server/src/main/java/org/sonar/server/computation/ws/ComponentAction.java
server/sonar-server/src/main/java/org/sonar/server/computation/ws/TaskAction.java
server/sonar-server/src/main/java/org/sonar/server/computation/ws/TaskFormatter.java
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ProjectStatusAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java
server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java
server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java
server/sonar-server/src/main/resources/org/sonar/server/computation/ws/activity-example.json
server/sonar-server/src/main/resources/org/sonar/server/computation/ws/component-example.json
server/sonar-server/src/main/resources/org/sonar/server/computation/ws/task-example.json
server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/project_status-example.json [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/ws/ActivityActionTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/ws/ComponentActionTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/ws/TaskActionTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/ws/TaskFormatterTest.java
server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ProjectStatusActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/tester/UserSessionRule.java
server/sonar-server/src/test/resources/org/sonar/server/qualitygate/ws/ProjectStatusActionTest/measure_data.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatterTest/quality_gate_details.json [new file with mode: 0644]
server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1004_add_ce_activity_snapshot_id.rb
sonar-db/src/main/java/org/sonar/db/ce/CeActivityDto.java
sonar-db/src/main/java/org/sonar/db/ce/CeQueueDto.java
sonar-db/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml
sonar-db/src/test/java/org/sonar/db/ce/CeQueueTesting.java [new file with mode: 0644]
sonar-db/src/test/java/org/sonar/db/measure/MeasureTesting.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/HttpWsClient.java
sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java
sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/ProjectStatusWsRequest.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/QualityGatesService.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygate/package-info.java [new file with mode: 0644]
sonar-ws/src/main/protobuf/ws-ce.proto
sonar-ws/src/main/protobuf/ws-qualitygates.proto [new file with mode: 0644]

index 03f16a80f701032f5d16226dde2b41182b04284d..5ee2466181fbf3dcefe5fa55db083f1554224574 100644 (file)
@@ -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 &ltp*&gt for period variations")
-      .setDefaultValue(FALSE)
-      .setPossibleValues(TRUE, FALSE);
+      .setDefaultValue(String.valueOf(false))
+      .setBooleanPossibleValues();
 
     action.createParam("includealerts")
       .setDescription("Include alerts data: add nodes &ltalert&gt (ERROR, WARN, OK) and &ltalert_text&gt")
-      .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")
index 6decd092133c517400b8e33b268e3e2afe8df862..e7b483eed85eb49b98ce3bdcfb3a11409c120577 100644 (file)
@@ -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"))
index 09b9e10599b44f6c62b12078f25f3855e53c9048..5db2f82871ffb35db8d4fccbbdf487972547bb18 100644 (file)
@@ -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,
index 07b70fd22e0f0195db727e91e55974a86355b444..24822f358a6cf1a87a9604817aa0fe9a34cace3d 100644 (file)
@@ -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);
index d3c3eb68974f5babb7280ef25e130a29d8f3500c..e79851156dd34343980d2b853202b3d34151005c 100644 (file)
@@ -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<String> 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.<br />" +
         "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);
index 1346e6e654d52582a2d714f13a0a456ece6dc614..471c9646e30ecef0d020622ef1583c8d6ae158f1 100644 (file)
@@ -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());
     }
index 53b153bb349b9d430131557613cbfffadcaed58b..25e8a70c500c12f94d7592ff98fb2dfd9b3dc390 100644 (file)
@@ -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 (file)
index 0000000..fb7e92c
--- /dev/null
@@ -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. <br />" +
+        "Returns a http code 404 if the analysis associated with the task is not found or does not exist.<br />" +
+        "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<MeasureDto> 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 (file)
index 0000000..0450cbc
--- /dev/null
@@ -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();
+  }
+}
index e8e6c831c4b8b9e51b8e95d379a2acb97ada5ac9..99b43978e7d34db3fc53ca864c9aadd5aa21cfa3 100644 (file)
@@ -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<T extends AbstractUserSession> impleme
     return this;
   }
 
+  @Override
+  public UserSession checkAnyGlobalPermissions(Collection<String> globalPermissionsToTest) {
+    List<String> 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);
index eb03462876eb66773b2e124dbecf1d8f8448aeb7..62011a7a2528418c21e4fd216ea84d86edfa26fd 100644 (file)
@@ -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<String> globalPermissions) {
+    return get().checkAnyGlobalPermissions(globalPermissions);
+  }
+
   @Override
   public boolean hasGlobalPermission(String globalPermission) {
     return get().hasGlobalPermission(globalPermission);
index 64a7b3f6d0e26bc65fe345e1a262d7d872d90b42..001c26d75c372a09ec9cead792f532eb5198097a 100644 (file)
@@ -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<String> globalPermissions);
+
   /**
    * Does the user have the given permission ?
    */
index de5018a0661653ae04e14cfa2426f2370736ffe7..93d300ba1d775a3aafef0180e47efc4e4d4220f4 100644 (file)
@@ -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",
index 93ead16116673bf9e78fcd907b4b36abef5135de..6adb3f0d7aac685a700e8fb544510228d2b8f8d9 100644 (file)
@@ -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",
index 5f02d9072bd16bfd78346f1b62df65dc028a9605..089e277fb625cee3a3c06b9284d975cf0c43baac 100644 (file)
@@ -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 (file)
index 0000000..cf46493
--- /dev/null
@@ -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"
+      }
+    ]
+  }
+}
index 6a0d284415d08af5b50175b985d1a055d7d12c66..a3f97031f1224f2efdfd61ac9392f4ed0ae23cfe 100644 (file)
@@ -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;
index 15c2dffa707b1cb6b97703bc7fd314d41849edb9..73c397f8b4b2b9f5e4ac910da770db3657336bb6 100644 (file)
@@ -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;
index a67dcbd65df79f4ba22dfa315cca7ec22b590eb5..4bb1ee8da09627740ecc3616065668d74ba3a4d7 100644 (file)
@@ -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
index cca32b55a955f822c11e2bd1d6c3c733827167ca..97465abc6fdb94351874685566dc9a94d7b488c2 100644 (file)
@@ -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 (file)
index 0000000..a11313e
--- /dev/null
@@ -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 (file)
index 0000000..5948869
--- /dev/null
@@ -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<ProjectStatusWsResponse.Condition> 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<ProjectStatusWsResponse.Period> 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();
+  }
+}
index 4a35ad5473e4223ceb517a89f0964406920deb0f..9cac2c7efccdfa7ee5f0efc6aba79a53955f35ff 100644 (file)
@@ -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<String> 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 (file)
index 0000000..679e079
--- /dev/null
@@ -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 (file)
index 0000000..4122aa7
--- /dev/null
@@ -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"
+    }
+  ]
+}
index b2e67caa934de7be2022eb24b181cc4beaef807c..9f41f7406175a5bcb2f62b2aab12009efb7942ec 100644 (file)
@@ -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
index db945aa99869a6f7d3244510baa2dbb0ca8de8e1..4119a12d4d937970af492588131458ff9797bef6 100644 (file)
@@ -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
index 3031029c0bdfda92c1dc0512b182aacbe8424cd4..524a93bd71355ae10bcbcfb7ea3c6d686fb1da70 100644 (file)
@@ -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
index 1050b0969548b8fa588d9c64e06f1ecf6f934b3b..c19c97a08003757da2f1f365c5090fc6f1d7d340 100644 (file)
@@ -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 (file)
index 0000000..bb46b3e
--- /dev/null
@@ -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 (file)
index 0000000..229d487
--- /dev/null
@@ -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);
+  }
+}
index d172f4bbf8840a9eb6a5e7ad7b4d01ecd502dbb9..0435aed90a8956f6358f8d45bcd66fd0b89503a4 100644 (file)
@@ -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;
+  }
 }
index 3f980d7c5910ba4d41b93b6707519bf9176531ec..67e088c46bedc6762b41c9d529123526dc4cbe91 100644 (file)
@@ -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 (file)
index 0000000..b9d435f
--- /dev/null
@@ -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 (file)
index 0000000..404619e
--- /dev/null
@@ -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 (file)
index 0000000..084636c
--- /dev/null
@@ -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;
+
index b6d930a8f02377b7fc6a0b435c75fd0de1405268..9c27b02f4c9d7ca8a4f4dc0718aced6f5a74c4c5 100644 (file)
@@ -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 (file)
index 0000000..f466941
--- /dev/null
@@ -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;
+  }
+}