Skip to content

Commit 411b64b

Browse files
Merge pull request #378 from TransactionProcessing/bug/#362_validateeventidonontroller
Enforce event Id on event controller
2 parents 83a4902 + c74f57e commit 411b64b

File tree

2 files changed

+91
-16
lines changed

2 files changed

+91
-16
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Moq;
4+
using Newtonsoft.Json;
5+
using Shared.EventStore.EventHandling;
6+
using Shouldly;
7+
using System;
8+
using System.Collections.Generic;
9+
using System.Linq;
10+
using System.Text;
11+
using System.Threading.Tasks;
12+
using TransactionProcessor.Controllers;
13+
using TransactionProcessor.Transaction.DomainEvents;
14+
using Xunit;
15+
16+
namespace TransactionProcessor.Tests.ControllerTests
17+
{
18+
using System.Threading;
19+
using Shared.General;
20+
using Shared.Logger;
21+
22+
public class ControllerTests
23+
{
24+
public ControllerTests()
25+
{
26+
Logger.Initialise(new NullLogger());
27+
}
28+
[Fact]
29+
public async Task DomainEventController_EventIdNotPresentInJson_ErrorThrown()
30+
{
31+
Mock<IDomainEventHandlerResolver> resolver = new Mock<IDomainEventHandlerResolver>();
32+
TypeMap.AddType<TransactionHasBeenCompletedEvent>("TransactionHasBeenCompletedEvent");
33+
DefaultHttpContext httpContext = new DefaultHttpContext();
34+
httpContext.Request.Headers["eventType"] = "TransactionHasBeenCompletedEvent";
35+
DomainEventController controller = new DomainEventController(resolver.Object)
36+
{
37+
ControllerContext = new ControllerContext()
38+
{
39+
HttpContext = httpContext
40+
}
41+
};
42+
String json = "{\r\n \"completedDateTime\": \"2022-11-08T15:40:07\",\r\n \"estateId\": \"435613ac-a468-47a3-ac4f-649d89764c22\",\r\n \"isAuthorised\": true,\r\n \"transactionAmount\": 35.0,\r\n \"merchantId\": \"8bc8434d-41f9-4cc3-83bc-e73f20c02e1d\",\r\n \"responseCode\": \"0000\",\r\n \"responseMessage\": \"SUCCESS\",\r\n \"transactionId\": \"626644c5-bb7b-40ca-821e-cf115488867b\",\r\n}";
43+
Object request = JsonConvert.DeserializeObject(json);
44+
ArgumentException ex = Should.Throw<ArgumentException>(async () => {
45+
await controller.PostEventAsync(request, CancellationToken.None);
46+
});
47+
ex.Message.ShouldBe("Domain Event must contain an Event Id");
48+
}
49+
50+
[Fact]
51+
public async Task DomainEventController_EventIdPresentInJson_NoErrorThrown()
52+
{
53+
Mock<IDomainEventHandlerResolver> resolver = new Mock<IDomainEventHandlerResolver>();
54+
TypeMap.AddType<TransactionHasBeenCompletedEvent>("TransactionHasBeenCompletedEvent");
55+
DefaultHttpContext httpContext = new DefaultHttpContext();
56+
httpContext.Request.Headers["eventType"] = "TransactionHasBeenCompletedEvent";
57+
DomainEventController controller = new DomainEventController(resolver.Object)
58+
{
59+
ControllerContext = new ControllerContext()
60+
{
61+
HttpContext = httpContext
62+
}
63+
};
64+
String json = "{\r\n \"completedDateTime\": \"2022-11-08T15:40:07\",\r\n \"estateId\": \"435613ac-a468-47a3-ac4f-649d89764c22\",\r\n \"isAuthorised\": true,\r\n \"transactionAmount\": 35.0,\r\n \"merchantId\": \"8bc8434d-41f9-4cc3-83bc-e73f20c02e1d\",\r\n \"responseCode\": \"0000\",\r\n \"responseMessage\": \"SUCCESS\",\r\n \"transactionId\": \"626644c5-bb7b-40ca-821e-cf115488867b\",\r\n \"eventId\": \"9840045a-df9f-4ae3-879d-db205a744bf3\"\r\n}";
65+
Object request = JsonConvert.DeserializeObject(json);
66+
Should.NotThrow(async () => {
67+
await controller.PostEventAsync(request, CancellationToken.None);
68+
});
69+
}
70+
}
71+
}

TransactionProcessor/Controllers/DomainEventController.cs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace TransactionProcessor.Controllers
99
using System.Threading;
1010
using Microsoft.AspNetCore.Mvc;
1111
using Newtonsoft.Json;
12+
using Newtonsoft.Json.Linq;
1213
using Shared.DomainDrivenDesign.EventSourcing;
1314
using Shared.EventStore.Aggregate;
1415
using Shared.EventStore.EventHandling;
@@ -94,11 +95,6 @@ public async Task<IActionResult> PostEventAsync([FromBody] Object request,
9495
}
9596
}
9697

97-
/// <summary>
98-
/// Callbacks the specified cancellation token.
99-
/// </summary>
100-
/// <param name="cancellationToken">The cancellation token.</param>
101-
/// <param name="eventId">The event identifier.</param>
10298
private void Callback(CancellationToken cancellationToken,
10399
Guid eventId)
104100
{
@@ -109,22 +105,17 @@ private void Callback(CancellationToken cancellationToken,
109105
}
110106
}
111107

112-
/// <summary>
113-
/// Gets the domain event.
114-
/// </summary>
115-
/// <param name="domainEvent">The domain event.</param>
116-
/// <returns></returns>
117108
private async Task<IDomainEvent> GetDomainEvent(Object domainEvent)
118109
{
119-
String eventType = this.Request.Query["eventType"].ToString();
110+
String eventType = this.Request.Headers["eventType"].ToString();
120111

121-
var type = TypeMap.GetType(eventType);
112+
Type type = TypeMap.GetType(eventType);
122113

123114
if (type == null)
124115
throw new Exception($"Failed to find a domain event with type {eventType}");
125116

126117
JsonIgnoreAttributeIgnorerContractResolver jsonIgnoreAttributeIgnorerContractResolver = new JsonIgnoreAttributeIgnorerContractResolver();
127-
var jsonSerialiserSettings = new JsonSerializerSettings
118+
JsonSerializerSettings jsonSerialiserSettings = new JsonSerializerSettings
128119
{
129120
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
130121
TypeNameHandling = TypeNameHandling.All,
@@ -135,15 +126,28 @@ private async Task<IDomainEvent> GetDomainEvent(Object domainEvent)
135126

136127
if (type.IsSubclassOf(typeof(DomainEvent)))
137128
{
138-
var json = JsonConvert.SerializeObject(domainEvent, jsonSerialiserSettings);
139-
DomainEventFactory domainEventFactory = new();
129+
String json = JsonConvert.SerializeObject(domainEvent, jsonSerialiserSettings);
140130

141-
return domainEventFactory.CreateDomainEvent(json, type);
131+
DomainEventFactory domainEventFactory = new();
132+
String validatedJson = this.ValidateEvent(json);
133+
return domainEventFactory.CreateDomainEvent(validatedJson, type);
142134
}
143135

144136
return null;
145137
}
146138

139+
private String ValidateEvent(String domainEventJson)
140+
{
141+
JObject domainEvent = JObject.Parse(domainEventJson);
142+
143+
if (domainEvent.ContainsKey("eventId") == false || domainEvent["eventId"].ToObject<Guid>() == Guid.Empty)
144+
{
145+
throw new ArgumentException("Domain Event must contain an Event Id");
146+
}
147+
148+
return domainEventJson;
149+
}
150+
147151
#endregion
148152

149153
#region Others

0 commit comments

Comments
 (0)