Skip to content

Commit 0680ffb

Browse files
Make ConfigurationSchema section name, options ref, and NuGet.targets generation configurable (#10226)
## Overview Add three settable properties to `ScmCodeModelGenerator` that allow downstream generators and plugins to customize the `ConfigurationSchema.json` output: - `ConfigurationSchemaSectionName` (default: "Clients") - Top-level section key - `ConfigurationSchemaOptionsRef` (default: "options") - Base options ref value - `GenerateNuGetTargets` (default: true) - Whether to emit the .NuGet.targets file ## Motivation Azure SDK generators need to use "AzureClients" instead of "Clients", reference "azureOptions" instead of "options", and suppress .NuGet.targets generation. These properties enable both `AzureClientGenerator` and `GeneratorPlugin.Apply()` to configure the behavior without overriding `WriteAdditionalFiles`. ## Changes - `ScmCodeModelGenerator.cs`: Added three public settable properties, updated `WriteAdditionalFiles` to use them - No breaking changes - defaults preserve existing behavior - All 1283 existing ClientModel tests pass --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 230d54b commit 0680ffb

3 files changed

Lines changed: 137 additions & 7 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
namespace Microsoft.TypeSpec.Generator.ClientModel
5+
{
6+
/// <summary>
7+
/// Options that control ConfigurationSchema.json generation.
8+
/// </summary>
9+
public class ConfigurationSchemaOptions
10+
{
11+
/// <summary>
12+
/// Gets or sets the top-level section name used in the generated ConfigurationSchema.json.
13+
/// Defaults to "Clients". Azure SDK generators should set this to "AzureClients".
14+
/// </summary>
15+
public string SectionName { get; set; } = ConfigurationSchemaGenerator.DefaultSectionName;
16+
17+
/// <summary>
18+
/// Gets or sets the $ref value used for the base options definition in the generated ConfigurationSchema.json.
19+
/// Defaults to "options". Azure SDK generators should set this to "azureOptions".
20+
/// </summary>
21+
public string OptionsRef { get; set; } = ConfigurationSchemaGenerator.DefaultOptionsRef;
22+
23+
/// <summary>
24+
/// Gets or sets whether to generate the .NuGet.targets file alongside the ConfigurationSchema.json.
25+
/// Defaults to true. Set to false when the build infrastructure handles targets file packing centrally.
26+
/// </summary>
27+
public bool GenerateNuGetTargets { get; set; } = true;
28+
}
29+
}

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmCodeModelGenerator.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ public class ScmCodeModelGenerator : CodeModelGenerator
3030
internal SerializationFormatDefinition SerializationFormatDefinition { get; } =
3131
new SerializationFormatDefinition();
3232

33+
/// <summary>
34+
/// Gets the options that control ConfigurationSchema.json generation.
35+
/// </summary>
36+
public ConfigurationSchemaOptions ConfigurationSchema { get; } = new();
37+
3338
[ImportingConstructor]
3439
public ScmCodeModelGenerator(GeneratorContext context)
3540
: base(context)
@@ -49,7 +54,10 @@ protected override void Configure()
4954

5055
public override async Task WriteAdditionalFiles(string outputPath)
5156
{
52-
var schemaContent = ConfigurationSchemaGenerator.Generate(OutputLibrary);
57+
var schemaContent = ConfigurationSchemaGenerator.Generate(
58+
OutputLibrary,
59+
ConfigurationSchema.SectionName,
60+
ConfigurationSchema.OptionsRef);
5361
if (schemaContent != null)
5462
{
5563
var schemaPath = Path.Combine(outputPath, "schema", "ConfigurationSchema.json");
@@ -61,12 +69,15 @@ public override async Task WriteAdditionalFiles(string outputPath)
6169
Emitter.Info($"Writing {Path.GetFullPath(schemaPath)}");
6270
await File.WriteAllTextAsync(schemaPath, schemaContent);
6371

64-
// Generate the .targets file for JsonSchemaSegment registration
65-
var packageName = Configuration.PackageName;
66-
var targetsPath = Path.Combine(outputPath, $"{packageName}.NuGet.targets");
67-
var targetsContent = GenerateTargetsFile();
68-
Emitter.Info($"Writing {Path.GetFullPath(targetsPath)}");
69-
await File.WriteAllTextAsync(targetsPath, targetsContent);
72+
if (ConfigurationSchema.GenerateNuGetTargets)
73+
{
74+
// Generate the .targets file for JsonSchemaSegment registration
75+
var packageName = Configuration.PackageName;
76+
var targetsPath = Path.Combine(outputPath, $"{packageName}.NuGet.targets");
77+
var targetsContent = GenerateTargetsFile();
78+
Emitter.Info($"Writing {Path.GetFullPath(targetsPath)}");
79+
await File.WriteAllTextAsync(targetsPath, targetsContent);
80+
}
7081
}
7182
}
7283

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/ConfigurationSchemaGeneratorTests.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,96 @@ public void GetJsonSchemaForType_ReturnsCorrectSchema_ForNullableTypes()
749749
Assert.AreEqual("boolean", nullableBoolSchema["type"]?.GetValue<string>());
750750
}
751751

752+
[Test]
753+
public void Generate_UsesCustomSectionName()
754+
{
755+
var client = InputFactory.Client("TestService");
756+
var clientProvider = new ClientProvider(client);
757+
758+
var output = new TestOutputLibrary([clientProvider]);
759+
var result = ConfigurationSchemaGenerator.Generate(output, sectionName: "AzureClients");
760+
761+
Assert.IsNotNull(result);
762+
var doc = JsonNode.Parse(result!)!;
763+
764+
Assert.IsNull(doc["properties"]?["Clients"], "Schema should not have a 'Clients' section");
765+
var azureClients = doc["properties"]?["AzureClients"];
766+
Assert.IsNotNull(azureClients, "Schema should have an 'AzureClients' section");
767+
768+
var testClient = azureClients!["properties"]?["TestService"];
769+
Assert.IsNotNull(testClient, "Schema should have a well-known 'TestService' entry under AzureClients");
770+
}
771+
772+
[Test]
773+
public void Generate_UsesCustomOptionsRef()
774+
{
775+
var client = InputFactory.Client("TestService");
776+
var clientProvider = new ClientProvider(client);
777+
778+
var output = new TestOutputLibrary([clientProvider]);
779+
var result = ConfigurationSchemaGenerator.Generate(output, optionsRef: "azureOptions");
780+
781+
Assert.IsNotNull(result);
782+
var doc = JsonNode.Parse(result!)!;
783+
784+
// The options definition should inherit from azureOptions instead of options
785+
var definitions = doc["definitions"];
786+
Assert.IsNotNull(definitions);
787+
788+
// Find the client options definition and verify it references azureOptions
789+
foreach (var def in definitions!.AsObject())
790+
{
791+
var allOf = def.Value?["allOf"];
792+
if (allOf != null)
793+
{
794+
var baseRef = allOf.AsArray()[0]?["$ref"]?.GetValue<string>();
795+
Assert.AreEqual("#/definitions/azureOptions", baseRef,
796+
$"Definition '{def.Key}' should reference azureOptions");
797+
}
798+
}
799+
}
800+
801+
[Test]
802+
public void Generate_UsesCustomSectionNameAndOptionsRef()
803+
{
804+
var client = InputFactory.Client("TestService");
805+
var clientProvider = new ClientProvider(client);
806+
807+
var output = new TestOutputLibrary([clientProvider]);
808+
var result = ConfigurationSchemaGenerator.Generate(
809+
output,
810+
sectionName: "AzureClients",
811+
optionsRef: "azureOptions");
812+
813+
Assert.IsNotNull(result);
814+
var doc = JsonNode.Parse(result!)!;
815+
816+
// Verify section name
817+
Assert.IsNull(doc["properties"]?["Clients"]);
818+
Assert.IsNotNull(doc["properties"]?["AzureClients"]);
819+
820+
// Verify options ref
821+
var clientEntry = doc["properties"]?["AzureClients"]?["properties"]?["TestService"];
822+
Assert.IsNotNull(clientEntry);
823+
824+
// The options definition should use azureOptions
825+
var optionsRef = clientEntry!["properties"]?["Options"]?["$ref"]?.GetValue<string>();
826+
Assert.IsNotNull(optionsRef);
827+
var defName = optionsRef!.Replace("#/definitions/", "");
828+
var optionsDef = doc["definitions"]?[defName];
829+
Assert.IsNotNull(optionsDef);
830+
Assert.AreEqual("#/definitions/azureOptions", optionsDef!["allOf"]?.AsArray()[0]?["$ref"]?.GetValue<string>());
831+
}
832+
833+
[Test]
834+
public void ConfigurationSchemaOptions_HasCorrectDefaults()
835+
{
836+
var options = new ConfigurationSchemaOptions();
837+
Assert.AreEqual("Clients", options.SectionName);
838+
Assert.AreEqual("options", options.OptionsRef);
839+
Assert.IsTrue(options.GenerateNuGetTargets);
840+
}
841+
752842
/// <summary>
753843
/// Test output library that wraps provided TypeProviders.
754844
/// </summary>

0 commit comments

Comments
 (0)