Skip to content

Commit

Permalink
Add generic json mapper
Browse files Browse the repository at this point in the history
add base mapper

feat: updated messagemapper to use native json serializer
  • Loading branch information
Toby Henderson authored and holytshirt committed Feb 7, 2022
1 parent 3950599 commit 8856ee8
Show file tree
Hide file tree
Showing 8 changed files with 341 additions and 0 deletions.
59 changes: 59 additions & 0 deletions Paramore.Brighter.Perf/Benchmark.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using BenchmarkDotNet.Attributes;

namespace Paramore.Brighter.Perf
{
public class Benchmark
{
private readonly JsonMessageMapper<TestCommand> _jsonMessageMapper;
private readonly JsonMessageMapper2<TestCommand> _mapper2;
private readonly Message _message;
private readonly TestCommand _testCommand;

public Benchmark()
{
RequestContext requestContext = new RequestContext();
_jsonMessageMapper = new JsonMessageMapper<TestCommand>(requestContext);
_mapper2 = new JsonMessageMapper2<TestCommand>(requestContext);

DateTime dateTime = DateTime.UtcNow;
_testCommand = new TestCommand
{
Message = "This is a message",
Number = 999,
DateNow = dateTime
};

string body =
"{\"message\":\"This is a message\",\"number\":999,\"dateNow\":\"2019-04-09T15:06:56.7623017Z\",\"id\":\"7d9120b9-a18e-43ac-a63e-8201a43ea623\"}";
Guid correlationId = Guid.NewGuid();
_message = new Message(
new MessageHeader(new Guid("7d9120b9-a18e-43ac-a63e-8201a43ea623"), "Blah", MessageType.MT_COMMAND,
correlationId), new MessageBody(body));
}

[Benchmark]
public void MapToMessageJsonByte()
{
_jsonMessageMapper.MapToMessage(_testCommand);
}

[Benchmark]
public void MapFromMessageJsonByte()
{
_jsonMessageMapper.MapToRequest(_message);
}

[Benchmark]
public void MapToMessageJsonString()
{
_mapper2.MapToMessage(_testCommand);
}

[Benchmark]
public void MapFromMessageJsonString()
{
_mapper2.MapToRequest(_message);
}
}
}
16 changes: 16 additions & 0 deletions Paramore.Brighter.Perf/Paramore.Brighter.Perf.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.11.5" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\src\Paramore.Brighter\Paramore.Brighter.csproj" />
</ItemGroup>

</Project>
26 changes: 26 additions & 0 deletions Paramore.Brighter.Perf/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using BenchmarkDotNet.Running;

namespace Paramore.Brighter.Perf
{
class Program
{
static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Benchmark>();
Console.WriteLine(summary);
}
}


public class TestCommand : Command
{
public TestCommand() : base(Guid.NewGuid())
{
}

public string Message { get; set; }
public int Number { get; set; }
public DateTime DateNow { get; set; }
}
}
45 changes: 45 additions & 0 deletions src/Paramore.Brighter/BaseMessageMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;

namespace Paramore.Brighter
{
public abstract class BaseMessageMapper<T> : IAmAMessageMapper<T> where T : class, IRequest
{
private readonly IRequestContext _requestContext;
private readonly Func<T, string> _routingAction;
private readonly RoutingKey _routingKey;

protected BaseMessageMapper(IRequestContext requestContext, RoutingKey routingKey = null, Func<T, string> routingKeyFunc = null)
{
_requestContext = requestContext;
_routingKey = routingKey;
_routingAction = routingKeyFunc;
}

public Message MapToMessage(T request)
{
MessageType messageType = request switch
{
Command _ => MessageType.MT_COMMAND,
Event _ => MessageType.MT_EVENT,
_ => throw new ArgumentException("This message mapper can only map Commands and Events", nameof(request))
};

var topic = _routingAction?.Invoke(request) ?? _routingKey ?? request.GetType().Name;

var messageHeader = new MessageHeader(request.Id, topic, messageType, _requestContext.Header.CorrelationId, contentType: "application/json");

return new Message(messageHeader, CreateMessageBody(request));
}

protected abstract MessageBody CreateMessageBody(T request);

public T MapToRequest(Message message)
{
_requestContext.Header.CorrelationId = message.Header.CorrelationId;

return CreateType(message);
}

protected abstract T CreateType(Message message);
}
}
2 changes: 2 additions & 0 deletions src/Paramore.Brighter/IRequestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,7 @@ public interface IRequestContext
/// Gets the Feature Switches
/// </summary>
IAmAFeatureSwitchRegistry FeatureSwitches { get; }

Header Header { get; }
}
}
23 changes: 23 additions & 0 deletions src/Paramore.Brighter/JsonMessageMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Text.Json;

namespace Paramore.Brighter
{
public class JsonMessageMapper<T> : BaseMessageMapper<T> where T : class, IRequest
{
public JsonMessageMapper(IRequestContext requestContext, RoutingKey routingKey = null,
Func<T, string> routingKeyFunc = null) : base(requestContext, routingKey, routingKeyFunc)
{
}

protected override MessageBody CreateMessageBody(T request)
{
return new MessageBody(JsonSerializer.SerializeToUtf8Bytes(request, JsonSerialisationOptions.Options), "JSON");
}

protected override T CreateType(Message message)
{
return JsonSerializer.Deserialize<T>(message.Body.Bytes, JsonSerialisationOptions.Options);
}
}
}
8 changes: 8 additions & 0 deletions src/Paramore.Brighter/RequestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ THE SOFTWARE. */

#endregion

using System;
using System.Collections.Generic;
using Paramore.Brighter.FeatureSwitch;
using Polly.Registry;
Expand Down Expand Up @@ -58,5 +59,12 @@ public RequestContext()
/// Gets the Feature Switches
/// </summary>
public IAmAFeatureSwitchRegistry FeatureSwitches { get; set; }

public Header Header { get; } = new Header();
}

public class Header
{
public Guid CorrelationId { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
using System;
using Xunit;

namespace Paramore.Brighter.Core.Tests.MessageMapper
{
public class JsonMessageMapperTests
{
[Fact]
public void When_mapping_an_event_to_a_message_as_json()
{
var requestContext = new RequestContext();

var mapper = new JsonMessageMapper<TestedEvent>(requestContext);

DateTime dateTime = DateTime.UtcNow;
TestedEvent testedEvent = new TestedEvent
{
Message = "This is a message",
Number = 999,
DateNow = dateTime
};

requestContext.Header.CorrelationId = Guid.NewGuid();

var message = mapper.MapToMessage(testedEvent);

// Message checks
Assert.Equal(testedEvent.Id, message.Id);

// Header checks
Assert.Equal("application/json", message.Header.ContentType);
Assert.Equal("TestedEvent", message.Header.Topic);
Assert.Equal(MessageType.MT_EVENT , message.Header.MessageType);
Assert.Equal( requestContext.Header.CorrelationId , message.Header.CorrelationId);

// Body checks
Assert.Equal("JSON", message.Body.BodyType);
Assert.Equal($"{{\"message\":\"{testedEvent.Message}\",\"number\":{testedEvent.Number},\"dateNow\":\"{dateTime:yyyy-MM-ddTHH:mm:ss.FFFFFFFK}\",\"id\":\"{testedEvent.Id}\"}}", message.Body.Value);
// Why not just datetime:O well the JsonSerializer does 7 decimal places with no trailing zeros, format "O" does 7 decimal places with trailing zeros
}


[Fact]
public void When_mapping_a_command_to_a_message_as_json()
{
var requestContext = new RequestContext();

var mapper = new JsonMessageMapper<TestCommand>(requestContext);

DateTime dateTime = DateTime.UtcNow;
TestCommand testCommand = new TestCommand
{
Message = "This is a message",
Number = 999,
DateNow = dateTime
};

requestContext.Header.CorrelationId = Guid.NewGuid();

var message = mapper.MapToMessage(testCommand);

// Message checks
Assert.Equal(testCommand.Id, message.Id);

// Header checks
Assert.Equal("application/json", message.Header.ContentType);
Assert.Equal("TestCommand", message.Header.Topic);
Assert.Equal(MessageType.MT_COMMAND , message.Header.MessageType);
Assert.Equal( requestContext.Header.CorrelationId , message.Header.CorrelationId);

// Body checks
Assert.Equal("JSON", message.Body.BodyType);
Assert.Equal($"{{\"message\":\"{testCommand.Message}\",\"number\":{testCommand.Number},\"dateNow\":\"{dateTime:yyyy-MM-ddTHH:mm:ss.FFFFFFFK}\",\"id\":\"{testCommand.Id}\"}}", message.Body.Value);
}

[Fact]
public void when_mapping_to_a_command_from_json()
{
var requestContext = new RequestContext();
var mapper = new JsonMessageMapper<TestCommand>(requestContext);


var body = "{\"message\":\"This is a message\",\"number\":999,\"dateNow\":\"2019-04-09T15:06:56.7623017Z\",\"id\":\"7d9120b9-a18e-43ac-a63e-8201a43ea623\"}";
var correlationId = Guid.NewGuid();
var message = new Message(new MessageHeader(new Guid("7d9120b9-a18e-43ac-a63e-8201a43ea623"),"Blah", MessageType.MT_COMMAND, correlationId: correlationId), new MessageBody(body));

var testCommand = mapper.MapToRequest(message);


Assert.Equal("7d9120b9-a18e-43ac-a63e-8201a43ea623", testCommand.Id.ToString());
Assert.Equal("This is a message", testCommand.Message);
Assert.Equal(999, testCommand.Number);
Assert.Equal(DateTime.Parse("2019-04-09T15:06:56.7623017Z").ToUniversalTime(), testCommand.DateNow);

Assert.Equal(correlationId, requestContext.Header.CorrelationId);
}

[Fact]
public void When_mapping_with_custom_routing_key_to_a_message()
{
var mapper = new JsonMessageMapper<TestCommand>(new RequestContext(), new RoutingKey("MyTestRoute"));

var testCommand = new TestCommand();

var message = mapper.MapToMessage(testCommand);

Assert.Equal("MyTestRoute", message.Header.Topic);
}

[Fact]
public void When_mapping_with_custom_routing_key_to_a_message2()
{
var mapper = new JsonMessageMapper<TestCommand>(new RequestContext(), routingKeyFunc: request =>
{
string topic = "TestPreAmble.";

string name = request.GetType().Name;

if (name.EndsWith("Command", StringComparison.InvariantCultureIgnoreCase))
topic = topic + name.Replace("Command", "", StringComparison.InvariantCultureIgnoreCase);
else if (name.EndsWith("Event", StringComparison.InvariantCultureIgnoreCase))
topic = topic + name.Replace("Event", "", StringComparison.InvariantCultureIgnoreCase);
else
{
topic = topic + name;
}

return topic;
} );

var testCommand = new TestCommand();

var message = mapper.MapToMessage(testCommand);

Assert.Equal("TestPreAmble.Test", message.Header.Topic);
}


}

public class TestCommand : Command
{
public TestCommand() : base(Guid.NewGuid())
{
}

public string Message { get; set; }
public int Number { get; set; }
public DateTime DateNow { get; set; }
}

public class TestedEvent : Event
{
public TestedEvent() : base(Guid.NewGuid())
{
}

public string Message { get; set; }
public int Number { get; set; }
public DateTime DateNow { get; set; }
}
}

0 comments on commit 8856ee8

Please sign in to comment.