Skip to content

Commit 9d0701f

Browse files
Merge pull request #157 from code42/INTEG-2943/pydantic-upgrade
INTEG-2943 - pydantic v2
2 parents 13a9202 + 9a225a3 commit 9d0701f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1280
-726
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
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+
## Unreleased
13+
14+
### Updated
15+
- The Incydr SDK and CLI now rely on Pydantic v2, instead of previously when they used v1. This means that the methods available on the models accepted and returned by many SDK methods have changed in some small ways. For most SDK and CLI workflows, no changes will need to be made to accommodate this upgrade. Details of the transition may be found [in Pydantic's documentation](https://docs.pydantic.dev/dev/migration/).
16+
1217
## 2.6.0 - 2025-07-23
1318

1419
### Added

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ dependencies = [
2828
"requests",
2929
"requests-toolbelt",
3030
"rich",
31-
"pydantic[dotenv]==1.*",
31+
"pydantic>=2.11",
32+
"pydantic-settings",
3233
"isodate",
3334
"python-dateutil",
3435
]

src/_incydr_cli/cmds/alerts.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -273,13 +273,13 @@ def bulk_update_state(
273273

274274
class AlertBulkCSV(CSVModel):
275275
alert_id: str = Field(csv_aliases=["id", "alert_id"])
276-
state: state_type = Field(csv_aliases=["state"])
277-
note: Optional[str]
276+
state: state_type = Field(None, csv_aliases=["state"]) # type: ignore
277+
note: Optional[str] = None
278278

279279
class AlertBulkJSON(Model):
280280
alert_id: str = Field(alias="id")
281-
state: state_type
282-
note: Optional[str]
281+
state: state_type = None # type: ignore
282+
note: Optional[str] = None
283283

284284
client = Client()
285285
if format_ == "csv":

src/_incydr_cli/cmds/audit_log.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -280,9 +280,9 @@ def _get_cursor_store(api_key):
280280

281281

282282
class DefaultAuditEvent(Model):
283-
type: str = Field(None, alias="type$")
284-
actor_id: str = Field(None, alias="actorId")
285-
actor_name: str = Field(None, alias="actorName")
286-
actor_agent: str = Field(None, alias="actorAgent")
287-
actor_ip_addresses: str = Field(None, alias="actorIpAddress")
288-
timestamp: str
283+
type: Optional[str] = Field(None, alias="type$")
284+
actor_id: Optional[str] = Field(None, alias="actorId")
285+
actor_name: Optional[str] = Field(None, alias="actorName")
286+
actor_agent: Optional[str] = Field(None, alias="actorAgent")
287+
actor_ip_addresses: Optional[str] = Field(None, alias="actorIpAddress")
288+
timestamp: str = None

src/_incydr_cli/cmds/cases.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import click
77
from pydantic import Field
8-
from pydantic import root_validator
8+
from pydantic import model_validator
99
from requests.exceptions import HTTPError
1010
from rich.panel import Panel
1111
from rich.progress import track
@@ -51,7 +51,8 @@ class FileEventCSV(CSVModel):
5151
class FileEventJSON(FileEventV2):
5252
event_id: str = Field(alias="eventId")
5353

54-
@root_validator(pre=True)
54+
@model_validator(mode="before")
55+
@classmethod
5556
def _event_id_required(cls, values): # noqa
5657
# check if input is V2 file event
5758
event = values.get("event")

src/_incydr_cli/cmds/models.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Optional
22

33
from pydantic import Field
4-
from pydantic import root_validator
4+
from pydantic import model_validator
55

66
from _incydr_sdk.core.models import CSVModel
77
from _incydr_sdk.core.models import Model
@@ -12,10 +12,11 @@ class UserCSV(CSVModel):
1212

1313

1414
class UserJSON(Model):
15-
username: Optional[str]
16-
userId: Optional[str]
15+
username: Optional[str] = None
16+
userId: Optional[str] = None
1717

18-
@root_validator(pre=True)
18+
@model_validator(mode="before")
19+
@classmethod
1920
def _validate(cls, values): # noqa
2021
if "username" not in values and "userId" not in values:
2122
raise ValueError("A json key of 'username' or 'userId' is required")
@@ -31,9 +32,10 @@ class AgentCSV(CSVModel):
3132

3233

3334
class AgentJSON(Model):
34-
agent_id: Optional[str]
35+
agent_id: Optional[str] = None
3536

36-
@root_validator(pre=True)
37+
@model_validator(mode="before")
38+
@classmethod
3739
def _validate(cls, values): # noqa
3840
if "agentGuid" in values:
3941
values["agent_id"] = values["agentGuid"]

src/_incydr_cli/cmds/sessions.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -344,14 +344,14 @@ def bulk_update_state(
344344
# Validate input models
345345

346346
class SessionCSV(CSVModel):
347-
session_id: str = Field(csv_aliases=["session_id", "sessionId"])
348-
state: state_type = Field(csv_aliases=["state"])
349-
note: Optional[str]
347+
session_id: str = Field(None, csv_aliases=["session_id", "sessionId"])
348+
state: state_type = Field(None, csv_aliases=["state"]) # type: ignore
349+
note: Optional[str] = None
350350

351351
class SessionJSON(Model):
352352
session_id: str = Field(alias="sessionId")
353-
state: state_type
354-
note: Optional[str]
353+
state: state_type = None # type: ignore
354+
note: Optional[str] = None
355355

356356
if format_ == "csv":
357357
models = SessionCSV.parse_csv(file)

src/_incydr_sdk/actors/models.py

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88

99

1010
class QueryActorsRequest(BaseModel):
11-
nameStartsWith: Optional[str]
12-
nameEndsWith: Optional[str]
13-
active: Optional[bool]
14-
pageSize: Optional[int]
15-
page: Optional[int]
11+
nameStartsWith: Optional[str] = None
12+
nameEndsWith: Optional[str] = None
13+
active: Optional[bool] = None
14+
pageSize: Optional[int] = None
15+
page: Optional[int] = None
1616

1717

1818
class Actor(ResponseModel):
@@ -43,90 +43,93 @@ class Actor(ResponseModel):
4343
"""
4444

4545
active: Optional[bool] = Field(
46-
None, description="Whether or not the actor is active.", example=True
46+
None, description="Whether or not the actor is active.", examples=[True]
4747
)
4848
actor_id: Optional[str] = Field(
49-
None, alias="actorId", description="The actor ID.", example="112233445566"
49+
None, alias="actorId", description="The actor ID.", examples=["112233445566"]
5050
)
5151
alternate_names: Optional[List[str]] = Field(
5252
None,
5353
alias="alternateNames",
5454
description="A list of other names the actor may have.",
55-
example=["johnsmith@gmail.com", "john-smith@company.com"],
55+
examples=[["johnsmith@gmail.com", "john-smith@company.com"]],
5656
)
5757
country: Optional[str] = Field(
58-
None, description="The actor's country", example="United States"
58+
None, description="The actor's country", examples=["United States"]
5959
)
6060
department: Optional[str] = Field(
61-
None, description="The actor's department", example="Product"
61+
None, description="The actor's department", examples=["Product"]
6262
)
6363
division: Optional[str] = Field(
64-
None, description="The actor's division", example="Engineering"
64+
None, description="The actor's division", examples=["Engineering"]
6565
)
6666
employee_type: Optional[str] = Field(
6767
None,
6868
alias="employeeType",
6969
description="The actor's employment, such as if they're a contractor.",
70-
example="full-time",
70+
examples=["full-time"],
7171
)
7272
end_date: Optional[str] = Field(
73-
None, alias="endDate", description="The actor's end date.", example="2024-09-18"
73+
None,
74+
alias="endDate",
75+
description="The actor's end date.",
76+
examples=["2024-09-18"],
7477
)
7578
first_name: Optional[str] = Field(
7679
None,
7780
alias="firstName",
7881
description="The first name of the actor.",
79-
example="Smith",
82+
examples=["Smith"],
8083
)
8184
in_scope: Optional[bool] = Field(
8285
None,
8386
alias="inScope",
8487
description="The actor's scope state. An actor is considered 'in scope' if their activity is monitored in at least one data source.",
85-
example=True,
88+
examples=[True],
8689
)
8790
last_name: Optional[str] = Field(
8891
None,
8992
alias="lastName",
9093
description="The last name of the actor.",
91-
example="John",
94+
examples=["John"],
9295
)
9396
locality: Optional[str] = Field(
94-
None, description="The actor's locality (city).", example="Minneapolis"
97+
None, description="The actor's locality (city).", examples=["Minneapolis"]
9598
)
9699
manager_actor_id: Optional[str] = Field(
97100
None,
98101
alias="managerActorId",
99102
description="The actor ID of the actor's manager",
100-
example="9988776655",
103+
examples=["9988776655"],
101104
)
102105
name: Optional[str] = Field(
103106
None,
104107
description="The actor's (eg. full username/email) name.",
105-
example="john.smith@gmail.com",
108+
examples=["john.smith@gmail.com"],
106109
)
107110
notes: Optional[str] = Field(
108111
None,
109112
alias="notes",
110113
description="Notes about the actor.",
111-
example="A super cool person.",
114+
examples=["A super cool person."],
112115
)
113116
parent_actor_id: Optional[str] = Field(
114117
None,
115118
alias="parentActorId",
116119
description="The actor ID of this actor's parent actor. (if the actor has a parent).",
117-
example="442244224422",
120+
examples=["442244224422"],
118121
)
119122
region: Optional[str] = Field(
120-
None, description="The actor's region.", example="Minnesota"
123+
None, description="The actor's region.", examples=["Minnesota"]
121124
)
122125
start_date: Optional[str] = Field(
123126
None,
124127
alias="startDate",
125128
description="The actor's start date.",
126-
example="2024-09-18",
129+
examples=["2024-09-18"],
127130
)
128131
title: Optional[str] = Field(
129-
None, description="The actor's job title", example="Software Engineer"
132+
None, description="The actor's job title", examples=["Software Engineer"]
130133
)
131134

132135

src/_incydr_sdk/agents/models.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from pydantic import BaseModel
99
from pydantic import Field
10-
from pydantic import validator
10+
from pydantic import field_validator
1111

1212
from _incydr_sdk.core.models import ResponseModel
1313
from _incydr_sdk.enums import _Enum
@@ -98,16 +98,17 @@ class AgentUpdateRequest(BaseModel):
9898

9999

100100
class QueryAgentsRequest(BaseModel):
101-
active: Optional[bool]
102-
agentType: Optional[Union[AgentType, str]]
103-
agentHealthy: Optional[bool]
104-
anyOfAgentHealthIssueTypes: Optional[List[str]]
105-
srtKey: Optional[Union[SortKeys, str]]
106-
srtDir: Optional[str]
107-
pageSize: Optional[int]
108-
page: Optional[int]
109-
110-
@validator("srtDir")
101+
active: Optional[bool] = None
102+
agentType: Optional[Union[AgentType, str]] = None
103+
agentHealthy: Optional[bool] = None
104+
anyOfAgentHealthIssueTypes: Optional[List[str]] = None
105+
srtKey: Optional[Union[SortKeys, str]] = None
106+
srtDir: Optional[str] = None
107+
pageSize: Optional[int] = None
108+
page: Optional[int] = None
109+
110+
@field_validator("srtDir")
111+
@classmethod
111112
def _validate(cls, value): # noqa
112113
value = str(value).upper()
113114
assert value in ("ASC", "DESC")

0 commit comments

Comments
 (0)