// Copyright (C) 2008 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.httpd.rpc.patch; import com.google.gerrit.common.data.ReviewerResult; import com.google.gerrit.common.data.ApprovalSummary; import com.google.gerrit.common.data.ApprovalSummarySet; import com.google.gerrit.common.data.ApprovalTypes; import com.google.gerrit.common.data.PatchDetailService; import com.google.gerrit.common.data.PatchScript; import com.google.gerrit.common.errors.NoSuchEntityException; import com.google.gerrit.httpd.rpc.BaseServiceImplementation; import com.google.gerrit.httpd.rpc.Handler; import com.google.gerrit.reviewdb.Account; import com.google.gerrit.reviewdb.AccountDiffPreference; import com.google.gerrit.reviewdb.AccountPatchReview; import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.ApprovalCategoryValue; import com.google.gerrit.reviewdb.Change; import com.google.gerrit.reviewdb.Patch; import com.google.gerrit.reviewdb.PatchLineComment; import com.google.gerrit.reviewdb.PatchSet; import com.google.gerrit.reviewdb.PatchSetApproval; import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.reviewdb.Patch.Key; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.account.AccountInfoCacheFactory; import com.google.gerrit.server.patch.PublishComments; import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.workflow.FunctionState; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwtjsonrpc.client.VoidResult; import com.google.gwtorm.client.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; class PatchDetailServiceImpl extends BaseServiceImplementation implements PatchDetailService { private final ApprovalTypes approvalTypes; private final AccountInfoCacheFactory.Factory accountInfoCacheFactory; private final AddReviewer.Factory addReviewerFactory; private final ChangeControl.Factory changeControlFactory; private final RemoveReviewer.Factory removeReviewerFactory; private final FunctionState.Factory functionStateFactory; private final PublishComments.Factory publishCommentsFactory; private final PatchScriptFactory.Factory patchScriptFactoryFactory; private final SaveDraft.Factory saveDraftFactory; @Inject PatchDetailServiceImpl(final Provider<ReviewDb> schema, final Provider<CurrentUser> currentUser, final ApprovalTypes approvalTypes, final AccountInfoCacheFactory.Factory accountInfoCacheFactory, final AddReviewer.Factory addReviewerFactory, final RemoveReviewer.Factory removeReviewerFactory, final ChangeControl.Factory changeControlFactory, final FunctionState.Factory functionStateFactory, final PatchScriptFactory.Factory patchScriptFactoryFactory, final PublishComments.Factory publishCommentsFactory, final SaveDraft.Factory saveDraftFactory) { super(schema, currentUser); this.approvalTypes = approvalTypes; this.accountInfoCacheFactory = accountInfoCacheFactory; this.addReviewerFactory = addReviewerFactory; this.removeReviewerFactory = removeReviewerFactory; this.changeControlFactory = changeControlFactory; this.functionStateFactory = functionStateFactory; this.patchScriptFactoryFactory = patchScriptFactoryFactory; this.publishCommentsFactory = publishCommentsFactory; this.saveDraftFactory = saveDraftFactory; } public void patchScript(final Patch.Key patchKey, final PatchSet.Id psa, final PatchSet.Id psb, final AccountDiffPreference dp, final AsyncCallback<PatchScript> callback) { if (psb == null) { callback.onFailure(new NoSuchEntityException()); return; } patchScriptFactoryFactory.create(patchKey, psa, psb, dp).to(callback); } public void saveDraft(final PatchLineComment comment, final AsyncCallback<PatchLineComment> callback) { saveDraftFactory.create(comment).to(callback); } public void deleteDraft(final PatchLineComment.Key commentKey, final AsyncCallback<VoidResult> callback) { run(callback, new Action<VoidResult>() { public VoidResult run(ReviewDb db) throws OrmException, Failure { final PatchLineComment comment = db.patchComments().get(commentKey); if (comment == null) { throw new Failure(new NoSuchEntityException()); } if (!getAccountId().equals(comment.getAuthor())) { throw new Failure(new NoSuchEntityException()); } if (comment.getStatus() != PatchLineComment.Status.DRAFT) { throw new Failure(new IllegalStateException("Comment published")); } db.patchComments().delete(Collections.singleton(comment)); return VoidResult.INSTANCE; } }); } public void publishComments(final PatchSet.Id psid, final String msg, final Set<ApprovalCategoryValue.Id> tags, final AsyncCallback<VoidResult> cb) { Handler.wrap(publishCommentsFactory.create(psid, msg, tags)).to(cb); } /** * Update the reviewed status for the file by user @code{account} */ public void setReviewedByCurrentUser(final Key patchKey, final boolean reviewed, AsyncCallback<VoidResult> callback) { run(callback, new Action<VoidResult>() { public VoidResult run(ReviewDb db) throws OrmException { Account.Id account = getAccountId(); AccountPatchReview.Key key = new AccountPatchReview.Key(patchKey, account); AccountPatchReview apr = db.accountPatchReviews().get(key); if (apr == null && reviewed) { db.accountPatchReviews().insert( Collections.singleton(new AccountPatchReview(patchKey, account))); } else if (apr != null && !reviewed) { db.accountPatchReviews().delete(Collections.singleton(apr)); } return VoidResult.INSTANCE; } }); } public void addReviewers(final Change.Id id, final List<String> reviewers, final AsyncCallback<ReviewerResult> callback) { addReviewerFactory.create(id, reviewers).to(callback); } public void removeReviewer(final Change.Id id, final Account.Id reviewerId, final AsyncCallback<ReviewerResult> callback) { removeReviewerFactory.create(id, reviewerId).to(callback); } public void userApprovals(final Set<Change.Id> cids, final Account.Id aid, final AsyncCallback<ApprovalSummarySet> callback) { run(callback, new Action<ApprovalSummarySet>() { public ApprovalSummarySet run(ReviewDb db) throws OrmException { final Map<Change.Id, ApprovalSummary> approvals = new HashMap<Change.Id, ApprovalSummary>(); final AccountInfoCacheFactory aicFactory = accountInfoCacheFactory.create(); aicFactory.want(aid); for (final Change.Id id : cids) { try { final ChangeControl cc = changeControlFactory.validateFor(id); final Change change = cc.getChange(); final PatchSet.Id ps_id = change.currentPatchSetId(); final Map<ApprovalCategory.Id, PatchSetApproval> psas = new HashMap<ApprovalCategory.Id, PatchSetApproval>(); final FunctionState fs = functionStateFactory.create(change, ps_id, psas.values()); for (final PatchSetApproval ca : db.patchSetApprovals() .byPatchSetUser(ps_id, aid)) { final ApprovalCategory.Id category = ca.getCategoryId(); if (change.getStatus().isOpen()) { fs.normalize(approvalTypes.getApprovalType(category), ca); } if (ca.getValue() == 0 || ApprovalCategory.SUBMIT.equals(category)) { continue; } psas.put(category, ca); } approvals.put(id, new ApprovalSummary(psas.values())); } catch (NoSuchChangeException nsce) { /* * The user has no access to see this change, so we simply do not * provide any details about it. */ } } return new ApprovalSummarySet(aicFactory.create(), approvals); } }); } public void strongestApprovals(final Set<Change.Id> cids, final AsyncCallback<ApprovalSummarySet> callback) { run(callback, new Action<ApprovalSummarySet>() { public ApprovalSummarySet run(ReviewDb db) throws OrmException { final Map<Change.Id, ApprovalSummary> approvals = new HashMap<Change.Id, ApprovalSummary>(); final AccountInfoCacheFactory aicFactory = accountInfoCacheFactory.create(); for (final Change.Id id : cids) { try { final ChangeControl cc = changeControlFactory.validateFor(id); final Change change = cc.getChange(); final PatchSet.Id ps_id = change.currentPatchSetId(); final Map<ApprovalCategory.Id, PatchSetApproval> psas = new HashMap<ApprovalCategory.Id, PatchSetApproval>(); final FunctionState fs = functionStateFactory.create(change, ps_id, psas.values()); for (PatchSetApproval ca : db.patchSetApprovals().byPatchSet(ps_id)) { final ApprovalCategory.Id category = ca.getCategoryId(); if (change.getStatus().isOpen()) { fs.normalize(approvalTypes.getApprovalType(category), ca); } if (ca.getValue() == 0 || ApprovalCategory.SUBMIT.equals(category)) { continue; } boolean keep = true; if (psas.containsKey(category)) { final short oldValue = psas.get(category).getValue(); final short newValue = ca.getValue(); keep = (Math.abs(oldValue) < Math.abs(newValue)) || ((Math.abs(oldValue) == Math.abs(newValue) && (newValue < oldValue))); } if (keep) { aicFactory.want(ca.getAccountId()); psas.put(category, ca); } } approvals.put(id, new ApprovalSummary(psas.values())); } catch (NoSuchChangeException nsce) { /* * The user has no access to see this change, so we simply do not * provide any details about it. */ } } return new ApprovalSummarySet(aicFactory.create(), approvals); } }); } }