feat(api): add webhook CRUD API router and tests (#393)

* feat(api): add webhook CRUD API router and tests

Add tRPC router for managing workspace webhooks:

- list, create, update, delete endpoints (admin role required)
- test endpoint to send a synthetic payload to a webhook URL
- URL validation, event subscription filtering
- Unit tests for all router procedures
- Integration tests with PGlite test database
- Add vitest config and test infrastructure for API package

Depends on #391 (DB schema & repository).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(api): use assertPermission instead of assertUserInWorkspace

Replace assertUserInWorkspace with assertPermission("workspace:manage")
per project conventions. The permissions system is the preferred
authorization approach for new code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(api): use @kan/db alias instead of relative imports in tests

Replace relative path imports (../../db/src/...) with the @kan/db
alias configured in vitest.config.ts for consistency and robustness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(api): use webhookUrlSchema in router input validation

Cherry-pick router-related changes from b2cc9ac:
- Use extracted webhookUrlSchema zod validator in create/update
  input schemas for consistent SSRF checks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(api): replace dynamic import with static import for webhook utility

Add packages/api/src/utils/webhook.ts with sendWebhookToUrl,
createCardWebhookPayload, and webhookUrlSchema. Replace the dynamic
import() in the test endpoint with a static import at the top of the
file for better tree-shaking, type-checking, and readability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(api): align sendWebhooksForWorkspace tests with merged PR #392

The merged delivery utility uses client-side event filtering
(getActiveByWorkspaceId takes 2 args, not 3). Update test assertions
to match the actual implementation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Henry <30578846+hjball@users.noreply.github.com>
This commit is contained in:
Nick Meinhold
2026-03-11 09:27:52 +11:00
committed by GitHub
parent 0b49f502a9
commit 1d5e3a936c
10 changed files with 1217 additions and 12 deletions

89
pnpm-lock.yaml generated
View File

@@ -351,6 +351,9 @@ importers:
typescript:
specifier: 'catalog:'
version: 5.9.2
vitest:
specifier: ^3.0.0
version: 3.2.4(@types/debug@4.1.12)(@types/node@25.0.0)(jiti@2.4.2)(terser@5.44.1)(yaml@2.8.1)
packages/auth:
dependencies:
@@ -12681,6 +12684,14 @@ snapshots:
optionalDependencies:
vite: 7.3.1(@types/node@20.19.11)(jiti@1.21.7)(terser@5.44.1)(yaml@2.8.1)
'@vitest/mocker@3.2.4(vite@7.3.1(@types/node@25.0.0)(jiti@2.4.2)(terser@5.44.1)(yaml@2.8.1))':
dependencies:
'@vitest/spy': 3.2.4
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
vite: 7.3.1(@types/node@25.0.0)(jiti@2.4.2)(terser@5.44.1)(yaml@2.8.1)
'@vitest/pretty-format@3.2.4':
dependencies:
tinyrainbow: 2.0.0
@@ -17944,6 +17955,27 @@ snapshots:
- tsx
- yaml
vite-node@3.2.4(@types/node@25.0.0)(jiti@2.4.2)(terser@5.44.1)(yaml@2.8.1):
dependencies:
cac: 6.7.14
debug: 4.4.3
es-module-lexer: 1.7.0
pathe: 2.0.3
vite: 7.3.1(@types/node@25.0.0)(jiti@2.4.2)(terser@5.44.1)(yaml@2.8.1)
transitivePeerDependencies:
- '@types/node'
- jiti
- less
- lightningcss
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
- tsx
- yaml
vite@7.3.1(@types/node@20.19.11)(jiti@1.21.7)(terser@5.44.1)(yaml@2.8.1):
dependencies:
esbuild: 0.27.2
@@ -17959,6 +17991,21 @@ snapshots:
terser: 5.44.1
yaml: 2.8.1
vite@7.3.1(@types/node@25.0.0)(jiti@2.4.2)(terser@5.44.1)(yaml@2.8.1):
dependencies:
esbuild: 0.27.2
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.6
rollup: 4.56.0
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 25.0.0
fsevents: 2.3.3
jiti: 2.4.2
terser: 5.44.1
yaml: 2.8.1
vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.19.11)(jiti@1.21.7)(terser@5.44.1)(yaml@2.8.1):
dependencies:
'@types/chai': 5.2.3
@@ -18001,6 +18048,48 @@ snapshots:
- tsx
- yaml
vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.0.0)(jiti@2.4.2)(terser@5.44.1)(yaml@2.8.1):
dependencies:
'@types/chai': 5.2.3
'@vitest/expect': 3.2.4
'@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@25.0.0)(jiti@2.4.2)(terser@5.44.1)(yaml@2.8.1))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
'@vitest/spy': 3.2.4
'@vitest/utils': 3.2.4
chai: 5.3.3
debug: 4.4.3
expect-type: 1.3.0
magic-string: 0.30.21
pathe: 2.0.3
picomatch: 4.0.3
std-env: 3.10.0
tinybench: 2.9.0
tinyexec: 0.3.2
tinyglobby: 0.2.15
tinypool: 1.1.1
tinyrainbow: 2.0.0
vite: 7.3.1(@types/node@25.0.0)(jiti@2.4.2)(terser@5.44.1)(yaml@2.8.1)
vite-node: 3.2.4(@types/node@25.0.0)(jiti@2.4.2)(terser@5.44.1)(yaml@2.8.1)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/debug': 4.1.12
'@types/node': 25.0.0
transitivePeerDependencies:
- jiti
- less
- lightningcss
- msw
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
- tsx
- yaml
w3c-keyname@2.2.8: {}
watchpack@2.4.4: