Add Follow-Up change UiAction to change screen

Change-Id: If4c9ef331a4646c736720f22fa9f921962f7147f
This commit is contained in:
David Ostrovsky 2014-09-12 00:35:03 +02:00
parent 0619f65b17
commit ceef9ffcda
10 changed files with 151 additions and 51 deletions

View File

@ -39,5 +39,6 @@ public class ChangeInfo {
public Map<String, LabelInfo> labels;
public Collection<ChangeMessageInfo> messages;
public Map<String, RevisionInfo> revisions;
public String baseChange;
public int _number;
}

View File

@ -36,7 +36,7 @@ class CreateChangeAction {
@Override
public void onSend() {
ChangeApi.createChange(project, getDestinationBranch(),
message.getText(),
message.getText(), null,
new GerritCallback<ChangeInfo>() {
@Override
public void onSuccess(ChangeInfo result) {

View File

@ -39,7 +39,7 @@ class Actions extends Composite {
private static final String[] CORE = {
"abandon", "restore", "revert", "topic",
"cherrypick", "submit", "rebase", "message",
"publish", "/"};
"publish", "followup", "/"};
interface Binder extends UiBinder<FlowPanel, Actions> {}
private static final Binder uiBinder = GWT.create(Binder.class);
@ -61,12 +61,17 @@ class Actions extends Composite {
@UiField Button restore;
private RestoreAction restoreAction;
@UiField Button followUp;
private FollowUpAction followUpAction;
private Change.Id changeId;
private ChangeInfo changeInfo;
private String revision;
private String project;
private String subject;
private String message;
private String branch;
private String key;
private boolean canSubmit;
Actions() {
@ -84,6 +89,8 @@ class Actions extends Composite {
project = info.project();
subject = commit.subject();
message = commit.message();
branch = info.branch();
key = info.change_id();
changeInfo = info;
initChangeActions(info, hasUser);
@ -102,6 +109,7 @@ class Actions extends Composite {
a2b(actions, "abandon", abandon);
a2b(actions, "restore", restore);
a2b(actions, "revert", revert);
a2b(actions, "followup", followUp);
for (String id : filterNonCore(actions)) {
add(new ActionButton(info, actions.get(id)));
}
@ -176,6 +184,15 @@ class Actions extends Composite {
return submit.isVisible() && submit.isEnabled();
}
@UiHandler("followUp")
void onFollowUp(ClickEvent e) {
if (followUpAction == null) {
followUpAction = new FollowUpAction(followUp, project,
branch, key);
}
followUpAction.show();
}
@UiHandler("abandon")
void onAbandon(ClickEvent e) {
if (abandonAction == null) {

View File

@ -108,6 +108,9 @@ limitations under the License.
<g:Button ui:field='restore' styleName='{style.red}' visible='false'>
<div><ui:msg>Restore</ui:msg></div>
</g:Button>
<g:Button ui:field='followUp' styleName='' visible='false'>
<div><ui:msg>Follow-Up</ui:msg></div>
</g:Button>
<g:Button ui:field='submit' styleName='{style.submit}' visible='false'/>
</g:FlowPanel>

View File

@ -0,0 +1,46 @@
// Copyright (C) 2013 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.change;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.ChangeInfo;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.common.PageLinks;
import com.google.gwt.user.client.ui.Button;
class FollowUpAction extends ActionMessageBox {
private final String project;
private final String branch;
private final String base;
FollowUpAction(Button b, String project, String branch, String key) {
super(b);
this.project = project;
this.branch = branch;
this.base = project + "~" + branch + "~" + key;
}
void send(String message) {
ChangeApi.createChange(project, branch, message, base,
new GerritCallback<ChangeInfo>() {
@Override
public void onSuccess(ChangeInfo result) {
Gerrit.display(PageLinks.toChange(result.legacy_id()));
hide();
}
});
}
}

View File

@ -36,11 +36,12 @@ public class ChangeApi {
/** Create a new change. */
public static void createChange(String project, String branch,
String subject, AsyncCallback<ChangeInfo> cb) {
String subject, String base, AsyncCallback<ChangeInfo> cb) {
CreateChangeInput input = CreateChangeInput.create();
input.project(emptyToNull(project));
input.branch(emptyToNull(branch));
input.subject(emptyToNull(subject));
input.base_change(emptyToNull(base));
new RestApi("/changes/").post(input, cb);
}
@ -213,6 +214,7 @@ public class ChangeApi {
public final native void branch(String b) /*-{ if(b)this.branch=b; }-*/;
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 base_change(String b) /*-{ if(b)this.base_change=b; }-*/;
protected CreateChangeInput() {
}

View File

@ -20,8 +20,10 @@ import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Ints;
import com.google.gerrit.common.errors.EmailException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.PatchSet;
@ -30,6 +32,7 @@ import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.ChangeMessages;
import com.google.gerrit.server.change.ChangeTriplet;
import com.google.gerrit.server.change.PatchSetInserter;
import com.google.gerrit.server.events.CommitReceivedEvent;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
@ -516,6 +519,39 @@ public class ChangeUtil {
db.patchSets().delete(Collections.singleton(patch));
}
public List<Change> findChanges(String id)
throws OrmException, ResourceNotFoundException {
// Try legacy id
if (id.matches("^[1-9][0-9]*$")) {
Change c = db.get().changes().get(Change.Id.parse(id));
if (c != null) {
return ImmutableList.of(c);
}
return Collections.emptyList();
}
// Try isolated changeId
if (!id.contains("~")) {
Change.Key key = new Change.Key(id);
if (key.get().length() == 41) {
return db.get().changes().byKey(key).toList();
} else {
return db.get().changes().byKeyRange(key, key.max()).toList();
}
}
// Try change triplet
ChangeTriplet triplet;
try {
triplet = new ChangeTriplet(id);
} catch (ChangeTriplet.ParseException e) {
throw new ResourceNotFoundException(id);
}
return db.get().changes().byBranchKey(
triplet.getBranchNameKey(),
triplet.getChangeKey()).toList();
}
private IdentifiedUser user() {
return (IdentifiedUser) userProvider.get();
}

View File

@ -62,6 +62,7 @@ import com.google.gerrit.extensions.config.DownloadScheme;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.extensions.webui.PrivateInternals_UiActionDescription;
import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
@ -331,6 +332,14 @@ public class ChangeJson {
userProvider)) {
out.actions.put(d.getId(), new ActionInfo(d));
}
if (userProvider.get().isIdentifiedUser()
&& in.getStatus().isOpen()) {
UiAction.Description descr = new UiAction.Description();
PrivateInternals_UiActionDescription.setId(descr, "followup");
PrivateInternals_UiActionDescription.setMethod(descr, "POST");
descr.setTitle("Create follow-up change");
out.actions.put(descr.getId(), new ActionInfo(descr));
}
}
return out;
}

View File

@ -14,7 +14,6 @@
package com.google.gerrit.server.change;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AcceptsPost;
import com.google.gerrit.extensions.restapi.IdString;
@ -24,7 +23,7 @@ import com.google.gerrit.extensions.restapi.RestCollection;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
@ -34,33 +33,32 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.Collections;
import java.util.List;
@Singleton
public class ChangesCollection implements
RestCollection<TopLevelResource, ChangeResource>,
AcceptsPost<TopLevelResource> {
private final Provider<ReviewDb> db;
private final Provider<CurrentUser> user;
private final ChangeControl.GenericFactory changeControlFactory;
private final Provider<QueryChanges> queryFactory;
private final DynamicMap<RestView<ChangeResource>> views;
private final ChangeUtil changeUtil;
private final CreateChange createChange;
@Inject
ChangesCollection(
Provider<ReviewDb> dbProvider,
Provider<CurrentUser> user,
ChangeControl.GenericFactory changeControlFactory,
Provider<QueryChanges> queryFactory,
DynamicMap<RestView<ChangeResource>> views,
ChangeUtil changeUtil,
CreateChange createChange) {
this.db = dbProvider;
this.user = user;
this.changeControlFactory = changeControlFactory;
this.queryFactory = queryFactory;
this.views = views;
this.changeUtil = changeUtil;
this.createChange = createChange;
}
@ -77,7 +75,7 @@ public class ChangesCollection implements
@Override
public ChangeResource parse(TopLevelResource root, IdString id)
throws ResourceNotFoundException, OrmException {
List<Change> changes = findChanges(id.encoded());
List<Change> changes = changeUtil.findChanges(id.encoded());
if (changes.size() != 1) {
throw new ResourceNotFoundException(id);
}
@ -101,39 +99,6 @@ public class ChangesCollection implements
return new ChangeResource(control);
}
private List<Change> findChanges(String id)
throws OrmException, ResourceNotFoundException {
// Try legacy id
if (id.matches("^[1-9][0-9]*$")) {
Change c = db.get().changes().get(Change.Id.parse(id));
if (c != null) {
return ImmutableList.of(c);
}
return Collections.emptyList();
}
// Try isolated changeId
if (!id.contains("~")) {
Change.Key key = new Change.Key(id);
if (key.get().length() == 41) {
return db.get().changes().byKey(key).toList();
} else {
return db.get().changes().byKeyRange(key, key.max()).toList();
}
}
// Try change triplet
ChangeTriplet triplet;
try {
triplet = new ChangeTriplet(id);
} catch (ChangeTriplet.ParseException e) {
throw new ResourceNotFoundException(id);
}
return db.get().changes().byBranchKey(
triplet.getBranchNameKey(),
triplet.getChangeKey()).toList();
}
@SuppressWarnings("unchecked")
@Override
public CreateChange post(TopLevelResource parent) throws RestApiException {

View File

@ -15,11 +15,13 @@
package com.google.gerrit.server.change;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.gerrit.common.data.Capable;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeStatus;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
@ -29,6 +31,7 @@ import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
@ -80,6 +83,7 @@ public class CreateChange implements
private final CommitValidators.Factory commitValidatorsFactory;
private final ChangeInserter.Factory changeInserterFactory;
private final ChangeJson json;
private final ChangeUtil changeUtil;
@Inject
CreateChange(Provider<ReviewDb> db,
@ -89,7 +93,8 @@ public class CreateChange implements
ProjectsCollection projectsCollection,
CommitValidators.Factory commitValidatorsFactory,
ChangeInserter.Factory changeInserterFactory,
ChangeJson json) {
ChangeJson json,
ChangeUtil changeUtil) {
this.db = db;
this.gitManager = gitManager;
this.serverTimeZone = myIdent.getTimeZone();
@ -98,13 +103,14 @@ public class CreateChange implements
this.commitValidatorsFactory = commitValidatorsFactory;
this.changeInserterFactory = changeInserterFactory;
this.json = json;
this.changeUtil = changeUtil;
}
@Override
public Response<ChangeJson.ChangeInfo> apply(TopLevelResource parent,
ChangeInfo input) throws AuthException, OrmException,
BadRequestException, UnprocessableEntityException, IOException,
InvalidChangeOperationException {
InvalidChangeOperationException, ResourceNotFoundException {
if (Strings.isNullOrEmpty(input.project)) {
throw new BadRequestException("project must be non-empty");
@ -148,17 +154,32 @@ public class CreateChange implements
try {
RevWalk rw = new RevWalk(git);
try {
Ref destRef = git.getRef(refName);
if (destRef == null) {
throw new UnprocessableEntityException(String.format(
"Branch %s does not exist.", refName));
ObjectId parentCommit;
if (input.baseChange != null) {
List<Change> changes = changeUtil.findChanges(input.baseChange);
if (changes.isEmpty()) {
throw new InvalidChangeOperationException(
"Base change not found: " + input.baseChange);
}
Change change = Iterables.getOnlyElement(changes);
PatchSet ps = db.get().patchSets().get(
new PatchSet.Id(change.getId(),
change.currentPatchSetId().get()));
parentCommit = ObjectId.fromString(ps.getRevision().get());
} else {
Ref destRef = git.getRef(refName);
if (destRef == null) {
throw new UnprocessableEntityException(String.format(
"Branch %s does not exist.", refName));
}
parentCommit = destRef.getObjectId();
}
RevCommit mergeTip = rw.parseCommit(parentCommit);
Timestamp now = TimeUtil.nowTs();
IdentifiedUser me = (IdentifiedUser) userProvider.get();
PersonIdent author = me.newCommitterIdent(now, serverTimeZone);
RevCommit mergeTip = rw.parseCommit(destRef.getObjectId());
ObjectId id = ChangeIdUtil.computeChangeId(mergeTip.getTree(),
mergeTip, author, author, input.subject);
String commitMessage = ChangeIdUtil.insertId(input.subject, id);
@ -169,7 +190,7 @@ public class CreateChange implements
getChangeId(id, c),
new Change.Id(db.get().nextChangeId()),
me.getAccountId(),
new Branch.NameKey(project, destRef.getName()),
new Branch.NameKey(project, refName),
now);
ChangeInserter ins =