Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
42abfb2
Initial plan
Copilot Mar 4, 2026
6b470fb
feat(http-client-csharp): auto-generate ClientSettings classes and IC…
Copilot Mar 4, 2026
c8d1455
refactor(http-client-csharp): address code review comments for Client…
Copilot Mar 4, 2026
78afaca
refactor(http-client-csharp): use typeof(ClientSettings) from System.…
Copilot Mar 5, 2026
9dc25d6
fix(http-client-csharp): add explicit package dependency, fix inline …
Copilot Mar 5, 2026
04121e3
fix(http-client-csharp): handle non-auth required params in settings,…
Copilot Mar 5, 2026
9174c45
fix(http-client-csharp): set Version to latest in IConfigurationSecti…
Copilot Mar 6, 2026
6ecdd2d
feat: AuthenticationPolicy internal constructor, Uri.TryCreate, Versi…
Copilot Mar 10, 2026
e88dd81
fix: update TestData files for internal AuthenticationPolicy construc…
Copilot Mar 11, 2026
6eecbcd
fix: refactor to single internal AuthenticationPolicy constructor; fi…
Copilot Mar 11, 2026
8273e9d
fix(http-client-csharp): fix StubLibraryVisitor to include internal c…
Copilot Mar 11, 2026
14a53b5
fix(http-client-csharp): fix client0/subscriptionId0 bug in this() in…
Copilot Mar 11, 2026
a2b7147
fix(http-client-csharp): remove internal ctors from stubbed libraries…
Copilot Mar 11, 2026
80a9b8d
fix(http-client-csharp): revert StubLibraryVisitor to keep internal c…
Copilot Mar 11, 2026
ee7e432
fix: address code review nits - use snippets, optimize ClientSettings…
Copilot Mar 11, 2026
d4ee3a5
Co-authored-by: jorgerangel-msft <54595583+jorgerangel-msft@users.nor…
Copilot Mar 11, 2026
88b42e2
fix: regenerate Spector specs to fix build - restore missing v1 files…
Copilot Mar 11, 2026
eea73ec
fix: add parameter validation assertions to internal implementation c…
Copilot Mar 11, 2026
17e468d
test: add tests for ClientSettings, settings constructor, and IConfig…
Copilot Mar 11, 2026
be7e618
test: improve property assertion precision in ClientSettingsProviderT…
Copilot Mar 11, 2026
8666c95
Add see-cref link to settings constructor XML docs
Copilot Mar 11, 2026
0d1d6cf
Sort LatestVersionsFields dictionary and add XML docs to ClientSettin…
Copilot Mar 11, 2026
272ad43
fix: update ClientSettings XML doc to follow pattern "Represents the …
Copilot Mar 11, 2026
e3998e6
fix: OAuth2 tests use BindingFlags.Static for _flows field; extract c…
Copilot Mar 11, 2026
ec76257
feat: add bool, int, TimeSpan TryParse bindings in ClientSettingsProv…
Copilot Mar 11, 2026
dc67cdb
regen
JoshLove-msft Mar 12, 2026
3c5b75a
Merge remote-tracking branch 'upstream/main' into copilot/auto-genera…
JoshLove-msft Mar 12, 2026
650560b
regen
JoshLove-msft Mar 12, 2026
2377e65
fix: remove duplicated assertions in sub-client constructors
Copilot Mar 12, 2026
b5a4ab5
Add Uri, List<string>, and enum type bindings in ClientSettingsProvid…
Copilot Mar 12, 2026
45d1f82
Fix comment to accurately reflect enum-only handling for non-framewor…
Copilot Mar 12, 2026
51fbd66
fix: use Enum.TryParse for fixed enums in ClientSettingsProvider Bind…
Copilot Mar 12, 2026
60a3de0
Add complex object binding handler and test for ClientSettingsProvider
Copilot Mar 12, 2026
18bcfd1
Improve complex object test assertion specificity
Copilot Mar 12, 2026
ca2320b
Add type-aware binding for ClientOptions IConfigurationSection constr…
Copilot Mar 12, 2026
76a4209
Address review feedback: use Declare/Is snippets, ToVariableName, add…
Copilot Mar 12, 2026
4510af8
refactor: use Declare snippet for variable declarations instead of ma…
Copilot Mar 12, 2026
4a47811
Replace BinaryOperatorExpression("is not") with Not(Is(Null)) snippet
Copilot Mar 12, 2026
dddef97
feat: add IsNot snippet to ValueExpression and ParameterProvider, use…
Copilot Mar 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ internal class StubLibraryVisitor : ScmLibraryVisitor
if (!IsCallingBaseCtor(constructor) &&
!IsEffectivelyPublic(constructor.Signature.Modifiers) &&
!IsParameterlessInternalCtorOnMrwSerializationType(constructor) &&
!IsInternalClientConstructor(constructor) &&
(constructor.EnclosingType is not ModelProvider model || model.DerivedModels.Count == 0))
return null;

Expand All @@ -57,6 +58,14 @@ internal class StubLibraryVisitor : ScmLibraryVisitor
return constructor;
}

private static bool IsInternalClientConstructor(ConstructorProvider constructor)
{
if (!constructor.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Internal))
return false;

return constructor.EnclosingType is ClientProvider;
}

private static bool IsParameterlessInternalCtorOnMrwSerializationType(ConstructorProvider constructor)
{
if (constructor.Signature.Parameters.Count != 0)
Expand All @@ -78,7 +87,13 @@ private static bool IsCallingBaseCtor(ConstructorProvider constructor)
protected override FieldProvider? VisitField(FieldProvider field)
{
// For ClientOptions, keep the non-public field as this currently represents the latest service version for a client.
return (field.Modifiers.HasFlag(FieldModifiers.Public) || field.EnclosingType.BaseType?.Equals(typeof(ClientPipelineOptions)) == true)
// For ClientProvider, keep const and static fields as they are referenced by stub constructor initializers
// (e.g. AuthorizationHeader const used in this() API key ctor, _flows static used in this() OAuth2 ctor).
return (field.Modifiers.HasFlag(FieldModifiers.Public)
|| field.EnclosingType.BaseType?.Equals(typeof(ClientPipelineOptions)) == true
|| (field.EnclosingType is ClientProvider
&& (field.Modifiers.HasFlag(FieldModifiers.Const)
|| field.Modifiers.HasFlag(FieldModifiers.Static))))
? field
: null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

<ItemGroup>
<PackageReference Include="System.ClientModel" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

using System.ClientModel;
using Microsoft.TypeSpec.Generator.ClientModel.Snippets;
using static Microsoft.TypeSpec.Generator.Snippets.Snippet;
using Microsoft.TypeSpec.Generator.Expressions;
using Microsoft.TypeSpec.Generator.Statements;
using Microsoft.TypeSpec.Generator.Primitives;
using Microsoft.TypeSpec.Generator.Statements;
using static Microsoft.TypeSpec.Generator.Snippets.Snippet;

namespace Microsoft.TypeSpec.Generator.ClientModel.Providers
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.TypeSpec.Generator.Primitives;
using Microsoft.TypeSpec.Generator.Providers;
using Microsoft.TypeSpec.Generator.Shared;
using Microsoft.TypeSpec.Generator.Snippets;
using Microsoft.TypeSpec.Generator.Statements;
using Microsoft.TypeSpec.Generator.Utilities;
using static Microsoft.TypeSpec.Generator.Snippets.Snippet;
Expand Down Expand Up @@ -230,9 +231,15 @@ protected override TypeProvider[] BuildNestedTypes()

protected override ConstructorProvider[] BuildConstructors()
{
var configSectionCtor = BuildConfigurationSectionConstructor();

if (LatestVersionsFields is null)
{
return [];
var defaultCtor = new ConstructorProvider(
new ConstructorSignature(Type, $"Initializes a new instance of {_clientProvider.Name}Options.", MethodSignatureModifiers.Public, []),
MethodBodyStatement.Empty,
this);
return [defaultCtor, configSectionCtor];
}

var constructorBody = new List<MethodBodyStatement>();
Expand Down Expand Up @@ -281,7 +288,85 @@ protected override ConstructorProvider[] BuildConstructors()
new ConstructorSignature(Type, $"Initializes a new instance of {_clientProvider.Name}Options.", MethodSignatureModifiers.Public, constructorParameters),
constructorBody,
this);
return [constructor];
return [constructor, configSectionCtor];
}

private ConstructorProvider BuildConfigurationSectionConstructor()
{
var sectionParam = new ParameterProvider(
"section",
$"The configuration section.",
ClientSettingsProvider.IConfigurationSectionType);

var experimentalAttr = new AttributeStatement(
typeof(System.Diagnostics.CodeAnalysis.ExperimentalAttribute),
[Literal(ClientSettingsProvider.ClientSettingsDiagnosticId)]);

// Set version to latest version before the guard so it is always initialized
var body = new List<MethodBodyStatement>();
if (LatestVersionsFields != null && VersionProperties != null)
{
foreach (var (_, serviceVersionEnum) in LatestVersionsFields.OrderBy(kvp => kvp.Key.Name))
{
if (VersionProperties.TryGetValue(serviceVersionEnum, out var versionProperty))
{
var latestVersion = serviceVersionEnum.EnumValues[^1];
body.Add(versionProperty.Assign(Literal(latestVersion.Value)).Terminate());
}
}
}

// if (section is null || !section.Exists()) { return; }
var guardCondition = sectionParam.Is(Null).Or(Not(sectionParam.Invoke("Exists")));
var guardStatement = new IfStatement(guardCondition) { Return() };

body.Add(guardStatement);

// Bind version properties from configuration (after guard, default already set before guard)
if (LatestVersionsFields != null && VersionProperties != null)
{
foreach (var (_, serviceVersionEnum) in LatestVersionsFields.OrderBy(kvp => kvp.Key.Name))
{
if (VersionProperties.TryGetValue(serviceVersionEnum, out var versionProperty))
{
// if (section["VersionPropertyName"] is string version) { Version = version; }
var versionVarDecl = Declare(versionProperty.Name.ToVariableName(), new CSharpType(typeof(string)), out var versionVar);
var ifVersionStatement = new IfStatement(new IndexerExpression(sectionParam, Literal(versionProperty.Name)).Is(versionVarDecl));
ifVersionStatement.Add(This.Property(versionProperty.Name).Assign(versionVar).Terminate());
body.Add(ifVersionStatement);
}
}
}

// Build a set of version property names for O(1) lookup
var versionPropertyNames = VersionProperties?.Values.Select(vp => vp.Name).ToHashSet();

// Bind non-version properties from configuration using type-aware binding
foreach (var property in Properties)
{
if (versionPropertyNames?.Contains(property.Name) == true)
{
continue;
}

ClientSettingsProvider.AppendBindingForProperty(
body,
sectionParam,
property.Name,
property.Name.ToVariableName(),
property.Type);
}

return new ConstructorProvider(
new ConstructorSignature(
Type,
$"Initializes a new instance of {_clientProvider.Name}Options from configuration.",
MethodSignatureModifiers.Internal,
[sectionParam],
attributes: [experimentalAttr],
initializer: new ConstructorInitializer(true, [sectionParam])),
new MethodBodyStatements([.. body]),
this);
}

protected override PropertyProvider[] BuildProperties()
Expand Down
Loading
Loading