Line data Source code
1 : // Copyright (C) 2013 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.account; 16 : 17 : import com.google.common.flogger.FluentLogger; 18 : import com.google.gerrit.exceptions.DuplicateKeyException; 19 : import com.google.gerrit.exceptions.StorageException; 20 : import com.google.gerrit.extensions.common.Input; 21 : import com.google.gerrit.extensions.registration.DynamicMap; 22 : import com.google.gerrit.extensions.restapi.AuthException; 23 : import com.google.gerrit.extensions.restapi.BadRequestException; 24 : import com.google.gerrit.extensions.restapi.ChildCollection; 25 : import com.google.gerrit.extensions.restapi.IdString; 26 : import com.google.gerrit.extensions.restapi.ResourceConflictException; 27 : import com.google.gerrit.extensions.restapi.ResourceNotFoundException; 28 : import com.google.gerrit.extensions.restapi.Response; 29 : import com.google.gerrit.extensions.restapi.RestApiException; 30 : import com.google.gerrit.extensions.restapi.RestCollectionCreateView; 31 : import com.google.gerrit.extensions.restapi.RestModifyView; 32 : import com.google.gerrit.extensions.restapi.RestReadView; 33 : import com.google.gerrit.extensions.restapi.RestView; 34 : import com.google.gerrit.extensions.restapi.TopLevelResource; 35 : import com.google.gerrit.extensions.restapi.UnprocessableEntityException; 36 : import com.google.gerrit.server.CurrentUser; 37 : import com.google.gerrit.server.IdentifiedUser; 38 : import com.google.gerrit.server.StarredChangesUtil; 39 : import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException; 40 : import com.google.gerrit.server.StarredChangesUtil.MutuallyExclusiveLabelsException; 41 : import com.google.gerrit.server.account.AccountResource; 42 : import com.google.gerrit.server.change.ChangeResource; 43 : import com.google.gerrit.server.permissions.PermissionBackendException; 44 : import com.google.gerrit.server.restapi.change.ChangesCollection; 45 : import com.google.gerrit.server.restapi.change.QueryChanges; 46 : import com.google.inject.Inject; 47 : import com.google.inject.Provider; 48 : import com.google.inject.Singleton; 49 : import java.io.IOException; 50 : 51 : @Singleton 52 : public class StarredChanges 53 : implements ChildCollection<AccountResource, AccountResource.StarredChange> { 54 138 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 55 : 56 : private final ChangesCollection changes; 57 : private final DynamicMap<RestView<AccountResource.StarredChange>> views; 58 : private final StarredChangesUtil starredChangesUtil; 59 : 60 : @Inject 61 : StarredChanges( 62 : ChangesCollection changes, 63 : DynamicMap<RestView<AccountResource.StarredChange>> views, 64 138 : StarredChangesUtil starredChangesUtil) { 65 138 : this.changes = changes; 66 138 : this.views = views; 67 138 : this.starredChangesUtil = starredChangesUtil; 68 138 : } 69 : 70 : @Override 71 : public AccountResource.StarredChange parse(AccountResource parent, IdString id) 72 : throws RestApiException, PermissionBackendException, IOException { 73 1 : IdentifiedUser user = parent.getUser(); 74 1 : ChangeResource change = changes.parse(TopLevelResource.INSTANCE, id); 75 1 : if (starredChangesUtil 76 1 : .getLabels(user.getAccountId(), change.getId()) 77 1 : .contains(StarredChangesUtil.DEFAULT_LABEL)) { 78 1 : return new AccountResource.StarredChange(user, change); 79 : } 80 1 : throw new ResourceNotFoundException(id); 81 : } 82 : 83 : @Override 84 : public DynamicMap<RestView<AccountResource.StarredChange>> views() { 85 1 : return views; 86 : } 87 : 88 : @Override 89 : public RestView<AccountResource> list() throws ResourceNotFoundException { 90 0 : return (RestReadView<AccountResource>) 91 : self -> { 92 0 : QueryChanges query = changes.list(); 93 0 : query.addQuery("has:star"); 94 0 : return query.apply(TopLevelResource.INSTANCE); 95 : }; 96 : } 97 : 98 : @Singleton 99 : public static class Create 100 : implements RestCollectionCreateView<AccountResource, AccountResource.StarredChange, Input> { 101 : private final Provider<CurrentUser> self; 102 : private final ChangesCollection changes; 103 : private final StarredChangesUtil starredChangesUtil; 104 : 105 : @Inject 106 : Create( 107 : Provider<CurrentUser> self, 108 : ChangesCollection changes, 109 148 : StarredChangesUtil starredChangesUtil) { 110 148 : this.self = self; 111 148 : this.changes = changes; 112 148 : this.starredChangesUtil = starredChangesUtil; 113 148 : } 114 : 115 : @Override 116 : public Response<?> apply(AccountResource rsrc, IdString id, Input in) 117 : throws RestApiException, IOException { 118 10 : if (!self.get().hasSameAccountId(rsrc.getUser())) { 119 0 : throw new AuthException("not allowed to add starred change"); 120 : } 121 : 122 : ChangeResource change; 123 : try { 124 10 : change = changes.parse(TopLevelResource.INSTANCE, id); 125 0 : } catch (ResourceNotFoundException e) { 126 0 : throw new UnprocessableEntityException(String.format("change %s not found", id.get()), e); 127 0 : } catch (StorageException | PermissionBackendException | IOException e) { 128 0 : logger.atSevere().withCause(e).log("cannot resolve change"); 129 0 : throw new UnprocessableEntityException("internal server error", e); 130 10 : } 131 : 132 : try { 133 10 : starredChangesUtil.star( 134 10 : self.get().getAccountId(), change.getId(), StarredChangesUtil.Operation.ADD); 135 0 : } catch (MutuallyExclusiveLabelsException e) { 136 0 : throw new ResourceConflictException(e.getMessage()); 137 0 : } catch (IllegalLabelException e) { 138 0 : throw new BadRequestException(e.getMessage()); 139 0 : } catch (DuplicateKeyException e) { 140 0 : return Response.none(); 141 10 : } 142 10 : return Response.none(); 143 : } 144 : } 145 : 146 : @Singleton 147 : public static class Put implements RestModifyView<AccountResource.StarredChange, Input> { 148 : private final Provider<CurrentUser> self; 149 : 150 : @Inject 151 138 : Put(Provider<CurrentUser> self) { 152 138 : this.self = self; 153 138 : } 154 : 155 : @Override 156 : public Response<?> apply(AccountResource.StarredChange rsrc, Input in) throws AuthException { 157 0 : if (!self.get().hasSameAccountId(rsrc.getUser())) { 158 0 : throw new AuthException("not allowed update starred changes"); 159 : } 160 0 : return Response.none(); 161 : } 162 : } 163 : 164 : @Singleton 165 : public static class Delete implements RestModifyView<AccountResource.StarredChange, Input> { 166 : private final Provider<CurrentUser> self; 167 : private final StarredChangesUtil starredChangesUtil; 168 : 169 : @Inject 170 148 : Delete(Provider<CurrentUser> self, StarredChangesUtil starredChangesUtil) { 171 148 : this.self = self; 172 148 : this.starredChangesUtil = starredChangesUtil; 173 148 : } 174 : 175 : @Override 176 : public Response<?> apply(AccountResource.StarredChange rsrc, Input in) 177 : throws AuthException, IOException, IllegalLabelException { 178 3 : if (!self.get().hasSameAccountId(rsrc.getUser())) { 179 0 : throw new AuthException("not allowed remove starred change"); 180 : } 181 3 : starredChangesUtil.star( 182 3 : self.get().getAccountId(), rsrc.getChange().getId(), StarredChangesUtil.Operation.REMOVE); 183 3 : return Response.none(); 184 : } 185 : } 186 : }