impreza-cli 0.3.0__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.
- impreza_cli/__init__.py +14 -0
- impreza_cli/commands/__init__.py +7 -0
- impreza_cli/commands/_helpers.py +128 -0
- impreza_cli/commands/account.py +576 -0
- impreza_cli/commands/catalog.py +270 -0
- impreza_cli/commands/context.py +232 -0
- impreza_cli/commands/doctor.py +427 -0
- impreza_cli/commands/domain.py +858 -0
- impreza_cli/commands/invoice.py +198 -0
- impreza_cli/commands/key.py +104 -0
- impreza_cli/commands/orders.py +478 -0
- impreza_cli/commands/services.py +100 -0
- impreza_cli/commands/vps.py +812 -0
- impreza_cli/commands/vps_cloud.py +865 -0
- impreza_cli/commands/vps_proxmox.py +727 -0
- impreza_cli/commands/webhooks.py +483 -0
- impreza_cli/config.py +405 -0
- impreza_cli/main.py +100 -0
- impreza_cli/output.py +207 -0
- impreza_cli/sdk.py +97 -0
- impreza_cli/state.py +94 -0
- impreza_cli-0.3.0.dist-info/METADATA +296 -0
- impreza_cli-0.3.0.dist-info/RECORD +26 -0
- impreza_cli-0.3.0.dist-info/WHEEL +5 -0
- impreza_cli-0.3.0.dist-info/entry_points.txt +2 -0
- impreza_cli-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
"""``impreza webhook`` subcommand surface — Phase 3.7.
|
|
2
|
+
|
|
3
|
+
Eight verbs over the :class:`~impreza.WebhooksResource` shipped in
|
|
4
|
+
1.6. All pure CLI work — the SDK side and server side are both
|
|
5
|
+
complete since Phase 0 (server) and Phase 1.6 (SDK).
|
|
6
|
+
|
|
7
|
+
* ``webhook list`` — every subscription on the account.
|
|
8
|
+
* ``webhook show <id>`` — one subscription's detail.
|
|
9
|
+
* ``webhook create --url URL --event TYPE [--event TYPE ...]
|
|
10
|
+
[--description D]`` — register a new subscription. The HMAC
|
|
11
|
+
secret is printed ONCE on this call and ONLY on this call (and
|
|
12
|
+
on ``rotate-secret``). Subsequent reads return null for the
|
|
13
|
+
secret field.
|
|
14
|
+
* ``webhook update <id> [--url ...] [--event TYPE ...]
|
|
15
|
+
[--description ...] [--activate/--deactivate]`` — PATCH semantics;
|
|
16
|
+
pass only the fields to change.
|
|
17
|
+
* ``webhook delete <id> [--yes]`` — remove the subscription.
|
|
18
|
+
Pending undelivered events are dropped at the server.
|
|
19
|
+
* ``webhook rotate-secret <id>`` — generate a fresh HMAC secret.
|
|
20
|
+
The previous secret stops working immediately; the new one is
|
|
21
|
+
printed ONCE.
|
|
22
|
+
* ``webhook deliveries <id>`` — up to 100 recent delivery
|
|
23
|
+
attempts for one subscription.
|
|
24
|
+
* ``webhook event-types`` — the catalog of subscribable event
|
|
25
|
+
types + wildcard patterns.
|
|
26
|
+
|
|
27
|
+
Event types are passed via repeated ``--event TYPE`` flags rather
|
|
28
|
+
than a comma-separated string so quoting edge cases (events with
|
|
29
|
+
periods, glob patterns like ``vps.*``) don't bite. The SDK accepts
|
|
30
|
+
wildcards (``"vps.*"``, ``"*"``) verbatim.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
from typing import Any
|
|
36
|
+
|
|
37
|
+
import typer
|
|
38
|
+
from impreza.exceptions import ApiError
|
|
39
|
+
|
|
40
|
+
from ..output import OutputFormat, error, print_dict, print_table, success
|
|
41
|
+
from ..sdk import make_client_or_exit
|
|
42
|
+
from ..state import confirm_or_exit, from_typer_context, resolve_output
|
|
43
|
+
from ._helpers import exit_on_api_error
|
|
44
|
+
|
|
45
|
+
app = typer.Typer(
|
|
46
|
+
name="webhook",
|
|
47
|
+
help="Manage webhook subscriptions and inspect delivery history.",
|
|
48
|
+
no_args_is_help=True,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# ── helpers ─────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _subscription_row(sub: Any, *, fmt: OutputFormat) -> dict[str, Any]:
|
|
56
|
+
"""Lift a WebhookSubscription into a flat row. Table mode joins
|
|
57
|
+
the events list with commas; JSON / YAML keep the list shape."""
|
|
58
|
+
events_field = (
|
|
59
|
+
", ".join(sub.events)
|
|
60
|
+
if fmt is OutputFormat.TABLE
|
|
61
|
+
else list(sub.events)
|
|
62
|
+
)
|
|
63
|
+
return {
|
|
64
|
+
"id": sub.id,
|
|
65
|
+
"url": sub.url,
|
|
66
|
+
"events": events_field,
|
|
67
|
+
"description": sub.description or "",
|
|
68
|
+
"is_active": "yes" if sub.is_active else "no",
|
|
69
|
+
"last_delivery_at": sub.last_delivery_at or "",
|
|
70
|
+
"last_delivery_status": (
|
|
71
|
+
sub.last_delivery_status
|
|
72
|
+
if sub.last_delivery_status is not None
|
|
73
|
+
else ""
|
|
74
|
+
),
|
|
75
|
+
"created_at": sub.created_at or "",
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
_LIST_COLUMNS = [
|
|
80
|
+
"id", "url", "events", "is_active",
|
|
81
|
+
"last_delivery_at", "last_delivery_status",
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ── webhook list ────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@app.command("list")
|
|
89
|
+
def list_webhooks(
|
|
90
|
+
typer_ctx: typer.Context,
|
|
91
|
+
output: OutputFormat | None = typer.Option(
|
|
92
|
+
None, "--output", "-o",
|
|
93
|
+
help="Output format. Overrides the global --output flag.",
|
|
94
|
+
case_sensitive=False,
|
|
95
|
+
),
|
|
96
|
+
) -> None:
|
|
97
|
+
"""List every webhook subscription on the account.
|
|
98
|
+
|
|
99
|
+
Wraps ``c.webhooks.list()``. The ``secret`` field is null on
|
|
100
|
+
all entries — it's only returned on create / rotate-secret.
|
|
101
|
+
"""
|
|
102
|
+
state = from_typer_context(typer_ctx)
|
|
103
|
+
fmt = resolve_output(state, output)
|
|
104
|
+
|
|
105
|
+
with make_client_or_exit(state) as client:
|
|
106
|
+
try:
|
|
107
|
+
subs = client.webhooks.list()
|
|
108
|
+
except ApiError as exc:
|
|
109
|
+
exit_on_api_error(exc)
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
if not subs:
|
|
113
|
+
typer.echo("No webhook subscriptions on this account.")
|
|
114
|
+
return
|
|
115
|
+
|
|
116
|
+
rows = [_subscription_row(s, fmt=fmt) for s in subs]
|
|
117
|
+
print_table(
|
|
118
|
+
f"Webhook subscriptions ({len(rows)})",
|
|
119
|
+
rows,
|
|
120
|
+
columns=_LIST_COLUMNS,
|
|
121
|
+
fmt=fmt,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# ── webhook show ────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@app.command("show")
|
|
129
|
+
def show_webhook(
|
|
130
|
+
typer_ctx: typer.Context,
|
|
131
|
+
subscription_id: int = typer.Argument(..., help="Subscription id."),
|
|
132
|
+
output: OutputFormat | None = typer.Option(
|
|
133
|
+
None, "--output", "-o",
|
|
134
|
+
help="Output format. Overrides the global --output flag.",
|
|
135
|
+
case_sensitive=False,
|
|
136
|
+
),
|
|
137
|
+
) -> None:
|
|
138
|
+
"""Show one subscription's detail.
|
|
139
|
+
|
|
140
|
+
Wraps ``c.webhooks.get(id)``.
|
|
141
|
+
"""
|
|
142
|
+
state = from_typer_context(typer_ctx)
|
|
143
|
+
fmt = resolve_output(state, output)
|
|
144
|
+
|
|
145
|
+
with make_client_or_exit(state) as client:
|
|
146
|
+
try:
|
|
147
|
+
sub = client.webhooks.get(subscription_id)
|
|
148
|
+
except ApiError as exc:
|
|
149
|
+
exit_on_api_error(exc)
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
data = _subscription_row(sub, fmt=fmt)
|
|
153
|
+
print_dict(f"Webhook subscription {subscription_id}", data, fmt=fmt)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# ── webhook create ──────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@app.command("create")
|
|
160
|
+
def create_webhook(
|
|
161
|
+
typer_ctx: typer.Context,
|
|
162
|
+
url: str = typer.Option(
|
|
163
|
+
...,
|
|
164
|
+
"--url", "-u",
|
|
165
|
+
help="HTTPS URL the server will POST events to.",
|
|
166
|
+
),
|
|
167
|
+
event: list[str] = typer.Option(
|
|
168
|
+
...,
|
|
169
|
+
"--event", "-e",
|
|
170
|
+
help=(
|
|
171
|
+
"Event type or wildcard. Pass multiple times. Examples: "
|
|
172
|
+
"`--event topup.paid --event vps.*` (concrete + wildcard); "
|
|
173
|
+
"`--event '*'` (everything). Run `webhook event-types` to "
|
|
174
|
+
"list the catalog."
|
|
175
|
+
),
|
|
176
|
+
),
|
|
177
|
+
description: str | None = typer.Option(
|
|
178
|
+
None, "--description", "-d",
|
|
179
|
+
help="Optional human-readable note for the subscription.",
|
|
180
|
+
),
|
|
181
|
+
) -> None:
|
|
182
|
+
"""Create a new webhook subscription.
|
|
183
|
+
|
|
184
|
+
Wraps ``c.webhooks.create(url=..., events=[...],
|
|
185
|
+
description=...)``. The HMAC secret is printed **only on this
|
|
186
|
+
call** (and on ``rotate-secret``). Capture it before the
|
|
187
|
+
terminal closes — there's no way to retrieve it later other
|
|
188
|
+
than rotating.
|
|
189
|
+
"""
|
|
190
|
+
state = from_typer_context(typer_ctx)
|
|
191
|
+
|
|
192
|
+
if not event:
|
|
193
|
+
error("--event requires at least one event type (pass it multiple times).")
|
|
194
|
+
raise typer.Exit(code=1)
|
|
195
|
+
|
|
196
|
+
with make_client_or_exit(state) as client:
|
|
197
|
+
try:
|
|
198
|
+
sub = client.webhooks.create(
|
|
199
|
+
url=url, events=event, description=description
|
|
200
|
+
)
|
|
201
|
+
except ApiError as exc:
|
|
202
|
+
exit_on_api_error(exc)
|
|
203
|
+
return
|
|
204
|
+
|
|
205
|
+
success(f"Subscription {sub.id} created: {sub.url}")
|
|
206
|
+
typer.echo(f" Events: {', '.join(sub.events)}")
|
|
207
|
+
if sub.secret:
|
|
208
|
+
typer.echo("")
|
|
209
|
+
typer.echo(f" HMAC SECRET (shown only once): {sub.secret}")
|
|
210
|
+
if sub.secret_warning:
|
|
211
|
+
typer.echo(f" WARNING: {sub.secret_warning}")
|
|
212
|
+
typer.echo(" Store this securely; rotate with `webhook rotate-secret`.")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# ── webhook update ──────────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@app.command("update")
|
|
219
|
+
def update_webhook(
|
|
220
|
+
typer_ctx: typer.Context,
|
|
221
|
+
subscription_id: int = typer.Argument(..., help="Subscription id."),
|
|
222
|
+
url: str | None = typer.Option(
|
|
223
|
+
None, "--url", "-u", help="New URL (optional)."
|
|
224
|
+
),
|
|
225
|
+
event: list[str] = typer.Option(
|
|
226
|
+
[],
|
|
227
|
+
"--event", "-e",
|
|
228
|
+
help=(
|
|
229
|
+
"Replace the event list with these tokens. Pass multiple "
|
|
230
|
+
"times. Pass at least one ``--event`` flag to update; "
|
|
231
|
+
"omit entirely to leave events untouched. (PATCH semantics.)"
|
|
232
|
+
),
|
|
233
|
+
),
|
|
234
|
+
description: str | None = typer.Option(
|
|
235
|
+
None, "--description", "-d", help="New description (optional)."
|
|
236
|
+
),
|
|
237
|
+
activate: bool = typer.Option(
|
|
238
|
+
False, "--activate", help="Set is_active=true."
|
|
239
|
+
),
|
|
240
|
+
deactivate: bool = typer.Option(
|
|
241
|
+
False, "--deactivate", help="Set is_active=false."
|
|
242
|
+
),
|
|
243
|
+
) -> None:
|
|
244
|
+
"""Update one or more fields on a subscription. **PATCH** — only
|
|
245
|
+
the flags you pass get updated.
|
|
246
|
+
|
|
247
|
+
Wraps ``c.webhooks.update(id, **kwargs)``. ``--activate`` and
|
|
248
|
+
``--deactivate`` are mutually exclusive. If you pass neither and
|
|
249
|
+
no other flag, the SDK rejects the empty body with a ValueError
|
|
250
|
+
— the CLI surfaces that with a clear stderr line.
|
|
251
|
+
"""
|
|
252
|
+
if activate and deactivate:
|
|
253
|
+
error("--activate and --deactivate are mutually exclusive.")
|
|
254
|
+
raise typer.Exit(code=1)
|
|
255
|
+
|
|
256
|
+
is_active: bool | None = None
|
|
257
|
+
if activate:
|
|
258
|
+
is_active = True
|
|
259
|
+
elif deactivate:
|
|
260
|
+
is_active = False
|
|
261
|
+
|
|
262
|
+
events_arg = event if event else None
|
|
263
|
+
|
|
264
|
+
state = from_typer_context(typer_ctx)
|
|
265
|
+
with make_client_or_exit(state) as client:
|
|
266
|
+
try:
|
|
267
|
+
sub = client.webhooks.update(
|
|
268
|
+
subscription_id,
|
|
269
|
+
url=url,
|
|
270
|
+
events=events_arg,
|
|
271
|
+
description=description,
|
|
272
|
+
is_active=is_active,
|
|
273
|
+
)
|
|
274
|
+
except ValueError as exc:
|
|
275
|
+
# SDK raises ValueError when no field was set to update
|
|
276
|
+
error(str(exc))
|
|
277
|
+
raise typer.Exit(code=1) from None
|
|
278
|
+
except ApiError as exc:
|
|
279
|
+
exit_on_api_error(exc)
|
|
280
|
+
return
|
|
281
|
+
|
|
282
|
+
success(
|
|
283
|
+
f"Subscription {sub.id} updated: {sub.url} "
|
|
284
|
+
f"(events: {', '.join(sub.events)}, "
|
|
285
|
+
f"is_active={sub.is_active})"
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
# ── webhook delete ──────────────────────────────────────────────────
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
@app.command("delete")
|
|
293
|
+
def delete_webhook(
|
|
294
|
+
typer_ctx: typer.Context,
|
|
295
|
+
subscription_id: int = typer.Argument(..., help="Subscription id."),
|
|
296
|
+
yes: bool = typer.Option(
|
|
297
|
+
False, "--yes", "-y", help="Skip the deletion confirmation prompt."
|
|
298
|
+
),
|
|
299
|
+
) -> None:
|
|
300
|
+
"""Delete a subscription. **Irreversible.** Pending undelivered
|
|
301
|
+
events are dropped at the server.
|
|
302
|
+
|
|
303
|
+
Wraps ``c.webhooks.delete(id)``.
|
|
304
|
+
"""
|
|
305
|
+
state = from_typer_context(typer_ctx)
|
|
306
|
+
confirm_or_exit(
|
|
307
|
+
f"Deleting webhook subscription {subscription_id} is "
|
|
308
|
+
"irreversible — pending undelivered events are dropped at "
|
|
309
|
+
"the server. Re-creating the subscription gets a fresh "
|
|
310
|
+
"secret and a new id.",
|
|
311
|
+
yes=yes,
|
|
312
|
+
)
|
|
313
|
+
with make_client_or_exit(state) as client:
|
|
314
|
+
try:
|
|
315
|
+
client.webhooks.delete(subscription_id)
|
|
316
|
+
except ApiError as exc:
|
|
317
|
+
exit_on_api_error(exc)
|
|
318
|
+
return
|
|
319
|
+
success(f"Webhook subscription {subscription_id} deleted.")
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
# ── webhook rotate-secret ───────────────────────────────────────────
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@app.command("rotate-secret")
|
|
326
|
+
def rotate_secret(
|
|
327
|
+
typer_ctx: typer.Context,
|
|
328
|
+
subscription_id: int = typer.Argument(..., help="Subscription id."),
|
|
329
|
+
yes: bool = typer.Option(
|
|
330
|
+
False, "--yes", "-y",
|
|
331
|
+
help="Skip the rotation confirmation prompt.",
|
|
332
|
+
),
|
|
333
|
+
) -> None:
|
|
334
|
+
"""Generate a fresh HMAC secret for a subscription. **The
|
|
335
|
+
previous secret stops working immediately.**
|
|
336
|
+
|
|
337
|
+
Wraps ``c.webhooks.rotate_secret(id)``. The new secret is
|
|
338
|
+
printed only on this call — capture it before the terminal
|
|
339
|
+
closes.
|
|
340
|
+
"""
|
|
341
|
+
state = from_typer_context(typer_ctx)
|
|
342
|
+
confirm_or_exit(
|
|
343
|
+
f"Rotating the secret for subscription {subscription_id} "
|
|
344
|
+
"invalidates the previous secret immediately. Receivers "
|
|
345
|
+
"still verifying with the old secret will reject deliveries "
|
|
346
|
+
"until you update them with the new one.",
|
|
347
|
+
yes=yes,
|
|
348
|
+
)
|
|
349
|
+
with make_client_or_exit(state) as client:
|
|
350
|
+
try:
|
|
351
|
+
secret = client.webhooks.rotate_secret(subscription_id)
|
|
352
|
+
except ApiError as exc:
|
|
353
|
+
exit_on_api_error(exc)
|
|
354
|
+
return
|
|
355
|
+
|
|
356
|
+
if not secret:
|
|
357
|
+
# Server didn't echo the secret — surface a clear failure
|
|
358
|
+
# instead of silently printing an empty line.
|
|
359
|
+
error(
|
|
360
|
+
f"Secret rotation succeeded but no secret was returned. "
|
|
361
|
+
f"This shouldn't happen; check subscription "
|
|
362
|
+
f"{subscription_id} via `webhook show` and contact "
|
|
363
|
+
f"support if rotation didn't actually take effect."
|
|
364
|
+
)
|
|
365
|
+
raise typer.Exit(code=1)
|
|
366
|
+
success(f"Secret rotated for subscription {subscription_id}:")
|
|
367
|
+
typer.echo(f" HMAC SECRET (shown only once): {secret}")
|
|
368
|
+
typer.echo(" Update every receiver verifying with the previous secret.")
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
# ── webhook deliveries ──────────────────────────────────────────────
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
_DELIVERY_COLUMNS = [
|
|
375
|
+
"id", "event_type", "event_id", "attempts",
|
|
376
|
+
"last_attempted_at", "last_response_code",
|
|
377
|
+
"delivered", "delivered_at",
|
|
378
|
+
]
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
@app.command("deliveries")
|
|
382
|
+
def deliveries(
|
|
383
|
+
typer_ctx: typer.Context,
|
|
384
|
+
subscription_id: int = typer.Argument(..., help="Subscription id."),
|
|
385
|
+
output: OutputFormat | None = typer.Option(
|
|
386
|
+
None, "--output", "-o",
|
|
387
|
+
help="Output format. Overrides the global --output flag.",
|
|
388
|
+
case_sensitive=False,
|
|
389
|
+
),
|
|
390
|
+
) -> None:
|
|
391
|
+
"""List recent delivery attempts (up to 100) for a subscription.
|
|
392
|
+
|
|
393
|
+
Wraps ``c.webhooks.deliveries(id)``. Useful for debugging why a
|
|
394
|
+
receiver isn't getting events — the ``last_error`` and
|
|
395
|
+
``last_response_code`` columns surface what the server saw on
|
|
396
|
+
the most recent attempt.
|
|
397
|
+
"""
|
|
398
|
+
state = from_typer_context(typer_ctx)
|
|
399
|
+
fmt = resolve_output(state, output)
|
|
400
|
+
|
|
401
|
+
with make_client_or_exit(state) as client:
|
|
402
|
+
try:
|
|
403
|
+
history = client.webhooks.deliveries(subscription_id)
|
|
404
|
+
except ApiError as exc:
|
|
405
|
+
exit_on_api_error(exc)
|
|
406
|
+
return
|
|
407
|
+
|
|
408
|
+
if not history:
|
|
409
|
+
typer.echo(
|
|
410
|
+
f"No delivery history yet for subscription {subscription_id}."
|
|
411
|
+
)
|
|
412
|
+
return
|
|
413
|
+
|
|
414
|
+
rows = [
|
|
415
|
+
{
|
|
416
|
+
"id": d.id,
|
|
417
|
+
"event_type": d.event_type,
|
|
418
|
+
"event_id": d.event_id,
|
|
419
|
+
"attempts": d.attempts,
|
|
420
|
+
"last_attempted_at": d.last_attempted_at or "",
|
|
421
|
+
"last_response_code": (
|
|
422
|
+
d.last_response_code if d.last_response_code is not None else ""
|
|
423
|
+
),
|
|
424
|
+
"delivered": "yes" if d.delivered else "no",
|
|
425
|
+
"delivered_at": d.delivered_at or "",
|
|
426
|
+
}
|
|
427
|
+
for d in history
|
|
428
|
+
]
|
|
429
|
+
print_table(
|
|
430
|
+
f"Deliveries for subscription {subscription_id} ({len(rows)})",
|
|
431
|
+
rows,
|
|
432
|
+
columns=_DELIVERY_COLUMNS,
|
|
433
|
+
fmt=fmt,
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
# ── webhook event-types ─────────────────────────────────────────────
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
@app.command("event-types")
|
|
441
|
+
def event_types(
|
|
442
|
+
typer_ctx: typer.Context,
|
|
443
|
+
output: OutputFormat | None = typer.Option(
|
|
444
|
+
None, "--output", "-o",
|
|
445
|
+
help="Output format. Overrides the global --output flag.",
|
|
446
|
+
case_sensitive=False,
|
|
447
|
+
),
|
|
448
|
+
) -> None:
|
|
449
|
+
"""Show the catalog of subscribable event types and wildcards.
|
|
450
|
+
|
|
451
|
+
Wraps ``c.webhooks.event_types()``. Use this to discover which
|
|
452
|
+
event names a receiver can subscribe to — the list grows as the
|
|
453
|
+
server adds events, so the SDK / CLI never hardcode a copy.
|
|
454
|
+
"""
|
|
455
|
+
state = from_typer_context(typer_ctx)
|
|
456
|
+
fmt = resolve_output(state, output)
|
|
457
|
+
|
|
458
|
+
with make_client_or_exit(state) as client:
|
|
459
|
+
try:
|
|
460
|
+
catalog = client.webhooks.event_types()
|
|
461
|
+
except ApiError as exc:
|
|
462
|
+
exit_on_api_error(exc)
|
|
463
|
+
return
|
|
464
|
+
|
|
465
|
+
if fmt is OutputFormat.TABLE:
|
|
466
|
+
if not catalog.event_types and not catalog.wildcards:
|
|
467
|
+
typer.echo("No event types configured upstream.")
|
|
468
|
+
return
|
|
469
|
+
if catalog.event_types:
|
|
470
|
+
typer.echo("Event types:")
|
|
471
|
+
for ev in catalog.event_types:
|
|
472
|
+
typer.echo(f" - {ev}")
|
|
473
|
+
if catalog.wildcards:
|
|
474
|
+
typer.echo("")
|
|
475
|
+
typer.echo("Wildcards:")
|
|
476
|
+
for pat, desc in catalog.wildcards.items():
|
|
477
|
+
typer.echo(f" {pat} - {desc}")
|
|
478
|
+
else:
|
|
479
|
+
data: dict[str, Any] = {
|
|
480
|
+
"event_types": list(catalog.event_types),
|
|
481
|
+
"wildcards": dict(catalog.wildcards),
|
|
482
|
+
}
|
|
483
|
+
print_dict("Webhook event-type catalog", data, fmt=fmt)
|