LCOV - code coverage report
Current view: top level - server/api/changes - ChangeApiImpl.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 233 278 83.8 %
Date: 2022-11-19 15:00:39 Functions: 59 60 98.3 %

          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.api.changes;
      16             : 
      17             : import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
      18             : 
      19             : import com.google.common.collect.ImmutableListMultimap;
      20             : import com.google.common.collect.ListMultimap;
      21             : import com.google.gerrit.common.Nullable;
      22             : import com.google.gerrit.extensions.api.changes.AbandonInput;
      23             : import com.google.gerrit.extensions.api.changes.ApplyPatchPatchSetInput;
      24             : import com.google.gerrit.extensions.api.changes.AssigneeInput;
      25             : import com.google.gerrit.extensions.api.changes.AttentionSetApi;
      26             : import com.google.gerrit.extensions.api.changes.AttentionSetInput;
      27             : import com.google.gerrit.extensions.api.changes.ChangeApi;
      28             : import com.google.gerrit.extensions.api.changes.ChangeEditApi;
      29             : import com.google.gerrit.extensions.api.changes.ChangeMessageApi;
      30             : import com.google.gerrit.extensions.api.changes.Changes;
      31             : import com.google.gerrit.extensions.api.changes.FixInput;
      32             : import com.google.gerrit.extensions.api.changes.HashtagsInput;
      33             : import com.google.gerrit.extensions.api.changes.IncludedInInfo;
      34             : import com.google.gerrit.extensions.api.changes.MoveInput;
      35             : import com.google.gerrit.extensions.api.changes.RebaseInput;
      36             : import com.google.gerrit.extensions.api.changes.RestoreInput;
      37             : import com.google.gerrit.extensions.api.changes.RevertInput;
      38             : import com.google.gerrit.extensions.api.changes.ReviewerApi;
      39             : import com.google.gerrit.extensions.api.changes.ReviewerInfo;
      40             : import com.google.gerrit.extensions.api.changes.ReviewerInput;
      41             : import com.google.gerrit.extensions.api.changes.ReviewerResult;
      42             : import com.google.gerrit.extensions.api.changes.RevisionApi;
      43             : import com.google.gerrit.extensions.api.changes.SubmittedTogetherInfo;
      44             : import com.google.gerrit.extensions.api.changes.SubmittedTogetherOption;
      45             : import com.google.gerrit.extensions.api.changes.TopicInput;
      46             : import com.google.gerrit.extensions.client.ListChangesOption;
      47             : import com.google.gerrit.extensions.common.AccountInfo;
      48             : import com.google.gerrit.extensions.common.ChangeInfo;
      49             : import com.google.gerrit.extensions.common.ChangeInfoDifference;
      50             : import com.google.gerrit.extensions.common.ChangeMessageInfo;
      51             : import com.google.gerrit.extensions.common.CommentInfo;
      52             : import com.google.gerrit.extensions.common.CommitMessageInput;
      53             : import com.google.gerrit.extensions.common.Input;
      54             : import com.google.gerrit.extensions.common.InputWithMessage;
      55             : import com.google.gerrit.extensions.common.MergePatchSetInput;
      56             : import com.google.gerrit.extensions.common.PureRevertInfo;
      57             : import com.google.gerrit.extensions.common.RevertSubmissionInfo;
      58             : import com.google.gerrit.extensions.common.RobotCommentInfo;
      59             : import com.google.gerrit.extensions.common.SubmitRequirementInput;
      60             : import com.google.gerrit.extensions.common.SubmitRequirementResultInfo;
      61             : import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
      62             : import com.google.gerrit.extensions.registration.DynamicMap;
      63             : import com.google.gerrit.extensions.restapi.BadRequestException;
      64             : import com.google.gerrit.extensions.restapi.IdString;
      65             : import com.google.gerrit.extensions.restapi.Response;
      66             : import com.google.gerrit.extensions.restapi.RestApiException;
      67             : import com.google.gerrit.server.DynamicOptions;
      68             : import com.google.gerrit.server.change.ChangeMessageResource;
      69             : import com.google.gerrit.server.change.ChangeResource;
      70             : import com.google.gerrit.server.change.WorkInProgressOp;
      71             : import com.google.gerrit.server.restapi.change.Abandon;
      72             : import com.google.gerrit.server.restapi.change.AddToAttentionSet;
      73             : import com.google.gerrit.server.restapi.change.ApplyPatch;
      74             : import com.google.gerrit.server.restapi.change.AttentionSet;
      75             : import com.google.gerrit.server.restapi.change.ChangeIncludedIn;
      76             : import com.google.gerrit.server.restapi.change.ChangeMessages;
      77             : import com.google.gerrit.server.restapi.change.Check;
      78             : import com.google.gerrit.server.restapi.change.CheckSubmitRequirement;
      79             : import com.google.gerrit.server.restapi.change.CreateMergePatchSet;
      80             : import com.google.gerrit.server.restapi.change.DeleteAssignee;
      81             : import com.google.gerrit.server.restapi.change.DeleteChange;
      82             : import com.google.gerrit.server.restapi.change.DeletePrivate;
      83             : import com.google.gerrit.server.restapi.change.GetAssignee;
      84             : import com.google.gerrit.server.restapi.change.GetChange;
      85             : import com.google.gerrit.server.restapi.change.GetHashtags;
      86             : import com.google.gerrit.server.restapi.change.GetMetaDiff;
      87             : import com.google.gerrit.server.restapi.change.GetPastAssignees;
      88             : import com.google.gerrit.server.restapi.change.GetPureRevert;
      89             : import com.google.gerrit.server.restapi.change.GetTopic;
      90             : import com.google.gerrit.server.restapi.change.Index;
      91             : import com.google.gerrit.server.restapi.change.ListChangeComments;
      92             : import com.google.gerrit.server.restapi.change.ListChangeDrafts;
      93             : import com.google.gerrit.server.restapi.change.ListChangeRobotComments;
      94             : import com.google.gerrit.server.restapi.change.ListReviewers;
      95             : import com.google.gerrit.server.restapi.change.Move;
      96             : import com.google.gerrit.server.restapi.change.PostHashtags;
      97             : import com.google.gerrit.server.restapi.change.PostPrivate;
      98             : import com.google.gerrit.server.restapi.change.PostReviewers;
      99             : import com.google.gerrit.server.restapi.change.PutAssignee;
     100             : import com.google.gerrit.server.restapi.change.PutMessage;
     101             : import com.google.gerrit.server.restapi.change.PutTopic;
     102             : import com.google.gerrit.server.restapi.change.Rebase;
     103             : import com.google.gerrit.server.restapi.change.Restore;
     104             : import com.google.gerrit.server.restapi.change.Revert;
     105             : import com.google.gerrit.server.restapi.change.RevertSubmission;
     106             : import com.google.gerrit.server.restapi.change.Reviewers;
     107             : import com.google.gerrit.server.restapi.change.Revisions;
     108             : import com.google.gerrit.server.restapi.change.SetReadyForReview;
     109             : import com.google.gerrit.server.restapi.change.SetWorkInProgress;
     110             : import com.google.gerrit.server.restapi.change.SubmittedTogether;
     111             : import com.google.gerrit.server.restapi.change.SuggestChangeReviewers;
     112             : import com.google.gerrit.util.cli.CmdLineParser;
     113             : import com.google.inject.Inject;
     114             : import com.google.inject.Injector;
     115             : import com.google.inject.Provider;
     116             : import com.google.inject.Singleton;
     117             : import com.google.inject.assistedinject.Assisted;
     118             : import java.util.EnumSet;
     119             : import java.util.List;
     120             : import java.util.Map;
     121             : import java.util.Set;
     122             : import org.kohsuke.args4j.CmdLineException;
     123             : 
     124             : class ChangeApiImpl implements ChangeApi {
     125             :   interface Factory {
     126             :     ChangeApiImpl create(ChangeResource change);
     127             :   }
     128             : 
     129             :   private final Changes changeApi;
     130             :   private final Reviewers reviewers;
     131             :   private final Revisions revisions;
     132             :   private final ReviewerApiImpl.Factory reviewerApi;
     133             :   private final RevisionApiImpl.Factory revisionApi;
     134             :   private final ChangeMessageApiImpl.Factory changeMessageApi;
     135             :   private final ChangeMessages changeMessages;
     136             :   private final SuggestChangeReviewers suggestReviewers;
     137             :   private final ListReviewers listReviewers;
     138             :   private final ChangeResource change;
     139             :   private final Abandon abandon;
     140             :   private final Revert revert;
     141             :   private final RevertSubmission revertSubmission;
     142             :   private final Restore restore;
     143             :   private final CreateMergePatchSet updateByMerge;
     144             :   private final ApplyPatch applyPatch;
     145             :   private final Provider<SubmittedTogether> submittedTogether;
     146             :   private final Rebase.CurrentRevision rebase;
     147             :   private final DeleteChange deleteChange;
     148             :   private final GetTopic getTopic;
     149             :   private final PutTopic putTopic;
     150             :   private final ChangeIncludedIn includedIn;
     151             :   private final PostReviewers postReviewers;
     152             :   private final Provider<GetChange> getChangeProvider;
     153             :   private final Provider<GetMetaDiff> getMetaDiffProvider;
     154             :   private final PostHashtags postHashtags;
     155             :   private final GetHashtags getHashtags;
     156             :   private final AttentionSet attentionSet;
     157             :   private final AttentionSetApiImpl.Factory attentionSetApi;
     158             :   private final AddToAttentionSet addToAttentionSet;
     159             :   private final PutAssignee putAssignee;
     160             :   private final GetAssignee getAssignee;
     161             :   private final GetPastAssignees getPastAssignees;
     162             :   private final DeleteAssignee deleteAssignee;
     163             :   private final Provider<ListChangeComments> listCommentsProvider;
     164             :   private final ListChangeRobotComments listChangeRobotComments;
     165             :   private final Provider<ListChangeDrafts> listDraftsProvider;
     166             :   private final ChangeEditApiImpl.Factory changeEditApi;
     167             :   private final Check check;
     168             :   private final Provider<CheckSubmitRequirement> checkSubmitRequirementProvider;
     169             :   private final Index index;
     170             :   private final Move move;
     171             :   private final PostPrivate postPrivate;
     172             :   private final DeletePrivate deletePrivate;
     173             :   private final SetWorkInProgress setWip;
     174             :   private final SetReadyForReview setReady;
     175             :   private final PutMessage putMessage;
     176             :   private final Provider<GetPureRevert> getPureRevertProvider;
     177             :   private final DynamicOptionParser dynamicOptionParser;
     178             :   private final Injector injector;
     179             :   private final DynamicMap<DynamicOptions.DynamicBean> dynamicBeans;
     180             : 
     181             :   @Inject
     182             :   ChangeApiImpl(
     183             :       Changes changeApi,
     184             :       Reviewers reviewers,
     185             :       Revisions revisions,
     186             :       ReviewerApiImpl.Factory reviewerApi,
     187             :       RevisionApiImpl.Factory revisionApi,
     188             :       ChangeMessageApiImpl.Factory changeMessageApi,
     189             :       ChangeMessages changeMessages,
     190             :       SuggestChangeReviewers suggestReviewers,
     191             :       ListReviewers listReviewers,
     192             :       Abandon abandon,
     193             :       Revert revert,
     194             :       RevertSubmission revertSubmission,
     195             :       Restore restore,
     196             :       CreateMergePatchSet updateByMerge,
     197             :       ApplyPatch applyPatch,
     198             :       Provider<SubmittedTogether> submittedTogether,
     199             :       Rebase.CurrentRevision rebase,
     200             :       DeleteChange deleteChange,
     201             :       GetTopic getTopic,
     202             :       PutTopic putTopic,
     203             :       ChangeIncludedIn includedIn,
     204             :       PostReviewers postReviewers,
     205             :       Provider<GetChange> getChangeProvider,
     206             :       Provider<GetMetaDiff> getMetaDiffProvider,
     207             :       PostHashtags postHashtags,
     208             :       GetHashtags getHashtags,
     209             :       AttentionSet attentionSet,
     210             :       AttentionSetApiImpl.Factory attentionSetApi,
     211             :       AddToAttentionSet addToAttentionSet,
     212             :       PutAssignee putAssignee,
     213             :       GetAssignee getAssignee,
     214             :       GetPastAssignees getPastAssignees,
     215             :       DeleteAssignee deleteAssignee,
     216             :       Provider<ListChangeComments> listCommentsProvider,
     217             :       ListChangeRobotComments listChangeRobotComments,
     218             :       Provider<ListChangeDrafts> listDraftsProvider,
     219             :       ChangeEditApiImpl.Factory changeEditApi,
     220             :       Check check,
     221             :       Provider<CheckSubmitRequirement> checkSubmitRequirement,
     222             :       Index index,
     223             :       Move move,
     224             :       PostPrivate postPrivate,
     225             :       DeletePrivate deletePrivate,
     226             :       SetWorkInProgress setWip,
     227             :       SetReadyForReview setReady,
     228             :       PutMessage putMessage,
     229             :       Provider<GetPureRevert> getPureRevertProvider,
     230             :       DynamicOptionParser dynamicOptionParser,
     231             :       @Assisted ChangeResource change,
     232             :       Injector injector,
     233          91 :       DynamicMap<DynamicOptions.DynamicBean> dynamicBeans) {
     234          91 :     this.changeApi = changeApi;
     235          91 :     this.revert = revert;
     236          91 :     this.revertSubmission = revertSubmission;
     237          91 :     this.reviewers = reviewers;
     238          91 :     this.revisions = revisions;
     239          91 :     this.reviewerApi = reviewerApi;
     240          91 :     this.revisionApi = revisionApi;
     241          91 :     this.changeMessageApi = changeMessageApi;
     242          91 :     this.changeMessages = changeMessages;
     243          91 :     this.suggestReviewers = suggestReviewers;
     244          91 :     this.listReviewers = listReviewers;
     245          91 :     this.abandon = abandon;
     246          91 :     this.restore = restore;
     247          91 :     this.updateByMerge = updateByMerge;
     248          91 :     this.applyPatch = applyPatch;
     249          91 :     this.submittedTogether = submittedTogether;
     250          91 :     this.rebase = rebase;
     251          91 :     this.deleteChange = deleteChange;
     252          91 :     this.getTopic = getTopic;
     253          91 :     this.putTopic = putTopic;
     254          91 :     this.includedIn = includedIn;
     255          91 :     this.postReviewers = postReviewers;
     256          91 :     this.getChangeProvider = getChangeProvider;
     257          91 :     this.getMetaDiffProvider = getMetaDiffProvider;
     258          91 :     this.postHashtags = postHashtags;
     259          91 :     this.getHashtags = getHashtags;
     260          91 :     this.attentionSet = attentionSet;
     261          91 :     this.attentionSetApi = attentionSetApi;
     262          91 :     this.addToAttentionSet = addToAttentionSet;
     263          91 :     this.putAssignee = putAssignee;
     264          91 :     this.getAssignee = getAssignee;
     265          91 :     this.getPastAssignees = getPastAssignees;
     266          91 :     this.deleteAssignee = deleteAssignee;
     267          91 :     this.listCommentsProvider = listCommentsProvider;
     268          91 :     this.listChangeRobotComments = listChangeRobotComments;
     269          91 :     this.listDraftsProvider = listDraftsProvider;
     270          91 :     this.changeEditApi = changeEditApi;
     271          91 :     this.check = check;
     272          91 :     this.checkSubmitRequirementProvider = checkSubmitRequirement;
     273          91 :     this.index = index;
     274          91 :     this.move = move;
     275          91 :     this.postPrivate = postPrivate;
     276          91 :     this.deletePrivate = deletePrivate;
     277          91 :     this.setWip = setWip;
     278          91 :     this.setReady = setReady;
     279          91 :     this.putMessage = putMessage;
     280          91 :     this.getPureRevertProvider = getPureRevertProvider;
     281          91 :     this.dynamicOptionParser = dynamicOptionParser;
     282          91 :     this.change = change;
     283          91 :     this.injector = injector;
     284          91 :     this.dynamicBeans = dynamicBeans;
     285          91 :   }
     286             : 
     287             :   @Override
     288             :   public String id() {
     289           8 :     return Integer.toString(change.getId().get());
     290             :   }
     291             : 
     292             :   @Override
     293             :   public RevisionApi revision(String id) throws RestApiException {
     294             :     try {
     295          79 :       return revisionApi.create(revisions.parse(change, IdString.fromDecoded(id)));
     296           0 :     } catch (Exception e) {
     297           0 :       throw asRestApiException("Cannot parse revision", e);
     298             :     }
     299             :   }
     300             : 
     301             :   @Override
     302             :   public ReviewerApi reviewer(String id) throws RestApiException {
     303             :     try {
     304          15 :       return reviewerApi.create(reviewers.parse(change, IdString.fromDecoded(id)));
     305           1 :     } catch (Exception e) {
     306           1 :       throw asRestApiException("Cannot parse reviewer", e);
     307             :     }
     308             :   }
     309             : 
     310             :   @Override
     311             :   public void abandon(AbandonInput in) throws RestApiException {
     312             :     try {
     313          19 :       abandon.apply(change, in);
     314           1 :     } catch (Exception e) {
     315           1 :       throw asRestApiException("Cannot abandon change", e);
     316          19 :     }
     317          19 :   }
     318             : 
     319             :   @Override
     320             :   public void restore(RestoreInput in) throws RestApiException {
     321             :     try {
     322           6 :       restore.apply(change, in);
     323           1 :     } catch (Exception e) {
     324           1 :       throw asRestApiException("Cannot restore change", e);
     325           6 :     }
     326           6 :   }
     327             : 
     328             :   @Override
     329             :   public void move(MoveInput in) throws RestApiException {
     330             :     try {
     331           2 :       move.apply(change, in);
     332           1 :     } catch (Exception e) {
     333           1 :       throw asRestApiException("Cannot move change", e);
     334           2 :     }
     335           2 :   }
     336             : 
     337             :   @Override
     338             :   public void setPrivate(boolean value, @Nullable String message) throws RestApiException {
     339             :     try {
     340          15 :       InputWithMessage input = new InputWithMessage(message);
     341          15 :       if (value) {
     342          15 :         postPrivate.apply(change, input);
     343             :       } else {
     344           1 :         deletePrivate.apply(change, input);
     345             :       }
     346           2 :     } catch (Exception e) {
     347           2 :       throw asRestApiException("Cannot change private status", e);
     348          15 :     }
     349          15 :   }
     350             : 
     351             :   @Override
     352             :   public void setWorkInProgress(@Nullable String message) throws RestApiException {
     353             :     try {
     354          10 :       setWip.apply(change, new WorkInProgressOp.Input(message));
     355           1 :     } catch (Exception e) {
     356           1 :       throw asRestApiException("Cannot set work in progress state", e);
     357          10 :     }
     358          10 :   }
     359             : 
     360             :   @Override
     361             :   public void setReadyForReview(@Nullable String message) throws RestApiException {
     362             :     try {
     363           7 :       setReady.apply(change, new WorkInProgressOp.Input(message));
     364           1 :     } catch (Exception e) {
     365           1 :       throw asRestApiException("Cannot set ready for review state", e);
     366           7 :     }
     367           7 :   }
     368             : 
     369             :   @Override
     370             :   public ChangeApi revert(RevertInput in) throws RestApiException {
     371             :     try {
     372          14 :       return changeApi.id(revert.apply(change, in).value()._number);
     373           2 :     } catch (Exception e) {
     374           2 :       throw asRestApiException("Cannot revert change", e);
     375             :     }
     376             :   }
     377             : 
     378             :   @Override
     379             :   public RevertSubmissionInfo revertSubmission(RevertInput in) throws RestApiException {
     380             :     try {
     381           1 :       return revertSubmission.apply(change, in).value();
     382           2 :     } catch (Exception e) {
     383           2 :       throw asRestApiException("Cannot revert a change submission", e);
     384             :     }
     385             :   }
     386             : 
     387             :   @Override
     388             :   public ChangeInfo createMergePatchSet(MergePatchSetInput in) throws RestApiException {
     389             :     try {
     390           1 :       return updateByMerge.apply(change, in).value();
     391           1 :     } catch (Exception e) {
     392           1 :       throw asRestApiException("Cannot update change by merge", e);
     393             :     }
     394             :   }
     395             : 
     396             :   @Override
     397             :   public ChangeInfo applyPatch(ApplyPatchPatchSetInput in) throws RestApiException {
     398             :     try {
     399           1 :       return applyPatch.apply(change, in).value();
     400           1 :     } catch (Exception e) {
     401           1 :       throw asRestApiException("Cannot apply patch", e);
     402             :     }
     403             :   }
     404             : 
     405             :   @Override
     406             :   public SubmittedTogetherInfo submittedTogether(
     407             :       EnumSet<ListChangesOption> listOptions, EnumSet<SubmittedTogetherOption> submitOptions)
     408             :       throws RestApiException {
     409             :     try {
     410          10 :       return submittedTogether
     411          10 :           .get()
     412          10 :           .addListChangesOption(listOptions)
     413          10 :           .addSubmittedTogetherOption(submitOptions)
     414          10 :           .applyInfo(change);
     415           0 :     } catch (Exception e) {
     416           0 :       throw asRestApiException("Cannot query submittedTogether", e);
     417             :     }
     418             :   }
     419             : 
     420             :   @Override
     421             :   public void rebase(RebaseInput in) throws RestApiException {
     422             :     try {
     423           4 :       rebase.apply(change, in);
     424           1 :     } catch (Exception e) {
     425           1 :       throw asRestApiException("Cannot rebase change", e);
     426           4 :     }
     427           4 :   }
     428             : 
     429             :   @Override
     430             :   public void delete() throws RestApiException {
     431             :     try {
     432           9 :       deleteChange.apply(change, null);
     433           1 :     } catch (Exception e) {
     434           1 :       throw asRestApiException("Cannot delete change", e);
     435           9 :     }
     436           9 :   }
     437             : 
     438             :   @Override
     439             :   public String topic() throws RestApiException {
     440             :     try {
     441           3 :       return getTopic.apply(change).value();
     442           0 :     } catch (Exception e) {
     443           0 :       throw asRestApiException("Cannot get topic", e);
     444             :     }
     445             :   }
     446             : 
     447             :   @Override
     448             :   public void topic(String topic) throws RestApiException {
     449          11 :     TopicInput in = new TopicInput();
     450          11 :     in.topic = topic;
     451             :     try {
     452          11 :       putTopic.apply(change, in);
     453           1 :     } catch (Exception e) {
     454           1 :       throw asRestApiException("Cannot set topic", e);
     455          11 :     }
     456          11 :   }
     457             : 
     458             :   @Override
     459             :   public IncludedInInfo includedIn() throws RestApiException {
     460             :     try {
     461           1 :       return includedIn.apply(change).value();
     462           0 :     } catch (Exception e) {
     463           0 :       throw asRestApiException("Could not extract IncludedIn data", e);
     464             :     }
     465             :   }
     466             : 
     467             :   @Override
     468             :   public ReviewerResult addReviewer(ReviewerInput in) throws RestApiException {
     469             :     try {
     470          30 :       return postReviewers.apply(change, in).value();
     471           0 :     } catch (Exception e) {
     472           0 :       throw asRestApiException("Cannot add change reviewer", e);
     473             :     }
     474             :   }
     475             : 
     476             :   @Override
     477             :   public SuggestedReviewersRequest suggestReviewers() throws RestApiException {
     478           1 :     return new SuggestedReviewersRequest() {
     479             :       @Override
     480             :       public List<SuggestedReviewerInfo> get() throws RestApiException {
     481           1 :         return ChangeApiImpl.this.suggestReviewers(this);
     482             :       }
     483             :     };
     484             :   }
     485             : 
     486             :   private List<SuggestedReviewerInfo> suggestReviewers(SuggestedReviewersRequest r)
     487             :       throws RestApiException {
     488             :     try {
     489           1 :       suggestReviewers.setQuery(r.getQuery());
     490           1 :       suggestReviewers.setLimit(r.getLimit());
     491           1 :       suggestReviewers.setExcludeGroups(r.getExcludeGroups());
     492           1 :       suggestReviewers.setReviewerState(r.getReviewerState());
     493           1 :       return suggestReviewers.apply(change).value();
     494           1 :     } catch (Exception e) {
     495           1 :       throw asRestApiException("Cannot retrieve suggested reviewers", e);
     496             :     }
     497             :   }
     498             : 
     499             :   @Override
     500             :   public List<ReviewerInfo> reviewers() throws RestApiException {
     501             :     try {
     502           2 :       return listReviewers.apply(change).value();
     503           0 :     } catch (Exception e) {
     504           0 :       throw asRestApiException("Cannot retrieve reviewers", e);
     505             :     }
     506             :   }
     507             : 
     508             :   @Override
     509             :   public ChangeInfo get(
     510             :       EnumSet<ListChangesOption> options, ImmutableListMultimap<String, String> pluginOptions)
     511             :       throws RestApiException {
     512          67 :     try (DynamicOptions dynamicOptions = new DynamicOptions(injector, dynamicBeans)) {
     513          67 :       GetChange getChange = getChangeProvider.get();
     514          67 :       options.forEach(getChange::addOption);
     515          67 :       dynamicOptionParser.parseDynamicOptions(getChange, pluginOptions, dynamicOptions);
     516          67 :       return getChange.apply(change).value();
     517           0 :     } catch (Exception e) {
     518           0 :       throw asRestApiException("Cannot retrieve change", e);
     519             :     }
     520             :   }
     521             : 
     522             :   @Override
     523             :   public ChangeInfoDifference metaDiff(
     524             :       @Nullable String oldMetaRevId,
     525             :       @Nullable String newMetaRevId,
     526             :       EnumSet<ListChangesOption> options,
     527             :       ImmutableListMultimap<String, String> pluginOptions)
     528             :       throws RestApiException {
     529           1 :     try (DynamicOptions dynamicOptions = new DynamicOptions(injector, dynamicBeans)) {
     530           1 :       GetMetaDiff metaDiff = getMetaDiffProvider.get();
     531           1 :       metaDiff.setOldMetaRevId(oldMetaRevId);
     532           1 :       metaDiff.setNewMetaRevId(newMetaRevId);
     533           1 :       options.forEach(metaDiff::addOption);
     534           1 :       dynamicOptionParser.parseDynamicOptions(metaDiff, pluginOptions, dynamicOptions);
     535           1 :       return metaDiff.apply(change).value();
     536           0 :     } catch (Exception e) {
     537           0 :       throw asRestApiException("Cannot retrieve metaDiff", e);
     538             :     }
     539             :   }
     540             : 
     541             :   @Override
     542             :   public ChangeEditApi edit() throws RestApiException {
     543          27 :     return changeEditApi.create(change);
     544             :   }
     545             : 
     546             :   @Override
     547             :   public void setMessage(CommitMessageInput in) throws RestApiException {
     548             :     try {
     549           4 :       putMessage.apply(change, in);
     550           1 :     } catch (Exception e) {
     551           1 :       throw asRestApiException("Cannot edit commit message", e);
     552           4 :     }
     553           4 :   }
     554             : 
     555             :   @Override
     556             :   public void setHashtags(HashtagsInput input) throws RestApiException {
     557             :     try {
     558           5 :       postHashtags.apply(change, input);
     559           1 :     } catch (Exception e) {
     560           1 :       throw asRestApiException("Cannot post hashtags", e);
     561           5 :     }
     562           5 :   }
     563             : 
     564             :   @Override
     565             :   public Set<String> getHashtags() throws RestApiException {
     566             :     try {
     567           4 :       return getHashtags.apply(change).value();
     568           0 :     } catch (Exception e) {
     569           0 :       throw asRestApiException("Cannot get hashtags", e);
     570             :     }
     571             :   }
     572             : 
     573             :   @Override
     574             :   public AccountInfo addToAttentionSet(AttentionSetInput input) throws RestApiException {
     575             :     try {
     576           6 :       return addToAttentionSet.apply(change, input).value();
     577           1 :     } catch (Exception e) {
     578           1 :       throw asRestApiException("Cannot add to attention set", e);
     579             :     }
     580             :   }
     581             : 
     582             :   @Override
     583             :   public AttentionSetApi attention(String id) throws RestApiException {
     584             :     try {
     585          13 :       return attentionSetApi.create(attentionSet.parse(change, IdString.fromDecoded(id)));
     586           1 :     } catch (Exception e) {
     587           1 :       throw asRestApiException("Cannot parse account", e);
     588             :     }
     589             :   }
     590             : 
     591             :   @Override
     592             :   public AccountInfo setAssignee(AssigneeInput input) throws RestApiException {
     593             :     try {
     594           6 :       return putAssignee.apply(change, input).value();
     595           1 :     } catch (Exception e) {
     596           1 :       throw asRestApiException("Cannot set assignee", e);
     597             :     }
     598             :   }
     599             : 
     600             :   @Nullable
     601             :   @Override
     602             :   public AccountInfo getAssignee() throws RestApiException {
     603             :     try {
     604           1 :       Response<AccountInfo> r = getAssignee.apply(change);
     605           1 :       return r.isNone() ? null : r.value();
     606           0 :     } catch (Exception e) {
     607           0 :       throw asRestApiException("Cannot get assignee", e);
     608             :     }
     609             :   }
     610             : 
     611             :   @Override
     612             :   public List<AccountInfo> getPastAssignees() throws RestApiException {
     613             :     try {
     614           1 :       return getPastAssignees.apply(change).value();
     615           0 :     } catch (Exception e) {
     616           0 :       throw asRestApiException("Cannot get past assignees", e);
     617             :     }
     618             :   }
     619             : 
     620             :   @Nullable
     621             :   @Override
     622             :   public AccountInfo deleteAssignee() throws RestApiException {
     623             :     try {
     624           1 :       Response<AccountInfo> r = deleteAssignee.apply(change, null);
     625           1 :       return r.isNone() ? null : r.value();
     626           0 :     } catch (Exception e) {
     627           0 :       throw asRestApiException("Cannot delete assignee", e);
     628             :     }
     629             :   }
     630             : 
     631             :   @Override
     632             :   public CommentsRequest commentsRequest() {
     633          11 :     return new CommentsRequest() {
     634             :       @Override
     635             :       public Map<String, List<CommentInfo>> get() throws RestApiException {
     636             :         try {
     637          10 :           ListChangeComments listComments = listCommentsProvider.get();
     638          10 :           listComments.setContext(this.getContext());
     639          10 :           listComments.setContextPadding(this.getContextPadding());
     640          10 :           return listComments.apply(change).value();
     641           0 :         } catch (Exception e) {
     642           0 :           throw asRestApiException("Cannot get comments", e);
     643             :         }
     644             :       }
     645             : 
     646             :       @Override
     647             :       public List<CommentInfo> getAsList() throws RestApiException {
     648             :         try {
     649           2 :           ListChangeComments listComments = listCommentsProvider.get();
     650           2 :           listComments.setContext(this.getContext());
     651           2 :           listComments.setContextPadding(this.getContextPadding());
     652           2 :           return listComments.getComments(change);
     653           0 :         } catch (Exception e) {
     654           0 :           throw asRestApiException("Cannot get comments", e);
     655             :         }
     656             :       }
     657             :     };
     658             :   }
     659             : 
     660             :   @Override
     661             :   public Map<String, List<RobotCommentInfo>> robotComments() throws RestApiException {
     662             :     try {
     663           3 :       return listChangeRobotComments.apply(change).value();
     664           0 :     } catch (Exception e) {
     665           0 :       throw asRestApiException("Cannot get robot comments", e);
     666             :     }
     667             :   }
     668             : 
     669             :   @Override
     670             :   public DraftsRequest draftsRequest() {
     671           7 :     return new DraftsRequest() {
     672             :       @Override
     673             :       public Map<String, List<CommentInfo>> get() throws RestApiException {
     674             :         try {
     675           6 :           ListChangeDrafts listDrafts = listDraftsProvider.get();
     676           6 :           listDrafts.setContext(this.getContext());
     677           6 :           listDrafts.setContextPadding(this.getContextPadding());
     678           6 :           return listDrafts.apply(change).value();
     679           0 :         } catch (Exception e) {
     680           0 :           throw asRestApiException("Cannot get drafts", e);
     681             :         }
     682             :       }
     683             : 
     684             :       @Override
     685             :       public List<CommentInfo> getAsList() throws RestApiException {
     686             :         try {
     687           2 :           ListChangeDrafts listDrafts = listDraftsProvider.get();
     688           2 :           listDrafts.setContext(this.getContext());
     689           2 :           listDrafts.setContextPadding(this.getContextPadding());
     690           2 :           return listDrafts.getComments(change);
     691           1 :         } catch (Exception e) {
     692           1 :           throw asRestApiException("Cannot get drafts", e);
     693             :         }
     694             :       }
     695             :     };
     696             :   }
     697             : 
     698             :   @Override
     699             :   public ChangeInfo check() throws RestApiException {
     700             :     try {
     701           0 :       return check.apply(change).value();
     702           0 :     } catch (Exception e) {
     703           0 :       throw asRestApiException("Cannot check change", e);
     704             :     }
     705             :   }
     706             : 
     707             :   @Override
     708             :   public ChangeInfo check(FixInput fix) throws RestApiException {
     709             :     try {
     710             :       // TODO(dborowitz): Convert to RetryingRestModifyView. Needs to plumb BatchUpdate.Factory into
     711             :       // ConsistencyChecker.
     712           1 :       return check.apply(change, fix).value();
     713           0 :     } catch (Exception e) {
     714           0 :       throw asRestApiException("Cannot check change", e);
     715             :     }
     716             :   }
     717             : 
     718             :   @Override
     719             :   public CheckSubmitRequirementRequest checkSubmitRequirementRequest() {
     720           1 :     return new CheckSubmitRequirementRequest() {
     721             :       @Override
     722             :       public SubmitRequirementResultInfo get() throws RestApiException {
     723             :         try {
     724           1 :           CheckSubmitRequirement check = checkSubmitRequirementProvider.get();
     725           1 :           check.setSrName(this.srName());
     726           1 :           check.setRefsConfigChangeId(this.getRefsConfigChangeId());
     727           1 :           return check.apply(change, null).value();
     728           1 :         } catch (Exception e) {
     729           1 :           throw asRestApiException("Cannot check submit requirement", e);
     730             :         }
     731             :       }
     732             :     };
     733             :   }
     734             : 
     735             :   @Override
     736             :   public SubmitRequirementResultInfo checkSubmitRequirement(SubmitRequirementInput input)
     737             :       throws RestApiException {
     738             :     try {
     739           1 :       return checkSubmitRequirementProvider.get().apply(change, input).value();
     740           0 :     } catch (Exception e) {
     741           0 :       throw asRestApiException("Cannot check submit requirement", e);
     742             :     }
     743             :   }
     744             : 
     745             :   @Override
     746             :   public void index() throws RestApiException {
     747             :     try {
     748           6 :       index.apply(change, new Input());
     749           0 :     } catch (Exception e) {
     750           0 :       throw asRestApiException("Cannot index change", e);
     751           6 :     }
     752           6 :   }
     753             : 
     754             :   @Override
     755             :   public PureRevertInfo pureRevert() throws RestApiException {
     756           1 :     return pureRevert(null);
     757             :   }
     758             : 
     759             :   @Override
     760             :   public PureRevertInfo pureRevert(@Nullable String claimedOriginal) throws RestApiException {
     761             :     try {
     762           1 :       GetPureRevert getPureRevert = getPureRevertProvider.get();
     763           1 :       getPureRevert.setClaimedOriginal(claimedOriginal);
     764           1 :       return getPureRevert.apply(change).value();
     765           1 :     } catch (Exception e) {
     766           1 :       throw asRestApiException("Cannot compute pure revert", e);
     767             :     }
     768             :   }
     769             : 
     770             :   @Override
     771             :   public List<ChangeMessageInfo> messages() throws RestApiException {
     772             :     try {
     773           7 :       return changeMessages.list().apply(change).value();
     774           0 :     } catch (Exception e) {
     775           0 :       throw asRestApiException("Cannot list change messages", e);
     776             :     }
     777             :   }
     778             : 
     779             :   @Override
     780             :   public ChangeMessageApi message(String id) throws RestApiException {
     781             :     try {
     782           2 :       ChangeMessageResource resource = changeMessages.parse(change, IdString.fromDecoded(id));
     783           2 :       return changeMessageApi.create(resource);
     784           1 :     } catch (Exception e) {
     785           1 :       throw asRestApiException("Cannot parse change message " + id, e);
     786             :     }
     787             :   }
     788             : 
     789             :   @Singleton
     790             :   static class DynamicOptionParser {
     791             :     private final CmdLineParser.Factory cmdLineParserFactory;
     792             : 
     793             :     @Inject
     794         149 :     DynamicOptionParser(CmdLineParser.Factory cmdLineParserFactory) {
     795         149 :       this.cmdLineParserFactory = cmdLineParserFactory;
     796         149 :     }
     797             : 
     798             :     void parseDynamicOptions(
     799             :         Object bean, ListMultimap<String, String> pluginOptions, DynamicOptions dynamicOptions)
     800             :         throws BadRequestException {
     801          68 :       CmdLineParser clp = cmdLineParserFactory.create(bean);
     802          68 :       dynamicOptions.setBean(bean);
     803          68 :       dynamicOptions.startLifecycleListeners();
     804          68 :       dynamicOptions.parseDynamicBeans(clp);
     805          68 :       dynamicOptions.setDynamicBeans();
     806          68 :       dynamicOptions.onBeanParseStart();
     807             :       try {
     808          68 :         clp.parseOptionMap(pluginOptions);
     809           0 :       } catch (CmdLineException | NumberFormatException e) {
     810           0 :         throw new BadRequestException(e.getMessage(), e);
     811          68 :       }
     812          68 :       dynamicOptions.onBeanParseEnd();
     813          68 :     }
     814             :   }
     815             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750