Merge "GetCheck: Backfill checkers"
diff --git a/java/com/google/gerrit/plugins/checks/api/CheckBackfiller.java b/java/com/google/gerrit/plugins/checks/api/CheckBackfiller.java
index da98cd9..d25e8af 100644
--- a/java/com/google/gerrit/plugins/checks/api/CheckBackfiller.java
+++ b/java/com/google/gerrit/plugins/checks/api/CheckBackfiller.java
@@ -22,6 +22,8 @@
import com.google.gerrit.plugins.checks.Check;
import com.google.gerrit.plugins.checks.CheckKey;
import com.google.gerrit.plugins.checks.Checker;
+import com.google.gerrit.plugins.checks.CheckerUuid;
+import com.google.gerrit.plugins.checks.Checkers;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.index.change.ChangeField;
@@ -34,22 +36,28 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import java.io.IOException;
import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jgit.errors.ConfigInvalidException;
@Singleton
class CheckBackfiller {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final ChangeData.Factory changeDataFactory;
+ private final Checkers checkers;
private final Provider<AnonymousUser> anonymousUserProvider;
private final Provider<ChangeQueryBuilder> queryBuilderProvider;
@Inject
CheckBackfiller(
Factory changeDataFactory,
+ Checkers checkers,
Provider<AnonymousUser> anonymousUserProvider,
Provider<ChangeQueryBuilder> queryBuilderProvider) {
this.changeDataFactory = changeDataFactory;
+ this.checkers = checkers;
this.anonymousUserProvider = anonymousUserProvider;
this.queryBuilderProvider = queryBuilderProvider;
}
@@ -69,22 +77,52 @@
// NOT_STARTED, with creation time matching the patch set.
ImmutableList.Builder<Check> result = ImmutableList.builderWithExpectedSize(candidates.size());
PatchSet ps = cd.patchSet(psId);
- ChangeQueryBuilder queryBuilder =
- queryBuilderProvider.get().asUser(anonymousUserProvider.get());
+ ChangeQueryBuilder queryBuilder = newQueryBuilder();
for (Checker checker : candidates) {
if (matches(checker, cd, queryBuilder)) {
// Add synthetic check at the creation time of the patch set.
- result.add(
- Check.builder(CheckKey.create(cd.project(), ps.getId(), checker.getUuid()))
- .setState(CheckState.NOT_STARTED)
- .setCreated(ps.getCreatedOn())
- .setUpdated(ps.getCreatedOn())
- .build());
+ result.add(newBackfilledCheck(cd, ps, checker));
}
}
return result.build();
}
+ Optional<Check> getBackfilledCheckForRelevantChecker(
+ CheckerUuid candidate, ChangeNotes notes, PatchSet.Id psId) throws OrmException, IOException {
+ ChangeData cd = changeDataFactory.create(notes);
+ if (!psId.equals(cd.change().currentPatchSetId())) {
+ // The query system can only match against the current patch set; it doesn't make sense to
+ // backfill checkers for old patch sets.
+ return Optional.empty();
+ }
+
+ Optional<Checker> checker;
+ try {
+ checker = checkers.getChecker(candidate);
+ } catch (ConfigInvalidException e) {
+ // Match behavior of Checkers#checkersOf, ignoring invalid config.
+ return Optional.empty();
+ }
+ if (!checker.isPresent()
+ || checker.get().getStatus() != CheckerStatus.ENABLED
+ || !matches(checker.get(), cd, newQueryBuilder())) {
+ return Optional.empty();
+ }
+ return Optional.of(newBackfilledCheck(cd, cd.patchSet(psId), checker.get()));
+ }
+
+ private Check newBackfilledCheck(ChangeData cd, PatchSet ps, Checker checker) {
+ return Check.builder(CheckKey.create(cd.project(), ps.getId(), checker.getUuid()))
+ .setState(CheckState.NOT_STARTED)
+ .setCreated(ps.getCreatedOn())
+ .setUpdated(ps.getCreatedOn())
+ .build();
+ }
+
+ private ChangeQueryBuilder newQueryBuilder() {
+ return queryBuilderProvider.get().asUser(anonymousUserProvider.get());
+ }
+
private static boolean matches(Checker checker, ChangeData cd, ChangeQueryBuilder queryBuilder)
throws OrmException {
if (!checker.getQuery().isPresent()) {
diff --git a/java/com/google/gerrit/plugins/checks/api/ChecksCollection.java b/java/com/google/gerrit/plugins/checks/api/ChecksCollection.java
index 55772bd..37d0350 100644
--- a/java/com/google/gerrit/plugins/checks/api/ChecksCollection.java
+++ b/java/com/google/gerrit/plugins/checks/api/ChecksCollection.java
@@ -36,17 +36,21 @@
@Singleton
public class ChecksCollection implements ChildCollection<RevisionResource, CheckResource> {
-
- private final ListChecks listChecks;
+ private final CheckBackfiller checkBackfiller;
private final Checks checks;
private final DynamicMap<RestView<CheckResource>> views;
+ private final ListChecks listChecks;
@Inject
ChecksCollection(
- ListChecks listChecks, Checks checks, DynamicMap<RestView<CheckResource>> views) {
- this.listChecks = listChecks;
+ CheckBackfiller checkBackfiller,
+ Checks checks,
+ DynamicMap<RestView<CheckResource>> views,
+ ListChecks listChecks) {
+ this.checkBackfiller = checkBackfiller;
this.checks = checks;
this.views = views;
+ this.listChecks = listChecks;
}
@Override
@@ -64,8 +68,19 @@
CheckKey checkKey =
CheckKey.create(parent.getProject(), parent.getPatchSet().getId(), checkerUuid);
Optional<Check> check = checks.getCheck(checkKey);
+ if (!check.isPresent()) {
+ check =
+ checkBackfiller.getBackfilledCheckForRelevantChecker(
+ checkerUuid, parent.getNotes(), checkKey.patchSet());
+ }
return new CheckResource(
- parent, check.orElseThrow(() -> new ResourceNotFoundException("Not found: " + id.get())));
+ parent,
+ check.orElseThrow(
+ () ->
+ new ResourceNotFoundException(
+ String.format(
+ "Patch set %s in project %s doesn't have check for checker %s.",
+ checkKey.patchSet(), checkKey.project(), checkerUuid))));
}
@Override
diff --git a/java/com/google/gerrit/plugins/checks/api/ChecksImpl.java b/java/com/google/gerrit/plugins/checks/api/ChecksImpl.java
index dab94b4..582e580 100644
--- a/java/com/google/gerrit/plugins/checks/api/ChecksImpl.java
+++ b/java/com/google/gerrit/plugins/checks/api/ChecksImpl.java
@@ -17,17 +17,13 @@
import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
import com.google.common.collect.ImmutableList;
-import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.plugins.checks.Check;
-import com.google.gerrit.plugins.checks.CheckKey;
import com.google.gerrit.plugins.checks.CheckerUuid;
-import com.google.gerrit.plugins.checks.Checks;
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.Optional;
class ChecksImpl implements com.google.gerrit.plugins.checks.api.Checks {
@@ -35,44 +31,31 @@
ChecksImpl create(RevisionResource revisionResource);
}
- private final Checks checks;
+ private final CheckApiImpl.Factory checkApiImplFactory;
+ private final ChecksCollection checksCollection;
private final ListChecks listChecks;
private final PostCheck postCheck;
- private final CheckApiImpl.Factory checkApiImplFactory;
private final RevisionResource revisionResource;
@Inject
ChecksImpl(
CheckApiImpl.Factory checkApiImplFactory,
- Checks checks,
+ ChecksCollection checksCollection,
ListChecks listChecks,
PostCheck postCheck,
@Assisted RevisionResource revisionResource) {
this.checkApiImplFactory = checkApiImplFactory;
- this.checks = checks;
- this.postCheck = postCheck;
+ this.checksCollection = checksCollection;
this.listChecks = listChecks;
+ this.postCheck = postCheck;
this.revisionResource = revisionResource;
}
@Override
public CheckApi id(CheckerUuid checkerUuid) throws RestApiException {
try {
- CheckKey checkKey =
- CheckKey.create(
- revisionResource.getProject(), revisionResource.getPatchSet().getId(), checkerUuid);
- Optional<Check> check = checks.getCheck(checkKey);
return checkApiImplFactory.create(
- new CheckResource(
- revisionResource,
- check.orElseThrow(
- () ->
- new ResourceNotFoundException(
- String.format(
- "Patch set %s in project %s doesn't have check for checker %s.",
- revisionResource.getPatchSet().getId(),
- revisionResource.getProject(),
- checkerUuid)))));
+ checksCollection.parse(revisionResource, IdString.fromDecoded(checkerUuid.toString())));
} catch (Exception e) {
throw asRestApiException("Cannot parse check", e);
}
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 b36aaf7..416134b 100644
--- a/javatests/com/google/gerrit/plugins/checks/acceptance/api/GetCheckIT.java
+++ b/javatests/com/google/gerrit/plugins/checks/acceptance/api/GetCheckIT.java
@@ -14,19 +14,27 @@
package com.google.gerrit.plugins.checks.acceptance.api;
+import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assert_;
+import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
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.acceptance.AbstractCheckersTest;
import com.google.gerrit.plugins.checks.api.CheckInfo;
import com.google.gerrit.plugins.checks.api.CheckState;
+import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
+import java.sql.Timestamp;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.RefUpdate;
@@ -37,10 +45,12 @@
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();
}
@@ -116,15 +126,47 @@
}
@Test
- public void getNonExistingCheck() throws Exception {
- CheckerUuid checkerUuid = checkerOperations.newChecker().repository(project).create();
+ public void getNonExistingCheckBackfillsForRelevantChecker() throws Exception {
+ String topic = name("topic");
+ Change.Id changeId = patchSetId.getParentKey();
+ CheckerUuid checkerUuid =
+ checkerOperations.newChecker().repository(project).query("topic:" + topic).create();
+ CheckKey checkKey = CheckKey.create(project, patchSetId, checkerUuid);
- exception.expect(ResourceNotFoundException.class);
- exception.expectMessage(
- String.format(
- "Patch set %s in project %s doesn't have check for checker %s.",
- patchSetId, project, checkerUuid));
- checksApiFactory.revision(patchSetId).id(checkerUuid);
+ assertCheckNotFound(patchSetId, checkerUuid);
+
+ 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()));
+ }
+
+ @Test
+ public void getNonExistingCheckDoesNotBackfillForDisabledChecker() throws Exception {
+ Change.Id changeId = patchSetId.getParentKey();
+ CheckerUuid checkerUuid = checkerOperations.newChecker().repository(project).create();
+ 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()));
+
+ checkerOperations.checker(checkerUuid).forUpdate().disable().update();
+
+ assertCheckNotFound(patchSetId, checkerUuid);
}
@Test
@@ -147,6 +189,12 @@
assertThat(checkInfo).isEqualTo(checkOperations.check(checkKey).asInfo());
}
+ private Timestamp getPatchSetCreated(Change.Id changeId) throws RestApiException {
+ return getOnlyElement(
+ gApi.changes().id(changeId.get()).get(CURRENT_REVISION).revisions.values())
+ .created;
+ }
+
private void deleteCheckerRef(CheckerUuid checkerUuid) throws Exception {
try (Repository allProjectsRepo = repoManager.openRepository(allProjects)) {
TestRepository<InMemoryRepository> testRepo =
@@ -156,4 +204,19 @@
assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
}
}
+
+ private void assertCheckNotFound(PatchSet.Id patchSetId, CheckerUuid checkerUuid)
+ throws Exception {
+ try {
+ checksApiFactory.revision(patchSetId).id(checkerUuid);
+ assert_().fail("expected ResourceNotFoundException");
+ } catch (ResourceNotFoundException e) {
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ String.format(
+ "Patch set %s in project %s doesn't have check for checker %s.",
+ patchSetId, project, checkerUuid));
+ }
+ }
}