def test_aggregate_duration_filter(self): assert parse_search_query("avg(transaction.duration):>500s") == [ SearchFilter( key=AggregateKey(name="avg(transaction.duration)"), operator=">", value=SearchValue(raw_value=500000.0), ) ]
def test_aggregate_duration_filter_overrides_numeric_shorthand(self): # 2m should mean 2 minutes for duration filters (as opposed to 2 million) assert parse_search_query("avg(transaction.duration):>2m") == [ SearchFilter( key=AggregateKey(name="avg(transaction.duration)"), operator=">", value=SearchValue(raw_value=120000.0), ) ]
def test_invalid(self): filters = [SearchFilter(SearchKey("status"), "=", SearchValue("wrong"))] with self.assertRaises(InvalidSearchQuery, expected_regex="invalid status value"): convert_query_values(filters, [self.project], self.user, None) filters = [AggregateFilter(AggregateKey("count_unique(user)"), ">", SearchValue("1"))] with self.assertRaises( InvalidSearchQuery, expected_regex="Aggregate filters (count_unique(user)) are not supported in issue searches.", ): convert_query_values(filters, [self.project], self.user, None)
def test_aggregate_rel_time_filter(self): now = timezone.now() with freeze_time(now): assert parse_search_query("last_seen():+7d") == [ AggregateFilter( key=AggregateKey(name="last_seen()"), operator="<=", value=SearchValue(raw_value=now - timedelta(days=7)), ) ] assert parse_search_query("last_seen():-2w") == [ AggregateFilter( key=AggregateKey(name="last_seen()"), operator=">=", value=SearchValue(raw_value=now - timedelta(days=14)), ) ] assert parse_search_query("random:-2w") == [ SearchFilter(key=SearchKey(name="random"), operator="=", value=SearchValue("-2w")) ]
def node_visitor(token): if token["type"] == "spaces": return None if token["type"] == "filter": # Filters with an invalid reason raises to signal to the test # runner that we should expect this exception if token.get("invalid"): raise InvalidSearchQuery(token["invalid"]["reason"]) # Transform the operator to match for list values if token["value"]["type"] in ["valueTextList", "valueNumberList"]: operator = "NOT IN" if token["negated"] else "IN" else: # Negate the operator if the filter is negated to match operator = token["operator"] or "=" operator = f"!{operator}" if token["negated"] else operator key = node_visitor(token["key"]) value = node_visitor(token["value"]) if token["filter"] == "boolean" and token["negated"]: operator = "=" value = SearchValue(raw_value=1 if value.raw_value == 0 else 0) return SearchFilter(key, operator, value) if token["type"] == "keySimple": return SearchKey(name=token["value"]) if token["type"] == "keyExplicitTag": return SearchKey(name=f"tags[{token['key']['value']}]") if token["type"] == "keyAggregate": name = node_visitor(token["name"]).name # Consistent join aggregate function parameters args = ", ".join(arg["value"]["value"] for arg in token["args"]["args"]) return AggregateKey(name=f"{name}({args})") if token["type"] == "valueText": # Noramlize values by removing the escaped quotes value = token["value"].replace('\\"', '"') return SearchValue(raw_value=value) if token["type"] == "valueNumber": return SearchValue( raw_value=parse_numeric_value(token["value"], token["unit"])) if token["type"] == "valueTextList": return SearchValue( raw_value=[item["value"]["value"] for item in token["items"]]) if token["type"] == "valueNumberList": return SearchValue(raw_value=[ item["value"]["rawValue"] for item in token["items"] ]) if token["type"] == "valueIso8601Date": return SearchValue(raw_value=parse_datetime_string(token["value"])) if token["type"] == "valueDuration": return SearchValue( raw_value=parse_duration(token["value"], token["unit"])) if token["type"] == "valueRelativeDate": return SearchValue( raw_value=parse_duration(token["value"], token["unit"])) if token["type"] == "valueBoolean": return SearchValue(raw_value=int(token["value"])) if token["type"] == "freeText": if token["quoted"]: # Normalize quotes value = token["value"].replace('\\"', '"') else: # Normalize spacing value = token["value"].strip(" ") if value == "": return None return SearchFilter( key=SearchKey(name="message"), operator="=", value=SearchValue(raw_value=value), )