Skip to content

Commit 6b4e9dc

Browse files
INTEG-2940 - add legal-hold
1 parent eb629e4 commit 6b4e9dc

File tree

12 files changed

+1265
-16
lines changed

12 files changed

+1265
-16
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
here.
1111
## Unreleased
1212

13+
### Added
14+
15+
- The `orgs` and `legal_hold` clients to the SDK.
16+
- The `orgs` and `legal-hold` command groups to the CLI.
17+
1318
## 2.4.0 - 2025-05-27
1419

1520
### Added

docs/cli/cmds/legal_hold.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Legal Hold Commands
2+
3+
::: mkdocs-click
4+
:module: _incydr_cli.cmds.legal_hold
5+
:command: legal_hold
6+
:list_subcommands:

docs/sdk/clients/legal_hold.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Legal Hold
2+
3+
::: _incydr_sdk.legal_hold.client.LegalHoldV1
4+
:docstring:
5+
:members:

mkdocs.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ nav:
5454
- File Events: 'sdk/clients/file_events.md'
5555
- Files: 'sdk/clients/files.md'
5656
- File Event Querying: 'sdk/clients/file_event_queries.md'
57+
- Legal Hold: 'sdk/clients/legal_hold.md'
5758
- Orgs: 'sdk/clients/orgs.md'
5859
- Sessions: 'sdk/clients/sessions.md'
5960
- Trusted Activites: 'sdk/clients/trusted_activities.md'
@@ -81,6 +82,7 @@ nav:
8182
- Directory Groups: 'cli/cmds/directory_groups.md'
8283
- File Events: 'cli/cmds/file_events.md'
8384
- Files: 'cli/cmds/files.md'
85+
- Legal Hold: 'cli/cmds/legal_hold.md'
8486
- Orgs: 'cli/cmds/orgs.md'
8587
- Sessions: 'cli/cmds/sessions.md'
8688
- Trusted Activites: 'cli/cmds/trusted_activities.md'

src/_incydr_cli/cmds/legal_hold.py

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
from typing import Optional
2+
3+
import click
4+
from rich.panel import Panel
5+
6+
from _incydr_cli import console
7+
from _incydr_cli import logging_options
8+
from _incydr_cli import render
9+
from _incydr_cli.cmds.options.output_options import columns_option
10+
from _incydr_cli.cmds.options.output_options import single_format_option
11+
from _incydr_cli.cmds.options.output_options import SingleFormat
12+
from _incydr_cli.cmds.options.output_options import table_format_option
13+
from _incydr_cli.cmds.options.output_options import TableFormat
14+
from _incydr_cli.core import IncydrCommand
15+
from _incydr_cli.core import IncydrGroup
16+
from _incydr_sdk.core.client import Client
17+
from _incydr_sdk.legal_hold.models import Custodian
18+
from _incydr_sdk.legal_hold.models import CustodianMatter
19+
from _incydr_sdk.legal_hold.models import LegalHoldPolicy
20+
from _incydr_sdk.legal_hold.models import Matter
21+
from _incydr_sdk.utils import model_as_card
22+
23+
24+
@click.group(cls=IncydrGroup)
25+
@logging_options
26+
def legal_hold():
27+
"""View and manage legal holds."""
28+
29+
30+
@legal_hold.command("list-matters-for-user", cls=IncydrCommand)
31+
@click.argument("user-id")
32+
@table_format_option
33+
@columns_option
34+
@logging_options
35+
def list_matters_for_user(user_id: str, format_: TableFormat, columns: Optional[str]):
36+
"""List the matter memberships for a specific user."""
37+
client = Client()
38+
memberships_ = client.legal_hold.v1.iter_all_memberships_for_user(user_id=user_id)
39+
40+
if format_ == TableFormat.csv:
41+
render.csv(CustodianMatter, memberships_, columns=columns, flat=True)
42+
elif format_ == TableFormat.table:
43+
render.table(CustodianMatter, memberships_, columns=columns, flat=False)
44+
elif format_ == TableFormat.json_pretty:
45+
for item in memberships_:
46+
console.print_json(item.json())
47+
else:
48+
for item in memberships_:
49+
click.echo(item.json())
50+
51+
52+
@legal_hold.command("list-custodians", cls=IncydrCommand)
53+
@click.argument("matter-id")
54+
@table_format_option
55+
@columns_option
56+
@logging_options
57+
def list_custodians(matter_id: str, format_: TableFormat, columns: Optional[str]):
58+
"""List the matter memberships for a specific user."""
59+
client = Client()
60+
memberships_ = client.legal_hold.v1.iter_all_custodians(matter_id=matter_id)
61+
62+
if format_ == TableFormat.csv:
63+
render.csv(Custodian, memberships_, columns=columns, flat=True)
64+
elif format_ == TableFormat.table:
65+
render.table(Custodian, memberships_, columns=columns, flat=False)
66+
elif format_ == TableFormat.json_pretty:
67+
for item in memberships_:
68+
console.print_json(item.json())
69+
else:
70+
for item in memberships_:
71+
click.echo(item.json())
72+
73+
74+
@legal_hold.command("add-custodian", cls=IncydrCommand)
75+
@click.option("--user-id", required=True, default=None, help="The user ID to add.")
76+
@click.option(
77+
"--matter-id",
78+
required=True,
79+
default=None,
80+
help="The matter ID to which the user will be added",
81+
)
82+
@single_format_option
83+
@logging_options
84+
def add_custodian(user_id: str, matter_id: str, format_: SingleFormat):
85+
"""Add a custodian to a matter."""
86+
client = Client()
87+
result = client.legal_hold.v1.add_custodian(user_id=user_id, matter_id=matter_id)
88+
89+
if format_ == SingleFormat.rich and client.settings.use_rich:
90+
console.print(
91+
Panel.fit(
92+
model_as_card(result), title=f"Membership: {result.custodian.username}"
93+
)
94+
)
95+
elif format_ == SingleFormat.json_pretty:
96+
console.print_json(result.json())
97+
else:
98+
click.echo(result.json())
99+
100+
101+
@legal_hold.command("remove-custodian", cls=IncydrCommand)
102+
@click.option("--user-id", required=True, default=None, help="The user ID to remove.")
103+
@click.option(
104+
"--matter-id",
105+
required=True,
106+
default=None,
107+
help="The matter ID from which the user will be removed",
108+
)
109+
@logging_options
110+
def remove_custodian(
111+
user_id: str,
112+
matter_id: str,
113+
):
114+
"""Remove custodian from a matter."""
115+
client = Client()
116+
client.legal_hold.v1.remove_custodian(user_id=user_id, matter_id=matter_id)
117+
console.log(f"User {user_id} removed successfully from matter {matter_id}.")
118+
119+
120+
@legal_hold.command("list-matters", cls=IncydrCommand)
121+
@click.option(
122+
"--creator-user-id",
123+
default=None,
124+
help="Find legal hold matters that were created by the user with this unique identifier.",
125+
)
126+
@click.option(
127+
"--active/--inactive",
128+
default=None,
129+
help="Filter by active or inactive matters. Defaults to returning both when when neither option is passed.",
130+
)
131+
@click.option(
132+
"--name",
133+
default=None,
134+
help="Find legal hold matters whose 'name' either equals or partially contains this value.",
135+
)
136+
@table_format_option
137+
@columns_option
138+
@logging_options
139+
def list_matters(
140+
creator_user_id: Optional[str],
141+
active: Optional[bool],
142+
name: Optional[str],
143+
format_: TableFormat,
144+
columns: Optional[str],
145+
):
146+
"""List all matters."""
147+
client = Client()
148+
result = client.legal_hold.v1.iter_all_matters(
149+
creator_user_id=creator_user_id, active=active, name=name
150+
)
151+
152+
if format_ == TableFormat.csv:
153+
render.csv(Matter, result, columns=columns, flat=True)
154+
elif format_ == TableFormat.table:
155+
render.table(Matter, result, columns=columns, flat=False)
156+
elif format_ == TableFormat.json_pretty:
157+
for item in result:
158+
console.print_json(item.json())
159+
else:
160+
for item in result:
161+
click.echo(item.json())
162+
163+
164+
@legal_hold.command("create-matter", cls=IncydrCommand)
165+
@click.option(
166+
"--policy-id",
167+
required=True,
168+
default=None,
169+
help="The policy ID to be used by the created matter. Required.",
170+
)
171+
@click.option(
172+
"--name",
173+
required=True,
174+
default=None,
175+
help="The name of the matter to be created. Required.",
176+
)
177+
@click.option(
178+
"--description", default=None, help="The description of the matter to be created."
179+
)
180+
@click.option("--notes", default=None, help="The notes for the matter to be created.")
181+
@single_format_option
182+
@logging_options
183+
def create_matter(
184+
policy_id: str,
185+
name: str,
186+
description: Optional[str],
187+
notes: Optional[str],
188+
format_: SingleFormat,
189+
):
190+
"""Create a matter."""
191+
client = Client()
192+
result = client.legal_hold.v1.create_matter(
193+
policy_id=policy_id, name=name, description=description, notes=notes
194+
)
195+
196+
if format_ == SingleFormat.rich and client.settings.use_rich:
197+
console.print(Panel.fit(model_as_card(result), title=f"Matter: {result.name}"))
198+
elif format_ == SingleFormat.json_pretty:
199+
console.print_json(result.json())
200+
else:
201+
click.echo(result.json())
202+
203+
204+
@legal_hold.command("deactivate-matter", cls=IncydrCommand)
205+
@click.argument("matter-id")
206+
@logging_options
207+
def deactivate_matter(matter_id: str):
208+
"""Deactivate a matter."""
209+
client = Client()
210+
client.legal_hold.v1.deactivate_matter(matter_id=matter_id)
211+
console.log(f"Successfully deactivated {matter_id}")
212+
213+
214+
@legal_hold.command("reactivate-matter", cls=IncydrCommand)
215+
@click.argument("matter-id")
216+
@logging_options
217+
def reactivate_matter(matter_id: str):
218+
"""Reactivate a matter."""
219+
client = Client()
220+
client.legal_hold.v1.reactivate_matter(matter_id=matter_id)
221+
console.log(f"Successfully reactivated {matter_id}")
222+
223+
224+
@legal_hold.command("show-matter", cls=IncydrCommand)
225+
@click.argument("matter-id")
226+
@single_format_option
227+
@logging_options
228+
def show_matter(matter_id: str, format_: SingleFormat):
229+
"""Show details for a matter."""
230+
client = Client()
231+
result = client.legal_hold.v1.get_matter(matter_id=matter_id)
232+
233+
if format_ == SingleFormat.rich and client.settings.use_rich:
234+
console.print(Panel.fit(model_as_card(result), title=f"Matter: {result.name}"))
235+
elif format_ == SingleFormat.json_pretty:
236+
console.print_json(result.json())
237+
else:
238+
click.echo(result.json())
239+
240+
241+
@legal_hold.command("list-policies", cls=IncydrCommand)
242+
@table_format_option
243+
@columns_option
244+
@logging_options
245+
def list_policies(format_: TableFormat, columns: Optional[str]):
246+
client = Client()
247+
result = client.legal_hold.v1.iter_all_policies()
248+
249+
if format_ == TableFormat.csv:
250+
render.csv(LegalHoldPolicy, result, columns=columns, flat=True)
251+
elif format_ == TableFormat.table:
252+
render.table(LegalHoldPolicy, result, columns=columns, flat=False)
253+
elif format_ == TableFormat.json_pretty:
254+
for item in result:
255+
console.print_json(item.json())
256+
else:
257+
for item in result:
258+
click.echo(item.json())
259+
260+
261+
@legal_hold.command("show-policy", cls=IncydrCommand)
262+
@click.argument("policy-id")
263+
@single_format_option
264+
@logging_options
265+
def show_policy(policy_id: str, format_: SingleFormat):
266+
client = Client()
267+
result = client.legal_hold.v1.get_policy(policy_id=policy_id)
268+
269+
if format_ == SingleFormat.rich and client.settings.use_rich:
270+
console.print(Panel.fit(model_as_card(result), title=f"Policy: {result.name}"))
271+
elif format_ == SingleFormat.json_pretty:
272+
console.print_json(result.json())
273+
else:
274+
click.echo(result.json())

src/_incydr_cli/cmds/orgs.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def create(
9393
external_reference: Optional[str],
9494
notes: Optional[str],
9595
parent_org_guid: Optional[str],
96-
format_: TableFormat,
96+
format_: SingleFormat,
9797
columns: Optional[str],
9898
):
9999
"""
@@ -134,7 +134,7 @@ def deactivate_org(org_guid: str):
134134
@logging_options
135135
def show(
136136
org_guid: str,
137-
format_: TableFormat,
137+
format_: SingleFormat,
138138
columns: Optional[str],
139139
):
140140
"""
@@ -174,7 +174,7 @@ def update(
174174
name: str,
175175
external_reference: Optional[str],
176176
notes: Optional[str],
177-
format_: TableFormat,
177+
format_: SingleFormat,
178178
columns: Optional[str],
179179
):
180180
"""

src/_incydr_cli/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from _incydr_cli.cmds.directory_groups import directory_groups
2020
from _incydr_cli.cmds.file_events import file_events
2121
from _incydr_cli.cmds.files import files as files_client
22+
from _incydr_cli.cmds.legal_hold import legal_hold
2223
from _incydr_cli.cmds.orgs import orgs
2324
from _incydr_cli.cmds.risk_profiles import risk_profiles
2425
from _incydr_cli.cmds.sessions import sessions
@@ -90,6 +91,7 @@ def incydr(version, python, script_dir):
9091
incydr.add_command(trusted_activities)
9192
incydr.add_command(users)
9293
incydr.add_command(orgs)
94+
incydr.add_command(legal_hold)
9395
incydr.add_command(watchlists)
9496

9597
if __name__ == "__main__":

src/_incydr_sdk/core/client.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from _incydr_sdk.exceptions import AuthMissingError
2424
from _incydr_sdk.file_events.client import FileEventsClient
2525
from _incydr_sdk.files.client import FilesClient
26+
from _incydr_sdk.legal_hold.client import LegalHoldClient
2627
from _incydr_sdk.orgs.client import OrgsClient
2728
from _incydr_sdk.risk_profiles.client import RiskProfiles
2829
from _incydr_sdk.sessions.client import SessionsClient
@@ -109,6 +110,7 @@ def response_hook(response, *args, **kwargs):
109110
self._directory_groups = DirectoryGroupsClient(self)
110111
self._file_events = FileEventsClient(self)
111112
self._files = FilesClient(self)
113+
self._legal_hold = LegalHoldClient(self)
112114
self._orgs = OrgsClient(self)
113115
self._sessions = SessionsClient(self)
114116
self._trusted_activities = TrustedActivitiesClient(self)
@@ -297,6 +299,17 @@ def files(self):
297299
"""
298300
return self._files
299301

302+
@property
303+
def legal_hold(self):
304+
"""
305+
Property returning a [`LegalHoldClient`](../legal_hold) for interacting with `/v1/legal-hold` API endpoints.
306+
307+
Usage:
308+
309+
>>> client.legal_hold.v1.iter_all_matters()
310+
"""
311+
return self._legal_hold
312+
300313
@property
301314
def orgs(self):
302315
"""

0 commit comments

Comments
 (0)