Support alias "self" in queries

Writing an expression like "owner:self status:open" will now identify
changes that the caller owns and are still open. This self alias
is valid in contexts where a user is expected as an argument to a
query operator.

Change-Id: I87e58dda43d8da560b31400f1257857b0ea96175
This commit is contained in:
Shawn O. Pearce
2012-04-07 14:59:11 -07:00
parent 8010d4b9a6
commit 068a0d8dde
3 changed files with 111 additions and 52 deletions

View File

@@ -75,7 +75,8 @@ Change-Id that was scraped out of the commit message.
[[owner]]
owner:'USER'::
+
Changes originally submitted by 'USER'.
Changes originally submitted by 'USER'. The special case of
`owner:self` will find changes owned by the caller.
[[ownerin]]
ownerin:'GROUP'::
@@ -85,7 +86,9 @@ Changes originally submitted by a user in 'GROUP'.
[[reviewer]]
reviewer:'USER'::
+
Changes that have been, or need to be, reviewed by 'USER'.
Changes that have been, or need to be, reviewed by 'USER'. The
special case of `reviewer:self` will find changes where the caller
has been added as a reviewer.
[[reviewerin]]
reviewerin:'GROUP'::
@@ -213,6 +216,16 @@ is:reviewed::
True if there is at least one non-zero score on the change, in any
approval category, by any user.
is:owner::
+
True on any change where the current user is the change owner.
Same as `owner:self`.
is:reviewer::
+
True on any change where the current user is a reviewer.
Same as `reviewer:self`.
is:open::
+
True if the change is other open or submitted, merge pending.
@@ -373,16 +386,20 @@ the change. This flag is always added to any query.
starredby:'USER'::
+
Matches changes that have been starred by 'USER'.
The special case `starredby:self` applies to the caller.
watchedby:'USER'::
+
Matches changes that 'USER' has configured watch filters for.
The special case `watchedby:self` applies to the caller.
draftby:'USER'::
+
Matches changes that 'USER' has left unpublished drafts on.
Since the drafts are unpublished, it is not possible to see the
draft text, or even how many drafts there are.
draft text, or even how many drafts there are. The special case
of `draftby:self` will find changes where the caller has created
a draft comment.
limit:'CNT'::
+

View File

@@ -14,6 +14,7 @@
package com.google.gerrit.server.query;
import com.google.common.collect.Iterables;
import com.google.gwtorm.server.OrmException;
import java.util.Collection;
@@ -44,23 +45,35 @@ import java.util.List;
public abstract class Predicate<T> {
/** Combine the passed predicates into a single AND node. */
public static <T> Predicate<T> and(final Predicate<T>... that) {
if (that.length == 1) {
return that[0];
}
return new AndPredicate<T>(that);
}
/** Combine the passed predicates into a single AND node. */
public static <T> Predicate<T> and(
final Collection<? extends Predicate<T>> that) {
if (that.size() == 1) {
return Iterables.getOnlyElement(that);
}
return new AndPredicate<T>(that);
}
/** Combine the passed predicates into a single OR node. */
public static <T> Predicate<T> or(final Predicate<T>... that) {
if (that.length == 1) {
return that[0];
}
return new OrPredicate<T>(that);
}
/** Combine the passed predicates into a single OR node. */
public static <T> Predicate<T> or(
final Collection<? extends Predicate<T>> that) {
if (that.size() == 1) {
return Iterables.getOnlyElement(that);
}
return new OrPredicate<T>(that);
}

View File

@@ -14,6 +14,7 @@
package com.google.gerrit.server.query.change;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.ApprovalTypes;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -44,6 +45,7 @@ import org.eclipse.jgit.lib.AbbreviatedObjectId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -206,10 +208,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
}
if ("draft".equalsIgnoreCase(value)) {
if (currentUser instanceof IdentifiedUser) {
return new HasDraftByPredicate(args.dbProvider,
((IdentifiedUser) currentUser).getAccountId());
}
return new HasDraftByPredicate(args.dbProvider, self());
}
throw new IllegalArgumentException();
@@ -233,6 +232,14 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
return new IsReviewedPredicate(args.dbProvider);
}
if ("owner".equalsIgnoreCase(value)) {
return new OwnerPredicate(args.dbProvider, self());
}
if ("reviewer".equalsIgnoreCase(value)) {
return new ReviewerPredicate(args.dbProvider, self());
}
try {
return status(value);
} catch (IllegalArgumentException e) {
@@ -303,42 +310,59 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
@Operator
public Predicate<ChangeData> starredby(String who)
throws QueryParseException, OrmException {
Account account = args.accountResolver.find(who);
if (account == null) {
throw error("User " + who + " not found");
if ("self".equals(who)) {
return new IsStarredByPredicate(args.dbProvider, currentUser);
}
return new IsStarredByPredicate(args.dbProvider, //
args.userFactory.create(args.dbProvider, account.getId()));
Set<Account.Id> m = parseAccount(who);
List<IsStarredByPredicate> p = Lists.newArrayListWithCapacity(m.size());
for (Account.Id id : m) {
p.add(new IsStarredByPredicate(args.dbProvider,
args.userFactory.create(args.dbProvider, id)));
}
return Predicate.or(p);
}
@Operator
public Predicate<ChangeData> watchedby(String who)
throws QueryParseException, OrmException {
Account account = args.accountResolver.find(who);
if (account == null) {
throw error("User " + who + " not found");
Set<Account.Id> m = parseAccount(who);
List<IsWatchedByPredicate> p = Lists.newArrayListWithCapacity(m.size());
for (Account.Id id : m) {
if (currentUser instanceof IdentifiedUser
&& id.equals(((IdentifiedUser) currentUser).getAccountId())) {
p.add(new IsWatchedByPredicate(args, currentUser));
} else {
p.add(new IsWatchedByPredicate(args,
args.userFactory.create(args.dbProvider, id)));
}
}
return new IsWatchedByPredicate(args, args.userFactory.create(
args.dbProvider, account.getId()));
return Predicate.or(p);
}
@Operator
public Predicate<ChangeData> draftby(String who) throws QueryParseException,
OrmException {
Account account = args.accountResolver.find(who);
if (account == null) {
throw error("User " + who + " not found");
Set<Account.Id> m = parseAccount(who);
List<HasDraftByPredicate> p = Lists.newArrayListWithCapacity(m.size());
for (Account.Id id : m) {
p.add(new HasDraftByPredicate(args.dbProvider, id));
}
return new HasDraftByPredicate(args.dbProvider, account.getId());
return Predicate.or(p);
}
@Operator
public Predicate<ChangeData> visibleto(String who)
throws QueryParseException, OrmException {
Account account = args.accountResolver.find(who);
if (account != null) {
return visibleto(args.userFactory
.create(args.dbProvider, account.getId()));
if ("self".equals(who)) {
return is_visible();
}
Set<Account.Id> m = args.accountResolver.findAll(who);
if (!m.isEmpty()) {
List<Predicate<ChangeData>> p = Lists.newArrayListWithCapacity(m.size());
for (Account.Id id : m) {
return visibleto(args.userFactory.create(args.dbProvider, id));
}
return Predicate.or(p);
}
// If its not an account, maybe its a group?
@@ -375,24 +399,17 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
@Operator
public Predicate<ChangeData> owner(String who) throws QueryParseException,
OrmException {
Set<Account.Id> m = args.accountResolver.findAll(who);
if (m.isEmpty()) {
throw error("User " + who + " not found");
} else if (m.size() == 1) {
Account.Id id = m.iterator().next();
return new OwnerPredicate(args.dbProvider, id);
} else {
List<OwnerPredicate> p = new ArrayList<OwnerPredicate>(m.size());
for (Account.Id id : m) {
p.add(new OwnerPredicate(args.dbProvider, id));
}
return Predicate.or(p);
Set<Account.Id> m = parseAccount(who);
List<OwnerPredicate> p = Lists.newArrayListWithCapacity(m.size());
for (Account.Id id : m) {
p.add(new OwnerPredicate(args.dbProvider, id));
}
return Predicate.or(p);
}
@Operator
public Predicate<ChangeData> ownerin(String group) throws QueryParseException,
OrmException {
public Predicate<ChangeData> ownerin(String group)
throws QueryParseException {
AccountGroup g = args.groupCache.get(new AccountGroup.NameKey(group));
if (g == null) {
throw error("Group " + group + " not found");
@@ -403,24 +420,17 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
@Operator
public Predicate<ChangeData> reviewer(String who)
throws QueryParseException, OrmException {
Set<Account.Id> m = args.accountResolver.findAll(who);
if (m.isEmpty()) {
throw error("User " + who + " not found");
} else if (m.size() == 1) {
Account.Id id = m.iterator().next();
return new ReviewerPredicate(args.dbProvider, id);
} else {
List<ReviewerPredicate> p = new ArrayList<ReviewerPredicate>(m.size());
for (Account.Id id : m) {
p.add(new ReviewerPredicate(args.dbProvider, id));
}
return Predicate.or(p);
Set<Account.Id> m = parseAccount(who);
List<ReviewerPredicate> p = Lists.newArrayListWithCapacity(m.size());
for (Account.Id id : m) {
p.add(new ReviewerPredicate(args.dbProvider, id));
}
return Predicate.or(p);
}
@Operator
public Predicate<ChangeData> reviewerin(String group)
throws QueryParseException, OrmException {
throws QueryParseException {
AccountGroup g = args.groupCache.get(new AccountGroup.NameKey(group));
if (g == null) {
throw error("Group " + group + " not found");
@@ -532,4 +542,23 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
throw error("Unsupported query:" + query);
}
}
private Set<Account.Id> parseAccount(String who)
throws QueryParseException, OrmException {
if ("self".equals(who)) {
return Collections.singleton(self());
}
Set<Account.Id> matches = args.accountResolver.findAll(who);
if (matches.isEmpty()) {
throw error("User " + who + " not found");
}
return matches;
}
private Account.Id self() {
if (currentUser instanceof IdentifiedUser) {
return ((IdentifiedUser) currentUser).getAccountId();
}
throw new IllegalArgumentException();
}
}