Skip to content

One builder per client - #17

Merged
pokornyd merged 11 commits into
vnextfrom
client-builders
Sep 3, 2026
Merged

One builder per client#17
pokornyd merged 11 commits into
vnextfrom
client-builders

Conversation

@pokornyd

@pokornyd pokornyd commented Sep 2, 2026

Copy link
Copy Markdown
Member

Client registration currently has a large number of overloads for the same underlying setup: delegates, configuration roots, sections, pre-built options, named clients, HTTP configuration, and resilience configuration.

This PR replaces that surface with the standard Add... + builder pattern used across Microsoft.Extensions.*.

Each product now has three Add…Client overloads and one …Client.Create method, all using the same I…ClientBuilder. The shared registration flow lives in src/common/Clients instead of being repeated per product.

Builder API

services.AddDeliveryClient("preview", delivery =>
{
    delivery.Options.BindConfiguration("Delivery:Preview");
    delivery.ConfigureResilience(p => p.AddRetry(new HttpRetryStrategyOptions { MaxRetryAttempts = 5 }));
    delivery.HttpClient.AddHttpMessageHandler<AuditingHandler>();
    delivery.UseMemoryCache(cache => cache.DefaultExpiration = TimeSpan.FromMinutes(30));
});

await using var client = SyncClient.Create(sync =>
{
    sync.Options.Configure(o =>
    {
        o.EnvironmentId = "…";
        o.UsePreviewApi("…");
    });
});

Options exposes the client's OptionsBuilder<TOptions>, so standard APIs such as Configure, Configure<TDep>, Bind, BindConfiguration, and Validate are available directly.

HttpClient exposes the named IHttpClientBuilder registered by the SDK. Management also exposes SubscriptionHttpClient.

ConfigureResilience replaces the default resilience pipeline.

Services and Name are available for additional client-specific registrations.

Consumer configuration runs after the SDK's own setup, including HTTP client configuration.

Create uses the same builder and registration path as DI-backed clients, but over a private container owned by the client. This keeps standalone and registered clients on the same composition path. The existing Management Create(options) constructor remains.

The old fluent options builders have been removed. Their endpoint helpers now live directly on the options types:

  • UsePreviewApi
  • UseProductionApi
  • UseCustomEndpoint

Single properties can be configured directly.

The Caching package's registration overloads are replaced by five Use… methods on IDeliveryClientBuilder.

Shared registration

src/common/Clients now contains the common registration implementation.

ClientBuilder<TOptions> backs each product-specific sealed builder.

ClientRegistration handles the shared sequence:

  • client name and duplicate checks
  • validated named options
  • keyed generated Refit transport clients
  • base address and timeout configuration
  • resilience
  • handlers
  • connection recycling
  • keyed client registration
  • factory registration
  • default client aliasing

Each product supplies a TransportRecipe<TOptions> describing its transport. Management registers two transports, one for each scope.

No generic builder types are exposed publicly.

Resilience configuration is read lazily by the handler because ConfigureResilience runs after the transport registration is created.

The unnamed IOptions<T> alias for the default client is registered through IConfigureNamedOptions. Resolving IOptionsMonitor<T> while it is being constructed causes recursive container resolution, so it cannot be implemented as a normal Configure<IOptionsMonitor<T>> registration.

Compatibility

This is a breaking API change for the upcoming major versions.

Removed registration forms have direct builder-based replacements documented in the upgrade guides for Sync, Management, and Delivery.

There are three behavioral/API changes worth calling out:

  • Create and the Management constructor now throw OptionsValidationException for invalid options, matching container-based validation.
  • Caching is configured as part of client registration. The previous separate registration step has been removed.
  • CopyTo is now public on all three options types so pre-built options can be used with named clients:
Options.Configure(instance.CopyTo);

The model generator still needs to compile against both the currently published packages and the new builder-based packages. It therefore registers Delivery and Management through the options-instance overload, which is supported by both surfaces.

Verification

Both build legs complete with 0 warnings.

Full test suite:

  • Delivery: 1068 passed, 4 skipped
  • Sync: 218 passed
  • Management: 996 passed
  • Abstractions: 53 passed
  • Remaining suites unchanged

Tests removed with the old registration APIs were matched to their replacements. Coverage for the CustomAssetDomain query-string and fragment rules was also restored.

DI descriptors and the complete HTTP handler chains were compared before and after for all three named clients. Base addresses, resolved timeouts, connection lifetimes, and handler ordering are unchanged.

The public API snapshots now contain only the new builder-based surface.

@codecov

codecov Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.16273% with 7 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (vnext@ffc1c89). Learn more about missing BASE report.

Files with missing lines Patch % Lines
...del-generator/Kontent.Ai.ModelGenerator/Program.cs 25.00% 2 Missing and 1 partial ⚠️
src/common/Clients/ClientRegistration.cs 98.33% 1 Missing ⚠️
...tensions/ServiceCollectionExtensions.HttpClient.cs 95.23% 0 Missing and 1 partial ⚠️
...tensions/ServiceCollectionExtensions.HttpClient.cs 93.33% 0 Missing and 1 partial ⚠️
....Ai.Sync/Extensions/ServiceCollectionExtensions.cs 97.22% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff            @@
##             vnext      #17   +/-   ##
========================================
  Coverage         ?   90.76%           
========================================
  Files            ?      510           
  Lines            ?    10076           
  Branches         ?      953           
========================================
  Hits             ?     9145           
  Misses           ?      606           
  Partials         ?      325           
Flag Coverage Δ
aspnetcore 90.76% <98.16%> (?)
delivery 90.76% <98.16%> (?)
management 90.76% <98.16%> (?)
model-generator 90.76% <98.16%> (?)
sync 90.76% <98.16%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/common/Clients/ClientBuilder.cs 100.00% <100.00%> (ø)
src/common/Http/ResilienceHandlers.cs 100.00% <100.00%> (ø)
...very.Abstractions/Configuration/DeliveryOptions.cs 83.33% <ø> (ø)
...stractions/Extensions/DeliveryOptionsExtensions.cs 100.00% <100.00%> (ø)
...ching/Extensions/DeliveryCacheOptionsExtensions.cs 0.00% <ø> (ø)
...tensions/DeliveryClientBuilderCachingExtensions.cs 100.00% <100.00%> (ø)
...Ai.Delivery/Configuration/DeliveryClientBuilder.cs 100.00% <100.00%> (ø)
...ntItems/RichText/Resolution/HtmlResolverBuilder.cs 84.93% <ø> (ø)
src/delivery/Kontent.Ai.Delivery/DeliveryClient.cs 91.94% <100.00%> (ø)
...Delivery/Extensions/ServiceCollectionExtensions.cs 100.00% <100.00%> (ø)
... and 13 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@pokornyd
pokornyd merged commit 18a2834 into vnext Sep 3, 2026
7 of 8 checks passed
@pokornyd
pokornyd deleted the client-builders branch September 3, 2026 06:15
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