Skip to content

Commit 7d6c85c

Browse files
authored
Merge branch 'vnext' into fix/v2-message-trait-headers
2 parents 4f3a7d7 + e816843 commit 7d6c85c

3 files changed

Lines changed: 197 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:

‎src/ByteBard.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ public AsyncApiV2VersionService(AsyncApiDiagnostic diagnostic)
3636
[typeof(AsyncApiAvroSchema)] = AsyncApiAvroSchemaDeserializer.LoadSchema,
3737
[typeof(AsyncApiJsonSchema)] = AsyncApiV2Deserializer.LoadJsonSchemaPayload,
3838
[typeof(AsyncApiAvroSchema)] = AsyncApiV2Deserializer.LoadAvroPayload,
39+
// 2.x has no Multi Format Schema Object. Only the local reference fallback asks for one, and it reads the document after the upgrade to 3.0.
40+
[typeof(AsyncApiMultiFormatSchema)] = AsyncApiV3Deserializer.LoadMultiFormatSchema,
3941
[typeof(AsyncApiSecurityScheme)] = AsyncApiV2Deserializer.LoadSecurityScheme,
4042
[typeof(AsyncApiServer)] = AsyncApiV2Deserializer.LoadServer,
4143
[typeof(AsyncApiServerVariable)] = AsyncApiV2Deserializer.LoadServerVariable,

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

Lines changed: 190 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,182 @@ 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+
217+
// A local payload reference that is not a registered component is resolved by falling back to the whole document.
218+
[TestCase("2.6.0")]
219+
[TestCase("3.0.0")]
220+
public void LocalPayloadReference_NotInWorkspace_ResolvesPayload(string version)
221+
{
222+
var yaml = version.StartsWith("2")
223+
? """
224+
asyncapi: 2.6.0
225+
info:
226+
title: test
227+
version: 1.0.0
228+
channels:
229+
workspace:
230+
publish:
231+
message:
232+
payload:
233+
$ref: '#/components/messages/shared/payload'
234+
components:
235+
messages:
236+
shared:
237+
payload:
238+
type: string
239+
"""
240+
: """
241+
asyncapi: 3.0.0
242+
info:
243+
title: test
244+
version: 1.0.0
245+
channels:
246+
workspace:
247+
messages:
248+
message:
249+
payload:
250+
$ref: '#/components/messages/shared/payload'
251+
components:
252+
messages:
253+
shared:
254+
payload:
255+
type: string
256+
""";
257+
258+
var doc = new AsyncApiStringReader().Read(yaml, out var diagnostic);
259+
260+
diagnostic.Errors.Should().BeEmpty();
261+
var payload = doc.Channels["workspace"].Messages.Values.First().Payload;
262+
payload.Should().BeOfType<AsyncApiMultiFormatSchemaReference>();
263+
payload.Schema.As<AsyncApiJsonSchema>().Type.Should().Be(SchemaType.String);
264+
}
265+
266+
// A local Avro payload reference that is not a registered component is resolved by falling back to the whole document.
267+
[TestCase("2.6.0")]
268+
[TestCase("3.0.0")]
269+
public void LocalAvroPayloadReference_NotInWorkspace_ResolvesPayload(string version)
270+
{
271+
var yaml = version.StartsWith("2")
272+
? """
273+
asyncapi: 2.6.0
274+
info:
275+
title: test
276+
version: 1.0.0
277+
channels:
278+
workspace:
279+
publish:
280+
message:
281+
schemaFormat: application/vnd.apache.avro;version=1.9.0
282+
payload:
283+
$ref: '#/components/messages/shared/payload'
284+
components:
285+
messages:
286+
shared:
287+
schemaFormat: application/vnd.apache.avro;version=1.9.0
288+
payload:
289+
type: record
290+
name: User
291+
fields:
292+
- name: name
293+
type: string
294+
"""
295+
: """
296+
asyncapi: 3.0.0
297+
info:
298+
title: test
299+
version: 1.0.0
300+
channels:
301+
workspace:
302+
messages:
303+
message:
304+
payload:
305+
$ref: '#/components/messages/shared/payload'
306+
components:
307+
messages:
308+
shared:
309+
payload:
310+
schemaFormat: application/vnd.apache.avro;version=1.9.0
311+
schema:
312+
type: record
313+
name: User
314+
fields:
315+
- name: name
316+
type: string
317+
""";
318+
319+
var doc = new AsyncApiStringReader().Read(yaml, out var diagnostic);
320+
321+
diagnostic.Errors.Should().BeEmpty();
322+
var payload = doc.Channels["workspace"].Messages.Values.First().Payload;
323+
payload.Should().BeOfType<AsyncApiMultiFormatSchemaReference>();
324+
payload.SchemaFormat.Should().StartWith("application/vnd.apache.avro");
325+
var record = payload.Schema.As<AsyncApiAvroSchema>().As<AvroRecord>();
326+
record.Name.Should().Be("User");
327+
record.Fields.Should().ContainSingle().Which.Name.Should().Be("name");
328+
}
329+
141330
[Test]
142331
public void V2_ReferencePointers()
143332
{
@@ -815,4 +1004,4 @@ public Task<Stream> LoadAsync(Uri baseUri, Uri uri)
8151004
return Task.FromResult(this.Load(baseUri, uri));
8161005
}
8171006
}
818-
}
1007+
}

0 commit comments

Comments
 (0)