Line data Source code
1 : // Copyright (C) 2018 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 java.util.Objects.requireNonNull; 18 : 19 : import com.google.common.base.Strings; 20 : import com.google.gerrit.common.Nullable; 21 : import com.google.gerrit.entities.Account; 22 : import com.google.gerrit.entities.Change; 23 : import com.google.gerrit.entities.ChangeMessage; 24 : import com.google.gerrit.entities.PatchSet; 25 : import com.google.gerrit.entities.Project; 26 : import com.google.gerrit.extensions.api.changes.DeleteChangeMessageInput; 27 : import com.google.gerrit.extensions.common.ChangeMessageInfo; 28 : import com.google.gerrit.extensions.common.Input; 29 : import com.google.gerrit.extensions.restapi.Response; 30 : import com.google.gerrit.extensions.restapi.RestApiException; 31 : import com.google.gerrit.extensions.restapi.RestModifyView; 32 : import com.google.gerrit.server.ChangeMessagesUtil; 33 : import com.google.gerrit.server.CurrentUser; 34 : import com.google.gerrit.server.account.AccountLoader; 35 : import com.google.gerrit.server.change.ChangeMessageResource; 36 : import com.google.gerrit.server.notedb.ChangeNotes; 37 : import com.google.gerrit.server.permissions.GlobalPermission; 38 : import com.google.gerrit.server.permissions.PermissionBackend; 39 : import com.google.gerrit.server.permissions.PermissionBackendException; 40 : import com.google.gerrit.server.update.BatchUpdate; 41 : import com.google.gerrit.server.update.BatchUpdateOp; 42 : import com.google.gerrit.server.update.ChangeContext; 43 : import com.google.gerrit.server.update.UpdateException; 44 : import com.google.gerrit.server.util.AccountTemplateUtil; 45 : import com.google.gerrit.server.util.time.TimeUtil; 46 : import com.google.inject.Inject; 47 : import com.google.inject.Provider; 48 : import com.google.inject.Singleton; 49 : import java.io.IOException; 50 : import java.util.List; 51 : 52 : /** Deletes a change message by rewriting history. */ 53 : @Singleton 54 : public class DeleteChangeMessage 55 : implements RestModifyView<ChangeMessageResource, DeleteChangeMessageInput> { 56 : 57 : private final Provider<CurrentUser> userProvider; 58 : private final PermissionBackend permissionBackend; 59 : private final BatchUpdate.Factory updateFactory; 60 : private final ChangeMessagesUtil changeMessagesUtil; 61 : private final AccountLoader.Factory accountLoaderFactory; 62 : private final ChangeNotes.Factory notesFactory; 63 : 64 : @Inject 65 : public DeleteChangeMessage( 66 : Provider<CurrentUser> userProvider, 67 : PermissionBackend permissionBackend, 68 : BatchUpdate.Factory updateFactory, 69 : ChangeMessagesUtil changeMessagesUtil, 70 : AccountLoader.Factory accountLoaderFactory, 71 138 : ChangeNotes.Factory notesFactory) { 72 138 : this.userProvider = userProvider; 73 138 : this.permissionBackend = permissionBackend; 74 138 : this.updateFactory = updateFactory; 75 138 : this.changeMessagesUtil = changeMessagesUtil; 76 138 : this.accountLoaderFactory = accountLoaderFactory; 77 138 : this.notesFactory = notesFactory; 78 138 : } 79 : 80 : @Override 81 : public Response<ChangeMessageInfo> apply( 82 : ChangeMessageResource resource, DeleteChangeMessageInput input) 83 : throws RestApiException, PermissionBackendException, UpdateException, IOException { 84 1 : CurrentUser user = userProvider.get(); 85 1 : permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER); 86 : 87 1 : String newChangeMessage = 88 1 : createNewChangeMessage(user.asIdentifiedUser().getAccountId(), input.reason); 89 1 : DeleteChangeMessageOp deleteChangeMessageOp = 90 1 : new DeleteChangeMessageOp(resource.getChangeMessageId(), newChangeMessage); 91 1 : try (BatchUpdate batchUpdate = 92 1 : updateFactory.create(resource.getChangeResource().getProject(), user, TimeUtil.now())) { 93 1 : batchUpdate.addOp(resource.getChangeId(), deleteChangeMessageOp).execute(); 94 : } 95 : 96 1 : ChangeMessageInfo updatedMessageInfo = 97 1 : createUpdatedChangeMessageInfo( 98 1 : resource.getChangeResource().getId(), 99 1 : resource.getChangeResource().getProject(), 100 1 : resource.getChangeMessageIndex()); 101 1 : return Response.created(updatedMessageInfo); 102 : } 103 : 104 : private ChangeMessageInfo createUpdatedChangeMessageInfo( 105 : Change.Id cId, Project.NameKey project, int targetIdx) throws PermissionBackendException { 106 1 : List<ChangeMessage> messages = 107 1 : changeMessagesUtil.byChange(notesFactory.createChecked(project, cId)); 108 1 : ChangeMessage updatedChangeMessage = messages.get(targetIdx); 109 1 : AccountLoader accountLoader = accountLoaderFactory.create(true); 110 1 : ChangeMessageInfo info = 111 1 : changeMessagesUtil.createChangeMessageInfoWithReplacedTemplates( 112 : updatedChangeMessage, accountLoader); 113 1 : accountLoader.fill(); 114 1 : return info; 115 : } 116 : 117 : public static String createNewChangeMessage( 118 : Account.Id deletedBy, @Nullable String deletedReason) { 119 1 : requireNonNull(deletedBy, "user must not be null"); 120 : 121 1 : if (Strings.isNullOrEmpty(deletedReason)) { 122 1 : return createNewChangeMessage(deletedBy); 123 : } 124 1 : return String.format( 125 : "Change message removed by: %s\nReason: %s", 126 1 : AccountTemplateUtil.getAccountTemplate(deletedBy), deletedReason); 127 : } 128 : 129 : public static String createNewChangeMessage(Account.Id deletedBy) { 130 1 : requireNonNull(deletedBy, "user must not be null"); 131 : 132 1 : return "Change message removed by: " + AccountTemplateUtil.getAccountTemplate(deletedBy); 133 : } 134 : 135 : private class DeleteChangeMessageOp implements BatchUpdateOp { 136 : private final String targetMessageId; 137 : private final String newMessage; 138 : 139 1 : DeleteChangeMessageOp(String targetMessageIdx, String newMessage) { 140 1 : this.targetMessageId = targetMessageIdx; 141 1 : this.newMessage = newMessage; 142 1 : } 143 : 144 : @Override 145 : public boolean updateChange(ChangeContext ctx) { 146 1 : PatchSet.Id psId = ctx.getChange().currentPatchSetId(); 147 1 : changeMessagesUtil.replaceChangeMessage(ctx.getUpdate(psId), targetMessageId, newMessage); 148 1 : return true; 149 : } 150 : } 151 : 152 : @Singleton 153 : public static class DefaultDeleteChangeMessage 154 : implements RestModifyView<ChangeMessageResource, Input> { 155 : private final DeleteChangeMessage deleteChangeMessage; 156 : 157 : @Inject 158 138 : public DefaultDeleteChangeMessage(DeleteChangeMessage deleteChangeMessage) { 159 138 : this.deleteChangeMessage = deleteChangeMessage; 160 138 : } 161 : 162 : @Override 163 : public Response<ChangeMessageInfo> apply(ChangeMessageResource resource, Input input) 164 : throws RestApiException, PermissionBackendException, UpdateException, IOException { 165 0 : return deleteChangeMessage.apply(resource, new DeleteChangeMessageInput()); 166 : } 167 : } 168 : }