affinity-sdk 0.9.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- affinity/__init__.py +139 -0
- affinity/cli/__init__.py +7 -0
- affinity/cli/click_compat.py +27 -0
- affinity/cli/commands/__init__.py +1 -0
- affinity/cli/commands/_entity_files_dump.py +219 -0
- affinity/cli/commands/_list_entry_fields.py +41 -0
- affinity/cli/commands/_v1_parsing.py +77 -0
- affinity/cli/commands/company_cmds.py +2139 -0
- affinity/cli/commands/completion_cmd.py +33 -0
- affinity/cli/commands/config_cmds.py +540 -0
- affinity/cli/commands/entry_cmds.py +33 -0
- affinity/cli/commands/field_cmds.py +413 -0
- affinity/cli/commands/interaction_cmds.py +875 -0
- affinity/cli/commands/list_cmds.py +3152 -0
- affinity/cli/commands/note_cmds.py +433 -0
- affinity/cli/commands/opportunity_cmds.py +1174 -0
- affinity/cli/commands/person_cmds.py +1980 -0
- affinity/cli/commands/query_cmd.py +444 -0
- affinity/cli/commands/relationship_strength_cmds.py +62 -0
- affinity/cli/commands/reminder_cmds.py +595 -0
- affinity/cli/commands/resolve_url_cmd.py +127 -0
- affinity/cli/commands/session_cmds.py +84 -0
- affinity/cli/commands/task_cmds.py +110 -0
- affinity/cli/commands/version_cmd.py +29 -0
- affinity/cli/commands/whoami_cmd.py +36 -0
- affinity/cli/config.py +108 -0
- affinity/cli/context.py +749 -0
- affinity/cli/csv_utils.py +195 -0
- affinity/cli/date_utils.py +42 -0
- affinity/cli/decorators.py +77 -0
- affinity/cli/errors.py +28 -0
- affinity/cli/field_utils.py +355 -0
- affinity/cli/formatters.py +551 -0
- affinity/cli/help_json.py +283 -0
- affinity/cli/logging.py +100 -0
- affinity/cli/main.py +261 -0
- affinity/cli/options.py +53 -0
- affinity/cli/paths.py +32 -0
- affinity/cli/progress.py +183 -0
- affinity/cli/query/__init__.py +163 -0
- affinity/cli/query/aggregates.py +357 -0
- affinity/cli/query/dates.py +194 -0
- affinity/cli/query/exceptions.py +147 -0
- affinity/cli/query/executor.py +1236 -0
- affinity/cli/query/filters.py +248 -0
- affinity/cli/query/models.py +333 -0
- affinity/cli/query/output.py +331 -0
- affinity/cli/query/parser.py +619 -0
- affinity/cli/query/planner.py +430 -0
- affinity/cli/query/progress.py +270 -0
- affinity/cli/query/schema.py +439 -0
- affinity/cli/render.py +1589 -0
- affinity/cli/resolve.py +222 -0
- affinity/cli/resolvers.py +249 -0
- affinity/cli/results.py +308 -0
- affinity/cli/runner.py +218 -0
- affinity/cli/serialization.py +65 -0
- affinity/cli/session_cache.py +276 -0
- affinity/cli/types.py +70 -0
- affinity/client.py +771 -0
- affinity/clients/__init__.py +19 -0
- affinity/clients/http.py +3664 -0
- affinity/clients/pipeline.py +165 -0
- affinity/compare.py +501 -0
- affinity/downloads.py +114 -0
- affinity/exceptions.py +615 -0
- affinity/filters.py +1128 -0
- affinity/hooks.py +198 -0
- affinity/inbound_webhooks.py +302 -0
- affinity/models/__init__.py +163 -0
- affinity/models/entities.py +798 -0
- affinity/models/pagination.py +513 -0
- affinity/models/rate_limit_snapshot.py +48 -0
- affinity/models/secondary.py +413 -0
- affinity/models/types.py +663 -0
- affinity/policies.py +40 -0
- affinity/progress.py +22 -0
- affinity/py.typed +0 -0
- affinity/services/__init__.py +42 -0
- affinity/services/companies.py +1286 -0
- affinity/services/lists.py +1892 -0
- affinity/services/opportunities.py +1330 -0
- affinity/services/persons.py +1348 -0
- affinity/services/rate_limits.py +173 -0
- affinity/services/tasks.py +193 -0
- affinity/services/v1_only.py +2445 -0
- affinity/types.py +83 -0
- affinity_sdk-0.9.5.dist-info/METADATA +622 -0
- affinity_sdk-0.9.5.dist-info/RECORD +92 -0
- affinity_sdk-0.9.5.dist-info/WHEEL +4 -0
- affinity_sdk-0.9.5.dist-info/entry_points.txt +2 -0
- affinity_sdk-0.9.5.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from contextlib import ExitStack
|
|
5
|
+
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.progress import BarColumn, Progress, TaskID, TextColumn, TimeElapsedColumn
|
|
8
|
+
|
|
9
|
+
from affinity.models.secondary import Note, NoteCreate, NoteUpdate
|
|
10
|
+
from affinity.models.types import InteractionType, NoteType
|
|
11
|
+
from affinity.types import CompanyId, NoteId, OpportunityId, PersonId, UserId
|
|
12
|
+
|
|
13
|
+
from ..click_compat import RichCommand, RichGroup, click
|
|
14
|
+
from ..context import CLIContext
|
|
15
|
+
from ..decorators import category, destructive
|
|
16
|
+
from ..errors import CLIError
|
|
17
|
+
from ..options import output_options
|
|
18
|
+
from ..results import CommandContext
|
|
19
|
+
from ..runner import CommandOutput, run_command
|
|
20
|
+
from ._v1_parsing import parse_choice, parse_iso_datetime
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@click.group(name="note", cls=RichGroup)
|
|
24
|
+
def note_group() -> None:
|
|
25
|
+
"""Note commands."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
_NOTE_TYPE_MAP = {
|
|
29
|
+
"plain-text": NoteType.PLAIN_TEXT,
|
|
30
|
+
"plain": NoteType.PLAIN_TEXT,
|
|
31
|
+
"html": NoteType.HTML,
|
|
32
|
+
"ai-notetaker": NoteType.AI_NOTETAKER,
|
|
33
|
+
"email-derived": NoteType.EMAIL_DERIVED,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _note_payload(note: Note) -> dict[str, object]:
|
|
38
|
+
# Convert enum values back to names for CLI display
|
|
39
|
+
type_name = NoteType(note.type).name.lower().replace("_", "-")
|
|
40
|
+
interaction_type_name = (
|
|
41
|
+
InteractionType(note.interaction_type).name.lower().replace("_", "-")
|
|
42
|
+
if note.interaction_type is not None
|
|
43
|
+
else None
|
|
44
|
+
)
|
|
45
|
+
return {
|
|
46
|
+
"id": int(note.id),
|
|
47
|
+
"type": type_name,
|
|
48
|
+
"creatorId": int(note.creator_id),
|
|
49
|
+
"content": note.content,
|
|
50
|
+
"personIds": [int(p) for p in note.person_ids],
|
|
51
|
+
"associatedPersonIds": [int(p) for p in note.associated_person_ids],
|
|
52
|
+
"interactionPersonIds": [int(p) for p in note.interaction_person_ids],
|
|
53
|
+
"mentionedPersonIds": [int(p) for p in note.mentioned_person_ids],
|
|
54
|
+
"companyIds": [int(o) for o in note.company_ids],
|
|
55
|
+
"opportunityIds": [int(o) for o in note.opportunity_ids],
|
|
56
|
+
"interactionId": note.interaction_id,
|
|
57
|
+
"interactionType": interaction_type_name,
|
|
58
|
+
"isMeeting": note.is_meeting,
|
|
59
|
+
"parentId": int(note.parent_id) if note.parent_id else None,
|
|
60
|
+
"createdAt": note.created_at,
|
|
61
|
+
"updatedAt": note.updated_at,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@category("read")
|
|
66
|
+
@note_group.command(name="ls", cls=RichCommand)
|
|
67
|
+
@click.option("--person-id", type=int, default=None, help="Filter by person id.")
|
|
68
|
+
@click.option("--company-id", type=int, default=None, help="Filter by company id.")
|
|
69
|
+
@click.option("--opportunity-id", type=int, default=None, help="Filter by opportunity id.")
|
|
70
|
+
@click.option("--creator-id", type=int, default=None, help="Filter by creator id.")
|
|
71
|
+
@click.option("--page-size", "-s", type=int, default=None, help="Page size (max 500).")
|
|
72
|
+
@click.option(
|
|
73
|
+
"--cursor", type=str, default=None, help="Resume from cursor (incompatible with --page-size)."
|
|
74
|
+
)
|
|
75
|
+
@click.option(
|
|
76
|
+
"--max-results", "--limit", "-n", type=int, default=None, help="Stop after N results total."
|
|
77
|
+
)
|
|
78
|
+
@click.option("--all", "-A", "all_pages", is_flag=True, help="Fetch all pages.")
|
|
79
|
+
@output_options
|
|
80
|
+
@click.pass_obj
|
|
81
|
+
def note_ls(
|
|
82
|
+
ctx: CLIContext,
|
|
83
|
+
*,
|
|
84
|
+
person_id: int | None,
|
|
85
|
+
company_id: int | None,
|
|
86
|
+
opportunity_id: int | None,
|
|
87
|
+
creator_id: int | None,
|
|
88
|
+
page_size: int | None,
|
|
89
|
+
cursor: str | None,
|
|
90
|
+
max_results: int | None,
|
|
91
|
+
all_pages: bool,
|
|
92
|
+
) -> None:
|
|
93
|
+
"""
|
|
94
|
+
List notes.
|
|
95
|
+
|
|
96
|
+
Examples:
|
|
97
|
+
|
|
98
|
+
- `xaffinity note ls --person-id 12345`
|
|
99
|
+
- `xaffinity note ls --company-id 67890 --all`
|
|
100
|
+
- `xaffinity note ls --creator-id 111 --max-results 50`
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
def fn(ctx: CLIContext, warnings: list[str]) -> CommandOutput:
|
|
104
|
+
client = ctx.get_client(warnings=warnings)
|
|
105
|
+
results: list[dict[str, object]] = []
|
|
106
|
+
first_page = True
|
|
107
|
+
page_token = cursor
|
|
108
|
+
person_id_value = PersonId(person_id) if person_id is not None else None
|
|
109
|
+
company_id_value = CompanyId(company_id) if company_id is not None else None
|
|
110
|
+
opportunity_id_value = OpportunityId(opportunity_id) if opportunity_id is not None else None
|
|
111
|
+
creator_id_value = UserId(creator_id) if creator_id is not None else None
|
|
112
|
+
|
|
113
|
+
# Build CommandContext upfront for use in all return paths
|
|
114
|
+
ctx_modifiers: dict[str, object] = {}
|
|
115
|
+
if person_id is not None:
|
|
116
|
+
ctx_modifiers["personId"] = person_id
|
|
117
|
+
if company_id is not None:
|
|
118
|
+
ctx_modifiers["companyId"] = company_id
|
|
119
|
+
if opportunity_id is not None:
|
|
120
|
+
ctx_modifiers["opportunityId"] = opportunity_id
|
|
121
|
+
if creator_id is not None:
|
|
122
|
+
ctx_modifiers["creatorId"] = creator_id
|
|
123
|
+
if page_size is not None:
|
|
124
|
+
ctx_modifiers["pageSize"] = page_size
|
|
125
|
+
if cursor is not None:
|
|
126
|
+
ctx_modifiers["cursor"] = cursor
|
|
127
|
+
if max_results is not None:
|
|
128
|
+
ctx_modifiers["maxResults"] = max_results
|
|
129
|
+
if all_pages:
|
|
130
|
+
ctx_modifiers["allPages"] = True
|
|
131
|
+
|
|
132
|
+
cmd_context = CommandContext(
|
|
133
|
+
name="note ls",
|
|
134
|
+
inputs={},
|
|
135
|
+
modifiers=ctx_modifiers,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
show_progress = (
|
|
139
|
+
ctx.progress != "never"
|
|
140
|
+
and not ctx.quiet
|
|
141
|
+
and (ctx.progress == "always" or sys.stderr.isatty())
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
with ExitStack() as stack:
|
|
145
|
+
progress: Progress | None = None
|
|
146
|
+
task_id: TaskID | None = None
|
|
147
|
+
if show_progress:
|
|
148
|
+
progress = stack.enter_context(
|
|
149
|
+
Progress(
|
|
150
|
+
TextColumn("{task.description}"),
|
|
151
|
+
BarColumn(),
|
|
152
|
+
TextColumn("{task.completed} rows"),
|
|
153
|
+
TimeElapsedColumn(),
|
|
154
|
+
console=Console(file=sys.stderr),
|
|
155
|
+
transient=True,
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
task_id = progress.add_task("Fetching", total=max_results)
|
|
159
|
+
|
|
160
|
+
while True:
|
|
161
|
+
page = client.notes.list(
|
|
162
|
+
person_id=person_id_value,
|
|
163
|
+
company_id=company_id_value,
|
|
164
|
+
opportunity_id=opportunity_id_value,
|
|
165
|
+
creator_id=creator_id_value,
|
|
166
|
+
page_size=page_size,
|
|
167
|
+
page_token=page_token,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
for idx, note in enumerate(page.data):
|
|
171
|
+
results.append(_note_payload(note))
|
|
172
|
+
if progress and task_id is not None:
|
|
173
|
+
progress.update(task_id, completed=len(results))
|
|
174
|
+
if max_results is not None and len(results) >= max_results:
|
|
175
|
+
stopped_mid_page = idx < (len(page.data) - 1)
|
|
176
|
+
if stopped_mid_page:
|
|
177
|
+
warnings.append(
|
|
178
|
+
"Results limited by --max-results. Use --all to fetch all results."
|
|
179
|
+
)
|
|
180
|
+
pagination = None
|
|
181
|
+
if page.next_page_token and not stopped_mid_page:
|
|
182
|
+
pagination = {"nextCursor": page.next_page_token, "prevCursor": None}
|
|
183
|
+
return CommandOutput(
|
|
184
|
+
data=results[:max_results], # Direct array, not wrapped
|
|
185
|
+
context=cmd_context,
|
|
186
|
+
pagination=pagination,
|
|
187
|
+
api_called=True,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if first_page and not all_pages and max_results is None:
|
|
191
|
+
pagination = (
|
|
192
|
+
{"nextCursor": page.next_page_token, "prevCursor": None}
|
|
193
|
+
if page.next_page_token
|
|
194
|
+
else None
|
|
195
|
+
)
|
|
196
|
+
return CommandOutput(
|
|
197
|
+
data=results, # Direct array, not wrapped
|
|
198
|
+
context=cmd_context,
|
|
199
|
+
pagination=pagination,
|
|
200
|
+
api_called=True,
|
|
201
|
+
)
|
|
202
|
+
first_page = False
|
|
203
|
+
|
|
204
|
+
page_token = page.next_page_token
|
|
205
|
+
if not page_token:
|
|
206
|
+
break
|
|
207
|
+
|
|
208
|
+
return CommandOutput(
|
|
209
|
+
data=results, # Direct array, not wrapped
|
|
210
|
+
context=cmd_context,
|
|
211
|
+
pagination=None,
|
|
212
|
+
api_called=True,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
run_command(ctx, command="note ls", fn=fn)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@category("read")
|
|
219
|
+
@note_group.command(name="get", cls=RichCommand)
|
|
220
|
+
@click.argument("note_id", type=int)
|
|
221
|
+
@output_options
|
|
222
|
+
@click.pass_obj
|
|
223
|
+
def note_get(ctx: CLIContext, note_id: int) -> None:
|
|
224
|
+
"""
|
|
225
|
+
Get a note by id.
|
|
226
|
+
|
|
227
|
+
Example: `xaffinity note get 12345`
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
def fn(ctx: CLIContext, warnings: list[str]) -> CommandOutput:
|
|
231
|
+
client = ctx.get_client(warnings=warnings)
|
|
232
|
+
note = client.notes.get(NoteId(note_id))
|
|
233
|
+
|
|
234
|
+
cmd_context = CommandContext(
|
|
235
|
+
name="note get",
|
|
236
|
+
inputs={"noteId": note_id},
|
|
237
|
+
modifiers={},
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
payload = _note_payload(note)
|
|
241
|
+
|
|
242
|
+
# For table display, separate content from metadata for better readability
|
|
243
|
+
if ctx.output != "json":
|
|
244
|
+
content = payload.pop("content", None)
|
|
245
|
+
data: dict[str, object] = {"note": payload}
|
|
246
|
+
if content:
|
|
247
|
+
# Use _text marker for clean text rendering without "Value:" wrapper
|
|
248
|
+
data["Content"] = {"_text": content}
|
|
249
|
+
else:
|
|
250
|
+
data = {"note": payload}
|
|
251
|
+
|
|
252
|
+
return CommandOutput(
|
|
253
|
+
data=data,
|
|
254
|
+
context=cmd_context,
|
|
255
|
+
api_called=True,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
run_command(ctx, command="note get", fn=fn)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
@category("write")
|
|
262
|
+
@note_group.command(name="create", cls=RichCommand)
|
|
263
|
+
@click.option("--content", type=str, required=True, help="Note content.")
|
|
264
|
+
@click.option(
|
|
265
|
+
"--type",
|
|
266
|
+
"note_type",
|
|
267
|
+
type=click.Choice(sorted(_NOTE_TYPE_MAP.keys())),
|
|
268
|
+
default=None,
|
|
269
|
+
help="Note type (plain-text, html, ai-notetaker, email-derived).",
|
|
270
|
+
)
|
|
271
|
+
@click.option("--person-id", "person_ids", multiple=True, type=int, help="Associate person id.")
|
|
272
|
+
@click.option(
|
|
273
|
+
"--company-id",
|
|
274
|
+
"company_ids",
|
|
275
|
+
multiple=True,
|
|
276
|
+
type=int,
|
|
277
|
+
help="Associate company id.",
|
|
278
|
+
)
|
|
279
|
+
@click.option(
|
|
280
|
+
"--opportunity-id",
|
|
281
|
+
"opportunity_ids",
|
|
282
|
+
multiple=True,
|
|
283
|
+
type=int,
|
|
284
|
+
help="Associate opportunity id.",
|
|
285
|
+
)
|
|
286
|
+
@click.option("--parent-id", type=int, default=None, help="Parent note id (reply).")
|
|
287
|
+
@click.option("--creator-id", type=int, default=None, help="Creator id override.")
|
|
288
|
+
@click.option(
|
|
289
|
+
"--created-at",
|
|
290
|
+
type=str,
|
|
291
|
+
default=None,
|
|
292
|
+
help="Creation timestamp (ISO-8601).",
|
|
293
|
+
)
|
|
294
|
+
@output_options
|
|
295
|
+
@click.pass_obj
|
|
296
|
+
def note_create(
|
|
297
|
+
ctx: CLIContext,
|
|
298
|
+
*,
|
|
299
|
+
content: str,
|
|
300
|
+
note_type: str | None,
|
|
301
|
+
person_ids: tuple[int, ...],
|
|
302
|
+
company_ids: tuple[int, ...],
|
|
303
|
+
opportunity_ids: tuple[int, ...],
|
|
304
|
+
parent_id: int | None,
|
|
305
|
+
creator_id: int | None,
|
|
306
|
+
created_at: str | None,
|
|
307
|
+
) -> None:
|
|
308
|
+
"""
|
|
309
|
+
Create a note attached to an entity.
|
|
310
|
+
|
|
311
|
+
Examples:
|
|
312
|
+
|
|
313
|
+
- `xaffinity note create --content "Meeting notes" --person-id 12345`
|
|
314
|
+
- `xaffinity note create --content "<b>Summary</b>" --type html --company-id 67890`
|
|
315
|
+
"""
|
|
316
|
+
|
|
317
|
+
def fn(ctx: CLIContext, warnings: list[str]) -> CommandOutput:
|
|
318
|
+
_ = warnings
|
|
319
|
+
if not (person_ids or company_ids or opportunity_ids or parent_id):
|
|
320
|
+
raise CLIError(
|
|
321
|
+
"Notes must be attached to at least one entity or parent note.",
|
|
322
|
+
exit_code=2,
|
|
323
|
+
error_type="usage_error",
|
|
324
|
+
hint="Provide --person-id/--company-id/--opportunity-id or --parent-id.",
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
parsed_type = parse_choice(note_type, _NOTE_TYPE_MAP, label="note type")
|
|
328
|
+
created_at_value = (
|
|
329
|
+
parse_iso_datetime(created_at, label="created-at") if created_at else None
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
client = ctx.get_client(warnings=warnings)
|
|
333
|
+
note = client.notes.create(
|
|
334
|
+
NoteCreate(
|
|
335
|
+
content=content,
|
|
336
|
+
type=parsed_type or NoteType.PLAIN_TEXT,
|
|
337
|
+
person_ids=[PersonId(pid) for pid in person_ids],
|
|
338
|
+
company_ids=[CompanyId(cid) for cid in company_ids],
|
|
339
|
+
opportunity_ids=[OpportunityId(oid) for oid in opportunity_ids],
|
|
340
|
+
parent_id=NoteId(parent_id) if parent_id else None,
|
|
341
|
+
creator_id=UserId(creator_id) if creator_id is not None else None,
|
|
342
|
+
created_at=created_at_value,
|
|
343
|
+
)
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
ctx_modifiers: dict[str, object] = {}
|
|
347
|
+
if note_type:
|
|
348
|
+
ctx_modifiers["type"] = note_type
|
|
349
|
+
if person_ids:
|
|
350
|
+
ctx_modifiers["personIds"] = list(person_ids)
|
|
351
|
+
if company_ids:
|
|
352
|
+
ctx_modifiers["companyIds"] = list(company_ids)
|
|
353
|
+
if opportunity_ids:
|
|
354
|
+
ctx_modifiers["opportunityIds"] = list(opportunity_ids)
|
|
355
|
+
if parent_id:
|
|
356
|
+
ctx_modifiers["parentId"] = parent_id
|
|
357
|
+
if creator_id is not None:
|
|
358
|
+
ctx_modifiers["creatorId"] = creator_id
|
|
359
|
+
if created_at:
|
|
360
|
+
ctx_modifiers["createdAt"] = created_at
|
|
361
|
+
|
|
362
|
+
cmd_context = CommandContext(
|
|
363
|
+
name="note create",
|
|
364
|
+
inputs={},
|
|
365
|
+
modifiers=ctx_modifiers,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
return CommandOutput(
|
|
369
|
+
data={"note": _note_payload(note)},
|
|
370
|
+
context=cmd_context,
|
|
371
|
+
api_called=True,
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
run_command(ctx, command="note create", fn=fn)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
@category("write")
|
|
378
|
+
@note_group.command(name="update", cls=RichCommand)
|
|
379
|
+
@click.argument("note_id", type=int)
|
|
380
|
+
@click.option("--content", type=str, required=True, help="Updated note content.")
|
|
381
|
+
@output_options
|
|
382
|
+
@click.pass_obj
|
|
383
|
+
def note_update(ctx: CLIContext, note_id: int, *, content: str) -> None:
|
|
384
|
+
"""Update a note."""
|
|
385
|
+
|
|
386
|
+
def fn(ctx: CLIContext, warnings: list[str]) -> CommandOutput:
|
|
387
|
+
client = ctx.get_client(warnings=warnings)
|
|
388
|
+
note = client.notes.update(NoteId(note_id), NoteUpdate(content=content))
|
|
389
|
+
|
|
390
|
+
cmd_context = CommandContext(
|
|
391
|
+
name="note update",
|
|
392
|
+
inputs={"noteId": note_id},
|
|
393
|
+
modifiers={"content": content},
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
return CommandOutput(
|
|
397
|
+
data={"note": _note_payload(note)},
|
|
398
|
+
context=cmd_context,
|
|
399
|
+
api_called=True,
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
run_command(ctx, command="note update", fn=fn)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
@category("write")
|
|
406
|
+
@destructive
|
|
407
|
+
@note_group.command(name="delete", cls=RichCommand)
|
|
408
|
+
@click.argument("note_id", type=int)
|
|
409
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt.")
|
|
410
|
+
@output_options
|
|
411
|
+
@click.pass_obj
|
|
412
|
+
def note_delete(ctx: CLIContext, note_id: int, yes: bool) -> None:
|
|
413
|
+
"""Delete a note."""
|
|
414
|
+
if not yes:
|
|
415
|
+
click.confirm(f"Delete note {note_id}?", abort=True)
|
|
416
|
+
|
|
417
|
+
def fn(ctx: CLIContext, warnings: list[str]) -> CommandOutput:
|
|
418
|
+
client = ctx.get_client(warnings=warnings)
|
|
419
|
+
success = client.notes.delete(NoteId(note_id))
|
|
420
|
+
|
|
421
|
+
cmd_context = CommandContext(
|
|
422
|
+
name="note delete",
|
|
423
|
+
inputs={"noteId": note_id},
|
|
424
|
+
modifiers={},
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
return CommandOutput(
|
|
428
|
+
data={"success": success},
|
|
429
|
+
context=cmd_context,
|
|
430
|
+
api_called=True,
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
run_command(ctx, command="note delete", fn=fn)
|