예제 #1
0
    def test_grouping_simple(self):
        result = parse_search_query(
            '(user.email:[email protected] OR user.email:[email protected])')
        assert result == [
            SearchBoolean(left_term=self.term1,
                          operator="OR",
                          right_term=self.term2)
        ]
        result = parse_search_query(
            '(user.email:[email protected] OR user.email:[email protected]) AND user.email:[email protected]'
        )
        assert result == [
            SearchBoolean(left_term=SearchBoolean(left_term=self.term1,
                                                  operator='OR',
                                                  right_term=self.term2),
                          operator='AND',
                          right_term=self.term3)
        ]

        result = parse_search_query(
            'user.email:[email protected] AND (user.email:[email protected] OR user.email:[email protected])'
        )
        assert result == [
            SearchBoolean(left_term=self.term1,
                          operator='AND',
                          right_term=SearchBoolean(left_term=self.term2,
                                                   operator='OR',
                                                   right_term=self.term3))
        ]
예제 #2
0
    def test_simple(self):
        assert parse_search_query(
            'user.email:[email protected] OR user.email:[email protected]'
        ) == [SearchBoolean(left_term=self.term1, operator="OR", right_term=self.term2)]

        assert parse_search_query(
            'user.email:[email protected] AND user.email:[email protected]'
        ) == [SearchBoolean(left_term=self.term1, operator="AND", right_term=self.term2)]
예제 #3
0
 def test_order_of_operations(self):
     assert parse_search_query(
         'user.email:[email protected] OR user.email:[email protected] AND user.email:[email protected]'
     ) == [
         SearchBoolean(left_term=self.term1,
                       operator='OR',
                       right_term=SearchBoolean(left_term=self.term2,
                                                operator='AND',
                                                right_term=self.term3))
     ]
     assert parse_search_query(
         'user.email:[email protected] AND user.email:[email protected] OR user.email:[email protected]'
     ) == [
         SearchBoolean(left_term=SearchBoolean(
             left_term=self.term1,
             operator='AND',
             right_term=self.term2,
         ),
                       operator='OR',
                       right_term=self.term3)
     ]
예제 #4
0
 def test_nested_grouping(self):
     result = parse_search_query(
         '(user.email:[email protected] OR (user.email:[email protected] OR user.email:[email protected]))'
     )
     assert result == [
         SearchBoolean(left_term=self.term1,
                       operator="OR",
                       right_term=SearchBoolean(left_term=self.term2,
                                                operator="OR",
                                                right_term=self.term3))
     ]
     result = parse_search_query(
         '(user.email:[email protected] OR (user.email:[email protected] OR (user.email:[email protected] AND user.email:[email protected] OR user.email:[email protected])))'
     )
     assert result == [
         SearchBoolean(left_term=self.term1,
                       operator="OR",
                       right_term=SearchBoolean(
                           left_term=self.term2,
                           operator="OR",
                           right_term=SearchBoolean(
                               left_term=SearchBoolean(
                                   left_term=self.term3,
                                   operator="AND",
                                   right_term=self.term4),
                               operator="OR",
                               right_term=self.term5,
                           )))
     ]
예제 #5
0
    def test_multiple_statements(self):
        assert parse_search_query(
            'user.email:[email protected] OR user.email:[email protected] OR user.email:[email protected]'
        ) == [
            SearchBoolean(left_term=self.term1,
                          operator='OR',
                          right_term=SearchBoolean(left_term=self.term2,
                                                   operator='OR',
                                                   right_term=self.term3))
        ]

        assert parse_search_query(
            'user.email:[email protected] AND user.email:[email protected] AND user.email:[email protected]'
        ) == [
            SearchBoolean(left_term=self.term1,
                          operator='AND',
                          right_term=SearchBoolean(left_term=self.term2,
                                                   operator='AND',
                                                   right_term=self.term3))
        ]

        # longer even number of terms
        assert parse_search_query(
            'user.email:[email protected] AND user.email:[email protected] OR user.email:[email protected] AND user.email:[email protected]'
        ) == [
            SearchBoolean(left_term=SearchBoolean(left_term=self.term1,
                                                  operator='AND',
                                                  right_term=self.term2),
                          operator='OR',
                          right_term=SearchBoolean(left_term=self.term3,
                                                   operator='AND',
                                                   right_term=self.term4))
        ]

        # longer odd number of terms
        assert parse_search_query(
            'user.email:[email protected] AND user.email:[email protected] OR user.email:[email protected] AND user.email:[email protected] AND user.email:[email protected]'
        ) == [
            SearchBoolean(left_term=SearchBoolean(left_term=self.term1,
                                                  operator='AND',
                                                  right_term=self.term2),
                          operator='OR',
                          right_term=SearchBoolean(left_term=self.term3,
                                                   operator='AND',
                                                   right_term=SearchBoolean(
                                                       left_term=self.term4,
                                                       operator='AND',
                                                       right_term=self.term5)))
        ]

        # absurdly long
        assert parse_search_query(
            'user.email:[email protected] AND user.email:[email protected] OR user.email:[email protected] AND user.email:[email protected] AND user.email:[email protected] OR user.email:[email protected] AND user.email:[email protected] OR user.email:[email protected] AND user.email:[email protected] AND user.email:[email protected]'
        ) == [
            SearchBoolean(
                left_term=SearchBoolean(left_term=self.term1,
                                        operator='AND',
                                        right_term=self.term2),
                operator='OR',
                right_term=SearchBoolean(
                    left_term=SearchBoolean(left_term=self.term3,
                                            operator='AND',
                                            right_term=SearchBoolean(
                                                left_term=self.term4,
                                                operator='AND',
                                                right_term=self.term5)),
                    operator='OR',
                    right_term=SearchBoolean(
                        left_term=SearchBoolean(left_term=self.term1,
                                                operator='AND',
                                                right_term=self.term2),
                        operator='OR',
                        right_term=SearchBoolean(left_term=self.term3,
                                                 operator='AND',
                                                 right_term=SearchBoolean(
                                                     left_term=self.term4,
                                                     operator='AND',
                                                     right_term=self.term5)))))
        ]
예제 #6
0
def get_filter(query=None, params=None):
    """
    Returns an eventstore filter given the search text provided by the user and
    URL params
    """
    # NOTE: this function assumes project permissions check already happened
    parsed_terms = []
    if query is not None:
        try:
            parsed_terms = parse_search_query(query,
                                              allow_boolean=True,
                                              params=params)
        except ParseError as e:
            raise InvalidSearchQuery(
                f"Parse error: {e.expr.name} (column {e.column():d})")

    kwargs = {
        "start": None,
        "end": None,
        "conditions": [],
        "having": [],
        "user_id": None,
        "organization_id": None,
        "project_ids": [],
        "group_ids": [],
        "condition_aggregates": [],
        "aliases": params.get("aliases", {}) if params is not None else {},
    }

    projects_to_filter = []
    if any(
            isinstance(term, ParenExpression)
            or SearchBoolean.is_operator(term) for term in parsed_terms):
        (
            condition,
            having,
            found_projects_to_filter,
            group_ids,
        ) = convert_search_boolean_to_snuba_query(parsed_terms, params)

        if condition:
            and_conditions = flatten_condition_tree(condition, SNUBA_AND)
            for func in and_conditions:
                kwargs["conditions"].append(
                    convert_function_to_condition(func))
        if having:
            kwargs["condition_aggregates"] = [
                term.key.name for term in parsed_terms
                if isinstance(term, AggregateFilter)
            ]
            and_having = flatten_condition_tree(having, SNUBA_AND)
            for func in and_having:
                kwargs["having"].append(convert_function_to_condition(func))
        if found_projects_to_filter:
            projects_to_filter = list(set(found_projects_to_filter))
        if group_ids is not None:
            kwargs["group_ids"].extend(list(set(group_ids)))
    else:
        projects_to_filter = set()
        for term in parsed_terms:
            if isinstance(term, SearchFilter):
                conditions, found_projects_to_filter, group_ids = format_search_filter(
                    term, params)
                if len(conditions) > 0:
                    kwargs["conditions"].extend(conditions)
                if found_projects_to_filter:
                    projects_to_filter.update(found_projects_to_filter)
                if group_ids is not None:
                    kwargs["group_ids"].extend(group_ids)
            elif isinstance(term, AggregateFilter):
                converted_filter = convert_aggregate_filter_to_snuba_query(
                    term, params)
                kwargs["condition_aggregates"].append(term.key.name)
                if converted_filter:
                    kwargs["having"].append(converted_filter)
        projects_to_filter = list(projects_to_filter)

    # Keys included as url params take precedent if same key is included in search
    # They are also considered safe and to have had access rules applied unlike conditions
    # from the query string.
    if params:
        for key in ("start", "end"):
            kwargs[key] = params.get(key, None)
        # OrganizationEndpoint.get_filter() uses project_id, but eventstore.Filter uses project_ids
        if "user_id" in params:
            kwargs["user_id"] = params["user_id"]
        if "organization_id" in params:
            kwargs["organization_id"] = params["organization_id"]
        if "project_id" in params:
            if projects_to_filter:
                kwargs["project_ids"] = projects_to_filter
            else:
                kwargs["project_ids"] = params["project_id"]
        if "environment" in params:
            term = SearchFilter(SearchKey("environment"), "=",
                                SearchValue(params["environment"]))
            kwargs["conditions"].append(
                convert_search_filter_to_snuba_query(term))
        if "group_ids" in params:
            kwargs["group_ids"] = to_list(params["group_ids"])
        # Deprecated alias, use `group_ids` instead
        if ISSUE_ID_ALIAS in params:
            kwargs["group_ids"] = to_list(params["issue.id"])

    return eventstore.Filter(**kwargs)
예제 #7
0
def convert_search_boolean_to_snuba_query(terms, params=None):
    if len(terms) == 1:
        return convert_snuba_condition_to_function(terms[0], params)

    # Filter out any ANDs since we can assume anything without an OR is an AND. Also do some
    # basic sanitization of the query: can't have two operators next to each other, and can't
    # start or end a query with an operator.
    prev = None
    new_terms = []
    for term in terms:
        if prev:
            if SearchBoolean.is_operator(prev) and SearchBoolean.is_operator(
                    term):
                raise InvalidSearchQuery(
                    f"Missing condition in between two condition operators: '{prev} {term}'"
                )
        else:
            if SearchBoolean.is_operator(term):
                raise InvalidSearchQuery(
                    f"Condition is missing on the left side of '{term}' operator"
                )

        if term != SearchBoolean.BOOLEAN_AND:
            new_terms.append(term)
        prev = term
    if SearchBoolean.is_operator(term):
        raise InvalidSearchQuery(
            f"Condition is missing on the right side of '{term}' operator")
    terms = new_terms

    # We put precedence on AND, which sort of counter-intuitevely means we have to split the query
    # on ORs first, so the ANDs are grouped together. Search through the query for ORs and split the
    # query on each OR.
    # We want to maintain a binary tree, so split the terms on the first OR we can find and recurse on
    # the two sides. If there is no OR, split the first element out to AND
    index = None
    lhs, rhs = None, None
    operator = None
    try:
        index = terms.index(SearchBoolean.BOOLEAN_OR)
        lhs, rhs = terms[:index], terms[index + 1:]
        operator = SNUBA_OR
    except Exception:
        lhs, rhs = terms[:1], terms[1:]
        operator = SNUBA_AND

    (
        lhs_condition,
        lhs_having,
        projects_to_filter,
        group_ids,
    ) = convert_search_boolean_to_snuba_query(lhs, params)
    (
        rhs_condition,
        rhs_having,
        rhs_projects_to_filter,
        rhs_group_ids,
    ) = convert_search_boolean_to_snuba_query(rhs, params)

    projects_to_filter.extend(rhs_projects_to_filter)
    group_ids.extend(rhs_group_ids)

    if operator == SNUBA_OR and (lhs_condition or
                                 rhs_condition) and (lhs_having or rhs_having):
        raise InvalidSearchQuery(
            "Having an OR between aggregate filters and normal filters is invalid."
        )

    condition, having = None, None
    if lhs_condition or rhs_condition:
        args = filter(None, [lhs_condition, rhs_condition])
        if not args:
            condition = None
        elif len(args) == 1:
            condition = args[0]
        else:
            condition = [operator, args]

    if lhs_having or rhs_having:
        args = filter(None, [lhs_having, rhs_having])
        if not args:
            having = None
        elif len(args) == 1:
            having = args[0]
        else:
            having = [operator, args]

    return condition, having, projects_to_filter, group_ids
예제 #8
0
    def test_multiple_statements(self):
        assert parse_search_query(
            'user.email:[email protected] OR user.email:[email protected] OR user.email:[email protected]'
        ) == [
            SearchBoolean(left_term=self.term1,
                          operator='OR',
                          right_term=SearchBoolean(left_term=self.term2,
                                                   operator='OR',
                                                   right_term=self.term3))
        ]

        assert parse_search_query(
            'user.email:[email protected] AND user.email:[email protected] AND user.email:[email protected]'
        ) == [
            SearchBoolean(left_term=self.term1,
                          operator='AND',
                          right_term=SearchBoolean(left_term=self.term2,
                                                   operator='AND',
                                                   right_term=self.term3))
        ]

        term4 = SearchFilter(
            key=SearchKey(name='user.email'),
            operator="=",
            value=SearchValue(raw_value='*****@*****.**'),
        )

        # longer even number of terms
        assert parse_search_query(
            'user.email:[email protected] AND user.email:[email protected] OR user.email:[email protected] AND user.email:[email protected]'
        ) == [
            SearchBoolean(left_term=SearchBoolean(left_term=self.term1,
                                                  operator='AND',
                                                  right_term=self.term2),
                          operator='OR',
                          right_term=SearchBoolean(left_term=self.term3,
                                                   operator='AND',
                                                   right_term=term4))
        ]

        term5 = SearchFilter(
            key=SearchKey(name='user.email'),
            operator="=",
            value=SearchValue(raw_value='*****@*****.**'),
        )

        # longer odd number of terms
        assert parse_search_query(
            'user.email:[email protected] AND user.email:[email protected] OR user.email:[email protected] AND user.email:[email protected] AND user.email:[email protected]'
        ) == [
            SearchBoolean(left_term=SearchBoolean(left_term=self.term1,
                                                  operator='AND',
                                                  right_term=self.term2),
                          operator='OR',
                          right_term=SearchBoolean(left_term=self.term3,
                                                   operator='AND',
                                                   right_term=SearchBoolean(
                                                       left_term=term4,
                                                       operator='AND',
                                                       right_term=term5)))
        ]

        # absurdly long
        assert parse_search_query(
            'user.email:[email protected] AND user.email:[email protected] OR user.email:[email protected] AND user.email:[email protected] AND user.email:[email protected] OR user.email:[email protected] AND user.email:[email protected] OR user.email:[email protected] AND user.email:[email protected] AND user.email:[email protected]'
        ) == [
            SearchBoolean(
                left_term=SearchBoolean(left_term=self.term1,
                                        operator='AND',
                                        right_term=self.term2),
                operator='OR',
                right_term=SearchBoolean(
                    left_term=SearchBoolean(left_term=self.term3,
                                            operator='AND',
                                            right_term=SearchBoolean(
                                                left_term=term4,
                                                operator='AND',
                                                right_term=term5)),
                    operator='OR',
                    right_term=SearchBoolean(
                        left_term=SearchBoolean(left_term=self.term1,
                                                operator='AND',
                                                right_term=self.term2),
                        operator='OR',
                        right_term=SearchBoolean(left_term=self.term3,
                                                 operator='AND',
                                                 right_term=SearchBoolean(
                                                     left_term=term4,
                                                     operator='AND',
                                                     right_term=term5)))))
        ]