feat: add per-call options channel and OperationParams projection SPI#154
feat: add per-call options channel and OperationParams projection SPI#154OmarAlJarrah wants to merge 1 commit into
Conversation
Introduce two sdk-core runtime primitives that let a single call override client behaviour and let an operation describe its inputs, without mutating the shared client or generating code. Per-call options: - CallOptions: an immutable, per-call options bag carrying a timeout overlay, a response-validation decision, an optional ad-hoc credential, and an open set of typed extension attributes keyed by CallOption<T>. applyDefaults merges a call-site set onto a client baseline with per-field precedence (receiver wins, falling back to defaults); CallOptions.NONE is the identity. - CallTimeout: a per-phase (connect/write/read/call) timeout overlay where a null phase means "inherit", with the same applyDefaults overlay semantics. - ResponseValidation: a three-state enum (INHERIT/ENABLED/DISABLED) so the inherit case is a first-class non-null value rather than a nullable boolean. - CallContext now exposes callOptions; DispatchContext mints it once at the head of the chain and every promotion carries it forward unchanged, so the dispatch / request / exchange phases all observe the same overrides. It defaults to CallOptions.NONE, preserving existing construction sites. Operation projection: - OperationParams: a narrow SPI projecting an operation's inputs into path, query, headers, and body, each with an empty default so an implementation overrides only what it contributes. PathParam and QueryParam are the raw, unencoded value types it returns. - RequestProjector: materializes an OperationParams plus a method and path template into a Request (owning percent-encoding for path segments and the query string), and projectInto promotes a DispatchContext straight into a RequestContext, tying the SPI to the context chain in one call. Public API surface regenerated via apiDump.
|
This adds a per-call options channel ( IssuesUnresolved-placeholder check can miss a malformed segment containing a slash — |
Two hand-written
sdk-coreruntime primitives: a per-call options channel threaded through the context chain, and a minimal SPI for projecting an operation’s inputs into a request. Both are usable by hand today; no code generation is introduced.Per-call options (Closes #27)
There was no way to override client behaviour for a single call without mutating the shared client. This adds an immutable options bag carried on every link of the
DispatchContext -> RequestContext -> ExchangeContextchain.CallOptions— a per-call timeout overlay, a response-validation decision, an optional ad-hoc credential, and an open set of typed extension attributes keyed byCallOption<T>.applyDefaultsmerges a call-site set onto a client baseline with per-field precedence (the receiver wins, falling back to the defaults);CallOptions.NONEis the identity element.CallTimeout— a per-phase (connect/write/read/call) overlay where anullphase means "inherit", with the sameapplyDefaultsoverlay semantics. The per-call timeout overlay the issue calls for.ResponseValidation— a three-state enum (INHERIT/ENABLED/DISABLED) so the inherit case is a first-class non-null value rather than a nullable boolean, matching the issue’s "non-null with defaults" guidance.CallContextnow exposescallOptions.DispatchContextmints it once at the head of the chain and each promotion (toRequestContext/toExchangeContext) carries it forward unchanged. It defaults toCallOptions.NONE, so every existing construction site is unaffected and the transport SPIs are untouched.OperationParams projection (Closes #57)
Thin services need a way to project an operation’s inputs into path/query/headers/body that feeds the context chain. The
QueryParamsmultimap (#28) is not in yet, so query contributions are modeled as flat ordered name/value pairs that migrate mechanically once it lands.OperationParams— a narrow SPI withpathParams(),queryParams(),headers(),body(), each defaulting to empty so an implementation overrides only what it contributes.PathParam/QueryParam— the raw, unencoded value types the projection returns.RequestProjector.project(baseUrl, method, pathTemplate, params)— materializes aRequest, substituting{name}path placeholders and appending the query string, and owning percent-encoding (path segments escape/; query names/values are form-encoded with+rewritten to%20).RequestProjector.projectInto(dispatch, ...)— projects and promotes aDispatchContextstraight into aRequestContext, registering it on the chain’s store slot and carrying the chain’s instrumentation, call key, and call options forward.Tests
New suites:
CallOptionsTest,CallTimeoutTest,ResponseValidationTest,CallOptionsPropagationTest(chain threading),OperationParamsTest,RequestProjectorTest(substitution, encoding, repeated/valueless query params, header/body application, error cases,projectIntopromotion).Gated build (module-scoped)
All green. Public API snapshot regenerated with
./gradlew :sdk-core:apiDump --no-daemonand committed.