Skip to content

Commit 87a19e9

Browse files
bieluclaude
andcommitted
fix: resolve external 3.x payload references
In 3.x a message payload reference is an AsyncApiMultiFormatSchemaReference. Resolving an external reference only handled JSON and Avro schema references, so an external payload such as `$ref: './payload.json'` was never read and reported "Unable to deserialize reference". The external content is now read as a Multi Format Schema Object, which also accepts a plain Schema Object. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 7ed7e04 commit 87a19e9

2 files changed

Lines changed: 82 additions & 1 deletion

File tree

‎src/ByteBard.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,11 @@ private IAsyncApiSerializable ResolveStreamReference(Stream stream, IAsyncApiRef
410410
result = this.ReadFragment<AsyncApiAvroSchema>(json, diagnostic.SpecificationVersion, out fragmentDiagnostic);
411411
}
412412

413+
if (reference is AsyncApiMultiFormatSchemaReference)
414+
{
415+
result = this.ReadFragment<AsyncApiMultiFormatSchema>(json, diagnostic.SpecificationVersion, out fragmentDiagnostic);
416+
}
417+
413418
break;
414419

415420
case ReferenceType.Server:

‎test/ByteBard.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs‎

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@ public class AsyncApiReference_Should : TestBase
2929
}
3030
""";
3131

32+
private const string ExternalPayloads =
33+
"""
34+
{
35+
"schemas": {
36+
"plain": { "type": "string" },
37+
"multiFormat": {
38+
"schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0",
39+
"schema": { "type": "integer" }
40+
}
41+
}
42+
}
43+
""";
44+
3245
private static readonly TestCaseData[] ExternalFragmentCases =
3346
{
3447
new TestCaseData("/components/schemas/plain", SchemaType.String).SetArgDisplayNames("nested path"),
@@ -138,6 +151,69 @@ private static AsyncApiDocument ReadWithExternalSchemas(string yaml, out AsyncAp
138151
return new AsyncApiStringReader(settings).Read(yaml, out diagnostic);
139152
}
140153

154+
[TestCase("payloads.json#/schemas/plain", SchemaType.String)]
155+
[TestCase("payloads.json#/schemas/multiFormat", SchemaType.Integer)]
156+
public void V3_ExternalPayloadReference_ResolvesPayload(string reference, SchemaType expectedType)
157+
{
158+
var yaml =
159+
$"""
160+
asyncapi: 3.0.0
161+
info:
162+
title: test
163+
version: 1.0.0
164+
channels:
165+
workspace:
166+
messages:
167+
message:
168+
payload:
169+
$ref: '{reference}'
170+
""";
171+
172+
var settings = new AsyncApiReaderSettings
173+
{
174+
ReferenceResolution = ReferenceResolutionSetting.ResolveAllReferences,
175+
ExternalReferenceLoader = new MockStringLoader(ExternalPayloads),
176+
};
177+
178+
var doc = new AsyncApiStringReader(settings).Read(yaml, out var diagnostic);
179+
180+
diagnostic.Errors.Should().BeEmpty();
181+
var payload = doc.Channels["workspace"].Messages["message"].Payload;
182+
payload.Should().BeOfType<AsyncApiMultiFormatSchemaReference>();
183+
payload.As<AsyncApiMultiFormatSchemaReference>().UnresolvedReference.Should().BeFalse();
184+
payload.Schema.As<AsyncApiJsonSchema>().Type.Should().Be(expectedType);
185+
doc.SerializeAsYaml(AsyncApiVersion.AsyncApi3_0).Should().Contain($"$ref: {reference}");
186+
}
187+
188+
[Test]
189+
public void V3_ExternalPayloadReference_WithoutFragment_ResolvesPayload()
190+
{
191+
var yaml =
192+
"""
193+
asyncapi: 3.0.0
194+
info:
195+
title: test
196+
version: 1.0.0
197+
channels:
198+
workspace:
199+
messages:
200+
message:
201+
payload:
202+
$ref: './payload.json'
203+
""";
204+
205+
var settings = new AsyncApiReaderSettings
206+
{
207+
ReferenceResolution = ReferenceResolutionSetting.ResolveAllReferences,
208+
ExternalReferenceLoader = new MockStringLoader("""{ "type": "boolean" }"""),
209+
};
210+
211+
var doc = new AsyncApiStringReader(settings).Read(yaml, out var diagnostic);
212+
213+
diagnostic.Errors.Should().BeEmpty();
214+
doc.Channels["workspace"].Messages["message"].Payload.Schema.As<AsyncApiJsonSchema>().Type.Should().Be(SchemaType.Boolean);
215+
}
216+
141217
[Test]
142218
public void V2_ReferencePointers()
143219
{
@@ -815,4 +891,4 @@ public Task<Stream> LoadAsync(Uri baseUri, Uri uri)
815891
return Task.FromResult(this.Load(baseUri, uri));
816892
}
817893
}
818-
}
894+
}

0 commit comments

Comments
 (0)