| // Copyright (C) 2021 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.entities; |
| |
| import com.google.auto.value.AutoValue; |
| import com.google.auto.value.extension.memoized.Memoized; |
| import com.google.gerrit.common.Nullable; |
| import com.google.gson.Gson; |
| import com.google.gson.TypeAdapter; |
| import java.util.Optional; |
| import org.eclipse.jgit.lib.ObjectId; |
| |
| /** Result of evaluating a {@link SubmitRequirement} on a given Change. */ |
| @AutoValue |
| public abstract class SubmitRequirementResult { |
| /** Submit requirement for which this result is evaluated. */ |
| public abstract SubmitRequirement submitRequirement(); |
| |
| /** Result of evaluating a {@link SubmitRequirement#applicabilityExpression()} on a change. */ |
| public abstract Optional<SubmitRequirementExpressionResult> applicabilityExpressionResult(); |
| |
| /** |
| * Result of evaluating a {@link SubmitRequirement#submittabilityExpression()} on a change. |
| * |
| * <p>Empty if submit requirement does not apply. |
| */ |
| public abstract Optional<SubmitRequirementExpressionResult> submittabilityExpressionResult(); |
| |
| /** |
| * Result of evaluating a {@link SubmitRequirement#overrideExpression()} on a change. |
| * |
| * <p>Empty if submit requirement does not apply, or if the submit requirement did not define an |
| * override expression. |
| */ |
| public abstract Optional<SubmitRequirementExpressionResult> overrideExpressionResult(); |
| |
| /** SHA-1 of the patchset commit ID for which the submit requirement was evaluated. */ |
| public abstract ObjectId patchSetCommitId(); |
| |
| /** |
| * Whether this result was created from a legacy {@link SubmitRecord}, or by evaluating a {@link |
| * SubmitRequirement}. |
| * |
| * <p>If equals {@link Optional#empty()}, we treat the result as non-legacy (false). |
| */ |
| public abstract Optional<Boolean> legacy(); |
| |
| public boolean isLegacy() { |
| return legacy().orElse(false); |
| } |
| |
| /** |
| * Boolean indicating if the "submit requirement" was bypassed during submission, e.g. by |
| * performing a push with the %submit option. |
| */ |
| public abstract Optional<Boolean> forced(); |
| |
| /** |
| * Whether this result should be filtered out when returned from REST API. |
| * |
| * <p>This can be used by {@link |
| * com.google.gerrit.server.project.OnStoreSubmitRequirementResultModifier}. It can override the |
| * {@code SubmitRequirementResult} status and might want to hide the SR from the API as if it was |
| * non-applicable (non-applicable SRs are currently hidden on UI). |
| */ |
| public abstract Optional<Boolean> hidden(); |
| |
| public boolean isHidden() { |
| return hidden().orElse(false); |
| } |
| |
| public Optional<String> errorMessage() { |
| if (!status().equals(Status.ERROR)) { |
| return Optional.empty(); |
| } |
| if (applicabilityExpressionResult().isPresent() |
| && applicabilityExpressionResult().get().errorMessage().isPresent()) { |
| return Optional.of( |
| "Applicability expression result has an error: " |
| + applicabilityExpressionResult().get().errorMessage().get()); |
| } |
| if (submittabilityExpressionResult().isPresent() |
| && submittabilityExpressionResult().get().errorMessage().isPresent()) { |
| return Optional.of( |
| "Submittability expression result has an error: " |
| + submittabilityExpressionResult().get().errorMessage().get()); |
| } |
| if (overrideExpressionResult().isPresent() |
| && overrideExpressionResult().get().errorMessage().isPresent()) { |
| return Optional.of( |
| "Override expression result has an error: " |
| + overrideExpressionResult().get().errorMessage().get()); |
| } |
| return Optional.of("No error logged."); |
| } |
| |
| @Memoized |
| public Status status() { |
| if (forced().orElse(false)) { |
| return Status.FORCED; |
| } else if (assertError(submittabilityExpressionResult()) |
| || assertError(applicabilityExpressionResult()) |
| || assertError(overrideExpressionResult())) { |
| return Status.ERROR; |
| } else if (assertFail(applicabilityExpressionResult())) { |
| return Status.NOT_APPLICABLE; |
| } else if (assertPass(overrideExpressionResult())) { |
| return Status.OVERRIDDEN; |
| } else if (assertPass(submittabilityExpressionResult())) { |
| return Status.SATISFIED; |
| } else { |
| return Status.UNSATISFIED; |
| } |
| } |
| |
| /** Returns true if the submit requirement is fulfilled and can allow change submission. */ |
| @Memoized |
| public boolean fulfilled() { |
| Status s = status(); |
| return s == Status.SATISFIED |
| || s == Status.OVERRIDDEN |
| || s == Status.NOT_APPLICABLE |
| || s == Status.FORCED; |
| } |
| |
| public static Builder builder() { |
| return new AutoValue_SubmitRequirementResult.Builder(); |
| } |
| |
| public abstract Builder toBuilder(); |
| |
| public static TypeAdapter<SubmitRequirementResult> typeAdapter(Gson gson) { |
| return new AutoValue_SubmitRequirementResult.GsonTypeAdapter(gson); |
| } |
| |
| public enum Status { |
| /** Submit requirement is fulfilled. */ |
| SATISFIED, |
| |
| /** |
| * Submit requirement is not satisfied. Happens when {@link |
| * SubmitRequirement#submittabilityExpression()} evaluates to false. |
| */ |
| UNSATISFIED, |
| |
| /** |
| * Submit requirement is overridden. Happens when {@link SubmitRequirement#overrideExpression()} |
| * evaluates to true. |
| */ |
| OVERRIDDEN, |
| |
| /** |
| * Submit requirement is not applicable for a given change. Happens when {@link |
| * SubmitRequirement#applicabilityExpression()} evaluates to false. |
| */ |
| NOT_APPLICABLE, |
| |
| /** |
| * Any of the applicability, submittability or override expressions contain invalid syntax and |
| * are not parsable. |
| */ |
| ERROR, |
| |
| /** |
| * The "submit requirement" was bypassed during submission, e.g. by pushing for review with the |
| * %submit option. |
| */ |
| FORCED |
| } |
| |
| @AutoValue.Builder |
| public abstract static class Builder { |
| public abstract Builder submitRequirement(SubmitRequirement submitRequirement); |
| |
| public abstract Builder applicabilityExpressionResult( |
| Optional<SubmitRequirementExpressionResult> value); |
| |
| public abstract Builder submittabilityExpressionResult( |
| Optional<SubmitRequirementExpressionResult> value); |
| |
| public abstract Builder submittabilityExpressionResult( |
| @Nullable SubmitRequirementExpressionResult value); |
| |
| public abstract Builder overrideExpressionResult( |
| Optional<SubmitRequirementExpressionResult> value); |
| |
| public abstract Builder patchSetCommitId(ObjectId value); |
| |
| public abstract Builder legacy(Optional<Boolean> value); |
| |
| public abstract Builder forced(Optional<Boolean> value); |
| |
| public abstract Builder hidden(Optional<Boolean> value); |
| |
| public abstract SubmitRequirementResult build(); |
| } |
| |
| public static boolean assertPass(Optional<SubmitRequirementExpressionResult> expressionResult) { |
| return assertStatus(expressionResult, SubmitRequirementExpressionResult.Status.PASS); |
| } |
| |
| public static boolean assertFail(Optional<SubmitRequirementExpressionResult> expressionResult) { |
| return assertStatus(expressionResult, SubmitRequirementExpressionResult.Status.FAIL); |
| } |
| |
| public static boolean assertError(Optional<SubmitRequirementExpressionResult> expressionResult) { |
| return assertStatus(expressionResult, SubmitRequirementExpressionResult.Status.ERROR); |
| } |
| |
| private static boolean assertStatus( |
| SubmitRequirementExpressionResult expressionResult, |
| SubmitRequirementExpressionResult.Status status) { |
| return expressionResult.status() == status; |
| } |
| |
| private static boolean assertStatus( |
| Optional<SubmitRequirementExpressionResult> expressionResult, |
| SubmitRequirementExpressionResult.Status status) { |
| return expressionResult.isPresent() && assertStatus(expressionResult.get(), status); |
| } |
| } |