Skip to content

Commit 1a95432

Browse files
authored
Populate originator property on saga creation (#457) (#465)
* add failing tests * Set originator value when creating new saga
1 parent 2f41833 commit 1a95432

File tree

2 files changed

+94
-11
lines changed

2 files changed

+94
-11
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: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,11 @@ async Task<HandleResult> InnerHandle(QueuedSagaMessage message, string handleMet
210210

211211
using (var session = new NonDurableSynchronizedStorageSession())
212212
{
213-
var contextBag = new ContextBag();
214-
215-
var loadResult = await LoadSagaData(message, session, contextBag).ConfigureAwait(false);
213+
var loadResult = await LoadSagaData(message, session, context).ConfigureAwait(false);
216214
saga.Entity = loadResult.Item1;
217215

218216
await sagaMapper.InvokeHandlerMethod(saga, handleMethodName, message, context).ConfigureAwait(false);
219-
await SaveSagaData(saga, loadResult.Item2, loadResult.Item3, session, contextBag).ConfigureAwait(false);
217+
await SaveSagaData(saga, loadResult.Item2, loadResult.Item3, session, context.Extensions).ConfigureAwait(false);
220218
await session.CompleteAsync().ConfigureAwait(false);
221219
}
222220

@@ -270,16 +268,14 @@ async Task<HandleResult> HandleTimeout(OutgoingMessage<object, SendOptions> time
270268
return await InnerHandle(queueMessage, methodName, context).ConfigureAwait(false);
271269
}
272270

273-
async Task<Tuple<TSagaData, bool, object>> LoadSagaData(QueuedSagaMessage message, SynchronizedStorageSession session, ContextBag contextBag)
271+
async Task<Tuple<TSagaData, bool, object>> LoadSagaData(QueuedSagaMessage message, SynchronizedStorageSession session, TestableMessageHandlerContext context)
274272
{
275273
var messageMetadata = sagaMapper.GetMessageMetadata(message.Type);
276274
TSagaData sagaData;
277275

278-
if (message.Headers != null &&
279-
message.Headers.TryGetValue(Headers.SagaId, out var sagaIdString) &&
280-
Guid.TryParse(sagaIdString, out Guid sagaId))
276+
if (message.Headers.TryGetValue(Headers.SagaId, out var sagaIdString) && Guid.TryParse(sagaIdString, out Guid sagaId))
281277
{
282-
sagaData = await persister.Get<TSagaData>(sagaId, session, contextBag).ConfigureAwait(false);
278+
sagaData = await persister.Get<TSagaData>(sagaId, session, context.Extensions).ConfigureAwait(false);
283279
if (sagaData != null)
284280
{
285281
return new Tuple<TSagaData, bool, object>(sagaData, false, null);
@@ -288,7 +284,7 @@ async Task<Tuple<TSagaData, bool, object>> LoadSagaData(QueuedSagaMessage messag
288284

289285
var messageMappedValue = sagaMapper.GetMessageMappedValue(message);
290286

291-
sagaData = await persister.Get<TSagaData>(sagaMapper.CorrelationPropertyName, messageMappedValue, session, contextBag).ConfigureAwait(false);
287+
sagaData = await persister.Get<TSagaData>(sagaMapper.CorrelationPropertyName, messageMappedValue, session, context.Extensions).ConfigureAwait(false);
292288

293289
if (sagaData != null)
294290
{
@@ -297,7 +293,10 @@ async Task<Tuple<TSagaData, bool, object>> LoadSagaData(QueuedSagaMessage messag
297293

298294
if (messageMetadata.IsAllowedToStartSaga)
299295
{
300-
sagaData = new TSagaData { Id = Guid.NewGuid() };
296+
var originatorAddress = message.Headers.TryGetValue(Headers.ReplyToAddress, out var replyAddress)
297+
? replyAddress
298+
: context.ReplyToAddress; // This property has a default value set even when the header isn't set to require less setup for testing
299+
sagaData = new TSagaData { Id = Guid.NewGuid(), Originator = originatorAddress };
301300
sagaMapper.SetCorrelationPropertyValue(sagaData, messageMappedValue);
302301
return new Tuple<TSagaData, bool, object>(sagaData, true, messageMappedValue);
303302
}

0 commit comments

Comments
 (0)