Skip to content

Latest commit

 

History

History
145 lines (116 loc) · 4.56 KB

File metadata and controls

145 lines (116 loc) · 4.56 KB

callbacks

Tier: Adapter · Status: Full · Java original: firefly-callbacks · .NET project: FireflyFramework.Callbacks.{Interfaces,Models,Core,Web,Sdk}

Overview

callbacks is the framework's outbound webhook subsystem. Services publish business events; the dispatcher signs each payload with HMAC-SHA256, retries with exponential backoff, and records every attempt to a pluggable Store for audit. A REST admin endpoint manages targets; an SDK type-safely calls the admin endpoint from upstream services.

Sub-packages mirror the .NET project split:

Sub-package What it provides
callbacks/interfaces DTOs (Target, CallbackEvent, Attempt) + Store, Dispatcher ports
callbacks/models In-memory MemoryStore implementing Store
callbacks/core HMAC-signing Dispatcher with retry, audit-log recording
callbacks/web REST admin handler (CRUD targets, list attempts)
callbacks/sdk Typed client for the admin REST API

Wire format

POST <target.URL> with body == event.Payload, plus headers:

Header Value
Content-Type application/json
X-Firefly-Event event.Type
X-Firefly-Event-Id event.ID
X-Firefly-Timestamp Unix seconds when the request was sent
X-Firefly-Signature sha256=<hmac-hex> keyed on target.Secret
X-Correlation-Id When event.CorrelationID is set
(custom) Anything from target.Headers

Retry policy

core.Config{MaxAttempts, InitialDelay} — defaults: 3 attempts, 200 ms initial delay, doubling. Each attempt records an Attempt audit row regardless of outcome.

Public surface

interfaces

type Target struct {
    ID, URL, Secret string
    EventTypes      []string
    Headers         map[string]string
    Active          bool
    CreatedAt       time.Time
}

type CallbackEvent struct {
    ID, Type      string
    Payload       []byte
    Headers       map[string]string
    CorrelationID string
}

type Attempt struct {
    ID, EventID, TargetID string
    Status                int
    Body, Error           string
    Attempt               int
    StartedAt, FinishedAt time.Time
}

type Store interface {
    UpsertTarget(ctx, Target) (Target, error)
    GetTarget(ctx, id) (Target, error)
    ListTargets(ctx) ([]Target, error)
    DeleteTarget(ctx, id) error
    RecordAttempt(ctx, Attempt) error
    ListAttempts(ctx, eventID) ([]Attempt, error)
}

type Dispatcher interface { Dispatch(ctx, CallbackEvent) error }

core

type Config struct {
    HTTPClient   *http.Client
    MaxAttempts  int
    InitialDelay time.Duration
    Now          func() time.Time
}
func Default() Config
func NewDispatcher(store cbi.Store, cfg Config) *Dispatcher

web

func Handler(store cbi.Store) http.Handler
// Routes:
//   GET    /callbacks/targets
//   POST   /callbacks/targets         (upsert)
//   GET    /callbacks/targets/{id}
//   DELETE /callbacks/targets/{id}
//   GET    /callbacks/attempts/{eventId}

sdk

type Client struct{ ... }
func New(baseURL string) *Client
func (*Client) Targets(ctx) ([]cbi.Target, error)
func (*Client) Upsert(ctx, cbi.Target) (cbi.Target, error)
func (*Client) Delete(ctx, id) error

Quick start

store := cbm.NewMemoryStore()
_, _ = store.UpsertTarget(ctx, cbi.Target{
    ID: "customers", URL: "https://customer.example.com/cb",
    Secret: "shared-secret", Active: true,
    EventTypes: []string{"order.placed", "order.shipped"},
})

d := core.NewDispatcher(store, core.Default())
_ = d.Dispatch(ctx, cbi.CallbackEvent{
    ID: uuid.New().String(),
    Type: "order.placed",
    Payload: []byte(`{"id":"o1","customer":"alice"}`),
})

Testing

cd callbacks
go test ./...

Covers HMAC signing, 5xx retry behaviour, event-type filtering, audit trail recording, and the REST admin CRUD.