Skip to content

feat: add SDK foundation, platform design, and System.Text.Json serialization#3

Merged
OmarAlJarrah merged 21 commits into
mainfrom
feat/serde-system-text-json
Jun 15, 2026
Merged

feat: add SDK foundation, platform design, and System.Text.Json serialization#3
OmarAlJarrah merged 21 commits into
mainfrom
feat/serde-system-text-json

Conversation

@OmarAlJarrah

Copy link
Copy Markdown
Member

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).

  • Foundation. Commits the existing core toolkit that had not yet been tracked — HTTP value models, request/response bodies, the IHttpClient / IAsyncHttpClient transport SPI, the error hierarchy, and the System.Net reference 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.
  • Design. Adds the platform architecture document and per-subsystem design specs (serde, options, instrumentation + context, pipeline, policies, auth, pagination, SSE, webhooks, DI integration) and the serialization implementation plan. The guiding principle is to design for .NET idioms first rather than transliterate the Java/Python SDKs.
  • Serialization. Introduces a serializer-agnostic ISerde seam in Core and a trim- and NativeAOT-safe System.Text.Json implementation (Dexpace.Sdk.Serialization.SystemTextJson) built on source-generated JsonTypeInfo<T>. Serializer failures map consistently to SerializationException / DeserializationException across the sync and async paths, while cancellation propagates unwrapped.
  • Conveniences. RequestBody.FromValue<T> and ResponseBody.ReadValueAsync<T>, plus a deserialize-on-demand HttpResponseException.GetErrorAsync<T>, all routed through ISerde so Core takes no dependency on a concrete serializer.
  • Hardening. Request URLs now require an http/https scheme, so relative or file:// URLs are rejected with a clear ArgumentException.
  • Targeting. The new package and the test projects multi-target 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 Release is clean under warnings-as-errors and the trim/AOT analyzer, on net8.0 and net10.0
  • dotnet test -c Release passes on both runtimes (43 tests: 30 core, 13 serialization)
  • dotnet format --verify-no-changes reports no changes

🤖 Generated with Claude Code

OmarAlJarrah and others added 21 commits June 15, 2026 17:07
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>
@OmarAlJarrah OmarAlJarrah changed the title Add SDK foundation, platform design, and System.Text.Json serialization feat: add SDK foundation, platform design, and System.Text.Json serialization Jun 15, 2026
@OmarAlJarrah OmarAlJarrah merged commit 3d53021 into main Jun 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant