From eef9cb95d03ff1165799ce50ba84b0f0b19e0564 Mon Sep 17 00:00:00 2001 From: "adam.gloyne" Date: Mon, 1 Jul 2024 09:57:31 +0100 Subject: [PATCH 1/6] feat: extend AWS policy --- .../Sns/SnsChannelBinding.cs | 4 +- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 24 ++++-- .../Sqs/SqsChannelBinding.cs | 4 +- .../Sqs/SqsOperationBinding.cs | 4 +- src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs | 25 ++++-- .../Bindings/Sns/SnsBindings_Should.cs | 42 ++++++++-- .../Bindings/Sqs/SqsBindings_should.cs | 82 ++++++++++++++----- 7 files changed, 144 insertions(+), 41 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 13676168..0328068b 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -57,8 +57,10 @@ public class SnsChannelBinding : ChannelBinding private static FixedFieldMap statementFixedFields = new() { { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "principal", (a, n) => { a.Principal = StringOrStringList.Parse(n); } }, + { "principal", (a, n) => { a.Principal = n.CreateAny(); } }, { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, + { "resource", (a, n) => { a.Resource = StringOrStringList.Parse(n); } }, + { "condition", (a, n) => { a.Condition = n.CreateAny(); } }, }; /// diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index 7f3771f0..4a15f0fb 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -1,28 +1,40 @@ // Copyright (c) The LEGO Group. All rights reserved. - namespace LEGO.AsyncAPI.Bindings.Sns { using System; using System.Collections.Generic; using LEGO.AsyncAPI.Attributes; + using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; public class Statement : IAsyncApiExtensible { + /// + /// Indicates whether the policy allows or denies access. + /// public Effect Effect { get; set; } /// - /// The AWS account or resource ARN that this statement applies to. + /// The AWS account(s) or resource ARN(s) that this statement applies to. /// - // public StringOrStringList Principal { get; set; } - public StringOrStringList Principal { get; set; } + public AsyncApiAny Principal { get; set; } /// /// The SNS permission being allowed or denied e.g. sns:Publish /// public StringOrStringList Action { get; set; } + /// + /// The resource(s) that this policy applies to. + /// + public StringOrStringList? Resource { get; set; } + + /// + /// Specific circumstances under which the policy grants permission. + /// + public AsyncApiAny? Condition { get; set; } + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) @@ -34,8 +46,10 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); - writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Write(w)); + writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Write(w)); writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w)); + writer.WriteOptionalObject("resource", this.Resource, (w, t) => t?.Value.Write(w)); + writer.WriteOptionalObject("condition", this.Condition, (w, t) => t?.Write(w)); writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs index 6f98da99..3670c0c5 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs @@ -64,8 +64,10 @@ public class SqsChannelBinding : ChannelBinding private static FixedFieldMap statementFixedFields = new() { { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "principal", (a, n) => { a.Principal = StringOrStringList.Parse(n); } }, + { "principal", (a, n) => { a.Principal = n.CreateAny(); } }, { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, + { "resource", (a, n) => { a.Resource = StringOrStringList.Parse(n); } }, + { "condition", (a, n) => { a.Condition = n.CreateAny(); } }, }; public override void SerializeProperties(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs index d8eb43dd..9aaf391a 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs @@ -56,8 +56,10 @@ public class SqsOperationBinding : OperationBinding private static FixedFieldMap statementFixedFields = new() { { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "principal", (a, n) => { a.Principal = StringOrStringList.Parse(n); } }, + { "principal", (a, n) => { a.Principal = n.CreateAny(); } }, { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, + { "resource", (a, n) => { a.Resource = StringOrStringList.Parse(n); } }, + { "condition", (a, n) => { a.Condition = n.CreateAny(); } }, }; public override void SerializeProperties(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs index 9518d2d4..1c015b32 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs @@ -5,24 +5,37 @@ namespace LEGO.AsyncAPI.Bindings.Sqs using System; using System.Collections.Generic; using LEGO.AsyncAPI.Attributes; + using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; public class Statement : IAsyncApiExtensible { + /// + /// Indicates whether the policy allows or denies access. + /// public Effect Effect { get; set; } /// - /// The AWS account or resource ARN that this statement applies to. + /// The AWS account(s) or resource ARN(s) that this statement applies to. /// - // public StringOrStringList Principal { get; set; } - public StringOrStringList Principal { get; set; } + public AsyncApiAny Principal { get; set; } /// - /// The SNS permission being allowed or denied e.g. sns:Publish + /// The SNS permission being allowed or denied e.g. sns:Publish. /// public StringOrStringList Action { get; set; } + /// + /// The resource(s) that this policy applies to. + /// + public StringOrStringList? Resource { get; set; } + + /// + /// Specific circumstances under which the policy grants permission. + /// + public AsyncApiAny? Condition { get; set; } + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) @@ -34,8 +47,10 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); - writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Write(w)); + writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Write(w)); writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w)); + writer.WriteOptionalObject("resource", this.Resource, (w, t) => t?.Value.Write(w)); + writer.WriteOptionalObject("condition", this.Condition, (w, t) => t?.Write(w)); writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 6d4f5779..db03847e 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -31,15 +31,24 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() policy: statements: - effect: Deny - principal: arn:aws:iam::123456789012:user/alex.wichmann + principal: '*' action: - sns:Publish - sns:Delete + condition: + StringEquals: + aws:username: + - johndoe + - mrsmith - effect: Allow principal: - - arn:aws:iam::123456789012:user/alex.wichmann - - arn:aws:iam::123456789012:user/dec.kolakowski + AWS: + - arn:aws:iam::123456789012:user/alex.wichmann + - arn:aws:iam::123456789012:user/dec.kolakowski action: sns:Create + condition: + NumericLessThanEquals: + aws:MultiFactorAuthAge: '3600' x-statementExtension: statementXPropertyName: statementXPropertyValue x-policyExtension: @@ -77,22 +86,39 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), + Principal = new AsyncApiAny("*"), Action = new StringOrStringList(new AsyncApiAny(new List() { "sns:Publish", "sns:Delete", })), + Condition = new AsyncApiAny(new Dictionary() + { + { + "StringEquals", new Dictionary>() + { + { "aws:username", new List() { "johndoe", "mrsmith" } }, + } + }, + }), }, new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList(new AsyncApiAny(new List() + Principal = new AsyncApiAny(new Dictionary>() { - "arn:aws:iam::123456789012:user/alex.wichmann", - "arn:aws:iam::123456789012:user/dec.kolakowski", - })), + { "AWS", new List() { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" } }, + }), Action = new StringOrStringList(new AsyncApiAny("sns:Create")), + Condition = new AsyncApiAny(new Dictionary() + { + { + "NumericLessThanEquals", new Dictionary() + { + { "aws:MultiFactorAuthAge", "3600" }, + } + }, + }), Extensions = new Dictionary() { { diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs index c031621c..cfc52ac8 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -42,17 +42,27 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() policy: statements: - effect: deny - principal: arn:aws:iam::123456789012:user/alex.wichmann + principal: + AWS: arn:aws:iam::123456789012:user/alex.wichmann action: - sqs:SendMessage - sqs:ReceiveMessage + condition: + StringEquals: + aws:username: + - johndoe + - mrsmith x-statementExtension: statementXPropertyName: statementXPropertyValue - effect: allow principal: - - arn:aws:iam::123456789012:user/alex.wichmann - - arn:aws:iam::123456789012:user/dec.kolakowski + AWS: + - arn:aws:iam::123456789012:user/alex.wichmann + - arn:aws:iam::123456789012:user/dec.kolakowski action: sqs:CreateQueue + condition: + NumericLessThanEquals: + aws:MultiFactorAuthAge: '3600' x-policyExtension: policyXPropertyName: policyXPropertyValue tags: @@ -69,7 +79,8 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() policy: statements: - effect: allow - principal: arn:aws:iam::123456789012:user/alex.wichmann + principal: + AWS: arn:aws:iam::123456789012:user/alex.wichmann action: - sqs:* x-internalObject: @@ -124,12 +135,24 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), + Principal = new AsyncApiAny(new Dictionary() + { + { "AWS", "arn:aws:iam::123456789012:user/alex.wichmann" }, + }), Action = new StringOrStringList(new AsyncApiAny(new List { "sqs:SendMessage", "sqs:ReceiveMessage", })), + Condition = new AsyncApiAny(new Dictionary() + { + { + "StringEquals", new Dictionary>() + { + { "aws:username", new List() { "johndoe", "mrsmith" } }, + } + }, + }), Extensions = new Dictionary() { { @@ -144,12 +167,20 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList(new AsyncApiAny(new List + Principal = new AsyncApiAny(new Dictionary>() { - "arn:aws:iam::123456789012:user/alex.wichmann", - "arn:aws:iam::123456789012:user/dec.kolakowski", - })), + { "AWS", new List() { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" } }, + }), Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), + Condition = new AsyncApiAny(new Dictionary() + { + { + "NumericLessThanEquals", new Dictionary() + { + { "aws:MultiFactorAuthAge", "3600" }, + } + }, + }), }, }, Extensions = new Dictionary() @@ -194,7 +225,10 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), + Principal = new AsyncApiAny(new Dictionary() + { + { "AWS", "arn:aws:iam::123456789012:user/alex.wichmann" }, + }), Action = new StringOrStringList(new AsyncApiAny(new List { "sqs:*", @@ -254,7 +288,8 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() policy: statements: - effect: deny - principal: arn:aws:iam::123456789012:user/alex.wichmann + principal: + AWS: arn:aws:iam::123456789012:user/alex.wichmann action: - sqs:SendMessage - sqs:ReceiveMessage @@ -262,8 +297,9 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() statementXPropertyName: statementXPropertyValue - effect: allow principal: - - arn:aws:iam::123456789012:user/alex.wichmann - - arn:aws:iam::123456789012:user/dec.kolakowski + AWS: + - arn:aws:iam::123456789012:user/alex.wichmann + - arn:aws:iam::123456789012:user/dec.kolakowski action: sqs:CreateQueue x-policyExtension: policyXPropertyName: policyXPropertyValue @@ -280,7 +316,8 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() policy: statements: - effect: allow - principal: arn:aws:iam::123456789012:user/alex.wichmann + principal: + AWS: arn:aws:iam::123456789012:user/alex.wichmann action: - sqs:* x-queueExtension: @@ -339,7 +376,10 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), + Principal = new AsyncApiAny(new Dictionary() + { + { "AWS", "arn:aws:iam::123456789012:user/alex.wichmann" }, + }), Action = new StringOrStringList(new AsyncApiAny(new List() { "sqs:SendMessage", @@ -359,11 +399,10 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList(new AsyncApiAny(new List + Principal = new AsyncApiAny(new Dictionary>() { - "arn:aws:iam::123456789012:user/alex.wichmann", - "arn:aws:iam::123456789012:user/dec.kolakowski", - })), + { "AWS", new List() { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" } }, + }), Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), }, }, @@ -409,7 +448,10 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), + Principal = new AsyncApiAny(new Dictionary() + { + { "AWS", "arn:aws:iam::123456789012:user/alex.wichmann" }, + }), Action = new StringOrStringList(new AsyncApiAny(new List { "sqs:*", From c20ad8d6cdcf3c8cefaea7e9bdc333512312b67b Mon Sep 17 00:00:00 2001 From: "adam.gloyne" Date: Tue, 23 Jul 2024 13:05:55 +0100 Subject: [PATCH 2/6] feat: implement principal parse logic --- src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs | 66 +++++++++++++++++++ .../Sns/SnsChannelBinding.cs | 2 +- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 6 +- src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs | 66 +++++++++++++++++++ .../Sqs/SqsChannelBinding.cs | 2 +- .../Sqs/SqsOperationBinding.cs | 2 +- src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs | 4 +- .../StringOrStringList.cs | 4 +- .../Bindings/Sns/SnsBindings_Should.cs | 21 ++++-- .../Bindings/Sqs/SqsBindings_should.cs | 48 +++++++++----- 10 files changed, 186 insertions(+), 35 deletions(-) create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs new file mode 100644 index 00000000..0316068e --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs @@ -0,0 +1,66 @@ +namespace LEGO.AsyncAPI.Bindings.Sns; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; +using LEGO.AsyncAPI.Models; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Readers.ParseNodes; + +public class Principal : IAsyncApiElement +{ + public Principal(AsyncApiAny value) + { + this.Value = value; + } + + public AsyncApiAny Value { get; } + + public static Principal Parse(ParseNode node) + { + switch (node) + { + case ValueNode: + var nodeValue = node.GetScalarValue(); + if (!IsStarString(nodeValue)) + { + throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + + $"Principal value without a property name can only be a string value of '*'."); + } + + return new Principal(new AsyncApiAny(nodeValue)); + case MapNode mapNode: + { + var propertyNode = mapNode.First(); + if (!IsValidPrincipalProperty(propertyNode.Name)) + { + throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + + $"Node should contain a valid AWS principal property name."); + } + + var parsedObject = new Dictionary() + { { propertyNode.Name, StringOrStringList.Parse(propertyNode.Value).Value } }; + + return new Principal(new AsyncApiAny(parsedObject)); + } + + default: + throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + + $"Node should contain a string value of '*' or a valid AWS principal property."); + } + } + + private static bool IsStarString(JsonNode value) + { + var element = JsonDocument.Parse(value.ToJsonString()).RootElement; + + return element.ValueKind == JsonValueKind.String && element.ValueEquals("*"); + } + + private static bool IsValidPrincipalProperty(string property) + { + return new[] { "AWS", "Service" }.Contains(property); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 0328068b..4d8668c9 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -57,7 +57,7 @@ public class SnsChannelBinding : ChannelBinding private static FixedFieldMap statementFixedFields = new() { { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "principal", (a, n) => { a.Principal = n.CreateAny(); } }, + { "principal", (a, n) => { a.Principal = Principal.Parse(n); } }, { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, { "resource", (a, n) => { a.Resource = StringOrStringList.Parse(n); } }, { "condition", (a, n) => { a.Condition = n.CreateAny(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index 4a15f0fb..2bccbb97 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -18,10 +18,10 @@ public class Statement : IAsyncApiExtensible /// /// The AWS account(s) or resource ARN(s) that this statement applies to. /// - public AsyncApiAny Principal { get; set; } + public Principal Principal { get; set; } /// - /// The SNS permission being allowed or denied e.g. sns:Publish + /// The SNS permission being allowed or denied e.g. sns:Publish. /// public StringOrStringList Action { get; set; } @@ -46,7 +46,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); - writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Write(w)); + writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Write(w)); writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w)); writer.WriteOptionalObject("resource", this.Resource, (w, t) => t?.Value.Write(w)); writer.WriteOptionalObject("condition", this.Condition, (w, t) => t?.Write(w)); diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs new file mode 100644 index 00000000..5a67b588 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs @@ -0,0 +1,66 @@ +namespace LEGO.AsyncAPI.Bindings.Sqs; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; +using LEGO.AsyncAPI.Models; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Readers.ParseNodes; + +public class Principal : IAsyncApiElement +{ + public Principal(AsyncApiAny value) + { + this.Value = value; + } + + public AsyncApiAny Value { get; } + + public static Principal Parse(ParseNode node) + { + switch (node) + { + case ValueNode: + var nodeValue = node.GetScalarValue(); + if (!IsStarString(nodeValue)) + { + throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + + $"Principal value without a property name can only be a string value of '*'."); + } + + return new Principal(new AsyncApiAny(nodeValue)); + case MapNode mapNode: + { + var propertyNode = mapNode.First(); + if (!IsValidPrincipalProperty(propertyNode.Name)) + { + throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + + $"Node should contain a valid AWS principal property name."); + } + + var parsedObject = new Dictionary() + { { propertyNode.Name, StringOrStringList.Parse(propertyNode.Value).Value } }; + + return new Principal(new AsyncApiAny(parsedObject)); + } + + default: + throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + + $"Node should contain a string value of '*' or a valid AWS principal property."); + } + } + + private static bool IsStarString(JsonNode value) + { + var element = JsonDocument.Parse(value.ToJsonString()).RootElement; + + return element.ValueKind == JsonValueKind.String && element.ValueEquals("*"); + } + + private static bool IsValidPrincipalProperty(string property) + { + return new[] { "AWS", "Service" }.Contains(property); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs index 3670c0c5..f0b24be7 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs @@ -64,7 +64,7 @@ public class SqsChannelBinding : ChannelBinding private static FixedFieldMap statementFixedFields = new() { { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "principal", (a, n) => { a.Principal = n.CreateAny(); } }, + { "principal", (a, n) => { a.Principal = Principal.Parse(n); } }, { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, { "resource", (a, n) => { a.Resource = StringOrStringList.Parse(n); } }, { "condition", (a, n) => { a.Condition = n.CreateAny(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs index 9aaf391a..ed278013 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs @@ -56,7 +56,7 @@ public class SqsOperationBinding : OperationBinding private static FixedFieldMap statementFixedFields = new() { { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "principal", (a, n) => { a.Principal = n.CreateAny(); } }, + { "principal", (a, n) => { a.Principal = Principal.Parse(n); } }, { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, { "resource", (a, n) => { a.Resource = StringOrStringList.Parse(n); } }, { "condition", (a, n) => { a.Condition = n.CreateAny(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs index 1c015b32..938f69c3 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs @@ -19,7 +19,7 @@ public class Statement : IAsyncApiExtensible /// /// The AWS account(s) or resource ARN(s) that this statement applies to. /// - public AsyncApiAny Principal { get; set; } + public Principal Principal { get; set; } /// /// The SNS permission being allowed or denied e.g. sns:Publish. @@ -47,7 +47,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); - writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Write(w)); + writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Write(w)); writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w)); writer.WriteOptionalObject("resource", this.Resource, (w, t) => t?.Value.Write(w)); writer.WriteOptionalObject("condition", this.Condition, (w, t) => t?.Write(w)); diff --git a/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs b/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs index 6be69094..b9946f08 100644 --- a/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs +++ b/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs @@ -30,10 +30,10 @@ public static StringOrStringList Parse(ParseNode node) { case ValueNode: return new StringOrStringList(new AsyncApiAny(node.GetScalarValue())); - case ListNode: + case ListNode listNode: { var jsonArray = new JsonArray(); - foreach (var item in node as ListNode) + foreach (var item in listNode) { jsonArray.Add(item.GetScalarValue()); } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index db03847e..a81b573f 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -2,6 +2,7 @@ namespace LEGO.AsyncAPI.Tests.Bindings.Sns { + using System; using System.Collections.Generic; using System.Linq; using FluentAssertions; @@ -86,7 +87,7 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new AsyncApiAny("*"), + Principal = new Principal(new AsyncApiAny("*")), Action = new StringOrStringList(new AsyncApiAny(new List() { "sns:Publish", @@ -105,10 +106,10 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new AsyncApiAny(new Dictionary>() + Principal = new Principal(new AsyncApiAny(new Dictionary>() { { "AWS", new List() { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" } }, - }), + })), Action = new StringOrStringList(new AsyncApiAny("sns:Create")), Condition = new AsyncApiAny(new Dictionary() { @@ -163,8 +164,11 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - var settings = new AsyncApiReaderSettings(); - settings.Bindings = BindingsCollection.Sns; + var settings = new AsyncApiReaderSettings + { + Bindings = BindingsCollection.Sns, + }; + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert @@ -407,8 +411,11 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - var settings = new AsyncApiReaderSettings(); - settings.Bindings = BindingsCollection.Sns; + var settings = new AsyncApiReaderSettings + { + Bindings = BindingsCollection.Sns, + }; + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); var binding2 = new AsyncApiStringReader(settings).ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); binding2.Bindings.First().Value.Extensions.TryGetValue("x-bindingExtension", out IAsyncApiExtension any); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs index cfc52ac8..0fc0154e 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -3,6 +3,7 @@ namespace LEGO.AsyncAPI.Tests.Bindings.Sqs { using System.Collections.Generic; + using System.Linq; using FluentAssertions; using LEGO.AsyncAPI.Bindings; using LEGO.AsyncAPI.Bindings.Sqs; @@ -80,7 +81,7 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() statements: - effect: allow principal: - AWS: arn:aws:iam::123456789012:user/alex.wichmann + Service: s3.amazonaws.com action: - sqs:* x-internalObject: @@ -135,10 +136,10 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new AsyncApiAny(new Dictionary() + Principal = new Principal(new AsyncApiAny(new Dictionary() { { "AWS", "arn:aws:iam::123456789012:user/alex.wichmann" }, - }), + })), Action = new StringOrStringList(new AsyncApiAny(new List { "sqs:SendMessage", @@ -167,10 +168,10 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new AsyncApiAny(new Dictionary>() + Principal = new Principal(new AsyncApiAny(new Dictionary>() { { "AWS", new List() { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" } }, - }), + })), Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), Condition = new AsyncApiAny(new Dictionary() { @@ -225,10 +226,10 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new AsyncApiAny(new Dictionary() + Principal = new Principal(new AsyncApiAny(new Dictionary() { - { "AWS", "arn:aws:iam::123456789012:user/alex.wichmann" }, - }), + { "Service", "s3.amazonaws.com" }, + })), Action = new StringOrStringList(new AsyncApiAny(new List { "sqs:*", @@ -252,8 +253,10 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - var settings = new AsyncApiReaderSettings(); - settings.Bindings = BindingsCollection.Sqs; + var settings = new AsyncApiReaderSettings + { + Bindings = BindingsCollection.Sqs, + }; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); @@ -261,6 +264,9 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() actual.Should() .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(channel); + + var expectedSqsBinding = (SqsChannelBinding)channel.Bindings.Values.First(); + expectedSqsBinding.Should().BeEquivalentTo((SqsChannelBinding)binding.Bindings.Values.First(), options => options.IgnoringCyclicReferences()); } [Test] @@ -376,10 +382,10 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new AsyncApiAny(new Dictionary() + Principal = new Principal(new AsyncApiAny(new Dictionary() { { "AWS", "arn:aws:iam::123456789012:user/alex.wichmann" }, - }), + })), Action = new StringOrStringList(new AsyncApiAny(new List() { "sqs:SendMessage", @@ -399,10 +405,10 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new AsyncApiAny(new Dictionary>() + Principal = new Principal(new AsyncApiAny(new Dictionary>() { { "AWS", new List() { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" } }, - }), + })), Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), }, }, @@ -448,10 +454,10 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new AsyncApiAny(new Dictionary() + Principal = new Principal(new AsyncApiAny(new Dictionary() { { "AWS", "arn:aws:iam::123456789012:user/alex.wichmann" }, - }), + })), Action = new StringOrStringList(new AsyncApiAny(new List { "sqs:*", @@ -486,14 +492,20 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - var settings = new AsyncApiReaderSettings(); - settings.Bindings = BindingsCollection.Sqs; + var settings = new AsyncApiReaderSettings + { + Bindings = BindingsCollection.Sqs, + }; + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert actual.Should() .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(operation); + + var expectedSqsBinding = (SqsOperationBinding)operation.Bindings.Values.First(); + expectedSqsBinding.Should().BeEquivalentTo((SqsOperationBinding)binding.Bindings.Values.First(), options => options.IgnoringCyclicReferences()); } } } \ No newline at end of file From ebc802494cf070603b3058fdb79933e9287efe5d Mon Sep 17 00:00:00 2001 From: "adam.gloyne" Date: Tue, 13 Aug 2024 11:22:13 +0100 Subject: [PATCH 3/6] replace asyncapi any --- .../Sns/IPrincipalValue.cs | 54 +++++++++++++++++++ src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs | 17 +++--- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 2 +- .../Sqs/IPrincipalValue.cs | 54 +++++++++++++++++++ src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs | 17 +++--- src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs | 2 +- .../Bindings/Sns/SnsBindings_Should.cs | 10 ++-- .../Bindings/Sqs/SqsBindings_should.cs | 38 +++++-------- 8 files changed, 148 insertions(+), 46 deletions(-) create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/IPrincipalValue.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sqs/IPrincipalValue.cs diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/IPrincipalValue.cs b/src/LEGO.AsyncAPI.Bindings/Sns/IPrincipalValue.cs new file mode 100644 index 00000000..1c475e8c --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/IPrincipalValue.cs @@ -0,0 +1,54 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.Sns; + +using System; +using System.Collections.Generic; +using LEGO.AsyncAPI.Writers; + +public interface IPrincipalValue +{ + void Serialize(IAsyncApiWriter writer); +} + +public struct PrincipalObject : IPrincipalValue +{ + private KeyValuePair PrincipalValue; + + public PrincipalObject(KeyValuePair principalValue) + { + this.PrincipalValue = principalValue; + } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredObject(this.PrincipalValue.Key, this.PrincipalValue.Value, (w, t) => t.Value.Write(w)); + writer.WriteEndObject(); + } +} + +public struct PrincipalStar : IPrincipalValue +{ + private string PrincipalValue; + + public PrincipalStar(string principalValue) + { + this.PrincipalValue = principalValue; + } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteValue(this.PrincipalValue); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs index 0316068e..3324a7aa 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Bindings.Sns; using System; @@ -5,18 +7,17 @@ namespace LEGO.AsyncAPI.Bindings.Sns; using System.Linq; using System.Text.Json; using System.Text.Json.Nodes; -using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; public class Principal : IAsyncApiElement { - public Principal(AsyncApiAny value) + public Principal(IPrincipalValue value) { this.Value = value; } - public AsyncApiAny Value { get; } + public IPrincipalValue Value { get; set; } public static Principal Parse(ParseNode node) { @@ -30,7 +31,8 @@ public static Principal Parse(ParseNode node) $"Principal value without a property name can only be a string value of '*'."); } - return new Principal(new AsyncApiAny(nodeValue)); + return new Principal(new PrincipalStar(nodeValue)); + case MapNode mapNode: { var propertyNode = mapNode.First(); @@ -40,10 +42,11 @@ public static Principal Parse(ParseNode node) $"Node should contain a valid AWS principal property name."); } - var parsedObject = new Dictionary() - { { propertyNode.Name, StringOrStringList.Parse(propertyNode.Value).Value } }; + IPrincipalValue principalValue = new PrincipalObject(new KeyValuePair( + propertyNode.Name, + StringOrStringList.Parse(propertyNode.Value))); - return new Principal(new AsyncApiAny(parsedObject)); + return new Principal(principalValue); } default: diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index 2bccbb97..de16d1d4 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -46,7 +46,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); - writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Write(w)); + writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Serialize(w)); writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w)); writer.WriteOptionalObject("resource", this.Resource, (w, t) => t?.Value.Write(w)); writer.WriteOptionalObject("condition", this.Condition, (w, t) => t?.Write(w)); diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/IPrincipalValue.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/IPrincipalValue.cs new file mode 100644 index 00000000..72462365 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/IPrincipalValue.cs @@ -0,0 +1,54 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.Sqs; + +using System; +using System.Collections.Generic; +using LEGO.AsyncAPI.Writers; + +public interface IPrincipalValue +{ + void Serialize(IAsyncApiWriter writer); +} + +public struct PrincipalObject : IPrincipalValue +{ + private KeyValuePair PrincipalValue; + + public PrincipalObject(KeyValuePair principalValue) + { + this.PrincipalValue = principalValue; + } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredObject(this.PrincipalValue.Key, this.PrincipalValue.Value, (w, t) => t.Value.Write(w)); + writer.WriteEndObject(); + } +} + +struct PrincipalStar : IPrincipalValue +{ + private string PrincipalValue; + + public PrincipalStar(string principalValue) + { + this.PrincipalValue = principalValue; + } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteValue(this.PrincipalValue); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs index 5a67b588..2e30e2de 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Bindings.Sqs; using System; @@ -5,18 +7,17 @@ namespace LEGO.AsyncAPI.Bindings.Sqs; using System.Linq; using System.Text.Json; using System.Text.Json.Nodes; -using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; public class Principal : IAsyncApiElement { - public Principal(AsyncApiAny value) + public Principal(IPrincipalValue value) { this.Value = value; } - public AsyncApiAny Value { get; } + public IPrincipalValue Value { get; set; } public static Principal Parse(ParseNode node) { @@ -30,7 +31,8 @@ public static Principal Parse(ParseNode node) $"Principal value without a property name can only be a string value of '*'."); } - return new Principal(new AsyncApiAny(nodeValue)); + return new Principal(new PrincipalStar(nodeValue)); + case MapNode mapNode: { var propertyNode = mapNode.First(); @@ -40,10 +42,11 @@ public static Principal Parse(ParseNode node) $"Node should contain a valid AWS principal property name."); } - var parsedObject = new Dictionary() - { { propertyNode.Name, StringOrStringList.Parse(propertyNode.Value).Value } }; + IPrincipalValue principalValue = new PrincipalObject(new KeyValuePair( + propertyNode.Name, + StringOrStringList.Parse(propertyNode.Value))); - return new Principal(new AsyncApiAny(parsedObject)); + return new Principal(principalValue); } default: diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs index 938f69c3..2dfff3b9 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs @@ -47,7 +47,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); - writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Write(w)); + writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Serialize(w)); writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w)); writer.WriteOptionalObject("resource", this.Resource, (w, t) => t?.Value.Write(w)); writer.WriteOptionalObject("condition", this.Condition, (w, t) => t?.Write(w)); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index a81b573f..e7005ef3 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -2,7 +2,6 @@ namespace LEGO.AsyncAPI.Tests.Bindings.Sns { - using System; using System.Collections.Generic; using System.Linq; using FluentAssertions; @@ -87,7 +86,7 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new Principal(new AsyncApiAny("*")), + Principal = new Principal(new PrincipalStar("*")), Action = new StringOrStringList(new AsyncApiAny(new List() { "sns:Publish", @@ -106,10 +105,9 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new Principal(new AsyncApiAny(new Dictionary>() - { - { "AWS", new List() { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" } }, - })), + Principal = new Principal(new PrincipalObject(new KeyValuePair( + "AWS", new StringOrStringList(new AsyncApiAny(new List + { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" }))))), Action = new StringOrStringList(new AsyncApiAny("sns:Create")), Condition = new AsyncApiAny(new Dictionary() { diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs index 0fc0154e..dd3b8a26 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -136,10 +136,8 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new Principal(new AsyncApiAny(new Dictionary() - { - { "AWS", "arn:aws:iam::123456789012:user/alex.wichmann" }, - })), + Principal = new Principal(new PrincipalObject(new KeyValuePair( + "AWS", new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann"))))), Action = new StringOrStringList(new AsyncApiAny(new List { "sqs:SendMessage", @@ -168,10 +166,9 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new Principal(new AsyncApiAny(new Dictionary>() - { - { "AWS", new List() { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" } }, - })), + Principal = new Principal(new PrincipalObject(new KeyValuePair( + "AWS", new StringOrStringList(new AsyncApiAny(new List + { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" }))))), Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), Condition = new AsyncApiAny(new Dictionary() { @@ -226,10 +223,8 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new Principal(new AsyncApiAny(new Dictionary() - { - { "Service", "s3.amazonaws.com" }, - })), + Principal = new Principal(new PrincipalObject(new KeyValuePair( + "Service", new StringOrStringList(new AsyncApiAny("s3.amazonaws.com"))))), Action = new StringOrStringList(new AsyncApiAny(new List { "sqs:*", @@ -382,10 +377,8 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new Principal(new AsyncApiAny(new Dictionary() - { - { "AWS", "arn:aws:iam::123456789012:user/alex.wichmann" }, - })), + Principal = new Principal(new PrincipalObject(new KeyValuePair( + "AWS", new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann"))))), Action = new StringOrStringList(new AsyncApiAny(new List() { "sqs:SendMessage", @@ -405,10 +398,9 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new Principal(new AsyncApiAny(new Dictionary>() - { - { "AWS", new List() { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" } }, - })), + Principal = new Principal(new PrincipalObject(new KeyValuePair( + "AWS", new StringOrStringList(new AsyncApiAny(new List + { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" }))))), Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), }, }, @@ -454,10 +446,8 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new Principal(new AsyncApiAny(new Dictionary() - { - { "AWS", "arn:aws:iam::123456789012:user/alex.wichmann" }, - })), + Principal = new Principal(new PrincipalObject(new KeyValuePair( + "AWS", new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann"))))), Action = new StringOrStringList(new AsyncApiAny(new List { "sqs:*", From 0e00170d7fc372b6cdc1f97f6a2e91e84983b14d Mon Sep 17 00:00:00 2001 From: "adam.gloyne" Date: Wed, 14 Aug 2024 11:24:41 +0100 Subject: [PATCH 4/6] hardcode star string --- src/LEGO.AsyncAPI.Bindings/Sns/IPrincipalValue.cs | 4 ++-- src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs | 2 +- src/LEGO.AsyncAPI.Bindings/Sqs/IPrincipalValue.cs | 4 ++-- src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs | 2 +- test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/IPrincipalValue.cs b/src/LEGO.AsyncAPI.Bindings/Sns/IPrincipalValue.cs index 1c475e8c..ab097a5a 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/IPrincipalValue.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/IPrincipalValue.cs @@ -37,9 +37,9 @@ public struct PrincipalStar : IPrincipalValue { private string PrincipalValue; - public PrincipalStar(string principalValue) + public PrincipalStar() { - this.PrincipalValue = principalValue; + this.PrincipalValue = "*"; } public void Serialize(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs index 3324a7aa..6d9ffa4a 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs @@ -31,7 +31,7 @@ public static Principal Parse(ParseNode node) $"Principal value without a property name can only be a string value of '*'."); } - return new Principal(new PrincipalStar(nodeValue)); + return new Principal(new PrincipalStar()); case MapNode mapNode: { diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/IPrincipalValue.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/IPrincipalValue.cs index 72462365..958aec9a 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/IPrincipalValue.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/IPrincipalValue.cs @@ -37,9 +37,9 @@ struct PrincipalStar : IPrincipalValue { private string PrincipalValue; - public PrincipalStar(string principalValue) + public PrincipalStar() { - this.PrincipalValue = principalValue; + this.PrincipalValue = "*"; } public void Serialize(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs index 2e30e2de..bf4948a2 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs @@ -31,7 +31,7 @@ public static Principal Parse(ParseNode node) $"Principal value without a property name can only be a string value of '*'."); } - return new Principal(new PrincipalStar(nodeValue)); + return new Principal(new PrincipalStar()); case MapNode mapNode: { diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index e7005ef3..56d3bcfc 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -86,7 +86,7 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new Principal(new PrincipalStar("*")), + Principal = new Principal(new PrincipalStar()), Action = new StringOrStringList(new AsyncApiAny(new List() { "sns:Publish", From cdf9f8b798574283d130171ff543c8562a9de9db Mon Sep 17 00:00:00 2001 From: "adam.gloyne" Date: Wed, 14 Aug 2024 16:59:29 +0100 Subject: [PATCH 5/6] simplify principal input --- src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs | 17 ++++++++----- src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs | 17 ++++++++----- .../Bindings/Sns/SnsBindings_Should.cs | 6 ++--- .../Bindings/Sqs/SqsBindings_should.cs | 24 +++++++++---------- 4 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs index 6d9ffa4a..40acd9c0 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs @@ -12,12 +12,17 @@ namespace LEGO.AsyncAPI.Bindings.Sns; public class Principal : IAsyncApiElement { - public Principal(IPrincipalValue value) + public Principal(KeyValuePair value) { - this.Value = value; + this.Value = new PrincipalObject(value); } - public IPrincipalValue Value { get; set; } + public Principal() + { + this.Value = new PrincipalStar(); + } + + public IPrincipalValue Value { get; private set; } public static Principal Parse(ParseNode node) { @@ -31,7 +36,7 @@ public static Principal Parse(ParseNode node) $"Principal value without a property name can only be a string value of '*'."); } - return new Principal(new PrincipalStar()); + return new Principal(); case MapNode mapNode: { @@ -42,9 +47,9 @@ public static Principal Parse(ParseNode node) $"Node should contain a valid AWS principal property name."); } - IPrincipalValue principalValue = new PrincipalObject(new KeyValuePair( + var principalValue = new KeyValuePair( propertyNode.Name, - StringOrStringList.Parse(propertyNode.Value))); + StringOrStringList.Parse(propertyNode.Value)); return new Principal(principalValue); } diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs index bf4948a2..acffe8d3 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs @@ -12,12 +12,17 @@ namespace LEGO.AsyncAPI.Bindings.Sqs; public class Principal : IAsyncApiElement { - public Principal(IPrincipalValue value) + public Principal(KeyValuePair value) { - this.Value = value; + this.Value = new PrincipalObject(value); } - public IPrincipalValue Value { get; set; } + public Principal() + { + this.Value = new PrincipalStar(); + } + + public IPrincipalValue Value { get; private set; } public static Principal Parse(ParseNode node) { @@ -31,7 +36,7 @@ public static Principal Parse(ParseNode node) $"Principal value without a property name can only be a string value of '*'."); } - return new Principal(new PrincipalStar()); + return new Principal(); case MapNode mapNode: { @@ -42,9 +47,9 @@ public static Principal Parse(ParseNode node) $"Node should contain a valid AWS principal property name."); } - IPrincipalValue principalValue = new PrincipalObject(new KeyValuePair( + var principalValue = new KeyValuePair( propertyNode.Name, - StringOrStringList.Parse(propertyNode.Value))); + StringOrStringList.Parse(propertyNode.Value)); return new Principal(principalValue); } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 56d3bcfc..f3deb55c 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -86,7 +86,7 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new Principal(new PrincipalStar()), + Principal = new Principal(), Action = new StringOrStringList(new AsyncApiAny(new List() { "sns:Publish", @@ -105,9 +105,9 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new Principal(new PrincipalObject(new KeyValuePair( + Principal = new Principal(new KeyValuePair( "AWS", new StringOrStringList(new AsyncApiAny(new List - { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" }))))), + { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" })))), Action = new StringOrStringList(new AsyncApiAny("sns:Create")), Condition = new AsyncApiAny(new Dictionary() { diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs index dd3b8a26..65978a80 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -136,8 +136,8 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new Principal(new PrincipalObject(new KeyValuePair( - "AWS", new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann"))))), + Principal = new Principal(new KeyValuePair( + "AWS", new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")))), Action = new StringOrStringList(new AsyncApiAny(new List { "sqs:SendMessage", @@ -166,9 +166,9 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new Principal(new PrincipalObject(new KeyValuePair( + Principal = new Principal(new KeyValuePair( "AWS", new StringOrStringList(new AsyncApiAny(new List - { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" }))))), + { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" })))), Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), Condition = new AsyncApiAny(new Dictionary() { @@ -223,8 +223,8 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new Principal(new PrincipalObject(new KeyValuePair( - "Service", new StringOrStringList(new AsyncApiAny("s3.amazonaws.com"))))), + Principal = new Principal(new KeyValuePair( + "Service", new StringOrStringList(new AsyncApiAny("s3.amazonaws.com")))), Action = new StringOrStringList(new AsyncApiAny(new List { "sqs:*", @@ -377,8 +377,8 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new Principal(new PrincipalObject(new KeyValuePair( - "AWS", new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann"))))), + Principal = new Principal(new KeyValuePair( + "AWS", new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")))), Action = new StringOrStringList(new AsyncApiAny(new List() { "sqs:SendMessage", @@ -398,9 +398,9 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new Principal(new PrincipalObject(new KeyValuePair( + Principal = new Principal(new KeyValuePair( "AWS", new StringOrStringList(new AsyncApiAny(new List - { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" }))))), + { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" })))), Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), }, }, @@ -446,8 +446,8 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new Principal(new PrincipalObject(new KeyValuePair( - "AWS", new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann"))))), + Principal = new Principal(new KeyValuePair( + "AWS", new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")))), Action = new StringOrStringList(new AsyncApiAny(new List { "sqs:*", From df823d78c249294b0e71f50f4b4b13bd6ac15a3e Mon Sep 17 00:00:00 2001 From: "adam.gloyne" Date: Thu, 15 Aug 2024 11:39:24 +0100 Subject: [PATCH 6/6] use abstract class --- src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs | 21 +++---------- ...{IPrincipalValue.cs => PrincipalObject.cs} | 31 ++----------------- .../Sns/PrincipalStar.cs | 24 ++++++++++++++ src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 2 +- src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs | 19 +++--------- ...{IPrincipalValue.cs => PrincipalObject.cs} | 31 ++----------------- .../Sqs/PrincipalStar.cs | 24 ++++++++++++++ src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs | 2 +- .../Bindings/Sns/SnsBindings_Should.cs | 4 +-- .../Bindings/Sqs/SqsBindings_should.cs | 12 +++---- 10 files changed, 72 insertions(+), 98 deletions(-) rename src/LEGO.AsyncAPI.Bindings/Sns/{IPrincipalValue.cs => PrincipalObject.cs} (50%) create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs rename src/LEGO.AsyncAPI.Bindings/Sqs/{IPrincipalValue.cs => PrincipalObject.cs} (51%) create mode 100644 src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs index 40acd9c0..a803f6c2 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs @@ -1,5 +1,3 @@ -// Copyright (c) The LEGO Group. All rights reserved. - namespace LEGO.AsyncAPI.Bindings.Sns; using System; @@ -9,20 +7,11 @@ namespace LEGO.AsyncAPI.Bindings.Sns; using System.Text.Json.Nodes; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; +using LEGO.AsyncAPI.Writers; -public class Principal : IAsyncApiElement +public abstract class Principal : IAsyncApiElement { - public Principal(KeyValuePair value) - { - this.Value = new PrincipalObject(value); - } - - public Principal() - { - this.Value = new PrincipalStar(); - } - - public IPrincipalValue Value { get; private set; } + public abstract void Serialize(IAsyncApiWriter writer); public static Principal Parse(ParseNode node) { @@ -36,7 +25,7 @@ public static Principal Parse(ParseNode node) $"Principal value without a property name can only be a string value of '*'."); } - return new Principal(); + return new PrincipalStar(); case MapNode mapNode: { @@ -51,7 +40,7 @@ public static Principal Parse(ParseNode node) propertyNode.Name, StringOrStringList.Parse(propertyNode.Value)); - return new Principal(principalValue); + return new PrincipalObject(principalValue); } default: diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/IPrincipalValue.cs b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs similarity index 50% rename from src/LEGO.AsyncAPI.Bindings/Sns/IPrincipalValue.cs rename to src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs index ab097a5a..a25c198f 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/IPrincipalValue.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs @@ -1,17 +1,10 @@ -// Copyright (c) The LEGO Group. All rights reserved. - namespace LEGO.AsyncAPI.Bindings.Sns; using System; using System.Collections.Generic; using LEGO.AsyncAPI.Writers; -public interface IPrincipalValue -{ - void Serialize(IAsyncApiWriter writer); -} - -public struct PrincipalObject : IPrincipalValue +public class PrincipalObject : Principal { private KeyValuePair PrincipalValue; @@ -20,7 +13,7 @@ public PrincipalObject(KeyValuePair principalValue) this.PrincipalValue = principalValue; } - public void Serialize(IAsyncApiWriter writer) + public override void Serialize(IAsyncApiWriter writer) { if (writer is null) { @@ -31,24 +24,4 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteRequiredObject(this.PrincipalValue.Key, this.PrincipalValue.Value, (w, t) => t.Value.Write(w)); writer.WriteEndObject(); } -} - -public struct PrincipalStar : IPrincipalValue -{ - private string PrincipalValue; - - public PrincipalStar() - { - this.PrincipalValue = "*"; - } - - public void Serialize(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - writer.WriteValue(this.PrincipalValue); - } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs new file mode 100644 index 00000000..533e9fb7 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs @@ -0,0 +1,24 @@ +namespace LEGO.AsyncAPI.Bindings.Sns; + +using System; +using LEGO.AsyncAPI.Writers; + +public class PrincipalStar : Principal +{ + private string PrincipalValue; + + public PrincipalStar() + { + this.PrincipalValue = "*"; + } + + public override void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteValue(this.PrincipalValue); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index de16d1d4..170fe371 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -46,7 +46,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); - writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Serialize(w)); + writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Serialize(w)); writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w)); writer.WriteOptionalObject("resource", this.Resource, (w, t) => t?.Value.Write(w)); writer.WriteOptionalObject("condition", this.Condition, (w, t) => t?.Write(w)); diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs index acffe8d3..2821f952 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs @@ -9,20 +9,11 @@ namespace LEGO.AsyncAPI.Bindings.Sqs; using System.Text.Json.Nodes; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; +using LEGO.AsyncAPI.Writers; -public class Principal : IAsyncApiElement +public abstract class Principal : IAsyncApiElement { - public Principal(KeyValuePair value) - { - this.Value = new PrincipalObject(value); - } - - public Principal() - { - this.Value = new PrincipalStar(); - } - - public IPrincipalValue Value { get; private set; } + public abstract void Serialize(IAsyncApiWriter writer); public static Principal Parse(ParseNode node) { @@ -36,7 +27,7 @@ public static Principal Parse(ParseNode node) $"Principal value without a property name can only be a string value of '*'."); } - return new Principal(); + return new PrincipalStar(); case MapNode mapNode: { @@ -51,7 +42,7 @@ public static Principal Parse(ParseNode node) propertyNode.Name, StringOrStringList.Parse(propertyNode.Value)); - return new Principal(principalValue); + return new PrincipalObject(principalValue); } default: diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/IPrincipalValue.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs similarity index 51% rename from src/LEGO.AsyncAPI.Bindings/Sqs/IPrincipalValue.cs rename to src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs index 958aec9a..2652060d 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/IPrincipalValue.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs @@ -1,17 +1,10 @@ -// Copyright (c) The LEGO Group. All rights reserved. - namespace LEGO.AsyncAPI.Bindings.Sqs; using System; using System.Collections.Generic; using LEGO.AsyncAPI.Writers; -public interface IPrincipalValue -{ - void Serialize(IAsyncApiWriter writer); -} - -public struct PrincipalObject : IPrincipalValue +public class PrincipalObject : Principal { private KeyValuePair PrincipalValue; @@ -20,7 +13,7 @@ public PrincipalObject(KeyValuePair principalValue) this.PrincipalValue = principalValue; } - public void Serialize(IAsyncApiWriter writer) + public override void Serialize(IAsyncApiWriter writer) { if (writer is null) { @@ -31,24 +24,4 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteRequiredObject(this.PrincipalValue.Key, this.PrincipalValue.Value, (w, t) => t.Value.Write(w)); writer.WriteEndObject(); } -} - -struct PrincipalStar : IPrincipalValue -{ - private string PrincipalValue; - - public PrincipalStar() - { - this.PrincipalValue = "*"; - } - - public void Serialize(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - writer.WriteValue(this.PrincipalValue); - } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs new file mode 100644 index 00000000..9e54bc5a --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs @@ -0,0 +1,24 @@ +namespace LEGO.AsyncAPI.Bindings.Sqs; + +using System; +using LEGO.AsyncAPI.Writers; + +public class PrincipalStar : Principal +{ + private string PrincipalValue; + + public PrincipalStar() + { + this.PrincipalValue = "*"; + } + + public override void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteValue(this.PrincipalValue); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs index 2dfff3b9..4abc05a6 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs @@ -47,7 +47,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); - writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Serialize(w)); + writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Serialize(w)); writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w)); writer.WriteOptionalObject("resource", this.Resource, (w, t) => t?.Value.Write(w)); writer.WriteOptionalObject("condition", this.Condition, (w, t) => t?.Write(w)); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index f3deb55c..fbb3622e 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -86,7 +86,7 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new Principal(), + Principal = new PrincipalStar(), Action = new StringOrStringList(new AsyncApiAny(new List() { "sns:Publish", @@ -105,7 +105,7 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new Principal(new KeyValuePair( + Principal = new PrincipalObject(new KeyValuePair( "AWS", new StringOrStringList(new AsyncApiAny(new List { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" })))), Action = new StringOrStringList(new AsyncApiAny("sns:Create")), diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs index 65978a80..3a0337a3 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -136,7 +136,7 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new Principal(new KeyValuePair( + Principal = new PrincipalObject(new KeyValuePair( "AWS", new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")))), Action = new StringOrStringList(new AsyncApiAny(new List { @@ -166,7 +166,7 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new Principal(new KeyValuePair( + Principal = new PrincipalObject(new KeyValuePair( "AWS", new StringOrStringList(new AsyncApiAny(new List { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" })))), Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), @@ -223,7 +223,7 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new Principal(new KeyValuePair( + Principal = new PrincipalObject(new KeyValuePair( "Service", new StringOrStringList(new AsyncApiAny("s3.amazonaws.com")))), Action = new StringOrStringList(new AsyncApiAny(new List { @@ -377,7 +377,7 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new Principal(new KeyValuePair( + Principal = new PrincipalObject(new KeyValuePair( "AWS", new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")))), Action = new StringOrStringList(new AsyncApiAny(new List() { @@ -398,7 +398,7 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new Principal(new KeyValuePair( + Principal = new PrincipalObject(new KeyValuePair( "AWS", new StringOrStringList(new AsyncApiAny(new List { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" })))), Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), @@ -446,7 +446,7 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new Principal(new KeyValuePair( + Principal = new PrincipalObject(new KeyValuePair( "AWS", new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")))), Action = new StringOrStringList(new AsyncApiAny(new List {