ecluse:ecluse-core
Safe HaskellNone
LanguageGHC2021

Ecluse.Core.Server.Contract

Description

The response-contract algebra: one value interpreted as both wire behaviour and capability-manifest documentation.

A ResponseContract is indexed by the value a handler must produce. Its constructor is private: callers can only build one from the leaf contracts in this module and combine those leaves with chooseContract. Each leaf owns both its ResponseDoc and the function that renders its payload, so those two interpretations cannot be supplied separately.

The route layer existentially packages a contract with a handler producing that contract's response type. The runtime gives the handler only the corresponding typed responder; a handler therefore cannot reach WAI with a status or body outside its route's contract. bodilessContract is the same interpretation for HEAD: statuses and headers are preserved while every documented and emitted body is removed.

Owned JSON bodies use the same autodocodec JSONCodec for encoding here and schema generation in the manifest tier. An intentionally transparent upstream relay is different: its status, media type, and bytes are not Écluse's to constrain, so passthroughContract documents an explicit OpenAPI default response instead of claiming a false closed set.

Synopsis

Documented body shapes

data BodySchema Source #

The structural shape of a response body, kept OpenAPI-free in the core.

SchemaPassthrough is deliberately broad: it means the operation transparently relays an upstream response whose media type and body shape are outside Écluse's control.

Constructors

SchemaEmpty

No body at all.

SchemaOpaque ByteString

Opaque bytes under one known media type.

SchemaJson (JSONCodec a)

JSON encoded from the same codec the manifest renders as a schema.

SchemaDocumented Text

An imperatively assembled JSON document with a named manifest schema.

SchemaPassthrough

An upstream-controlled body under an upstream-controlled media type.

data RequestSpec Source #

A request body a route accepts: its prose, requiredness, and documented shape.

Request decoding is not part of the response algebra. A hand-authored request schema still owes the separate conformance check described in the API-surface architecture.

Constructors

RequestSpec 

Fields

data ResponseStatus Source #

Whether a documented response has one exact status or covers every other status.

data ResponseDoc Source #

One response entry for the capability manifest. This is a projection of a ResponseContract leaf, never independently supplied by a route.

Constructors

ResponseDoc 

Fields

A response contract

data ResponseContract response Source #

A response contract indexed by the only value its handler may answer with.

The constructor is private. The list and renderer can therefore only be extended together through this module's leaves and chooseContract.

responseDocs :: ResponseContract response -> [ResponseDoc] Source #

The manifest projection of a response contract.

responseToWai :: ResponseContract response -> response -> Response Source #

Render one value through its contract and into WAI. This is the only application boundary at which a route response becomes an unrestricted WAI Response.

bodilessContract :: ResponseContract response -> ResponseContract response Source #

Derive the HEAD interpretation of a contract: the same response alternatives, statuses, and headers, with no documented or emitted body.

Exact response leaves

data ResponseValue a Source #

A payload for an exact-status response, carrying additional response headers.

responseValue :: [Header] -> a -> ResponseValue a Source #

Supply the additional headers and payload for an exact response leaf.

jsonContract :: Status -> Text -> JSONCodec a -> ResponseContract (ResponseValue a) Source #

One exact JSON response, encoded through the codec its manifest schema uses.

documentedJsonContract :: Status -> Text -> Text -> ResponseContract (ResponseValue LByteString) Source #

One exact JSON response whose bytes are assembled imperatively and whose schema is the named hand-authored component in the manifest.

emptyContract :: Status -> Text -> ResponseContract (ResponseValue ()) Source #

One exact bodiless response.

Open response leaves

data VariableResponse a Source #

A response whose status is supplied by the handler while its media type remains fixed by the contract. Used for the publication target's arbitrary JSON-labelled status.

variableResponse :: Status -> [Header] -> a -> VariableResponse a Source #

Supply a dynamic status, additional headers, and body to a variable-status leaf.

variableOpaqueContract :: ByteString -> Text -> ResponseContract (VariableResponse LByteString) Source #

An OpenAPI default response carrying opaque bytes under a fixed media type.

The schema is intentionally binary even for application/json: Écluse relays the publication target's bytes without parsing them, so it must not promise they satisfy a JSON schema it never checks.

data PassthroughBody Source #

The body of a transparent upstream response.

data PassthroughResponse Source #

A transparent upstream response: status, headers, and body all remain upstream's.

passthroughContract :: Text -> ResponseContract PassthroughResponse Source #

An explicit OpenAPI default contract for a transparent upstream relay.

This is the honest contract when the proxy intentionally forwards arbitrary upstream statuses and media types. It prevents drift by documenting that open behaviour rather than placing an inaccurate finite status set beside it.

Combining closed alternatives

data ResponseChoice a b Source #

A binary response choice. Nesting ResponseChoices forms a closed route response sum without type-level programming; chooseContract builds its two matching interpretations together.

Constructors

FirstResponse a 
SecondResponse b 

chooseContract :: ResponseContract a -> ResponseContract b -> ResponseContract (ResponseChoice a b) Source #

Combine two response contracts into a closed choice of their alternatives.

Rendering JSON through a codec

encodeBody :: JSONCodec a -> a -> LByteString Source #

Encode a JSON value to bytes through its autodocodec codec.