Adapt ProjectBranchesScreen to use REST for listing/deleting branches

This removes the old RPC for deleting branches which is now not used
anymore.

Change-Id: Ib50c61aa7aaff86f5dfe0244b2925d3532018f93
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
Edwin Kempin
2013-06-06 14:46:26 +02:00
parent 9c6e5376e6
commit 71b43a9b73
11 changed files with 175 additions and 252 deletions

View File

@@ -16,7 +16,6 @@ package com.google.gerrit.common.data;
import com.google.gerrit.common.audit.Audit;
import com.google.gerrit.common.auth.SignInRequired;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwtjsonrpc.common.AsyncCallback;
@@ -25,7 +24,6 @@ import com.google.gwtjsonrpc.common.RpcImpl;
import com.google.gwtjsonrpc.common.RpcImpl.Version;
import java.util.List;
import java.util.Set;
@RpcImpl(version = Version.V2_0)
public interface ProjectAdminService extends RemoteJsonService {
@@ -55,9 +53,4 @@ public interface ProjectAdminService extends RemoteJsonService {
void listBranches(Project.NameKey projectName,
AsyncCallback<ListBranchesResult> callback);
@Audit
@SignInRequired
void deleteBranch(Project.NameKey projectName, Set<Branch.NameKey> ids,
AsyncCallback<Set<Branch.NameKey>> callback);
}

View File

@@ -0,0 +1,53 @@
// 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.access;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.RestApi;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.user.client.rpc.AsyncCallback;
import java.util.Collections;
import java.util.Set;
/** Access rights available from {@code /access/}. */
public class AccessMap extends NativeMap<ProjectAccessInfo> {
public static void get(Set<Project.NameKey> projects,
AsyncCallback<AccessMap> callback) {
RestApi api = new RestApi("/access/");
for (Project.NameKey p : projects) {
api.addParameter("project", p.get());
}
api.get(NativeMap.copyKeysIntoChildren(callback));
}
public static void get(final Project.NameKey project,
final AsyncCallback<ProjectAccessInfo> cb) {
get(Collections.singleton(project), new AsyncCallback<AccessMap>() {
@Override
public void onSuccess(AccessMap result) {
cb.onSuccess(result.get(project.get()));
}
@Override
public void onFailure(Throwable caught) {
cb.onFailure(caught);
}
});
}
protected AccessMap() {
}
}

View File

@@ -0,0 +1,24 @@
// 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.access;
import com.google.gwt.core.client.JavaScriptObject;
public class ProjectAccessInfo extends JavaScriptObject {
public final native boolean canAddRefs() /*-{ return this.can_add ? true : false; }-*/;
protected ProjectAccessInfo() {
}
}

View File

@@ -89,7 +89,6 @@ public interface AdminConstants extends Constants {
String initialRevision();
String buttonAddBranch();
String buttonDeleteBranch();
String branchDeletionOpenChanges();
String groupItemHelp();

View File

@@ -68,8 +68,6 @@ columnBranchRevision = Revision
initialRevision = Initial Revision
buttonAddBranch = Create Branch
buttonDeleteBranch = Delete
branchDeletionOpenChanges = The following branches were not deleted \
because they have open changes:
groupItemHelp = group

View File

@@ -19,17 +19,19 @@ import com.google.gerrit.client.ConfirmationDialog;
import com.google.gerrit.client.ErrorDialog;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.GitwebLink;
import com.google.gerrit.client.VoidResult;
import com.google.gerrit.client.access.AccessMap;
import com.google.gerrit.client.access.ProjectAccessInfo;
import com.google.gerrit.client.projects.BranchInfo;
import com.google.gerrit.client.projects.ProjectApi;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.rpc.ScreenLoadCallback;
import com.google.gerrit.client.ui.BranchLink;
import com.google.gerrit.client.ui.FancyFlexTable;
import com.google.gerrit.client.ui.HintTextBox;
import com.google.gerrit.common.data.ListBranchesResult;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.dom.client.ClickEvent;
@@ -43,9 +45,7 @@ import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
import java.util.HashSet;
@@ -53,7 +53,7 @@ import java.util.List;
import java.util.Set;
public class ProjectBranchesScreen extends ProjectScreen {
private BranchesTable branches;
private BranchesTable branchTable;
private Button delBranch;
private Button addBranch;
private HintTextBox nameTxtBox;
@@ -67,32 +67,32 @@ public class ProjectBranchesScreen extends ProjectScreen {
@Override
protected void onLoad() {
super.onLoad();
Util.PROJECT_SVC.listBranches(getProjectKey(),
new ScreenLoadCallback<ListBranchesResult>(this) {
addPanel.setVisible(false);
AccessMap.get(getProjectKey(),
new GerritCallback<ProjectAccessInfo>() {
@Override
public void preDisplay(final ListBranchesResult result) {
if (result.getNoRepository()) {
branches.setVisible(false);
addPanel.setVisible(false);
delBranch.setVisible(false);
Label no = new Label(Util.C.errorNoGitRepository());
no.setStyleName(Gerrit.RESOURCES.css().smallHeading());
add(no);
} else {
enableForm(true);
display(result.getBranches());
addPanel.setVisible(result.getCanAdd());
}
public void onSuccess(ProjectAccessInfo result) {
addPanel.setVisible(result.canAddRefs());
}
});
refreshBranches();
savedPanel = BRANCH;
}
private void display(final List<Branch> listBranches) {
branches.display(listBranches);
delBranch.setVisible(branches.hasBranchCanDelete());
private void refreshBranches() {
ProjectApi.getBranches(getProjectKey(),
new ScreenLoadCallback<JsArray<BranchInfo>>(this) {
@Override
public void preDisplay(final JsArray<BranchInfo> result) {
enableForm(true);
display(Natives.asList(result));
}
});
}
private void display(final List<BranchInfo> branches) {
branchTable.display(branches);
delBranch.setVisible(branchTable.hasBranchCanDelete());
}
private void enableForm(final boolean on) {
@@ -150,17 +150,17 @@ public class ProjectBranchesScreen extends ProjectScreen {
addPanel.add(addGrid);
addPanel.add(addBranch);
branches = new BranchesTable();
branchTable = new BranchesTable();
delBranch = new Button(Util.C.buttonDeleteBranch());
delBranch.addClickHandler(new ClickHandler() {
@Override
public void onClick(final ClickEvent event) {
branches.deleteChecked();
branchTable.deleteChecked();
}
});
add(branches);
add(branchTable);
add(delBranch);
add(addPanel);
}
@@ -189,17 +189,11 @@ public class ProjectBranchesScreen extends ProjectScreen {
ProjectApi.createBranch(getProjectKey(), branchName, rev,
new GerritCallback<BranchInfo>() {
@Override
public void onSuccess(BranchInfo result) {
public void onSuccess(BranchInfo branch) {
addBranch.setEnabled(true);
nameTxtBox.setText("");
irevTxtBox.setText("");
Util.PROJECT_SVC.listBranches(getProjectKey(),
new GerritCallback<ListBranchesResult>() {
@Override
public void onSuccess(ListBranchesResult result) {
display(result.getBranches());
}
});
refreshBranches();
}
@Override
@@ -216,7 +210,7 @@ public class ProjectBranchesScreen extends ProjectScreen {
textBox.setFocus(true);
}
private class BranchesTable extends FancyFlexTable<Branch> {
private class BranchesTable extends FancyFlexTable<BranchInfo> {
boolean canDelete;
BranchesTable() {
@@ -240,16 +234,16 @@ public class ProjectBranchesScreen extends ProjectScreen {
b.closeElement("b");
b.openElement("p");
final HashSet<Branch.NameKey> ids = new HashSet<Branch.NameKey>();
final HashSet<String> ids = new HashSet<String>();
for (int row = 1; row < table.getRowCount(); row++) {
final Branch k = getRowItem(row);
final BranchInfo k = getRowItem(row);
if (k != null && table.getWidget(row, 1) instanceof CheckBox
&& ((CheckBox) table.getWidget(row, 1)).getValue()) {
if (!ids.isEmpty()) {
b.append(",").br();
}
b.append(k.getName());
ids.add(k.getNameKey());
b.append(k.ref());
ids.add(k.ref());
}
}
b.closeElement("p");
@@ -268,53 +262,35 @@ public class ProjectBranchesScreen extends ProjectScreen {
confirmationDialog.center();
}
private void deleteBranches(final Set<Branch.NameKey> branchIds) {
Util.PROJECT_SVC.deleteBranch(getProjectKey(), branchIds,
new GerritCallback<Set<Branch.NameKey>>() {
public void onSuccess(final Set<Branch.NameKey> deleted) {
if (!deleted.isEmpty()) {
for (int row = 1; row < table.getRowCount();) {
final Branch k = getRowItem(row);
if (k != null && deleted.contains(k.getNameKey())) {
table.removeRow(row);
} else {
row++;
}
private void deleteBranches(final Set<String> branches) {
ProjectApi.deleteBranches(getProjectKey(), branches,
new GerritCallback<VoidResult>() {
public void onSuccess(VoidResult result) {
for (int row = 1; row < table.getRowCount();) {
BranchInfo k = getRowItem(row);
if (k != null && branches.contains(k.ref())) {
table.removeRow(row);
} else {
row++;
}
}
}
branchIds.removeAll(deleted);
if (!branchIds.isEmpty()) {
final VerticalPanel p = new VerticalPanel();
final ErrorDialog errorDialog = new ErrorDialog(p);
final Label l = new Label(Util.C.branchDeletionOpenChanges());
l.setStyleName(Gerrit.RESOURCES.css().errorDialogText());
p.add(l);
for (final Branch.NameKey branch : branchIds) {
final BranchLink link =
new BranchLink(branch.getParentKey(), Change.Status.NEW,
branch.get(), null) {
@Override
public void go() {
errorDialog.hide();
super.go();
};
};
p.add(link);
}
errorDialog.center();
}
@Override
public void onFailure(Throwable caught) {
refreshBranches();
super.onFailure(caught);
}
});
}
void display(final List<Branch> result) {
void display(List<BranchInfo> branches) {
canDelete = false;
while (1 < table.getRowCount())
table.removeRow(table.getRowCount() - 1);
for (final Branch k : result) {
for (final BranchInfo k : branches) {
final int row = table.getRowCount();
table.insertRow(row);
applyDataRowStyle(row);
@@ -322,10 +298,10 @@ public class ProjectBranchesScreen extends ProjectScreen {
}
}
void populate(final int row, final Branch k) {
void populate(int row, BranchInfo k) {
final GitwebLink c = Gerrit.getGitwebLink();
if (k.getCanDelete()) {
if (k.canDelete()) {
table.setWidget(row, 1, new CheckBox());
canDelete = true;
} else {
@@ -334,15 +310,15 @@ public class ProjectBranchesScreen extends ProjectScreen {
table.setText(row, 2, k.getShortName());
if (k.getRevision() != null) {
table.setText(row, 3, k.getRevision().get());
if (k.revision() != null) {
table.setText(row, 3, k.revision());
} else {
table.setText(row, 3, "");
}
if (c != null) {
table.setWidget(row, 4, new Anchor(c.getLinkName(), false, c.toBranch(k
.getNameKey())));
table.setWidget(row, 4, new Anchor(c.getLinkName(), false,
c.toBranch(new Branch.NameKey(getProjectKey(), k.ref()))));
}
final FlexCellFormatter fmt = table.getFlexCellFormatter();

View File

@@ -14,9 +14,16 @@
package com.google.gerrit.client.projects;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gwt.core.client.JavaScriptObject;
public class BranchInfo extends JavaScriptObject {
public final String getShortName() {
return ref().startsWith(Branch.R_HEADS)
? ref().substring(Branch.R_HEADS.length())
: ref();
}
public final native String ref() /*-{ return this.ref; }-*/;
public final native String revision() /*-{ return this.revision; }-*/;
public final native boolean canDelete() /*-{ return this['can_delete'] ? true : false; }-*/;

View File

@@ -14,32 +14,60 @@
package com.google.gerrit.client.projects;
import com.google.gerrit.client.VoidResult;
import com.google.gerrit.client.rpc.CallbackGroup;
import com.google.gerrit.client.rpc.RestApi;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.user.client.rpc.AsyncCallback;
import java.util.Set;
public class ProjectApi {
/** Create a new project */
public static void createProject(String projectName, String parent,
Boolean createEmptyCcommit, Boolean permissionsOnly,
AsyncCallback<VoidResult> asyncCallback) {
AsyncCallback<VoidResult> cb) {
ProjectInput input = ProjectInput.create();
input.setName(projectName);
input.setParent(parent);
input.setPermissionsOnly(permissionsOnly);
input.setCreateEmptyCommit(createEmptyCcommit);
new RestApi("/projects/").id(projectName).ifNoneMatch()
.put(input, asyncCallback);
.put(input, cb);
}
/** Create a new branch */
public static void createBranch(Project.NameKey projectName, String ref,
String revision, AsyncCallback<BranchInfo> asyncCallback) {
String revision, AsyncCallback<BranchInfo> cb) {
BranchInput input = BranchInput.create();
input.setRevision(revision);
new RestApi("/projects/").id(projectName.get()).view("branches").id(ref)
.ifNoneMatch().put(input, asyncCallback);
.ifNoneMatch().put(input, cb);
}
/** Retrieve all visible branches of the project */
public static void getBranches(Project.NameKey projectName,
AsyncCallback<JsArray<BranchInfo>> cb) {
new RestApi("/projects/").id(projectName.get()).view("branches").get(cb);
}
/**
* Delete branches. For each branch to be deleted a separate DELETE request is
* fired to the server. The {@code onSuccess} method of the provided callback
* is invoked once after all requests succeeded. If any request fails the
* callbacks' {@code onFailure} method is invoked. In a failure case it can be
* that still some of the branches were successfully deleted.
*/
public static void deleteBranches(Project.NameKey projectName,
Set<String> refs, AsyncCallback<VoidResult> cb) {
CallbackGroup group = new CallbackGroup();
for (String ref : refs) {
new RestApi("/projects/").id(projectName.get()).view("branches").id(ref)
.delete(group.add(cb));
cb = CallbackGroup.emptyCallback();
}
group.done();
}
static RestApi config(Project.NameKey name) {

View File

@@ -1,142 +0,0 @@
// Copyright (C) 2009 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.httpd.rpc.project;
import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
class DeleteBranches extends Handler<Set<Branch.NameKey>> {
private static final Logger log =
LoggerFactory.getLogger(DeleteBranches.class);
interface Factory {
DeleteBranches create(@Assisted Project.NameKey name,
@Assisted Set<Branch.NameKey> toRemove);
}
private final ProjectControl.Factory projectControlFactory;
private final GitRepositoryManager repoManager;
private final GitReferenceUpdated gitRefUpdated;
private final IdentifiedUser identifiedUser;
private final ChangeHooks hooks;
private final ReviewDb db;
private final Project.NameKey projectName;
private final Set<Branch.NameKey> toRemove;
@Inject
DeleteBranches(final ProjectControl.Factory projectControlFactory,
final GitRepositoryManager repoManager,
final GitReferenceUpdated gitRefUpdated,
final IdentifiedUser identifiedUser,
final ChangeHooks hooks,
final ReviewDb db,
@Assisted Project.NameKey name, @Assisted Set<Branch.NameKey> toRemove) {
this.projectControlFactory = projectControlFactory;
this.repoManager = repoManager;
this.gitRefUpdated = gitRefUpdated;
this.identifiedUser = identifiedUser;
this.hooks = hooks;
this.db = db;
this.projectName = name;
this.toRemove = toRemove;
}
@Override
public Set<Branch.NameKey> call() throws NoSuchProjectException,
RepositoryNotFoundException, OrmException, IOException {
final ProjectControl projectControl =
projectControlFactory.controlFor(projectName);
final Iterator<Branch.NameKey> branchIt = toRemove.iterator();
while (branchIt.hasNext()) {
final Branch.NameKey k = branchIt.next();
if (!projectName.equals(k.getParentKey())) {
throw new IllegalArgumentException("All keys must be from same project");
}
if (!projectControl.controlForRef(k).canDelete()) {
throw new IllegalStateException("Cannot delete " + k.getShortName());
}
if (db.changes().byBranchOpenAll(k).iterator().hasNext()) {
branchIt.remove();
}
}
final Set<Branch.NameKey> deleted = new HashSet<Branch.NameKey>();
final Repository r = repoManager.openRepository(projectName);
try {
for (final Branch.NameKey branchKey : toRemove) {
final String refname = branchKey.get();
final RefUpdate.Result result;
final RefUpdate u;
try {
u = r.updateRef(refname);
u.setForceUpdate(true);
result = u.delete();
} catch (IOException e) {
log.error("Cannot delete " + branchKey, e);
continue;
}
switch (result) {
case NEW:
case NO_CHANGE:
case FAST_FORWARD:
case FORCED:
deleted.add(branchKey);
gitRefUpdated.fire(projectName, u);
hooks.doRefUpdatedHook(branchKey, u, identifiedUser.getAccount());
break;
case REJECTED_CURRENT_BRANCH:
log.warn("Cannot delete " + branchKey + ": " + result.name());
break;
default:
log.error("Cannot delete " + branchKey + ": " + result.name());
break;
}
}
} finally {
r.close();
}
return deleted;
}
}

View File

@@ -19,7 +19,6 @@ import com.google.gerrit.common.data.ListBranchesResult;
import com.google.gerrit.common.data.ProjectAccess;
import com.google.gerrit.common.data.ProjectAdminService;
import com.google.gerrit.common.data.ProjectDetail;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwtjsonrpc.common.AsyncCallback;
@@ -28,13 +27,11 @@ import com.google.inject.Inject;
import org.eclipse.jgit.lib.ObjectId;
import java.util.List;
import java.util.Set;
class ProjectAdminServiceImpl implements ProjectAdminService {
private final ChangeProjectAccess.Factory changeProjectAccessFactory;
private final ReviewProjectAccess.Factory reviewProjectAccessFactory;
private final ChangeProjectSettings.Factory changeProjectSettingsFactory;
private final DeleteBranches.Factory deleteBranchesFactory;
private final ListBranches.Factory listBranchesFactory;
private final VisibleProjectDetails.Factory visibleProjectDetailsFactory;
private final ProjectAccessFactory.Factory projectAccessFactory;
@@ -44,7 +41,6 @@ class ProjectAdminServiceImpl implements ProjectAdminService {
ProjectAdminServiceImpl(final ChangeProjectAccess.Factory changeProjectAccessFactory,
final ReviewProjectAccess.Factory reviewProjectAccessFactory,
final ChangeProjectSettings.Factory changeProjectSettingsFactory,
final DeleteBranches.Factory deleteBranchesFactory,
final ListBranches.Factory listBranchesFactory,
final VisibleProjectDetails.Factory visibleProjectDetailsFactory,
final ProjectAccessFactory.Factory projectAccessFactory,
@@ -52,7 +48,6 @@ class ProjectAdminServiceImpl implements ProjectAdminService {
this.changeProjectAccessFactory = changeProjectAccessFactory;
this.reviewProjectAccessFactory = reviewProjectAccessFactory;
this.changeProjectSettingsFactory = changeProjectSettingsFactory;
this.deleteBranchesFactory = deleteBranchesFactory;
this.listBranchesFactory = listBranchesFactory;
this.visibleProjectDetailsFactory = visibleProjectDetailsFactory;
this.projectAccessFactory = projectAccessFactory;
@@ -108,11 +103,4 @@ class ProjectAdminServiceImpl implements ProjectAdminService {
final AsyncCallback<ListBranchesResult> callback) {
listBranchesFactory.create(projectName).to(callback);
}
@Override
public void deleteBranch(final Project.NameKey projectName,
final Set<Branch.NameKey> toRemove,
final AsyncCallback<Set<Branch.NameKey>> callback) {
deleteBranchesFactory.create(projectName, toRemove).to(callback);
}
}

View File

@@ -31,7 +31,6 @@ public class ProjectModule extends RpcServletModule {
factory(ChangeProjectAccess.Factory.class);
factory(ReviewProjectAccess.Factory.class);
factory(ChangeProjectSettings.Factory.class);
factory(DeleteBranches.Factory.class);
factory(ListBranches.Factory.class);
factory(VisibleProjectDetails.Factory.class);
factory(ProjectAccessFactory.Factory.class);