Skip to content

Commit 6ff9217

Browse files
committed
fix: score nested anyof envelope properties
1 parent b612a9a commit 6ff9217

File tree

3 files changed

+124
-12
lines changed

3 files changed

+124
-12
lines changed

src/libs/AutoSDK.CSharp/Enrichment/CSharpSchemaDataFactory.cs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -238,18 +238,7 @@ public static AnyOfData CreateAnyOfData(SchemaContext context)
238238
name = CSharpPropertyNameGenerator.AvoidObjectMemberNameCollision(name);
239239
name = AvoidNamedAnyOfMemberNameCollision(name);
240240

241-
var resolvedSchema = child.Schema.ResolveIfRequired();
242-
var jsonPropertyNames = ImmutableArray<string>.Empty;
243-
if (resolvedSchema.Properties is { Count: > 0 } props)
244-
{
245-
var jpnBuilder = ImmutableArray.CreateBuilder<string>(props.Count);
246-
foreach (var key in props.Keys.OrderBy(x => x, StringComparer.Ordinal))
247-
{
248-
jpnBuilder.Add(key);
249-
}
250-
251-
jsonPropertyNames = jpnBuilder.MoveToImmutable();
252-
}
241+
var jsonPropertyNames = CollectJsonPropertyNames(child.Schema);
253242

254243
builder.Add((PropertyData.Default with
255244
{
@@ -360,6 +349,34 @@ private static string ComputeDiscriminatorValue(
360349
return string.Empty;
361350
}
362351

352+
private static ImmutableArray<string> CollectJsonPropertyNames(IOpenApiSchema schema)
353+
{
354+
var resolvedSchema = schema.ResolveIfRequired();
355+
if (resolvedSchema.Properties is not { Count: > 0 } properties)
356+
{
357+
return ImmutableArray<string>.Empty;
358+
}
359+
360+
var builder = ImmutableArray.CreateBuilder<string>();
361+
foreach (var property in properties.OrderBy(x => x.Key, StringComparer.Ordinal))
362+
{
363+
builder.Add(property.Key);
364+
365+
var nestedSchema = property.Value.ResolveIfRequired();
366+
if (nestedSchema.Properties is not { Count: > 0 } nestedProperties)
367+
{
368+
continue;
369+
}
370+
371+
foreach (var nestedProperty in nestedProperties.Keys.OrderBy(x => x, StringComparer.Ordinal))
372+
{
373+
builder.Add($"{property.Key}.{nestedProperty}");
374+
}
375+
}
376+
377+
return builder.ToImmutable();
378+
}
379+
363380
private static EquatableArray<PropertyData> DeduplicatePropertyNames(
364381
EquatableArray<PropertyData> properties)
365382
{

src/libs/AutoSDK.CSharp/Sources/Sources.JsonConverters.AnyOf.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public static string GenerateAnyOfJsonConverter(
2525
: $"global::{anyOfData.Namespace}.{anyOfData.Name}";
2626
var hasDiscriminator = anyOfData.DiscriminatorType != null && anyOfData.Properties.All(x => !string.IsNullOrWhiteSpace(x.DiscriminatorValue));
2727
var hasPropertyInfo = anyOfData.Properties.Any(x => !x.JsonPropertyNames.IsEmpty);
28+
var hasNestedPropertyInfo = anyOfData.Properties.Any(x => x.JsonPropertyNames.Any(propName => propName.Contains('.')));
2829

2930
string read;
3031
if (hasDiscriminator)
@@ -77,6 +78,14 @@ public static string GenerateAnyOfJsonConverter(
7778
foreach (var __jsonProp in __jsonDocument.RootElement.EnumerateObject())
7879
{{
7980
__jsonProps.Add(__jsonProp.Name);
81+
{(hasNestedPropertyInfo ? @" if (__jsonProp.Value.ValueKind == global::System.Text.Json.JsonValueKind.Object)
82+
{
83+
foreach (var __nestedJsonProp in __jsonProp.Value.EnumerateObject())
84+
{
85+
__jsonProps.Add(__jsonProp.Name + ""."" + __nestedJsonProp.Name);
86+
}
87+
}
88+
" : string.Empty)}
8089
}}
8190
}}
8291

src/tests/AutoSDK.UnitTests/Tests.WebSocketClient.cs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,92 @@ public void AsyncApiServerVariables_GenerateTypedConnectAsync_UsesTemplateAndCon
165165
source.Should().Contain("global::System.Threading.CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)");
166166
}
167167

168+
[TestMethod]
169+
public void AsyncApiNestedEnvelopeServerEvents_GenerateNestedPropertyScoring()
170+
{
171+
const string yaml = """
172+
asyncapi: 3.0.0
173+
info:
174+
title: Realtime API
175+
version: 1.0.0
176+
channels:
177+
realtime:
178+
address: /realtime
179+
messages:
180+
Transcript:
181+
payload:
182+
$ref: '#/components/schemas/TranscriptEvent'
183+
Usage:
184+
payload:
185+
$ref: '#/components/schemas/UsageEvent'
186+
SpeechStarted:
187+
payload:
188+
$ref: '#/components/schemas/SpeechStartedEvent'
189+
operations:
190+
receiveUpdates:
191+
action: receive
192+
channel:
193+
$ref: '#/channels/realtime'
194+
messages:
195+
- $ref: '#/channels/realtime/messages/Transcript'
196+
- $ref: '#/channels/realtime/messages/Usage'
197+
- $ref: '#/channels/realtime/messages/SpeechStarted'
198+
components:
199+
schemas:
200+
TranscriptEvent:
201+
type: object
202+
properties:
203+
result:
204+
type: object
205+
properties:
206+
transcription:
207+
type: object
208+
properties:
209+
text:
210+
type: string
211+
UsageEvent:
212+
type: object
213+
properties:
214+
result:
215+
type: object
216+
properties:
217+
usage:
218+
type: object
219+
properties:
220+
input_tokens:
221+
type: integer
222+
SpeechStartedEvent:
223+
type: object
224+
properties:
225+
result:
226+
type: object
227+
properties:
228+
speechStarted:
229+
type: object
230+
properties:
231+
offset_ms:
232+
type: integer
233+
""";
234+
235+
var settings = Settings.Default with
236+
{
237+
Namespace = "G",
238+
JsonSerializerContext = "G.SourceGenerationContext",
239+
};
240+
241+
var data = AsyncApiData.Prepare(((yaml, settings), GlobalSettings: settings));
242+
var anyOf = data.AnyOfs.Should().ContainSingle(x => x.Name == "ServerEvent").Subject;
243+
var source = Sources.GenerateAnyOfJsonConverter(anyOf);
244+
245+
anyOf.Properties.SelectMany(x => x.JsonPropertyNames).Should().Contain("result.transcription");
246+
anyOf.Properties.SelectMany(x => x.JsonPropertyNames).Should().Contain("result.usage");
247+
anyOf.Properties.SelectMany(x => x.JsonPropertyNames).Should().Contain("result.speechStarted");
248+
source.Should().Contain("__jsonProps.Add(__jsonProp.Name + \".\" + __nestedJsonProp.Name);");
249+
source.Should().Contain("if (__jsonProps.Contains(\"result.transcription\")) __score0++;");
250+
source.Should().Contain("if (__jsonProps.Contains(\"result.usage\")) __score1++;");
251+
source.Should().Contain("if (__jsonProps.Contains(\"result.speechStarted\")) __score2++;");
252+
}
253+
168254
[TestMethod]
169255
public void WebSocketBinaryPayloadHelpers_GenerateDecodedBytesProperty_AndConvenienceSendOverload()
170256
{

0 commit comments

Comments
 (0)