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