Line data Source code
1 : // Copyright (C) 2021 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.entities; 16 : 17 : import com.google.auto.value.AutoValue; 18 : import com.google.auto.value.extension.memoized.Memoized; 19 : import com.google.gerrit.common.Nullable; 20 : import com.google.gson.Gson; 21 : import com.google.gson.TypeAdapter; 22 : import java.util.Optional; 23 : import org.eclipse.jgit.lib.ObjectId; 24 : 25 : /** Result of evaluating a {@link SubmitRequirement} on a given Change. */ 26 : @AutoValue 27 104 : public abstract class SubmitRequirementResult { 28 : /** Submit requirement for which this result is evaluated. */ 29 : public abstract SubmitRequirement submitRequirement(); 30 : 31 : /** Result of evaluating a {@link SubmitRequirement#applicabilityExpression()} on a change. */ 32 : public abstract Optional<SubmitRequirementExpressionResult> applicabilityExpressionResult(); 33 : 34 : /** 35 : * Result of evaluating a {@link SubmitRequirement#submittabilityExpression()} on a change. 36 : * 37 : * <p>Empty if submit requirement does not apply. 38 : */ 39 : public abstract Optional<SubmitRequirementExpressionResult> submittabilityExpressionResult(); 40 : 41 : /** 42 : * Result of evaluating a {@link SubmitRequirement#overrideExpression()} on a change. 43 : * 44 : * <p>Empty if submit requirement does not apply, or if the submit requirement did not define an 45 : * override expression. 46 : */ 47 : public abstract Optional<SubmitRequirementExpressionResult> overrideExpressionResult(); 48 : 49 : /** SHA-1 of the patchset commit ID for which the submit requirement was evaluated. */ 50 : public abstract ObjectId patchSetCommitId(); 51 : 52 : /** 53 : * Whether this result was created from a legacy {@link SubmitRecord}, or by evaluating a {@link 54 : * SubmitRequirement}. 55 : * 56 : * <p>If equals {@link Optional#empty()}, we treat the result as non-legacy (false). 57 : */ 58 : public abstract Optional<Boolean> legacy(); 59 : 60 : public boolean isLegacy() { 61 103 : return legacy().orElse(false); 62 : } 63 : 64 : /** 65 : * Boolean indicating if the "submit requirement" was bypassed during submission, e.g. by 66 : * performing a push with the %submit option. 67 : */ 68 : public abstract Optional<Boolean> forced(); 69 : 70 : /** 71 : * Whether this result should be filtered out when returned from REST API. 72 : * 73 : * <p>This can be used by {@link 74 : * com.google.gerrit.server.project.OnStoreSubmitRequirementResultModifier}. It can override the 75 : * {@code SubmitRequirementResult} status and might want to hide the SR from the API as if it was 76 : * non-applicable (non-applicable SRs are currently hidden on UI). 77 : */ 78 : public abstract Optional<Boolean> hidden(); 79 : 80 : public boolean isHidden() { 81 103 : return hidden().orElse(false); 82 : } 83 : 84 : public Optional<String> errorMessage() { 85 14 : if (!status().equals(Status.ERROR)) { 86 13 : return Optional.empty(); 87 : } 88 1 : if (applicabilityExpressionResult().isPresent() 89 0 : && applicabilityExpressionResult().get().errorMessage().isPresent()) { 90 0 : return Optional.of( 91 : "Applicability expression result has an error: " 92 0 : + applicabilityExpressionResult().get().errorMessage().get()); 93 : } 94 1 : if (submittabilityExpressionResult().isPresent() 95 1 : && submittabilityExpressionResult().get().errorMessage().isPresent()) { 96 1 : return Optional.of( 97 : "Submittability expression result has an error: " 98 1 : + submittabilityExpressionResult().get().errorMessage().get()); 99 : } 100 0 : if (overrideExpressionResult().isPresent() 101 0 : && overrideExpressionResult().get().errorMessage().isPresent()) { 102 0 : return Optional.of( 103 : "Override expression result has an error: " 104 0 : + overrideExpressionResult().get().errorMessage().get()); 105 : } 106 0 : return Optional.of("No error logged."); 107 : } 108 : 109 : @Memoized 110 : public Status status() { 111 103 : if (forced().orElse(false)) { 112 9 : return Status.FORCED; 113 103 : } else if (assertError(submittabilityExpressionResult()) 114 103 : || assertError(applicabilityExpressionResult()) 115 103 : || assertError(overrideExpressionResult())) { 116 4 : return Status.ERROR; 117 103 : } else if (assertFail(applicabilityExpressionResult())) { 118 2 : return Status.NOT_APPLICABLE; 119 103 : } else if (assertPass(overrideExpressionResult())) { 120 2 : return Status.OVERRIDDEN; 121 103 : } else if (assertPass(submittabilityExpressionResult())) { 122 58 : return Status.SATISFIED; 123 : } else { 124 103 : return Status.UNSATISFIED; 125 : } 126 : } 127 : 128 : /** Returns true if the submit requirement is fulfilled and can allow change submission. */ 129 : @Memoized 130 : public boolean fulfilled() { 131 103 : Status s = status(); 132 103 : return s == Status.SATISFIED 133 : || s == Status.OVERRIDDEN 134 : || s == Status.NOT_APPLICABLE 135 : || s == Status.FORCED; 136 : } 137 : 138 : public static Builder builder() { 139 104 : return new AutoValue_SubmitRequirementResult.Builder(); 140 : } 141 : 142 : public abstract Builder toBuilder(); 143 : 144 : public static TypeAdapter<SubmitRequirementResult> typeAdapter(Gson gson) { 145 65 : return new AutoValue_SubmitRequirementResult.GsonTypeAdapter(gson); 146 : } 147 : 148 103 : public enum Status { 149 : /** Submit requirement is fulfilled. */ 150 103 : SATISFIED, 151 : 152 : /** 153 : * Submit requirement is not satisfied. Happens when {@link 154 : * SubmitRequirement#submittabilityExpression()} evaluates to false. 155 : */ 156 103 : UNSATISFIED, 157 : 158 : /** 159 : * Submit requirement is overridden. Happens when {@link SubmitRequirement#overrideExpression()} 160 : * evaluates to true. 161 : */ 162 103 : OVERRIDDEN, 163 : 164 : /** 165 : * Submit requirement is not applicable for a given change. Happens when {@link 166 : * SubmitRequirement#applicabilityExpression()} evaluates to false. 167 : */ 168 103 : NOT_APPLICABLE, 169 : 170 : /** 171 : * Any of the applicability, submittability or override expressions contain invalid syntax and 172 : * are not parsable. 173 : */ 174 103 : ERROR, 175 : 176 : /** 177 : * The "submit requirement" was bypassed during submission, e.g. by pushing for review with the 178 : * %submit option. 179 : */ 180 103 : FORCED 181 : } 182 : 183 : @AutoValue.Builder 184 104 : public abstract static class Builder { 185 : public abstract Builder submitRequirement(SubmitRequirement submitRequirement); 186 : 187 : public abstract Builder applicabilityExpressionResult( 188 : Optional<SubmitRequirementExpressionResult> value); 189 : 190 : public abstract Builder submittabilityExpressionResult( 191 : Optional<SubmitRequirementExpressionResult> value); 192 : 193 : public abstract Builder submittabilityExpressionResult( 194 : @Nullable SubmitRequirementExpressionResult value); 195 : 196 : public abstract Builder overrideExpressionResult( 197 : Optional<SubmitRequirementExpressionResult> value); 198 : 199 : public abstract Builder patchSetCommitId(ObjectId value); 200 : 201 : public abstract Builder legacy(Optional<Boolean> value); 202 : 203 : public abstract Builder forced(Optional<Boolean> value); 204 : 205 : public abstract Builder hidden(Optional<Boolean> value); 206 : 207 : public abstract SubmitRequirementResult build(); 208 : } 209 : 210 : public static boolean assertPass(Optional<SubmitRequirementExpressionResult> expressionResult) { 211 103 : return assertStatus(expressionResult, SubmitRequirementExpressionResult.Status.PASS); 212 : } 213 : 214 : public static boolean assertFail(Optional<SubmitRequirementExpressionResult> expressionResult) { 215 103 : return assertStatus(expressionResult, SubmitRequirementExpressionResult.Status.FAIL); 216 : } 217 : 218 : public static boolean assertError(Optional<SubmitRequirementExpressionResult> expressionResult) { 219 103 : return assertStatus(expressionResult, SubmitRequirementExpressionResult.Status.ERROR); 220 : } 221 : 222 : private static boolean assertStatus( 223 : SubmitRequirementExpressionResult expressionResult, 224 : SubmitRequirementExpressionResult.Status status) { 225 103 : return expressionResult.status() == status; 226 : } 227 : 228 : private static boolean assertStatus( 229 : Optional<SubmitRequirementExpressionResult> expressionResult, 230 : SubmitRequirementExpressionResult.Status status) { 231 103 : return expressionResult.isPresent() && assertStatus(expressionResult.get(), status); 232 : } 233 : }