Skip to content
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
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
how a consumer would use the library or CLI tool (e.g. adding unit tests, updating documentation, etc) are not captured
here.

## Unreleased
## 2.12.4 - 2026-09-30
- Fixed an issue where listing trusted activities in some cases would loop infinitely.

### 2.12.3 - 2026-09-11
## 2.12.3 - 2026-09-11
- The `retry_on_rate_limit` setting, which controls whether the SDK will retry 429 responses. Defaults to on, retrying most 429 responses up to 3 times. Set this to False on SDK initialization if you wish to manually handle retrying 429 responses. When enabled, if retries are exhausted, requests.exceptions.RetryError will be raised.

## 2.12.2 - 2026-06-22
Expand Down
2 changes: 1 addition & 1 deletion src/_incydr_sdk/__version__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-present Code42 Software <integrations@code42.com>
#
# SPDX-License-Identifier: MIT
__version__ = "2.12.3"
__version__ = "2.12.4"
11 changes: 9 additions & 2 deletions src/_incydr_sdk/trusted_activities/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ def iter_all(
"""

page_size = page_size or self._parent.settings.page_size
last_page_ids = set()
for page_num in count(1):
page = self.get_page(
page_num=page_num,
Expand All @@ -112,8 +113,14 @@ def iter_all(
sort_key=sort_key,
sort_direction=sort_direction,
)
yield from page.trusted_activities
if len(page.trusted_activities) < page_size:
results_to_yield = [
x for x in page.trusted_activities if x.activity_id not in last_page_ids
]
yield from results_to_yield
last_page_ids = {x.activity_id for x in page.trusted_activities}
if (len(page.trusted_activities) < page_size) or (
len(results_to_yield) < page_size
):
break

def add_domain(
Expand Down
38 changes: 37 additions & 1 deletion tests/test_trusted_activities.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
],
}
],
"activityId": "1324",
"activityId": "13245",
"isHighValueSource": True,
"description": "This is a description",
"principalType": "API_KEY",
Expand Down Expand Up @@ -142,6 +142,42 @@ def test_iter_all_when_default_params_returns_expected_data(
assert total_trusted_activities == 2


def test_iter_all_when_page_repeats_returns_expected_data(
httpserver_auth: HTTPServer,
):
query_1 = {
"page_num": 1,
"page_size": 2,
}
query_2 = {"page_num": 2, "page_size": 2}

trusted_activities_data_1 = {
"trustedActivities": [TEST_TRUSTED_ACTIVITY_1, TEST_TRUSTED_ACTIVITY_2],
"totalCount": 2,
}

httpserver_auth.expect_request(
"/v2/trusted-activities", method="GET", query_string=urlencode(query_1)
).respond_with_json(trusted_activities_data_1)

httpserver_auth.expect_request(
"/v2/trusted-activities", method="GET", query_string=urlencode(query_2)
).respond_with_json(trusted_activities_data_1)

client = Client()
iterator = client.trusted_activities.v2.iter_all(page_size=2)
total_trusted_activities = 0
expected_trusted_activities = [TEST_TRUSTED_ACTIVITY_1, TEST_TRUSTED_ACTIVITY_2]

for item in iterator:
total_trusted_activities += 1
assert isinstance(item, TrustedActivity)
assert item.json() == json.dumps(
expected_trusted_activities.pop(0), separators=(",", ":")
)
assert total_trusted_activities == 2


def test_add_domain_when_default_params_returns_expected_data(
httpserver_auth: HTTPServer,
):
Expand Down
Loading