Coverage for /builds/BuildGrid/buildgrid/buildgrid/server/operations/filtering/interpreter.py: 95.65%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

23 statements  

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. 

14 

15 

16import operator 

17from typing import List 

18 

19from lark import Tree 

20from lark.visitors import Interpreter 

21 

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) 

30 

31 

32# Valid operation filters mapped to regexes representing valid values. 

33VALID_OPERATION_FILTERS = { 

34 "stage": OperationStageValueSanitizer("stage"), 

35 

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+"), 

40 

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".+"), 

46 

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"), 

51 

52 # Backends determine what sort orders are acceptable 

53 "sort_order": SortKeyValueSanitizer("sort_order") 

54} 

55 

56 

57OPERATOR_MAP = { 

58 "=": operator.eq, 

59 ">": operator.gt, 

60 ">=": operator.ge, 

61 "<": operator.lt, 

62 "<=": operator.le, 

63 "!=": operator.ne 

64} 

65 

66 

67class FilterTreeInterpreter(Interpreter): 

68 """ Interpreter for the parse tree. 

69 

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) 

74 

75 def filter_elem(self, tree: Tree) -> OperationFilter: 

76 try: 

77 token_map = {token.type: str(token) for token in tree.children} # type: ignore 

78 

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}].") 

83 

84 # Sanitize the value 

85 sanitizer = VALID_OPERATION_FILTERS[parameter] 

86 sanitized_value = sanitizer.sanitize(token_map["VALUE"]) 

87 

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