Line data Source code
1 : // Copyright (C) 2015 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.change; 16 : 17 : import static java.util.Objects.requireNonNull; 18 : 19 : import com.google.common.collect.ImmutableMap; 20 : import com.google.common.collect.Iterables; 21 : import com.google.common.collect.Lists; 22 : import com.google.gerrit.common.Nullable; 23 : import com.google.gerrit.extensions.api.changes.ActionVisitor; 24 : import com.google.gerrit.extensions.common.ActionInfo; 25 : import com.google.gerrit.extensions.common.ChangeInfo; 26 : import com.google.gerrit.extensions.common.RevisionInfo; 27 : import com.google.gerrit.extensions.registration.DynamicMap; 28 : import com.google.gerrit.extensions.registration.DynamicSet; 29 : import com.google.gerrit.extensions.restapi.RestView; 30 : import com.google.gerrit.extensions.webui.PrivateInternals_UiActionDescription; 31 : import com.google.gerrit.extensions.webui.UiAction; 32 : import com.google.gerrit.server.CurrentUser; 33 : import com.google.gerrit.server.extensions.webui.UiActions; 34 : import com.google.gerrit.server.query.change.ChangeData; 35 : import com.google.inject.Inject; 36 : import com.google.inject.Provider; 37 : import com.google.inject.Singleton; 38 : import java.util.ArrayList; 39 : import java.util.Collections; 40 : import java.util.LinkedHashMap; 41 : import java.util.List; 42 : import java.util.Map; 43 : 44 : @Singleton 45 : public class ActionJson { 46 : private final DynamicMap<RestView<RevisionResource>> revisionViews; 47 : private final ChangeJson.Factory changeJsonFactory; 48 : private final ChangeResource.Factory changeResourceFactory; 49 : private final UiActions uiActions; 50 : private final DynamicMap<RestView<ChangeResource>> changeViews; 51 : private final DynamicSet<ActionVisitor> visitorSet; 52 : private final Provider<CurrentUser> userProvider; 53 : 54 : @Inject 55 : ActionJson( 56 : DynamicMap<RestView<RevisionResource>> views, 57 : ChangeJson.Factory changeJsonFactory, 58 : ChangeResource.Factory changeResourceFactory, 59 : UiActions uiActions, 60 : DynamicMap<RestView<ChangeResource>> changeViews, 61 : DynamicSet<ActionVisitor> visitorSet, 62 146 : Provider<CurrentUser> userProvider) { 63 146 : this.revisionViews = views; 64 146 : this.changeJsonFactory = changeJsonFactory; 65 146 : this.changeResourceFactory = changeResourceFactory; 66 146 : this.uiActions = uiActions; 67 146 : this.changeViews = changeViews; 68 146 : this.visitorSet = visitorSet; 69 146 : this.userProvider = userProvider; 70 146 : } 71 : 72 : public Map<String, ActionInfo> format(RevisionResource rsrc) { 73 6 : ChangeInfo changeInfo = null; 74 6 : RevisionInfo revisionInfo = null; 75 6 : List<ActionVisitor> visitors = visitors(); 76 6 : if (!visitors.isEmpty()) { 77 1 : changeInfo = changeJson().format(rsrc); 78 1 : revisionInfo = requireNonNull(Iterables.getOnlyElement(changeInfo.revisions.values())); 79 1 : changeInfo.revisions = null; 80 : } 81 6 : return toActionMap(rsrc, visitors, changeInfo, revisionInfo); 82 : } 83 : 84 : private ChangeJson changeJson() { 85 1 : return changeJsonFactory.noOptions(); 86 : } 87 : 88 : private ArrayList<ActionVisitor> visitors() { 89 57 : return Lists.newArrayList(visitorSet); 90 : } 91 : 92 : void addChangeActions(ChangeInfo to, ChangeData changeData) { 93 57 : List<ActionVisitor> visitors = visitors(); 94 57 : to.actions = toActionMap(changeData, visitors, copy(visitors, to)); 95 57 : } 96 : 97 : void addRevisionActions(@Nullable ChangeInfo changeInfo, RevisionInfo to, RevisionResource rsrc) { 98 57 : List<ActionVisitor> visitors = visitors(); 99 57 : if (!visitors.isEmpty()) { 100 1 : if (changeInfo != null) { 101 1 : changeInfo = copy(visitors, changeInfo); 102 : } else { 103 0 : changeInfo = changeJson().format(rsrc); 104 : } 105 : } 106 57 : to.actions = toActionMap(rsrc, visitors, changeInfo, copy(visitors, to)); 107 57 : } 108 : 109 : @Nullable 110 : private ChangeInfo copy(List<ActionVisitor> visitors, ChangeInfo changeInfo) { 111 57 : if (visitors.isEmpty()) { 112 57 : return null; 113 : } 114 : // Include all fields from ChangeJson#toChangeInfoImpl that are not protected by any 115 : // ListChangesOptions. 116 1 : ChangeInfo copy = new ChangeInfo(); 117 1 : copy.project = changeInfo.project; 118 1 : copy.branch = changeInfo.branch; 119 1 : copy.topic = changeInfo.topic; 120 1 : copy.attentionSet = 121 1 : changeInfo.attentionSet == null ? null : ImmutableMap.copyOf(changeInfo.attentionSet); 122 1 : copy.removedFromAttentionSet = 123 1 : changeInfo.removedFromAttentionSet == null 124 1 : ? null 125 1 : : ImmutableMap.copyOf(changeInfo.removedFromAttentionSet); 126 1 : copy.assignee = changeInfo.assignee; 127 1 : copy.hashtags = changeInfo.hashtags; 128 1 : copy.changeId = changeInfo.changeId; 129 1 : copy.submitType = changeInfo.submitType; 130 1 : copy.mergeable = changeInfo.mergeable; 131 1 : copy.insertions = changeInfo.insertions; 132 1 : copy.deletions = changeInfo.deletions; 133 1 : copy.hasReviewStarted = changeInfo.hasReviewStarted; 134 1 : copy.isPrivate = changeInfo.isPrivate; 135 1 : copy.subject = changeInfo.subject; 136 1 : copy.status = changeInfo.status; 137 1 : copy.owner = changeInfo.owner; 138 1 : copy.created = changeInfo.created; 139 1 : copy.updated = changeInfo.updated; 140 1 : copy._number = changeInfo._number; 141 1 : copy.requirements = changeInfo.requirements; 142 1 : copy.revertOf = changeInfo.revertOf; 143 1 : copy.submissionId = changeInfo.submissionId; 144 1 : copy.starred = changeInfo.starred; 145 1 : copy.stars = changeInfo.stars; 146 1 : copy.submitted = changeInfo.submitted; 147 1 : copy.submitter = changeInfo.submitter; 148 1 : copy.unresolvedCommentCount = changeInfo.unresolvedCommentCount; 149 1 : copy.workInProgress = changeInfo.workInProgress; 150 1 : copy.id = changeInfo.id; 151 1 : copy.cherryPickOfChange = changeInfo.cherryPickOfChange; 152 1 : copy.cherryPickOfPatchSet = changeInfo.cherryPickOfPatchSet; 153 1 : return copy; 154 : } 155 : 156 : @Nullable 157 : private RevisionInfo copy(List<ActionVisitor> visitors, RevisionInfo revisionInfo) { 158 57 : if (visitors.isEmpty()) { 159 57 : return null; 160 : } 161 : // Include all fields from RevisionJson#toRevisionInfo that are not protected by any 162 : // ListChangesOptions. 163 1 : RevisionInfo copy = new RevisionInfo(); 164 1 : copy.isCurrent = revisionInfo.isCurrent; 165 1 : copy._number = revisionInfo._number; 166 1 : copy.ref = revisionInfo.ref; 167 1 : copy.created = revisionInfo.created; 168 1 : copy.uploader = revisionInfo.uploader; 169 1 : copy.fetch = revisionInfo.fetch; 170 1 : copy.kind = revisionInfo.kind; 171 1 : copy.description = revisionInfo.description; 172 1 : return copy; 173 : } 174 : 175 : private Map<String, ActionInfo> toActionMap( 176 : ChangeData changeData, List<ActionVisitor> visitors, ChangeInfo changeInfo) { 177 57 : CurrentUser user = userProvider.get(); 178 57 : Map<String, ActionInfo> out = new LinkedHashMap<>(); 179 57 : if (!user.isIdentifiedUser()) { 180 5 : return out; 181 : } 182 : 183 57 : Iterable<UiAction.Description> descs = 184 57 : uiActions.from(changeViews, changeResourceFactory.create(changeData, user)); 185 : 186 : // The followup action is a client-side only operation that does not 187 : // have a server side handler. It must be manually registered into the 188 : // resulting action map. 189 57 : if (!changeData.change().isAbandoned()) { 190 57 : UiAction.Description descr = new UiAction.Description(); 191 57 : PrivateInternals_UiActionDescription.setId(descr, "followup"); 192 57 : PrivateInternals_UiActionDescription.setMethod(descr, "POST"); 193 57 : descr.setTitle("Create follow-up change"); 194 57 : descr.setLabel("Follow-Up"); 195 57 : descs = Iterables.concat(descs, Collections.singleton(descr)); 196 : } 197 : 198 : ACTION: 199 57 : for (UiAction.Description d : descs) { 200 57 : ActionInfo actionInfo = new ActionInfo(d); 201 57 : for (ActionVisitor visitor : visitors) { 202 1 : if (!visitor.visit(d.getId(), actionInfo, changeInfo)) { 203 1 : continue ACTION; 204 : } 205 1 : } 206 57 : out.put(d.getId(), actionInfo); 207 57 : } 208 57 : return out; 209 : } 210 : 211 : private ImmutableMap<String, ActionInfo> toActionMap( 212 : RevisionResource rsrc, 213 : List<ActionVisitor> visitors, 214 : ChangeInfo changeInfo, 215 : RevisionInfo revisionInfo) { 216 57 : if (!rsrc.getUser().isIdentifiedUser()) { 217 0 : return ImmutableMap.of(); 218 : } 219 : 220 57 : Map<String, ActionInfo> out = new LinkedHashMap<>(); 221 : ACTION: 222 57 : for (UiAction.Description d : uiActions.from(revisionViews, rsrc)) { 223 57 : ActionInfo actionInfo = new ActionInfo(d); 224 57 : for (ActionVisitor visitor : visitors) { 225 1 : if (!visitor.visit(d.getId(), actionInfo, changeInfo, revisionInfo)) { 226 1 : continue ACTION; 227 : } 228 1 : } 229 57 : out.put(d.getId(), actionInfo); 230 57 : } 231 57 : return ImmutableMap.copyOf(out); 232 : } 233 : }