Skip to content

Commit

Permalink
Minor/refactor tenant id from alertquery (#99)
Browse files Browse the repository at this point in the history
* -fix exceptions to wrap the response correctly
-adjust tests

* - argument naming consistency
- clarify docstring
- show inherited members in Exceptions methoddocs

* - update changelog
- add test that response is what we expect

* correct casing on DateObserved term

* fix AlertQuery to allow constructing with just tenant_id

* Refactor tenant_id from AlertQuery into the AlertClient.search() method

* update changelog

* rename to _add_tenant_id_if_missing
  • Loading branch information
timabrmsn committed Apr 28, 2020
1 parent 562b43b commit 7d9e7b3
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 40 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@ how a consumer would use the library (e.g. adding unit tests, updating documenta

## Unreleased

### Added

- `sdk.archives.update_cold_storage_purge_date()`, allowing the changing of the date at which a cold storage archive
will be purged.

### Changed

- Exceptions that inherit from `Py42HTTPError` now return the original `requests.Response` object on the exception's
`.response` property instead of a string representation of the `HTTPError` that was raised.

- `departure_date` is now an optional parameter for `sdk.detectionlists.departing_employee.add()`.

- Added `sdk.archives.update_cold_storage_purge_date()`, allowing the changing of the date at which a cold storage archive will be purged.
- `py42.sdk.queries.alerts.alert_query.AlertQuery` no longer requires a `tenant_id` to be added to the query manually,
the `AlertClient.search()` method now adds the tenant_id automatically from the user_context.


## 1.0.0 - 2020-04-21

Expand Down
9 changes: 1 addition & 8 deletions docs/userguides/searches.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,11 @@ To start, import the filters and query object:
```python
from py42.sdk.queries.alerts.filters import *
from py42.sdk.queries.alerts.alert_query import AlertQuery
```

The one difference between constructing alert queries and file event queries is that alert queries require a tenant
ID. You can get the tenant ID from the `sdk.usercontext` object:

```python
# Create a query for getting all open alerts with severity either 'High' or 'Medium'.

filters = [AlertState.eq(AlertState.OPEN), Severity.is_in([Severity.HIGH, Severity.MEDIUM])]
tenant_id = sdk.usercontext.get_current_tenant_id()
# Notice the constructor takes the tenant ID first.
query = AlertQuery(tenant_id, *filters)
query = AlertQuery(*filters)
```

To execute the search, use the `alerts.AlertClient.search()` method:
Expand Down
11 changes: 10 additions & 1 deletion src/py42/clients/alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def search(self, query):
:class:`py42.response.Py42Response`: A response containing the alerts that match the given
query.
"""
query = str(query)
query = self._add_tenant_id_if_missing(query)
uri = self._uri_prefix.format(u"query-alerts")
return self._session.post(uri, data=query)

Expand Down Expand Up @@ -94,3 +94,12 @@ def reopen(self, alert_ids, tenant_id=None, reason=None):
uri = self._uri_prefix.format(u"reopen-alert")
data = {u"tenantId": tenant_id, u"alertIds": alert_ids, u"reason": reason}
return self._session.post(uri, data=json.dumps(data))

def _add_tenant_id_if_missing(self, query):
query_dict = json.loads(str(query))
tenant_id = query_dict.get(u"tenantId", None)
if tenant_id is None:
query_dict[u"tenantId"] = self._user_context.get_current_tenant_id()
return json.dumps(query_dict)
else:
return str(query)
12 changes: 3 additions & 9 deletions src/py42/sdk/queries/alerts/alert_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,22 @@ class AlertQuery(BaseQuery):
For convenience, the :class:`AlertQuery` constructor does the same as ``all()``.
A tenant ID is required in either the constructor or ``all()`` or ``any()``. You can get the
tenant ID from the method :meth:`SDKClient.usercontext.get_current_tenant_id()`.
Usage example::
state_filter = AlertState.eq(AlertState.OPEN)
rule_name_filter = RuleName.contains("EmailRule")
tenant_id = sdk.usercontext.get_current_tenant_id()
query = AlertQuery(tenant_id).all(state_filter, rule_name_filter)
query = AlertQuery.all(state_filter, rule_name_filter)
"""

def __init__(self, tenant_id, *args, **kwargs):
def __init__(self, *args, **kwargs):
super(AlertQuery, self).__init__(*args, **kwargs)
self._tenant_id = tenant_id
self.sort_key = u"CreatedAt"
self.page_number = 0
self.sort_direction = u"desc"

def __str__(self):
groups_string = u",".join(str(group_item) for group_item in self._filter_group_list)
json = u'{{"tenantId":"{0}", "groupClause":"{1}", "groups":[{2}], "pgNum":{3}, "pgSize":{4}, "srtDirection":"{5}", "srtKey":"{6}"}}'.format(
self._tenant_id,
json = u'{{"tenantId": null, "groupClause":"{0}", "groups":[{1}], "pgNum":{2}, "pgSize":{3}, "srtDirection":"{4}", "srtKey":"{5}"}}'.format(
self._group_clause,
groups_string,
self.page_number,
Expand Down
2 changes: 1 addition & 1 deletion src/py42/sdk/queries/alerts/filters/alert_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def not_contains(cls, value):
class DateObserved(QueryFilterTimestampField):
"""Class that filters alerts based on the timestamp the alert was triggered."""

_term = u"CreatedAt"
_term = u"createdAt"


class Actor(AlertQueryFilterStringField):
Expand Down
4 changes: 2 additions & 2 deletions tests/clients/test_alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def successful_post(self, mock_session, successful_response):
def test_search_posts_expected_data(self, mock_session, user_context, successful_post):
alert_client = AlertClient(mock_session, user_context)
_filter = AlertState.eq("OPEN")
query = AlertQuery(TENANT_ID_FROM_RESPONSE, _filter)
query = AlertQuery(_filter)
alert_client.search(query)
post_data = json.loads(mock_session.post.call_args[1]["data"])
assert (
Expand All @@ -35,7 +35,7 @@ def test_search_posts_expected_data(self, mock_session, user_context, successful
def test_search_posts_to_expected_url(self, mock_session, user_context, successful_post):
alert_client = AlertClient(mock_session, user_context)
_filter = AlertState.eq("OPEN")
query = AlertQuery(TENANT_ID_FROM_RESPONSE, _filter)
query = AlertQuery(_filter)
alert_client.search(query)
assert mock_session.post.call_args[0][0] == u"/svc/api/v1/query-alerts"

Expand Down
36 changes: 18 additions & 18 deletions tests/sdk/queries/alerts/test_alert_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
format_timestamp,
)

_TENANT_ID = u"tenant-id"
JSON_QUERY_BASE = u'{{"tenantId":"{0}", "groupClause":"{1}", "groups":[{2}], "pgNum":{3}, "pgSize":{4}, "srtDirection":"{5}", "srtKey":"{6}"}}'
_TENANT_ID = u"null"
JSON_QUERY_BASE = u'{{"tenantId": {0}, "groupClause":"{1}", "groups":[{2}], "pgNum":{3}, "pgSize":{4}, "srtDirection":"{5}", "srtKey":"{6}"}}'


def build_query_json(group_clause, group_list):
Expand All @@ -34,7 +34,7 @@ def test_alert_query_repr_does_not_throw_type_error():
# On python 2, `repr` doesn't throw.
# On python 3, if `repr` doesn't return type `str`, then an exception is thrown.
try:
_ = repr(AlertQuery(_TENANT_ID))
_ = repr(AlertQuery())
except TypeError:
assert False

Expand All @@ -44,61 +44,61 @@ def test_alert_query_constructs_successfully(event_filter_group):


def test_alert_query_str_with_single_filter_gives_correct_json_representation(event_filter_group,):
alert_query = AlertQuery(_TENANT_ID, event_filter_group)
alert_query = AlertQuery(event_filter_group)
json_query_str = build_query_json("AND", event_filter_group)
assert str(alert_query) == json_query_str


def test_alert_query_unicode_with_single_filter_gives_correct_json_representation(
unicode_event_filter_group,
):
alert_query = AlertQuery(_TENANT_ID, unicode_event_filter_group)
alert_query = AlertQuery(unicode_event_filter_group)
json_query_str = build_query_json("AND", unicode_event_filter_group)
assert str(alert_query) == json_query_str


def test_alert_query_str_with_single_filter_and_specified_gives_correct_json_representation(
event_filter_group,
):
alert_query = AlertQuery(_TENANT_ID, event_filter_group, group_clause="AND")
alert_query = AlertQuery(event_filter_group, group_clause="AND")
json_query_str = build_query_json("AND", event_filter_group)
assert str(alert_query) == json_query_str


def test_alert_query_str_with_single_filter_or_specified_gives_correct_json_representation(
event_filter_group,
):
alert_query = AlertQuery(_TENANT_ID, event_filter_group, group_clause="OR")
alert_query = AlertQuery(event_filter_group, group_clause="OR")
json_query_str = build_query_json("OR", event_filter_group)
assert str(alert_query) == json_query_str


def test_alert_query_str_with_many_filters_gives_correct_json_representation(
event_filter_group_list,
):
alert_query = AlertQuery(_TENANT_ID, event_filter_group_list)
alert_query = AlertQuery(event_filter_group_list)
json_query_str = build_query_json("AND", event_filter_group_list)
assert str(alert_query) == json_query_str


def test_alert_query_str_with_many_filters_and_specified_gives_correct_json_representation(
event_filter_group_list,
):
alert_query = AlertQuery(_TENANT_ID, event_filter_group_list, group_clause="AND")
alert_query = AlertQuery(event_filter_group_list, group_clause="AND")
json_query_str = build_query_json("AND", event_filter_group_list)
assert str(alert_query) == json_query_str


def test_alert_query_str_with_many_filters_or_specified_gives_correct_json_representation(
event_filter_group_list,
):
alert_query = AlertQuery(_TENANT_ID, event_filter_group_list, group_clause="OR")
alert_query = AlertQuery(event_filter_group_list, group_clause="OR")
json_query_str = build_query_json("OR", event_filter_group_list)
assert str(alert_query) == json_query_str


def test_alert_query_str_with_page_num_gives_correct_json_representation(event_filter_group):
alert_query = AlertQuery(_TENANT_ID, event_filter_group)
alert_query = AlertQuery(event_filter_group)
alert_query.page_number = 5
json_query_str = JSON_QUERY_BASE.format(
_TENANT_ID, "AND", event_filter_group, 5, 10000, "desc", "CreatedAt"
Expand All @@ -107,7 +107,7 @@ def test_alert_query_str_with_page_num_gives_correct_json_representation(event_f


def test_alert_query_str_with_page_size_gives_correct_json_representation(event_filter_group):
alert_query = AlertQuery(_TENANT_ID, event_filter_group)
alert_query = AlertQuery(event_filter_group)
alert_query.page_size = 500
json_query_str = JSON_QUERY_BASE.format(
_TENANT_ID, "AND", event_filter_group, 0, 500, "desc", "CreatedAt"
Expand All @@ -116,7 +116,7 @@ def test_alert_query_str_with_page_size_gives_correct_json_representation(event_


def test_alert_query_str_with_sort_direction_gives_correct_json_representation(event_filter_group,):
alert_query = AlertQuery(_TENANT_ID, event_filter_group)
alert_query = AlertQuery(event_filter_group)
alert_query.sort_direction = "asc"
json_query_str = JSON_QUERY_BASE.format(
_TENANT_ID, "AND", event_filter_group, 0, 10000, "asc", "CreatedAt"
Expand All @@ -125,7 +125,7 @@ def test_alert_query_str_with_sort_direction_gives_correct_json_representation(e


def test_alert_query_str_with_sort_key_gives_correct_json_representation(event_filter_group):
alert_query = AlertQuery(_TENANT_ID, event_filter_group)
alert_query = AlertQuery(event_filter_group)
alert_query.sort_key = "some_field_to_sort_by"
json_query_str = JSON_QUERY_BASE.format(
_TENANT_ID, "AND", event_filter_group, 0, 10000, "desc", "some_field_to_sort_by"
Expand All @@ -137,15 +137,15 @@ def test_date_observed_on_or_after_str_gives_correct_json_representation():
test_time = time()
formatted = format_timestamp(test_time)
_filter = DateObserved.on_or_after(test_time)
expected = ON_OR_AFTER.format("CreatedAt", formatted)
expected = ON_OR_AFTER.format("createdAt", formatted)
assert str(_filter) == expected


def test_date_observed_on_or_before_str_gives_correct_json_representation():
test_time = time()
formatted = format_timestamp(test_time)
_filter = DateObserved.on_or_before(test_time)
expected = ON_OR_BEFORE.format("CreatedAt", formatted)
expected = ON_OR_BEFORE.format("createdAt", formatted)
assert str(_filter) == expected


Expand All @@ -155,7 +155,7 @@ def test_date_observed_in_range_str_gives_correct_json_representation():
formatted_before = format_timestamp(test_before_time)
formatted_after = format_timestamp(test_after_time)
_filter = DateObserved.in_range(test_before_time, test_after_time)
expected = IN_RANGE.format("CreatedAt", formatted_before, formatted_after)
expected = IN_RANGE.format("createdAt", formatted_before, formatted_after)
assert str(_filter) == expected


Expand All @@ -167,7 +167,7 @@ def test_date_observed_on_same_day_str_gives_correct_json_representation():
formatted_before = format_datetime(start_time)
formatted_after = format_datetime(end_time)
_filter = DateObserved.on_same_day(test_time)
expected = IN_RANGE.format("CreatedAt", formatted_before, formatted_after)
expected = IN_RANGE.format("createdAt", formatted_before, formatted_after)
assert str(_filter) == expected


Expand Down

0 comments on commit 7d9e7b3

Please sign in to comment.