| // Copyright (C) 2020 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.codeowners.config; |
| |
| import static com.google.common.base.Preconditions.checkArgument; |
| import static com.google.common.base.Preconditions.checkState; |
| import static java.util.Objects.requireNonNull; |
| |
| import com.google.auto.value.AutoValue; |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.primitives.Ints; |
| import com.google.gerrit.entities.LabelType; |
| import com.google.gerrit.entities.PatchSetApproval; |
| import com.google.gerrit.server.project.ProjectState; |
| import java.util.Optional; |
| |
| /** |
| * Approval that is required for an action. |
| * |
| * <p>A required approval is used to represent: |
| * |
| * <ul> |
| * <li>the approval that is required from code owners to approve the files in a change (see {@link |
| * RequiredApprovalConfig}) |
| * <li>the approval that is required to override the code owners submit check (see {@link |
| * OverrideApprovalConfig}) |
| * </ul> |
| */ |
| @AutoValue |
| public abstract class RequiredApproval { |
| /** The label on which an approval is required. */ |
| public abstract LabelType labelType(); |
| |
| /** The voting value that is required on the {@link #labelType()} . */ |
| public abstract short value(); |
| |
| /** |
| * Whether the given patch set approval fulfills this required approval |
| * |
| * @param patchSetApproval the patch set approval for which it should be checked whether it is |
| * fulfilled by this required approval |
| */ |
| public boolean isApprovedBy(PatchSetApproval patchSetApproval) { |
| requireNonNull(patchSetApproval, "patchSetApproval"); |
| return labelType().getLabelId().equals(patchSetApproval.key().labelId()) |
| && patchSetApproval.value() >= value(); |
| } |
| |
| @Override |
| public final String toString() { |
| return labelType().getName() + "+" + value(); |
| } |
| |
| /** |
| * Parses a string-representation of a {@link RequiredApproval}. |
| * |
| * @param projectState the project for which the required approval should be parsed |
| * @param requiredApprovalString the string-representation of a {@link RequiredApproval}, must |
| * have the format "<label-name>+<label-vote>" |
| * @return the parsed {@link RequiredApproval} |
| * @throws IllegalArgumentException thrown if the given string cannot be parsed as {@link |
| * RequiredApproval} or if it contains an invalid label name or voting value |
| * @throws IllegalStateException thrown if the parsed label doesn't exist on the project, or if |
| * the parsed voting value is not allowed for the label |
| */ |
| static RequiredApproval parse(ProjectState projectState, String requiredApprovalString) |
| throws IllegalArgumentException, IllegalStateException { |
| requireNonNull(projectState, "projectState"); |
| requireNonNull(requiredApprovalString, "requiredApprovalString"); |
| |
| int pos = requiredApprovalString.lastIndexOf('+'); |
| checkArgument(pos > 0, "Invalid format, expected '<label-name>+<label-value>'."); |
| String labelName = requiredApprovalString.substring(0, pos); |
| LabelType.checkName(labelName); |
| |
| Optional<LabelType> labelType = |
| projectState.getLabelTypes().getLabelTypes().stream() |
| .filter(lt -> lt.getName().equals(labelName)) |
| .findFirst(); |
| checkState( |
| labelType.isPresent(), |
| "Label %s doesn't exist for project %s.", |
| labelName, |
| projectState.getName()); |
| |
| String votingValueString = requiredApprovalString.substring(pos + 1); |
| Integer votingValue = Ints.tryParse(votingValueString); |
| checkArgument(votingValue != null, "Invalid voting value: %s", votingValueString); |
| checkArgument(votingValue > 0, "Voting value must be positive: %s", votingValueString); |
| checkState( |
| labelType.get().getByValue().containsKey(votingValue.shortValue()), |
| "Label %s on project %s doesn't allow value %s.", |
| labelName, |
| projectState.getName(), |
| votingValue); |
| |
| return create(labelType.get(), votingValue.shortValue()); |
| } |
| |
| /** |
| * Creates the default {@link RequiredApproval} if no {@link RequiredApproval} was configured. |
| * |
| * @param projectState the project for which the default required approval should be returned |
| * @param defaultLabel name of the default label |
| * @param defaultValue default value that is required for the default label |
| * @return the default {@link RequiredApproval} |
| * @throws IllegalStateException thrown if the default label doesn't exist on the project, or if |
| * the default voting value is not allowed for the label |
| */ |
| static RequiredApproval createDefault( |
| ProjectState projectState, String defaultLabel, short defaultValue) |
| throws IllegalStateException { |
| requireNonNull(projectState, "projectState"); |
| requireNonNull(defaultLabel, "defaultLabel"); |
| |
| Optional<LabelType> labelType = |
| projectState.getLabelTypes().getLabelTypes().stream() |
| .filter(lt -> lt.getName().equals(defaultLabel)) |
| .findFirst(); |
| checkState( |
| labelType.isPresent(), |
| "Default label %s doesn't exist for project %s.", |
| defaultLabel, |
| projectState.getName()); |
| checkState( |
| labelType.get().getByValue().containsKey(defaultValue), |
| "Label %s on project %s doesn't allow default value %s.", |
| defaultLabel, |
| projectState.getName(), |
| defaultValue); |
| return create(labelType.get(), defaultValue); |
| } |
| |
| @VisibleForTesting |
| public static RequiredApproval create(LabelType labelType, short vote) { |
| return new AutoValue_RequiredApproval(labelType, vote); |
| } |
| } |