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.restapi.project; 16 : 17 : import com.google.common.base.Strings; 18 : import com.google.gerrit.entities.SubmitRequirement; 19 : import com.google.gerrit.entities.SubmitRequirementExpression; 20 : import com.google.gerrit.extensions.common.SubmitRequirementInfo; 21 : import com.google.gerrit.extensions.common.SubmitRequirementInput; 22 : import com.google.gerrit.extensions.restapi.AuthException; 23 : import com.google.gerrit.extensions.restapi.BadRequestException; 24 : import com.google.gerrit.extensions.restapi.Response; 25 : import com.google.gerrit.extensions.restapi.RestModifyView; 26 : import com.google.gerrit.server.CurrentUser; 27 : import com.google.gerrit.server.git.meta.MetaDataUpdate; 28 : import com.google.gerrit.server.permissions.PermissionBackend; 29 : import com.google.gerrit.server.permissions.PermissionBackendException; 30 : import com.google.gerrit.server.permissions.ProjectPermission; 31 : import com.google.gerrit.server.project.ProjectCache; 32 : import com.google.gerrit.server.project.ProjectConfig; 33 : import com.google.gerrit.server.project.SubmitRequirementExpressionsValidator; 34 : import com.google.gerrit.server.project.SubmitRequirementJson; 35 : import com.google.gerrit.server.project.SubmitRequirementResource; 36 : import com.google.gerrit.server.project.SubmitRequirementsUtil; 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.List; 42 : import java.util.Optional; 43 : import org.eclipse.jgit.errors.ConfigInvalidException; 44 : 45 : /** 46 : * A rest modify view that updates the definition of an existing submit requirement for a project. 47 : */ 48 : @Singleton 49 : public class UpdateSubmitRequirement 50 : implements RestModifyView<SubmitRequirementResource, SubmitRequirementInput> { 51 : private final Provider<CurrentUser> user; 52 : private final PermissionBackend permissionBackend; 53 : private final MetaDataUpdate.User updateFactory; 54 : private final ProjectConfig.Factory projectConfigFactory; 55 : private final ProjectCache projectCache; 56 : private final SubmitRequirementExpressionsValidator submitRequirementExpressionsValidator; 57 : 58 : @Inject 59 : public UpdateSubmitRequirement( 60 : Provider<CurrentUser> user, 61 : PermissionBackend permissionBackend, 62 : MetaDataUpdate.User updateFactory, 63 : ProjectConfig.Factory projectConfigFactory, 64 : ProjectCache projectCache, 65 138 : SubmitRequirementExpressionsValidator submitRequirementExpressionsValidator) { 66 138 : this.user = user; 67 138 : this.permissionBackend = permissionBackend; 68 138 : this.updateFactory = updateFactory; 69 138 : this.projectConfigFactory = projectConfigFactory; 70 138 : this.projectCache = projectCache; 71 138 : this.submitRequirementExpressionsValidator = submitRequirementExpressionsValidator; 72 138 : } 73 : 74 : @Override 75 : public Response<SubmitRequirementInfo> apply( 76 : SubmitRequirementResource rsrc, SubmitRequirementInput input) 77 : throws AuthException, BadRequestException, PermissionBackendException, IOException { 78 2 : if (!user.get().isIdentifiedUser()) { 79 0 : throw new AuthException("Authentication required"); 80 : } 81 : 82 2 : permissionBackend 83 2 : .currentUser() 84 2 : .project(rsrc.getProject().getNameKey()) 85 2 : .check(ProjectPermission.WRITE_CONFIG); 86 : 87 2 : if (input == null) { 88 0 : input = new SubmitRequirementInput(); 89 : } 90 : 91 2 : if (input.name != null && !input.name.equals(rsrc.getSubmitRequirement().name())) { 92 0 : throw new BadRequestException("name in input must match name in URL"); 93 : } 94 : 95 2 : try (MetaDataUpdate md = updateFactory.create(rsrc.getProject().getNameKey())) { 96 2 : ProjectConfig config = projectConfigFactory.read(md); 97 : 98 2 : SubmitRequirement submitRequirement = 99 1 : createSubmitRequirement(config, rsrc.getSubmitRequirement().name(), input); 100 : 101 1 : md.setMessage(String.format("Update Submit Requirement %s", submitRequirement.name())); 102 1 : config.commit(md); 103 : 104 1 : projectCache.evict(rsrc.getProject().getNameKey()); 105 : 106 1 : return Response.created(SubmitRequirementJson.format(submitRequirement)); 107 0 : } catch (ConfigInvalidException e) { 108 0 : throw new IOException("Failed to read project config", e); 109 : } 110 : } 111 : 112 : public SubmitRequirement createSubmitRequirement( 113 : ProjectConfig config, String name, SubmitRequirementInput input) throws BadRequestException { 114 2 : validateSRName(name); 115 2 : if (Strings.isNullOrEmpty(input.submittabilityExpression)) { 116 2 : throw new BadRequestException("submittability_expression is required"); 117 : } 118 1 : if (input.allowOverrideInChildProjects == null) { 119 : // default is false 120 0 : input.allowOverrideInChildProjects = false; 121 : } 122 : SubmitRequirement submitRequirement = 123 1 : SubmitRequirement.builder() 124 1 : .setName(name) 125 1 : .setDescription(Optional.ofNullable(input.description)) 126 1 : .setApplicabilityExpression( 127 1 : SubmitRequirementExpression.of(input.applicabilityExpression)) 128 1 : .setSubmittabilityExpression( 129 1 : SubmitRequirementExpression.create(input.submittabilityExpression)) 130 1 : .setOverrideExpression(SubmitRequirementExpression.of(input.overrideExpression)) 131 1 : .setAllowOverrideInChildProjects(input.allowOverrideInChildProjects) 132 1 : .build(); 133 : 134 1 : List<String> validationMessages = 135 1 : submitRequirementExpressionsValidator.validateExpressions(submitRequirement); 136 1 : if (!validationMessages.isEmpty()) { 137 1 : throw new BadRequestException( 138 1 : String.format("Invalid submit requirement input: %s", validationMessages)); 139 : } 140 : 141 1 : config.upsertSubmitRequirement(submitRequirement); 142 1 : return submitRequirement; 143 : } 144 : 145 : private void validateSRName(String name) throws BadRequestException { 146 : try { 147 2 : SubmitRequirementsUtil.validateName(name); 148 0 : } catch (IllegalArgumentException e) { 149 0 : throw new BadRequestException(e.getMessage(), e); 150 2 : } 151 2 : } 152 : }