Skip to content

Latest commit

 

History

History
104 lines (83 loc) · 3.3 KB

File metadata and controls

104 lines (83 loc) · 3.3 KB

client

Tier: Adapter · Status: REST Full; SOAP / gRPC / WS scaffolds · Java original: firefly-service-client · .NET project: FireflyFramework.Client

Overview

client is the framework's outbound HTTP client builder — a fluent RESTBuilder that composes timeouts, retries, default headers, and correlation-id propagation into a RESTClient with a single Do(ctx, method, path, body, out) method. Non-2xx responses are decoded into *kernel.FireflyError (RFC 7807-aware), so every consumer of every external service sees the same error shape.

SOAP / gRPC / WebSocket builders share the same New*(baseURL) shape and currently return ErrTransportNotRegistered — production adapters land in dedicated modules in v26.06.

Why a separate module?

The Java firefly-service-client integrates Resilience4j + service discovery + OAuth2 token caching + GraphQL helper. ASP.NET defers much of this to IHttpClientFactory plus Polly. Both worlds settle on the same shape: a typed builder that yields a typed client. This module is the Go equivalent — small, stdlib-based, composable with resilience.Chain.

Public surface

type RESTBuilder struct{ ... }
func NewREST(baseURL string) *RESTBuilder
func (*RESTBuilder) WithHeader(k, v string) *RESTBuilder
func (*RESTBuilder) WithTimeout(time.Duration) *RESTBuilder
func (*RESTBuilder) WithHTTPClient(*http.Client) *RESTBuilder
func (*RESTBuilder) WithRetries(n int) *RESTBuilder
func (*RESTBuilder) Build() *RESTClient

type RESTClient struct{ ... }
func (*RESTClient) Do(ctx, method, path string, body, out any) error

// Non-REST placeholders (return ErrTransportNotRegistered until wired)
func NewSOAP(string) error
func NewGRPC(string) error
func NewWebSocket(string) error
var ErrTransportNotRegistered = errors.New("…transport adapter not registered")

Do automatically:

  • JSON-encodes body (when non-nil) and sets Content-Type: application/json.
  • Forwards the correlation id from the context as X-Correlation-Id.
  • Retries on network errors and 429 / 5xx status codes (exponential backoff capped at 2 s).
  • Decodes RFC 7807 application/problem+json bodies into a typed *kernel.FireflyError populated with Code, Title, Status, Detail, and Fields.

Quick start

import (
    "context"
    "net/http"
    "time"
    "github.com/fireflyframework/fireflyframework-go/client"
    "github.com/fireflyframework/fireflyframework-go/kernel"
)

c := client.NewREST("https://api.example.com").
    WithHeader("X-Tenant", "acme").
    WithTimeout(5 * time.Second).
    WithRetries(3).
    Build()

type Order struct{ ID, Customer string }
var out Order
if err := c.Do(ctx, http.MethodPost, "/orders", req, &out); err != nil {
    var fe *kernel.FireflyError
    if errors.As(err, &fe) {
        log.Printf("upstream %d: %s", fe.Status, fe.Detail)
    }
}

Composition with resilience

guarded := resilience.Chain(
    resilience.AsDecorator(resilience.NewTimeout(5*time.Second)),
    resilience.AsDecorator(circuitBreaker),
)
err := guarded(ctx, func() error { return c.Do(ctx, "POST", "/charge", req, &out) })

Testing

cd client
go test ./...

Covers happy-path JSON round-trip, ProblemDetail decoding into a *kernel.FireflyError, retry on 5xx (3 attempts), and the SOAP / gRPC / WS sentinel returns.