Merge "Clarify that WARNING is a passing state"
diff --git a/java/com/google/gerrit/plugins/checks/CheckJson.java b/java/com/google/gerrit/plugins/checks/CheckJson.java
index bc2019b..070fd4d 100644
--- a/java/com/google/gerrit/plugins/checks/CheckJson.java
+++ b/java/com/google/gerrit/plugins/checks/CheckJson.java
@@ -14,13 +14,51 @@
 
 package com.google.gerrit.plugins.checks;
 
+import com.google.common.collect.ImmutableSet;
+import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.plugins.checks.api.CheckInfo;
+import com.google.inject.Inject;
 import com.google.inject.Singleton;
+import com.google.inject.assistedinject.Assisted;
+import java.io.IOException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
 
 /** Formats a {@link Check} as JSON. */
-@Singleton
 public class CheckJson {
-  public CheckInfo format(Check check) {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+  @Singleton
+  public static class Factory {
+    private final AssistedFactory assistedFactory;
+
+    @Inject
+    Factory(AssistedFactory assistedFactory) {
+      this.assistedFactory = assistedFactory;
+    }
+
+    public CheckJson create(Iterable<ListChecksOption> options) {
+      return assistedFactory.create(options);
+    }
+
+    public CheckJson noOptions() {
+      return create(ImmutableSet.of());
+    }
+  }
+
+  interface AssistedFactory {
+    CheckJson create(Iterable<ListChecksOption> options);
+  }
+
+  private final Checkers checkers;
+  private final ImmutableSet<ListChecksOption> options;
+
+  @Inject
+  CheckJson(Checkers checkers, @Assisted Iterable<ListChecksOption> options) {
+    this.checkers = checkers;
+    this.options = ImmutableSet.copyOf(options);
+  }
+
+  public CheckInfo format(Check check) throws IOException {
     CheckInfo info = new CheckInfo();
     info.checkerUuid = check.key().checkerUuid().toString();
     info.changeNumber = check.key().patchSet().changeId.id;
@@ -34,6 +72,25 @@
 
     info.created = check.created();
     info.updated = check.updated();
+
+    if (options.contains(ListChecksOption.CHECKER)) {
+      populateCheckerFields(check.key().checkerUuid(), info);
+    }
     return info;
   }
+
+  private void populateCheckerFields(CheckerUuid checkerUuid, CheckInfo info) throws IOException {
+    try {
+      checkers
+          .getChecker(checkerUuid)
+          .ifPresent(
+              checker -> {
+                info.checkerName = checker.getName().orElse(null);
+                info.checkerStatus = checker.getStatus();
+                info.blocking = checker.getBlockingConditions();
+              });
+    } catch (ConfigInvalidException e) {
+      logger.atWarning().withCause(e).log("skipping invalid checker %s", checkerUuid);
+    }
+  }
 }
diff --git a/java/com/google/gerrit/plugins/checks/ListChecksOption.java b/java/com/google/gerrit/plugins/checks/ListChecksOption.java
new file mode 100644
index 0000000..ead047f
--- /dev/null
+++ b/java/com/google/gerrit/plugins/checks/ListChecksOption.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.plugins.checks;
+
+import com.google.gerrit.extensions.client.ListOption;
+
+/** Options for requesting checks. */
+public enum ListChecksOption implements ListOption {
+  CHECKER(0);
+
+  private final int value;
+
+  ListChecksOption(int value) {
+    this.value = value;
+  }
+
+  @Override
+  public int getValue() {
+    return value;
+  }
+}
diff --git a/java/com/google/gerrit/plugins/checks/Module.java b/java/com/google/gerrit/plugins/checks/Module.java
index 7d2f347..ba535e2 100644
--- a/java/com/google/gerrit/plugins/checks/Module.java
+++ b/java/com/google/gerrit/plugins/checks/Module.java
@@ -29,6 +29,7 @@
 public class Module extends FactoryModule {
   @Override
   protected void configure() {
+    factory(CheckJson.AssistedFactory.class);
     install(new NoteDbCheckersModule());
 
     bind(CapabilityDefinition.class)
diff --git a/java/com/google/gerrit/plugins/checks/PostCheck.java b/java/com/google/gerrit/plugins/checks/PostCheck.java
index 9ae04a9..9327842 100644
--- a/java/com/google/gerrit/plugins/checks/PostCheck.java
+++ b/java/com/google/gerrit/plugins/checks/PostCheck.java
@@ -41,7 +41,7 @@
   private final Checkers checkers;
   private final Checks checks;
   private final Provider<ChecksUpdate> checksUpdate;
-  private final CheckJson checkJson;
+  private final CheckJson.Factory checkJsonFactory;
 
   @Inject
   PostCheck(
@@ -50,13 +50,13 @@
       Checkers checkers,
       Checks checks,
       @UserInitiated Provider<ChecksUpdate> checksUpdate,
-      CheckJson checkJson) {
+      CheckJson.Factory checkJsonFactory) {
     this.permissionBackend = permissionBackend;
     this.permission = permission;
     this.checkers = checkers;
     this.checks = checks;
     this.checksUpdate = checksUpdate;
-    this.checkJson = checkJson;
+    this.checkJsonFactory = checkJsonFactory;
   }
 
   @Override
@@ -79,6 +79,7 @@
 
     CheckKey key = CheckKey.create(rsrc.getProject(), rsrc.getPatchSet().getId(), checkerUuid);
     Optional<Check> check = checks.getCheck(key);
+    Check updatedCheck;
     if (!check.isPresent()) {
       checkers
           .getChecker(checkerUuid)
@@ -86,12 +87,11 @@
               () ->
                   new UnprocessableEntityException(
                       String.format("checker %s not found", checkerUuid)));
-
-      Check updatedCheck = checksUpdate.get().createCheck(key, toCheckUpdate(input));
-      return checkJson.format(updatedCheck);
+      updatedCheck = checksUpdate.get().createCheck(key, toCheckUpdate(input));
+    } else {
+      updatedCheck = checksUpdate.get().updateCheck(key, toCheckUpdate(input));
     }
-    Check updatedCheck = checksUpdate.get().updateCheck(key, toCheckUpdate(input));
-    return checkJson.format(updatedCheck);
+    return checkJsonFactory.noOptions().format(updatedCheck);
   }
 
   private static CheckUpdate toCheckUpdate(CheckInput input) throws BadRequestException {
diff --git a/java/com/google/gerrit/plugins/checks/acceptance/testsuite/CheckOperations.java b/java/com/google/gerrit/plugins/checks/acceptance/testsuite/CheckOperations.java
index de3917f..055baa0 100644
--- a/java/com/google/gerrit/plugins/checks/acceptance/testsuite/CheckOperations.java
+++ b/java/com/google/gerrit/plugins/checks/acceptance/testsuite/CheckOperations.java
@@ -17,6 +17,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.gerrit.plugins.checks.Check;
 import com.google.gerrit.plugins.checks.CheckKey;
+import com.google.gerrit.plugins.checks.ListChecksOption;
 import com.google.gerrit.plugins.checks.api.CheckInfo;
 import com.google.gerrit.reviewdb.client.RevId;
 
@@ -102,9 +103,10 @@
      *
      * <p><strong>Note:</strong>This call will fail with an exception if the check doesn't exist.
      *
+     * @param options output options.
      * @return this check as {@link CheckInfo}
      */
-    CheckInfo asInfo() throws Exception;
+    CheckInfo asInfo(ListChecksOption... options) throws Exception;
 
     /**
      * Starts the fluent chain to update a check. The returned builder can be used to specify how
diff --git a/java/com/google/gerrit/plugins/checks/acceptance/testsuite/CheckOperationsImpl.java b/java/com/google/gerrit/plugins/checks/acceptance/testsuite/CheckOperationsImpl.java
index 4452707..9cd2aa5 100644
--- a/java/com/google/gerrit/plugins/checks/acceptance/testsuite/CheckOperationsImpl.java
+++ b/java/com/google/gerrit/plugins/checks/acceptance/testsuite/CheckOperationsImpl.java
@@ -17,6 +17,7 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.plugins.checks.Check;
 import com.google.gerrit.plugins.checks.CheckJson;
 import com.google.gerrit.plugins.checks.CheckKey;
@@ -24,6 +25,7 @@
 import com.google.gerrit.plugins.checks.CheckerRef;
 import com.google.gerrit.plugins.checks.Checks;
 import com.google.gerrit.plugins.checks.ChecksUpdate;
+import com.google.gerrit.plugins.checks.ListChecksOption;
 import com.google.gerrit.plugins.checks.api.CheckInfo;
 import com.google.gerrit.reviewdb.client.RevId;
 import com.google.gerrit.server.ServerInitiated;
@@ -39,18 +41,18 @@
 public final class CheckOperationsImpl implements CheckOperations {
   private final Checks checks;
   private final ChecksUpdate checksUpdate;
-  private final CheckJson checkJson;
+  private final CheckJson.Factory checkJsonFactory;
   private final GitRepositoryManager repoManager;
 
   @Inject
   public CheckOperationsImpl(
       Checks checks,
       GitRepositoryManager repoManager,
-      CheckJson checkJson,
+      CheckJson.Factory checkJsonFactory,
       @ServerInitiated ChecksUpdate checksUpdate) {
     this.checks = checks;
     this.repoManager = repoManager;
-    this.checkJson = checkJson;
+    this.checkJsonFactory = checkJsonFactory;
     this.checksUpdate = checksUpdate;
   }
 
@@ -105,8 +107,8 @@
     }
 
     @Override
-    public CheckInfo asInfo() throws Exception {
-      return checkJson.format(get());
+    public CheckInfo asInfo(ListChecksOption... options) throws Exception {
+      return checkJsonFactory.create(ImmutableSet.copyOf(options)).format(get());
     }
 
     @Override
diff --git a/java/com/google/gerrit/plugins/checks/api/CheckApi.java b/java/com/google/gerrit/plugins/checks/api/CheckApi.java
index 34008e1..d137645 100644
--- a/java/com/google/gerrit/plugins/checks/api/CheckApi.java
+++ b/java/com/google/gerrit/plugins/checks/api/CheckApi.java
@@ -16,11 +16,12 @@
 
 import com.google.gerrit.extensions.restapi.NotImplementedException;
 import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.plugins.checks.ListChecksOption;
 
 /** Java API to interact with single {@code Check}s. */
 public interface CheckApi {
-  /** Returns a {@link CheckInfo} for the scoped resource. */
-  CheckInfo get() throws RestApiException;
+  /** Returns a {@link CheckInfo} for the scoped resource with the given options. */
+  CheckInfo get(ListChecksOption... options) throws RestApiException;
 
   /** Updates a check and returns the {@link CheckInfo} for the updated resource. */
   CheckInfo update(CheckInput input) throws RestApiException;
@@ -31,7 +32,7 @@
    */
   class NotImplemented implements CheckApi {
     @Override
-    public CheckInfo get() throws RestApiException {
+    public CheckInfo get(ListChecksOption... options) throws RestApiException {
       throw new NotImplementedException();
     }
 
diff --git a/java/com/google/gerrit/plugins/checks/api/CheckApiImpl.java b/java/com/google/gerrit/plugins/checks/api/CheckApiImpl.java
index 083ba1e..378ce64 100644
--- a/java/com/google/gerrit/plugins/checks/api/CheckApiImpl.java
+++ b/java/com/google/gerrit/plugins/checks/api/CheckApiImpl.java
@@ -17,8 +17,10 @@
 import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
 
 import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.plugins.checks.ListChecksOption;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
+import java.util.Arrays;
 
 public class CheckApiImpl implements CheckApi {
   public interface Factory {
@@ -37,8 +39,13 @@
   }
 
   @Override
-  public CheckInfo get() throws RestApiException {
-    return getCheck.apply(checkResource);
+  public CheckInfo get(ListChecksOption... options) throws RestApiException {
+    try {
+      Arrays.stream(options).forEach(getCheck::addOption);
+      return getCheck.apply(checkResource);
+    } catch (Exception e) {
+      throw asRestApiException("Cannot retrieve check", e);
+    }
   }
 
   @Override
diff --git a/java/com/google/gerrit/plugins/checks/api/CheckInfo.java b/java/com/google/gerrit/plugins/checks/api/CheckInfo.java
index b5d9f06..be8f34a 100644
--- a/java/com/google/gerrit/plugins/checks/api/CheckInfo.java
+++ b/java/com/google/gerrit/plugins/checks/api/CheckInfo.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.common.Nullable;
 import java.sql.Timestamp;
 import java.util.Objects;
+import java.util.Set;
 
 /** REST API representation of a {@link com.google.gerrit.plugins.checks.Check}. */
 public class CheckInfo {
@@ -44,6 +45,15 @@
   /** Timestamp of when this check was last updated. */
   public Timestamp updated;
 
+  /** Name of the checker that produced this check. */
+  public String checkerName;
+
+  /** Status of the checker that produced this check. */
+  public CheckerStatus checkerStatus;
+
+  /** Blocking conditions that apply to this check. */
+  public Set<BlockingCondition> blocking;
+
   @Override
   public boolean equals(Object o) {
     if (!(o instanceof CheckInfo)) {
@@ -59,7 +69,10 @@
         && Objects.equals(other.started, started)
         && Objects.equals(other.finished, finished)
         && Objects.equals(other.created, created)
-        && Objects.equals(other.updated, updated);
+        && Objects.equals(other.updated, updated)
+        && Objects.equals(other.checkerName, checkerName)
+        && Objects.equals(other.checkerStatus, checkerStatus)
+        && Objects.equals(other.blocking, blocking);
   }
 
   @Override
@@ -74,7 +87,10 @@
         started,
         finished,
         created,
-        updated);
+        updated,
+        checkerName,
+        checkerStatus,
+        blocking);
   }
 
   @Override
@@ -90,6 +106,9 @@
         .add("finished", finished)
         .add("created", created)
         .add("updated", updated)
+        .add("checkerName", checkerName)
+        .add("checkerStatus", checkerStatus)
+        .add("blocking", blocking)
         .toString();
   }
 }
diff --git a/java/com/google/gerrit/plugins/checks/api/Checks.java b/java/com/google/gerrit/plugins/checks/api/Checks.java
index dec5cae..b5a6720 100644
--- a/java/com/google/gerrit/plugins/checks/api/Checks.java
+++ b/java/com/google/gerrit/plugins/checks/api/Checks.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.extensions.restapi.NotImplementedException;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.plugins.checks.CheckerUuid;
+import com.google.gerrit.plugins.checks.ListChecksOption;
 
 /** Java API to interact with {@code Check}s. */
 public interface Checks {
@@ -35,7 +36,7 @@
 
   CheckApi create(CheckInput input) throws RestApiException;
 
-  ImmutableList<CheckInfo> list() throws RestApiException;
+  ImmutableList<CheckInfo> list(ListChecksOption... options) throws RestApiException;
 
   /**
    * A default implementation which allows source compatibility when adding new methods to the
@@ -53,7 +54,7 @@
     }
 
     @Override
-    public ImmutableList<CheckInfo> list() throws RestApiException {
+    public ImmutableList<CheckInfo> list(ListChecksOption... options) throws RestApiException {
       throw new NotImplementedException();
     }
   }
diff --git a/java/com/google/gerrit/plugins/checks/api/ChecksImpl.java b/java/com/google/gerrit/plugins/checks/api/ChecksImpl.java
index 582e580..d7d67bf 100644
--- a/java/com/google/gerrit/plugins/checks/api/ChecksImpl.java
+++ b/java/com/google/gerrit/plugins/checks/api/ChecksImpl.java
@@ -20,10 +20,12 @@
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.plugins.checks.CheckerUuid;
+import com.google.gerrit.plugins.checks.ListChecksOption;
 import com.google.gerrit.plugins.checks.PostCheck;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
+import java.util.Arrays;
 
 class ChecksImpl implements com.google.gerrit.plugins.checks.api.Checks {
 
@@ -72,8 +74,9 @@
   }
 
   @Override
-  public ImmutableList<CheckInfo> list() throws RestApiException {
+  public ImmutableList<CheckInfo> list(ListChecksOption... options) throws RestApiException {
     try {
+      Arrays.stream(options).forEach(listChecks::addOption);
       return listChecks.apply(revisionResource);
     } catch (Exception e) {
       throw asRestApiException("Cannot list checks", e);
diff --git a/java/com/google/gerrit/plugins/checks/api/GetCheck.java b/java/com/google/gerrit/plugins/checks/api/GetCheck.java
index c78fc25..ab2abde 100644
--- a/java/com/google/gerrit/plugins/checks/api/GetCheck.java
+++ b/java/com/google/gerrit/plugins/checks/api/GetCheck.java
@@ -14,25 +14,41 @@
 
 package com.google.gerrit.plugins.checks.api;
 
+import com.google.gerrit.extensions.client.ListOption;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.plugins.checks.CheckJson;
+import com.google.gerrit.plugins.checks.ListChecksOption;
 import com.google.inject.Inject;
+import java.io.IOException;
+import java.util.EnumSet;
+import org.kohsuke.args4j.Option;
 
 public class GetCheck implements RestReadView<CheckResource> {
+  private final CheckJson.Factory checkJsonFactory;
 
-  private final CheckJson checkJson;
+  private final EnumSet<ListChecksOption> options = EnumSet.noneOf(ListChecksOption.class);
+
+  @Option(name = "-o", usage = "Output options")
+  void addOption(ListChecksOption o) {
+    options.add(o);
+  }
+
+  @Option(name = "-O", usage = "Output option flags, in hex")
+  void setOptionFlagsHex(String hex) {
+    options.addAll(ListOption.fromBits(ListChecksOption.class, Integer.parseInt(hex, 16)));
+  }
 
   @Inject
-  GetCheck(CheckJson checkJson) {
-    this.checkJson = checkJson;
+  GetCheck(CheckJson.Factory checkJsonFactory) {
+    this.checkJsonFactory = checkJsonFactory;
   }
 
   @Override
   public CheckInfo apply(CheckResource resource)
-      throws AuthException, BadRequestException, ResourceConflictException {
-    return checkJson.format(resource.getCheck());
+      throws AuthException, BadRequestException, ResourceConflictException, IOException {
+    return checkJsonFactory.create(options).format(resource.getCheck());
   }
 }
diff --git a/java/com/google/gerrit/plugins/checks/api/ListChecks.java b/java/com/google/gerrit/plugins/checks/api/ListChecks.java
index 6cfa6fc..a787e69 100644
--- a/java/com/google/gerrit/plugins/checks/api/ListChecks.java
+++ b/java/com/google/gerrit/plugins/checks/api/ListChecks.java
@@ -17,6 +17,7 @@
 import static java.util.stream.Collectors.toMap;
 
 import com.google.common.collect.ImmutableList;
+import com.google.gerrit.extensions.client.ListOption;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -27,25 +28,41 @@
 import com.google.gerrit.plugins.checks.CheckerUuid;
 import com.google.gerrit.plugins.checks.Checkers;
 import com.google.gerrit.plugins.checks.Checks;
+import com.google.gerrit.plugins.checks.ListChecksOption;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
-import com.google.inject.Singleton;
 import java.io.IOException;
+import java.util.EnumSet;
 import java.util.Map;
+import org.kohsuke.args4j.Option;
 
-@Singleton
 public class ListChecks implements RestReadView<RevisionResource> {
   private final CheckBackfiller checkBackfiller;
-  private final CheckJson checkJson;
+  private final CheckJson.Factory checkJsonFactory;
   private final Checkers checkers;
   private final Checks checks;
 
+  private final EnumSet<ListChecksOption> options = EnumSet.noneOf(ListChecksOption.class);
+
+  @Option(name = "-o", usage = "Output options")
+  void addOption(ListChecksOption o) {
+    options.add(o);
+  }
+
+  @Option(name = "-O", usage = "Output option flags, in hex")
+  void setOptionFlagsHex(String hex) {
+    options.addAll(ListOption.fromBits(ListChecksOption.class, Integer.parseInt(hex, 16)));
+  }
+
   @Inject
   ListChecks(
-      CheckBackfiller checkBackfiller, CheckJson checkJson, Checkers checkers, Checks checks) {
+      CheckBackfiller checkBackfiller,
+      CheckJson.Factory checkJsonFactory,
+      Checkers checkers,
+      Checks checks) {
     this.checkBackfiller = checkBackfiller;
-    this.checkJson = checkJson;
+    this.checkJsonFactory = checkJsonFactory;
     this.checkers = checkers;
     this.checks = checks;
   }
@@ -54,10 +71,9 @@
   public ImmutableList<CheckInfo> apply(RevisionResource resource)
       throws AuthException, BadRequestException, ResourceConflictException, OrmException,
           IOException {
+    CheckJson checkJson = checkJsonFactory.create(options);
     Map<CheckerUuid, Checker> checkersByUuid =
-        checkers
-            .checkersOf(resource.getProject())
-            .stream()
+        checkers.checkersOf(resource.getProject()).stream()
             .collect(toMap(Checker::getUuid, c -> c));
 
     ImmutableList.Builder<CheckInfo> result =
@@ -67,12 +83,11 @@
       result.add(checkJson.format(check));
     }
 
-    checkBackfiller
-        .getBackfilledChecksForRelevantCheckers(
-            checkersByUuid.values(), resource.getNotes(), resource.getPatchSet().getId())
-        .stream()
-        .map(checkJson::format)
-        .forEach(result::add);
+    for (Check check :
+        checkBackfiller.getBackfilledChecksForRelevantCheckers(
+            checkersByUuid.values(), resource.getNotes(), resource.getPatchSet().getId())) {
+      result.add(checkJson.format(check));
+    }
 
     return result.build();
   }
diff --git a/javatests/com/google/gerrit/plugins/checks/acceptance/api/GetCheckIT.java b/javatests/com/google/gerrit/plugins/checks/acceptance/api/GetCheckIT.java
index 2dff266..2d097db 100644
--- a/javatests/com/google/gerrit/plugins/checks/acceptance/api/GetCheckIT.java
+++ b/javatests/com/google/gerrit/plugins/checks/acceptance/api/GetCheckIT.java
@@ -19,17 +19,19 @@
 import static com.google.common.truth.Truth.assert_;
 import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.plugins.checks.Check;
-import com.google.gerrit.plugins.checks.CheckJson;
 import com.google.gerrit.plugins.checks.CheckKey;
 import com.google.gerrit.plugins.checks.CheckerUuid;
+import com.google.gerrit.plugins.checks.ListChecksOption;
 import com.google.gerrit.plugins.checks.acceptance.AbstractCheckersTest;
+import com.google.gerrit.plugins.checks.api.BlockingCondition;
 import com.google.gerrit.plugins.checks.api.CheckInfo;
 import com.google.gerrit.plugins.checks.api.CheckState;
+import com.google.gerrit.plugins.checks.api.CheckerStatus;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
@@ -41,12 +43,10 @@
 public class GetCheckIT extends AbstractCheckersTest {
   @Inject private RequestScopeOperations requestScopeOperations;
 
-  private CheckJson checkJson;
   private PatchSet.Id patchSetId;
 
   @Before
   public void setUp() throws Exception {
-    checkJson = plugin.getSysInjector().getInstance(CheckJson.class);
     patchSetId = createChange().getPatchSetId();
   }
 
@@ -58,7 +58,44 @@
     checkOperations.newCheck(checkKey).setState(CheckState.RUNNING).upsert();
 
     CheckInfo checkInfo = checksApiFactory.revision(patchSetId).id(checkerUuid).get();
-    assertThat(checkInfo).isEqualTo(checkOperations.check(checkKey).asInfo());
+    CheckInfo expected = new CheckInfo();
+    expected.project = checkKey.project().get();
+    expected.changeNumber = checkKey.patchSet().getParentKey().get();
+    expected.patchSetId = checkKey.patchSet().get();
+    expected.checkerUuid = checkKey.checkerUuid().toString();
+    expected.state = CheckState.RUNNING;
+    expected.created = checkOperations.check(checkKey).get().created();
+    expected.updated = expected.created;
+    assertThat(checkInfo).isEqualTo(expected);
+  }
+
+  @Test
+  public void getCheckWithOptions() throws Exception {
+    CheckerUuid checkerUuid =
+        checkerOperations
+            .newChecker()
+            .name("My Checker")
+            .blockingConditions(BlockingCondition.STATE_NOT_PASSING)
+            .repository(project)
+            .create();
+
+    CheckKey checkKey = CheckKey.create(project, patchSetId, checkerUuid);
+    checkOperations.newCheck(checkKey).setState(CheckState.RUNNING).upsert();
+
+    CheckInfo checkInfo =
+        checksApiFactory.revision(patchSetId).id(checkerUuid).get(ListChecksOption.CHECKER);
+    CheckInfo expected = new CheckInfo();
+    expected.project = checkKey.project().get();
+    expected.changeNumber = checkKey.patchSet().getParentKey().get();
+    expected.patchSetId = checkKey.patchSet().get();
+    expected.checkerUuid = checkKey.checkerUuid().toString();
+    expected.state = CheckState.RUNNING;
+    expected.created = checkOperations.check(checkKey).get().created();
+    expected.updated = expected.created;
+    expected.checkerName = "My Checker";
+    expected.blocking = ImmutableSet.of(BlockingCondition.STATE_NOT_PASSING);
+    expected.checkerStatus = CheckerStatus.ENABLED;
+    assertThat(checkInfo).isEqualTo(expected);
   }
 
   @Test
@@ -134,14 +171,15 @@
     gApi.changes().id(changeId.get()).topic(topic);
 
     Timestamp psCreated = getPatchSetCreated(changeId);
-    assertThat(checksApiFactory.revision(patchSetId).id(checkerUuid).get())
-        .isEqualTo(
-            checkJson.format(
-                Check.builder(checkKey)
-                    .setState(CheckState.NOT_STARTED)
-                    .setCreated(psCreated)
-                    .setUpdated(psCreated)
-                    .build()));
+    CheckInfo expected = new CheckInfo();
+    expected.project = checkKey.project().get();
+    expected.changeNumber = checkKey.patchSet().getParentKey().get();
+    expected.patchSetId = checkKey.patchSet().get();
+    expected.checkerUuid = checkKey.checkerUuid().toString();
+    expected.state = CheckState.NOT_STARTED;
+    expected.created = psCreated;
+    expected.updated = psCreated;
+    assertThat(checksApiFactory.revision(patchSetId).id(checkerUuid).get()).isEqualTo(expected);
   }
 
   @Test
@@ -151,14 +189,15 @@
     CheckKey checkKey = CheckKey.create(project, patchSetId, checkerUuid);
 
     Timestamp psCreated = getPatchSetCreated(changeId);
-    assertThat(checksApiFactory.revision(patchSetId).id(checkerUuid).get())
-        .isEqualTo(
-            checkJson.format(
-                Check.builder(checkKey)
-                    .setState(CheckState.NOT_STARTED)
-                    .setCreated(psCreated)
-                    .setUpdated(psCreated)
-                    .build()));
+    CheckInfo expected = new CheckInfo();
+    expected.project = checkKey.project().get();
+    expected.changeNumber = checkKey.patchSet().getParentKey().get();
+    expected.patchSetId = checkKey.patchSet().get();
+    expected.checkerUuid = checkKey.checkerUuid().toString();
+    expected.state = CheckState.NOT_STARTED;
+    expected.created = psCreated;
+    expected.updated = psCreated;
+    assertThat(checksApiFactory.revision(patchSetId).id(checkerUuid).get()).isEqualTo(expected);
 
     checkerOperations.checker(checkerUuid).forUpdate().disable().update();
 
diff --git a/javatests/com/google/gerrit/plugins/checks/acceptance/api/ListChecksIT.java b/javatests/com/google/gerrit/plugins/checks/acceptance/api/ListChecksIT.java
index 927e327..eded791 100644
--- a/javatests/com/google/gerrit/plugins/checks/acceptance/api/ListChecksIT.java
+++ b/javatests/com/google/gerrit/plugins/checks/acceptance/api/ListChecksIT.java
@@ -18,14 +18,15 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.plugins.checks.Check;
-import com.google.gerrit.plugins.checks.CheckJson;
 import com.google.gerrit.plugins.checks.CheckKey;
 import com.google.gerrit.plugins.checks.CheckerUuid;
+import com.google.gerrit.plugins.checks.ListChecksOption;
 import com.google.gerrit.plugins.checks.acceptance.AbstractCheckersTest;
 import com.google.gerrit.plugins.checks.api.CheckInfo;
 import com.google.gerrit.plugins.checks.api.CheckState;
+import com.google.gerrit.plugins.checks.api.CheckerStatus;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
@@ -35,18 +36,17 @@
 import org.junit.Test;
 
 public class ListChecksIT extends AbstractCheckersTest {
-  private CheckJson checkJson;
   private PatchSet.Id patchSetId;
   private CheckKey checkKey1;
   private CheckKey checkKey2;
 
   @Before
   public void setUp() throws Exception {
-    checkJson = plugin.getSysInjector().getInstance(CheckJson.class);
     patchSetId = createChange().getPatchSetId();
 
     CheckerUuid checker1Uuid = checkerOperations.newChecker().repository(project).create();
-    CheckerUuid checker2Uuid = checkerOperations.newChecker().repository(project).create();
+    CheckerUuid checker2Uuid =
+        checkerOperations.newChecker().name("Checker Two").repository(project).create();
 
     checkKey1 = CheckKey.create(project, patchSetId, checker1Uuid);
     checkOperations.newCheck(checkKey1).setState(CheckState.RUNNING).upsert();
@@ -58,9 +58,58 @@
   @Test
   public void listAll() throws Exception {
     Collection<CheckInfo> info = checksApiFactory.revision(patchSetId).list();
-    assertThat(info)
-        .containsExactly(
-            checkOperations.check(checkKey1).asInfo(), checkOperations.check(checkKey2).asInfo());
+
+    CheckInfo expected1 = new CheckInfo();
+    expected1.project = checkKey1.project().get();
+    expected1.changeNumber = checkKey1.patchSet().getParentKey().get();
+    expected1.patchSetId = checkKey1.patchSet().get();
+    expected1.checkerUuid = checkKey1.checkerUuid().toString();
+    expected1.state = CheckState.RUNNING;
+    expected1.created = checkOperations.check(checkKey1).get().created();
+    expected1.updated = expected1.created;
+
+    CheckInfo expected2 = new CheckInfo();
+    expected2.project = checkKey2.project().get();
+    expected2.changeNumber = checkKey2.patchSet().getParentKey().get();
+    expected2.patchSetId = checkKey2.patchSet().get();
+    expected2.checkerUuid = checkKey2.checkerUuid().toString();
+    expected2.state = CheckState.RUNNING;
+    expected2.created = checkOperations.check(checkKey2).get().created();
+    expected2.updated = expected2.created;
+
+    assertThat(info).containsExactly(expected1, expected2);
+  }
+
+  @Test
+  public void listAllWithOptions() throws Exception {
+    Collection<CheckInfo> info =
+        checksApiFactory.revision(patchSetId).list(ListChecksOption.CHECKER);
+
+    Timestamp psCreated = getPatchSetCreated(patchSetId.getParentKey());
+    CheckInfo expected1 = new CheckInfo();
+    expected1.project = checkKey1.project().get();
+    expected1.changeNumber = checkKey1.patchSet().getParentKey().get();
+    expected1.patchSetId = checkKey1.patchSet().get();
+    expected1.checkerUuid = checkKey1.checkerUuid().toString();
+    expected1.state = CheckState.RUNNING;
+    expected1.created = psCreated;
+    expected1.updated = psCreated;
+    expected1.blocking = ImmutableSet.of();
+    expected1.checkerStatus = CheckerStatus.ENABLED;
+
+    CheckInfo expected2 = new CheckInfo();
+    expected2.project = checkKey2.project().get();
+    expected2.changeNumber = checkKey2.patchSet().getParentKey().get();
+    expected2.patchSetId = checkKey2.patchSet().get();
+    expected2.checkerUuid = checkKey2.checkerUuid().toString();
+    expected2.state = CheckState.RUNNING;
+    expected2.created = psCreated;
+    expected2.updated = psCreated;
+    expected2.checkerName = "Checker Two";
+    expected2.blocking = ImmutableSet.of();
+    expected2.checkerStatus = CheckerStatus.ENABLED;
+
+    assertThat(info).containsExactly(expected1, expected2);
   }
 
   @Test
@@ -138,38 +187,35 @@
     // Update change to match checker3's query.
     gApi.changes().id(changeId.get()).topic(topic);
 
-    // TODO(dborowitz): Get from checkOperations once we backfill the get API as well.
-    Timestamp psCreated = getPatchSetCreated(changeId);
-    CheckInfo checkInfo3 =
-        checkJson.format(
-            Check.builder(checkKey3)
-                .setState(CheckState.NOT_STARTED)
-                .setCreated(psCreated)
-                .setUpdated(psCreated)
-                .build());
+    Timestamp psCreated = getPatchSetCreated(patchSetId.getParentKey());
+    CheckInfo checkInfo3 = new CheckInfo();
+    checkInfo3.project = checkKey3.project().get();
+    checkInfo3.changeNumber = checkKey3.patchSet().getParentKey().get();
+    checkInfo3.patchSetId = checkKey3.patchSet().get();
+    checkInfo3.checkerUuid = checkKey3.checkerUuid().toString();
+    checkInfo3.state = CheckState.NOT_STARTED;
+    checkInfo3.created = psCreated;
+    checkInfo3.updated = psCreated;
     assertThat(checksApiFactory.revision(patchSetId).list())
         .containsExactly(checkInfo1, checkInfo2, checkInfo3);
   }
 
   @Test
   public void listDoesntBackfillForDisabledChecker() throws Exception {
-    Change.Id changeId = patchSetId.getParentKey();
     CheckerUuid checker3Uuid = checkerOperations.newChecker().repository(project).create();
     CheckKey checkKey3 = CheckKey.create(project, patchSetId, checker3Uuid);
 
     CheckInfo checkInfo1 = checkOperations.check(checkKey1).asInfo();
     CheckInfo checkInfo2 = checkOperations.check(checkKey2).asInfo();
-
-    // TODO(dborowitz): Get from checkOperations once we backfill the get API as well.
-    Timestamp psCreated = getPatchSetCreated(changeId);
-    CheckInfo checkInfo3 =
-        checkJson.format(
-            Check.builder(checkKey3)
-                .setState(CheckState.NOT_STARTED)
-                .setCreated(psCreated)
-                .setUpdated(psCreated)
-                .build());
-
+    Timestamp psCreated = getPatchSetCreated(patchSetId.getParentKey());
+    CheckInfo checkInfo3 = new CheckInfo();
+    checkInfo3.project = checkKey3.project().get();
+    checkInfo3.changeNumber = checkKey3.patchSet().getParentKey().get();
+    checkInfo3.patchSetId = checkKey3.patchSet().get();
+    checkInfo3.checkerUuid = checkKey3.checkerUuid().toString();
+    checkInfo3.state = CheckState.NOT_STARTED;
+    checkInfo3.created = psCreated;
+    checkInfo3.updated = psCreated;
     assertThat(checksApiFactory.revision(patchSetId).list())
         .containsExactly(checkInfo1, checkInfo2, checkInfo3);
 
diff --git a/src/main/resources/Documentation/rest-api-checks.md b/src/main/resources/Documentation/rest-api-checks.md
index 4263582..08fc979 100644
--- a/src/main/resources/Documentation/rest-api-checks.md
+++ b/src/main/resources/Documentation/rest-api-checks.md
@@ -13,6 +13,8 @@
 
 Retrieves all checks for a given revision and change.
 
+Additional fields can be obtained by adding [`o` parameters](#query-options).
+
 #### Request
 
 ```
@@ -61,6 +63,8 @@
 Returns a check regardless of the state that the checker is in (also for
 `DISABLED` checkers).
 
+Additional fields can be obtained by adding [`o` parameters](#query-options).
+
 #### Request
 
 ```
@@ -169,6 +173,9 @@
 | `finished`        | optional | The [timestamp](../../../Documentation/rest-api.html#timestamp) of when the check finished processing.
 | `created`         |          | The [timestamp](../../../Documentation/rest-api.html#timestamp) of when the check was created.
 | `updated`         |          | The [timestamp](../../../Documentation/rest-api.html#timestamp) of when the check was last updated.
+| `checker_name`    | optional | The name of the checker that produced this check.<br />Only set if [checker details](#option-checker) are requested.
+| `checker_status`  | optional | The [status](rest-api-checkers.md#checker-info) of the checker that produced this check.<br />Only set if [checker details](#option-checker) are requested.
+| `blocking`        | optional | Set of [blocking conditions](rest-api-checkers.md#blocking-conditions) that apply to this checker.<br />Only set if [checker details](#option-checker) are requested.
 
 ### <a id="check-input"> CheckInput
 The `CheckInput` entity contains information for creating or updating a check.
@@ -184,3 +191,10 @@
 ### <a id="check-state"> CheckState (enum)
 The `CheckState` enum can have the following values: `NOT_STARTED`, `FAILED`,
 `SCHEDULED`, `RUNNING`, `SUCCESSFUL` and `NOT_RELEVANT`.
+
+### <a id="query-options"> Options
+
+The following query options are supported in the `o` field of certain requests:
+
+* <a id="option-checker"></a> `CHECKER`: Include details from the configuration of
+  the checker that produced this check.