-
Notifications
You must be signed in to change notification settings - Fork 474
webhook testing #964
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
webhook testing #964
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughIntroduces a complete webhook testing feature: a new POST endpoint that sends test webhook messages via Svix with delivery verification and retry logic, dashboard UI components for creating and testing endpoints with status feedback, and Admin interface methods to trigger test webhooks across multiple packages. Changes
Sequence DiagramsequenceDiagram
participant User as User
participant Dashboard as Dashboard UI
participant AdminAPI as Admin App
participant Backend as Backend Route
participant Svix as Svix Service
User->>Dashboard: Click "Test" on endpoint
activate Dashboard
Dashboard->>Dashboard: Open TestEndpointDialog
Dashboard->>AdminAPI: sendTestWebhook(endpointId)
deactivate Dashboard
activate AdminAPI
AdminAPI->>Backend: POST /send-test-webhook {endpoint_id}
deactivate AdminAPI
activate Backend
Backend->>Svix: Get endpoint
Backend->>Svix: Create test message
Note over Backend: Retry up to 3x
Backend->>Svix: List message attempts
Backend->>Svix: Verify successful delivery
Backend-->>AdminAPI: { success: true }
deactivate Backend
activate AdminAPI
AdminAPI-->>Dashboard: Result.ok()
deactivate AdminAPI
activate Dashboard
Dashboard->>Dashboard: Show success status
Dashboard->>User: Display test result & payload
deactivate Dashboard
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes The changes span multiple layers with varied complexity: async retry logic in the backend route with Svix integration, UI state management with form validation and dialog coordination, and interface consistency across three packages. While individual interface additions are simple, the orchestration across components and error handling patterns require careful review. Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review by RecurseML
🔍 Review performed on b677e3f..c9bc00c
✨ No bugs found, your code is sparkling clean
✅ Files analyzed, no issues (5)
• apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx
• apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
• packages/stack-shared/src/interface/admin-interface.ts
• packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
• packages/template/src/lib/stack-app/apps/interfaces/admin-app.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Greptile Overview
Summary
Adds webhook testing functionality that allows users to send test events to webhook endpoints and verify delivery through the dashboard.
Key Changes
- Backend: New
/internal/send-test-webhookAPI endpoint that sends astack.testevent to a specified endpoint and verifies delivery usingResult.retrypattern - Frontend: Enhanced webhook management UI with test dialogs, success/error states, and post-creation test flow
- Type-safe integration: Added
sendTestWebhookmethod across interface, implementation, and type definition layers
Implementation Details
- Uses SmartRouteHandler for the API endpoint (follows custom instruction ab376f82-1d3d-4684-a503-9fb6824b49c2)
- Retries webhook delivery check up to 3 times to allow for async processing
- Comprehensive error handling with user-friendly error messages
- Dialog-based UI with payload preview and status feedback
Confidence Score: 4/5
- This PR is safe to merge with minor style improvements recommended
- Well-structured implementation following established patterns. One minor style issue: form submission should use
runAsynchronouslyWithAlertper custom instructions. No logical errors or security concerns found. - page-client.tsx:117 should use
runAsynchronouslyWithAlertfor consistency with project conventions
Important Files Changed
File Analysis
| Filename | Score | Overview |
|---|---|---|
| apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx | 4/5 | New API endpoint for testing webhook delivery, uses SmartRouteHandler and Result.retry pattern correctly |
| apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx | 3/5 | Adds test webhook UI with dialogs and forms; async handler doesn't use runAsynchronouslyWithAlert |
Sequence Diagram
sequenceDiagram
participant User as User (Dashboard)
participant UI as Webhooks Page
participant AdminApp as StackAdminApp
participant API as Backend API
participant Svix as Svix Service
participant Endpoint as Webhook Endpoint
User->>UI: Click "Test" on endpoint
UI->>UI: Open TestEndpointDialog
User->>UI: Click "Send test event"
UI->>AdminApp: sendTestWebhook({endpointId})
AdminApp->>API: POST /internal/send-test-webhook
API->>Svix: getOrCreate(application)
API->>Svix: endpoint.get(endpointId)
alt Endpoint not found
Svix-->>API: Error
API-->>UI: {success: false, error_message}
UI->>UI: Show error alert
else Endpoint found
API->>Svix: message.create(stack.test event)
alt Message creation fails
Svix-->>API: Error
API->>API: captureError()
API-->>UI: {success: false, error_message}
UI->>UI: Show error alert
else Message created
API->>API: Result.retry(check attempts, 3 times)
loop Retry up to 3 times
API->>Svix: messageAttempt.listByMsg(messageId)
Svix-->>API: Attempt list
API->>API: Check if endpoint has successful attempt
end
alt Successful delivery
API-->>UI: {success: true}
UI->>UI: Show success alert
else No successful delivery
API-->>UI: {success: false, error_message}
UI->>UI: Show error alert
end
end
end
5 files reviewed, 1 comment
| ) : ( | ||
| <Form {...form}> | ||
| <form | ||
| onSubmit={(e) => runAsynchronously(handleSubmit(e))} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: use runAsynchronouslyWithAlert instead per custom instructions
| onSubmit={(e) => runAsynchronously(handleSubmit(e))} | |
| onSubmit={(e) => runAsynchronouslyWithAlert(handleSubmit(e))} |
Context Used: Rule from dashboard - Use runAsynchronouslyWithAlert from @stackframe/stack-shared/dist/utils/promises for async butto... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
Line: 117:117
Comment:
**style:** use `runAsynchronouslyWithAlert` instead per custom instructions
```suggestion
onSubmit={(e) => runAsynchronouslyWithAlert(handleSubmit(e))}
```
**Context Used:** Rule from `dashboard` - Use `runAsynchronouslyWithAlert` from `@stackframe/stack-shared/dist/utils/promises` for async butto... ([source](https://app.greptile.com/review/custom-context?memory=5e671275-7493-402a-93a8-969537ec4d63))
How can I resolve this? If you propose a fix, please make it concise.
Documentation Changes Required1. docs/templates/concepts/webhooks.mdx
Example addition: ### Testing webhooks locally
In addition to third-party services like Svix Playground and Webhook.site, you can now use the built-in `sendTestWebhook` method to test your webhooks locally. This method is available in the StackAdminApp interface and allows you to send a test webhook to a specified endpoint.
Here's an example of how to use the `sendTestWebhook` method:
```typescript
const result = await stackAdminApp.sendTestWebhook({
endpointId: 'your-endpoint-id'
});
if (result.ok) {
console.log('Test webhook sent successfully');
} else {
console.error('Error sending test webhook:', result.error.errorMessage);
}This new feature provides a convenient way to test your webhook endpoints directly from your application. |
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx (1)
196-205: Fix copy: “Delete domain” → “Delete endpoint”Dialog title mismatches the action (endpoints, not domains).
- title="Delete domain" + title="Delete endpoint"
🧹 Nitpick comments (4)
apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx (3)
78-87: Add small backoff and consider a few more retries to avoid false negativesImmediate polling can race delivery; add a short delay between retries and consider 5 attempts.
Apply for example:
- const attemptResult = await Result.retry(async () => { - const attempts = await svix.messageAttempt.listByMsg( - projectId, - messageResult.data.id, - { status: MessageStatus.Success } - ); - const success = attempts.data.some(a => a.endpointId === body.endpoint_id); - return success ? Result.ok(undefined) : Result.error("No successful attempt found"); - }, 3); + const attemptResult = await Result.retry(async () => { + // brief backoff to give delivery a chance to complete + await new Promise(r => setTimeout(r, 600)); + const attempts = await svix.messageAttempt.listByMsg( + projectId, + messageResult.data.id, + { status: MessageStatus.Success } + ); + const success = attempts.data.some(a => a.endpointId === body.endpoint_id); + return success ? Result.ok(undefined) : Result.error("No successful attempt found"); + }, 5);
48-60: Optional: ensure thestack.testevent type exists before sendingSending usually works without pre-creating, but creating once improves catalog visibility and filters in the portal.
const messageResult = await Result.fromPromise(svix.message.create( projectId, { eventType: "stack.test", payload: { type: "stack.test", data: { message: "Stack webhook test event triggered from the Stack dashboard.", endpointUrl: endpoint.url, }, }, }, )); + // Optionally ensure the event type exists for catalog/filters (no-op if it already exists) + // Ignore conflicts/duplicates. + // await svix.eventType.create({ name: "stack.test", description: "Stack dashboard test event" }).catch(() => {});Reference: Svix docs recommend adding event types ahead of time and show
eventType.createusage. (docs.svix.com)
78-85: Optimize attempt listing by filtering server-sideThe Svix SDK v1.76+ supports passing both
statusandendpointIdtomessageAttempt.listByMsg. Update to filter attempts server-side instead of scanning all results:const attempts = await svix.messageAttempt.listByMsg( projectId, messageResult.data.id, - { status: MessageStatus.Success } + { status: MessageStatus.Success, endpointId: body.endpoint_id } ); - const success = attempts.data.some(a => a.endpointId === body.endpoint_id); + const success = attempts.data.length > 0;apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx (1)
255-263: Disable “Send test event” while sending to avoid double-submitsPrevents duplicate requests if the user clicks multiple times.
- okButton={{ - label: 'Send test event', - onClick: async () => { + okButton={{ + label: 'Send test event', + props: { disabled: status === 'sending' }, + onClick: async () => { await sendTestEvent(); return 'prevent-close'; }, }}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx(1 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx(7 hunks)packages/stack-shared/src/interface/admin-interface.ts(1 hunks)packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts(1 hunks)packages/template/src/lib/stack-app/apps/interfaces/admin-app.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
packages/template/**
📄 CodeRabbit inference engine (AGENTS.md)
When modifying the SDK copies, make changes in packages/template (source of truth)
Files:
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.tspackages/template/src/lib/stack-app/apps/interfaces/admin-app.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer ES6 Map over Record when representing key–value collections
Files:
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.tspackages/template/src/lib/stack-app/apps/interfaces/admin-app.tspackages/stack-shared/src/interface/admin-interface.tsapps/backend/src/app/api/latest/internal/send-test-webhook/route.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
apps/backend/src/app/api/latest/**
📄 CodeRabbit inference engine (AGENTS.md)
apps/backend/src/app/api/latest/**: Organize backend API routes by resource under /api/latest (e.g., auth at /api/latest/auth/, users at /api/latest/users/, teams at /api/latest/teams/, oauth providers at /api/latest/oauth-providers/)
Use the custom route handler system in the backend to ensure consistent API responses
Files:
apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx
{apps/dashboard,apps/dev-launchpad,packages/stack-ui,packages/react}/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
For blocking alerts and errors in UI, do not use toast notifications; use alerts instead
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
{apps/dashboard,apps/dev-launchpad,packages/stack-ui,packages/react}/**/*.{tsx,jsx,css}
📄 CodeRabbit inference engine (AGENTS.md)
Keep hover/click animations snappy; avoid pre-transition delays on hover and apply transitions after the action (e.g., fade-out on hover end)
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
🧬 Code graph analysis (3)
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts (1)
packages/stack-shared/src/utils/errors.tsx (1)
throwErr(10-19)
apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx (4)
apps/backend/src/route-handlers/smart-route-handler.tsx (1)
createSmartRouteHandler(209-294)packages/stack-shared/src/schema-fields.ts (6)
yupObject(247-251)adminAuthTypeSchema(483-483)adaptSchema(330-330)yupString(187-190)yupNumber(191-194)yupBoolean(195-198)apps/backend/src/lib/webhooks.tsx (1)
getSvixClient(13-18)packages/stack-shared/src/utils/errors.tsx (2)
captureError(126-134)StackAssertionError(69-85)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx (6)
packages/stack-shared/src/schema-fields.ts (1)
urlSchema(334-342)packages/stack-ui/src/components/action-dialog.tsx (1)
ActionDialog(31-135)packages/stack-shared/src/utils/promises.tsx (1)
runAsynchronously(343-366)apps/dashboard/src/components/form-fields.tsx (1)
InputField(59-97)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/utils.tsx (1)
getSvixResult(12-32)apps/dashboard/src/components/settings.tsx (1)
SettingCard(11-48)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: Vercel Agent Review
- GitHub Check: all-good
- GitHub Check: build (22.x)
- GitHub Check: docker
- GitHub Check: build (22.x)
- GitHub Check: docker
- GitHub Check: restart-dev-and-test
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: setup-tests
- GitHub Check: lint_and_build (latest)
- GitHub Check: Security Check
🔇 Additional comments (13)
packages/stack-shared/src/interface/admin-interface.ts (1)
357-368: LGTM: interface method matches backend route and response contractPayload key (
endpoint_id) and return shape are consistent with the server. No issues.packages/template/src/lib/stack-app/apps/interfaces/admin-app.ts (1)
59-60: LGTM: API surface is consistent with Result-pattern used elsewhereSignature and error mapping align with adjacent methods.
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts (1)
467-477: LGTM: correct delegation and error mappingGood use of
throwErrfallback and consistent Result handling.apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx (5)
61-75: Nice: captured send failure with contextGood use of
captureErrorandStackAssertionErrorwithcauseand identifiers.
8-29: Schema and metadata look correctAdmin auth required, strict POST, and typed JSON response are aligned with our handler system.
34-47: Endpoint existence handling is clearReturning a friendly message when the endpoint is missing is good UX for admins.
98-106: LGTM: success pathConsistent response shape with interface/clients.
1-7: Confirmed: MessageStatus is compatible with Svix 1.25.0The Svix npm package version 1.25.0 exports the MessageStatus enum with members including Success, which is declared in the backend's package.json and used correctly at line 82 of the route file (
{ status: MessageStatus.Success }). No compatibility issues exist.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx (5)
120-137: Good security UX: HTTP warningClear warning for insecure URLs in dev; aligns with our UI guidelines (alerts over toasts).
41-49: Form setup looks solidyupResolver + onChange mode and defaults are correct; resets on close to keep state clean.
59-67: Endpoint create flow is straightforwardCreates via Svix SDK then shows confirmation with follow-up actions. Nice touch using
onTestRequested.
321-326: Action menu addition is appropriate“Test” action wires through to dialog; good cohesion with
onTestEndpoint.
371-406: Provider wiring and test-dialog state handling look correctUses
useSvixToken, refresh via counter, and controlled dialog state. LGTM.
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
Show resolved
Hide resolved
|
You have run out of free Bugbot PR reviews for this billing cycle. This will reset on November 10. To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial. |
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
Show resolved
Hide resolved
| const attempts = await svix.messageAttempt.listByMsg( | ||
| projectId, | ||
| messageResult.data.id, | ||
| { status: MessageStatus.Success } | ||
| ); | ||
| const success = attempts.data.some(a => a.endpointId === body.endpoint_id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| const attempts = await svix.messageAttempt.listByMsg( | |
| projectId, | |
| messageResult.data.id, | |
| { status: MessageStatus.Success } | |
| ); | |
| const success = attempts.data.some(a => a.endpointId === body.endpoint_id); | |
| const attemptsResult = await Result.fromPromise(svix.messageAttempt.listByMsg( | |
| projectId, | |
| messageResult.data.id, | |
| { status: MessageStatus.Success } | |
| )); | |
| if (attemptsResult.status === "error") { | |
| return Result.error("Failed to list message attempts"); | |
| } | |
| const attempts = attemptsResult.data; | |
| const success = attempts.data?.some(a => a.endpointId === body.endpoint_id); |
The svix.messageAttempt.listByMsg() call inside the retry function is not wrapped in error handling, which means exceptions would crash the retry logic instead of being caught and retried.
View Details
Analysis
Unhandled exception in Result.retry() for svix.messageAttempt.listByMsg() call
What fails: The svix.messageAttempt.listByMsg() call in send-test-webhook route handler (line 79-82) is not wrapped in Result.fromPromise(), causing unhandled exceptions to escape the retry logic
How to reproduce:
- Trigger webhook test with network failure or API rate limiting
- Svix client throws exception (e.g., network timeout, rate limit)
- Exception propagates out of
Result.retry()function instead of being caught and retried
Result: Route handler crashes with unhandled promise rejection instead of graceful error handling. Retry logic bypassed completely.
Expected: All exceptions should be converted to Result.error() via Result.fromPromise() wrapper, allowing retry logic to handle failures properly (consistent with other Svix calls in same file at lines 35 and 48)
Evidence: Result.retry() function signature expects (attemptIndex: number) => Result<T> | Promise<Result<T>> but current code calls async function that can throw exceptions directly
| const projectId = auth.tenancy.project.id; | ||
| const svix = getSvixClient(); | ||
|
|
||
| await svix.application.getOrCreate({ uid: projectId, name: projectId }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| await svix.application.getOrCreate({ uid: projectId, name: projectId }); | |
| const appResult = await Result.fromPromise(svix.application.getOrCreate({ uid: projectId, name: projectId })); | |
| if (appResult.status === "error") { | |
| return { | |
| statusCode: 200, | |
| bodyType: "json", | |
| body: { | |
| success: false, | |
| error_message: "Failed to create or retrieve application.", | |
| }, | |
| }; | |
| } | |
The svix.application.getOrCreate() call is not wrapped in Result.fromPromise, which means exceptions thrown by the Svix SDK would crash the route handler instead of being handled gracefully.
View Details
Analysis
Unhandled exception in send-test-webhook route causes 500 errors
What fails: svix.application.getOrCreate() in /apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx:34 throws ApiException when Svix API calls fail, crashing the route handler
How to reproduce:
// Trigger with invalid Svix credentials or network issues
const svix = new Svix('invalid-api-key');
await svix.application.getOrCreate({ uid: 'test-project', name: 'test-project' });Result: Throws ApiException with HTTP 401/network errors, causing route handler to return 500 instead of graceful error response
Expected: Should return structured error response like other Svix calls in the same file (lines 35, 48) that use Result.fromPromise()
| okButton={{ | ||
| label: 'Send test event', | ||
| onClick: async () => { | ||
| await sendTestEvent(); | ||
| return 'prevent-close'; | ||
| }, | ||
| }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "Send test event" button in the TestEndpointDialog is not disabled during the async webhook test operation, allowing users to click it multiple times and trigger duplicate API requests.
View Details
📝 Patch Details
diff --git a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
index b34fe218..cdea27fe 100644
--- a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
+++ b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
@@ -258,6 +258,7 @@ function TestEndpointDialog(props: { endpoint: Endpoint, open: boolean, onOpenCh
await sendTestEvent();
return 'prevent-close';
},
+ props: { disabled: status === 'sending' }
}}
cancelButton={status === 'sending' ? false : { label: 'Cancel' }}
>
Analysis
"Send test event" button allows multiple concurrent webhook API calls
What fails: TestEndpointDialog okButton remains enabled during async sendTestEvent() operation (status === 'sending'), allowing multiple simultaneous API requests
How to reproduce:
- Open webhook test dialog in dashboard
- Click "Send test event" button rapidly multiple times before first request completes
- Multiple API calls to
stackAdminApp.sendTestWebhook()execute concurrently
Result: Multiple identical webhook test requests sent simultaneously instead of button being disabled during operation
Expected: Button should be disabled during sending state, consistent with cancelButton behavior which hides when status === 'sending'
Fix: Added props: { disabled: status === 'sending' } to okButton configuration, following existing pattern used elsewhere in codebase for disabled buttons
| const handleSubmit = form.handleSubmit(async (values) => { | ||
| const created = await svix.endpoint.create(appId, { url: values.url, description: values.description }); | ||
| setCreatedEndpoint({ | ||
| id: created.id, | ||
| url: created.url, | ||
| description: created.description, | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If svix.endpoint.create() fails, the error will be silently caught and logged but not displayed to the user, leaving them unaware that endpoint creation failed.
View Details
📝 Patch Details
diff --git a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
index b34fe218..55705902 100644
--- a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
+++ b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx
@@ -16,7 +16,7 @@ import { AppEnabledGuard } from "../app-enabled-guard";
import { PageLayout } from "../page-layout";
import { useAdminApp } from "../use-admin-app";
import { getSvixResult } from "./utils";
-import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises";
+import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
type Endpoint = {
id: string,
@@ -114,7 +114,7 @@ function CreateDialog(props: {
) : (
<Form {...form}>
<form
- onSubmit={(e) => runAsynchronously(handleSubmit(e))}
+ onSubmit={(e) => runAsynchronouslyWithAlert(handleSubmit(e))}
className="space-y-4"
>
<Alert>
Analysis
Silent error handling in webhook endpoint creation leaves users without feedback
What fails: CreateDialog in apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsx uses runAsynchronously() which catches svix.endpoint.create() errors but doesn't show them to users
How to reproduce:
- Navigate to webhook settings page
- Click "Create new endpoint"
- Enter invalid URL or trigger network failure
- Click "Create" button
Result: Form submission fails silently - errors logged to console but user sees no feedback, form remains in normal state
Expected: Error should be displayed to user via alert (like other forms) so they know endpoint creation failed
Fix applied: Changed runAsynchronously() to runAsynchronouslyWithAlert() to match pattern used in new-project creation and provide user error feedback
| }; | ||
| } | ||
|
|
||
| const attemptResult = await Result.retry(async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Result.retry callback function is missing the required attemptIndex parameter. The function signature expects (attemptIndex: number) => Result<T>, but the code passes a function with no parameters.
View Details
📝 Patch Details
diff --git a/apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx b/apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx
index c0b42dc7..26b4f6d9 100644
--- a/apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx
+++ b/apps/backend/src/app/api/latest/internal/send-test-webhook/route.tsx
@@ -75,7 +75,7 @@ export const POST = createSmartRouteHandler({
};
}
- const attemptResult = await Result.retry(async () => {
+ const attemptResult = await Result.retry(async (_attemptIndex) => {
const attempts = await svix.messageAttempt.listByMsg(
projectId,
messageResult.data.id,
diff --git a/apps/backend/src/lib/webhooks.tsx b/apps/backend/src/lib/webhooks.tsx
index cac56608..f6820c2d 100644
--- a/apps/backend/src/lib/webhooks.tsx
+++ b/apps/backend/src/lib/webhooks.tsx
@@ -46,7 +46,7 @@ async function sendWebhooks(options: {
function createWebhookSender<T extends yup.Schema>(event: WebhookEvent<T>) {
return async (options: { projectId: string, data: yup.InferType<typeof event.schema> }) => {
- await Result.retry(async () => {
+ await Result.retry(async (_attemptIndex) => {
try {
return Result.ok(await sendWebhooks({
type: event.type,
Analysis
Missing required parameter in Result.retry callback functions
What fails: Result.retry function calls in send-test-webhook/route.tsx:78 and webhooks.tsx:49 pass callback functions with no parameters, but the function signature requires (attemptIndex: number) => Result<T>
How to reproduce:
# TypeScript compilation should catch this with --strict mode:
cd apps/backend && pnpm run typecheckResult: TypeScript parameter count mismatch error (function expects 1 parameter but receives 0)
Expected: Callback functions should declare the attemptIndex parameter per the TypeScript function signature - use _attemptIndex if not needed (matching pattern used elsewhere in codebase)
https://www.loom.com/share/abbd19d1deaa4b3d8f8ae9c1f2f9b098?sid=d105ee21-f380-4c37-85f1-a58610cfff9d
High-level PR Summary
This PR adds webhook testing functionality to the dashboard, allowing users to send test events to their configured webhook endpoints and verify they're working correctly. The implementation adds a new backend API endpoint that creates a
stack.testevent via Svix, verifies successful delivery, and provides clear feedback. The dashboard UI is enhanced with a test dialog that shows a preview of the test payload and displays success or error messages, with the test action accessible both from the endpoint creation flow and the action menu for existing endpoints.⏱️ Estimated Review Time: 15-30 minutes
💡 Review Order Suggestion
packages/stack-shared/src/interface/admin-interface.tspackages/template/src/lib/stack-app/apps/interfaces/admin-app.tspackages/template/src/lib/stack-app/apps/implementations/admin-app-impl.tsapps/backend/src/app/api/latest/internal/send-test-webhook/route.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/page-client.tsxImportant
Add functionality to test webhooks from the dashboard, including backend route and frontend UI updates.
POSTroute insend-test-webhook/route.tsxto handle test webhook requests.yup.svixand handle success/error cases.page-client.tsxto include a dialog for creating and testing endpoints.TestEndpointDialogcomponent to handle the test webhook UI.CreateDialogandActionMenuto support triggering the test webhook.sendTestWebhookmethod toStackAdminInterfaceinadmin-interface.ts.sendTestWebhookin_StackAdminAppImplIncompleteinadmin-app-impl.ts.StackAdminAppinterface inadmin-app.tsto includesendTestWebhookmethod.This description was created by
for c9bc00c. You can customize this summary. It will automatically update as commits are pushed.