From 068a0d8ddefc3d6b346598f36c900100097fa629 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 7 Apr 2012 14:59:11 -0700 Subject: [PATCH] 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 --- Documentation/user-search.txt | 23 +++- .../google/gerrit/server/query/Predicate.java | 13 ++ .../query/change/ChangeQueryBuilder.java | 127 +++++++++++------- 3 files changed, 111 insertions(+), 52 deletions(-) diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt index 3aafe8c164..890c964a3a 100644 --- a/Documentation/user-search.txt +++ b/Documentation/user-search.txt @@ -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':: + diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/Predicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/Predicate.java index ce47d2deed..77e082d2c3 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/Predicate.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/Predicate.java @@ -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 { /** Combine the passed predicates into a single AND node. */ public static Predicate and(final Predicate... that) { + if (that.length == 1) { + return that[0]; + } return new AndPredicate(that); } /** Combine the passed predicates into a single AND node. */ public static Predicate and( final Collection> that) { + if (that.size() == 1) { + return Iterables.getOnlyElement(that); + } return new AndPredicate(that); } /** Combine the passed predicates into a single OR node. */ public static Predicate or(final Predicate... that) { + if (that.length == 1) { + return that[0]; + } return new OrPredicate(that); } /** Combine the passed predicates into a single OR node. */ public static Predicate or( final Collection> that) { + if (that.size() == 1) { + return Iterables.getOnlyElement(that); + } return new OrPredicate(that); } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java index 4d8e806bbe..8c1157e8cf 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java @@ -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 { } 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 { 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 { @Operator public Predicate 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 m = parseAccount(who); + List 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 watchedby(String who) throws QueryParseException, OrmException { - Account account = args.accountResolver.find(who); - if (account == null) { - throw error("User " + who + " not found"); + Set m = parseAccount(who); + List 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 draftby(String who) throws QueryParseException, OrmException { - Account account = args.accountResolver.find(who); - if (account == null) { - throw error("User " + who + " not found"); + Set m = parseAccount(who); + List 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 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 m = args.accountResolver.findAll(who); + if (!m.isEmpty()) { + List> 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 { @Operator public Predicate owner(String who) throws QueryParseException, OrmException { - Set 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 p = new ArrayList(m.size()); - for (Account.Id id : m) { - p.add(new OwnerPredicate(args.dbProvider, id)); - } - return Predicate.or(p); + Set m = parseAccount(who); + List p = Lists.newArrayListWithCapacity(m.size()); + for (Account.Id id : m) { + p.add(new OwnerPredicate(args.dbProvider, id)); } + return Predicate.or(p); } @Operator - public Predicate ownerin(String group) throws QueryParseException, - OrmException { + public Predicate 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 { @Operator public Predicate reviewer(String who) throws QueryParseException, OrmException { - Set 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 p = new ArrayList(m.size()); - for (Account.Id id : m) { - p.add(new ReviewerPredicate(args.dbProvider, id)); - } - return Predicate.or(p); + Set m = parseAccount(who); + List p = Lists.newArrayListWithCapacity(m.size()); + for (Account.Id id : m) { + p.add(new ReviewerPredicate(args.dbProvider, id)); } + return Predicate.or(p); } @Operator public Predicate 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 { throw error("Unsupported query:" + query); } } + + private Set parseAccount(String who) + throws QueryParseException, OrmException { + if ("self".equals(who)) { + return Collections.singleton(self()); + } + Set 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(); + } }