Skip to content

Commit 6bf7bbb

Browse files
author
Ben Johnson
committed
PL-115218: add agent health last modified date
1 parent 969de1a commit 6bf7bbb

File tree

7 files changed

+89
-1
lines changed

7 files changed

+89
-1
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@
99
how a consumer would use the library or CLI tool (e.g. adding unit tests, updating documentation, etc) are not captured
1010
here.
1111

12+
## 2.10.0 - 2026-01-27
13+
14+
### Added
15+
- Added support for filtering agents by agent health state modification date.
16+
- Added the `agent_health_modified_in_last_days` parameter in the SDK's agent get methods to filter agents by health state modification date.
17+
- Added the `--agent-health-modified-within-days` option to the CLI's `incydr agents list` command to filter agents by health state modification date.
18+
- Added the `agent_health_modification_date` field to the Agent response model.
19+
1220
## 2.9.0 - 2026-01-22
1321

1422
### Added

src/_incydr_cli/cmds/agents.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,20 @@ def agents():
6060
"of the given health issue type(s). Health issue types include the following: NOT_CONNECTING, NOT_SENDING_SECURITY_EVENTS, SECURITY_INGEST_REJECTED, MISSING_MACOS_PERMISSION_FULL_DISK_ACCESS, MISSING_MACOS_PERMISSION_ACCESSIBILITY.",
6161
cls=incompatible_with("healthy"),
6262
)
63+
@click.option(
64+
"--agent-health-modified-within-days",
65+
type=int,
66+
default=None,
67+
help="Filter agents that have had agent health modified in the last N days (starting from midnight this morning), where N is the value of the parameter.",
68+
)
6369
@table_format_option
6470
@columns_option
6571
@logging_options
6672
def list_(
6773
active: bool = None,
6874
healthy: bool = None,
6975
unhealthy: str = None,
76+
agent_health_modified_within_days: int = None,
7077
format_: TableFormat = None,
7178
columns: str = None,
7279
):
@@ -90,6 +97,7 @@ def list_(
9097
active=active,
9198
agent_healthy=agent_healthy,
9299
agent_health_issue_types=health_issues,
100+
agent_health_modified_in_last_days=agent_health_modified_within_days,
93101
)
94102

95103
if format_ == TableFormat.table:

src/_incydr_sdk/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# SPDX-FileCopyrightText: 2022-present Code42 Software <integrations@code42.com>
22
#
33
# SPDX-License-Identifier: MIT
4-
__version__ = "2.9.0"
4+
__version__ = "2.10.0"

src/_incydr_sdk/agents/client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def get_page(
3939
page_size: int = 500,
4040
agent_healthy: bool = None,
4141
agent_health_issue_types: Union[List[str], str] = None,
42+
agent_health_modified_in_last_days: Optional[int] = None,
4243
user_id: str = None,
4344
) -> AgentsPage:
4445
"""
@@ -56,6 +57,7 @@ def get_page(
5657
* **sort_key**: [`SortKeys`][agents-sort-keys] - Values on which the response will be sorted. Defaults to agent name.
5758
* **agent_healthy**: `bool | None` - Optionally retrieve agents with this health status. Agents that have no health issue types are considered healthy.
5859
* **agent_health_issue_types**: `List[str] | str` - Optionally retrieve agents that have (at least) any of the given issue type(s). Health issue types include the following: `NOT_CONNECTING`, `NOT_SENDING_SECURITY_EVENTS`, `SECURITY_INGEST_REJECTED`, `MISSING_MACOS_PERMISSION_FULL_DISK_ACCESS`, `MISSING_MACOS_PERMISSION_ACCESSIBILITY`.
60+
* **agent_health_modified_in_last_days**: `int | None` - Optionally retrieve agents that have had their agent health modified in the last N days.
5961
* **user_id**: `str` - Optionally retrieve only agents associated with this user ID.
6062
6163
**Returns**: An [`AgentsPage`][agentspage-model] object.
@@ -67,6 +69,7 @@ def get_page(
6769
anyOfAgentHealthIssueTypes=[agent_health_issue_types]
6870
if isinstance(agent_health_issue_types, str)
6971
else agent_health_issue_types,
72+
agentHealthModifiedInLastDays=agent_health_modified_in_last_days,
7073
srtDir=sort_dir,
7174
srtKey=sort_key,
7275
pageSize=page_size,
@@ -85,6 +88,7 @@ def iter_all(
8588
page_size: int = 500,
8689
agent_healthy: bool = None,
8790
agent_health_issue_types: List[str] = None,
91+
agent_health_modified_in_last_days: Optional[int] = None,
8892
user_id: str = None,
8993
) -> Iterator[Agent]:
9094
"""
@@ -100,6 +104,7 @@ def iter_all(
100104
agent_type=agent_type,
101105
agent_healthy=agent_healthy,
102106
agent_health_issue_types=agent_health_issue_types,
107+
agent_health_modified_in_last_days=agent_health_modified_in_last_days,
103108
sort_dir=sort_dir,
104109
sort_key=sort_key,
105110
page_num=page_num,

src/_incydr_sdk/agents/models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class Agent(ResponseModel):
4848
* **active**: `bool` If the agent status is active.
4949
* **agent_type**: [`AgentType`][agent-type] The type of agent.
5050
* **agent_health_issue_types: `List[str]` List of health issues with the agent. Health issue types include the following: `NOT_CONNECTING`, `NOT_SENDING_SECURITY_EVENTS`, `SECURITY_INGEST_REJECTED`, `MISSING_MACOS_PERMISSION_FULL_DISK_ACCESS`, `MISSING_MACOS_PERMISSION_ACCESSIBILITY`.
51+
* **agent_health_modification_date**: `datetime` The time the agent's health state was last modified.
5152
* **app_version**: `str` The app version of the agent.
5253
* **product_version**: `str` The product version of the agent.
5354
* **last_connected**: `datetime` The time the agent last connected to a Code42 Authority server.
@@ -66,6 +67,7 @@ class Agent(ResponseModel):
6667
active: Optional[bool]
6768
agent_type: Optional[Union[AgentType, str]] = Field(alias="agentType")
6869
agent_health_issue_types: Optional[List[str]] = Field(alias="agentHealthIssueTypes")
70+
agent_health_modification_date: Optional[datetime] = Field(alias="agentHealthModificationDate")
6971
app_version: Optional[str] = Field(alias="appVersion")
7072
product_version: Optional[str] = Field(alias="productVersion")
7173
last_connected: Optional[datetime] = Field(alias="lastConnected")
@@ -102,6 +104,7 @@ class QueryAgentsRequest(BaseModel):
102104
agentType: Optional[Union[AgentType, str]] = None
103105
agentHealthy: Optional[bool] = None
104106
anyOfAgentHealthIssueTypes: Optional[List[str]] = None
107+
agentHealthModifiedInLastDays: Optional[int] = None
105108
srtKey: Optional[Union[SortKeys, str]] = None
106109
srtDir: Optional[str] = None
107110
pageSize: Optional[int] = None

tests/test_agents.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"active": True,
2727
"agentType": "COMBINED",
2828
"agentHealthIssueTypes": ["NOT_CONNECTING"],
29+
"agentHealthModificationDate": "2022-07-14T17:03:22.123000Z",
2930
"appVersion": "1.0",
3031
"productVersion": "2.0",
3132
"lastConnected": "2022-07-14T17:05:44.524000Z",
@@ -46,6 +47,7 @@
4647
"active": True,
4748
"agentType": "COMBINED",
4849
"agentHealthIssueTypes": [],
50+
"agentHealthModificationDate": "2022-07-14T17:02:15.456000Z",
4951
"appVersion": "1.0",
5052
"productVersion": "2.0",
5153
"lastConnected": "2022-07-14T17:05:44.524000Z",
@@ -65,6 +67,7 @@
6567
"active": True,
6668
"agentType": "COMBINED",
6769
"agentHealthIssueTypes": [],
70+
"agentHealthModificationDate": "2022-07-14T17:01:30.789000Z",
6871
"appVersion": "1.0",
6972
"productVersion": "2.0",
7073
"lastConnected": "2022-07-14T17:05:44.524000Z",
@@ -117,6 +120,9 @@ def test_get_agent_returns_expected_data(mock_get_agent):
117120
assert agent.modification_date == datetime.fromisoformat(
118121
TEST_AGENT_1["modificationDate"].replace("Z", "+00:00")
119122
)
123+
assert agent.agent_health_modification_date == datetime.fromisoformat(
124+
TEST_AGENT_1["agentHealthModificationDate"].replace("Z", "+00:00")
125+
)
120126

121127

122128
def test_get_page_when_default_query_params_returns_expected_data(
@@ -165,6 +171,35 @@ def test_get_page_when_custom_query_params_returns_expected_data(
165171
assert page.total_count == len(page.agents) == 2
166172

167173

174+
def test_get_page_when_agent_health_modified_in_last_days_passed_makes_expected_call(
175+
httpserver_auth: HTTPServer,
176+
):
177+
query = {
178+
"agentHealthModifiedInLastDays": 7,
179+
"srtKey": "NAME",
180+
"srtDir": "ASC",
181+
"pageSize": 500,
182+
"page": 1,
183+
}
184+
185+
agents_data = {
186+
"agents": [TEST_AGENT_1, TEST_AGENT_2],
187+
"totalCount": 2,
188+
"pageSize": 500,
189+
"page": 1,
190+
}
191+
httpserver_auth.expect_request(
192+
uri="/v1/agents", method="GET", query_string=urlencode(query)
193+
).respond_with_json(agents_data)
194+
195+
client = Client()
196+
page = client.agents.v1.get_page(agent_health_modified_in_last_days=7)
197+
assert isinstance(page, AgentsPage)
198+
assert page.agents[0].json() == json.dumps(TEST_AGENT_1, separators=(",", ":"))
199+
assert page.agents[1].json() == json.dumps(TEST_AGENT_2, separators=(",", ":"))
200+
assert page.total_count == len(page.agents) == 2
201+
202+
168203
def test_iter_all_when_default_params_returns_expected_data(
169204
httpserver_auth: HTTPServer,
170205
):
@@ -346,6 +381,32 @@ def test_cli_list_when_unhealthy_option_passed_with_string_parses_issue_types_co
346381
assert result.exit_code == 0
347382

348383

384+
def test_cli_list_when_health_modified_days_option_passed_makes_expected_call(
385+
httpserver_auth: HTTPServer, runner
386+
):
387+
query = {
388+
"agentHealthModifiedInLastDays": 7,
389+
"srtKey": "NAME",
390+
"srtDir": "ASC",
391+
"pageSize": 500,
392+
"page": 1,
393+
}
394+
395+
agents_data = {
396+
"agents": [TEST_AGENT_1, TEST_AGENT_2],
397+
"totalCount": 2,
398+
"pageSize": 500,
399+
"page": 1,
400+
}
401+
httpserver_auth.expect_request(
402+
uri="/v1/agents", method="GET", query_string=urlencode(query)
403+
).respond_with_json(agents_data)
404+
405+
result = runner.invoke(incydr, ["agents", "list", "--agent-health-modified-within-days", "7"])
406+
httpserver_auth.check()
407+
assert result.exit_code == 0
408+
409+
349410
def test_cli_show_when_custom_params_makes_expected_call(
350411
httpserver_auth: HTTPServer, runner, mock_get_agent
351412
):

tests/test_users.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"active": True,
3535
"agentType": "COMBINED",
3636
"agentHealthIssueTypes": ["NOT_CONNECTING"],
37+
"agentHealthModificationDate": "2022-07-14T17:03:22.123000Z",
3738
"appVersion": "1.0",
3839
"productVersion": "2.0",
3940
"lastConnected": "2022-07-14T17:05:44.524000Z",
@@ -54,6 +55,7 @@
5455
"active": True,
5556
"agentType": "COMBINED",
5657
"agentHealthIssueTypes": [],
58+
"agentHealthModificationDate": "2022-07-14T17:02:15.456000Z",
5759
"appVersion": "1.0",
5860
"productVersion": "2.0",
5961
"lastConnected": "2022-07-14T17:05:44.524000Z",
@@ -73,6 +75,7 @@
7375
"active": True,
7476
"agentType": "COMBINED",
7577
"agentHealthIssueTypes": [],
78+
"agentHealthModificationDate": "2022-07-14T17:01:30.789000Z",
7679
"appVersion": "1.0",
7780
"productVersion": "2.0",
7881
"lastConnected": "2022-07-14T17:05:44.524000Z",

0 commit comments

Comments
 (0)