Skip to content

feat: notification channels v3 APIs - #4829

Open
borosr wants to merge 1 commit into
mainfrom
feat/v3-notification-channels
Open

feat: notification channels v3 APIs#4829
borosr wants to merge 1 commit into
mainfrom
feat/v3-notification-channels

Conversation

@borosr

@borosr borosr commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Overview

Notes for reviewer

Summary by CodeRabbit

  • New Features

    • Added notification channel management to the API and JavaScript/Go SDKs.
    • Supports listing with pagination, sorting, and filters, plus creating, retrieving, updating, and deleting webhook channels.
    • Added channel configuration for URLs, custom headers, labels, disabled status, and signing secrets.
  • Bug Fixes

    • Preserves existing signing secrets when updates omit them.
    • Added validation for webhook URLs, channel types, and filter combinations.
  • Documentation

    • Added notification channel operations and SDK usage to the API documentation.

Greptile Summary

The PR adds v3 notification-channel CRUD APIs and corresponding generated JavaScript and Go SDK surfaces.

  • Defines webhook-channel schemas, filtering, sorting, pagination, and validation.
  • Adds v3 handlers and wires them through the notification service, database adapter, and Svix integration.
  • Preserves existing signing secrets when an update omits the secret.

Confidence Score: 4/5

The PR is not yet safe to merge because clearing custom headers still leaves the previous values active in Svix.

The update mapper and service persist an empty header set, but the Svix updater skips its headers API when that set is empty, leaving delivery behavior inconsistent with the channel returned by the API.

Files Needing Attention: api/v3/handlers/notification/channels/convert.go, openmeter/notification/service/channel.go, openmeter/notification/webhook/svix/webhook.go

Important Files Changed

Filename Overview
api/v3/handlers/notification/channels/convert.go Maps v3 channel request and response representations to notification-domain models, including type and filter translation.
openmeter/notification/service/channel.go Extends channel updates to preserve omitted signing secrets and synchronize persisted channel configuration with Svix.
openmeter/notification/adapter/channel.go Replaces legacy list parameters with reusable typed filters and adds configurable ordering.
openmeter/notification/channel.go Adds channel filtering fields, tenant-scope validation, and absolute HTTP(S) webhook URL validation.
api/v3/server/routes.go Registers the new notification-channel operations on the v3 API surface.

Sequence Diagram

sequenceDiagram
  participant Client
  participant V3 as v3 channel handler
  participant Service as notification service
  participant DB as channel adapter
  participant Svix
  Client->>V3: PUT notification channel
  V3->>Service: UpdateChannel
  Service->>DB: Load existing channel
  DB-->>Service: Existing configuration
  Service->>DB: Persist replacement
  Service->>Svix: Update webhook endpoint
  Service-->>V3: Updated channel
  V3-->>Client: 200 NotificationChannel
Loading

Reviews (2): Last reviewed commit: "feat: notification channels v3 APIs" | Re-trigger Greptile

Context used:

@borosr borosr self-assigned this Aug 2, 2026
@borosr
borosr requested a review from a team as a code owner August 2, 2026 15:00
@borosr borosr added the release-note/feature Release note: Exciting New Features label Aug 2, 2026
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Notification channels now have TypeSpec contracts, domain filtering and validation, v3 HTTP CRUD handlers, Go client support, and JavaScript SDK support. Generated API artifacts expose webhook channel models, pagination, filtering, and CRUD operations.

Changes

Notification channel API contracts

Layer / File(s) Summary
TypeSpec models and operations
api/spec/packages/aip/src/notifications/*
Defines webhook channel models, update semantics, filters, pagination, and CRUD operations.
API registration
api/spec/packages/aip/src/{openmeter,konnect}.tsp, api/spec/packages/aip/src/shared/consts.tsp
Registers the notifications namespace, metadata, and /openmeter/notification/channels endpoint.

Domain and server implementation

Layer / File(s) Summary
Domain filtering and validation
openmeter/notification/*
Adds typed channel filters, namespace validation, URL validation, signing-secret preservation, and rule lookup updates.
HTTP handlers and routing
api/v3/handlers/notification/channels/*, api/v3/server/*
Adds list, create, get, update, and delete handlers with request conversion, service calls, response conversion, error encoding, and route registration.
Generated API
api/v3/api.gen.go
Adds notification-channel models, parameters, server methods, middleware, routes, and the regenerated embedded specification.

Client libraries

Layer / File(s) Summary
Go client
api/v3/client/*notifications*, api/v3/client/client.go
Adds notification-channel models, filtering, pagination, CRUD methods, client initialization, and documentation.
JavaScript SDK
api/spec/packages/aip-client-javascript/src/{models,sdk,funcs}/*
Adds notification-channel schemas, types, operation functions, public exports, internal methods, pagination, and CRUD methods.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GoClient
  participant V3Server
  participant ChannelHandler
  participant NotificationService
  participant NotificationAdapter
  GoClient->>V3Server: Send notification-channel request
  V3Server->>ChannelHandler: Route CRUD operation
  ChannelHandler->>NotificationService: Convert request and invoke service
  NotificationService->>NotificationAdapter: Query or persist channel
  NotificationAdapter-->>NotificationService: Return result
  NotificationService-->>ChannelHandler: Return domain response
  ChannelHandler-->>V3Server: Encode API response
  V3Server-->>GoClient: Return notification-channel response
Loading

Possibly related PRs

Suggested reviewers: tothandras, chrisgacsal, turip

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding v3 notification channel APIs.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/v3-notification-channels

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread api/v3/handlers/notification/channels/convert.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
api/v3/handlers/notification/channels/list.go (1)

79-135: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider folding the repeated bad-request wrapping into one small helper.

Six filter fields repeat the same three-line apierrors.NewBadRequestError block. Only the field name changes. A tiny named helper keeps the mapping easy to scan and stops the blocks from drifting apart when a new filter arrives.

♻️ Sketch of the helper
func invalidFilterParam(ctx context.Context, field string, err error) error {
	return apierrors.NewBadRequestError(ctx, err, apierrors.InvalidParameters{
		{Field: field, Reason: err.Error(), Source: apierrors.InvalidParamSourceQuery},
	})
}
 			id, err := filters.FromAPIFilterULID(params.Filter.Id)
 			if err != nil {
-				return ListNotificationChannelsRequest{}, apierrors.NewBadRequestError(ctx, err, apierrors.InvalidParameters{
-					{Field: "filter[id]", Reason: err.Error(), Source: apierrors.InvalidParamSourceQuery},
-				})
+				return ListNotificationChannelsRequest{}, invalidFilterParam(ctx, "filter[id]", err)
 			}
 			req.ID = id
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v3/handlers/notification/channels/list.go` around lines 79 - 135, Extract
the repeated apierrors.NewBadRequestError construction in the filter parsing
flow into a small named helper, such as invalidFilterParam, accepting the
context, field name, and error. Replace each filter-specific three-line error
block for ID, name, type, disabled, created_at, and updated_at with this helper
while preserving the existing field names and validation behavior.
openmeter/notification/service/channel_test.go (1)

134-143: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use defer clock.UnFreeze() instead of an explicit call.

clock.FreezeTime(tAlphaUpdated) at Line 134 is not paired with defer clock.UnFreeze(). It is followed by an explicit clock.UnFreeze() at Line 142. Today this is safe because the explicit unfreeze runs unconditionally before require.NoError at Line 143. Pair the freeze with a deferred unfreeze to match the repo convention and to stay safe if this block is edited later.

🧹 Proposed fix
 	clock.FreezeTime(tAlphaUpdated)
+	defer clock.UnFreeze()
 	_, err := env.adapter.UpdateChannel(t.Context(), notification.UpdateChannelInput{
 		NamespacedID: models.NamespacedID{Namespace: ns, ID: alpha.ID},
 		Type:         alpha.Type,
 		Name:         alpha.Name,
 		Disabled:     alpha.Disabled,
 		Config:       alpha.Config,
 	})
-	clock.UnFreeze()
 	require.NoError(t, err, "updating alpha to advance its updated_at must not fail")
As per coding guidelines, "Pair `clock.FreezeTime(...)` immediately with `defer clock.UnFreeze()` in the same scope."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/notification/service/channel_test.go` around lines 134 - 143, In
the test block that calls FreezeTime around adapter.UpdateChannel, pair it
immediately with defer clock.UnFreeze() in the same scope and remove the later
explicit UnFreeze call. Keep the existing update and error assertion unchanged.

Source: Coding guidelines

openmeter/notification/channel.go (1)

99-115: 🔒 Security & Privacy | 🔵 Trivial

Nice tightening of the URL check — consider SSRF hardening as a follow-up.

The new check correctly rejects empty, malformed, non-http(s), and non-absolute URLs. That closes an obvious gap.

One thing to keep in mind: this URL is a live webhook delivery target. Format validation alone does not stop a user from pointing a webhook at an internal address (for example a private IP range or a cloud metadata endpoint). If the outbound webhook call path does not already apply an allow-list or deny private/link-local ranges, consider adding that check at request time.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/notification/channel.go` around lines 99 - 115, The current
Validate method only checks URL format; add SSRF protection in the outbound
webhook delivery path by enforcing the existing allow-list or rejecting private,
loopback, link-local, and metadata-reserved destinations at request time. Keep
WebHookChannelConfig.Validate focused on syntactic validation and ensure blocked
targets cannot be requested.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openmeter/notification/adapter/channel.go`:
- Around line 30-35: Make the v3 channel-list behavior consistent for an omitted
filter.disabled value by ensuring ListChannelsInput.Disabled applies a default
false predicate, so disabled channels remain hidden unless explicitly requested.
Update the filter application in the channel adapter around
channeldb.FieldDisabled and preserve explicit disabled filter values;
alternatively, document the permissive default if that is the intended contract.

---

Nitpick comments:
In `@api/v3/handlers/notification/channels/list.go`:
- Around line 79-135: Extract the repeated apierrors.NewBadRequestError
construction in the filter parsing flow into a small named helper, such as
invalidFilterParam, accepting the context, field name, and error. Replace each
filter-specific three-line error block for ID, name, type, disabled, created_at,
and updated_at with this helper while preserving the existing field names and
validation behavior.

In `@openmeter/notification/channel.go`:
- Around line 99-115: The current Validate method only checks URL format; add
SSRF protection in the outbound webhook delivery path by enforcing the existing
allow-list or rejecting private, loopback, link-local, and metadata-reserved
destinations at request time. Keep WebHookChannelConfig.Validate focused on
syntactic validation and ensure blocked targets cannot be requested.

In `@openmeter/notification/service/channel_test.go`:
- Around line 134-143: In the test block that calls FreezeTime around
adapter.UpdateChannel, pair it immediately with defer clock.UnFreeze() in the
same scope and remove the later explicit UnFreeze call. Keep the existing update
and error assertion unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2549fe12-7b8f-4dee-b7a3-38290f62eabf

📥 Commits

Reviewing files that changed from the base of the PR and between 4613041 and 2e27f4a.

⛔ Files ignored due to path filters (1)
  • api/v3/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (40)
  • api/spec/packages/aip-client-javascript/README.md
  • api/spec/packages/aip-client-javascript/src/funcs/index.ts
  • api/spec/packages/aip-client-javascript/src/funcs/notifications.ts
  • api/spec/packages/aip-client-javascript/src/index.ts
  • api/spec/packages/aip-client-javascript/src/models/operations/notifications.ts
  • api/spec/packages/aip-client-javascript/src/models/schemas.ts
  • api/spec/packages/aip-client-javascript/src/models/types.ts
  • api/spec/packages/aip-client-javascript/src/sdk/internal.ts
  • api/spec/packages/aip-client-javascript/src/sdk/notifications.ts
  • api/spec/packages/aip/src/konnect.tsp
  • api/spec/packages/aip/src/notifications/channel.tsp
  • api/spec/packages/aip/src/notifications/index.tsp
  • api/spec/packages/aip/src/notifications/operations.tsp
  • api/spec/packages/aip/src/openmeter.tsp
  • api/spec/packages/aip/src/shared/consts.tsp
  • api/v3/api.gen.go
  • api/v3/client/README.md
  • api/v3/client/client.go
  • api/v3/client/models_notifications.go
  • api/v3/client/notifications.go
  • api/v3/handlers/notification/channels/convert.go
  • api/v3/handlers/notification/channels/convert_test.go
  • api/v3/handlers/notification/channels/create.go
  • api/v3/handlers/notification/channels/delete.go
  • api/v3/handlers/notification/channels/error_encoder.go
  • api/v3/handlers/notification/channels/get.go
  • api/v3/handlers/notification/channels/handler.go
  • api/v3/handlers/notification/channels/list.go
  • api/v3/handlers/notification/channels/update.go
  • api/v3/server/routes.go
  • api/v3/server/server.go
  • openmeter/notification/adapter/channel.go
  • openmeter/notification/adapter/channel_test.go
  • openmeter/notification/channel.go
  • openmeter/notification/httpdriver/channel.go
  • openmeter/notification/service/channel.go
  • openmeter/notification/service/channel_test.go
  • openmeter/notification/service/rule.go
  • openmeter/server/server.go
  • test/notification/channel.go

Comment thread openmeter/notification/adapter/channel.go
@borosr
borosr force-pushed the feat/v3-notification-channels branch from 2e27f4a to d33c926 Compare August 2, 2026 15:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/feature Release note: Exciting New Features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant