Implement the ChangeListService in terms of ReviewDb's ChangeAccess

We now support showing the dashboard of any user via the "dashboard,$id"
URL syntax.  Since they are all public hyperlinks anyway its not really
a security issue to show our internal database key in the URL.

Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2008-11-26 16:19:10 -08:00
parent 6337849ae5
commit 51dc2c6f06
19 changed files with 347 additions and 126 deletions

View File

@@ -15,7 +15,6 @@
class='com.google.gerrit.server.LoginServlet'/>
<servlet path='/rpc/AccountService'
class='com.google.gerrit.server.AccountServiceSrv'/>
<servlet path='/rpc/ChangeListService'
class='com.google.gerrit.server.ChangeListServiceImpl'/>
class='com.google.gerrit.server.ChangeListServiceSrv'/>
</module>

View File

@@ -18,7 +18,9 @@ import com.google.gerrit.client.account.AccountSettings;
import com.google.gerrit.client.changes.ChangeScreen;
import com.google.gerrit.client.changes.MineScreen;
import com.google.gerrit.client.changes.MineStarredScreen;
import com.google.gerrit.client.data.ChangeHeader;
import com.google.gerrit.client.data.AccountInfo;
import com.google.gerrit.client.data.ChangeInfo;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gwt.user.client.HistoryListener;
public class Link implements HistoryListener {
@@ -35,8 +37,12 @@ public class Link implements HistoryListener {
public static final String ADMIN_GROUPS = "admin,groups";
public static final String ADMIN_PROJECTS = "admin,projects";
public static String toChange(final ChangeHeader c) {
return "change," + c.id;
public static String toChange(final ChangeInfo c) {
return "change," + c.getId().get();
}
public static String toAccountDashboard(final AccountInfo acct) {
return "dashboard," + acct.getId().get();
}
public void onHistoryChanged(final String token) {
@@ -66,6 +72,11 @@ public class Link implements HistoryListener {
return new ChangeScreen(Integer.parseInt(id));
}
else if (token.matches("^dashboard,\\d+$")) {
final String id = token.substring("dashboard,".length());
return new MineScreen(new Account.Id(Integer.parseInt(id)));
}
return null;
}
}

View File

@@ -16,16 +16,16 @@ package com.google.gerrit.client.changes;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.Link;
import com.google.gerrit.client.data.ChangeHeader;
import com.google.gerrit.client.data.ChangeInfo;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.ui.Hyperlink;
public class ChangeLink extends Hyperlink {
private ChangeHeader change;
private ChangeInfo change;
public ChangeLink(final String text, final ChangeHeader c) {
public ChangeLink(final String text, final ChangeInfo c) {
super(text, Link.toChange(c));
change = c;
}

View File

@@ -12,11 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.data;
package com.google.gerrit.client.changes;
import com.google.gerrit.client.data.AccountDashboardInfo;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.RemoteJsonService;
public interface ChangeListService extends RemoteJsonService {
public void mine(AsyncCallback<MineResult> callback);
void forAccount(Account.Id id, AsyncCallback<AccountDashboardInfo> callback);
}

View File

@@ -0,0 +1,82 @@
package com.google.gerrit.client.changes;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.data.AccountCache;
import com.google.gerrit.client.data.AccountDashboardInfo;
import com.google.gerrit.client.data.AccountInfo;
import com.google.gerrit.client.data.ChangeInfo;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.reviewdb.Change;
import com.google.gerrit.client.reviewdb.ChangeAccess;
import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.CookieAccess;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.ResultSet;
import com.google.gwtorm.client.SchemaFactory;
import java.util.ArrayList;
import java.util.List;
public class ChangeListServiceImpl implements ChangeListService {
private final SchemaFactory<ReviewDb> schema;
public ChangeListServiceImpl(final SchemaFactory<ReviewDb> rdf) {
schema = rdf;
}
public void forAccount(Account.Id id,
AsyncCallback<AccountDashboardInfo> callback) {
if (id == null) {
id = idFromCookie();
}
if (id == null) {
callback.onFailure(new IllegalArgumentException("No Account.Id"));
return;
}
try {
final ReviewDb db = schema.open();
try {
final AccountCache accts = new AccountCache(db);
final Account user = accts.get(id);
if (user == null) {
callback.onFailure(new IllegalArgumentException("No such user"));
return;
}
final ChangeAccess changes = db.changes();
final AccountDashboardInfo d;
d = new AccountDashboardInfo(new AccountInfo(user));
d.setByOwner(toInfoList(changes.byOwnerOpen(user.getId()), accts));
d.setClosed(toInfoList(changes.byOwnerMerged(user.getId()), accts));
callback.onSuccess(d);
} finally {
db.close();
}
} catch (OrmException e) {
callback.onFailure(e);
}
}
private List<ChangeInfo> toInfoList(final ResultSet<Change> rs,
final AccountCache accts) throws OrmException {
final ArrayList<ChangeInfo> r = new ArrayList<ChangeInfo>();
for (final Change c : rs) {
r.add(new ChangeInfo(c, accts));
}
return r;
}
private static Account.Id idFromCookie() {
final String myid = CookieAccess.getTokenText(Gerrit.ACCOUNT_COOKIE);
if (myid != null && myid.length() > 0) {
try {
return new Account.Id(Integer.parseInt(myid));
} catch (NumberFormatException e) {
}
}
return null;
}
}

View File

@@ -15,7 +15,7 @@
package com.google.gerrit.client.changes;
import com.google.gerrit.client.Screen;
import com.google.gerrit.client.data.ChangeHeader;
import com.google.gerrit.client.data.ChangeInfo;
public class ChangeScreen extends Screen {
@@ -23,7 +23,7 @@ public class ChangeScreen extends Screen {
super("Loading Change " + id);
}
public ChangeScreen(final ChangeHeader c) {
super(c.subject);
public ChangeScreen(final ChangeInfo c) {
super(c.getSubject());
}
}

View File

@@ -14,8 +14,10 @@
package com.google.gerrit.client.changes;
import com.google.gerrit.client.data.ChangeHeader;
import com.google.gerrit.client.Link;
import com.google.gerrit.client.data.ChangeInfo;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Hyperlink;
import java.util.ArrayList;
import java.util.List;
@@ -62,19 +64,20 @@ public class ChangeTable extends FlexTable {
setStyleName(row, C_ID, "gerrit-ChangeTable-ColumnID");
}
private void populateChangeRow(final int row, final ChangeHeader c) {
setWidget(row, C_ID, new ChangeLink(String.valueOf(c.id), c));
private void populateChangeRow(final int row, final ChangeInfo c) {
setWidget(row, C_ID, new ChangeLink(String.valueOf(c.getId().get()), c));
String s = c.subject;
if (c.status != null) {
s += " (" + c.status + ")";
String s = c.getSubject();
if (c.getStatus() != null) {
s += " (" + c.getStatus().name() + ")";
}
setWidget(row, C_SUBJECT, new ChangeLink(s, c));
setText(row, C_OWNER, c.owner.fullName);
setWidget(row, C_OWNER, new Hyperlink(c.getOwner().getFullName(), Link
.toAccountDashboard(c.getOwner())));
setText(row, C_REVIEWERS, "TODO");
setText(row, C_PROJECT, c.project.name);
setText(row, C_LAST_UPDATE, c.lastUpdate.toString());
setText(row, C_PROJECT, c.getProject().getName());
setText(row, C_LAST_UPDATE, "TODO");
}
private void setStyleName(final int row, final int col, final String name) {
@@ -142,7 +145,7 @@ public class ChangeTable extends FlexTable {
this.titleText = titleText;
}
public void display(final List<ChangeHeader> changeList) {
public void display(final List<ChangeInfo> changeList) {
final int sz = changeList != null ? changeList.size() : 0;
final boolean hadData = rows > 0;

View File

@@ -15,30 +15,35 @@
package com.google.gerrit.client.changes;
import com.google.gerrit.client.Screen;
import com.google.gerrit.client.data.MineResult;
import com.google.gerrit.client.data.AccountDashboardInfo;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
public class MineScreen extends Screen {
private ChangeTable table;
private ChangeTable.Section byMe;
private ChangeTable.Section byOwner;
private ChangeTable.Section forReview;
private ChangeTable.Section closed;
public MineScreen() {
this(null);
}
public MineScreen(final Account.Id id) {
super(Util.C.mineHeading());
table = new ChangeTable();
byMe = new ChangeTable.Section(Util.C.mineByMe());
byOwner = new ChangeTable.Section(Util.C.mineByMe());
forReview = new ChangeTable.Section(Util.C.mineForReview());
closed = new ChangeTable.Section(Util.C.mineClosed());
Util.LIST_SVC.mine(new AsyncCallback<MineResult>() {
public void onSuccess(final MineResult r) {
byMe.display(r.byMe);
forReview.display(r.forReview);
closed.display(r.closed);
Util.LIST_SVC.forAccount(id, new AsyncCallback<AccountDashboardInfo>() {
public void onSuccess(final AccountDashboardInfo r) {
byOwner.display(r.getByOwner());
forReview.display(r.getForReview());
closed.display(r.getClosed());
}
public void onFailure(final Throwable caught) {
@@ -46,7 +51,7 @@ public class MineScreen extends Screen {
}
});
table.addSection(byMe);
table.addSection(byOwner);
table.addSection(forReview);
table.addSection(closed);

View File

@@ -14,7 +14,6 @@
package com.google.gerrit.client.changes;
import com.google.gerrit.client.data.ChangeListService;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.ServiceDefTarget;

View File

@@ -0,0 +1,26 @@
package com.google.gerrit.client.data;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gwtorm.client.OrmException;
import java.util.HashMap;
public class AccountCache {
private final ReviewDb db;
private final HashMap<Account.Id, Account> cache;
public AccountCache(final ReviewDb schema) {
db = schema;
cache = new HashMap<Account.Id, Account>();
}
public Account get(final Account.Id id) throws OrmException {
Account a = cache.get(id);
if (a == null) {
a = db.accounts().byId(id);
cache.put(id, a);
}
return a;
}
}

View File

@@ -0,0 +1,59 @@
// Copyright 2008 Google Inc.
//
// 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.data;
import java.util.List;
public class AccountDashboardInfo {
protected AccountInfo owner;
protected List<ChangeInfo> byOwner;
protected List<ChangeInfo> forReview;
protected List<ChangeInfo> closed;
protected AccountDashboardInfo() {
}
public AccountDashboardInfo(final AccountInfo forUser) {
owner = forUser;
}
public AccountInfo getOwner() {
return owner;
}
public List<ChangeInfo> getByOwner() {
return byOwner;
}
public void setByOwner(List<ChangeInfo> c) {
byOwner = c;
}
public List<ChangeInfo> getForReview() {
return forReview;
}
public void setForReview(List<ChangeInfo> c) {
forReview = c;
}
public List<ChangeInfo> getClosed() {
return closed;
}
public void setClosed(List<ChangeInfo> c) {
closed = c;
}
}

View File

@@ -14,6 +14,31 @@
package com.google.gerrit.client.data;
public class ProjectIdentity {
public String name;
import com.google.gerrit.client.reviewdb.Account;
public class AccountInfo {
protected Account.Id id;
protected String fullName;
protected String preferredEmail;
protected AccountInfo() {
}
public AccountInfo(final Account a) {
id = a.getId();
fullName = a.getFullName();
preferredEmail = a.getPreferredEmail();
}
public Account.Id getId() {
return id;
}
public String getFullName() {
return fullName;
}
public String getPreferredEmail() {
return preferredEmail;
}
}

View File

@@ -0,0 +1,58 @@
// Copyright 2008 Google Inc.
//
// 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.data;
import com.google.gerrit.client.reviewdb.Change;
import com.google.gwtorm.client.OrmException;
public class ChangeInfo {
protected Change.Id id;
protected String subject;
protected Change.Status status;
protected AccountInfo owner;
protected ProjectInfo project;
protected ChangeInfo() {
}
public ChangeInfo(final Change c, final AccountCache accounts)
throws OrmException {
id = c.getKey();
subject = c.getSubject();
status = c.getStatus();
owner = new AccountInfo(accounts.get(c.getOwner()));
project = new ProjectInfo(c.getDest().getParentKey());
}
public Change.Id getId() {
return id;
}
public String getSubject() {
return subject;
}
public Change.Status getStatus() {
return status;
}
public AccountInfo getOwner() {
return owner;
}
public ProjectInfo getProject() {
return project;
}
}

View File

@@ -14,15 +14,19 @@
package com.google.gerrit.client.data;
import java.util.Date;
import java.util.List;
import com.google.gerrit.client.reviewdb.Project;
public class ChangeHeader {
public int id;
public String subject;
public String status;
public UserIdentity owner;
public List<UserIdentity> reviewers;
public ProjectIdentity project;
public Date lastUpdate;
public class ProjectInfo {
protected Project.NameKey key;
protected ProjectInfo() {
}
public ProjectInfo(final Project.NameKey key) {
this.key = key;
}
public String getName() {
return key.get();
}
}

View File

@@ -1,20 +0,0 @@
// Copyright 2008 Google Inc.
//
// 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.data;
public class UserIdentity {
public String fullName;
public String emailAddress;
}

View File

@@ -39,10 +39,13 @@ public final class Change {
}
}
public static enum Status {
NEW('n'),
protected static final char STATUS_NEW = 'n';
protected static final char STATUS_MERGED = 'M';
MERGED('M'),
public static enum Status {
NEW(STATUS_NEW),
MERGED(STATUS_MERGED),
ABANDONED('A');
@@ -134,6 +137,10 @@ public final class Change {
return dest;
}
public String getSubject() {
return subject;
}
/** Get the id of the most current {@link PatchSet} in this change. */
public PatchSet.Id currentPatchSetId() {
if (currentPatchSetId > 0) {

View File

@@ -17,8 +17,18 @@ package com.google.gerrit.client.reviewdb;
import com.google.gwtorm.client.Access;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.PrimaryKey;
import com.google.gwtorm.client.Query;
import com.google.gwtorm.client.ResultSet;
public interface ChangeAccess extends Access<Change, Change.Id> {
@PrimaryKey("changeId")
Change get(Change.Id id) throws OrmException;
@Query("WHERE owner = ? AND status = '" + Change.STATUS_NEW
+ "' ORDER BY createdOn DESC")
ResultSet<Change> byOwnerOpen(Account.Id id) throws OrmException;
@Query("WHERE owner = ? AND status = '" + Change.STATUS_MERGED
+ "' ORDER BY createdOn DESC LIMIT 20")
ResultSet<Change> byOwnerMerged(Account.Id id) throws OrmException;
}

View File

@@ -1,51 +0,0 @@
// Copyright 2008 Google Inc.
//
// 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.server;
import com.google.gerrit.client.data.ChangeHeader;
import com.google.gerrit.client.data.ChangeListService;
import com.google.gerrit.client.data.MineResult;
import com.google.gerrit.client.data.ProjectIdentity;
import com.google.gerrit.client.data.UserIdentity;
import com.google.gwt.user.client.rpc.AsyncCallback;
import java.util.ArrayList;
import java.util.Date;
public class ChangeListServiceImpl extends GerritJsonServlet implements ChangeListService {
@Override
protected Object createServiceHandle() throws Exception {
return this;
}
public void mine(final AsyncCallback<MineResult> callback) {
final MineResult r = new MineResult();
r.byMe = new ArrayList<ChangeHeader>();
for (int i = 10; i < 10 + 2; i++) {
final ChangeHeader c = new ChangeHeader();
c.id = i;
c.subject = "Change " + i;
c.owner = new UserIdentity();
c.owner.fullName = "User " + i;
c.project = new ProjectIdentity();
c.project.name = "platform/test";
c.lastUpdate = new Date();
r.byMe.add(c);
}
callback.onSuccess(r);
}
}

View File

@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.data;
package com.google.gerrit.server;
import java.util.List;
import com.google.gerrit.client.changes.ChangeListServiceImpl;
public class MineResult {
public List<ChangeHeader> byMe;
public List<ChangeHeader> forReview;
public List<ChangeHeader> closed;
/** Publishes {@link ChangeListServiceImpl} over JSON. */
public class ChangeListServiceSrv extends GerritJsonServlet {
@Override
protected Object createServiceHandle() throws Exception {
return new ChangeListServiceImpl(GerritServer.getInstance().getDatabase());
}
}