Documentation
¶
Overview ¶
Package http is the HTTP adapter — the chi-based router that fronts the application use cases. It owns three responsibilities:
- Compose the fixed middleware chain (recover → correlation ID → request log → metrics → inbound rate limit → idempotency → handler) in the order mandated by CLAUDE.md §3 / §10.
- Mount the HTTP handlers that translate REST calls into use case invocations and translate domain errors into RFC 7807 problem responses.
- Mount auxiliary endpoints — health, /metrics, Swagger UI, the WebSocket upgrade endpoint.
The package name collides with stdlib net/http; callers import this package aliased as httpadapter and the stdlib as nethttp when both are needed in the same file. Inside this package the stdlib is aliased; chi handlers themselves are stdlib-compatible.
Index ¶
- Variables
- func ContextWithCorrelationID(parent context.Context, id string) context.Context
- func CorrelationIDFromContext(ctx context.Context) string
- func CorrelationIDMiddleware(gen ports.IDGenerator) func(nethttp.Handler) nethttp.Handler
- func IdempotencyMiddleware(store ports.IdempotencyStore) func(nethttp.Handler) nethttp.Handler
- func InboundRateLimitMiddleware(limiter ports.RateLimiter) func(nethttp.Handler) nethttp.Handler
- func MetricsMiddleware(m *metrics.Metrics) func(nethttp.Handler) nethttp.Handler
- func MountDocs(r chi.Router)
- func NewRouter(cfg Config) *chi.Mux
- func RespondWithError(w nethttp.ResponseWriter, r *nethttp.Request, err error)
- func WriteError(w nethttp.ResponseWriter, r *nethttp.Request, err error)
- func WriteProblem(w nethttp.ResponseWriter, r *nethttp.Request, p Problem)
- type CancelNotificationExecutor
- type Config
- type CreateBatchExecutor
- type CreateNotificationExecutor
- type CreateTemplateExecutor
- type DeleteTemplateExecutor
- type GetBatchExecutor
- type GetNotificationExecutor
- type GetNotificationTraceExecutor
- type GetTemplateExecutor
- type JSONMetricsProvider
- type JSONMetricsSnapshot
- type ListNotificationsExecutor
- type ListTemplatesExecutor
- type Problem
- type ReadinessCheck
- type ReplaceTemplateExecutor
- type Server
- func (s *Server) CancelNotification(ctx context.Context, req api.CancelNotificationRequestObject) (api.CancelNotificationResponseObject, error)
- func (s *Server) CreateBatch(ctx context.Context, req api.CreateBatchRequestObject) (api.CreateBatchResponseObject, error)
- func (s *Server) CreateNotification(ctx context.Context, req api.CreateNotificationRequestObject) (api.CreateNotificationResponseObject, error)
- func (s *Server) CreateTemplate(ctx context.Context, req api.CreateTemplateRequestObject) (api.CreateTemplateResponseObject, error)
- func (s *Server) DeleteTemplate(ctx context.Context, req api.DeleteTemplateRequestObject) (api.DeleteTemplateResponseObject, error)
- func (s *Server) GetBatch(ctx context.Context, req api.GetBatchRequestObject) (api.GetBatchResponseObject, error)
- func (s *Server) GetHealthzLive(_ context.Context, _ api.GetHealthzLiveRequestObject) (api.GetHealthzLiveResponseObject, error)
- func (s *Server) GetHealthzReady(ctx context.Context, _ api.GetHealthzReadyRequestObject) (api.GetHealthzReadyResponseObject, error)
- func (s *Server) GetJSONMetrics(ctx context.Context, _ api.GetJSONMetricsRequestObject) (api.GetJSONMetricsResponseObject, error)
- func (s *Server) GetNotification(ctx context.Context, req api.GetNotificationRequestObject) (api.GetNotificationResponseObject, error)
- func (s *Server) GetNotificationTrace(ctx context.Context, req api.GetNotificationTraceRequestObject) (api.GetNotificationTraceResponseObject, error)
- func (s *Server) GetPrometheusMetrics(_ context.Context, _ api.GetPrometheusMetricsRequestObject) (api.GetPrometheusMetricsResponseObject, error)
- func (s *Server) GetTemplate(ctx context.Context, req api.GetTemplateRequestObject) (api.GetTemplateResponseObject, error)
- func (s *Server) ListNotifications(ctx context.Context, req api.ListNotificationsRequestObject) (api.ListNotificationsResponseObject, error)
- func (s *Server) ListTemplates(ctx context.Context, req api.ListTemplatesRequestObject) (api.ListTemplatesResponseObject, error)
- func (s *Server) ReplaceTemplate(ctx context.Context, req api.ReplaceTemplateRequestObject) (api.ReplaceTemplateResponseObject, error)
- type ServerOptions
- type WebSocketHandler
Constants ¶
This section is empty.
Variables ¶
var ErrNotImplemented = errors.New("not implemented")
ErrNotImplemented is the sentinel an operation returns when no concrete handler has been wired to it yet. The strict-server error handler maps it to a 501 RFC 7807 problem response. Phase 4 tasks override one operation each in the Server struct; until then the embedded unimplementedServer returns this error.
Functions ¶
func ContextWithCorrelationID ¶
ContextWithCorrelationID is the matching re-export of correlation.WithContext.
func CorrelationIDFromContext ¶
CorrelationIDFromContext is a thin re-export of correlation.FromContext kept for backward compatibility with existing call sites in this adapter. New code should reach for the infrastructure package directly.
func CorrelationIDMiddleware ¶
CorrelationIDMiddleware reads X-Correlation-ID from the inbound request, generates a new id via gen when the header is missing or blank, stashes the resolved id in the request context, and echoes it in the response header. Downstream handlers retrieve it via CorrelationIDFromContext.
The middleware deliberately takes the full ports.IDGenerator rather than a narrower factory function — the same generator is wired into the application use cases (CreateNotification, ProcessNotification, ...) so the http adapter shares it instead of constructing a duplicate.
func IdempotencyMiddleware ¶
IdempotencyMiddleware caches the response of write requests keyed by the client-supplied Idempotency-Key header so a duplicate request returns the original response without re-running the handler (CLAUDE.md §3.9).
Semantics:
- No header → pass through; the store is never touched.
- Header + cache hit → return cached body and Content-Type with status 200 (intentionally collapses 201/202 to 200 so the client can distinguish "you saw this before" from "we just created this").
- Header + cache miss → run the handler with a capturing writer. 2xx responses are cached with a 24h ttl; non-2xx are not cached because the result may be transient (5xx) or the client may want to retry with corrections (4xx).
- Store Get error → fail-open (run the handler). Availability beats strict enforcement when Redis hiccups.
- Store Set error → log and continue; the client still gets the handler's response. The cache will be re-populated on the next attempt.
func InboundRateLimitMiddleware ¶
InboundRateLimitMiddleware throttles inbound HTTP traffic at a fixed budget of 60 requests/minute per client IP (CLAUDE.md §2.6 / §10). The middleware delegates the counting itself to ports.RateLimiter so production wires the Redis-backed implementation while tests inject a deterministic fake.
Bucket key shape: "ip:<addr>". The "ip:" prefix is the inbound namespace separator — outbound (per channel) uses "channel:<name>" against the same RateLimiter and must not collide (CLAUDE.md §2.6).
Failure policy: when the limiter returns an error (Redis unreachable etc.) the middleware logs and lets the request through (fail-open). Availability is more valuable than strict enforcement at the inbound edge — a brief abuse window beats taking the whole API offline when Redis hiccups.
func MetricsMiddleware ¶
MetricsMiddleware records one http_requests_total increment and one http_request_duration_seconds observation per request (CLAUDE.md §12.1). The label set uses the chi route PATTERN (not URL.Path) so high-cardinality path params like /notifications/{id} collapse into a single series.
Unmatched requests fall into a fixed "unknown" pattern so a script spraying random paths cannot blow up the cardinality.
func MountDocs ¶
MountDocs registers the documentation handlers on the supplied router:
- GET /docs and /docs/ → Swagger UI HTML shell.
- GET /docs/openapi.yaml → the embedded spec as text/yaml.
Mounted outside the strict-server because OpenAPI 3.0 does not describe its own documentation surface — the page is a static asset in spirit, even though one byte of it is the spec embedded at build.
func NewRouter ¶
NewRouter returns a chi.Mux with Recoverer wired first and the configured middlewares appended in order. Callers register routes on the returned mux via Get/Post/Mount/Route — chi's behavior is unchanged, this constructor only fixes the chain order.
Recoverer is always present so a panic in any handler degrades to a 500 instead of taking down the API process — the worker fleet and reconciler continue running.
func RespondWithError ¶
func RespondWithError(w nethttp.ResponseWriter, r *nethttp.Request, err error)
RespondWithError is the unified strict-server error handler — wire it into BOTH StrictHTTPServerOptions.RequestErrorHandlerFunc (body decode failures) and ResponseErrorHandlerFunc (handler errors). It bridges the strict-server world to the project's RFC 7807 translator (problem.go) so every operation gets uniform error semantics — adding a new domain error never requires touching this function, only the errorMappings table in problem.go.
Special cases handled inline rather than via errorMappings:
- ErrNotImplemented → 501. Purely an http-adapter concern (the domain has no concept of "operation not wired yet"); keeping it here preserves the layering.
- JSON decode errors (*json.SyntaxError, *json.UnmarshalTypeError, io.ErrUnexpectedEOF) → 400. The strict-server wraps these and passes them through RequestErrorHandlerFunc; a 400 with a generic Detail is the correct semantic response.
func WriteError ¶
func WriteError(w nethttp.ResponseWriter, r *nethttp.Request, err error)
WriteError is the single translation function CLAUDE.md §3.5 mandates: it maps a domain or port error to the appropriate RFC 7807 problem response. Adding a new domain error means adding one branch to errorMappings — the test catalog in problem_test.go is the spec.
Calling WriteError with a nil error is a deliberate no-op so handlers that defer a single `if err != nil { WriteError(...) }` check don't have to guard the call site too.
func WriteProblem ¶
func WriteProblem(w nethttp.ResponseWriter, r *nethttp.Request, p Problem)
WriteProblem serializes p as application/problem+json and writes it to w with the status carried in p. The request is used to fill in fields the caller didn't set explicitly: Instance defaults to the request URL path, CorrelationID defaults to the value in r.Context() (zero when the middleware has not run).
Type defaults to "about:blank" per RFC 7807 §3.1 — a Problem without a more specific type still validates against the spec.
Types ¶
type CancelNotificationExecutor ¶
type CancelNotificationExecutor func(ctx context.Context, in application.CancelNotificationInput) (*domain.Notification, error)
CancelNotificationExecutor is the slim contract for PATCH /api/v1/notifications/{id}/cancel. Production wires (*application.CancelNotification).Execute; tests pass a closure.
type Config ¶
type Config struct {
// Middlewares is the ordered list applied after Recoverer. Each
// entry has the standard func(http.Handler) http.Handler shape so
// it can come from chi/middleware, this package, or a third-party.
Middlewares []func(nethttp.Handler) nethttp.Handler
}
Config wires the optional middlewares the chain composes after the always-on Recoverer. The Middlewares slice is applied in order, so the caller (cmd/api) is responsible for preserving the canonical chain:
recover (always first, wired by NewRouter) → correlation ID → request log → metrics → inbound rate limit → idempotency → handler
nil entries are silently skipped — tests and reduced-stack composes pass a partial chain without panicking. Tasks 2-4 in phase 4 add the concrete middleware constructors that fill the slots.
type CreateBatchExecutor ¶
type CreateBatchExecutor func(ctx context.Context, in application.CreateBatchInput) (*domain.Batch, error)
CreateBatchExecutor is the slim contract for POST /api/v1/notifications/batch. Production wires (*application.CreateBatch).Execute; tests pass a closure.
type CreateNotificationExecutor ¶
type CreateNotificationExecutor func(ctx context.Context, in application.CreateNotificationInput) (*domain.Notification, error)
CreateNotificationExecutor is the slim function-type contract the CreateNotification handler depends on. Mirrors the pattern used by the asynq processor: the production wiring (cmd/api) passes (*application.CreateNotification).Execute, tests pass a closure that records inputs and returns a controlled outcome.
type CreateTemplateExecutor ¶
type CreateTemplateExecutor func(ctx context.Context, in application.CreateTemplateInput) (*domain.Template, error)
CreateTemplateExecutor is the slim contract for POST /api/v1/templates.
type DeleteTemplateExecutor ¶
type DeleteTemplateExecutor func(ctx context.Context, in application.DeleteTemplateInput) error
DeleteTemplateExecutor is the slim contract for DELETE /api/v1/templates/{id}.
type GetBatchExecutor ¶
type GetBatchExecutor func(ctx context.Context, in application.GetBatchInput) (*domain.Batch, error)
GetBatchExecutor is the slim contract for GET /api/v1/notifications/batch/{id}. Production wires (*application.GetBatch).Execute; tests pass a closure.
type GetNotificationExecutor ¶
type GetNotificationExecutor func(ctx context.Context, in application.GetNotificationInput) (*domain.Notification, error)
GetNotificationExecutor is the slim contract for GET /api/v1/notifications/{id}. Production wires (*application.GetNotification).Execute; tests pass a closure.
type GetNotificationTraceExecutor ¶
type GetNotificationTraceExecutor func(ctx context.Context, in application.GetNotificationTraceInput) ([]*domain.NotificationLog, error)
GetNotificationTraceExecutor is the slim contract for GET /api/v1/notifications/{id}/trace. Production wires (*application.GetNotificationTrace).Execute; tests pass a closure.
type GetTemplateExecutor ¶
type GetTemplateExecutor func(ctx context.Context, in application.GetTemplateInput) (*domain.Template, error)
GetTemplateExecutor is the slim contract for GET /api/v1/templates/{id}.
type JSONMetricsProvider ¶
type JSONMetricsProvider func(ctx context.Context) (JSONMetricsSnapshot, error)
JSONMetricsProvider returns one snapshot per scrape. Production wires a Prometheus-querying provider (or an inline counter aggregation); tests inject a closure.
type JSONMetricsSnapshot ¶
type JSONMetricsSnapshot struct {
CreatedPerMinute int
DeliveredPerMinute int
FailedPerMinute int
QueueDepth int
SuccessRate *float64
}
JSONMetricsSnapshot is the small, JSON-friendly subset of the Prometheus exposition data that /api/v1/metrics returns. Used by clients that cannot consume Prometheus text format (dashboards, ad-hoc scripts). SuccessRate is a pointer so the provider can explicitly signal "no data this window" instead of an ambiguous 0.
type ListNotificationsExecutor ¶
type ListNotificationsExecutor func(ctx context.Context, in application.ListNotificationsInput) (application.ListNotificationsOutput, error)
ListNotificationsExecutor is the slim contract for GET /api/v1/notifications. Production wires (*application.ListNotifications).Execute; tests pass a closure.
type ListTemplatesExecutor ¶
type ListTemplatesExecutor func(ctx context.Context, in application.ListTemplatesInput) (application.ListTemplatesOutput, error)
ListTemplatesExecutor is the slim contract for GET /api/v1/templates.
type Problem ¶
type Problem struct {
Type string `json:"type"`
Title string `json:"title"`
Status int `json:"status"`
Detail string `json:"detail,omitempty"`
Instance string `json:"instance,omitempty"`
CorrelationID string `json:"correlation_id,omitempty"`
}
Problem is the on-wire shape of an RFC 7807 Problem Details object (CLAUDE.md §3.5). Title and Status are required; Type defaults to "about:blank" per RFC 7807 §3.1 when omitted; Detail and Instance are optional. CorrelationID is a non-standard but commonly-added member (CLAUDE.md §10 example).
type ReadinessCheck ¶
ReadinessCheck verifies that one downstream dependency is reachable. /healthz/ready invokes every configured check in turn; any error flips the response to 503. Production wires one check per critical dependency (Postgres ping, Redis ping); tests inject closures.
type ReplaceTemplateExecutor ¶
type ReplaceTemplateExecutor func(ctx context.Context, in application.ReplaceTemplateInput) (*domain.Template, error)
ReplaceTemplateExecutor is the slim contract for PUT /api/v1/templates/{id}.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the adapter that implements api.StrictServerInterface by dispatching each operation to its application use case. Every handler method begins with a nil-check on its executor and returns ErrNotImplemented (→ 501) when absent — tests wire only the slots they exercise.
Concurrency: Server holds only constructor-injected references; it is safe to share across goroutines once NewServer returns.
func NewServer ¶
func NewServer(opts ServerOptions) *Server
NewServer wires the executors carried by opts into a Server. Any operation whose executor is nil falls through to a 501 response — the nil-check lives in each handler method.
func (*Server) CancelNotification ¶
func (s *Server) CancelNotification(ctx context.Context, req api.CancelNotificationRequestObject) (api.CancelNotificationResponseObject, error)
CancelNotification overrides the embedded stub and dispatches to the wired executor. It is the operation behind `PATCH /api/v1/notifications/{id}/cancel`.
Notable error semantics handled entirely by RespondWithError:
- ports.ErrNotFound → 404 (id does not exist).
- *domain.TransitionError → 409 (already delivered/failed/cancelled). The detail string carries the offending from→to pair so support can immediately tell what state the notification was already in.
func (*Server) CreateBatch ¶
func (s *Server) CreateBatch(ctx context.Context, req api.CreateBatchRequestObject) (api.CreateBatchResponseObject, error)
CreateBatch overrides the embedded unimplementedServer stub and dispatches to the wired CreateBatch executor. It is the operation behind `POST /api/v1/notifications/batch`.
The response intentionally omits the member notifications — the openapi.yaml description spells out that POST 202 keeps the payload small and the caller fetches members later via `GET /api/v1/notifications/batch/{id}`. Returning hundreds of full Notification structs on every create would dwarf the actual signal (batch id and size) the client needs in the synchronous reply.
func (*Server) CreateNotification ¶
func (s *Server) CreateNotification(ctx context.Context, req api.CreateNotificationRequestObject) (api.CreateNotificationResponseObject, error)
CreateNotification overrides the embedded unimplementedServer stub and dispatches to the wired executor. It is the operation behind `POST /api/v1/notifications`.
Flow:
- Reject an absent body — the strict-server already enforces Content-Type=application/json and JSON-decodes it, but the body pointer is nil when the caller sends an empty request. Treat that as a domain.ValidationError so the error handler emits a 400 problem.
- Map the wire-level request and params into the use case input and invoke the executor. Validation errors from the domain (ErrInvalidChannel, etc.) propagate as-is; the error handler dispatches them via WriteError.
- On success, convert the domain.Notification to its wire shape, compute the Location URI, and return a typed 202 response.
If the embedded executor is nil (no override wired) we still need a usable signal — return ErrNotImplemented so RespondWithError emits a 501 just like the unimplementedServer stub does for the other operations.
func (*Server) CreateTemplate ¶
func (s *Server) CreateTemplate(ctx context.Context, req api.CreateTemplateRequestObject) (api.CreateTemplateResponseObject, error)
CreateTemplate creates a new template and returns 201 with the new resource and a Location header.
func (*Server) DeleteTemplate ¶
func (s *Server) DeleteTemplate(ctx context.Context, req api.DeleteTemplateRequestObject) (api.DeleteTemplateResponseObject, error)
DeleteTemplate removes a template. Returns 204 on success, 404 when the id is unknown.
func (*Server) GetBatch ¶
func (s *Server) GetBatch(ctx context.Context, req api.GetBatchRequestObject) (api.GetBatchResponseObject, error)
GetBatch overrides the embedded stub and dispatches to the wired executor. It is the operation behind `GET /api/v1/notifications/batch/{id}`.
In contrast to POST 202 (which omits the member notifications to keep the synchronous reply small), GET inlines them — the client has explicitly asked for the full picture.
func (*Server) GetHealthzLive ¶
func (s *Server) GetHealthzLive(_ context.Context, _ api.GetHealthzLiveRequestObject) (api.GetHealthzLiveResponseObject, error)
GetHealthzLive is the liveness probe. It always returns 200 — its only job is to confirm the process is running and the HTTP server is accepting requests. Kubernetes uses this to decide whether to restart the pod; including downstream checks would cause a Redis hiccup to restart every API replica simultaneously.
func (*Server) GetHealthzReady ¶
func (s *Server) GetHealthzReady(ctx context.Context, _ api.GetHealthzReadyRequestObject) (api.GetHealthzReadyResponseObject, error)
GetHealthzReady runs every configured ReadinessCheck and reports ready only when all of them succeed. A single failure flips the response to 503 — Kubernetes stops routing traffic but the pod stays alive so the dependency can recover.
The check loop returns on the first failure (no need to keep probing — the verdict is already 503). The failed check's error is logged with structured fields so operators can correlate the 503 with the offending dependency.
func (*Server) GetJSONMetrics ¶
func (s *Server) GetJSONMetrics(ctx context.Context, _ api.GetJSONMetricsRequestObject) (api.GetJSONMetricsResponseObject, error)
GetJSONMetrics renders /api/v1/metrics. The endpoint exists for clients that cannot speak the Prometheus text exposition format (dashboards, ad-hoc scripts, smoke tests) — the data is the same data /metrics exposes, just JSON-shaped and trimmed to the most useful counters.
SuccessRate is intentionally a pointer in the snapshot: explicit `nil` means "no traffic in the window, no rate to report" so the API does not lie about the success of zero deliveries.
func (*Server) GetNotification ¶
func (s *Server) GetNotification(ctx context.Context, req api.GetNotificationRequestObject) (api.GetNotificationResponseObject, error)
GetNotification overrides the embedded unimplementedServer stub and dispatches to the wired GetNotification executor. It is the operation behind `GET /api/v1/notifications/{id}`.
ports.ErrNotFound returned by the use case flows through RespondWithError and is translated to a 404 problem — no special handling here. The handler only ferries the parsed path id into the use case and the resulting domain.Notification back out.
func (*Server) GetNotificationTrace ¶
func (s *Server) GetNotificationTrace(ctx context.Context, req api.GetNotificationTraceRequestObject) (api.GetNotificationTraceResponseObject, error)
GetNotificationTrace overrides the embedded stub and dispatches to the wired executor. It is the operation behind `GET /api/v1/notifications/{id}/trace`.
The use case verifies the notification exists (so an unknown id produces 404 instead of an empty 200) — no special handling here. The handler maps each domain.NotificationLog into its wire shape and packages them under the notification_id top-level field.
func (*Server) GetPrometheusMetrics ¶
func (s *Server) GetPrometheusMetrics(_ context.Context, _ api.GetPrometheusMetricsRequestObject) (api.GetPrometheusMetricsResponseObject, error)
GetPrometheusMetrics overrides the embedded stub and renders the registered metric set in the Prometheus exposition format. The handler is wired through the strict-server (not promhttp.Handler mounted on a sibling route) so the same chain — request id, log, metrics middleware — wraps it as every other endpoint.
Encoding cost: every scrape rebuilds the response body. Production scrape intervals are 10-60s; the gather + encode for a few hundred metrics is dominated by network IO, so the simplicity is the right trade.
func (*Server) GetTemplate ¶
func (s *Server) GetTemplate(ctx context.Context, req api.GetTemplateRequestObject) (api.GetTemplateResponseObject, error)
GetTemplate returns a single template by id, or a 404 problem when the id is unknown.
func (*Server) ListNotifications ¶
func (s *Server) ListNotifications(ctx context.Context, req api.ListNotificationsRequestObject) (api.ListNotificationsResponseObject, error)
ListNotifications overrides the embedded stub and dispatches to the wired executor. It is the operation behind `GET /api/v1/notifications`.
The use case owns:
- String parsing of status and channel (an invalid value surfaces domain.ErrInvalidStatus / ErrInvalidChannel which the translator turns into a 400).
- Limit clamping (defaults and ceilings are policy, not transport).
The handler only ferries query params into ListNotificationsInput and the resulting page out into the wire-level NotificationPage.
func (*Server) ListTemplates ¶
func (s *Server) ListTemplates(ctx context.Context, req api.ListTemplatesRequestObject) (api.ListTemplatesResponseObject, error)
ListTemplates returns a page of templates. The use case currently applies only the limit filter; cursor and channel filters are forward-compatible (the use case ignores them and the response omits next_cursor).
func (*Server) ReplaceTemplate ¶
func (s *Server) ReplaceTemplate(ctx context.Context, req api.ReplaceTemplateRequestObject) (api.ReplaceTemplateResponseObject, error)
ReplaceTemplate updates a template's fields. PUT semantics: every field in the request replaces the existing value; CreatedAt is preserved by the use case.
type ServerOptions ¶
type ServerOptions struct {
CreateNotification CreateNotificationExecutor
CreateBatch CreateBatchExecutor
GetNotification GetNotificationExecutor
ListNotifications ListNotificationsExecutor
CancelNotification CancelNotificationExecutor
GetNotificationTrace GetNotificationTraceExecutor
GetBatch GetBatchExecutor
CreateTemplate CreateTemplateExecutor
GetTemplate GetTemplateExecutor
ListTemplates ListTemplatesExecutor
ReplaceTemplate ReplaceTemplateExecutor
DeleteTemplate DeleteTemplateExecutor
// ReadinessChecks runs against /healthz/ready. Empty means the
// API has no downstream dependencies to verify and reports ready
// the moment it can accept HTTP.
ReadinessChecks []ReadinessCheck
// PrometheusGatherer is the source for /metrics. Production wires
// prometheus.DefaultGatherer (or a custom registry); leave nil and
// the endpoint falls through to 501 via the embedded stub.
PrometheusGatherer prometheus.Gatherer
// JSONMetrics returns the snapshot rendered by /api/v1/metrics.
JSONMetrics JSONMetricsProvider
}
ServerOptions bundles the per-operation executors the Server needs. Each operation has its own slot so partial wiring is legal — every handler method on Server begins with a nil-check on its executor and returns ErrNotImplemented (→ 501) when absent. Production composes the full set; tests inject only what they exercise.
type WebSocketHandler ¶
type WebSocketHandler struct {
// contains filtered or unexported fields
}
WebSocketHandler upgrades incoming HTTP requests to WebSocket connections and runs the subscribe/unsubscribe protocol against the shared Hub. Production wiring (cmd/api) mounts this handler at `GET /api/v1/ws/notifications`; the openapi spec deliberately does NOT model this endpoint because OpenAPI 3.0 has no native WebSocket description.
func NewWebSocketHandler ¶
func NewWebSocketHandler(hub *wsadapter.Hub) *WebSocketHandler
NewWebSocketHandler wires the handler to the shared Hub.
func (*WebSocketHandler) ServeHTTP ¶
func (h *WebSocketHandler) ServeHTTP(w nethttp.ResponseWriter, r *nethttp.Request)
ServeHTTP performs the upgrade and runs the client's read loop until the connection closes. UnsubscribeAll is deferred so the Hub never leaks references to dead connections.
AcceptOptions: OriginPatterns="*" — CORS validation belongs in a dedicated middleware layer at the edge, not in the WebSocket handshake itself (mixing the two has bitten other projects).
Source Files
¶
- cancel_notification_handler.go
- correlation.go
- create_batch_handler.go
- create_notification_handler.go
- docs_handler.go
- error_handler.go
- get_batch_handler.go
- get_notification_handler.go
- get_notification_trace_handler.go
- health_handlers.go
- idempotency.go
- inbound_rate_limit.go
- json_metrics_handler.go
- list_notifications_handler.go
- mapping.go
- metrics_middleware.go
- problem.go
- prometheus_handler.go
- router.go
- server.go
- template_handlers.go
- websocket_handler.go