Skip to content

Commit 607a89e

Browse files
HavenDVclaude
andcommitted
fix: Fix multi-spec generation issues for CLI
- Add namespace prefix to UnixTimestampJsonConverter file name to prevent overwrites when generating multiple specs to the same directory - Remove UnixTimestampJsonConverter from AsyncAPI converters list (not needed) - Fix value type deserialization in WebSocket receive method to use non-generic Deserialize overload compatible with JsonSerializerContext - Add --json-serializer-context CLI option for custom context class names - Extract context class name from Settings.JsonSerializerContext property Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 40d3363 commit 607a89e

188 files changed

Lines changed: 3903 additions & 3942 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/libs/AutoSDK.CLI/Commands/GenerateCommand.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,13 @@ internal sealed class GenerateCommand : Command
145145
Description = "Override class name for the generated WebSocket client (AsyncAPI specs only).",
146146
};
147147

148+
private Option<string> JsonSerializerContextName { get; } = new(
149+
name: "--json-serializer-context")
150+
{
151+
DefaultValueFactory = _ => string.Empty,
152+
Description = "Override the JsonSerializerContext class name (default: SourceGenerationContext). Useful when generating multiple specs to the same project.",
153+
};
154+
148155
public GenerateCommand() : base(name: "generate", description: "Generates client sdk using an OpenAPI or AsyncAPI spec.")
149156
{
150157
Arguments.Add(Input);
@@ -165,6 +172,7 @@ internal sealed class GenerateCommand : Command
165172
Options.Add(BaseUrl);
166173
Options.Add(OpenApiOverrides);
167174
Options.Add(WebSocketClientClassName);
175+
Options.Add(JsonSerializerContextName);
168176

169177
SetAction(HandleAsync);
170178
}
@@ -176,7 +184,11 @@ private async Task HandleAsync(ParseResult parseResult)
176184
bool singleFile = parseResult.GetRequiredValue(SingleFile);
177185

178186
var namespaceValue = parseResult.GetRequiredValue(Namespace);
179-
187+
var contextName = parseResult.GetRequiredValue(JsonSerializerContextName);
188+
var contextClassName = string.IsNullOrWhiteSpace(contextName)
189+
? "SourceGenerationContext"
190+
: contextName;
191+
180192
Settings settings = Settings.Default with
181193
{
182194
TargetFramework = parseResult.GetRequiredValue(TargetFramework),
@@ -185,7 +197,7 @@ private async Task HandleAsync(ParseResult parseResult)
185197
ClsCompliantEnumPrefix = parseResult.GetRequiredValue(ClsCompliantEnumPrefix),
186198
MethodNamingConvention = parseResult.GetRequiredValue(MethodNamingConvention),
187199
ExcludeDeprecatedOperations = parseResult.GetRequiredValue(ExcludeDeprecatedOperations),
188-
JsonSerializerContext = $"{namespaceValue}.SourceGenerationContext",
200+
JsonSerializerContext = $"{namespaceValue}.{contextClassName}",
189201
GenerateJsonSerializerContextTypes = true,
190202
ComputeDiscriminators = parseResult.GetRequiredValue(ComputeDiscriminators),
191203
GenerateModelValidationMethods = parseResult.GetRequiredValue(GenerateModelValidationMethods),

src/libs/AutoSDK/Sources/AsyncApiData.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -650,10 +650,6 @@ private static ImmutableArray<string> BuildConverters(
650650
$"global::{settings.Namespace}.JsonConverters.{x.AnyOfData?.SubType}JsonConverter<{string.Join(", ", x.Children
651651
.Where(y => y.Hint == (x.IsAnyOf ? Hint.AnyOf : x.IsOneOf ? Hint.OneOf : Hint.AllOf))
652652
.Select(y => y.TypeData.CSharpTypeWithNullabilityForValueTypes))}>"))
653-
.Concat(new[]
654-
{
655-
$"global::{globalSettings.Namespace}.JsonConverters.UnixTimestampJsonConverter",
656-
})
657653
.ToImmutableArray();
658654
}
659655
}

src/libs/AutoSDK/Sources/Sources.JsonSerializerContext.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ public static string GenerateJsonSerializerContext(
1818
return string.Empty;
1919
}
2020

21+
// Extract class name from fully-qualified JsonSerializerContext setting
22+
var contextClassName = client.Settings.JsonSerializerContext.Contains(".")
23+
? client.Settings.JsonSerializerContext.Substring(client.Settings.JsonSerializerContext.LastIndexOf('.') + 1)
24+
: "SourceGenerationContext";
25+
2126
return $@"
2227
#nullable enable
2328
@@ -36,7 +41,7 @@ namespace {client.Settings.Namespace}
3641
").Inject()}
3742
}})]
3843
{(types.IsEmpty ? " " : GenerateJsonSerializableAttributes(client, types))}
39-
public sealed partial class SourceGenerationContext : global::System.Text.Json.Serialization.JsonSerializerContext
44+
public sealed partial class {contextClassName} : global::System.Text.Json.Serialization.JsonSerializerContext
4045
{{
4146
}}
4247
}}".RemoveBlankLinesWhereOnlyWhitespaces();

src/libs/AutoSDK/Sources/Sources.WebSocketReceiveMethod.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ public static string GenerateWebSocketReceiveMethod(
3232
string deserializeAndYield;
3333
if (isValueType)
3434
{
35-
// Value types (structs/anyOf) — Deserialize<T> returns T directly, no null check needed
35+
// Value types (structs/anyOf) — use non-generic Deserialize with cast (no null check needed for value types)
3636
var deserializeCall = hasOptions
3737
? $"global::System.Text.Json.JsonSerializer.Deserialize<{eventTypeName}>(json, JsonSerializerOptions)"
38-
: $"global::System.Text.Json.JsonSerializer.Deserialize<{eventTypeName}>(json, JsonSerializerContext)";
38+
: $"({eventTypeName})global::System.Text.Json.JsonSerializer.Deserialize(json, typeof({eventTypeName}), JsonSerializerContext)!";
3939
deserializeAndYield = $@" var @event = {deserializeCall};
4040
4141
yield return @event;";

src/libs/AutoSDK/Sources/Sources.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ public static FileWithName UnixTimestampJsonConverter(
234234
CancellationToken cancellationToken = default)
235235
{
236236
return new FileWithName(
237-
Name: "JsonConverters.UnixTimestamp.g.cs",
237+
Name: $"{settings.Namespace}.JsonConverters.UnixTimestamp.g.cs",
238238
Text: GenerateUnixTimestampJsonConverter(settings, cancellationToken: cancellationToken));
239239
}
240240

src/tests/AutoSDK.SnapshotTests/Snapshots/CLI/Empty/_#JsonConverters.UnixTimestamp.g.verified.cs renamed to src/tests/AutoSDK.SnapshotTests/Snapshots/CLI/Empty/_#G.JsonConverters.UnixTimestamp.g.verified.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//HintName: JsonConverters.UnixTimestamp.g.cs
1+
//HintName: G.JsonConverters.UnixTimestamp.g.cs
22
#nullable enable
33

44
namespace G.JsonConverters

src/tests/AutoSDK.SnapshotTests/Snapshots/CLI/anthropic/_#JsonConverters.UnixTimestamp.g.verified.cs renamed to src/tests/AutoSDK.SnapshotTests/Snapshots/CLI/anthropic/_#G.JsonConverters.UnixTimestamp.g.verified.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//HintName: JsonConverters.UnixTimestamp.g.cs
1+
//HintName: G.JsonConverters.UnixTimestamp.g.cs
22
#nullable enable
33

44
namespace G.JsonConverters

src/tests/AutoSDK.SnapshotTests/Snapshots/CLI/assemblyai/_#JsonConverters.UnixTimestamp.g.verified.cs renamed to src/tests/AutoSDK.SnapshotTests/Snapshots/CLI/assemblyai/_#G.JsonConverters.UnixTimestamp.g.verified.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//HintName: JsonConverters.UnixTimestamp.g.cs
1+
//HintName: G.JsonConverters.UnixTimestamp.g.cs
22
#nullable enable
33

44
namespace G.JsonConverters

src/tests/AutoSDK.SnapshotTests/Snapshots/CLI/circular-refs/_#JsonConverters.UnixTimestamp.g.verified.cs renamed to src/tests/AutoSDK.SnapshotTests/Snapshots/CLI/circular-refs/_#G.JsonConverters.UnixTimestamp.g.verified.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//HintName: JsonConverters.UnixTimestamp.g.cs
1+
//HintName: G.JsonConverters.UnixTimestamp.g.cs
22
#nullable enable
33

44
namespace G.JsonConverters
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//HintName: G.JsonConverters.UnixTimestamp.g.cs
2+
#nullable enable
3+
4+
namespace G.JsonConverters
5+
{
6+
/// <inheritdoc />
7+
public class UnixTimestampJsonConverter : global::System.Text.Json.Serialization.JsonConverter<global::System.DateTimeOffset>
8+
{
9+
/// <inheritdoc />
10+
public override global::System.DateTimeOffset Read(
11+
ref global::System.Text.Json.Utf8JsonReader reader,
12+
global::System.Type typeToConvert,
13+
global::System.Text.Json.JsonSerializerOptions options)
14+
{
15+
if (reader.TokenType == global::System.Text.Json.JsonTokenType.Number)
16+
{
17+
if (reader.TryGetInt64(out long unixTimestamp))
18+
{
19+
if (unixTimestamp >= -62135596800L && unixTimestamp <= 253402300799L)
20+
{
21+
return global::System.DateTimeOffset.FromUnixTimeSeconds(unixTimestamp);
22+
}
23+
24+
return global::System.DateTimeOffset.FromUnixTimeMilliseconds(unixTimestamp);
25+
}
26+
if (reader.TryGetInt32(out int unixTimestampInt))
27+
{
28+
return global::System.DateTimeOffset.FromUnixTimeSeconds(unixTimestampInt);
29+
}
30+
}
31+
32+
return default;
33+
}
34+
35+
/// <inheritdoc />
36+
public override void Write(
37+
global::System.Text.Json.Utf8JsonWriter writer,
38+
global::System.DateTimeOffset value,
39+
global::System.Text.Json.JsonSerializerOptions options)
40+
{
41+
long unixTimestamp = value.ToUnixTimeSeconds();
42+
writer.WriteNumberValue(unixTimestamp);
43+
}
44+
}
45+
}

0 commit comments

Comments
 (0)