Skip to content

Commit 453db29

Browse files
committed
mas avro goodness
1 parent 4f53f13 commit 453db29

File tree

14 files changed

+150
-18
lines changed

14 files changed

+150
-18
lines changed

src/LEGO.AsyncAPI.Readers/V2/AsyncApiAvroSchemaDeserializer.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace LEGO.AsyncAPI.Readers
44
{
55
using System;
6-
using System.Collections;
76
using LEGO.AsyncAPI.Models;
87
using LEGO.AsyncAPI.Readers.Exceptions;
98
using LEGO.AsyncAPI.Readers.ParseNodes;
@@ -18,7 +17,7 @@ public class AsyncApiAvroSchemaDeserializer
1817
{ "doc", (a, n) => a.Doc = n.GetScalarValue() },
1918
{ "default", (a, n) => a.Default = n.CreateAny() },
2019
{ "aliases", (a, n) => a.Aliases = n.CreateSimpleList(n2 => n2.GetScalarValue()) },
21-
{ "order", (a, n) => a.Order = n.GetScalarValue() },
20+
{ "order", (a, n) => a.Order = n.GetScalarValue().GetEnumFromDisplayName<AvroFieldOrder>() },
2221
};
2322

2423
private static readonly FixedFieldMap<AvroRecord> RecordFixedFields = new()
@@ -130,6 +129,17 @@ public static AvroSchema LoadSchema(ParseNode node)
130129

131130
if (node is MapNode mapNode)
132131
{
132+
var pointer = mapNode.GetReferencePointer();
133+
134+
if (pointer != null)
135+
{
136+
return new AvroRecord
137+
{
138+
UnresolvedReference = true,
139+
Reference = node.Context.VersionService.ConvertToAsyncApiReference(pointer, ReferenceType.Schema),
140+
};
141+
}
142+
133143
var type = mapNode["type"]?.Value.GetScalarValue();
134144

135145
switch (type)

src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public static IAsyncApiMessagePayload LoadAvroPayload(ParseNode n)
7777

7878
private static IAsyncApiMessagePayload LoadPayload(ParseNode n, string format)
7979
{
80+
8081
if (n == null)
8182
{
8283
return null;
@@ -107,6 +108,9 @@ private static IAsyncApiMessagePayload LoadPayload(ParseNode n, string format)
107108

108109
static readonly IEnumerable<string> SupportedAvroSchemaFormats = new List<string>
109110
{
111+
"application/vnd.apache.avro",
112+
"application/vnd.apache.avro+json",
113+
"application/vnd.apache.avro+yaml",
110114
"application/vnd.apache.avro+json;version=1.9.0",
111115
"application/vnd.apache.avro+yaml;version=1.9.0",
112116
};

src/LEGO.AsyncAPI/Models/AsyncApiAvroSchemaPayload.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,16 @@ public AsyncApiAvroSchemaPayload()
1818
{
1919
}
2020

21-
public bool UnresolvedReference { get; set; }
21+
public bool TryGetAs<T>(out T schema)
22+
where T : AvroSchema
23+
{
24+
schema = this.Schema as T;
25+
return schema != null;
26+
}
27+
28+
public bool UnresolvedReference { get => this.Schema.UnresolvedReference; set => this.Schema.UnresolvedReference = value; }
2229

23-
public AsyncApiReference Reference { get; set; }
30+
public AsyncApiReference Reference { get => this.Schema.Reference; set => this.Schema.Reference = value; }
2431

2532
public void SerializeV2(IAsyncApiWriter writer)
2633
{

src/LEGO.AsyncAPI/Models/Avro/AvroArray.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public class AvroArray : AvroSchema
2020
/// </summary>
2121
public override IDictionary<string, AsyncApiAny> Metadata { get; set; } = new Dictionary<string, AsyncApiAny>();
2222

23-
public override void SerializeV2(IAsyncApiWriter writer)
23+
public override void SerializeV2WithoutReference(IAsyncApiWriter writer)
2424
{
2525
writer.WriteStartObject();
2626
writer.WriteOptionalProperty("type", this.Type);

src/LEGO.AsyncAPI/Models/Avro/AvroEnum.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public class AvroEnum : AvroSchema
4545
/// </summary>
4646
public override IDictionary<string, AsyncApiAny> Metadata { get; set; } = new Dictionary<string, AsyncApiAny>();
4747

48-
public override void SerializeV2(IAsyncApiWriter writer)
48+
public override void SerializeV2WithoutReference(IAsyncApiWriter writer)
4949
{
5050
writer.WriteStartObject();
5151
writer.WriteOptionalProperty("type", this.Type);

src/LEGO.AsyncAPI/Models/Avro/AvroField.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,23 @@ namespace LEGO.AsyncAPI.Models
44
{
55
using System.Collections.Generic;
66
using System.Linq;
7+
using LEGO.AsyncAPI.Attributes;
78
using LEGO.AsyncAPI.Models.Interfaces;
89
using LEGO.AsyncAPI.Writers;
910

11+
public enum AvroFieldOrder
12+
{
13+
None = 0,
14+
15+
[Display("ascending")]
16+
Ascending,
17+
18+
[Display("descending")]
19+
Descending,
20+
21+
[Display("ignore")]
22+
Ignore,
23+
}
1024
/// <summary>
1125
/// Represents a field within an Avro record schema.
1226
/// </summary>
@@ -35,7 +49,7 @@ public class AvroField : IAsyncApiSerializable
3549
/// <summary>
3650
/// The order of the field, can be 'ascending', 'descending', or 'ignore'.
3751
/// </summary>
38-
public string Order { get; set; }
52+
public AvroFieldOrder Order { get; set; }
3953

4054
/// <summary>
4155
/// Alternate names for this record (optional).
@@ -54,7 +68,11 @@ public void SerializeV2(IAsyncApiWriter writer)
5468
writer.WriteOptionalObject("type", this.Type, (w, s) => s.SerializeV2(w));
5569
writer.WriteOptionalProperty("doc", this.Doc);
5670
writer.WriteOptionalObject("default", this.Default, (w, s) => w.WriteAny(s));
57-
writer.WriteOptionalProperty("order", this.Order);
71+
if (this.Order != AvroFieldOrder.None)
72+
{
73+
writer.WriteOptionalProperty("order", this.Order.GetDisplayName());
74+
}
75+
5876
writer.WriteOptionalCollection("aliases", this.Aliases, (w, s) => w.WriteValue(s));
5977
if (this.Metadata.Any())
6078
{

src/LEGO.AsyncAPI/Models/Avro/AvroFixed.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public class AvroFixed : AvroSchema
3636
/// </summary>
3737
public override IDictionary<string, AsyncApiAny> Metadata { get; set; } = new Dictionary<string, AsyncApiAny>();
3838

39-
public override void SerializeV2(IAsyncApiWriter writer)
39+
public override void SerializeV2WithoutReference(IAsyncApiWriter writer)
4040
{
4141
writer.WriteStartObject();
4242
writer.WriteOptionalProperty("type", this.Type);

src/LEGO.AsyncAPI/Models/Avro/AvroMap.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class AvroMap : AvroSchema
1818
/// </summary>
1919
public override IDictionary<string, AsyncApiAny> Metadata { get; set; } = new Dictionary<string, AsyncApiAny>();
2020

21-
public override void SerializeV2(IAsyncApiWriter writer)
21+
public override void SerializeV2WithoutReference(IAsyncApiWriter writer)
2222
{
2323
writer.WriteStartObject();
2424
writer.WriteOptionalProperty("type", this.Type);

src/LEGO.AsyncAPI/Models/Avro/AvroPrimitive.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public AvroPrimitive()
2424
{
2525
}
2626

27-
public override void SerializeV2(IAsyncApiWriter writer)
27+
public override void SerializeV2WithoutReference(IAsyncApiWriter writer)
2828
{
2929
writer.WriteValue(this.Type);
3030
if (this.Metadata.Any())

src/LEGO.AsyncAPI/Models/Avro/AvroRecord.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class AvroRecord : AvroSchema
4040
/// </summary>
4141
public override IDictionary<string, AsyncApiAny> Metadata { get; set; } = new Dictionary<string, AsyncApiAny>();
4242

43-
public override void SerializeV2(IAsyncApiWriter writer)
43+
public override void SerializeV2WithoutReference(IAsyncApiWriter writer)
4444
{
4545
writer.WriteStartObject();
4646
writer.WriteOptionalProperty("type", this.Type);

src/LEGO.AsyncAPI/Models/Avro/AvroSchema.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
namespace LEGO.AsyncAPI.Models
44
{
5+
using System;
56
using System.Collections.Generic;
67
using LEGO.AsyncAPI.Models.Interfaces;
78
using LEGO.AsyncAPI.Writers;
89

9-
public abstract class AvroSchema : IAsyncApiSerializable
10+
public abstract class AvroSchema : IAsyncApiSerializable, IAsyncApiReferenceable
1011
{
1112
public abstract string Type { get; }
1213

@@ -15,11 +16,31 @@ public abstract class AvroSchema : IAsyncApiSerializable
1516
/// </summary>
1617
public abstract IDictionary<string, AsyncApiAny> Metadata { get; set; }
1718

19+
public bool UnresolvedReference { get; set; }
20+
21+
public AsyncApiReference Reference { get; set; }
22+
1823
public static implicit operator AvroSchema(AvroPrimitiveType type)
1924
{
2025
return new AvroPrimitive(type);
2126
}
2227

23-
public abstract void SerializeV2(IAsyncApiWriter writer);
28+
public void SerializeV2(IAsyncApiWriter writer)
29+
{
30+
if (writer is null)
31+
{
32+
throw new ArgumentNullException(nameof(writer));
33+
}
34+
35+
if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference))
36+
{
37+
this.Reference.SerializeV2(writer);
38+
return;
39+
}
40+
41+
this.SerializeV2WithoutReference(writer);
42+
}
43+
44+
public abstract void SerializeV2WithoutReference(IAsyncApiWriter writer);
2445
}
2546
}

src/LEGO.AsyncAPI/Models/Avro/AvroUnion.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public class AvroUnion : AvroSchema
2020
/// </summary>
2121
public override IDictionary<string, AsyncApiAny> Metadata { get; set; } = new Dictionary<string, AsyncApiAny>();
2222

23-
public override void SerializeV2(IAsyncApiWriter writer)
23+
public override void SerializeV2WithoutReference(IAsyncApiWriter writer)
2424
{
2525
writer.WriteStartArray();
2626
foreach (var type in this.Types)

test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public void AsyncApiMessage_WithNoSchemaFormat_DoesNotSerializeSchemaFormat()
129129
}
130130

131131
[Test]
132-
public void AsyncApiMessage_WithSchemaFormat_Serializes()
132+
public void AsyncApiMessage_WithJsonSchemaFormat_Serializes()
133133
{
134134
// Arrange
135135
var expected =
@@ -168,6 +168,78 @@ public void AsyncApiMessage_WithSchemaFormat_Serializes()
168168
message.Should().BeEquivalentTo(deserializedMessage);
169169
}
170170

171+
[Test]
172+
public void AsyncApiMessage_WithAvroSchemaFormat_Serializes()
173+
{
174+
// Arrange
175+
var expected =
176+
"""
177+
payload:
178+
type: record
179+
name: User
180+
namespace: com.example
181+
fields:
182+
- name: username
183+
type: string
184+
doc: The username of the user.
185+
default: guest
186+
order: ascending
187+
schemaFormat: application/vnd.apache.avro
188+
""";
189+
190+
var message = new AsyncApiMessage();
191+
message.SchemaFormat = "application/vnd.apache.avro";
192+
message.Payload = new AsyncApiAvroSchemaPayload()
193+
{
194+
Schema = new AvroRecord()
195+
{
196+
Name = "User",
197+
Namespace = "com.example",
198+
Fields = new List<AvroField>
199+
{
200+
new AvroField()
201+
{
202+
Name = "username",
203+
Type = AvroPrimitiveType.String,
204+
Doc = "The username of the user.",
205+
Default = new AsyncApiAny("guest"),
206+
Order = AvroFieldOrder.Ascending,
207+
},
208+
},
209+
},
210+
};
211+
212+
// Act
213+
var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0);
214+
var deserializedMessage = new AsyncApiStringReader().ReadFragment<AsyncApiMessage>(expected, AsyncApiVersion.AsyncApi2_0, out _);
215+
216+
// Assert
217+
actual.Should()
218+
.BePlatformAgnosticEquivalentTo(expected);
219+
message.Should().BeEquivalentTo(deserializedMessage);
220+
}
221+
222+
[Test]
223+
public void AsyncApiMessage_WithAvroAsReference_Deserializes()
224+
{
225+
// Arrange
226+
var input =
227+
"""
228+
schemaFormat: 'application/vnd.apache.avro+yaml;version=1.9.0'
229+
payload:
230+
$ref: 'path/to/user-create.avsc/#UserCreate'
231+
""";
232+
233+
// Act
234+
var deserializedMessage = new AsyncApiStringReader().ReadFragment<AsyncApiMessage>(input, AsyncApiVersion.AsyncApi2_0, out _);
235+
236+
// Assert
237+
deserializedMessage.Payload.Reference.Should().NotBeNull();
238+
deserializedMessage.Payload.Reference.IsExternal.Should().BeTrue();
239+
deserializedMessage.Payload.Reference.IsFragment.Should().BeTrue();
240+
241+
}
242+
171243
[Test]
172244
public void AsyncApiMessage_WithFilledObject_Serializes()
173245
{

test/LEGO.AsyncAPI.Tests/Models/AvroSchema_Should.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public void SerializeV2_SerializesCorrectly()
129129
Type = AvroPrimitiveType.String,
130130
Doc = "The username of the user.",
131131
Default = new AsyncApiAny("guest"),
132-
Order = "ascending",
132+
Order = AvroFieldOrder.Ascending,
133133
},
134134
new AvroField
135135
{
@@ -292,7 +292,7 @@ public void ReadFragment_DeserializesCorrectly()
292292
Type = AvroPrimitiveType.String,
293293
Doc = "The username of the user.",
294294
Default = new AsyncApiAny("guest"),
295-
Order = "ascending",
295+
Order = AvroFieldOrder.Ascending,
296296
},
297297
new AvroField
298298
{

0 commit comments

Comments
 (0)