All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gerrit.server.rules.IgnoreSelfApprovalRule Maven / Gradle / Ivy

There is a newer version: 3.10.0-rc4
Show newest version
// 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.server.rules;

import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.LabelFunction;
import com.google.gerrit.common.data.LabelType;
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.config.FactoryModule;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * Rule to require an approval from a user that did not upload the current patch set or block
 * submission.
 */
@Singleton
public class IgnoreSelfApprovalRule implements SubmitRule {
  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
  private static final String E_UNABLE_TO_FETCH_UPLOADER = "Unable to fetch uploader";
  private static final String E_UNABLE_TO_FETCH_LABELS =
      "Unable to fetch labels and approvals for the change";

  public static class Module extends FactoryModule {
    @Override
    public void configure() {
      bind(SubmitRule.class)
          .annotatedWith(Exports.named("IgnoreSelfApprovalRule"))
          .to(IgnoreSelfApprovalRule.class);
    }
  }

  @Inject
  IgnoreSelfApprovalRule() {}

  @Override
  public Collection evaluate(ChangeData cd, SubmitRuleOptions options) {
    List labelTypes;
    List approvals;
    try {
      labelTypes = cd.getLabelTypes().getLabelTypes();
      approvals = cd.currentApprovals();
    } catch (OrmException e) {
      logger.atWarning().withCause(e).log(E_UNABLE_TO_FETCH_LABELS);
      return singletonRuleError(E_UNABLE_TO_FETCH_LABELS);
    }

    boolean shouldIgnoreSelfApproval = labelTypes.stream().anyMatch(l -> l.ignoreSelfApproval());
    if (!shouldIgnoreSelfApproval) {
      // Shortcut to avoid further processing if no label should ignore uploader approvals
      return ImmutableList.of();
    }

    Account.Id uploader;
    try {
      uploader = cd.currentPatchSet().getUploader();
    } catch (OrmException e) {
      logger.atWarning().withCause(e).log(E_UNABLE_TO_FETCH_UPLOADER);
      return singletonRuleError(E_UNABLE_TO_FETCH_UPLOADER);
    }

    SubmitRecord submitRecord = new SubmitRecord();
    submitRecord.status = SubmitRecord.Status.OK;
    submitRecord.labels = new ArrayList<>(labelTypes.size());
    submitRecord.requirements = new ArrayList<>();

    for (LabelType t : labelTypes) {
      if (!t.ignoreSelfApproval()) {
        // The default rules are enough in this case.
        continue;
      }

      LabelFunction labelFunction = t.getFunction();
      if (labelFunction == null) {
        continue;
      }

      Collection allApprovalsForLabel = filterApprovalsByLabel(approvals, t);
      SubmitRecord.Label allApprovalsCheckResult = labelFunction.check(t, allApprovalsForLabel);
      SubmitRecord.Label ignoreSelfApprovalCheckResult =
          labelFunction.check(t, filterOutPositiveApprovalsOfUser(allApprovalsForLabel, uploader));

      if (labelCheckPassed(allApprovalsCheckResult)
          && !labelCheckPassed(ignoreSelfApprovalCheckResult)) {
        // The label has a valid approval from the uploader and no other valid approval. Set the
        // label
        // to NOT_READY and indicate the need for non-uploader approval as requirement.
        submitRecord.labels.add(ignoreSelfApprovalCheckResult);
        submitRecord.status = SubmitRecord.Status.NOT_READY;
        // Add an additional requirement to be more descriptive on why the label counts as not
        // approved.
        submitRecord.requirements.add(
            SubmitRequirement.builder()
                .setFallbackText("Approval from non-uploader required")
                .setType("non_uploader_approval")
                .build());
      }
    }

    if (submitRecord.labels.isEmpty()) {
      return ImmutableList.of();
    }

    return ImmutableList.of(submitRecord);
  }

  private static boolean labelCheckPassed(SubmitRecord.Label label) {
    switch (label.status) {
      case OK:
      case MAY:
        return true;

      case NEED:
      case REJECT:
      case IMPOSSIBLE:
        return false;
    }
    return false;
  }

  private static Collection singletonRuleError(String reason) {
    SubmitRecord submitRecord = new SubmitRecord();
    submitRecord.errorMessage = reason;
    submitRecord.status = SubmitRecord.Status.RULE_ERROR;
    return ImmutableList.of(submitRecord);
  }

  @VisibleForTesting
  static Collection filterOutPositiveApprovalsOfUser(
      Collection approvals, Account.Id user) {
    return approvals
        .stream()
        .filter(input -> input.getValue() < 0 || !input.getAccountId().equals(user))
        .collect(toImmutableList());
  }

  @VisibleForTesting
  static Collection filterApprovalsByLabel(
      Collection approvals, LabelType t) {
    return approvals
        .stream()
        .filter(input -> input.getLabelId().get().equals(t.getLabelId().get()))
        .collect(toImmutableList());
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy