// Copyright (C) 2012 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.client.changes; import com.google.gerrit.client.Gerrit; import com.google.gerrit.client.info.AccountInfo; import com.google.gerrit.client.info.ChangeInfo; import com.google.gerrit.client.info.ChangeInfo.CommitInfo; import com.google.gerrit.client.info.ChangeInfo.EditInfo; import com.google.gerrit.client.info.ChangeInfo.IncludedInInfo; import com.google.gerrit.client.rpc.CallbackGroup.Callback; import com.google.gerrit.client.rpc.NativeString; import com.google.gerrit.client.rpc.RestApi; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.user.client.rpc.AsyncCallback; /** A collection of static methods which work on the Gerrit REST API for specific changes. */ public class ChangeApi { /** Abandon the change, ending its review. */ public static void abandon(int id, String msg, AsyncCallback<ChangeInfo> cb) { MessageInput input = MessageInput.create(); input.message(emptyToNull(msg)); call(id, "abandon").post(input, cb); } /** * Create a new change. * * <p>The new change is created as DRAFT unless the draft workflow is disabled by * `change.allowDrafts = false` in the configuration, in which case the new change is created as * NEW. */ public static void createChange( String project, String branch, String topic, String subject, String base, AsyncCallback<ChangeInfo> cb) { CreateChangeInput input = CreateChangeInput.create(); input.project(emptyToNull(project)); input.branch(emptyToNull(branch)); input.topic(emptyToNull(topic)); input.subject(emptyToNull(subject)); input.baseChange(emptyToNull(base)); if (Gerrit.info().change().allowDrafts()) { input.status(Change.Status.DRAFT.toString()); } new RestApi("/changes/").post(input, cb); } /** Restore a previously abandoned change to be open again. */ public static void restore(int id, String msg, AsyncCallback<ChangeInfo> cb) { MessageInput input = MessageInput.create(); input.message(emptyToNull(msg)); call(id, "restore").post(input, cb); } /** Create a new change that reverts the delta caused by this change. */ public static void revert(int id, String msg, AsyncCallback<ChangeInfo> cb) { MessageInput input = MessageInput.create(); input.message(emptyToNull(msg)); call(id, "revert").post(input, cb); } /** Update the topic of a change. */ public static void topic(int id, String topic, AsyncCallback<String> cb) { RestApi call = call(id, "topic"); topic = emptyToNull(topic); if (topic != null) { TopicInput input = TopicInput.create(); input.topic(topic); call.put(input, NativeString.unwrap(cb)); } else { call.delete(NativeString.unwrap(cb)); } } public static void detail(int id, AsyncCallback<ChangeInfo> cb) { detail(id).get(cb); } public static RestApi detail(int id) { return call(id, "detail"); } public static RestApi blame(PatchSet.Id id, String path, boolean base) { return revision(id).view("files").id(path).view("blame").addParameter("base", base); } public static RestApi actions(int id, String revision) { if (revision == null || revision.equals("")) { revision = "current"; } return call(id, revision, "actions"); } public static void deleteAssignee(int id, AsyncCallback<AccountInfo> cb) { change(id).view("assignee").delete(cb); } public static void setAssignee(int id, String user, AsyncCallback<AccountInfo> cb) { AssigneeInput input = AssigneeInput.create(); input.assignee(user); change(id).view("assignee").put(input, cb); } public static void markPrivate(int id, AsyncCallback<JavaScriptObject> cb) { change(id).view("private").post(PrivateInput.create(), cb); } public static void unmarkPrivate(int id, AsyncCallback<JavaScriptObject> cb) { change(id).view("private.delete").post(PrivateInput.create(), cb); } public static RestApi comments(int id) { return call(id, "comments"); } public static RestApi drafts(int id) { return call(id, "drafts"); } public static void edit(int id, AsyncCallback<EditInfo> cb) { edit(id).get(cb); } public static void editWithFiles(int id, AsyncCallback<EditInfo> cb) { edit(id).addParameterTrue("list").get(cb); } public static RestApi edit(int id) { return change(id).view("edit"); } public static RestApi editWithCommands(int id) { return edit(id).addParameterTrue("download-commands"); } public static void includedIn(int id, AsyncCallback<IncludedInInfo> cb) { call(id, "in").get(cb); } public static RestApi revision(int id, String revision) { return change(id).view("revisions").id(revision); } public static RestApi revision(PatchSet.Id id) { int cn = id.getParentKey().get(); String revision = RevisionInfoCache.get(id); if (revision != null) { return revision(cn, revision); } return change(cn).view("revisions").id(id.get()); } public static RestApi reviewers(int id) { return change(id).view("reviewers"); } public static RestApi suggestReviewers(int id, String q, int n, boolean e) { RestApi api = change(id).view("suggest_reviewers").addParameter("n", n).addParameter("e", e); if (q != null) { api.addParameter("q", q); } return api; } public static RestApi vote(int id, int reviewer, String vote) { return reviewer(id, reviewer).view("votes").id(vote); } public static RestApi reviewer(int id, int reviewer) { return change(id).view("reviewers").id(reviewer); } public static RestApi reviewer(int id, String reviewer) { return change(id).view("reviewers").id(reviewer); } public static RestApi hashtags(int changeId) { return change(changeId).view("hashtags"); } public static RestApi hashtag(int changeId, String hashtag) { return change(changeId).view("hashtags").id(hashtag); } /** Submit a specific revision of a change. */ public static void cherrypick( int id, String commit, String destination, String message, AsyncCallback<ChangeInfo> cb) { CherryPickInput cherryPickInput = CherryPickInput.create(); cherryPickInput.setMessage(message); cherryPickInput.setDestination(destination); call(id, commit, "cherrypick").post(cherryPickInput, cb); } /** Edit commit message for specific revision of a change. */ public static void message( int id, String commit, String message, AsyncCallback<JavaScriptObject> cb) { CherryPickInput input = CherryPickInput.create(); input.setMessage(message); call(id, commit, "message").post(input, cb); } /** Submit a specific revision of a change. */ public static void submit(int id, String commit, AsyncCallback<SubmitInfo> cb) { JavaScriptObject in = JavaScriptObject.createObject(); call(id, commit, "submit").post(in, cb); } /** Publish a specific revision of a draft change. */ public static void publish(int id, String commit, AsyncCallback<JavaScriptObject> cb) { JavaScriptObject in = JavaScriptObject.createObject(); call(id, commit, "publish").post(in, cb); } /** Delete a specific draft change. */ public static void deleteChange(int id, AsyncCallback<JavaScriptObject> cb) { change(id).delete(cb); } /** Delete a specific draft patch set. */ public static void deleteRevision(int id, String commit, AsyncCallback<JavaScriptObject> cb) { revision(id, commit).delete(cb); } /** Delete change edit. */ public static void deleteEdit(int id, AsyncCallback<JavaScriptObject> cb) { edit(id).delete(cb); } /** Publish change edit. */ public static void publishEdit(int id, AsyncCallback<JavaScriptObject> cb) { JavaScriptObject in = JavaScriptObject.createObject(); change(id).view("edit:publish").post(in, cb); } /** Rebase change edit on latest patch set. */ public static void rebaseEdit(int id, AsyncCallback<JavaScriptObject> cb) { JavaScriptObject in = JavaScriptObject.createObject(); change(id).view("edit:rebase").post(in, cb); } /** Rebase a revision onto the branch tip or another change. */ public static void rebase(int id, String commit, String base, AsyncCallback<ChangeInfo> cb) { RebaseInput rebaseInput = RebaseInput.create(); rebaseInput.setBase(base); call(id, commit, "rebase").post(rebaseInput, cb); } private static class MessageInput extends JavaScriptObject { final native void message(String m) /*-{ if(m)this.message=m; }-*/; static MessageInput create() { return (MessageInput) createObject(); } protected MessageInput() {} } private static class AssigneeInput extends JavaScriptObject { final native void assignee(String a) /*-{ if(a)this.assignee=a; }-*/; static AssigneeInput create() { return (AssigneeInput) createObject(); } protected AssigneeInput() {} } private static class TopicInput extends JavaScriptObject { final native void topic(String t) /*-{ if(t)this.topic=t; }-*/; static TopicInput create() { return (TopicInput) createObject(); } protected TopicInput() {} } private static class CreateChangeInput extends JavaScriptObject { static CreateChangeInput create() { return (CreateChangeInput) createObject(); } public final native void branch(String b) /*-{ if(b)this.branch=b; }-*/; public final native void topic(String t) /*-{ if(t)this.topic=t; }-*/; public final native void project(String p) /*-{ if(p)this.project=p; }-*/; public final native void subject(String s) /*-{ if(s)this.subject=s; }-*/; public final native void status(String s) /*-{ if(s)this.status=s; }-*/; public final native void baseChange(String b) /*-{ if(b)this.base_change=b; }-*/; protected CreateChangeInput() {} } private static class CherryPickInput extends JavaScriptObject { static CherryPickInput create() { return (CherryPickInput) createObject(); } final native void setDestination(String d) /*-{ this.destination = d; }-*/; final native void setMessage(String m) /*-{ this.message = m; }-*/; protected CherryPickInput() {} } private static class PrivateInput extends JavaScriptObject { static PrivateInput create() { return (PrivateInput) createObject(); } final native void setMessage(String m) /*-{ this.message = m; }-*/; protected PrivateInput() {} } private static class RebaseInput extends JavaScriptObject { final native void setBase(String b) /*-{ this.base = b; }-*/; static RebaseInput create() { return (RebaseInput) createObject(); } protected RebaseInput() {} } private static RestApi call(int id, String action) { return change(id).view(action); } private static RestApi call(int id, String commit, String action) { return change(id).view("revisions").id(commit).view(action); } public static RestApi change(int id) { // TODO Switch to triplet project~branch~id format in URI. return new RestApi("/changes/").id(String.valueOf(id)); } public static String emptyToNull(String str) { return str == null || str.isEmpty() ? null : str; } public static void commitWithLinks(int changeId, String revision, Callback<CommitInfo> callback) { revision(changeId, revision).view("commit").addParameterTrue("links").get(callback); } }