@@ -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