Java tutorial
// Copyright (C) 2013 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.gerrit.server.change; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.gerrit.common.ChangeHooks; import com.google.gerrit.common.data.GroupDescription; import com.google.gerrit.common.errors.EmailException; import com.google.gerrit.common.errors.NoSuchGroupException; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.DefaultInput; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.UnprocessableEntityException; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSetApproval; import com.google.gerrit.reviewdb.client.PatchSetApproval.LabelId; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.AccountCache; import com.google.gerrit.server.account.AccountInfo; import com.google.gerrit.server.account.AccountsCollection; import com.google.gerrit.server.account.GroupMembers; import com.google.gerrit.server.change.PostReviewers.Input; import com.google.gerrit.server.change.ReviewerJson.PostResult; import com.google.gerrit.server.change.ReviewerJson.ReviewerInfo; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.group.GroupsCollection; import com.google.gerrit.server.mail.AddReviewerSender; import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; import org.eclipse.jgit.lib.Config; import java.io.IOException; import java.text.MessageFormat; import java.util.List; import java.util.Set; public class PostReviewers implements RestModifyView<ChangeResource, Input> { public final static int DEFAULT_MAX_REVIEWERS_WITHOUT_CHECK = 10; public final static int DEFAULT_MAX_REVIEWERS = 20; public static class Input { @DefaultInput public String reviewer; Boolean confirmed; boolean confirmed() { return Objects.firstNonNull(confirmed, false); } } private final AccountsCollection accounts; private final ReviewerResource.Factory reviewerFactory; private final AddReviewerSender.Factory addReviewerSenderFactory; private final Provider<GroupsCollection> groupsCollection; private final GroupMembers.Factory groupMembersFactory; private final AccountInfo.Loader.Factory accountLoaderFactory; private final Provider<ReviewDb> db; private final IdentifiedUser currentUser; private final IdentifiedUser.GenericFactory identifiedUserFactory; private final Config cfg; private final ChangeHooks hooks; private final AccountCache accountCache; private final ReviewerJson json; @Inject PostReviewers(AccountsCollection accounts, ReviewerResource.Factory reviewerFactory, AddReviewerSender.Factory addReviewerSenderFactory, Provider<GroupsCollection> groupsCollection, GroupMembers.Factory groupMembersFactory, AccountInfo.Loader.Factory accountLoaderFactory, Provider<ReviewDb> db, IdentifiedUser currentUser, IdentifiedUser.GenericFactory identifiedUserFactory, @GerritServerConfig Config cfg, ChangeHooks hooks, AccountCache accountCache, ReviewerJson json) { this.accounts = accounts; this.reviewerFactory = reviewerFactory; this.addReviewerSenderFactory = addReviewerSenderFactory; this.groupsCollection = groupsCollection; this.groupMembersFactory = groupMembersFactory; this.accountLoaderFactory = accountLoaderFactory; this.db = db; this.currentUser = currentUser; this.identifiedUserFactory = identifiedUserFactory; this.cfg = cfg; this.hooks = hooks; this.accountCache = accountCache; this.json = json; } @Override public PostResult apply(ChangeResource rsrc, Input input) throws BadRequestException, ResourceNotFoundException, AuthException, UnprocessableEntityException, OrmException, EmailException, IOException { if (input.reviewer == null) { throw new BadRequestException("missing reviewer field"); } try { Account.Id accountId = accounts.parse(input.reviewer).getAccountId(); return putAccount(reviewerFactory.create(rsrc, accountId)); } catch (UnprocessableEntityException e) { try { return putGroup(rsrc, input); } catch (UnprocessableEntityException e2) { throw new UnprocessableEntityException( MessageFormat.format(ChangeMessages.get().reviewerNotFound, input.reviewer)); } } } private PostResult putAccount(ReviewerResource rsrc) throws OrmException, EmailException { PostResult result = new PostResult(); addReviewers(rsrc, result, ImmutableSet.of(rsrc.getUser())); return result; } private PostResult putGroup(ChangeResource rsrc, Input input) throws BadRequestException, UnprocessableEntityException, OrmException, EmailException, IOException { GroupDescription.Basic group = groupsCollection.get().parseInternal(input.reviewer); PostResult result = new PostResult(); if (!isLegalReviewerGroup(group.getGroupUUID())) { result.error = MessageFormat.format(ChangeMessages.get().groupIsNotAllowed, group.getName()); return result; } Set<IdentifiedUser> reviewers = Sets.newLinkedHashSet(); ChangeControl control = rsrc.getControl(); Set<Account> members; try { members = groupMembersFactory.create(control.getCurrentUser()).listAccounts(group.getGroupUUID(), control.getProject().getNameKey()); } catch (NoSuchGroupException e) { throw new UnprocessableEntityException(e.getMessage()); } catch (NoSuchProjectException e) { throw new BadRequestException(e.getMessage()); } // if maxAllowed is set to 0, it is allowed to add any number of // reviewers int maxAllowed = cfg.getInt("addreviewer", "maxAllowed", DEFAULT_MAX_REVIEWERS); if (maxAllowed > 0 && members.size() > maxAllowed) { result.error = MessageFormat.format(ChangeMessages.get().groupHasTooManyMembers, group.getName()); return result; } // if maxWithoutCheck is set to 0, we never ask for confirmation int maxWithoutConfirmation = cfg.getInt("addreviewer", "maxWithoutConfirmation", DEFAULT_MAX_REVIEWERS_WITHOUT_CHECK); if (!input.confirmed() && maxWithoutConfirmation > 0 && members.size() > maxWithoutConfirmation) { result.confirm = true; result.error = MessageFormat.format(ChangeMessages.get().groupManyMembersConfirmation, group.getName(), members.size()); return result; } for (Account member : members) { if (member.isActive()) { IdentifiedUser user = identifiedUserFactory.create(member.getId()); // Does not account for draft status as a user might want to let a // reviewer see a draft. if (control.forUser(user).isRefVisible()) { reviewers.add(user); } } } addReviewers(rsrc, result, reviewers); return result; } private void addReviewers(ChangeResource rsrc, PostResult result, Set<IdentifiedUser> reviewers) throws OrmException, EmailException { if (reviewers.isEmpty()) { result.reviewers = ImmutableList.of(); return; } PatchSet.Id psid = rsrc.getChange().currentPatchSetId(); Set<Account.Id> existing = Sets.newHashSet(); for (PatchSetApproval psa : db.get().patchSetApprovals().byPatchSet(psid)) { existing.add(psa.getAccountId()); } result.reviewers = Lists.newArrayListWithCapacity(reviewers.size()); List<PatchSetApproval> toInsert = Lists.newArrayListWithCapacity(reviewers.size()); for (IdentifiedUser user : reviewers) { Account.Id id = user.getAccountId(); if (existing.contains(id)) { continue; } ChangeControl control = rsrc.getControl().forUser(user); PatchSetApproval psa = dummyApproval(control, psid, id); result.reviewers.add(json.format(new ReviewerInfo(id), control, ImmutableList.of(psa))); toInsert.add(psa); } db.get().patchSetApprovals().insert(toInsert); accountLoaderFactory.create(true).fill(result.reviewers); postAdd(rsrc.getChange(), result); } private void postAdd(Change change, PostResult result) throws OrmException, EmailException { if (result.reviewers.isEmpty()) { return; } // Execute hook for added reviewers // PatchSet patchSet = db.get().patchSets().get(change.currentPatchSetId()); for (AccountInfo info : result.reviewers) { Account account = accountCache.get(info._id).getAccount(); hooks.doReviewerAddedHook(change, account, patchSet, db.get()); } // Email the reviewers // // The user knows they added themselves, don't bother emailing them. List<Account.Id> added = Lists.newArrayListWithCapacity(result.reviewers.size()); for (AccountInfo info : result.reviewers) { if (!info._id.equals(currentUser.getAccountId())) { added.add(info._id); } } if (!added.isEmpty()) { AddReviewerSender cm; cm = addReviewerSenderFactory.create(change); cm.setFrom(currentUser.getAccountId()); cm.addReviewers(added); cm.send(); } } public static boolean isLegalReviewerGroup(AccountGroup.UUID groupUUID) { return !(AccountGroup.ANONYMOUS_USERS.equals(groupUUID) || AccountGroup.REGISTERED_USERS.equals(groupUUID)); } private PatchSetApproval dummyApproval(ChangeControl ctl, PatchSet.Id patchSetId, Account.Id reviewerId) { LabelId id = Iterables.getLast(ctl.getLabelTypes().getLabelTypes()).getLabelId(); PatchSetApproval dummyApproval = new PatchSetApproval(new PatchSetApproval.Key(patchSetId, reviewerId, id), (short) 0); dummyApproval.cache(ctl.getChange()); return dummyApproval; } }