Skip to content

Commit da8a261

Browse files
authored
fix: report invalid SQS/SNS policy values as diagnostics (#46)
1 parent eb68984 commit da8a261

6 files changed

Lines changed: 121 additions & 26 deletions

File tree

‎src/ByteBard.AsyncAPI.Bindings/Sns/Condition.cs‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ namespace ByteBard.AsyncAPI.Bindings.Sns;
44
using System.Collections.Generic;
55
using System.Linq;
66
using ByteBard.AsyncAPI.Models.Interfaces;
7+
using ByteBard.AsyncAPI.Readers.Exceptions;
78
using ByteBard.AsyncAPI.Readers.ParseNodes;
89
using ByteBard.AsyncAPI.Writers;
910

@@ -49,17 +50,21 @@ public static Condition Parse(ParseNode node)
4950
.ToDictionary(x => x.Key, x => x.Value)));
5051
break;
5152
default:
52-
throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " +
53-
$"AWS condition values should be one or more key value pairs.");
53+
throw new AsyncApiReaderException(
54+
$"An error occured while parsing a {nameof(Condition)} node. " +
55+
$"AWS condition values should be one or more key value pairs.",
56+
node.Context);
5457
}
5558
}
5659

5760
return new Condition(conditionValues);
5861
}
5962

6063
default:
61-
throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " +
62-
$"Node should contain a collection of condition types.");
64+
throw new AsyncApiReaderException(
65+
$"An error occured while parsing a {nameof(Condition)} node. " +
66+
$"Node should contain a collection of condition types.",
67+
node.Context);
6368
}
6469
}
6570
}

‎src/ByteBard.AsyncAPI.Bindings/Sns/Principal.cs‎

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace ByteBard.AsyncAPI.Bindings.Sns;
66
using System.Text.Json;
77
using System.Text.Json.Nodes;
88
using ByteBard.AsyncAPI.Models.Interfaces;
9+
using ByteBard.AsyncAPI.Readers.Exceptions;
910
using ByteBard.AsyncAPI.Readers.ParseNodes;
1011
using ByteBard.AsyncAPI.Writers;
1112

@@ -21,19 +22,23 @@ public static Principal Parse(ParseNode node)
2122
var nodeValue = node.GetScalarValue();
2223
if (!IsStarString(nodeValue))
2324
{
24-
throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
25-
$"Principal value without a property name can only be a string value of '*'.");
25+
throw new AsyncApiReaderException(
26+
$"An error occured while parsing a {nameof(Principal)} node. " +
27+
$"Principal value without a property name can only be a string value of '*'.",
28+
node.Context);
2629
}
2730

2831
return new PrincipalStar();
2932

3033
case MapNode mapNode:
3134
{
32-
var propertyNode = mapNode.First();
33-
if (!IsValidPrincipalProperty(propertyNode.Name))
35+
var propertyNode = mapNode.FirstOrDefault();
36+
if (propertyNode == null || !IsValidPrincipalProperty(propertyNode.Name))
3437
{
35-
throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
36-
$"Node should contain a valid AWS principal property name.");
38+
throw new AsyncApiReaderException(
39+
$"An error occured while parsing a {nameof(Principal)} node. " +
40+
$"Node should contain a valid AWS principal property name.",
41+
node.Context);
3742
}
3843

3944
var principalValue = new KeyValuePair<string, StringOrStringList>(
@@ -44,8 +49,10 @@ public static Principal Parse(ParseNode node)
4449
}
4550

4651
default:
47-
throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
48-
$"Node should contain a string value of '*' or a valid AWS principal property.");
52+
throw new AsyncApiReaderException(
53+
$"An error occured while parsing a {nameof(Principal)} node. " +
54+
$"Node should contain a string value of '*' or a valid AWS principal property.",
55+
node.Context);
4956
}
5057
}
5158

‎src/ByteBard.AsyncAPI.Bindings/Sqs/Condition.cs‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ namespace ByteBard.AsyncAPI.Bindings.Sqs;
44
using System.Collections.Generic;
55
using System.Linq;
66
using ByteBard.AsyncAPI.Models.Interfaces;
7+
using ByteBard.AsyncAPI.Readers.Exceptions;
78
using ByteBard.AsyncAPI.Readers.ParseNodes;
89
using ByteBard.AsyncAPI.Writers;
910

@@ -49,17 +50,21 @@ public static Condition Parse(ParseNode node)
4950
.ToDictionary(x => x.Key, x => x.Value)));
5051
break;
5152
default:
52-
throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " +
53-
$"AWS condition values should be one or more key value pairs.");
53+
throw new AsyncApiReaderException(
54+
$"An error occured while parsing a {nameof(Condition)} node. " +
55+
$"AWS condition values should be one or more key value pairs.",
56+
node.Context);
5457
}
5558
}
5659

5760
return new Condition(conditionValues);
5861
}
5962

6063
default:
61-
throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " +
62-
$"Node should contain a collection of AWS condition types.");
64+
throw new AsyncApiReaderException(
65+
$"An error occured while parsing a {nameof(Condition)} node. " +
66+
$"Node should contain a collection of AWS condition types.",
67+
node.Context);
6368
}
6469
}
6570
}

‎src/ByteBard.AsyncAPI.Bindings/Sqs/Principal.cs‎

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace ByteBard.AsyncAPI.Bindings.Sqs;
66
using System.Text.Json;
77
using System.Text.Json.Nodes;
88
using ByteBard.AsyncAPI.Models.Interfaces;
9+
using ByteBard.AsyncAPI.Readers.Exceptions;
910
using ByteBard.AsyncAPI.Readers.ParseNodes;
1011
using ByteBard.AsyncAPI.Writers;
1112

@@ -21,19 +22,23 @@ public static Principal Parse(ParseNode node)
2122
var nodeValue = node.GetScalarValue();
2223
if (!IsStarString(nodeValue))
2324
{
24-
throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
25-
$"Principal value without a property name can only be a string value of '*'.");
25+
throw new AsyncApiReaderException(
26+
$"An error occured while parsing a {nameof(Principal)} node. " +
27+
$"Principal value without a property name can only be a string value of '*'.",
28+
node.Context);
2629
}
2730

2831
return new PrincipalStar();
2932

3033
case MapNode mapNode:
3134
{
32-
var propertyNode = mapNode.First();
33-
if (!IsValidPrincipalProperty(propertyNode.Name))
35+
var propertyNode = mapNode.FirstOrDefault();
36+
if (propertyNode == null || !IsValidPrincipalProperty(propertyNode.Name))
3437
{
35-
throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
36-
$"Node should contain a valid AWS principal property name.");
38+
throw new AsyncApiReaderException(
39+
$"An error occured while parsing a {nameof(Principal)} node. " +
40+
$"Node should contain a valid AWS principal property name.",
41+
node.Context);
3742
}
3843

3944
var principalValue = new KeyValuePair<string, StringOrStringList>(
@@ -44,8 +49,10 @@ public static Principal Parse(ParseNode node)
4449
}
4550

4651
default:
47-
throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
48-
$"Node should contain a string value of '*' or a valid AWS principal property.");
52+
throw new AsyncApiReaderException(
53+
$"An error occured while parsing a {nameof(Principal)} node. " +
54+
$"Node should contain a string value of '*' or a valid AWS principal property.",
55+
node.Context);
4956
}
5057
}
5158

‎src/ByteBard.AsyncAPI.Bindings/StringOrStringList.cs‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace ByteBard.AsyncAPI.Bindings
66
using System.Text.Json.Nodes;
77
using ByteBard.AsyncAPI.Models;
88
using ByteBard.AsyncAPI.Models.Interfaces;
9+
using ByteBard.AsyncAPI.Readers.Exceptions;
910
using ByteBard.AsyncAPI.Readers.ParseNodes;
1011

1112
public class StringOrStringList : IAsyncApiElement
@@ -40,8 +41,10 @@ public static StringOrStringList Parse(ParseNode node)
4041
}
4142

4243
default:
43-
throw new ArgumentException($"An error occured while parsing a {nameof(StringOrStringList)} node. " +
44-
$"Node should contain a string value or a list of strings.");
44+
throw new AsyncApiReaderException(
45+
$"An error occured while parsing a {nameof(StringOrStringList)} node. " +
46+
$"Node should contain a string value or a list of strings.",
47+
node.Context);
4548
}
4649
}
4750

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
namespace ByteBard.AsyncAPI.Tests.Bindings
2+
{
3+
using FluentAssertions;
4+
using ByteBard.AsyncAPI.Models;
5+
using ByteBard.AsyncAPI.Readers;
6+
using NUnit.Framework;
7+
using BindingsCollection = ByteBard.AsyncAPI.Bindings.BindingsCollection;
8+
9+
internal class AwsPolicyParsing_Should : TestBase
10+
{
11+
[TestCase("action:\n foo: bar", "StringOrStringList")]
12+
[TestCase("principal: someone", "Principal")]
13+
[TestCase("principal:\n Unknown: arn", "Principal")]
14+
[TestCase("principal: {}", "Principal")]
15+
[TestCase("principal:\n - '*'", "Principal")]
16+
[TestCase("condition: invalid", "Condition")]
17+
[TestCase("condition:\n StringEquals: invalid", "Condition")]
18+
public void SqsChannelBinding_WithInvalidPolicyStatement_ReportsDiagnosticInsteadOfThrowing(string statementField, string expectedType)
19+
{
20+
var yaml =
21+
"bindings:\n" +
22+
" sqs:\n" +
23+
" queue:\n" +
24+
" name: myQueue\n" +
25+
" policy:\n" +
26+
" statements:\n" +
27+
" - effect: Allow\n" +
28+
Indent(statementField, 12);
29+
30+
var settings = new AsyncApiReaderSettings { Bindings = BindingsCollection.Sqs };
31+
32+
AsyncApiDiagnostic diagnostic = null;
33+
var act = () => new AsyncApiStringReader(settings).ReadFragment<AsyncApiChannel>(yaml, AsyncApiVersion.AsyncApi2_0, out diagnostic);
34+
35+
act.Should().NotThrow();
36+
diagnostic.Errors.Should().ContainSingle().Which.Message.Should().Contain(expectedType);
37+
}
38+
39+
[TestCase("action:\n foo: bar", "StringOrStringList")]
40+
[TestCase("principal: someone", "Principal")]
41+
[TestCase("condition: invalid", "Condition")]
42+
public void SnsChannelBinding_WithInvalidPolicyStatement_ReportsDiagnosticInsteadOfThrowing(string statementField, string expectedType)
43+
{
44+
var yaml =
45+
"bindings:\n" +
46+
" sns:\n" +
47+
" name: myTopic\n" +
48+
" policy:\n" +
49+
" statements:\n" +
50+
" - effect: Allow\n" +
51+
Indent(statementField, 10);
52+
53+
var settings = new AsyncApiReaderSettings { Bindings = BindingsCollection.Sns };
54+
55+
AsyncApiDiagnostic diagnostic = null;
56+
var act = () => new AsyncApiStringReader(settings).ReadFragment<AsyncApiChannel>(yaml, AsyncApiVersion.AsyncApi2_0, out diagnostic);
57+
58+
act.Should().NotThrow();
59+
diagnostic.Errors.Should().ContainSingle().Which.Message.Should().Contain(expectedType);
60+
}
61+
62+
private static string Indent(string value, int spaces)
63+
{
64+
var prefix = new string(' ', spaces);
65+
return prefix + value.Replace("\n", "\n" + prefix) + "\n";
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)