Add CURRENT_ACTIONS to revision information

This exports any plugin actions that apply to the current revision,
telling the UI what can be invoked over the REST API.

Change-Id: Ieb5e182d43628497c20077b3bc746a6b5bf71abb
This commit is contained in:
Shawn Pearce 2013-07-12 10:54:38 -07:00
parent e19513ce99
commit dc4a9b25fa
3 changed files with 82 additions and 2 deletions

View File

@ -213,6 +213,13 @@ default. Optional fields are:
* `MESSAGES`: include messages associated with the change.
--
[[actions]]
--
* `CURRENT_ACTIONS`: include information on available actions
for the change and its current revision. The caller must be
authenticated to obtain the available actions.
--
.Request
----
GET /changes/?q=97&o=CURRENT_REVISION&o=CURRENT_COMMIT&o=CURRENT_FILES HTTP/1.0
@ -2285,6 +2292,37 @@ Message to be added as review comment to the change when abandoning the
change.
|===========================
[[action-info]]
ActionInfo
~~~~~~~~~~
The `ActionInfo` entity describes a REST API call the client can
make to manipulate a resource. These are frequently implemented by
plugins and may be discovered at runtime.
[options="header",width="50%",cols="1,^1,5"]
|====================================
|Field Name ||Description
|`method` |optional|
HTTP method to use with the action. Most actions use `POST`, `PUT`
or `DELETE` to cause state changes.
|`label` |optional|
Short title to display to a user describing the action. In the
Gerrit web interface the label is used as the text on the button
presented in the UI.
|`title` |optional|
Longer text to display describing the action. In a web UI this
should be the title attribute of the element, displaying when
the user hovers the mouse.
|`enabled` |optional|
If true the action is permitted at this time and the caller is
likely allowed to execute it. This may change if state is updated
at the server or permissions are modified. Not present if false.
|`confirmation_message` |optional|
Warning message to display to the user at the client side after
the user has selected to use this action, but before the action
request is sent to the server.
|====================================
[[add-reviewer-result]]
AddReviewerResult
~~~~~~~~~~~~~~~~~
@ -2853,6 +2891,10 @@ link:#commit-info[CommitInfo] entity.
|`files` ||
The files of the patch set as a map that maps the file names to
link:#file-info[FileInfo] entities.
|`actions` ||
Actions the caller might be able to perform on this revision. The
information is a map of view name to link:#action-info[ActionInfo]
entities.
|===========================
[[rule-input]]

View File

@ -37,7 +37,10 @@ public enum ListChangesOption {
DETAILED_ACCOUNTS(7),
/** Include messages associated with the change. */
MESSAGES(9);
MESSAGES(9),
/** Include allowed actions client could perform. */
CURRENT_ACTIONS(10);
private final int value;

View File

@ -17,6 +17,7 @@ package com.google.gerrit.server.change;
import static com.google.gerrit.common.changes.ListChangesOption.ALL_COMMITS;
import static com.google.gerrit.common.changes.ListChangesOption.ALL_FILES;
import static com.google.gerrit.common.changes.ListChangesOption.ALL_REVISIONS;
import static com.google.gerrit.common.changes.ListChangesOption.CURRENT_ACTIONS;
import static com.google.gerrit.common.changes.ListChangesOption.CURRENT_COMMIT;
import static com.google.gerrit.common.changes.ListChangesOption.CURRENT_FILES;
import static com.google.gerrit.common.changes.ListChangesOption.CURRENT_REVISION;
@ -45,7 +46,9 @@ import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.UiCommandDetail;
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.extensions.webui.UiCommand;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Change.Status;
@ -63,6 +66,7 @@ import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountInfo;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.extensions.webui.UiCommands;
import com.google.gerrit.server.git.LabelNormalizer;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
@ -125,6 +129,8 @@ public class ChangeJson {
private final AccountInfo.Loader.Factory accountLoaderFactory;
private final Provider<String> urlProvider;
private final Urls urls;
private final Revisions revisions;
private ChangeControl.Factory changeControlUserFactory;
private SshInfo sshInfo;
private EnumSet<ListChangesOption> options;
@ -143,7 +149,8 @@ public class ChangeJson {
FileInfoJson fileInfoJson,
AccountInfo.Loader.Factory ailf,
@CanonicalWebUrl Provider<String> curl,
Urls urls) {
Urls urls,
Revisions revisions) {
this.db = db;
this.labelNormalizer = ln;
this.user = u;
@ -155,6 +162,7 @@ public class ChangeJson {
this.accountLoaderFactory = ailf;
this.urlProvider = curl;
this.urls = urls;
this.revisions = revisions;
options = EnumSet.noneOf(ListChangesOption.class);
}
@ -769,6 +777,16 @@ public class ChangeJson {
log.warn("Cannot load PatchList " + in.getId(), e);
}
}
if (out.isCurrent && has(CURRENT_ACTIONS) && user instanceof IdentifiedUser) {
out.actions = Maps.newTreeMap();
for (UiCommandDetail c : UiCommands.from(
revisions,
new RevisionResource(new ChangeResource(control(cd)), in),
EnumSet.of(UiCommand.Place.PATCHSET_ACTION_PANEL))) {
out.actions.put(c.id, new ActionInfo(c));
}
}
return out;
}
@ -876,6 +894,7 @@ public class ChangeJson {
Map<String, FetchInfo> fetch;
CommitInfo commit;
Map<String, FileInfoJson.FileInfo> files;
Map<String, ActionInfo> actions;
}
static class FetchInfo {
@ -943,4 +962,20 @@ public class ChangeJson {
String message;
Integer _revisionNumber;
}
static class ActionInfo {
String method;
String label;
String title;
Boolean enabled;
String confirmationMessage;
ActionInfo(UiCommandDetail c) {
method = c.method;
label = c.label;
title = c.title;
enabled = c.enabled ? true : null;
confirmationMessage = c.confirmationMessage;
}
}
}