Skip to content

Commit e15fb26

Browse files
authored
Populate originator property on saga creation (#457) (#464)
* add failing tests * Set originator value when creating new saga
1 parent da5eaf8 commit e15fb26

File tree

2 files changed

+93
-8
lines changed

2 files changed

+93
-8
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
namespace NServiceBus.Testing.Tests.Sagas
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using NUnit.Framework;
8+
9+
[TestFixture]
10+
public class ReplyToOriginator
11+
{
12+
[Test]
13+
public async Task ReplyToOriginatorShouldReplyToInitialOriginator()
14+
{
15+
const string originatorAddress = "expectedReplyAddress";
16+
17+
var saga = new TestableSaga<ReplyingSaga, ReplyingSagaData>();
18+
// the Originator value is populated by the header value, not the context property
19+
await saga.Handle(new StartSagaMessage() { CorrelationProperty = Guid.NewGuid() }, messageHeaders: new Dictionary<string, string>()
20+
{
21+
{Headers.ReplyToAddress, originatorAddress}
22+
});
23+
var result = await saga.HandleQueuedMessage();
24+
25+
var reply = result.Context.RepliedMessages.SingleOrDefault();
26+
Assert.NotNull(reply);
27+
Assert.AreEqual(originatorAddress, reply.Options.GetDestination());
28+
Assert.AreEqual(originatorAddress, reply.Message<ReplyMessage>().OriginatorAddress);
29+
}
30+
31+
[Test]
32+
public async Task OriginatorShouldBeSetByDefault()
33+
{
34+
// ensure the testing API also works without explicitly defining a replyTo header value
35+
var saga = new TestableSaga<ReplyingSaga, ReplyingSagaData>();
36+
37+
await saga.Handle(new StartSagaMessage() { CorrelationProperty = Guid.NewGuid() });
38+
var result = await saga.HandleQueuedMessage();
39+
40+
var reply = result.Context.RepliedMessages.SingleOrDefault();
41+
Assert.NotNull(reply);
42+
string replyAddress = reply.Options.GetDestination();
43+
Assert.NotNull(replyAddress);
44+
Assert.AreEqual(replyAddress, reply.Message<ReplyMessage>().OriginatorAddress);
45+
}
46+
47+
class ReplyingSaga : NServiceBus.Saga<ReplyingSagaData>, IAmStartedByMessages<StartSagaMessage>, IHandleMessages<SendReplyMessage>
48+
{
49+
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<ReplyingSagaData> mapper) => mapper
50+
.ConfigureMapping<StartSagaMessage>(m => m.CorrelationProperty)
51+
.ToSaga(s => s.CorrelationProperty);
52+
53+
public Task Handle(StartSagaMessage message, IMessageHandlerContext context)
54+
{
55+
return context.SendLocal(new SendReplyMessage());
56+
}
57+
58+
public Task Handle(SendReplyMessage message, IMessageHandlerContext context)
59+
{
60+
return ReplyToOriginator(context, new ReplyMessage { OriginatorAddress = Data.Originator });
61+
}
62+
}
63+
64+
class ReplyingSagaData : ContainSagaData
65+
{
66+
public Guid CorrelationProperty { get; set; }
67+
}
68+
69+
class StartSagaMessage : IMessage
70+
{
71+
public Guid CorrelationProperty { get; set; }
72+
}
73+
74+
class SendReplyMessage : ICommand
75+
{
76+
public Guid CorrelationProperty { get; set; }
77+
}
78+
79+
class ReplyMessage : IMessage
80+
{
81+
public string OriginatorAddress { get; set; }
82+
}
83+
}
84+
}

src/NServiceBus.Testing/Sagas/TestableSaga.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ async Task<HandleResult> InnerHandle(QueuedSagaMessage message, string handleMet
213213
{
214214
await session.Open(context.Extensions, context.CancellationToken).ConfigureAwait(false);
215215

216-
var (data, isNew, mappedValue) = await LoadSagaData(message, session, context.Extensions, context.CancellationToken).ConfigureAwait(false);
216+
var (data, isNew, mappedValue) = await LoadSagaData(message, session, context).ConfigureAwait(false);
217217
saga.Entity = data;
218218

219219
await sagaMapper.InvokeHandlerMethod(saga, handleMethodName, message, context).ConfigureAwait(false);
@@ -279,16 +279,14 @@ async Task<HandleResult> HandleTimeout(OutgoingMessage<object, SendOptions> time
279279
}
280280

281281
async Task<(TSagaData Data, bool IsNew, object MappedValue)> LoadSagaData(
282-
QueuedSagaMessage message, ISynchronizedStorageSession session, ContextBag contextBag, CancellationToken cancellationToken)
282+
QueuedSagaMessage message, ISynchronizedStorageSession session, TestableMessageHandlerContext context)
283283
{
284284
var messageMetadata = sagaMapper.GetMessageMetadata(message.Type);
285285
TSagaData sagaData;
286286

287-
if (message.Headers != null &&
288-
message.Headers.TryGetValue(Headers.SagaId, out var sagaIdString) &&
289-
Guid.TryParse(sagaIdString, out Guid sagaId))
287+
if (message.Headers.TryGetValue(Headers.SagaId, out var sagaIdString) && Guid.TryParse(sagaIdString, out Guid sagaId))
290288
{
291-
sagaData = await persister.Get<TSagaData>(sagaId, session, contextBag, cancellationToken).ConfigureAwait(false);
289+
sagaData = await persister.Get<TSagaData>(sagaId, session, context.Extensions, context.CancellationToken).ConfigureAwait(false);
292290
if (sagaData != null)
293291
{
294292
return (sagaData, false, null);
@@ -297,7 +295,7 @@ async Task<HandleResult> HandleTimeout(OutgoingMessage<object, SendOptions> time
297295

298296
var messageMappedValue = sagaMapper.GetMessageMappedValue(message);
299297

300-
sagaData = await persister.Get<TSagaData>(sagaMapper.CorrelationPropertyName, messageMappedValue, session, contextBag, cancellationToken).ConfigureAwait(false);
298+
sagaData = await persister.Get<TSagaData>(sagaMapper.CorrelationPropertyName, messageMappedValue, session, context.Extensions, context.CancellationToken).ConfigureAwait(false);
301299

302300
if (sagaData != null)
303301
{
@@ -306,7 +304,10 @@ async Task<HandleResult> HandleTimeout(OutgoingMessage<object, SendOptions> time
306304

307305
if (messageMetadata.IsAllowedToStartSaga)
308306
{
309-
sagaData = new TSagaData { Id = Guid.NewGuid() };
307+
var originatorAddress = message.Headers.TryGetValue(Headers.ReplyToAddress, out var replyAddress)
308+
? replyAddress
309+
: context.ReplyToAddress; // This property has a default value set even when the header isn't set to require less setup for testing
310+
sagaData = new TSagaData { Id = Guid.NewGuid(), Originator = originatorAddress };
310311
sagaMapper.SetCorrelationPropertyValue(sagaData, messageMappedValue);
311312
return (sagaData, true, messageMappedValue);
312313
}

0 commit comments

Comments
 (0)