Skip to content

refactor: notification validators - #3529

Merged
chrisgacsal merged 4 commits into
mainfrom
refactor/notification-validators
Oct 24, 2025
Merged

refactor: notification validators#3529
chrisgacsal merged 4 commits into
mainfrom
refactor/notification-validators

Conversation

@chrisgacsal

@chrisgacsal chrisgacsal commented Oct 24, 2025

Copy link
Copy Markdown
Collaborator

Overview

Reafctor validators in notification package to use common ValidationError and to support Validator and CustomValidator interfaces.

Summary by CodeRabbit

  • Bug Fixes

    • Stronger runtime validation across notification rules, channels, payloads and delivery status with consistent aggregated reporting.
  • Improvements

    • Event listing now defaults to ID-based ordering.
    • GetEvent and various update requests now use explicit Namespace and ID fields (unified identifier shape).
    • Validation now uses a unified generic validation error type.
  • Tests

    • Notification tests updated to reflect identifier and validation changes.

@chrisgacsal chrisgacsal self-assigned this Oct 24, 2025
@chrisgacsal chrisgacsal added the release-note/ignore Ignore this change when generating release notes label Oct 24, 2025
@chrisgacsal
chrisgacsal marked this pull request as ready for review October 24, 2025 10:07
@chrisgacsal
chrisgacsal requested a review from a team as a code owner October 24, 2025 10:07
@coderabbitai

coderabbitai Bot commented Oct 24, 2025

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Multiple notification package refactors: migrate validations to models.Validator/models.GenericValidationError patterns (add Validate/ValidateWith across inputs), switch several Update/Get inputs to use NamespacedID or top-level Namespace+ID, add runtime rule/config validation, and adjust event ordering default to ID.

Changes

Cohort / File(s) Summary
DB validation
openmeter/ent/db/notificationrule_create.go, openmeter/ent/db/notificationrule_update.go
Add runtime validation of NotificationRule.config in create/update checks by calling Validate() and wrapping failures.
Validation primitives removed
openmeter/notification/errors.go, openmeter/notification/validator.go
Remove package ValidationError type and the package-local validator interface; migrate error/validator usage to models.GenericValidationError / models.Validator patterns.
Validation framework & public types
openmeter/notification/channel.go, openmeter/notification/event.go, openmeter/notification/rule.go, openmeter/notification/deliverystatus.go, openmeter/notification/entitlements.go, openmeter/notification/invoice.go, openmeter/notification/eventpayload.go, openmeter/notification/validator.go
Add ValidateWith/Validate methods and models.Validator assertions for many public types; change validations from context-based to models.Validator-based; aggregate validation errors via multi-error patterns; introduce/newly expose config types (e.g., ChannelConfigMeta, ChannelConfig, WebHookChannelConfig) and change several inputs to use NamespacedID or top-level Namespace+ID.
Adapter ordering change
openmeter/notification/adapter/event.go
Reorder listing switch: OrderByCreatedAt maps to created-at ordering; default (or OrderByID) now yields ID ordering unless OrderByCreatedAt is requested.
HTTP driver adjustments
openmeter/notification/httpdriver/errors.go, openmeter/notification/httpdriver/event.go, openmeter/notification/httpdriver/mapping.go
Error encoder now recognizes *models.GenericValidationError for 400 responses; GetEvent request shape changed to top-level Namespace/ID; mapping helpers updated to accept explicit resource IDs and populate NamespacedID for update inputs.
Service layer updates
openmeter/notification/service/channel.go, openmeter/notification/service/deliverystatus.go, openmeter/notification/service/event.go, openmeter/notification/service/rule.go
Call new parameterless Validate() on inputs (remove ctx/service from Validate calls); switch update inputs to NamespacedID; add pre-create/pre-update config validation via ValidateRuleConfigWithFeatures; standardize error wrapping.
Tests updated
test/notification/channel.go, test/notification/event.go, test/notification/repository.go, test/notification/rule.go
Update tests to use new NamespacedID / top-level Namespace+ID shapes; add BalanceThresholdRuleConfig in test rule setup; adjust test payloads accordingly.
HTTP mapping & helper signatures
openmeter/notification/httpdriver/mapping.go
Update several mapping helper signatures to accept explicit channelID/ruleID and to populate NamespacedID in constructed update inputs.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "refactor: notification validators" accurately describes the main change in the changeset. The raw_summary shows a comprehensive refactoring of validation logic across the notification package, including removal of the package-local ValidationError type, introduction of ValidateWith/Validate method patterns across multiple types, replacement of error handling with models.GenericValidationError, and implementation of Validator and CustomValidator interface conformance checks. The title is concise, clear, and specific enough that a teammate scanning the repository history would understand this represents a reorganization of notification validation infrastructure rather than a feature addition or bug fix.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/notification-validators

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0352511 and 8cda90d.

📒 Files selected for processing (2)
  • openmeter/notification/service/channel.go (6 hunks)
  • openmeter/notification/service/rule.go (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • openmeter/notification/service/rule.go
🧰 Additional context used
🧬 Code graph analysis (1)
openmeter/notification/service/channel.go (3)
openmeter/notification/channel.go (3)
  • CreateChannelInput (144-155)
  • Channel (37-51)
  • UpdateChannelInput (188-199)
pkg/models/id.go (1)
  • NamespacedID (7-10)
pkg/models/errors.go (1)
  • NewGenericValidationError (138-140)
⏰ 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). (8)
  • GitHub Check: E2E
  • GitHub Check: Quickstart
  • GitHub Check: Migration Checks
  • GitHub Check: Test
  • GitHub Check: Lint
  • GitHub Check: Code Generators
  • GitHub Check: Build
  • GitHub Check: Analyze (go)
🔇 Additional comments (5)
openmeter/notification/service/channel.go (5)

13-13: LGTM: Import added for refactored validation patterns.

The pkg/models import is necessary to support the new NamespacedID structure and GenericValidationError type introduced in this refactoring.


17-17: LGTM: Validation interface simplified consistently.

The refactor removes context and service parameters from all Validate() calls, making the validation interface cleaner and more focused on input state validation rather than runtime dependencies. This change is applied consistently across all channel operations.

Also applies to: 25-25, 95-95, 148-148, 156-156


67-76: LGTM: NamespacedID structure correctly adopted.

The UpdateChannelInput construction now uses the refactored models.NamespacedID type, properly setting both Namespace and ID fields. This aligns with the updated type definition and provides better encapsulation of namespace-scoped identifiers.


126-129: LGTM: Appropriate use of GenericValidationError.

Wrapping the rule assignment check error with models.NewGenericValidationError is correct, as this represents a validation failure (cannot delete a channel that's assigned to active rules). The error message provides good context with namespace, channel ID, and affected rule IDs.


183-192: LGTM: Excellent addition of type immutability validation.

The ValidateWith custom validation prevents channel type changes during updates, which is important for data integrity. This runtime validation is correctly placed after fetching the existing channel and properly uses the CustomValidator pattern mentioned in the PR objectives. The error message clearly communicates the constraint.


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 and usage tips.

@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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (12)
test/notification/event.go (3)

232-233: Fix misleading assertion messages in TestGetEvent

Use “event” wording.

-	require.NoError(t, err, "Creating rule must not return error")
-	require.NotNil(t, event, "Rule must not be nil")
+	require.NoError(t, err, "Creating event must not return error")
+	require.NotNil(t, event, "Event must not be nil")

273-276: Test asserts the wrong variable; may mask failures

You’re checking ‘event’ instead of ‘events’.

-	events, err := service.ListEvents(ctx, listIn)
-	require.NoError(t, err, "Listing notification events must not return error")
-	require.NotNil(t, event, "Notification events must not be nil")
+	events, err := service.ListEvents(ctx, listIn)
+	require.NoError(t, err, "Listing notification events must not return error")
+	require.NotNil(t, events, "Notification events must not be nil")

307-310: Same issue: wrong NotNil target in TestListDeliveryStatus

Asserting ‘event’ instead of ‘statuses’.

-	statuses, err := service.ListEvents(ctx, listIn)
-	require.NoError(t, err, "Listing notification event delivery statuses must not return error")
-	require.NotNil(t, event, "Notification event delivery statuses must not be nil")
+	statuses, err := service.ListEvents(ctx, listIn)
+	require.NoError(t, err, "Listing notification event delivery statuses must not return error")
+	require.NotNil(t, statuses, "Notification event delivery statuses must not be nil")
test/notification/rule.go (2)

161-169: Wrong namespace passed to NewCreateRuleInput across tests

You’re passing a name where a namespace is expected, which can break creation and listing.

-	createIn1 := NewCreateRuleInput("NotificationListRule1", s.channel.ID)
+	createIn1 := NewCreateRuleInput(s.Env.Namespace(), "NotificationListRule1", s.channel.ID)
-	createIn2 := NewCreateRuleInput("NotificationListRule2", s.channel.ID)
+	createIn2 := NewCreateRuleInput(s.Env.Namespace(), "NotificationListRule2", s.channel.ID)

-	createIn := NewCreateRuleInput("NotificationUpdateRule1", s.channel.ID)
+	createIn := NewCreateRuleInput(s.Env.Namespace(), "NotificationUpdateRule1", s.channel.ID)

-	createIn := NewCreateRuleInput("NotificationDeleteRule1", s.channel.ID)
+	createIn := NewCreateRuleInput(s.Env.Namespace(), "NotificationDeleteRule1", s.channel.ID)

-	createIn := NewCreateRuleInput("NotificationGetRule1", s.channel.ID)
+	createIn := NewCreateRuleInput(s.Env.Namespace(), "NotificationGetRule1", s.channel.ID)

Also applies to: 204-208, 243-249, 260-263


278-281: Fix self-equality assertions in TestGet

You’re comparing a value to itself; compare to rule2.

-	assert.Equal(t, rule.Type, rule.Type, "Rule type must be the same")
-	assert.Equal(t, rule.Channels, rule.Channels, "Rule channels must be the same")
-	assert.EqualValues(t, rule.Config, rule.Config, "Rule config must be the same")
+	assert.Equal(t, rule.Type, rule2.Type, "Rule type must be the same")
+	assert.EqualValues(t, rule.Channels, rule2.Channels, "Rule channels must be the same")
+	assert.EqualValues(t, rule.Config, rule2.Config, "Rule config must be the same")
test/notification/channel.go (1)

124-126: Tidy up assertion messages; add name assertion

Minor message fixes and verify updated name.

-	channel2, err := service.UpdateChannel(ctx, updateIn)
-	require.NoError(t, err, "Creating channel must not return error")
+	channel2, err := service.UpdateChannel(ctx, updateIn)
+	require.NoError(t, err, "Updating channel must not return error")
 	require.NotNil(t, channel2, "Channel must not be nil")
+	assert.Equal(t, updateIn.Name, channel2.Name, "Channel name must match")

-	channel2, err := service.GetChannel(ctx, notification.GetChannelInput{
+	channel2, err := service.GetChannel(ctx, notification.GetChannelInput{
 		Namespace: channel.Namespace,
 		ID:        channel.ID,
 	})
-	require.NoError(t, err, "Deleting channel must not return error")
+	require.NoError(t, err, "Getting channel must not return error")
 	require.NotNil(t, channel2, "Channel must not be nil")
 	assert.NotEmpty(t, channel2.ID, "Channel ID must not be empty")
 	assert.Equal(t, channel.Namespace, channel2.Namespace, "Channel namespace must be equal")
 	assert.Equal(t, channel.ID, channel2.ID, "Channel ID must be equal")
-	assert.Equal(t, channel.Disabled, channel2.Disabled, "Channel disabled must not be equal")
+	assert.Equal(t, channel.Disabled, channel2.Disabled, "Channel disabled must match")

Also applies to: 162-163, 167-169

openmeter/notification/service/channel.go (2)

199-202: Wrong error message: says create on update path

Change to “failed to update channel”.

-			return nil, fmt.Errorf("failed to create channel: %w", err)
+			return nil, fmt.Errorf("failed to update channel: %w", err)

85-86: Align invalid type errors with validation error scheme

Return a GenericValidationError for invalid channel type to match delete-path semantics.

-			return nil, fmt.Errorf("invalid channel type: %s", channel.Type)
+			return nil, models.NewGenericValidationError(
+				fmt.Errorf("invalid channel type: %s", channel.Type),
+			)

And similarly in UpdateChannel’s default branch.

Also applies to: 227-229

openmeter/notification/httpdriver/event.go (1)

114-116: Clarify error message on nil event

This endpoint “gets” events, not “creates”.

-				return GetEventResponse{}, errors.New("failed to create test event: nil event returned")
+				return GetEventResponse{}, errors.New("failed to get event: nil event returned")
openmeter/notification/service/rule.go (2)

47-63: External side effects inside transaction can leave inconsistent state

UpdateWebhookChannels is called inside the DB transaction. If the transaction later rolls back, webhook changes may persist, causing drift. Prefer: commit DB first then apply webhook updates (with retries/outbox), or use a post‑commit hook.


184-206: Update: external side effects before DB update

Webhook channel updates happen before s.adapter.UpdateRule. If UpdateRule fails, webhooks are already mutated. Apply after a successful commit or via outbox to avoid divergence.

openmeter/notification/channel.go (1)

3-11: Fix import path and add net/url. Build currently breaks.

The webhook import path includes an extra “openmeter” segment and fails to resolve. Also add net/url for URL validation (see next comment).

Apply this diff:

 import (
   "errors"
   "fmt"
+  "net/url"
 
-  "github.com/openmeterio/openmeter/openmeter/notification/webhook"
+  "github.com/openmeterio/openmeter/notification/webhook"
   "github.com/openmeterio/openmeter/pkg/models"
   "github.com/openmeterio/openmeter/pkg/pagination"
   "github.com/openmeterio/openmeter/pkg/sortx"
 )
🧹 Nitpick comments (9)
openmeter/notification/service/channel.go (1)

67-79: Optional: validate synthesized updateIn before adapter call

Defensive check; keeps invariants even when upstream changes occur.

 			updateIn := notification.UpdateChannelInput{
 				NamespacedID: models.NamespacedID{
 					Namespace: params.Namespace,
 					ID:        channel.ID,
 				},
 				Type:     channel.Type,
 				Name:     channel.Name,
 				Disabled: channel.Disabled,
 				Config:   channel.Config,
 			}
 			updateIn.Config.WebHook.SigningSecret = wb.Secret
+			if err := updateIn.Validate(); err != nil {
+				return nil, fmt.Errorf("invalid synthesized update payload: %w", err)
+			}
openmeter/notification/httpdriver/mapping.go (1)

503-516: Optional: unify error type from FromEventType default

For consistency, consider returning models.NewGenericValidationError on invalid event type here too.

- default:
-   return "", fmt.Errorf("invalid notification event type: %s", t)
+ default:
+   return "", models.NewGenericValidationError(fmt.Errorf("invalid notification event type: %s", t))
openmeter/notification/event.go (2)

166-178: CreateEventInput: consider basic required-field checks

Add namespace and ruleId presence checks to catch obvious mistakes early.

 func (i CreateEventInput) Validate() error {
   var errs []error

   if err := i.Type.Validate(); err != nil {
     errs = append(errs, err)
   }
+  if i.Namespace == "" {
+    errs = append(errs, errors.New("namespace is required"))
+  }
+  if i.RuleID == "" {
+    errs = append(errs, errors.New("ruleId is required"))
+  }

   return models.NewNillableGenericValidationError(errors.Join(errs...))
 }

22-24: Optional: return a copy from EventTypes()

Avoid exposing the backing slice to callers to prevent accidental mutation.

-func EventTypes() []EventType {
-	return eventTypes
-}
+func EventTypes() []EventType {
+	out := make([]EventType, len(eventTypes))
+	copy(out, eventTypes)
+	return out
+}
openmeter/notification/rule.go (3)

39-63: Add MaxChannelsPerRule parity to Rule.Validate.

Create/Update inputs enforce MaxChannelsPerRule; Rule itself should mirror this for consistency (e.g., on upserts/backfills).

Apply this diff:

 func (r Rule) Validate() error {
   var errs []error
@@
   if err := r.Config.Validate(); err != nil {
     errs = append(errs, err)
   }
+
+  if len(r.Channels) > MaxChannelsPerRule {
+    errs = append(errs, fmt.Errorf("too many channels: %d > %d", len(r.Channels), MaxChannelsPerRule))
+  }
 
   return models.NewNillableGenericValidationError(errors.Join(errs...))
 }

193-217: Reject duplicate/empty channel IDs in CreateRuleInput.

Prevent duplicates and empty IDs to avoid ambiguous routing and noisy rules.

Apply this diff:

 func (i CreateRuleInput) Validate() error {
   var errs []error
@@
   if err := i.Config.Validate(); err != nil {
     errs = append(errs, err)
   }
+
+  // Enforce unique, non-empty channels
+  seen := make(map[string]struct{}, len(i.Channels))
+  for _, ch := range i.Channels {
+    if ch == "" {
+      errs = append(errs, errors.New("channel id must not be empty"))
+      continue
+    }
+    if _, ok := seen[ch]; ok {
+      errs = append(errs, fmt.Errorf("duplicate channel: %s", ch))
+      continue
+    }
+    seen[ch] = struct{}{}
+  }
 
   if len(i.Channels) > MaxChannelsPerRule {
     errs = append(errs, fmt.Errorf("too many channels: %d > %d", len(i.Channels), MaxChannelsPerRule))
   }

243-271: Mirror duplicate/empty channel ID checks in UpdateRuleInput.

Same rationale as Create: avoid duplicate/empty channel references on updates.

Apply this diff:

 func (i UpdateRuleInput) Validate() error {
   var errs []error
@@
   if err := i.Config.Validate(); err != nil {
     errs = append(errs, err)
   }
+
+  // Enforce unique, non-empty channels
+  seen := make(map[string]struct{}, len(i.Channels))
+  for _, ch := range i.Channels {
+    if ch == "" {
+      errs = append(errs, errors.New("channel id must not be empty"))
+      continue
+    }
+    if _, ok := seen[ch]; ok {
+      errs = append(errs, fmt.Errorf("duplicate channel: %s", ch))
+      continue
+    }
+    seen[ch] = struct{}{}
+  }
 
   if len(i.Channels) > MaxChannelsPerRule {
     errs = append(errs, fmt.Errorf("too many channels: %d > %d", len(i.Channels), MaxChannelsPerRule))
   }
openmeter/notification/channel.go (2)

96-111: Validate webhook URL scheme and host.

Avoid accepting malformed/unsupported URLs; require http/https and a host.

Apply this diff:

 func (w WebHookChannelConfig) Validate() error {
   var errs []error
 
   if w.URL == "" {
     errs = append(errs, errors.New("missing URL"))
+  } else {
+    u, err := url.ParseRequestURI(w.URL)
+    if err != nil {
+      errs = append(errs, fmt.Errorf("invalid URL: %v", err))
+    } else {
+      if u.Scheme != "http" && u.Scheme != "https" {
+        errs = append(errs, errors.New("invalid URL scheme: must be http or https"))
+      }
+      if u.Host == "" {
+        errs = append(errs, errors.New("invalid URL: host is required"))
+      }
+    }
   }

256-259: Align CustomValidator generic parameter with the alias type.

Use DeleteChannelInput for consistency with DeleteRuleInput and clearer intent. Behavior is identical (type alias), but this improves readability.

Apply this diff:

 var (
   _ models.Validator                        = (*DeleteChannelInput)(nil)
-  _ models.CustomValidator[GetChannelInput] = (*DeleteChannelInput)(nil)
+  _ models.CustomValidator[DeleteChannelInput] = (*DeleteChannelInput)(nil)
 )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 867155d and 06127df.

📒 Files selected for processing (23)
  • openmeter/ent/db/notificationrule_create.go (1 hunks)
  • openmeter/ent/db/notificationrule_update.go (2 hunks)
  • openmeter/notification/adapter/event.go (1 hunks)
  • openmeter/notification/channel.go (9 hunks)
  • openmeter/notification/deliverystatus.go (6 hunks)
  • openmeter/notification/entitlements.go (5 hunks)
  • openmeter/notification/errors.go (0 hunks)
  • openmeter/notification/event.go (6 hunks)
  • openmeter/notification/eventpayload.go (2 hunks)
  • openmeter/notification/httpdriver/errors.go (1 hunks)
  • openmeter/notification/httpdriver/event.go (1 hunks)
  • openmeter/notification/httpdriver/mapping.go (8 hunks)
  • openmeter/notification/invoice.go (2 hunks)
  • openmeter/notification/rule.go (7 hunks)
  • openmeter/notification/service/channel.go (5 hunks)
  • openmeter/notification/service/deliverystatus.go (2 hunks)
  • openmeter/notification/service/event.go (2 hunks)
  • openmeter/notification/service/rule.go (5 hunks)
  • openmeter/notification/validator.go (0 hunks)
  • test/notification/channel.go (1 hunks)
  • test/notification/event.go (1 hunks)
  • test/notification/repository.go (1 hunks)
  • test/notification/rule.go (1 hunks)
💤 Files with no reviewable changes (2)
  • openmeter/notification/errors.go
  • openmeter/notification/validator.go
🧰 Additional context used
🧬 Code graph analysis (17)
openmeter/notification/eventpayload.go (2)
pkg/models/errors.go (1)
  • NewGenericValidationError (138-140)
pkg/models/validator.go (1)
  • Validate (16-26)
openmeter/ent/db/notificationrule_update.go (1)
pkg/models/validator.go (1)
  • Validate (16-26)
openmeter/notification/deliverystatus.go (3)
pkg/models/errors.go (2)
  • NewGenericValidationError (138-140)
  • NewNillableGenericValidationError (129-135)
pkg/models/validator.go (3)
  • CustomValidator (12-14)
  • ValidatorFunc (10-10)
  • Validate (16-26)
pkg/models/id.go (1)
  • NamespacedID (7-10)
test/notification/rule.go (2)
pkg/models/id.go (1)
  • NamespacedID (7-10)
openmeter/ent/db/notificationrule/where.go (4)
  • Namespace (70-72)
  • ID (15-17)
  • Name (90-92)
  • Disabled (95-97)
openmeter/notification/service/event.go (2)
pkg/models/validator.go (1)
  • Validate (16-26)
pkg/models/errors.go (1)
  • NewGenericValidationError (138-140)
openmeter/notification/invoice.go (1)
pkg/models/validator.go (3)
  • CustomValidator (12-14)
  • ValidatorFunc (10-10)
  • Validate (16-26)
openmeter/notification/service/rule.go (3)
pkg/models/validator.go (1)
  • Validate (16-26)
openmeter/notification/service/service.go (2)
  • Service (20-29)
  • Config (31-39)
openmeter/notification/entitlements.go (1)
  • ValidateRuleConfigWithFeatures (118-169)
test/notification/repository.go (1)
openmeter/notification/entitlements.go (3)
  • BalanceThreshold (68-68)
  • BalanceThresholdRuleConfig (77-82)
  • BalanceThresholdTypeNumber (59-59)
openmeter/notification/httpdriver/errors.go (3)
pkg/framework/transport/httptransport/encoder/encoder.go (1)
  • ErrorEncoder (14-14)
pkg/framework/commonhttp/errors.go (1)
  • HandleErrorIfTypeMatches (64-77)
pkg/models/errors.go (1)
  • GenericValidationError (145-147)
openmeter/notification/adapter/event.go (3)
pkg/sortx/order.go (1)
  • Order (3-3)
openmeter/ent/db/notificationevent/notificationevent.go (2)
  • ByCreatedAt (117-119)
  • ByID (107-109)
openmeter/notification/service.go (1)
  • OrderByID (10-10)
openmeter/notification/rule.go (3)
pkg/models/validator.go (3)
  • CustomValidator (12-14)
  • ValidatorFunc (10-10)
  • Validate (16-26)
pkg/models/errors.go (2)
  • NewNillableGenericValidationError (129-135)
  • NewGenericValidationError (138-140)
pkg/models/id.go (1)
  • NamespacedID (7-10)
openmeter/notification/service/channel.go (3)
openmeter/notification/channel.go (4)
  • ListChannelsInput (118-127)
  • CreateChannelInput (144-155)
  • Channel (37-51)
  • UpdateChannelInput (188-199)
pkg/models/id.go (1)
  • NamespacedID (7-10)
pkg/models/errors.go (1)
  • NewGenericValidationError (138-140)
openmeter/notification/event.go (3)
pkg/models/errors.go (2)
  • NewGenericValidationError (138-140)
  • NewNillableGenericValidationError (129-135)
pkg/models/validator.go (3)
  • CustomValidator (12-14)
  • ValidatorFunc (10-10)
  • Validate (16-26)
pkg/models/id.go (1)
  • NamespacedID (7-10)
test/notification/channel.go (1)
pkg/models/id.go (1)
  • NamespacedID (7-10)
openmeter/notification/httpdriver/mapping.go (4)
pkg/models/errors.go (1)
  • NewGenericValidationError (138-140)
openmeter/notification/channel.go (1)
  • UpdateChannelInput (188-199)
pkg/models/id.go (1)
  • NamespacedID (7-10)
openmeter/notification/rule.go (1)
  • UpdateRuleInput (224-237)
openmeter/notification/entitlements.go (4)
pkg/models/validator.go (3)
  • CustomValidator (12-14)
  • ValidatorFunc (10-10)
  • Validate (16-26)
pkg/models/errors.go (2)
  • NewNillableGenericValidationError (129-135)
  • NewGenericValidationError (138-140)
openmeter/notification/service.go (1)
  • FeatureService (51-53)
openmeter/notification/rule.go (1)
  • RuleConfig (98-107)
openmeter/notification/channel.go (4)
pkg/models/errors.go (2)
  • NewGenericValidationError (138-140)
  • NewNillableGenericValidationError (129-135)
openmeter/notification/webhook/secret.go (1)
  • ValidateSigningSecret (30-43)
pkg/models/validator.go (3)
  • CustomValidator (12-14)
  • ValidatorFunc (10-10)
  • Validate (16-26)
pkg/models/id.go (1)
  • NamespacedID (7-10)
⏰ 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). (8)
  • GitHub Check: Artifacts / Container image
  • GitHub Check: Test
  • GitHub Check: Code Generators
  • GitHub Check: Build
  • GitHub Check: Lint
  • GitHub Check: Migration Checks
  • GitHub Check: Repository Scan
  • GitHub Check: Analyze (go)
🔇 Additional comments (44)
openmeter/ent/db/notificationrule_create.go (1)

241-245: LGTM: Runtime validation added for config field.

The validation correctly calls Validate() on the config value and wraps any errors appropriately. This ensures invalid configurations are caught before database persistence.

test/notification/repository.go (1)

60-68: LGTM: Test data updated to match new BalanceThresholdRuleConfig structure.

The test correctly instantiates a BalanceThresholdRuleConfig with the required Features and Thresholds fields, aligning with the refactored validation interfaces.

openmeter/ent/db/notificationrule_update.go (1)

228-232: LGTM: Runtime validation added for config field on updates.

The validation mirrors the create-path logic and ensures config values are validated during updates as well. Consistent error handling.

openmeter/notification/httpdriver/errors.go (1)

19-19: LGTM: Error type updated to generic validation error.

The change from notification.ValidationError to *models.GenericValidationError aligns with the PR's objective to use a common validation error type across the notification package.

openmeter/notification/eventpayload.go (1)

46-68: LGTM: Validation errors migrated to generic validation error type.

All validation error returns correctly updated to use models.NewGenericValidationError, consistent with the PR's refactoring objectives.

openmeter/notification/service/event.go (4)

14-14: LGTM: Validation simplified to remove context and service parameters.

The updated validation signature aligns with the refactored validator interface that no longer requires context-dependent validation.


22-22: LGTM: Validation simplified to remove context and service parameters.


30-30: LGTM: Validation simplified to remove context and service parameters.


60-60: LGTM: Disabled rule error migrated to generic validation error.

The error correctly uses models.NewGenericValidationError for the disabled rule scenario, consistent with the refactoring approach.

openmeter/notification/service/deliverystatus.go (3)

12-12: LGTM: Validation simplified to remove context and service parameters.

The updated validation calls align with the refactored validator interface across all delivery status operations.


24-24: LGTM: Validation simplified to remove context and service parameters.


32-32: LGTM: Validation simplified to remove context and service parameters.

openmeter/notification/adapter/event.go (1)

88-94: I need to verify the original implementation and understand the impact on production callers.

Let me search the codebase more deeply to understand the impact:

Based on the script output analysis, I can now generate the rewritten review comment:

Verify the impact of default ordering change on reconciliation logic.

The refactor changes default event ordering from CreatedAt to ID when OrderBy is unspecified. Three production callers don't explicitly specify OrderBy and will be affected:

  1. openmeter/notification/eventhandler/reconcile.go:71 – fetches events for reconciliation without ordering specification
  2. openmeter/notification/consumer/entitlementreset.go:66 – fetches last event with pagination but no explicit ordering
  3. openmeter/notification/consumer/entitlementbalancethreshold.go:101 – fetches last event with pagination but no explicit ordering

Confirm whether ID-based ordering is semantically equivalent for these use cases (particularly for reconciliation, which may require consistent ordering for deterministic results). If CreatedAt ordering is required, update these callers to explicitly specify OrderBy: notification.OrderByCreatedAt.

test/notification/event.go (1)

248-250: API shape update to GetEventInput looks good

Switch to top-level Namespace/ID is correct and matches the broader refactor.

test/notification/rule.go (1)

210-216: UpdateRuleInput: move to NamespacedID is correct

The new identification pattern (Namespace + ID) is consistent with the refactor.

test/notification/channel.go (1)

105-112: UpdateChannelInput: NamespacedID migration looks good

The new identification shape is correct and aligns with production changes.

openmeter/notification/service/channel.go (3)

17-19: Good: input validation gates added

Validate() before service actions improves safety and error hygiene.

Also applies to: 25-27, 95-97, 148-150, 156-158


68-76: Good: internal UpdateChannelInput now uses NamespacedID

Consistent with public API and avoids mixed identification forms.


126-130: Good: use GenericValidationError for delete-guard

Wrapping the “assigned to rules” case as a validation error is appropriate.

openmeter/notification/httpdriver/event.go (1)

102-104: GetEvent: request construction update is correct

Using top-level Namespace/ID matches the new API.

openmeter/notification/service/rule.go (8)

17-19: Good shift to param self-validation

Using params.Validate() simplifies service logic and unifies error handling.


25-27: Create: param validation LGTM

Consistent "invalid params: %w" wrapping matches the new convention.


35-38: Config validation placement is correct; ensure type coherence

Validating with ValidateRuleConfigWithFeatures before create is good. Please confirm params.Type matches params.Config.Type (or that params.Validate() enforces it).


72-74: Delete: param validation LGTM


106-109: Get: param validation LGTM


115-117: Update: param validation LGTM


126-129: Update: config validation placement is good

Early feature check avoids wasted work later.


77-80: ****

DeleteRuleInput is a type alias to GetRuleInput (defined as type DeleteRuleInput = GetRuleInput), making them the same type at compile time. Go treats type aliases as fully interchangeable with their underlying type, so passing params of type DeleteRuleInput to GetRule which expects GetRuleInput is valid and will compile without errors. No type mismatch exists.

Likely an incorrect or invalid review comment.

openmeter/notification/invoice.go (1)

15-18: Validator conformance and no-op Validate are appropriate

Compile-time assertions and delegating ValidateWith to models.Validate look good; a nil Validate is fine for an empty config.

Also applies to: 22-24, 26-27

openmeter/notification/httpdriver/mapping.go (4)

22-23: Consistent validation error type

Returning models.NewGenericValidationError for invalid channel type aligns with the new error model.


115-118: Rules update mappers: NamespacedID adoption LGTM

Uniform use of NamespacedID for rule update inputs looks correct.

Also applies to: 157-160, 194-201, 231-236


278-279: Generic validation error for invalid rule type

Good consistency with the new validation model.


497-498: Generic validation error for invalid event payload type

Matches the overall error strategy.

openmeter/notification/deliverystatus.go (5)

35-36: Enum validation uses generic validation error

Good alignment with models.GenericValidationError.


63-66: Validator conformance for ListEventsDeliveryStatusInput

Assertions look correct.


89-107: List validation: aggregated errors

Collecting and joining errors with NewNillableGenericValidationError is correct.


125-141: Get validation: NamespacedID + aggregated checks

Clear messages and nil-able generic error are good.


163-183: Update validation: ID pair logic and state check

Validation is complete and consistent.

openmeter/notification/event.go (4)

26-29: Compile-time conformance for EventType

Good practice to assert fmt.Stringer and Validator.


37-43: EventType.Validate returns generic validation error

Matches new validation model.


100-118: ListEventsInput: error aggregation is clean

Time range and OrderBy checks look good.


127-145: GetEventInput as NamespacedID-derived type

Validation is straightforward; aggregated error pattern is consistent.

openmeter/notification/rule.go (1)

39-63: Aggregated validation pattern is solid.

Good switch to collecting field errors and returning a single models.GenericValidationError via NewNillableGenericValidationError. This improves UX and keeps validators composable.

openmeter/notification/entitlements.go (1)

88-116: Confirm valid bounds for percent thresholds.

Should percent-based thresholds be bounded (e.g., 0 < value ≤ 100)? If yes, add an upper-bound check to prevent >100.

Would you like me to add the guard once confirmed?

Comment thread openmeter/notification/deliverystatus.go
Comment thread openmeter/notification/entitlements.go
Comment thread openmeter/notification/httpdriver/mapping.go
@chrisgacsal
chrisgacsal merged commit 2b33566 into main Oct 24, 2025
28 checks passed
@chrisgacsal
chrisgacsal deleted the refactor/notification-validators branch October 24, 2025 19:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/ignore Ignore this change when generating release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants