Skip to content

feat: event-filtered webhooks, real admin metrics, consistent pagination (closes #192, #193, #196) - #263

Merged
cypriannwokolo2-creator merged 1 commit into
cocor-tech:masterfrom
YazarAyobami:feat/192-193-196-webhooks-metrics-pagination
Aug 26, 2026
Merged

feat: event-filtered webhooks, real admin metrics, consistent pagination (closes #192, #193, #196)#263
cypriannwokolo2-creator merged 1 commit into
cocor-tech:masterfrom
YazarAyobami:feat/192-193-196-webhooks-metrics-pagination

Conversation

@YazarAyobami

Copy link
Copy Markdown

Summary

Implements three Stellar Wave issues in one cohesive backend change:

Changes

#192 — Event-filtered webhook delivery

  • webhook/service.go
    • WebhookRegistration now carries Events, IsActive, LastDeliveryAt, and FailureCount; empty event list = subscribe to all events.
    • PostgresRepository was rewritten to match the actual webhooks schema (url, events, secret_hash, is_active, last_delivery_at, failure_count) — the previous queries referenced columns that don't exist in migration 010.
    • DispatchPayload(ctx, eventType, payload, maxRetries) now filters active webhooks by subscription (skipped webhooks get a skipped delivery log entry), retries failures with exponential backoff, updates last_delivery_at / failure_count, and logs each attempt to webhook_deliveries.
    • New repository methods: Delete, UpdateDeliveryOutcome, LogDelivery, ListDeliveries.
  • internal/database/migrations/037_create_webhook_deliveries.{up,down}.sql — new delivery-log table with indexes on (webhook_id, created_at) and (event_type, created_at).
  • internal/api/handler/webhook_handler.go — registrations now persist through the repository (with a generated HMAC secret) instead of an in-memory map; added GET /webhooks/:id/deliveries (owner-only, paginated) to inspect delivery history.
  • internal/api/router.go — registers the new deliveries route.

#193 — Real admin metrics

  • New internal/domain/admin package:
    • model.goMetrics (users, circles, contributions, payouts, active users, 30d new users, contribution/payout volume, total volume, 30d volume) and DailyVolumePoint.
    • repository.go / repository_pg.go — aggregate SQL: totals, active users (activity in the trailing window), volume from confirmed contributions + payouts, and a generate_series-backed per-day volume series.
    • service.goService with an in-memory TTL cache (default 60s) for the expensive aggregates; errors are never cached.
  • internal/api/handler/admin_handler.goGetMetrics serves the aggregates (flat response keeps legacy totalUsers/totalCircles/activeCircles/totalVolumeUSD keys), accepts an optional ?days= window (default 30, max 365).
  • cmd/api-server/main.go — wires the new service.

#196 — Consistent pagination metadata

  • pkg/response/response.goPaginationMeta and Pagination now include hasMore / has_more computed by NewPaginationMeta (page < totalPages).
  • internal/api/handler/circle_handler.go:
    • GetPayouts accepts page / page_size instead of a fixed 1, 50.
    • GetRounds accepts page / page_size (applied to the underlying contribution/payout history queries) and now returns meta; rounds are sorted by round number for a deterministic response.
  • All other list endpoints (contributions, payouts, notifications, circles, admin lists, communities) already used pagination.Parse + OKWithMeta, so they automatically inherit the new hasMore field.

Drive-by fix

  • internal/api/handler/swap_handler.go — the swap endpoints called response.Error(...) with the wrong signature and didn't compile against the current pkg/response; replaced with the standard response helpers so the repo builds cleanly.

Tests

  • webhook/service_test.go — event-type filtering (subscribed vs non-subscribed + skipped log), empty-subscription = all events, retry-until-success with attempt logging, retry exhaustion → failed log, network-error logging; fake repo made thread-safe for the async dispatcher.
  • internal/domain/admin/service_test.go — cache hit, cache expiry, per-window cache isolation, error propagation (never cached), default TTL.
  • internal/api/handler/admin_handler_test.goGetMetrics returns real aggregates (non-zero totalVolumeUSD).
  • internal/api/handler/circle_pagination_test.goGetPayouts / GetRounds honor client page/page_size and return hasMore metadata.
  • pkg/response/response_test.go — asserts hasMore/has_more presence and edge cases.
  • tests/integration/api_routes_test.go — webhook routes now run against an in-memory repo implementing the expanded interface.

Verification

  • go build ./... — passes.
  • go vet ./... — passes.
  • go test ./... -count=1 — all tests pass. (Note: TestConstantTimeCompareTiming is a pre-existing timing flake that only fails under -race, unchanged by this PR.)

Notes

  • Volumes are summed in the circle's native currency units (USDC/XLM); totalVolumeUSD is the sum of contribution + payout volume until per-currency conversion rates are wired in (documented in internal/domain/admin/model.go).
  • The secret_hash column continues to store the raw HMAC secret so incoming signature verification (POST /webhooks/incoming/:id) keeps working (existing convention, now documented in scanWebhook).

- cocor-tech#192: webhook dispatcher now filters deliveries per webhook event-type
  subscription, retries with exponential backoff, and records every attempt
  (delivered/failed/skipped) in a new webhook_deliveries table exposed via
  GET /webhooks/:id/deliveries. Registration persists to Postgres with the
  actual webhooks schema (url/events/secret_hash/is_active).
- cocor-tech#193: /admin/metrics returns real aggregates (users, circles, contributions,
  payouts, active users, 30d new users, volume) plus a time-bucketed daily
  volume series, computed by a new internal/domain/admin package and cached
  in-memory for 60s.
- cocor-tech#196: standard pagination metadata now includes hasMore (meta.hasMore /
  pagination.has_more); circle GetRounds/GetPayouts accept client page and
  page_size params and return consistent meta.
- Drive-by: fix swap handler compile errors against the response package so
  the repo builds.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@drips-wave

drips-wave Bot commented Aug 25, 2026

Copy link
Copy Markdown

@YazarAyobami Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants