LCOV - code coverage report
Current view: top level - server/project - SubmitRuleEvaluator.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 62 69 89.9 %
Date: 2022-11-19 15:00:39 Functions: 9 9 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2012 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.server.project;
      16             : 
      17             : import static com.google.common.collect.ImmutableList.toImmutableList;
      18             : 
      19             : import com.google.common.collect.Streams;
      20             : import com.google.common.flogger.FluentLogger;
      21             : import com.google.gerrit.entities.Change;
      22             : import com.google.gerrit.entities.Project;
      23             : import com.google.gerrit.entities.SubmitRecord;
      24             : import com.google.gerrit.entities.SubmitTypeRecord;
      25             : import com.google.gerrit.exceptions.StorageException;
      26             : import com.google.gerrit.extensions.api.changes.ChangeApi;
      27             : import com.google.gerrit.metrics.Description;
      28             : import com.google.gerrit.metrics.Description.Units;
      29             : import com.google.gerrit.metrics.MetricMaker;
      30             : import com.google.gerrit.metrics.Timer0;
      31             : import com.google.gerrit.server.change.ChangeJson;
      32             : import com.google.gerrit.server.index.OnlineReindexMode;
      33             : import com.google.gerrit.server.logging.CallerFinder;
      34             : import com.google.gerrit.server.plugincontext.PluginSetContext;
      35             : import com.google.gerrit.server.query.change.ChangeData;
      36             : import com.google.gerrit.server.rules.DefaultSubmitRule;
      37             : import com.google.gerrit.server.rules.PrologRule;
      38             : import com.google.gerrit.server.rules.SubmitRule;
      39             : import com.google.inject.Inject;
      40             : import com.google.inject.assistedinject.Assisted;
      41             : import java.util.List;
      42             : import java.util.Optional;
      43             : 
      44             : /**
      45             :  * Evaluates a submit-like Prolog rule found in the rules.pl file of the current project and filters
      46             :  * the results through rules found in the parent projects, all the way up to All-Projects.
      47             :  */
      48             : public class SubmitRuleEvaluator {
      49         103 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      50             : 
      51             :   private final ProjectCache projectCache;
      52             :   private final PrologRule prologRule;
      53             :   private final PluginSetContext<SubmitRule> submitRules;
      54             :   private final Timer0 submitRuleEvaluationLatency;
      55             :   private final Timer0 submitTypeEvaluationLatency;
      56             :   private final SubmitRuleOptions opts;
      57             :   private final CallerFinder callerFinder;
      58             : 
      59             :   public interface Factory {
      60             :     /** Returns a new {@link SubmitRuleEvaluator} with the specified options */
      61             :     SubmitRuleEvaluator create(SubmitRuleOptions options);
      62             :   }
      63             : 
      64             :   @Inject
      65             :   private SubmitRuleEvaluator(
      66             :       ProjectCache projectCache,
      67             :       PrologRule prologRule,
      68             :       PluginSetContext<SubmitRule> submitRules,
      69             :       MetricMaker metricMaker,
      70         103 :       @Assisted SubmitRuleOptions options) {
      71         103 :     this.projectCache = projectCache;
      72         103 :     this.prologRule = prologRule;
      73         103 :     this.submitRules = submitRules;
      74         103 :     this.submitRuleEvaluationLatency =
      75         103 :         metricMaker.newTimer(
      76             :             "change/submit_rule_evaluation",
      77             :             new Description("Latency for evaluating submit rules on a change.")
      78         103 :                 .setCumulative()
      79         103 :                 .setUnit(Units.MILLISECONDS));
      80         103 :     this.submitTypeEvaluationLatency =
      81         103 :         metricMaker.newTimer(
      82             :             "change/submit_type_evaluation",
      83             :             new Description("Latency for evaluating the submit type on a change.")
      84         103 :                 .setCumulative()
      85         103 :                 .setUnit(Units.MILLISECONDS));
      86             : 
      87         103 :     this.opts = options;
      88             : 
      89         103 :     this.callerFinder =
      90         103 :         CallerFinder.builder()
      91         103 :             .addTarget(ChangeApi.class)
      92         103 :             .addTarget(ChangeJson.class)
      93         103 :             .addTarget(ChangeData.class)
      94         103 :             .addTarget(SubmitRequirementsEvaluatorImpl.class)
      95         103 :             .build();
      96         103 :   }
      97             : 
      98             :   /**
      99             :    * Evaluate the submit rules.
     100             :    *
     101             :    * @return List of {@link SubmitRecord} objects returned from the evaluated rules, including any
     102             :    *     errors.
     103             :    * @param cd ChangeData to evaluate
     104             :    */
     105             :   public List<SubmitRecord> evaluate(ChangeData cd) {
     106         103 :     logger.atFine().log(
     107             :         "Evaluate submit rules for change %d (caller: %s)",
     108         103 :         cd.change().getId().get(), callerFinder.findCallerLazy());
     109         103 :     try (Timer0.Context ignored = submitRuleEvaluationLatency.start()) {
     110             :       Change change;
     111             :       ProjectState projectState;
     112             :       try {
     113         103 :         change = cd.change();
     114         103 :         if (change == null) {
     115           0 :           throw new StorageException("Change not found");
     116             :         }
     117             : 
     118         103 :         Project.NameKey name = cd.project();
     119         103 :         Optional<ProjectState> projectStateOptional = projectCache.get(name);
     120         103 :         if (!projectStateOptional.isPresent()) {
     121           0 :           throw new NoSuchProjectException(name);
     122             :         }
     123         103 :         projectState = projectStateOptional.get();
     124           0 :       } catch (NoSuchProjectException e) {
     125           0 :         throw new IllegalStateException("Unable to find project while evaluating submit rule", e);
     126         103 :       }
     127             : 
     128         103 :       if (change.isClosed() && (!opts.recomputeOnClosedChanges() || OnlineReindexMode.isActive())) {
     129          57 :         return cd.notes().getSubmitRecords().stream()
     130          57 :             .map(
     131             :                 r -> {
     132          54 :                   SubmitRecord record = r.deepCopy();
     133          54 :                   if (record.status == SubmitRecord.Status.OK) {
     134             :                     // Submit records that were OK when they got merged are CLOSED now.
     135          47 :                     record.status = SubmitRecord.Status.CLOSED;
     136             :                   }
     137          54 :                   return record;
     138             :                 })
     139          57 :             .collect(toImmutableList());
     140             :       }
     141             : 
     142             :       // We evaluate all the plugin-defined evaluators,
     143             :       // and then we collect the results in one list.
     144         103 :       return Streams.stream(submitRules)
     145             :           // Skip evaluating the default submit rule if the project has prolog rules.
     146             :           // Note that in this case, the prolog submit rule will handle labels for us
     147         103 :           .filter(
     148         103 :               projectState.hasPrologRules()
     149           5 :                   ? rule -> !(rule.get() instanceof DefaultSubmitRule)
     150         103 :                   : rule -> true)
     151         103 :           .map(
     152             :               c ->
     153         103 :                   c.call(
     154             :                       s -> {
     155         103 :                         Optional<SubmitRecord> record = s.evaluate(cd);
     156         103 :                         if (record.isPresent() && record.get().ruleName == null) {
     157             :                           // Only back-fill the ruleName if it was not populated by the "submit
     158             :                           // rule".
     159         103 :                           record.get().ruleName =
     160         103 :                               c.getPluginName() + "~" + s.getClass().getSimpleName();
     161             :                         }
     162         103 :                         return record;
     163             :                       }))
     164         103 :           .filter(Optional::isPresent)
     165         103 :           .map(Optional::get)
     166         103 :           .collect(toImmutableList());
     167          57 :     }
     168             :   }
     169             : 
     170             :   /**
     171             :    * Evaluate the submit type rules to get the submit type.
     172             :    *
     173             :    * @return record from the evaluated rules.
     174             :    */
     175             :   public SubmitTypeRecord getSubmitType(ChangeData cd) {
     176         103 :     try (Timer0.Context ignored = submitTypeEvaluationLatency.start()) {
     177             :       try {
     178         103 :         Project.NameKey name = cd.project();
     179         103 :         Optional<ProjectState> project = projectCache.get(name);
     180         103 :         if (!project.isPresent()) {
     181           0 :           throw new NoSuchProjectException(name);
     182             :         }
     183           0 :       } catch (NoSuchProjectException e) {
     184           0 :         throw new IllegalStateException("Unable to find project while evaluating submit rule", e);
     185         103 :       }
     186             : 
     187         103 :       return prologRule.getSubmitType(cd);
     188             :     }
     189             :   }
     190             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750