From d32aedb347cc18289d64ee84d8ac66677e0bfdad Mon Sep 17 00:00:00 2001 From: Maxime Guerreiro Date: Thu, 22 Mar 2018 15:29:10 +0100 Subject: [PATCH] Expose the submit requirements in the Change API Add requirements in the Change API, allowing PolyGerrit and API consumers to use this information, and hopefuly improve communication with the final users (change owner and contributors). The API is thought to be both human and programs friendly, by exposing a human readable description, a "type" identifying accurately the requirement, and additional values in a key-value fashion. For instance, the PolyGerrit UI might use these informations to display each requirement with markup depending on its type, and fallback on the fallbackText if the type is unknown (hence the name). Change-Id: Ifb9e15a3c08ebaf42d8eb1469257c6e41cc22882 --- Documentation/rest-api-changes.txt | 29 +++++++ .../gerrit/extensions/common/ChangeInfo.java | 1 + .../common/SubmitRequirementInfo.java | 53 +++++++++++++ .../gerrit/server/change/ChangeJson.java | 21 +++++ .../api/change/ChangeSubmitRequirementIT.java | 78 +++++++++++++++++++ 5 files changed, 182 insertions(+) create mode 100644 java/com/google/gerrit/extensions/common/SubmitRequirementInfo.java create mode 100644 javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt index 8f889ac586..9a098364d5 100644 --- a/Documentation/rest-api-changes.txt +++ b/Documentation/rest-api-changes.txt @@ -5642,6 +5642,9 @@ AccountInfo] entity. Actions the caller might be able to perform on this revision. The information is a map of view name to link:#action-info[ActionInfo] entities. +|`requirements` |optional| +List of the link:rest-api-changes.html#requirement[requirements] to be met before this change +can be submitted. |`labels` |optional| The labels of the change as a map that maps the label names to link:#label-info[LabelInfo] entries. + @@ -6591,6 +6594,32 @@ describing the related changes. Sorted by git commit order, newest to oldest. Empty if there are no related changes. |=========================== + +[[requirement]] +=== Requirement +The `Requirement` entity contains information about a requirement relative to a change. + +type:: Alphanumerical (plus hyphens or underscores) string to identify what the requirement is and +why it was triggered. Can be seen as a class: requirements sharing the same type were created for a +similar reason, and the data structure will follow one set of rules. + +data:: (Optional) Additional key-value data linked to this requirement. This is used in templates to +render rich status messages. + +[options="header",cols="1,^1,5"] +|=========================== +|Field Name | |Description +|`status` | | Status of the requirement. Can be either `OK`, `NOT_READY` or `RULE_ERROR`. +|`fallbackText` | | A human readable reason +|`type` | | +Alphanumerical (plus hyphens or underscores) string to identify what the requirement is and why it +was triggered. Can be seen as a class: requirements sharing the same type were created for a similar +reason, and the data structure will follow one set of rules. +|`data` |optional| +Holds custom key-value strings, used in templates to render richer status messages +|=========================== + + [[restore-input]] === RestoreInput The `RestoreInput` entity contains information for restoring a change. diff --git a/java/com/google/gerrit/extensions/common/ChangeInfo.java b/java/com/google/gerrit/extensions/common/ChangeInfo.java index f802049762..c95dcc36f3 100644 --- a/java/com/google/gerrit/extensions/common/ChangeInfo.java +++ b/java/com/google/gerrit/extensions/common/ChangeInfo.java @@ -70,4 +70,5 @@ public class ChangeInfo { public List problems; public List plugins; public Collection trackingIds; + public Collection requirements; } diff --git a/java/com/google/gerrit/extensions/common/SubmitRequirementInfo.java b/java/com/google/gerrit/extensions/common/SubmitRequirementInfo.java new file mode 100644 index 0000000000..a940403dec --- /dev/null +++ b/java/com/google/gerrit/extensions/common/SubmitRequirementInfo.java @@ -0,0 +1,53 @@ +// Copyright (C) 2018 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.extensions.common; + +import java.util.Map; +import java.util.Objects; + +public class SubmitRequirementInfo { + public final String status; + public final String fallbackText; + public final String type; + public final Map data; + + public SubmitRequirementInfo( + String status, String fallbackText, String type, Map data) { + this.status = status; + this.fallbackText = fallbackText; + this.type = type; + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SubmitRequirementInfo)) { + return false; + } + SubmitRequirementInfo that = (SubmitRequirementInfo) o; + return Objects.equals(status, that.status) + && Objects.equals(fallbackText, that.fallbackText) + && Objects.equals(type, that.type) + && Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(status, fallbackText, type, data); + } +} diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java index 6b4f90f26a..e90703920f 100644 --- a/java/com/google/gerrit/server/change/ChangeJson.java +++ b/java/com/google/gerrit/server/change/ChangeJson.java @@ -63,6 +63,8 @@ import com.google.gerrit.common.data.LabelType; import com.google.gerrit.common.data.LabelTypes; import com.google.gerrit.common.data.LabelValue; import com.google.gerrit.common.data.SubmitRecord; +import com.google.gerrit.common.data.SubmitRecord.Status; +import com.google.gerrit.common.data.SubmitRequirement; import com.google.gerrit.common.data.SubmitTypeRecord; import com.google.gerrit.extensions.api.changes.FixInput; import com.google.gerrit.extensions.client.ListChangesOption; @@ -78,6 +80,7 @@ import com.google.gerrit.extensions.common.ProblemInfo; import com.google.gerrit.extensions.common.PushCertificateInfo; import com.google.gerrit.extensions.common.ReviewerUpdateInfo; import com.google.gerrit.extensions.common.RevisionInfo; +import com.google.gerrit.extensions.common.SubmitRequirementInfo; import com.google.gerrit.extensions.common.TrackingIdInfo; import com.google.gerrit.extensions.common.VotingRangeInfo; import com.google.gerrit.extensions.common.WebLinkInfo; @@ -425,6 +428,23 @@ public class ChangeJson { return out; } + private static Collection requirementsFor(ChangeData cd) { + Collection reqInfos = new ArrayList<>(); + for (SubmitRecord submitRecord : cd.submitRecords(SUBMIT_RULE_OPTIONS_STRICT)) { + if (submitRecord.requirements == null) { + continue; + } + for (SubmitRequirement requirement : submitRecord.requirements) { + reqInfos.add(requirementToInfo(requirement, submitRecord.status)); + } + } + return reqInfos; + } + + private static SubmitRequirementInfo requirementToInfo(SubmitRequirement req, Status status) { + return new SubmitRequirementInfo(status.name(), req.fallbackText(), req.type(), req.data()); + } + private void ensureLoaded(Iterable all) throws OrmException { if (lazyLoad) { ChangeData.ensureChangeLoaded(all); @@ -603,6 +623,7 @@ public class ChangeJson { } out.labels = labelsFor(cd, has(LABELS), has(DETAILED_LABELS)); + out.requirements = requirementsFor(cd); if (out.labels != null && has(DETAILED_LABELS)) { // If limited to specific patch sets but not the current patch set, don't diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java new file mode 100644 index 0000000000..f087b78c07 --- /dev/null +++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeSubmitRequirementIT.java @@ -0,0 +1,78 @@ +// Copyright (C) 2018 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.acceptance.api.change; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.PushOneCommit; +import com.google.gerrit.common.data.SubmitRecord; +import com.google.gerrit.common.data.SubmitRequirement; +import com.google.gerrit.extensions.annotations.Exports; +import com.google.gerrit.extensions.common.ChangeInfo; +import com.google.gerrit.extensions.common.SubmitRequirementInfo; +import com.google.gerrit.extensions.config.FactoryModule; +import com.google.gerrit.server.project.SubmitRuleOptions; +import com.google.gerrit.server.query.change.ChangeData; +import com.google.gerrit.server.rules.SubmitRule; +import com.google.inject.Module; +import java.util.ArrayList; +import java.util.Collection; +import org.junit.Test; + +public class ChangeSubmitRequirementIT extends AbstractDaemonTest { + private static final SubmitRequirement req = + SubmitRequirement.builder() + .setType("custom_rule") + .setFallbackText("Fallback text") + .addCustomValue("key", "value") + .build(); + private static final SubmitRequirementInfo reqInfo = + new SubmitRequirementInfo( + "NOT_READY", "Fallback text", "custom_rule", ImmutableMap.of("key", "value")); + + @Override + public Module createModule() { + return new FactoryModule() { + @Override + public void configure() { + bind(SubmitRule.class) + .annotatedWith(Exports.named("CustomSubmitRule")) + .to(CustomSubmitRule.class); + } + }; + } + + @Test + public void checkSubmitRequirementIsPropagated() throws Exception { + PushOneCommit.Result r = createChange(); + + ChangeInfo result = gApi.changes().id(r.getChangeId()).get(); + assertThat(result.requirements).containsExactly(reqInfo); + } + + private static class CustomSubmitRule implements SubmitRule { + @Override + public Collection evaluate(ChangeData changeData, SubmitRuleOptions options) { + SubmitRecord record = new SubmitRecord(); + record.labels = new ArrayList<>(); + record.status = SubmitRecord.Status.NOT_READY; + record.requirements = ImmutableList.of(req); + return ImmutableList.of(record); + } + } +}