LCOV - code coverage report
Current view: top level - server/project - SubmitRequirementConfigValidator.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 35 40 87.5 %
Date: 2022-11-19 15:00:39 Functions: 6 6 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2022 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 com.google.common.collect.ImmutableList;
      18             : import com.google.gerrit.entities.RefNames;
      19             : import com.google.gerrit.entities.SubmitRequirement;
      20             : import com.google.gerrit.server.events.CommitReceivedEvent;
      21             : import com.google.gerrit.server.git.validators.CommitValidationException;
      22             : import com.google.gerrit.server.git.validators.CommitValidationListener;
      23             : import com.google.gerrit.server.git.validators.CommitValidationMessage;
      24             : import com.google.gerrit.server.git.validators.ValidationMessage;
      25             : import com.google.gerrit.server.patch.DiffNotAvailableException;
      26             : import com.google.gerrit.server.patch.DiffOperations;
      27             : import com.google.gerrit.server.patch.DiffOptions;
      28             : import com.google.inject.Inject;
      29             : import java.io.IOException;
      30             : import java.util.List;
      31             : import java.util.stream.Collectors;
      32             : import org.eclipse.jgit.errors.ConfigInvalidException;
      33             : 
      34             : /**
      35             :  * Validates the expressions of submit requirements in {@code project.config}.
      36             :  *
      37             :  * <p>Other validation of submit requirements is done in {@link ProjectConfig}, see {@code
      38             :  * ProjectConfig#loadSubmitRequirementSections(Config)}.
      39             :  *
      40             :  * <p>The validation of the expressions cannot be in {@link ProjectConfig} as it requires injecting
      41             :  * {@link SubmitRequirementsEvaluator} and we cannot do injections into {@link ProjectConfig} (since
      42             :  * {@link ProjectConfig} is cached in the project cache).
      43             :  */
      44             : public class SubmitRequirementConfigValidator implements CommitValidationListener {
      45             :   private final DiffOperations diffOperations;
      46             :   private final ProjectConfig.Factory projectConfigFactory;
      47             :   private final SubmitRequirementExpressionsValidator submitRequirementExpressionsValidator;
      48             : 
      49             :   @Inject
      50             :   SubmitRequirementConfigValidator(
      51             :       DiffOperations diffOperations,
      52             :       ProjectConfig.Factory projectConfigFactory,
      53         109 :       SubmitRequirementExpressionsValidator submitRequirementExpressionsValidator) {
      54         109 :     this.diffOperations = diffOperations;
      55         109 :     this.projectConfigFactory = projectConfigFactory;
      56         109 :     this.submitRequirementExpressionsValidator = submitRequirementExpressionsValidator;
      57         109 :   }
      58             : 
      59             :   @Override
      60             :   public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent event)
      61             :       throws CommitValidationException {
      62             :     try {
      63         109 :       if (!event.refName.equals(RefNames.REFS_CONFIG)
      64          13 :           || !isFileChanged(event, ProjectConfig.PROJECT_CONFIG)) {
      65             :         // the project.config file in refs/meta/config was not modified, hence we do not need to
      66             :         // validate the submit requirements in it
      67         109 :         return ImmutableList.of();
      68             :       }
      69             : 
      70           6 :       ProjectConfig projectConfig = getProjectConfig(event);
      71           6 :       ImmutableList.Builder<String> validationMsgsBuilder = ImmutableList.builder();
      72             :       for (SubmitRequirement submitRequirement :
      73           6 :           projectConfig.getSubmitRequirementSections().values()) {
      74           2 :         validationMsgsBuilder.addAll(
      75           2 :             submitRequirementExpressionsValidator.validateExpressions(submitRequirement));
      76           2 :       }
      77           6 :       ImmutableList<String> validationMsgs = validationMsgsBuilder.build();
      78           6 :       if (!validationMsgs.isEmpty()) {
      79           1 :         throw new CommitValidationException(
      80           1 :             String.format(
      81             :                 "invalid submit requirement expressions in %s (revision = %s)",
      82           1 :                 ProjectConfig.PROJECT_CONFIG, projectConfig.getRevision().name()),
      83             :             new ImmutableList.Builder<CommitValidationMessage>()
      84           1 :                 .add(
      85             :                     new CommitValidationMessage(
      86             :                         "Invalid project configuration", ValidationMessage.Type.ERROR))
      87           1 :                 .addAll(
      88           1 :                     validationMsgs.stream()
      89           1 :                         .map(m -> toCommitValidationMessage(m))
      90           1 :                         .collect(Collectors.toList()))
      91           1 :                 .build());
      92             :       }
      93           6 :       return ImmutableList.of();
      94           0 :     } catch (IOException | DiffNotAvailableException | ConfigInvalidException e) {
      95           0 :       throw new CommitValidationException(
      96           0 :           String.format(
      97             :               "failed to validate submit requirement expressions in %s for revision %s in ref %s"
      98             :                   + " of project %s",
      99             :               ProjectConfig.PROJECT_CONFIG,
     100           0 :               event.commit.getName(),
     101             :               RefNames.REFS_CONFIG,
     102           0 :               event.project.getNameKey()),
     103             :           e);
     104             :     }
     105             :   }
     106             : 
     107             :   private static CommitValidationMessage toCommitValidationMessage(String message) {
     108           1 :     return new CommitValidationMessage(message, ValidationMessage.Type.ERROR);
     109             :   }
     110             : 
     111             :   /**
     112             :    * Whether the given file was changed in the given revision.
     113             :    *
     114             :    * @param receiveEvent the receive event
     115             :    * @param fileName the name of the file
     116             :    */
     117             :   private boolean isFileChanged(CommitReceivedEvent receiveEvent, String fileName)
     118             :       throws DiffNotAvailableException {
     119          13 :     return diffOperations
     120          13 :         .listModifiedFilesAgainstParent(
     121          13 :             receiveEvent.project.getNameKey(),
     122             :             receiveEvent.commit,
     123             :             /* parentNum=*/ 0,
     124             :             DiffOptions.DEFAULTS)
     125          13 :         .keySet().stream()
     126          13 :         .anyMatch(fileName::equals);
     127             :   }
     128             : 
     129             :   private ProjectConfig getProjectConfig(CommitReceivedEvent receiveEvent)
     130             :       throws IOException, ConfigInvalidException {
     131           6 :     ProjectConfig projectConfig = projectConfigFactory.create(receiveEvent.project.getNameKey());
     132           6 :     projectConfig.load(receiveEvent.revWalk, receiveEvent.commit);
     133           6 :     return projectConfig;
     134             :   }
     135             : }

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