LCOV - code coverage report
Current view: top level - server/restapi/project - SetLabel.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 93 96 96.9 %
Date: 2022-11-19 15:00:39 Functions: 3 3 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2019 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.restapi.project;
      16             : 
      17             : import com.google.common.base.Strings;
      18             : import com.google.gerrit.entities.LabelType;
      19             : import com.google.gerrit.extensions.common.LabelDefinitionInfo;
      20             : import com.google.gerrit.extensions.common.LabelDefinitionInput;
      21             : import com.google.gerrit.extensions.restapi.AuthException;
      22             : import com.google.gerrit.extensions.restapi.BadRequestException;
      23             : import com.google.gerrit.extensions.restapi.ResourceConflictException;
      24             : import com.google.gerrit.extensions.restapi.Response;
      25             : import com.google.gerrit.extensions.restapi.RestModifyView;
      26             : import com.google.gerrit.index.query.QueryParseException;
      27             : import com.google.gerrit.server.CurrentUser;
      28             : import com.google.gerrit.server.git.meta.MetaDataUpdate;
      29             : import com.google.gerrit.server.permissions.PermissionBackend;
      30             : import com.google.gerrit.server.permissions.PermissionBackendException;
      31             : import com.google.gerrit.server.permissions.ProjectPermission;
      32             : import com.google.gerrit.server.project.LabelDefinitionJson;
      33             : import com.google.gerrit.server.project.LabelResource;
      34             : import com.google.gerrit.server.project.ProjectCache;
      35             : import com.google.gerrit.server.project.ProjectConfig;
      36             : import com.google.gerrit.server.query.approval.ApprovalQueryBuilder;
      37             : import com.google.inject.Inject;
      38             : import com.google.inject.Provider;
      39             : import com.google.inject.Singleton;
      40             : import java.io.IOException;
      41             : import java.util.Optional;
      42             : import org.eclipse.jgit.errors.ConfigInvalidException;
      43             : 
      44             : @Singleton
      45             : public class SetLabel implements RestModifyView<LabelResource, LabelDefinitionInput> {
      46             :   private final Provider<CurrentUser> user;
      47             :   private final PermissionBackend permissionBackend;
      48             :   private final MetaDataUpdate.User updateFactory;
      49             :   private final ProjectConfig.Factory projectConfigFactory;
      50             :   private final ProjectCache projectCache;
      51             :   private final ApprovalQueryBuilder approvalQueryBuilder;
      52             : 
      53             :   @Inject
      54             :   public SetLabel(
      55             :       Provider<CurrentUser> user,
      56             :       PermissionBackend permissionBackend,
      57             :       MetaDataUpdate.User updateFactory,
      58             :       ProjectConfig.Factory projectConfigFactory,
      59             :       ProjectCache projectCache,
      60         146 :       ApprovalQueryBuilder approvalQueryBuilder) {
      61         146 :     this.user = user;
      62         146 :     this.permissionBackend = permissionBackend;
      63         146 :     this.updateFactory = updateFactory;
      64         146 :     this.projectConfigFactory = projectConfigFactory;
      65         146 :     this.projectCache = projectCache;
      66         146 :     this.approvalQueryBuilder = approvalQueryBuilder;
      67         146 :   }
      68             : 
      69             :   @Override
      70             :   public Response<LabelDefinitionInfo> apply(LabelResource rsrc, LabelDefinitionInput input)
      71             :       throws AuthException, BadRequestException, ResourceConflictException,
      72             :           PermissionBackendException, IOException, ConfigInvalidException {
      73           2 :     if (!user.get().isIdentifiedUser()) {
      74           0 :       throw new AuthException("Authentication required");
      75             :     }
      76             : 
      77           2 :     permissionBackend
      78           2 :         .currentUser()
      79           2 :         .project(rsrc.getProject().getNameKey())
      80           2 :         .check(ProjectPermission.WRITE_CONFIG);
      81             : 
      82           2 :     if (input == null) {
      83           0 :       input = new LabelDefinitionInput();
      84             :     }
      85             : 
      86           2 :     LabelType labelType = rsrc.getLabelType();
      87             : 
      88           2 :     try (MetaDataUpdate md = updateFactory.create(rsrc.getProject().getNameKey())) {
      89           2 :       ProjectConfig config = projectConfigFactory.read(md);
      90             : 
      91           2 :       if (updateLabel(config, labelType, input)) {
      92           1 :         if (input.commitMessage != null) {
      93           1 :           md.setMessage(Strings.emptyToNull(input.commitMessage.trim()));
      94             :         } else {
      95           1 :           md.setMessage("Update label");
      96             :         }
      97           1 :         String newName = Strings.nullToEmpty(input.name).trim();
      98           1 :         labelType =
      99           1 :             config.getLabelSections().get(newName.isEmpty() ? labelType.getName() : newName);
     100             : 
     101           1 :         config.commit(md);
     102           1 :         projectCache.evictAndReindex(rsrc.getProject().getProjectState().getProject());
     103             :       }
     104             :     }
     105           2 :     return Response.ok(LabelDefinitionJson.format(rsrc.getProject().getNameKey(), labelType));
     106             :   }
     107             : 
     108             :   /**
     109             :    * Updates the given label.
     110             :    *
     111             :    * @param config the project config
     112             :    * @param labelType the label type that should be updated
     113             :    * @param input the input that describes the label update
     114             :    * @return whether the label type was modified
     115             :    * @throws BadRequestException if there was invalid data in the input
     116             :    * @throws ResourceConflictException if the update cannot be applied due to a conflict
     117             :    */
     118             :   public boolean updateLabel(ProjectConfig config, LabelType labelType, LabelDefinitionInput input)
     119             :       throws BadRequestException, ResourceConflictException {
     120           3 :     boolean dirty = false;
     121           3 :     LabelType.Builder labelTypeBuilder = labelType.toBuilder();
     122             : 
     123           3 :     if (input.name != null) {
     124           2 :       String newName = input.name.trim();
     125           2 :       if (newName.isEmpty()) {
     126           1 :         throw new BadRequestException("name cannot be empty");
     127             :       }
     128           2 :       if (!newName.equals(labelType.getName())) {
     129           2 :         if (config.getLabelSections().containsKey(newName)) {
     130           1 :           throw new ResourceConflictException(String.format("name %s already in use", newName));
     131             :         }
     132             : 
     133           2 :         for (String labelName : config.getLabelSections().keySet()) {
     134           2 :           if (labelName.equalsIgnoreCase(newName)) {
     135           1 :             throw new ResourceConflictException(
     136           1 :                 String.format("name %s conflicts with existing label %s", newName, labelName));
     137             :           }
     138           2 :         }
     139             : 
     140             :         try {
     141           2 :           LabelType.checkName(newName);
     142           1 :         } catch (IllegalArgumentException e) {
     143           1 :           throw new BadRequestException("invalid name: " + input.name, e);
     144           2 :         }
     145             : 
     146           2 :         labelTypeBuilder.setName(newName);
     147           2 :         dirty = true;
     148             :       }
     149             :     }
     150             : 
     151           3 :     if (input.description != null) {
     152           1 :       String description = Strings.emptyToNull(input.description.trim());
     153           1 :       labelTypeBuilder.setDescription(Optional.ofNullable(description));
     154           1 :       dirty = true;
     155             :     }
     156             : 
     157           3 :     if (input.function != null) {
     158           2 :       if (input.function.trim().isEmpty()) {
     159           1 :         throw new BadRequestException("function cannot be empty");
     160             :       }
     161           2 :       labelTypeBuilder.setFunction(LabelDefinitionInputParser.parseFunction(input.function));
     162           2 :       dirty = true;
     163             :     }
     164             : 
     165           3 :     if (input.values != null) {
     166           1 :       if (input.values.isEmpty()) {
     167           1 :         throw new BadRequestException("values cannot be empty");
     168             :       }
     169           1 :       labelTypeBuilder.setValues(LabelDefinitionInputParser.parseValues(input.values));
     170           1 :       dirty = true;
     171             :     }
     172             : 
     173           3 :     if (input.defaultValue != null) {
     174           1 :       labelTypeBuilder.setDefaultValue(
     175           1 :           LabelDefinitionInputParser.parseDefaultValue(labelTypeBuilder, input.defaultValue));
     176           1 :       dirty = true;
     177             :     }
     178             : 
     179           3 :     if (input.branches != null) {
     180           1 :       labelTypeBuilder.setRefPatterns(LabelDefinitionInputParser.parseBranches(input.branches));
     181           1 :       dirty = true;
     182             :     }
     183             : 
     184           3 :     if (input.canOverride != null) {
     185           1 :       labelTypeBuilder.setCanOverride(input.canOverride);
     186           1 :       dirty = true;
     187             :     }
     188             : 
     189           3 :     input.copyCondition = Strings.emptyToNull(input.copyCondition);
     190           3 :     if (input.copyCondition != null) {
     191             :       try {
     192           1 :         approvalQueryBuilder.parse(input.copyCondition);
     193           1 :       } catch (QueryParseException e) {
     194           1 :         throw new BadRequestException(
     195           1 :             "unable to parse copy condition. got: " + input.copyCondition + ". " + e.getMessage(),
     196             :             e);
     197           1 :       }
     198           1 :       labelTypeBuilder.setCopyCondition(input.copyCondition);
     199           1 :       dirty = true;
     200           1 :       if (Boolean.TRUE.equals(input.unsetCopyCondition)) {
     201           0 :         throw new BadRequestException("can't set and unset copyCondition in the same request");
     202             :       }
     203             :     }
     204           3 :     if (Boolean.TRUE.equals(input.unsetCopyCondition)) {
     205           1 :       labelTypeBuilder.setCopyCondition(null);
     206           1 :       dirty = true;
     207             :     }
     208             : 
     209           3 :     if (input.allowPostSubmit != null) {
     210           1 :       labelTypeBuilder.setAllowPostSubmit(input.allowPostSubmit);
     211           1 :       dirty = true;
     212             :     }
     213             : 
     214           3 :     if (input.ignoreSelfApproval != null) {
     215           1 :       labelTypeBuilder.setIgnoreSelfApproval(input.ignoreSelfApproval);
     216           1 :       dirty = true;
     217             :     }
     218             : 
     219           3 :     config.getLabelSections().remove(labelType.getName());
     220           3 :     config.upsertLabelType(labelTypeBuilder.build());
     221             : 
     222           3 :     return dirty;
     223             :   }
     224             : }

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