Skip to content

Commit a3c572b

Browse files
authored
fix: stop flagging binding extensions as missing bindings (#44)
1 parent 2a590ee commit a3c572b

10 files changed

Lines changed: 282 additions & 336 deletions

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

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
namespace ByteBard.AsyncAPI.Readers
1+
namespace ByteBard.AsyncAPI.Readers
22
{
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using ByteBard.AsyncAPI.Exceptions;
36
using ByteBard.AsyncAPI.Extensions;
7+
using ByteBard.AsyncAPI.Models;
48
using ByteBard.AsyncAPI.Models.Interfaces;
9+
using ByteBard.AsyncAPI.Readers.Interface;
510
using ByteBard.AsyncAPI.Readers.ParseNodes;
611

712
public class BindingDeserializer
@@ -17,6 +22,52 @@ public static T LoadBinding<T>(string nodeName, ParseNode node, FixedFieldMap<T>
1722
return binding;
1823
}
1924

25+
internal static AsyncApiBindings<TBinding> LoadBindings<TBinding>(
26+
MapNode mapNode,
27+
IDictionary<string, IBindingParser<TBinding>> parsers,
28+
PatternFieldMap<AsyncApiBindings<TBinding>> patternFields)
29+
where TBinding : IBinding
30+
{
31+
var context = mapNode.Context;
32+
var bindings = new AsyncApiBindings<TBinding>();
33+
foreach (var property in mapNode)
34+
{
35+
if (patternFields.Keys.Any(isMatch => isMatch(property.Name)))
36+
{
37+
continue;
38+
}
39+
40+
if (!parsers.TryGetValue(property.Name, out var parser))
41+
{
42+
// TBinding is the binding interface (e.g. IChannelBinding), so drop the leading "I" for the diagnostic.
43+
var bindingType = typeof(TBinding).Name.Substring(1);
44+
45+
// Reported as an error because the binding is dropped from the model and cannot be written back.
46+
context.Diagnostic.Errors.Add(
47+
new AsyncApiError(context.GetLocation(), $"{bindingType} {property.Name} is not found"));
48+
continue;
49+
}
50+
51+
try
52+
{
53+
var binding = parser.LoadBinding(property);
54+
if (binding != null)
55+
{
56+
bindings.Add(binding);
57+
}
58+
}
59+
catch (AsyncApiException ex)
60+
{
61+
ex.Pointer = context.GetLocation();
62+
context.Diagnostic.Errors.Add(new AsyncApiError(ex));
63+
}
64+
}
65+
66+
mapNode.ParseFields(bindings, patternFields);
67+
68+
return bindings;
69+
}
70+
2071
private static PatternFieldMap<T> BindingPatternExtensionFields<T>()
2172
where T : IBinding, new()
2273
{
Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1-
namespace ByteBard.AsyncAPI.Readers
1+
namespace ByteBard.AsyncAPI.Readers
22
{
3-
using ByteBard.AsyncAPI.Exceptions;
3+
using ByteBard.AsyncAPI.Extensions;
44
using ByteBard.AsyncAPI.Models;
55
using ByteBard.AsyncAPI.Models.Interfaces;
66
using ByteBard.AsyncAPI.Readers.ParseNodes;
77

88
internal static partial class AsyncApiV2Deserializer
99
{
10+
private static readonly PatternFieldMap<AsyncApiBindings<IChannelBinding>> channelBindingPatternFields =
11+
new()
12+
{
13+
{ s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n)) },
14+
};
15+
1016
internal static AsyncApiBindings<IChannelBinding> LoadChannelBindings(ParseNode node)
1117
{
1218
var mapNode = node.CheckMapNode("channelBindings");
@@ -16,42 +22,7 @@ internal static AsyncApiBindings<IChannelBinding> LoadChannelBindings(ParseNode
1622
return new AsyncApiBindingsReference<IChannelBinding>(pointer);
1723
}
1824

19-
var channelBindings = new AsyncApiBindings<IChannelBinding>();
20-
foreach (var property in mapNode)
21-
{
22-
var channelBinding = LoadChannelBinding(property);
23-
24-
if (channelBinding != null)
25-
{
26-
channelBindings.Add(channelBinding);
27-
}
28-
else
29-
{
30-
mapNode.Context.Diagnostic.Errors.Add(
31-
new AsyncApiError(node.Context.GetLocation(), $"ChannelBinding {property.Name} is not found"));
32-
}
33-
}
34-
35-
return channelBindings;
36-
}
37-
38-
private static IChannelBinding LoadChannelBinding(ParseNode node)
39-
{
40-
var property = node as PropertyNode;
41-
try
42-
{
43-
if (node.Context.ChannelBindingParsers.TryGetValue(property.Name, out var parser))
44-
{
45-
return parser.LoadBinding(property);
46-
}
47-
}
48-
catch (AsyncApiException ex)
49-
{
50-
ex.Pointer = node.Context.GetLocation();
51-
node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex));
52-
}
53-
54-
return null;
25+
return BindingDeserializer.LoadBindings(mapNode, node.Context.ChannelBindingParsers, channelBindingPatternFields);
5526
}
5627
}
5728
}
Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1-
namespace ByteBard.AsyncAPI.Readers
1+
namespace ByteBard.AsyncAPI.Readers
22
{
3-
using ByteBard.AsyncAPI.Exceptions;
3+
using ByteBard.AsyncAPI.Extensions;
44
using ByteBard.AsyncAPI.Models;
55
using ByteBard.AsyncAPI.Models.Interfaces;
66
using ByteBard.AsyncAPI.Readers.ParseNodes;
77

88
internal static partial class AsyncApiV2Deserializer
99
{
10+
private static readonly PatternFieldMap<AsyncApiBindings<IMessageBinding>> messageBindingPatternFields =
11+
new()
12+
{
13+
{ s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n)) },
14+
};
15+
1016
internal static AsyncApiBindings<IMessageBinding> LoadMessageBindings(ParseNode node)
1117
{
1218
var mapNode = node.CheckMapNode("messageBindings");
@@ -16,42 +22,7 @@ internal static AsyncApiBindings<IMessageBinding> LoadMessageBindings(ParseNode
1622
return new AsyncApiBindingsReference<IMessageBinding>(pointer);
1723
}
1824

19-
var messageBindings = new AsyncApiBindings<IMessageBinding>();
20-
foreach (var property in mapNode)
21-
{
22-
var messageBinding = LoadMessageBinding(property);
23-
24-
if (messageBinding != null)
25-
{
26-
messageBindings.Add(messageBinding);
27-
}
28-
else
29-
{
30-
mapNode.Context.Diagnostic.Errors.Add(
31-
new AsyncApiError(node.Context.GetLocation(), $"MessageBinding {property.Name} is not found"));
32-
}
33-
}
34-
35-
return messageBindings;
36-
}
37-
38-
internal static IMessageBinding LoadMessageBinding(ParseNode node)
39-
{
40-
var property = node as PropertyNode;
41-
try
42-
{
43-
if (node.Context.MessageBindingParsers.TryGetValue(property.Name, out var parser))
44-
{
45-
return parser.LoadBinding(property);
46-
}
47-
}
48-
catch (AsyncApiException ex)
49-
{
50-
ex.Pointer = node.Context.GetLocation();
51-
node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex));
52-
}
53-
54-
return null;
25+
return BindingDeserializer.LoadBindings(mapNode, node.Context.MessageBindingParsers, messageBindingPatternFields);
5526
}
5627
}
5728
}
Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1-
namespace ByteBard.AsyncAPI.Readers
1+
namespace ByteBard.AsyncAPI.Readers
22
{
3-
using ByteBard.AsyncAPI.Exceptions;
3+
using ByteBard.AsyncAPI.Extensions;
44
using ByteBard.AsyncAPI.Models;
55
using ByteBard.AsyncAPI.Models.Interfaces;
66
using ByteBard.AsyncAPI.Readers.ParseNodes;
77

88
internal static partial class AsyncApiV2Deserializer
99
{
10+
private static readonly PatternFieldMap<AsyncApiBindings<IOperationBinding>> operationBindingPatternFields =
11+
new()
12+
{
13+
{ s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n)) },
14+
};
15+
1016
internal static AsyncApiBindings<IOperationBinding> LoadOperationBindings(ParseNode node)
1117
{
1218
var mapNode = node.CheckMapNode("operationBindings");
@@ -16,42 +22,7 @@ internal static AsyncApiBindings<IOperationBinding> LoadOperationBindings(ParseN
1622
return new AsyncApiBindingsReference<IOperationBinding>(pointer);
1723
}
1824

19-
var operationBindings = new AsyncApiBindings<IOperationBinding>();
20-
foreach (var property in mapNode)
21-
{
22-
var operationBinding = LoadOperationBinding(property);
23-
24-
if (operationBinding != null)
25-
{
26-
operationBindings.Add(operationBinding);
27-
}
28-
else
29-
{
30-
mapNode.Context.Diagnostic.Errors.Add(
31-
new AsyncApiError(node.Context.GetLocation(), $"OperationBinding {property.Name} is not found"));
32-
}
33-
}
34-
35-
return operationBindings;
36-
}
37-
38-
internal static IOperationBinding LoadOperationBinding(ParseNode node)
39-
{
40-
var property = node as PropertyNode;
41-
try
42-
{
43-
if (node.Context.OperationBindingParsers.TryGetValue(property.Name, out var parser))
44-
{
45-
return parser.LoadBinding(property);
46-
}
47-
}
48-
catch (AsyncApiException ex)
49-
{
50-
ex.Pointer = node.Context.GetLocation();
51-
node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex));
52-
}
53-
54-
return null;
25+
return BindingDeserializer.LoadBindings(mapNode, node.Context.OperationBindingParsers, operationBindingPatternFields);
5526
}
5627
}
5728
}
Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1-
namespace ByteBard.AsyncAPI.Readers
1+
namespace ByteBard.AsyncAPI.Readers
22
{
3-
using ByteBard.AsyncAPI.Exceptions;
3+
using ByteBard.AsyncAPI.Extensions;
44
using ByteBard.AsyncAPI.Models;
55
using ByteBard.AsyncAPI.Models.Interfaces;
66
using ByteBard.AsyncAPI.Readers.ParseNodes;
77

88
internal static partial class AsyncApiV2Deserializer
99
{
10+
private static readonly PatternFieldMap<AsyncApiBindings<IServerBinding>> serverBindingPatternFields =
11+
new()
12+
{
13+
{ s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n)) },
14+
};
15+
1016
internal static AsyncApiBindings<IServerBinding> LoadServerBindings(ParseNode node)
1117
{
1218
var mapNode = node.CheckMapNode("serverBindings");
@@ -16,42 +22,7 @@ internal static AsyncApiBindings<IServerBinding> LoadServerBindings(ParseNode no
1622
return new AsyncApiBindingsReference<IServerBinding>(pointer);
1723
}
1824

19-
var serverBindings = new AsyncApiBindings<IServerBinding>();
20-
foreach (var property in mapNode)
21-
{
22-
var serverBinding = LoadServerBinding(property);
23-
24-
if (serverBinding != null)
25-
{
26-
serverBindings.Add(serverBinding);
27-
}
28-
else
29-
{
30-
mapNode.Context.Diagnostic.Errors.Add(
31-
new AsyncApiError(node.Context.GetLocation(), $"ServerBinding {property.Name} is not found"));
32-
}
33-
}
34-
35-
return serverBindings;
36-
}
37-
38-
internal static IServerBinding LoadServerBinding(ParseNode node)
39-
{
40-
var property = node as PropertyNode;
41-
try
42-
{
43-
if (node.Context.ServerBindingParsers.TryGetValue(property.Name, out var parser))
44-
{
45-
return parser.LoadBinding(property);
46-
}
47-
}
48-
catch (AsyncApiException ex)
49-
{
50-
ex.Pointer = node.Context.GetLocation();
51-
node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex));
52-
}
53-
54-
return null;
25+
return BindingDeserializer.LoadBindings(mapNode, node.Context.ServerBindingParsers, serverBindingPatternFields);
5526
}
5627
}
5728
}
Lines changed: 8 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
namespace ByteBard.AsyncAPI.Readers
1+
namespace ByteBard.AsyncAPI.Readers
22
{
3-
using ByteBard.AsyncAPI.Exceptions;
43
using ByteBard.AsyncAPI.Extensions;
54
using ByteBard.AsyncAPI.Models;
65
using ByteBard.AsyncAPI.Models.Interfaces;
76
using ByteBard.AsyncAPI.Readers.ParseNodes;
87

98
internal static partial class AsyncApiV3Deserializer
109
{
10+
private static readonly PatternFieldMap<AsyncApiBindings<IChannelBinding>> channelBindingPatternFields =
11+
new()
12+
{
13+
{ s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n)) },
14+
};
15+
1116
internal static AsyncApiBindings<IChannelBinding> LoadChannelBindings(ParseNode node)
1217
{
1318
var mapNode = node.CheckMapNode("channelBindings");
@@ -17,50 +22,7 @@ internal static AsyncApiBindings<IChannelBinding> LoadChannelBindings(ParseNode
1722
return new AsyncApiBindingsReference<IChannelBinding>(pointer);
1823
}
1924

20-
var channelBindings = new AsyncApiBindings<IChannelBinding>();
21-
foreach (var property in mapNode)
22-
{
23-
var channelBinding = LoadChannelBinding(property);
24-
25-
if (channelBinding != null)
26-
{
27-
channelBindings.Add(channelBinding);
28-
}
29-
else
30-
{
31-
mapNode.Context.Diagnostic.Errors.Add(
32-
new AsyncApiError(node.Context.GetLocation(), $"ChannelBinding {property.Name} is not found"));
33-
}
34-
}
35-
36-
mapNode.ParseFields(channelBindings, channelBindingPatternFields);
37-
38-
return channelBindings;
39-
}
40-
41-
private static readonly PatternFieldMap<AsyncApiBindings<IChannelBinding>> channelBindingPatternFields =
42-
new()
43-
{
44-
{ s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n)) },
45-
};
46-
47-
private static IChannelBinding LoadChannelBinding(ParseNode node)
48-
{
49-
var property = node as PropertyNode;
50-
try
51-
{
52-
if (node.Context.ChannelBindingParsers.TryGetValue(property.Name, out var parser))
53-
{
54-
return parser.LoadBinding(property);
55-
}
56-
}
57-
catch (AsyncApiException ex)
58-
{
59-
ex.Pointer = node.Context.GetLocation();
60-
node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex));
61-
}
62-
63-
return null;
25+
return BindingDeserializer.LoadBindings(mapNode, node.Context.ChannelBindingParsers, channelBindingPatternFields);
6426
}
6527
}
6628
}

0 commit comments

Comments
 (0)