feat: add SDK foundation, platform design, and System.Text.Json serialization#3
Merged
Conversation
The core toolkit (HTTP value models, request/response bodies, transport SPI, error hierarchy), the System.Net reference transport, the xUnit suite, and the central build configuration — present in the working tree but never committed. Also resolves analyzer and documentation errors that blocked a clean Release build under the .NET 10 SDK: CS1734, CA1816, CA2215, CA1859, CS0103, CA1861. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Captures the platform-level design for the .NET SDK: the native-first guiding principle, package topology, cross-cutting decisions (standard abstractions in core, transport-agnostic pipeline with IHttpClientFactory interop, multi-target net8.0/net10.0 with AOT), and the ordered slice breakdown for the remaining subsystems. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Defines the ISerde seam (generic, context-backed, AOT-safe), the SystemTextJsonSerde reference implementation, the Core body conveniences (FromValue / ReadValueAsync), and the deserialize-on-demand typed-error accessor. Scopes out Optional<T>/PATCH (tracked separately) and the error-body buffering owned by the policies slice. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tics Options are plain POCOs in Core (per-policy, aggregated under DexpaceClientOptions); the Microsoft.Extensions.Options machinery (IOptions, configuration binding, validation) lives in the DI integration package. Updates platform-spec D2 and the package graph to move Options out of Core, leaving Logging.Abstractions and DiagnosticSource as Core's only added dependencies. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Telemetry emits through Activity/ActivitySource, Meter, and ILogger under a single Dexpace.Sdk source/meter, following OpenTelemetry HTTP semantic conventions. Per-call state flows through an explicit, mutable PipelineContext threaded by the pipeline; trace correlation uses Activity.Current with no ContextStore or ambient state. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Object-policy model (HttpPipelinePolicy with explicit PipelineRunner next), named stages with pillar semantics, a type-targeted builder (InsertAfter/Replace/Remove), and the transport as the fixed terminal. Retry/redirect re-invoke the downstream chain in a loop, so per-call vs per-attempt is determined purely by a policy's stage position. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Defines the concrete policies (operation, redirect, retry, idempotency, set-date, client-identity, instrumentation), the default pipeline assembly, and the error-response contract: SendAsync returns the Response and EnsureSuccessAsync throws HttpResponseException with a bounded buffered error body. Finalizes the stage ordering, splitting once-per-call (above Retry) from per-attempt (below Retry) policies. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Vendor-neutral TokenCredential/AccessToken in Core (no Azure dependency) with an optional Azure.Identity bridge package planned; ApiKey/Basic/ Bearer credentials; in-memory token cache with proactive refresh and single-flight; auth policies at the Auth stage that withhold credentials on cross-origin redirects; RFC 7235 challenge parsing plus a Basic handler. Digest (RFC 7616) deferred to issue #2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pagination as AsyncPageable<T> with typed selectors; SSE as an IAsyncEnumerable parser plus a reconnecting client; Standard Webhooks HMAC-SHA256 verification behind an extensible IWebhookVerifier; and a DI integration package (AddDexpaceClient builder, options binding with ValidateOnStart, IHttpClientFactory interop) tying the toolkit together. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bite-sized TDD plan for slice 1: scaffold the multi-target/AOT-validated SystemTextJson package, the ISerde seam in Core, the SystemTextJsonSerde implementation (async + sync, error mapping), and the body/typed-error conveniences (FromValue, ReadValueAsync, GetErrorAsync). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the System.Text.Json package project (net8.0;net10.0, AOT-compatible, references Dexpace.Sdk.Core) and its xUnit test project, registered in the solution. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Multi-targets the core test project to net8.0;net10.0 (matching the serialization test project) and installs the .NET 8 runtime in CI so the net8.0 test target executes. Local runs use the installed net10.0 runtime via 'dotnet test -f net10.0'; CI exercises both. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Request accepted any absolute URI, so "/relative" parsed as an absolute file:// URI on Unix and slipped through, while Request.Create surfaced UriFormatException instead of the documented ArgumentException for genuinely relative input. The constructor now requires an http/https scheme and Create parses with Uri.TryCreate, so non-absolute, malformed, and non-HTTP URLs all fail fast with one clear ArgumentException. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nd async Widen catch filters on all four JsonSerializer call-sites from the narrow JsonException-only clause to also cover NotSupportedException (and InvalidOperationException for the sync Serialize path), so runtime failures that STJ surfaces through those types are mapped to SerializationException / DeserializationException as documented. OperationCanceledException / TaskCanceledException are excluded by the when-filter and propagate unwrapped. Add three tests that lock the widened contract: reference-cycle detection (sync + async SerializeAsync), and a cancelled-token guardrail asserting OperationCanceledException propagates unwrapped through DeserializeAsync. Test 3 (unsupported abstract-member shape) was skipped — the source generator does not emit metadata for abstract interface members, blocking a clean test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This is the first substantial increment for the .NET SDK. It establishes the repository baseline, captures the platform design, and ships the first implemented subsystem (serialization).
IHttpClient/IAsyncHttpClienttransport SPI, the error hierarchy, and theSystem.Netreference transport — together with the central build configuration. Also resolves the analyzer and documentation errors that prevented a clean Release build under the .NET 10 SDK.ISerdeseam inCoreand a trim- and NativeAOT-safeSystem.Text.Jsonimplementation (Dexpace.Sdk.Serialization.SystemTextJson) built on source-generatedJsonTypeInfo<T>. Serializer failures map consistently toSerializationException/DeserializationExceptionacross the sync and async paths, while cancellation propagates unwrapped.RequestBody.FromValue<T>andResponseBody.ReadValueAsync<T>, plus a deserialize-on-demandHttpResponseException.GetErrorAsync<T>, all routed throughISerdesoCoretakes no dependency on a concrete serializer.http/httpsscheme, so relative orfile://URLs are rejected with a clearArgumentException.net8.0;net10.0, and CI installs both runtimes to exercise the full matrix.Optional follow-ups are tracked as issues: three-state optional fields for PATCH (#1) and an RFC 7616 Digest auth handler (#2).
Test plan
dotnet build -c Releaseis clean under warnings-as-errors and the trim/AOT analyzer, on net8.0 and net10.0dotnet test -c Releasepasses on both runtimes (43 tests: 30 core, 13 serialization)dotnet format --verify-no-changesreports no changes🤖 Generated with Claude Code