Coverage for /builds/BuildGrid/buildgrid/buildgrid/server/operations/filtering/interpreter.py: 95.65%
23 statements
« prev ^ index » next coverage.py v6.4.1, created at 2022-06-22 21:04 +0000
« prev ^ index » next coverage.py v6.4.1, created at 2022-06-22 21:04 +0000
1# Copyright (C) 2020 Bloomberg LP
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.
16import operator
17from typing import List
19from lark import Tree
20from lark.visitors import Interpreter
22from buildgrid._exceptions import InvalidArgumentError
23from buildgrid.server.operations.filtering import OperationFilter
24from buildgrid.server.operations.filtering.sanitizer import (
25 DatetimeValueSanitizer,
26 OperationStageValueSanitizer,
27 RegexValueSanitizer,
28 SortKeyValueSanitizer
29)
32# Valid operation filters mapped to regexes representing valid values.
33VALID_OPERATION_FILTERS = {
34 "stage": OperationStageValueSanitizer("stage"),
36 # The operation name can technically be parsed as a UUID4, but this
37 # parses it as an arbitrary string in case the naming scheme changes
38 # in the future
39 "name": RegexValueSanitizer("name", r"\S+"),
41 # The request metadata is all arbitrary strings
42 "invocation_id": RegexValueSanitizer("invocation_id", r".+"),
43 "correlated_invocations_id": RegexValueSanitizer("correlated_invocations_id", r".+"),
44 "tool_name": RegexValueSanitizer("tool_name", r".+"),
45 "tool_version": RegexValueSanitizer("tool_version", r".+"),
47 # Validate timestamps with a special sanitizer
48 "queued_time": DatetimeValueSanitizer("queued_time"),
49 "start_time": DatetimeValueSanitizer("start_time"),
50 "completed_time": DatetimeValueSanitizer("completed_time"),
52 # Backends determine what sort orders are acceptable
53 "sort_order": SortKeyValueSanitizer("sort_order")
54}
57OPERATOR_MAP = {
58 "=": operator.eq,
59 ">": operator.gt,
60 ">=": operator.ge,
61 "<": operator.lt,
62 "<=": operator.le,
63 "!=": operator.ne
64}
67class FilterTreeInterpreter(Interpreter):
68 """ Interpreter for the parse tree.
70 Calling FilterTreeInterpreter().visit(tree) walks the parse tree and
71 outputs a list of OperationFilters. """
72 def filter_phrase(self, tree: Tree) -> List[OperationFilter]:
73 return self.visit_children(tree)
75 def filter_elem(self, tree: Tree) -> OperationFilter:
76 try:
77 token_map = {token.type: str(token) for token in tree.children} # type: ignore
79 # Check that the parameter is valid
80 parameter = token_map["PARAMETER"]
81 if parameter not in VALID_OPERATION_FILTERS:
82 raise InvalidArgumentError(f"Invalid filter parameter [{parameter}].")
84 # Sanitize the value
85 sanitizer = VALID_OPERATION_FILTERS[parameter]
86 sanitized_value = sanitizer.sanitize(token_map["VALUE"])
88 return OperationFilter(
89 parameter=token_map["PARAMETER"],
90 operator=OPERATOR_MAP[token_map["OPERATOR"]],
91 value=sanitized_value
92 )
93 except KeyError as e:
94 raise InvalidArgumentError(f"Invalid filter element. Token map: {token_map}") from e