Skip to content

Commit cab0d7b

Browse files
authored
LAMBJ-141 Stack Notification Event Model + Converter (#451)
1 parent f04bb5e commit cab0d7b

File tree

4 files changed

+360
-1
lines changed

4 files changed

+360
-1
lines changed

.github/releases/v0.9.0-beta3.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
## New Features
22

3-
- SNS Event Handling is now built in to Lambdajection. Add the Lambdajection.Sns package to your project and use the SnsEventHandlerAttribute to denote Lambdas that are sourced from SNS events. The lambda handler will be invoked once for each SNS record received.
3+
- SNS Event Handling is now built in to Lambdajection. Add the Lambdajection.Sns package to your project and use the SnsEventHandlerAttribute to denote Lambdas that are sourced from SNS events. The lambda handler will be invoked once for each SNS record received.
4+
- A model for CloudFormation Stack Notifications is included in the Lambdajection.Sns package for processing stack notification events.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System;
2+
using System.Text.Json.Serialization;
3+
4+
namespace Lambdajection.Sns
5+
{
6+
/// <summary>
7+
/// Represents a CloudFormation stack event sent via SNS.
8+
/// </summary>
9+
[JsonConverter(typeof(CloudFormationStackEventConverter))]
10+
public class CloudFormationStackEvent
11+
{
12+
/// <summary>
13+
/// Gets or sets the source topic of the stack event.
14+
/// </summary>
15+
public string SourceTopic { get; set; } = string.Empty;
16+
17+
/// <summary>
18+
/// Gets or sets the id of the stack this event came from.
19+
/// </summary>
20+
public string StackId { get; set; } = string.Empty;
21+
22+
/// <summary>
23+
/// Gets or sets the timestamp of this event.
24+
/// </summary>
25+
public DateTime Timestamp { get; set; } = DateTime.Now;
26+
27+
/// <summary>
28+
/// Gets or sets the id of this event.
29+
/// </summary>
30+
public string EventId { get; set; } = string.Empty;
31+
32+
/// <summary>
33+
/// Gets or sets the logical resource id pertaining to this event.
34+
/// </summary>
35+
public string LogicalResourceId { get; set; } = string.Empty;
36+
37+
/// <summary>
38+
/// Gets or sets the physical resource id pertaining to this event.
39+
/// </summary>
40+
public string PhysicalResourceId { get; set; } = string.Empty;
41+
42+
/// <summary>
43+
/// Gets or sets the namespace pertaining to this event.
44+
/// </summary>
45+
public string Namespace { get; set; } = string.Empty;
46+
47+
/// <summary>
48+
/// Gets or sets the principal id pertaining to this event.
49+
/// </summary>
50+
public string PrincipalId { get; set; } = string.Empty;
51+
52+
/// <summary>
53+
/// Gets or sets the resource properties associated with this event.
54+
/// </summary>
55+
public object? ResourceProperties { get; set; }
56+
57+
/// <summary>
58+
/// Gets or sets the resource status associated with this event.
59+
/// </summary>
60+
public string ResourceStatus { get; set; } = string.Empty;
61+
62+
/// <summary>
63+
/// Gets or sets the resource type associated with this event.
64+
/// </summary>
65+
public string ResourceType { get; set; } = string.Empty;
66+
67+
/// <summary>
68+
/// Gets or sets the name of the stack this event originates from.
69+
/// </summary>
70+
public string StackName { get; set; } = string.Empty;
71+
72+
/// <summary>
73+
/// Gets or sets the client request token pertaining to this event.
74+
/// </summary>
75+
public string ClientRequestToken { get; set; } = string.Empty;
76+
}
77+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
using System;
2+
using System.Text.Json;
3+
using System.Text.Json.Serialization;
4+
5+
namespace Lambdajection.Sns
6+
{
7+
/// <summary>
8+
/// Converts a <see cref="CloudFormationStackEvent" /> to and from json.
9+
/// </summary>
10+
public class CloudFormationStackEventConverter : JsonConverter<CloudFormationStackEvent>
11+
{
12+
/// <inheritdoc />
13+
public override CloudFormationStackEvent? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
14+
{
15+
var source = reader.GetString();
16+
var lines = source!.Split('\n');
17+
var result = new CloudFormationStackEvent();
18+
19+
foreach (var line in lines)
20+
{
21+
var delimiterIndex = line.IndexOf('=');
22+
var key = line[0..delimiterIndex].Trim();
23+
var value = line[(delimiterIndex + 1)..].Trim('\'');
24+
25+
switch (key)
26+
{
27+
case nameof(CloudFormationStackEvent.SourceTopic):
28+
{
29+
result.SourceTopic = value;
30+
break;
31+
}
32+
33+
case nameof(CloudFormationStackEvent.StackId):
34+
{
35+
result.StackId = value;
36+
break;
37+
}
38+
39+
case nameof(CloudFormationStackEvent.Timestamp):
40+
{
41+
result.Timestamp = DateTime.Parse(value);
42+
break;
43+
}
44+
45+
case nameof(CloudFormationStackEvent.EventId):
46+
{
47+
result.EventId = value;
48+
break;
49+
}
50+
51+
case nameof(CloudFormationStackEvent.LogicalResourceId):
52+
{
53+
result.LogicalResourceId = value;
54+
break;
55+
}
56+
57+
case nameof(CloudFormationStackEvent.PhysicalResourceId):
58+
{
59+
result.PhysicalResourceId = value;
60+
break;
61+
}
62+
63+
case nameof(CloudFormationStackEvent.Namespace):
64+
{
65+
result.Namespace = value;
66+
break;
67+
}
68+
69+
case nameof(CloudFormationStackEvent.PrincipalId):
70+
{
71+
result.PrincipalId = value;
72+
break;
73+
}
74+
75+
case nameof(CloudFormationStackEvent.ResourceProperties):
76+
{
77+
result.ResourceProperties = JsonSerializer.Deserialize<object>(value);
78+
break;
79+
}
80+
81+
case nameof(CloudFormationStackEvent.ResourceStatus):
82+
{
83+
result.ResourceStatus = value;
84+
break;
85+
}
86+
87+
case nameof(CloudFormationStackEvent.ResourceType):
88+
{
89+
result.ResourceType = value;
90+
break;
91+
}
92+
93+
case nameof(CloudFormationStackEvent.StackName):
94+
{
95+
result.StackName = value;
96+
break;
97+
}
98+
99+
case nameof(CloudFormationStackEvent.ClientRequestToken):
100+
{
101+
result.ClientRequestToken = value;
102+
break;
103+
}
104+
105+
default: break;
106+
}
107+
}
108+
109+
return result;
110+
}
111+
112+
/// <inheritdoc />
113+
public override void Write(Utf8JsonWriter writer, CloudFormationStackEvent value, JsonSerializerOptions options)
114+
{
115+
throw new NotSupportedException();
116+
}
117+
}
118+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
using System;
2+
using System.Text.Json;
3+
4+
using FluentAssertions;
5+
6+
using NUnit.Framework;
7+
8+
namespace Lambdajection.Sns
9+
{
10+
[Category("Unit")]
11+
public class CloudFormationStackEventConverterTests
12+
{
13+
[TestFixture]
14+
[Category("Unit")]
15+
public class Deserialization
16+
{
17+
[Test, Auto]
18+
public void SourceTopicShouldDeserialize(
19+
string sourceTopic
20+
)
21+
{
22+
var source = $@"""SourceTopic={sourceTopic}""";
23+
var result = JsonSerializer.Deserialize<CloudFormationStackEvent>(source);
24+
25+
result!.SourceTopic.Should().Be(sourceTopic);
26+
}
27+
28+
[Test, Auto]
29+
public void StackIdShouldDeserialize(
30+
string stackId
31+
)
32+
{
33+
var source = $@"""StackId={stackId}""";
34+
var result = JsonSerializer.Deserialize<CloudFormationStackEvent>(source);
35+
36+
result!.StackId.Should().Be(stackId);
37+
}
38+
39+
[Test, Auto]
40+
public void TimestampShouldDeserialize(
41+
DateTime timestamp
42+
)
43+
{
44+
var timestampString = timestamp.ToString("MM/dd/yyyy HH:mm:ss.fffffff");
45+
var source = $@"""Timestamp={timestampString}""";
46+
var result = JsonSerializer.Deserialize<CloudFormationStackEvent>(source);
47+
48+
result!.Timestamp.Should().Be(timestamp);
49+
}
50+
51+
[Test, Auto]
52+
public void EventIdShouldDeserialize(
53+
string eventId
54+
)
55+
{
56+
var source = $@"""EventId={eventId}""";
57+
var result = JsonSerializer.Deserialize<CloudFormationStackEvent>(source);
58+
59+
result!.EventId.Should().Be(eventId);
60+
}
61+
62+
[Test, Auto]
63+
public void LogicalResourceIdShouldDeserialize(
64+
string logicalResourceId
65+
)
66+
{
67+
var source = $@"""PhysicalResourceId=test\nLogicalResourceId={logicalResourceId}""";
68+
var result = JsonSerializer.Deserialize<CloudFormationStackEvent>(source);
69+
70+
result!.LogicalResourceId.Should().Be(logicalResourceId);
71+
}
72+
73+
[Test, Auto]
74+
public void PhysicalResourceIdShouldDeserialize(
75+
string physicalResourceId
76+
)
77+
{
78+
var source = $@"""PhysicalResourceId={physicalResourceId}\nLogicalResourceId=test""";
79+
var result = JsonSerializer.Deserialize<CloudFormationStackEvent>(source);
80+
81+
result!.PhysicalResourceId.Should().Be(physicalResourceId);
82+
}
83+
84+
[Test, Auto]
85+
public void NamespaceShouldDeserialize(
86+
string @namespace
87+
)
88+
{
89+
var source = $@"""Namespace={@namespace}""";
90+
var result = JsonSerializer.Deserialize<CloudFormationStackEvent>(source);
91+
92+
result!.Namespace.Should().Be(@namespace);
93+
}
94+
95+
[Test, Auto]
96+
public void PrincipalIdShouldDeserialize(
97+
string principalId
98+
)
99+
{
100+
var source = $@"""PrincipalId={principalId}""";
101+
var result = JsonSerializer.Deserialize<CloudFormationStackEvent>(source);
102+
103+
result!.PrincipalId.Should().Be(principalId);
104+
}
105+
106+
[Test, Auto]
107+
public void ResourcePropertiesShouldDeserialize(
108+
string data
109+
)
110+
{
111+
var source = $"\"ResourceProperties='{{\\\"Data\\\":\\\"{data}\\\"}}'\"";
112+
var result = JsonSerializer.Deserialize<CloudFormationStackEvent>(source);
113+
114+
var props = (JsonElement)result!.ResourceProperties!;
115+
props.GetProperty("Data").GetString().Should().Be(data);
116+
}
117+
118+
[Test, Auto]
119+
public void ResourceStatusShouldDeserialize(
120+
string resourceStatus
121+
)
122+
{
123+
var source = $@"""ResourceStatus={resourceStatus}""";
124+
var result = JsonSerializer.Deserialize<CloudFormationStackEvent>(source);
125+
126+
result!.ResourceStatus.Should().Be(resourceStatus);
127+
}
128+
129+
[Test, Auto]
130+
public void ResourceTypeShouldDeserialize(
131+
string resourceType
132+
)
133+
{
134+
var source = $@"""ResourceType={resourceType}""";
135+
var result = JsonSerializer.Deserialize<CloudFormationStackEvent>(source);
136+
137+
result!.ResourceType.Should().Be(resourceType);
138+
}
139+
140+
[Test, Auto]
141+
public void StackNameShouldDeserialize(
142+
string stackName
143+
)
144+
{
145+
var source = $@"""StackName={stackName}""";
146+
var result = JsonSerializer.Deserialize<CloudFormationStackEvent>(source);
147+
148+
result!.StackName.Should().Be(stackName);
149+
}
150+
151+
[Test, Auto]
152+
public void ClientRequestTokenShouldDeserialize(
153+
string clientRequestToken
154+
)
155+
{
156+
var source = $@"""ClientRequestToken={clientRequestToken}""";
157+
var result = JsonSerializer.Deserialize<CloudFormationStackEvent>(source);
158+
159+
result!.ClientRequestToken.Should().Be(clientRequestToken);
160+
}
161+
}
162+
}
163+
}

0 commit comments

Comments
 (0)