Line data Source code
1 : // Copyright (C) 2021 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.approval; 16 : 17 : import static java.util.stream.Collectors.joining; 18 : 19 : import com.google.common.base.Enums; 20 : import com.google.common.primitives.Ints; 21 : import com.google.gerrit.entities.AccountGroup; 22 : import com.google.gerrit.entities.GroupDescription; 23 : import com.google.gerrit.extensions.client.ChangeKind; 24 : import com.google.gerrit.index.query.Predicate; 25 : import com.google.gerrit.index.query.QueryBuilder; 26 : import com.google.gerrit.index.query.QueryParseException; 27 : import com.google.gerrit.server.account.GroupControl; 28 : import com.google.gerrit.server.group.GroupResolver; 29 : import com.google.inject.Inject; 30 : import java.util.Arrays; 31 : import java.util.Optional; 32 : 33 : public class ApprovalQueryBuilder extends QueryBuilder<ApprovalContext, ApprovalQueryBuilder> { 34 148 : private static final QueryBuilder.Definition<ApprovalContext, ApprovalQueryBuilder> mydef = 35 : new QueryBuilder.Definition<>(ApprovalQueryBuilder.class); 36 : 37 : private final MagicValuePredicate.Factory magicValuePredicate; 38 : private final UserInPredicate.Factory userInPredicate; 39 : private final GroupResolver groupResolver; 40 : private final GroupControl.Factory groupControl; 41 : private final ListOfFilesUnchangedPredicate listOfFilesUnchangedPredicate; 42 : 43 : @Inject 44 : protected ApprovalQueryBuilder( 45 : MagicValuePredicate.Factory magicValuePredicate, 46 : UserInPredicate.Factory userInPredicate, 47 : GroupResolver groupResolver, 48 : GroupControl.Factory groupControl, 49 : ListOfFilesUnchangedPredicate listOfFilesUnchangedPredicate) { 50 148 : super(mydef, null); 51 148 : this.magicValuePredicate = magicValuePredicate; 52 148 : this.userInPredicate = userInPredicate; 53 148 : this.groupResolver = groupResolver; 54 148 : this.groupControl = groupControl; 55 148 : this.listOfFilesUnchangedPredicate = listOfFilesUnchangedPredicate; 56 148 : } 57 : 58 : @Operator 59 : public Predicate<ApprovalContext> changekind(String value) throws QueryParseException { 60 12 : return parseEnumValue(ChangeKind.class, value) 61 12 : .map(ChangeKindPredicate::new) 62 12 : .orElseThrow( 63 : () -> 64 1 : new QueryParseException( 65 1 : String.format( 66 : "%s is not a valid value for operator 'changekind'. Valid values: %s", 67 1 : value, formatEnumValues(ChangeKind.class)))); 68 : } 69 : 70 : @Operator 71 : public Predicate<ApprovalContext> is(String value) throws QueryParseException { 72 : // try to parse exact value 73 16 : Optional<Integer> exactValue = Optional.ofNullable(Ints.tryParse(value)); 74 16 : if (exactValue.isPresent()) { 75 3 : return new ExactValuePredicate(exactValue.get().shortValue()); 76 : } 77 : 78 : // try to parse magic value 79 16 : Optional<MagicValuePredicate.MagicValue> magicValue = 80 16 : parseEnumValue(MagicValuePredicate.MagicValue.class, value); 81 16 : if (magicValue.isPresent()) { 82 16 : return magicValuePredicate.create(magicValue.get()); 83 : } 84 : 85 : // it's neither an exact value nor a magic value 86 1 : throw new QueryParseException( 87 1 : String.format( 88 : "%s is not a valid value for operator 'is'. Valid values: %s or integer", 89 1 : value, formatEnumValues(MagicValuePredicate.MagicValue.class))); 90 : } 91 : 92 : @Operator 93 : public Predicate<ApprovalContext> approverin(String group) throws QueryParseException { 94 3 : return userInPredicate.create(UserInPredicate.Field.APPROVER, parseGroupOrThrow(group)); 95 : } 96 : 97 : @Operator 98 : public Predicate<ApprovalContext> uploaderin(String group) throws QueryParseException { 99 4 : return userInPredicate.create(UserInPredicate.Field.UPLOADER, parseGroupOrThrow(group)); 100 : } 101 : 102 : @Operator 103 : public Predicate<ApprovalContext> has(String value) throws QueryParseException { 104 2 : if (value.equals("unchanged-files")) { 105 2 : return listOfFilesUnchangedPredicate; 106 : } 107 1 : throw error( 108 1 : String.format( 109 : "'%s' is not a valid value for operator 'has'." 110 : + " The only valid value is 'unchanged-files'.", 111 : value)); 112 : } 113 : 114 : private static <T extends Enum<T>> Optional<T> parseEnumValue(Class<T> clazz, String value) { 115 17 : return Optional.ofNullable( 116 17 : Enums.getIfPresent(clazz, value.toUpperCase().replace('-', '_')).orNull()); 117 : } 118 : 119 : private <T extends Enum<T>> String formatEnumValues(Class<T> clazz) { 120 1 : return Arrays.stream(clazz.getEnumConstants()) 121 1 : .map(Object::toString) 122 1 : .sorted() 123 1 : .collect(joining(", ")); 124 : } 125 : 126 : private AccountGroup.UUID parseGroupOrThrow(String maybeUUID) throws QueryParseException { 127 5 : GroupDescription.Basic g = groupResolver.parseId(maybeUUID); 128 5 : if (g == null || !groupControl.controlFor(g).isVisible()) { 129 3 : throw error("Group " + maybeUUID + " not found"); 130 : } 131 5 : return g.getGroupUUID(); 132 : } 133 : }