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.restapi.change; 16 : 17 : import static com.google.gerrit.extensions.conditions.BooleanCondition.and; 18 : import static com.google.gerrit.server.project.ProjectCache.illegalState; 19 : 20 : import com.google.gerrit.entities.BranchNameKey; 21 : import com.google.gerrit.entities.RefNames; 22 : import com.google.gerrit.extensions.api.changes.CherryPickInput; 23 : import com.google.gerrit.extensions.common.ChangeInfo; 24 : import com.google.gerrit.extensions.restapi.BadRequestException; 25 : import com.google.gerrit.extensions.restapi.ResourceConflictException; 26 : import com.google.gerrit.extensions.restapi.Response; 27 : import com.google.gerrit.extensions.restapi.RestApiException; 28 : import com.google.gerrit.extensions.restapi.RestModifyView; 29 : import com.google.gerrit.extensions.webui.UiAction; 30 : import com.google.gerrit.server.change.ChangeJson; 31 : import com.google.gerrit.server.change.RevisionResource; 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.permissions.RefPermission; 36 : import com.google.gerrit.server.project.ContributorAgreementsChecker; 37 : import com.google.gerrit.server.project.InvalidChangeOperationException; 38 : import com.google.gerrit.server.project.NoSuchChangeException; 39 : import com.google.gerrit.server.project.NoSuchProjectException; 40 : import com.google.gerrit.server.project.ProjectCache; 41 : import com.google.gerrit.server.project.ProjectState; 42 : import com.google.gerrit.server.update.UpdateException; 43 : import com.google.inject.Inject; 44 : import com.google.inject.Singleton; 45 : import java.io.IOException; 46 : import org.eclipse.jgit.errors.ConfigInvalidException; 47 : 48 : @Singleton 49 : public class CherryPick 50 : implements RestModifyView<RevisionResource, CherryPickInput>, UiAction<RevisionResource> { 51 : private final PermissionBackend permissionBackend; 52 : private final CherryPickChange cherryPickChange; 53 : private final ChangeJson.Factory json; 54 : private final ContributorAgreementsChecker contributorAgreements; 55 : private final ProjectCache projectCache; 56 : 57 : @Inject 58 : CherryPick( 59 : PermissionBackend permissionBackend, 60 : CherryPickChange cherryPickChange, 61 : ChangeJson.Factory json, 62 : ContributorAgreementsChecker contributorAgreements, 63 145 : ProjectCache projectCache) { 64 145 : this.permissionBackend = permissionBackend; 65 145 : this.cherryPickChange = cherryPickChange; 66 145 : this.json = json; 67 145 : this.contributorAgreements = contributorAgreements; 68 145 : this.projectCache = projectCache; 69 145 : } 70 : 71 : @Override 72 : public Response<ChangeInfo> apply(RevisionResource rsrc, CherryPickInput input) 73 : throws IOException, UpdateException, RestApiException, PermissionBackendException, 74 : ConfigInvalidException, NoSuchProjectException { 75 8 : input.parent = input.parent == null ? 1 : input.parent; 76 8 : if (input.destination == null || input.destination.trim().isEmpty()) { 77 1 : throw new BadRequestException("destination must be non-empty"); 78 : } 79 : 80 7 : String refName = RefNames.fullName(input.destination); 81 6 : contributorAgreements.check(rsrc.getProject(), rsrc.getUser()); 82 : 83 6 : permissionBackend 84 6 : .currentUser() 85 6 : .project(rsrc.getChange().getProject()) 86 6 : .ref(refName) 87 6 : .check(RefPermission.CREATE_CHANGE); 88 6 : projectCache 89 6 : .get(rsrc.getProject()) 90 6 : .orElseThrow(illegalState(rsrc.getProject())) 91 6 : .checkStatePermitsWrite(); 92 : 93 : try { 94 6 : CherryPickChange.Result cherryPickResult = 95 6 : cherryPickChange.cherryPick( 96 6 : rsrc.getChange(), 97 6 : rsrc.getPatchSet(), 98 : input, 99 6 : BranchNameKey.create(rsrc.getProject(), refName)); 100 6 : ChangeInfo changeInfo = 101 6 : json.noOptions().format(rsrc.getProject(), cherryPickResult.changeId()); 102 6 : changeInfo.containsGitConflicts = 103 6 : !cherryPickResult.filesWithGitConflicts().isEmpty() ? true : null; 104 6 : return Response.ok(changeInfo); 105 1 : } catch (InvalidChangeOperationException e) { 106 1 : throw new BadRequestException(e.getMessage()); 107 0 : } catch (NoSuchChangeException e) { 108 0 : throw new ResourceConflictException(e.getMessage()); 109 : } 110 : } 111 : 112 : @Override 113 : public UiAction.Description getDescription(RevisionResource rsrc) { 114 57 : boolean projectStatePermitsWrite = 115 57 : projectCache.get(rsrc.getProject()).map(ProjectState::statePermitsWrite).orElse(false); 116 57 : return new UiAction.Description() 117 57 : .setLabel("Cherry Pick") 118 57 : .setTitle("Cherry pick change to a different branch") 119 57 : .setVisible( 120 57 : and( 121 57 : rsrc.isCurrent() && projectStatePermitsWrite, 122 : permissionBackend 123 57 : .currentUser() 124 57 : .project(rsrc.getProject()) 125 57 : .testCond(ProjectPermission.CREATE_CHANGE))); 126 : } 127 : }