Skip to content

feat: enhance notification system and migrate to pubsub/v2 to fix CI#642

Open
nothing0012 wants to merge 3 commits intomainfrom
zz/add-notif-1
Open

feat: enhance notification system and migrate to pubsub/v2 to fix CI#642
nothing0012 wants to merge 3 commits intomainfrom
zz/add-notif-1

Conversation

@nothing0012
Copy link
Copy Markdown
Contributor

@nothing0012 nothing0012 commented Dec 24, 2025

Summary

Adds a notification webhook system to Flagr that fires on every flag CRUD operation. A single generic webhook provider sends a structured JSON payload to a configurable URL — simple, extensible, and dependency-light.

Changes

New: pkg/notification/

File Purpose
notifier.go Notifier interface, SendNotification(), MockNotifier, Notification struct
dispatch.go Async dispatch with semaphore (max 100), panic recovery, statsd metrics
webhook.go Generic HTTP POST webhook with custom headers, retry, io.LimitReader safety
retry.go Exponential backoff with jitter for 5xx errors
validate.go Startup config validation with warning logs

Modified

File What
pkg/entity/flag_snapshot.go SaveFlagSnapshot fires SendNotification after commit
pkg/handler/crud.go Every CRUD op passes operation + component info (flag/segment/variant/constraint/distribution/tag)
pkg/config/env.go 7 new env vars (webhook URL, timeout, retry, diff toggle)
pkg/util/util.go ParseHeaders() for webhook custom headers

Dependencies

  • pmezard/go-difflib — for CalculateDiff() (unified diff of flag snapshots)

Webhook Payload

{
  "operation": "update",
  "flag_id": 123,
  "flag_key": "my-feature-flag",
  "component_type": "segment",
  "component_id": 7,
  "component_key": "power-users",
  "pre_value": "...",
  "post_value": "...",
  "diff": "--- Previous\n+++ Current\n@@ ...",
  "user": "admin@example.com",
  "timestamp": "2026-04-26T18:51:03Z"
}

pre_value, post_value, diff only appear when FLAGR_NOTIFICATION_DETAILED_DIFF_ENABLED=true

Configuration

Env var Default Description
FLAGR_NOTIFICATION_WEBHOOK_ENABLED false Enable webhook notifications
FLAGR_NOTIFICATION_WEBHOOK_URL Target URL for POST
FLAGR_NOTIFICATION_WEBHOOK_HEADERS Custom headers (Key: Val, Key: Val)
FLAGR_NOTIFICATION_TIMEOUT 10s Per-notification context timeout
FLAGR_NOTIFICATION_DETAILED_DIFF_ENABLED false Include pre/post value and unified diff
FLAGR_NOTIFICATION_MAX_RETRIES 3 Retry attempts for 5xx errors
FLAGR_NOTIFICATION_RETRY_BASE 1s Base delay for backoff with jitter
FLAGR_NOTIFICATION_RETRY_MAX 10s Max delay between retries

Design Decisions

  • Webhook-only — no Slack/Email providers. The webhook is the most general transport; users can point it at Slack incoming webhooks, SendGrid APIs, or any custom service. Adding providers later requires only implementing the Notifier interface.
  • snake_case JSON with omitempty on optional fields
  • flag_id/flag_key not entity_* — avoids confusion with Flagr's evaluation entity concept
  • component_type/component_id/component_key — tracks which sub-component was modified
  • No description — the flag's static description isn't useful context for a change event; operation + component + diff already tell the full story
  • Privacy-by-default — diffs disabled unless explicitly opted in
  • Async, non-blocking — notifications fire in background goroutines, failures never affect API responses
  • Context timeout per notification, no redundant HTTP client timeout
  • Jitter on exponential backoff to avoid thundering herds

Tests

27/27 passing across pkg/notification, pkg/handler, pkg/entity — covering dispatch, retry (with body leak tracking), webhook payload format, concurrency safety, and handler integration.

Types of changes

  • New feature
  • Documentation

@nothing0012 nothing0012 changed the title feat: enhance notification system with rich details and opt-in diffs feat: enhance notification system and migrate to pubsub/v2 to fix CI Dec 24, 2025
…rations

- Add generic HTTP webhook notifier with retry, jitter, and config validation
- Fire notifications on all flag CRUD operations (create/update/delete/restore)
- Track component changes (flag/segment/variant/constraint/distribution/tag)
- Async dispatch with semaphore limiting (max 100 concurrent)
- Privacy-by-default: diffs disabled unless FLAGR_NOTIFICATION_DETAILED_DIFF_ENABLED=true
- Payload: operation, flag_id, flag_key, component_type, component_id, component_key,
  pre_value, post_value, diff, user, timestamp (snake_case JSON with omitempty)
- Statsd metrics (notification.sent) tagged by provider, operation, and status
- Docs: flagr_notifications.md with payload spec and env var reference
zhouzhuojie and others added 2 commits April 26, 2026 19:46
…ants

Add ComponentType type with constants (ComponentFlag, ComponentSegment,
ComponentVariant, ComponentConstraint, ComponentDistribution, ComponentTag)
to replace magic string literals across handler call sites.
…te events

Sub-resource handlers (CreateTag, CreateSegment, CreateConstraint,
CreateVariant) were using OperationUpdate instead of OperationCreate.
Similarly, DeleteTag, DeleteSegment, DeleteConstraint, DeleteVariant
were using OperationUpdate instead of OperationDelete. Fix them so
the operation field accurately reflects what happened to the component.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants