Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

282 fix and update filters #310

Merged
merged 2 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions tests/test_query_filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from thehive4py import TheHiveApi
from thehive4py.helpers import now_to_ts
from thehive4py.query.filters import (
Between,
Contains,
EndsWith,
Eq,
Gt,
Gte,
Id,
In,
Like,
Lt,
Lte,
Match,
Ne,
StartsWith,
)
from thehive4py.types.user import OutputUser


class TestQueryFilters:
def test_between_contains_in(self, thehive: TheHiveApi, test_user: OutputUser):
assert thehive.user.find(
filters=Between(field="_createdAt", start=0, end=now_to_ts())
)
assert thehive.user.find(filters=Contains(field="login"))
assert thehive.user.find(
filters=In(field="login", values=["...", "xyz", test_user["login"]])
)

def test_endswith_startswith(self, thehive: TheHiveApi, test_user: OutputUser):
assert thehive.user.find(
filters=EndsWith(field="login", value=test_user["login"])
)
assert thehive.user.find(
filters=StartsWith(field="login", value=test_user["login"])
)

def test_eq_ne_id(self, thehive: TheHiveApi, test_user: OutputUser):
assert thehive.user.find(filters=Eq(field="_id", value=test_user["_id"]))
assert thehive.user.find(filters=Ne(field="_id", value=test_user["login"]))
assert thehive.user.find(filters=Id(id=test_user["_id"]))

def test_gt_gte_lt_lte(self, thehive: TheHiveApi, test_user: OutputUser):
assert thehive.user.find(filters=Gt(field="_createdAt", value=0))
assert thehive.user.find(filters=Gte(field="_createdAt", value=0))
assert thehive.user.find(filters=Lt(field="_createdAt", value=now_to_ts()))
assert thehive.user.find(filters=Lte(field="_createdAt", value=now_to_ts()))

def test_like_match(self, thehive: TheHiveApi, test_user: OutputUser):
assert thehive.user.find(filters=Like(field="login", value=test_user["login"]))
assert thehive.user.find(filters=Match(field="login", value=test_user["login"]))

def test_and_or_not(self, thehive: TheHiveApi, test_user: OutputUser):
assert thehive.user.find(
filters=Id(id=test_user["_id"])
& (
Eq(field="login", value=test_user["login"])
| Eq(field="login", value="...")
)
& ~Eq(field="login", value="...")
)
9 changes: 4 additions & 5 deletions thehive4py/query/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from typing import List, Union

from .filters import Between, Contains, Eq # noqa
from .filters import FilterExpr as _FilterExpr # noqa
from .filters import Gt, Gte, Like, Lt, Lte # noqa
from .filters import Match, Before, After, StartsWith, EndsWith # noqa
from .filters import Between, Contains, EndsWith, Eq
from .filters import FilterExpr as _FilterExpr
from .filters import Gt, Gte, Id, In, Like, Lt, Lte, Match, Ne, StartsWith
from .page import Paginate
from .sort import Asc, Desc # noqa
from .sort import Asc, Desc
from .sort import SortExpr as _SortExpr

QueryExpr = List[Union[_FilterExpr, _SortExpr, Paginate, dict]]
133 changes: 85 additions & 48 deletions thehive4py/query/filters.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
from collections import UserDict
from collections import UserDict as _UserDict
from typing import Any as _Any
from typing import Union as _Union

FilterExpr = _Union["_FilterBase", dict]

class FilterExpr(UserDict):
"""Base class for filter expressions."""

def __and__(self, other: "FilterExpr") -> "FilterExpr":
if not isinstance(other, FilterExpr):
class _FilterBase(_UserDict):
"""Base class for filters."""

def __and__(self, other: "_FilterBase") -> "_FilterBase":
if not isinstance(other, _FilterBase):
self._raise_type_error("&", self, other)
args = self.get("_and", [self]) + other.get("_and", [other])
return FilterExpr(_and=args)
return _FilterBase(_and=args)

def __or__(self, other: "FilterExpr") -> "FilterExpr": # type:ignore
if not isinstance(other, FilterExpr):
def __or__(self, other: "_FilterBase") -> "_FilterBase": # type:ignore
if not isinstance(other, _FilterBase):
self._raise_type_error("|", self, other)
args = self.get("_or", [self]) + other.get("_or", [other])
return FilterExpr(_or=args)
return _FilterBase(_or=args)

def __invert__(self) -> "FilterExpr":
return FilterExpr(_not=self)
def __invert__(self) -> "_FilterBase":
return _FilterBase(_not=self)

def _raise_type_error(self, operand, first, second):
raise TypeError(
Expand All @@ -26,66 +30,99 @@ def _raise_type_error(self, operand, first, second):
)


class Eq(FilterExpr):
def __init__(self, field: str, value):
super().__init__(_field=field, _value=value)
class Lt(_FilterBase):
"""Field less than value."""

def __init__(self, field: str, value: _Any):
super().__init__(_lt={field: value})

class Like(FilterExpr):
def __init__(self, field: str, value):
super().__init__(_like={"_field": field, "_value": value})

class Gt(_FilterBase):
"""Field greater than value."""

class Match(FilterExpr):
def __init__(self, field: str, value):
super().__init__(_match={"_field": field, "_value": value})
def __init__(self, field: str, value: _Any):
super().__init__(_gt={field: value})


class StartsWith(FilterExpr):
def __init__(self, field: str, value):
super().__init__(_startsWith={"_field": field, "_value": value})
class Lte(_FilterBase):
"""Field less than or equal value."""

def __init__(self, field: str, value: _Any):
super().__init__(_lte={field: value})

class EndsWith(FilterExpr):
def __init__(self, field: str, value):
super().__init__(_endsWith={"_field": field, "_value": value})

class Gte(_FilterBase):
"""Field less than or equal value."""

class Contains(FilterExpr):
def __init__(self, field: str):
super().__init__(_contains=field)
def __init__(self, field: str, value: _Any):
super().__init__(_gte={field: value})


class Gt(FilterExpr):
def __init__(self, field: str, value):
super().__init__(_gt={field: value})
class Ne(_FilterBase):
"""Field not equal value."""

def __init__(self, field: str, value: _Any):
super().__init__(_ne={"_field": field, "_value": value})

class Gte(FilterExpr):
def __init__(self, field: str, value):
super().__init__(_gte={field: value})

class Eq(_FilterBase):
"""Field equal value."""

class Lt(FilterExpr):
def __init__(self, field: str, value):
super().__init__(_lt={field: value})
def __init__(self, field: str, value: _Any):
super().__init__(_eq={"_field": field, "_value": value})


class Lte(FilterExpr):
def __init__(self, field: str, value):
super().__init__(_lte={field: value})
class StartsWith(_FilterBase):
"""Field starts with value."""

def __init__(self, field: str, value: str):
super().__init__(_startsWith={"_field": field, "_value": value})


class Between(FilterExpr):
class EndsWith(_FilterBase):
"""Field ends with value."""

def __init__(self, field: str, value: str):
super().__init__(_endsWith={"_field": field, "_value": value})


class Id(_FilterBase):
"""FIlter by ID."""

def __init__(self, id: str):
super().__init__(_id=id)


class Between(_FilterBase):
"""Field between inclusive from and exclusive to values."""

def __init__(self, field: str, start: int, end: int):
super().__init__(_between={"_field": field, "_from": start, "_to": end})


class Before(FilterExpr):
def __init__(self, field: str, end: int):
super().__init__(_between={"_field": field, "_to": end})
class In(_FilterBase):
"""Field is one of the values."""

def __init__(self, field: _Any, values: list):
super().__init__(_in={"_field": field, "_values": values})


class Contains(_FilterBase):
"""Object contains the field."""

def __init__(self, field: str):
super().__init__(_contains=field)


class After(FilterExpr):
def __init__(self, field: str, start: int):
super().__init__(_between={"_field": field, "_from": start})
class Like(_FilterBase):
"""Field contains the value."""

def __init__(self, field: str, value: str):
super().__init__(_like={"_field": field, "_value": value})


class Match(_FilterBase):
"""Field contains the value"""

def __init__(self, field: str, value: str):
super().__init__(_match={"_field": field, "_value": value})