Line data Source code
1 : // Copyright (C) 2017 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.query.group; 16 : 17 : import static com.google.common.collect.ImmutableList.toImmutableList; 18 : 19 : import com.google.common.base.Strings; 20 : import com.google.common.collect.Lists; 21 : import com.google.common.primitives.Ints; 22 : import com.google.gerrit.entities.Account; 23 : import com.google.gerrit.entities.AccountGroup; 24 : import com.google.gerrit.entities.GroupReference; 25 : import com.google.gerrit.entities.InternalGroup; 26 : import com.google.gerrit.index.query.LimitPredicate; 27 : import com.google.gerrit.index.query.Predicate; 28 : import com.google.gerrit.index.query.QueryBuilder; 29 : import com.google.gerrit.index.query.QueryParseException; 30 : import com.google.gerrit.index.query.QueryRequiresAuthException; 31 : import com.google.gerrit.server.account.AccountResolver; 32 : import com.google.gerrit.server.account.AccountResolver.UnresolvableAccountException; 33 : import com.google.gerrit.server.account.GroupBackend; 34 : import com.google.gerrit.server.account.GroupBackends; 35 : import com.google.gerrit.server.account.GroupCache; 36 : import com.google.inject.Inject; 37 : import java.io.IOException; 38 : import java.util.List; 39 : import java.util.Optional; 40 : import java.util.Set; 41 : import org.eclipse.jgit.errors.ConfigInvalidException; 42 : 43 : /** Parses a query string meant to be applied to group objects. */ 44 : public class GroupQueryBuilder extends QueryBuilder<InternalGroup, GroupQueryBuilder> { 45 : public static final String FIELD_UUID = "uuid"; 46 : public static final String FIELD_DESCRIPTION = "description"; 47 : public static final String FIELD_INNAME = "inname"; 48 : public static final String FIELD_NAME = "name"; 49 : public static final String FIELD_OWNER = "owner"; 50 : public static final String FIELD_LIMIT = "limit"; 51 : 52 12 : private static final QueryBuilder.Definition<InternalGroup, GroupQueryBuilder> mydef = 53 : new QueryBuilder.Definition<>(GroupQueryBuilder.class); 54 : 55 : public static class Arguments { 56 : final GroupCache groupCache; 57 : final GroupBackend groupBackend; 58 : final AccountResolver accountResolver; 59 : 60 : @Inject 61 12 : Arguments(GroupCache groupCache, GroupBackend groupBackend, AccountResolver accountResolver) { 62 12 : this.groupCache = groupCache; 63 12 : this.groupBackend = groupBackend; 64 12 : this.accountResolver = accountResolver; 65 12 : } 66 : } 67 : 68 : private final Arguments args; 69 : 70 : @Inject 71 : GroupQueryBuilder(Arguments args) { 72 12 : super(mydef, null); 73 12 : this.args = args; 74 12 : } 75 : 76 : @Operator 77 : public Predicate<InternalGroup> uuid(String uuid) { 78 7 : return GroupPredicates.uuid(AccountGroup.uuid(uuid)); 79 : } 80 : 81 : @Operator 82 : public Predicate<InternalGroup> description(String description) throws QueryParseException { 83 7 : if (Strings.isNullOrEmpty(description)) { 84 2 : throw error("description operator requires a value"); 85 : } 86 : 87 7 : return GroupPredicates.description(description); 88 : } 89 : 90 : @Operator 91 : public Predicate<InternalGroup> inname(String namePart) { 92 7 : if (namePart.isEmpty()) { 93 0 : return name(namePart); 94 : } 95 7 : return GroupPredicates.inname(namePart); 96 : } 97 : 98 : @Operator 99 : public Predicate<InternalGroup> name(String name) { 100 12 : return GroupPredicates.name(name); 101 : } 102 : 103 : @Operator 104 : public Predicate<InternalGroup> owner(String owner) throws QueryParseException { 105 7 : AccountGroup.UUID groupUuid = parseGroup(owner); 106 7 : return GroupPredicates.owner(groupUuid); 107 : } 108 : 109 : @Operator 110 : public Predicate<InternalGroup> is(String value) throws QueryParseException { 111 2 : if ("visibletoall".equalsIgnoreCase(value)) { 112 2 : return GroupPredicates.isVisibleToAll(); 113 : } 114 0 : throw error("Invalid query"); 115 : } 116 : 117 : @Override 118 : protected Predicate<InternalGroup> defaultField(String query) throws QueryParseException { 119 : // Adapt the capacity of this list when adding more default predicates. 120 7 : List<Predicate<InternalGroup>> preds = Lists.newArrayListWithCapacity(5); 121 7 : preds.add(uuid(query)); 122 7 : preds.add(name(query)); 123 7 : preds.add(inname(query)); 124 7 : if (!Strings.isNullOrEmpty(query)) { 125 7 : preds.add(description(query)); 126 : } 127 : try { 128 7 : preds.add(owner(query)); 129 6 : } catch (QueryParseException e) { 130 : // Skip. 131 7 : } 132 7 : return Predicate.or(preds); 133 : } 134 : 135 : @Operator 136 : public Predicate<InternalGroup> member(String query) 137 : throws QueryParseException, ConfigInvalidException, IOException { 138 2 : Set<Account.Id> accounts = parseAccount(query); 139 2 : List<Predicate<InternalGroup>> predicates = 140 2 : accounts.stream().map(GroupPredicates::member).collect(toImmutableList()); 141 2 : return Predicate.or(predicates); 142 : } 143 : 144 : @Operator 145 : public Predicate<InternalGroup> subgroup(String query) throws QueryParseException { 146 2 : AccountGroup.UUID groupUuid = parseGroup(query); 147 2 : return GroupPredicates.subgroup(groupUuid); 148 : } 149 : 150 : @Operator 151 : public Predicate<InternalGroup> limit(String query) throws QueryParseException { 152 0 : Integer limit = Ints.tryParse(query); 153 0 : if (limit == null) { 154 0 : throw error("Invalid limit: " + query); 155 : } 156 0 : return new LimitPredicate<>(FIELD_LIMIT, limit); 157 : } 158 : 159 : private Set<Account.Id> parseAccount(String nameOrEmail) 160 : throws QueryParseException, IOException, ConfigInvalidException { 161 : try { 162 2 : return args.accountResolver.resolve(nameOrEmail).asNonEmptyIdSet(); 163 0 : } catch (UnresolvableAccountException e) { 164 0 : if (e.isSelf()) { 165 0 : throw new QueryRequiresAuthException(e.getMessage(), e); 166 : } 167 0 : throw new QueryParseException(e.getMessage(), e); 168 : } 169 : } 170 : 171 : private AccountGroup.UUID parseGroup(String groupNameOrUuid) throws QueryParseException { 172 7 : Optional<InternalGroup> group = args.groupCache.get(AccountGroup.uuid(groupNameOrUuid)); 173 7 : if (group.isPresent()) { 174 2 : return group.get().getGroupUUID(); 175 : } 176 7 : GroupReference groupReference = 177 7 : GroupBackends.findBestSuggestion(args.groupBackend, groupNameOrUuid); 178 7 : if (groupReference == null) { 179 6 : throw error("Group " + groupNameOrUuid + " not found"); 180 : } 181 7 : return groupReference.getUUID(); 182 : } 183 : }