diff --git a/src/Testing/CoreTests/Persistence/Sagas/saga_identity_precedence.cs b/src/Testing/CoreTests/Persistence/Sagas/saga_identity_precedence.cs new file mode 100644 index 000000000..0dd08f7b6 --- /dev/null +++ b/src/Testing/CoreTests/Persistence/Sagas/saga_identity_precedence.cs @@ -0,0 +1,169 @@ +using Microsoft.Extensions.Logging; +using Wolverine.Persistence.Sagas; +using Wolverine.Tracking; +using Xunit; + +namespace CoreTests.Persistence.Sagas; + +// Because of https://github.com/JasperFx/wolverine/issues/2095 +public class saga_identity_precedence : IntegrationContext +{ + public saga_identity_precedence(DefaultApp @default) : base(@default) + { + } + + [Fact] + public async Task use_the_correct_saga_id_from_message() + { + var startMsg = new StartParent("ParentId", "SubId"); + + // Create the saga + var startSession = await Host.TrackActivity().PublishMessageAndWaitAsync(startMsg); + startSession.FindEnvelopesWithMessageType(); + + var comSession = await Host.TrackActivity() + .PublishMessageAndWaitAsync(new SendComToSubSaga(startMsg.ParentSagaId, startMsg.SubSagaId)); + + comSession.FindSingleTrackedMessageOfType(); + comSession.FindSingleTrackedMessageOfType(); + } + + [Fact] + public async Task use_the_correct_saga_id_from_message_int_saga_id_type() + { + var startMsg = new StartParent2(1, 2); + + // Create the saga + var startSession = await Host.TrackActivity().PublishMessageAndWaitAsync(startMsg); + startSession.FindEnvelopesWithMessageType(); + + var comSession = await Host.TrackActivity() + .PublishMessageAndWaitAsync(new SendComToSubSaga2(startMsg.ParentSagaId, startMsg.SubSagaId)); + + comSession.FindSingleTrackedMessageOfType(); + comSession.FindSingleTrackedMessageOfType(); + } +} + +public sealed record StartParent(string ParentSagaId, string SubSagaId); +public sealed record StartSub(string ParentSagaId, string SubSagaId); +public sealed record SendComToSubSaga(string ParentSagaId, string SubSagaId); +public sealed record SubSagaMsg([property: SagaIdentity] string SubSagaId); +public sealed record ParentSagaMsg([property: SagaIdentity] string ParentSagaId); + +public class ParentSaga : Saga +{ + public required string Id { get; set; } + + public static (ParentSaga, StartSub) Start(StartParent msg, Logger logger) + { + logger.LogInformation("ParentSaga Id: {SagaIdentity}", msg.ParentSagaId); + return (new ParentSaga { Id = msg.ParentSagaId }, new StartSub(msg.ParentSagaId, msg.SubSagaId)); + } + + public static void NotFound(ParentSagaMsg msg, Envelope envelope, Logger logger) + { + logger.LogInformation("NotFound(ParentSagaMsg): Envelope saga identity {EnvelopeIdentity}; message {@Msg}", envelope.SagaId, msg); + } + + public void Handle( + [SagaIdentityFrom(nameof(ParentSagaMsg.ParentSagaId))] ParentSagaMsg msg, + Envelope envelope, + Logger logger + ) + { + logger.LogInformation("ParentSagaMsg: Envelope saga identity {EnvelopeIdentity}; message {@Msg}", envelope.SagaId, msg); + } + + public SubSagaMsg Handle([SagaIdentityFrom(nameof(SendComToSubSaga.ParentSagaId))] SendComToSubSaga msg) + { + return new SubSagaMsg(msg.SubSagaId); + } +} + +public class SubSaga : Saga +{ + public required string Id { get; set; } + public required string ParentId { get; set; } + + public static SubSaga Start(StartSub msg, Logger logger) + { + logger.LogInformation("SubSaga Id: {SagaIdentity}", msg.SubSagaId); + return new SubSaga { Id = msg.SubSagaId, ParentId = msg.ParentSagaId }; + } + + public static void NotFound(SubSagaMsg msg, Envelope envelope, Logger logger) + { + logger.LogInformation("NotFound(SubSagaMsg): Envelope saga identity {EnvelopeIdentity}; message {@Msg}", envelope.SagaId, msg); + } + + public ParentSagaMsg Handle([SagaIdentityFrom(nameof(SubSagaMsg.SubSagaId))] SubSagaMsg msg, Envelope envelope, Logger logger) + { + logger.LogInformation("SubSagaMsg: Envelope saga identity {EnvelopeIdentity}; message {@Msg}", envelope.SagaId, msg); + + return new ParentSagaMsg(ParentId); + } +} + + +/******** Numbered **************/ + +public sealed record StartParent2(int ParentSagaId, int SubSagaId); +public sealed record StartSub2(int ParentSagaId, int SubSagaId); +public sealed record SendComToSubSaga2(int ParentSagaId, int SubSagaId); +public sealed record SubSagaMsg2([property: SagaIdentity] int SubSagaId); +public sealed record ParentSagaMsg2([property: SagaIdentity] int ParentSagaId); + +public class ParentSaga2 : Saga +{ + public required int Id { get; set; } + + public static (ParentSaga2, StartSub2) Start(StartParent2 msg, Logger logger) + { + logger.LogInformation("ParentSaga Id: {SagaIdentity}", msg.ParentSagaId); + return (new ParentSaga2 { Id = msg.ParentSagaId }, new StartSub2(msg.ParentSagaId, msg.SubSagaId)); + } + + public static void NotFound(ParentSagaMsg2 msg, Envelope envelope, Logger logger) + { + logger.LogInformation("NotFound(ParentSagaMsg): Envelope saga identity {EnvelopeIdentity}; message {@Msg}", envelope.SagaId, msg); + } + + public void Handle( + [SagaIdentityFrom(nameof(ParentSagaMsg.ParentSagaId))] ParentSagaMsg2 msg, + Envelope envelope, + Logger logger + ) + { + logger.LogInformation("ParentSagaMsg: Envelope saga identity {EnvelopeIdentity}; message {@Msg}", envelope.SagaId, msg); + } + + public SubSagaMsg2 Handle([SagaIdentityFrom(nameof(SendComToSubSaga.ParentSagaId))] SendComToSubSaga2 msg) + { + return new SubSagaMsg2(msg.SubSagaId); + } +} + +public class SubSaga2 : Saga +{ + public required int Id { get; set; } + public required int ParentId { get; set; } + + public static SubSaga2 Start(StartSub2 msg, Logger logger) + { + logger.LogInformation("SubSaga Id: {SagaIdentity}", msg.SubSagaId); + return new SubSaga2 { Id = msg.SubSagaId, ParentId = msg.ParentSagaId }; + } + + public static void NotFound(SubSagaMsg2 msg, Envelope envelope, Logger logger) + { + logger.LogInformation("NotFound(SubSagaMsg): Envelope saga identity {EnvelopeIdentity}; message {@Msg}", envelope.SagaId, msg); + } + + public ParentSagaMsg2 Handle([SagaIdentityFrom(nameof(SubSagaMsg.SubSagaId))] SubSagaMsg2 msg, Envelope envelope, Logger logger) + { + logger.LogInformation("SubSagaMsg: Envelope saga identity {EnvelopeIdentity}; message {@Msg}", envelope.SagaId, msg); + + return new ParentSagaMsg2(ParentId); + } +} \ No newline at end of file diff --git a/src/Wolverine/Persistence/Sagas/PullSagaIdFromMessageFrame.cs b/src/Wolverine/Persistence/Sagas/PullSagaIdFromMessageFrame.cs index cb024da35..6bb91c734 100644 --- a/src/Wolverine/Persistence/Sagas/PullSagaIdFromMessageFrame.cs +++ b/src/Wolverine/Persistence/Sagas/PullSagaIdFromMessageFrame.cs @@ -37,7 +37,7 @@ public override void GenerateCode(GeneratedMethod method, ISourceWriter writer) if (_sagaIdType == typeof(string)) { writer.Write( - $"{_sagaIdType.NameInCode()} {SagaChain.SagaIdVariableName} = {_envelope!.Usage}.{nameof(Envelope.SagaId)} ?? {_message!.Usage}.{_sagaIdMember.Name};"); + $"{_sagaIdType.NameInCode()} {SagaChain.SagaIdVariableName} = {_message!.Usage}.{_sagaIdMember.Name} ?? {_envelope!.Usage}.{nameof(Envelope.SagaId)};"); writer.Write( $"if (string.{nameof(string.IsNullOrEmpty)}({SagaChain.SagaIdVariableName})) throw new {typeof(IndeterminateSagaStateIdException).FullName}({_envelope.Usage});"); } @@ -47,9 +47,9 @@ public override void GenerateCode(GeneratedMethod method, ISourceWriter writer) ? typeof(Guid).FullName : _sagaIdType!.NameInCode(); - + writer.Write($"{typeNameInCode} {SagaChain.SagaIdVariableName} = {_message!.Usage}.{_sagaIdMember.Name};"); writer.Write( - $"if (!{typeNameInCode}.TryParse({_envelope!.Usage}.{nameof(Envelope.SagaId)}, out {typeNameInCode} sagaId)) sagaId = {_message!.Usage}.{_sagaIdMember.Name};"); + $"if ({SagaChain.SagaIdVariableName} == default && !{typeNameInCode}.TryParse({_envelope!.Usage}.{nameof(Envelope.SagaId)}, out sagaId)) sagaId = {_message!.Usage}.{_sagaIdMember.Name};"); if (_sagaIdType == typeof(Guid)) {