[.NET Generator] Auto-generate ClientSettings class and IConfiguration-enabled constructors#9920
[.NET Generator] Auto-generate ClientSettings class and IConfiguration-enabled constructors#9920
Conversation
…onfiguration-enabled constructors
- Add new ClientSettingsProvider class that generates {Client}Settings classes
- Extends ClientSettings from System.ClientModel.Primitives (future type)
- Properties for endpoint (Uri?) and options ({Client}Options?)
- BindCore override method binding from IConfigurationSection
- Marked [Experimental("SCME0002")]
- Add IConfigurationSection constructor to ClientOptionsProvider
- Internal constructor calling base(section)
- Guards with null/Exists check
- Binds non-version string properties from configuration
- Marked [Experimental("SCME0002")]
- Add settings constructor to ClientProvider for root clients with configurable endpoints
- Calls primary constructor via this() initializer
- Passes endpoint, credential (if auth), and options from settings
- Marked [Experimental("SCME0002")]
- Add ClientSettingsProvider output in ScmOutputLibrary.BuildClient
- Add CSharpType.FromExternalType() public factory method for types not in current NuGet
- Update tests to account for new constructors and generated code
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Settings feature - Use HashSet for O(1) version property name lookup in BuildConfigurationSectionConstructor - Rename local variable from 'propValue' suffix to 'FromConfig' for clarity - Extract IsSettingsConstructor helper in test for maintainability Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
...p/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientSettingsProvider.cs
Outdated
Show resolved
Hide resolved
...p/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientSettingsProvider.cs
Outdated
Show resolved
Hide resolved
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Primitives/CSharpType.cs
Outdated
Show resolved
Hide resolved
|
@copilot run Generate.ps1 and investigate any failures. |
...rp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs
Outdated
Show resolved
Hide resolved
…ClientModel 1.9.0 instead of string-based type reference Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
…guard, fix API key credential in settings constructor, fix missing default ctor Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
… fix API key credential extraction, add default ctor, add package dep Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
Run complete in 04121e3. Generate.ps1 succeeded and the full solution builds with 0 errors. Fixed issues found:
|
...rp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/SampleTypeSpecClientOptions.cs
Show resolved
Hide resolved
…on constructor before guard Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
…uration constructor body Add test coverage for the new configuration-based client initialization features: - ClientOptionsProviderTests: TestConfigurationSectionConstructorBody validates the IConfigurationSection constructor has proper guard statements, version initialization, and config section reading. - ClientProviderTests: TestBuildConstructors_SettingsConstructor validates the settings constructor is public, has correct parameter, uses this() initializer, and has empty body. TestBuildConstructors_NoSettingsConstructor_WhenNoEndpoint validates settings constructor is not generated without an endpoint. - ClientSettingsProviderTests: New test class validating name, base type, properties, BindCore method, experimental attribute, and namespace. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ests Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
|
|
||
| namespace SampleTypeSpec | ||
| { | ||
| /// <summary></summary> |
There was a problem hiding this comment.
@copilot we should include docs and also link to the docs for the ClientSettings type.
There was a problem hiding this comment.
IT should follow this pattern - Represents the settings used to configure a SecretClient that can be loaded from an IConfigurationSection.
There was a problem hiding this comment.
Added in 0d1d6cf. The generated {Client}Settings class now has a summary doc: Settings for the {Client} client, extending <see cref="ClientSettings"/>.
There was a problem hiding this comment.
@copilot please update to this pattern:
Represents the settings used to configure a SecretClient that can be loaded from an IConfigurationSection.
There was a problem hiding this comment.
Applied in 272ad43. The generated {Client}Settings class summary now follows the pattern:
/// <summary> Represents the settings used to configure a <see cref="SampleTypeSpecClient"/> that can be loaded from an <see cref="IConfigurationSection"/>. </summary>…gsProvider Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
…settings used to configure a {Client} that can be loaded from an IConfigurationSection"
Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
| // Add non-auth required parameters from the SAME parameter list (requiredParameters) | ||
| // to ensure the initializer references the same objects as the constructor signature. | ||
| string? authParamName = authFields != null | ||
| ? (authFields.AuthField.Name != TokenProviderFieldName ? "credential" : authFields.AuthField.AsParameter.Name) |
There was a problem hiding this comment.
nit: could we move the param name to a constant for consistency with the existing code?
There was a problem hiding this comment.
Added SettingsParamName constant in e3998e6.
There was a problem hiding this comment.
Applied in e3998e6 — added SettingsParamName constant at line 41 and CredentialParamName constant at line 40, referenced throughout.
| if (authFields is ApiKeyFields keyFields) | ||
| { | ||
| // ApiKeyAuthenticationPolicy.CreateHeaderApiKeyPolicy(credential, AuthorizationHeader, prefix?) | ||
| var credParam = requiredParameters.FirstOrDefault(p => p.Name == "credential"); |
There was a problem hiding this comment.
nit: can we reference the credential string here as a const?
There was a problem hiding this comment.
Added CredentialParamName constant and replaced all 4 "credential" string literals in e3998e6.
There was a problem hiding this comment.
Applied in e3998e6 — added CredentialParamName constant and replaced all 4 "credential" string literals.
|
|
||
| /// <summary> Binds configuration values from the given section. </summary> | ||
| /// <param name="section"> The configuration section. </param> | ||
| protected override void BindCore(IConfigurationSection section) |
There was a problem hiding this comment.
it would be great if we could test parsing the other supported property types like in https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/src/Credentials/DefaultAzureCredentialOptions.cs#L73. Specifically, maybe just the ones outlined in the issue for starters #9842. This can go as a follow up if we don't want to block this pr
There was a problem hiding this comment.
Added bool, int, and TimeSpan TryParse bindings in ec76257, with 4 new tests (TestBindCoreMethod_WithBoolParam, TestBindCoreMethod_WithIntParam, TestBindCoreMethod_WithStringParam, TestProperties_WithMultipleParamTypes). All 1187 tests pass.
|
@copilot the Oauth spector tests are failing Starting test execution, please wait... Stack Trace:
|
|
@copilot you need to run Generate.ps1 and check in the changes |
…redential/settings param name constants Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
... Fixed in e3998e6. The |
…ider with tests Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
... The OAuth2 test failure was fixed in e3998e6 —
Ran Generate.ps1 for all http specs — the generated code is already up to date (no diff). The OAuth2 fix was in the test file (e3998e6), not in generated code. Also added |
|
No changes needing a change description found. |
ClientSettingsProvider.cs- new TypeProvider generating{Client}Settingsclass extendingClientSettingsClientOptionsProvider.cs- addinternal {Client}Options(IConfigurationSection section)constructorClientProvider.cs- addpublic {Client}({Client}Settings settings)constructor and internal(AuthenticationPolicy?, endpoint, options)implementation constructorScmOutputLibrary.cs- registerClientSettingsProviderin the output_flowsis nowstatic readonly, useBindingFlags.StaticandGetValue(null)instead ofBindingFlags.InstanceCredentialParamNameandSettingsParamNameconstants inClientProvider.csfor consistencybool,int,TimeSpanTryParse bindings inClientSettingsProvider.BindCore()for supported property typesbool,int,stringparam binding and multi-type property scenariosOriginal prompt
This section details on the original issue you should resolve
<issue_title>[.NET Generator] Auto-generate ClientSettings class and IConfiguration-enabled constructors for clients</issue_title>
<issue_description>## Overview
Parent issue: Azure/azure-sdk-for-net#55491
The .NET generator should automatically produce
IConfiguration-based client construction support for every generated client. This enables developers to configure clients using standard .NET configuration patterns (appsettings.json, environment variables) and register them with dependency injection containers.Two services have been manually implemented as reference:
The generator should produce these automatically so every client gets configuration support without manual customization.
What the Generator Needs to Produce
Three artifacts per client:
A.
{Client}SettingsclassClientSettings(fromSystem.ClientModel.Primitives)BindCore(IConfigurationSection section)override[Experimental("SCME0002")]B.
internal {Client}Options(IConfigurationSection section)constructorbase(section)— the base options class handles common properties (see Forwarding Binding)if (section is null || !section.Exists()) { return; }before binding any properties[Experimental("SCME0002")]C.
public {Client}({Client}Settings settings)constructor on the client(AuthenticationPolicy, Options)or(Uri, AuthenticationPolicy, Options)depending on the clientAuthenticationPolicy.Create(settings)to pass to the primary constructorsettings?.Endpoint,settings?.Options)[Experimental("SCME0002")]How to Determine Settings Properties
The generator should inspect the client's primary public constructor (the one with the body implementation) parameters:
Uri?properties on Settings (e.g.,VaultUri,Endpoint){Client}Options?property on SettingsClientSettings.Credential/CredentialProvider, NOT duplicatedHow to Implement BindCore
`csharp
protected override void BindCore(IConfigurationSection section)
{
if (section is null || !section.Exists())
{
return;
}
}
`
Key rule: If a configuration segment doesn't exist, bail early — don't set the property. No validation in BindCore. Let the client constructor that receives the settings do all required parameter validation (e.g.,
Argument.AssertNotNull).Type Binding Patterns
The generator should use these patterns to bind properties from
IConfigurationSection:stringsection[name] is string valTenantId = valboolbool.TryParse(section[name], out bool val)DisableChallengeResourceVerification = valUriUri.TryCreate(section[name], UriKind.Absolute, out Uri val)RedirectUri = valTimeSpanTimeSpan.TryParse(section[name], out TimeSpan val)NetworkTimeout = valintint.TryParse(section[name], out int val)MaxRetries = valstring[]/List<string>section.GetSection(name).GetChildren().Where(c => c.Value is not null).Select(c => c.Value!).ToList()AdditionallyAllowedTenantssection.GetSection(name)+.Exists()+new Type(section)BrowserCustomization = new BrowserCustomizationOptions(browserSection)new TypeName(section[name])after null checkAudience = new AppConfigurationAudience(audience)For a comprehensive example of bin...
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.