Add connector, response policy, telemetry emitter and gateway
Some checks failed
ci / build (push) Failing after 23s
Some checks failed
ci / build (push) Failing after 23s
Completes the request path for FLUID-WP-0003 T01-T03 and T05-T07. The gateway resolves a revision, routes to the adapter process, and records what happened, without ever depending on the control plane to serve. Three behaviours carry tests because the architecture rests on them: Emit never blocks against a stalled sink (Blueprint 34.2), the gateway keeps serving after control-plane loss (invariant 2), and backend internals do not leak into error responses (5.7). The connector retries only idempotent methods, so a slow adapter cannot cause a hall-of-helix entry to be published twice, and breakers are per-revision so a broken candidate does not take the stable revision down with it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014KmVxhJ35tCo7rE7UnLwWu Assistant: claude-code Assistant-Model: opus Assistant-Process: 1116572@bnt-lap001 Assistant-Session: 8ba9bb93-a72a-4883-b189-2499cce5c400
This commit is contained in:
parent
42891e08a2
commit
791e419973
7 changed files with 1088 additions and 6 deletions
91
internal/runtime/response.go
Normal file
91
internal/runtime/response.go
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package runtime
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/tegwick/fluid-core/internal/contract"
|
||||
)
|
||||
|
||||
// ErrorKind classifies a response the interface produced rather than the
|
||||
// backend. These map onto the telemetry error classes so that a response and
|
||||
// the evidence it generates cannot disagree.
|
||||
type ErrorKind string
|
||||
|
||||
const (
|
||||
ErrorValidation ErrorKind = "validation"
|
||||
ErrorUnknownPath ErrorKind = "unknown_path"
|
||||
ErrorUnsupported ErrorKind = "unsupported_parameter"
|
||||
ErrorAuthorization ErrorKind = "authorization"
|
||||
ErrorUnavailable ErrorKind = "backend_failure"
|
||||
ErrorTimeout ErrorKind = "timeout"
|
||||
ErrorPolicy ErrorKind = "policy_rejection"
|
||||
ErrorNoCapability ErrorKind = "missing_capability"
|
||||
)
|
||||
|
||||
// ErrorBody is the interface's error representation.
|
||||
//
|
||||
// ArchitectureBlueprint.md section 5.7: errors should carry enough structured
|
||||
// information to improve observability without leaking backend detail, and may
|
||||
// include a correlation reference that lets downstream analysis link the
|
||||
// response a consumer saw to the pressure it generated.
|
||||
type ErrorBody struct {
|
||||
Kind ErrorKind `json:"kind"`
|
||||
// Message is written for the consumer and must stay free of backend
|
||||
// internals: hostnames, stack traces, driver errors, upstream payloads.
|
||||
Message string `json:"message"`
|
||||
// Correlation is the reference a consumer can quote back, and the key that
|
||||
// ties this response to its telemetry.
|
||||
Correlation string `json:"correlation"`
|
||||
// Revision tells the consumer which contract answered. Without it a client
|
||||
// debugging an unexpected response has no way to know what it was talking to.
|
||||
Revision contract.RevisionID `json:"revision,omitempty"`
|
||||
// Field names the offending input for validation failures.
|
||||
Field string `json:"field,omitempty"`
|
||||
// Feedback points at the explicit-feedback endpoint. Turning a dead end into
|
||||
// an invitation is the cheapest pressure signal the interface can collect.
|
||||
Feedback string `json:"feedback,omitempty"`
|
||||
}
|
||||
|
||||
// statusFor maps an error kind to its HTTP status.
|
||||
var statusFor = map[ErrorKind]int{
|
||||
ErrorValidation: http.StatusBadRequest,
|
||||
ErrorUnknownPath: http.StatusNotFound,
|
||||
ErrorUnsupported: http.StatusBadRequest,
|
||||
ErrorAuthorization: http.StatusForbidden,
|
||||
ErrorUnavailable: http.StatusBadGateway,
|
||||
ErrorTimeout: http.StatusGatewayTimeout,
|
||||
ErrorPolicy: http.StatusForbidden,
|
||||
ErrorNoCapability: http.StatusNotImplemented,
|
||||
}
|
||||
|
||||
// ResponsePolicy renders interface errors consistently.
|
||||
type ResponsePolicy struct {
|
||||
// FeedbackPath, when set, is advertised on every error.
|
||||
FeedbackPath string
|
||||
}
|
||||
|
||||
// WriteError renders an error response.
|
||||
func (p ResponsePolicy) WriteError(w http.ResponseWriter, kind ErrorKind, correlation, message string, rev contract.RevisionID, field string) {
|
||||
status, ok := statusFor[kind]
|
||||
if !ok {
|
||||
status = http.StatusInternalServerError
|
||||
}
|
||||
|
||||
body := ErrorBody{
|
||||
Kind: kind,
|
||||
Message: message,
|
||||
Correlation: correlation,
|
||||
Revision: rev,
|
||||
Field: field,
|
||||
Feedback: p.FeedbackPath,
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("X-FLUID-Correlation", correlation)
|
||||
if rev != "" {
|
||||
w.Header().Set("X-FLUID-Revision", string(rev))
|
||||
}
|
||||
w.WriteHeader(status)
|
||||
_ = json.NewEncoder(w).Encode(body)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue