├── .github
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── build-and-publish.yml
│ ├── build-and-test.yml
│ └── snyk-test.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Examples
├── Example.Application.Testing
│ ├── Example.Application.Testing.csproj
│ ├── ExampleService.cs
│ ├── FakeReceiverMessage.cs
│ ├── FizzBuzzEngine.cs
│ ├── IDatabase.cs
│ ├── README.md
│ └── Tests
│ │ ├── ExampleServiceTests.cs
│ │ └── FizzBuzzEngineTests.cs
├── Example.Common.Tests
│ ├── CommandSendingServiceTests.cs
│ ├── DataSendingServiceTests.cs
│ ├── Example.Common.Tests.csproj
│ ├── FakeReceiverMessage.cs
│ ├── ReceivingServiceTests.cs
│ ├── SendingServiceTests.cs
│ └── SingleMessageServiceTests.cs
├── Example.Common
│ ├── Casing.cs
│ ├── CommandSendingService.cs
│ ├── DataSendingService.cs
│ ├── Example.Common.csproj
│ ├── ReceivingService.cs
│ ├── SendingService.cs
│ └── SingleMessageService.cs
├── Example.Messaging.CloudEvents
│ ├── Example.Messaging.CloudEvents.csproj
│ ├── ExampleOptions.cs
│ ├── ExampleService.cs
│ ├── Program.cs
│ └── appsettings.json
├── Example.Messaging.Http
│ ├── Example.Messaging.Http.csproj
│ ├── Program.cs
│ └── appsettings.json
├── Example.Messaging.Kafka
│ ├── Example.Messaging.Kafka.csproj
│ ├── Program.cs
│ └── appsettings.json
├── Example.Messaging.NamedPipes
│ ├── Example.Messaging.NamedPipes.csproj
│ ├── Program.cs
│ └── appsettings.json
├── Example.Messaging.RabbitMQ
│ ├── Example.Messaging.RabbitMQ.csproj
│ ├── Program.cs
│ └── appsettings.json
├── Example.Messaging.SNS
│ ├── Example.Messaging.SNS.csproj
│ └── Program.cs
└── Example.Messaging.SQS
│ ├── Example.Messaging.SQS.csproj
│ ├── Program.cs
│ └── appsettings.json
├── LICENSE.md
├── README.md
├── RockLib.Messaging.All.sln
├── RockLib.Messaging.CloudEvents
├── .editorconfig
├── CHANGELOG.md
├── CloudEvent.cs
├── CloudEventExtensions.CopyConstructor.cs
├── CloudEventExtensions.MessageConstructor.cs
├── CloudEventExtensions.ValidateMethod.cs
├── CloudEventExtensions.cs
├── CloudEventValidationException.cs
├── CorrelatedEvent.cs
├── Correlating
│ └── CorrelatingExtensions.cs
├── DataSerialization.cs
├── Directory.Build.props
├── HttpUtils.cs
├── IProtocolBinding.cs
├── PartitionedEvent.cs
├── Partitioning
│ └── PartitioningExtensions.cs
├── ProtocolBindings.cs
├── RockLib.Messaging.CloudEvents.csproj
├── RockLib.Messaging.CloudEvents.sln
├── SequenceTypes.cs
├── Sequencing
│ └── SequencingExtensions.cs
└── SequentialEvent.cs
├── RockLib.Messaging.Http
├── .editorconfig
├── CHANGELOG.md
├── DefaultHttpResponseGenerator.cs
├── Directory.Build.props
├── HttpClientSender.cs
├── HttpListenerReceiver.cs
├── HttpListenerReceiverMessage.cs
├── HttpResponse.cs
├── HttpUtils.cs
├── IHttpResponseGenerator.cs
├── RequiredHttpRequestHeaders.cs
├── RockLib.Messaging.Http.csproj
└── RockLib.Messaging.Http.sln
├── RockLib.Messaging.Kafka
├── .editorconfig
├── CHANGELOG.md
├── Constants.cs
├── DependencyInjection
│ ├── KafkaExtensions.cs
│ ├── KafkaReceiverOptions.cs
│ └── KafkaSenderOptions.cs
├── Directory.Build.props
├── Exceptions
│ └── InvalidMessageException.cs
├── KafkaReceiver.cs
├── KafkaReceiverMessage.cs
├── KafkaSender.cs
├── MessageExtensions.cs
├── RockLib.Messaging.Kafka.csproj
├── RockLib.Messaging.Kafka.sln
└── StatisticsExtensions.cs
├── RockLib.Messaging.NamedPipes
├── .editorconfig
├── CHANGELOG.md
├── DependencyInjection
│ ├── NamedPipeExtensions.cs
│ └── NamedPipeOptions.cs
├── Directory.Build.props
├── NamedPipeMessage.cs
├── NamedPipeMessageSerializer.cs
├── NamedPipeReceiver.cs
├── NamedPipeReceiverMessage.cs
├── NamedPipeSender.cs
├── RockLib.Messaging.NamedPipes.csproj
└── RockLib.Messaging.NamedPipes.sln
├── RockLib.Messaging.RabbitMQ
├── .editorconfig
├── CHANGELOG.md
├── Directory.Build.props
├── RabbitReceiver.cs
├── RabbitReceiverMessage.cs
├── RabbitSender.cs
├── RockLib.Messaging.RabbitMQ.csproj
└── RockLib.Messaging.RabbitMQ.sln
├── RockLib.Messaging.SNS
├── .editorconfig
├── CHANGELOG.md
├── DependencyInjection
│ ├── SNSExtensions.cs
│ └── SNSSenderOptions.cs
├── Directory.Build.props
├── RockLib.Messaging.SNS.csproj
├── RockLib.Messaging.SNS.sln
└── SNSSender.cs
├── RockLib.Messaging.SQS
├── .editorconfig
├── CHANGELOG.md
├── DependencyInjection
│ ├── SQSExtensions.cs
│ ├── SQSReceiverOptions.cs
│ └── SQSSenderOptions.cs
├── Directory.Build.props
├── InternalsVisibleTo.cs
├── RockLib.Messaging.SQS.csproj
├── RockLib.Messaging.SQS.sln
├── SQSReceiver.cs
├── SQSReceiverMessage.cs
└── SQSSender.cs
├── RockLib.Messaging
├── .editorconfig
├── AssemblyAttributes.cs
├── CHANGELOG.md
├── DependencyInjection
│ ├── DecorationDelegates.cs
│ ├── ForwardingExtensions.cs
│ ├── ForwardingReceiverOptions.cs
│ ├── IForwardingReceiverOptions.cs
│ ├── IReceiverBuilder.cs
│ ├── ISenderBuilder.cs
│ ├── ITransactionalSenderBuilder.cs
│ ├── LookupDelegates.cs
│ ├── MessagingExtensions.cs
│ ├── ReceiverBuilder.cs
│ ├── RegistrationDelegates.cs
│ ├── ReloadingReceiver.cs
│ ├── ReloadingSender.cs
│ ├── SenderBuilder.cs
│ ├── TransactionalSenderBuilder.cs
│ └── ValidationExtensions.cs
├── Directory.Build.props
├── DisconnectedEventArgs.cs
├── ErrorEventArgs.cs
├── ForwardingMessageHandler.cs
├── ForwardingOutcome.cs
├── ForwardingReceiver.cs
├── ForwardingReceiverMessage.cs
├── GZipCompressor.cs
├── GZipDecompressor.cs
├── HeaderDictionary.cs
├── HeaderNames.cs
├── IMessageHandler.cs
├── IReceiver.cs
├── IReceiverMessage.cs
├── ISender.cs
├── ISenderTransaction.cs
├── ITransactionalSender.cs
├── InternalVisibleTo.cs
├── MessagingScenarioFactory.cs
├── NullableAttributes.cs
├── OnMessageReceivedDelegate.cs
├── Receiver.cs
├── ReceiverExtensions.cs
├── ReceiverMessage.cs
├── ReceiverMessageExtensions.cs
├── RockLib.Messaging.csproj
├── RockLib.Messaging.sln
├── SenderExtensions.cs
├── SenderMessage.cs
├── ValidatingSender.cs
├── ValidatingSenderTransaction.cs
└── ValidatingTransactionalSender.cs
├── Tests
├── RockLib.Messaging.CloudEvents.Tests
│ ├── .editorconfig
│ ├── AssemblySettings.cs
│ ├── CloudEventExtensionsTests.cs
│ ├── CloudEventTests.cs
│ ├── CorrelatedEventTests.cs
│ ├── CorrelatingExtensionsTests.cs
│ ├── Directory.Build.props
│ ├── FakeReceiverMessage.cs
│ ├── MockExtensions.cs
│ ├── PartitionedEventTests.cs
│ ├── PartitioningExtensionsTests.cs
│ ├── ProtocolBindingsTests.cs
│ ├── RockLib.Messaging.CloudEvents.Tests.csproj
│ ├── SequencingExtensionsTests.cs
│ └── SequentialEventTests.cs
├── RockLib.Messaging.Http.Tests
│ ├── .editorconfig
│ ├── AssemblySettings.cs
│ ├── Directory.Build.props
│ ├── HttpTests.cs
│ ├── Properties
│ │ └── launchSettings.json
│ └── RockLib.Messaging.Http.Tests.csproj
├── RockLib.Messaging.Kafka.Tests
│ ├── .editorconfig
│ ├── AssemblySettings.cs
│ ├── DependencyInjectionTests.cs
│ ├── Directory.Build.props
│ ├── KafkaReceiverTests.cs
│ ├── KafkaSenderTests.cs
│ ├── RockLib.Messaging.Kafka.Tests.csproj
│ └── StatisticsExtensionsTests.cs
├── RockLib.Messaging.NamedPipes.Tests
│ ├── .editorconfig
│ ├── AssemblySettings.cs
│ ├── DependencyInjectionTests.cs
│ ├── Directory.Build.props
│ ├── NamedPipesTests.cs
│ └── RockLib.Messaging.NamedPipes.Tests.csproj
├── RockLib.Messaging.RabbitMQ.Tests
│ ├── .editorconfig
│ ├── AssemblySettings.cs
│ ├── Directory.Build.props
│ ├── RabbitReceiverTests.cs
│ ├── RabbitSenderTests.cs
│ └── RockLib.Messaging.RabbitMQ.Tests.csproj
├── RockLib.Messaging.SNS.Tests
│ ├── .editorconfig
│ ├── AssemblySettings.cs
│ ├── DependencyInjectionTests.cs
│ ├── Directory.Build.props
│ ├── RockLib.Messaging.SNS.Tests.csproj
│ └── SNSTests.cs
├── RockLib.Messaging.SQS.Tests
│ ├── .editorconfig
│ ├── AssemblySettings.cs
│ ├── DependencyInjectionTests.cs
│ ├── Directory.Build.props
│ ├── RockLib.Messaging.SQS.Tests.csproj
│ └── SQSTests.cs
└── RockLib.Messaging.Tests
│ ├── .editorconfig
│ ├── AssemblySettings.cs
│ ├── CustomConfigFiles
│ ├── MultipleReceivers_appsettings.json
│ ├── MultipleSenders_appsettings.json
│ ├── SingleReceiver_appsettings.json
│ └── SingleSender_appsettings.json
│ ├── DependencyInjectionTests.cs
│ ├── Directory.Build.props
│ ├── EncodingTests.cs
│ ├── FakeMessageHandler.cs
│ ├── FakeReceiver.cs
│ ├── FakeReceiverMessage.cs
│ ├── FakeSender.cs
│ ├── ForwardingMessageHandlerTests.cs
│ ├── ForwardingReceiverMessageTests.cs
│ ├── ForwardingReceiverTests.cs
│ ├── MessagingExtensionsTests.cs
│ ├── MessagingScenarioFactoryTests.cs
│ ├── ReceiverExtensionsTests.cs
│ ├── ReloadingReceiverTests.cs
│ ├── ReloadingSenderTests.cs
│ ├── RockLib.Messaging.Tests.csproj
│ ├── SenderMessageTests.cs
│ ├── TestReceiverMessageTests.cs
│ ├── ValidatingSenderTests.cs
│ ├── ValidatingSenderTransactionTests.cs
│ ├── ValidatingTransactionalSenderTests.cs
│ └── appsettings.json
├── docs
├── CloudEvents.md
├── Compressed.md
├── DIContainer.md
├── ForwardingReceiver.md
├── GettingStarted.md
├── Http.md
├── Kafka.md
├── NamedPipes.md
├── RabbitMQ.md
├── ReceivingMessages.md
├── SNS.md
├── SQS.md
├── SendingMessages.md
└── TransactionalSend.md
└── icon.png
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 |
12 |
13 | ## Type of change:
14 |
15 | 1. Non-functional change (e.g. documentation changes, removing unused `using` directives, renaming local variables, etc)
16 | 2. Bug fix (non-breaking change that fixes an issue)
17 | 3. New feature (non-breaking change that adds functionality)
18 | 4. Breaking change (fix or feature that could cause existing functionality to not work as expected)
19 |
20 | ## Checklist:
21 |
22 | - Have you reviewed your own code? Do you understand every change?
23 | - Are you following the [contributing guidelines](../blob/main/CONTRIBUTING.md)?
24 | - Have you added tests that prove your fix is effective or that this feature works?
25 | - New and existing unit tests pass locally with these changes?
26 | - Have you made corresponding changes to the documentation?
27 | - Will this change require an update to an example project? (if so, create an issue and link to it)
28 |
29 | ---
30 |
31 | _[Reviewer guidelines](../blob/main/CONTRIBUTING.md#reviewing-changes)_
32 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-publish.yml:
--------------------------------------------------------------------------------
1 | name: Build and Publish
2 |
3 | ####################################################################################################
4 | ## WORKFLOW TRIGGER
5 | ####################################################################################################
6 | on:
7 | # Workflow will run when a release is published.
8 | release:
9 | types: [ released, prereleased ]
10 |
11 | ####################################################################################################
12 | ## WORKFLOW JOBS
13 | ####################################################################################################
14 | jobs:
15 | # Calls the shared build-and-publish workflow.
16 | call_build_and_publish:
17 | name: Call build-and-publish workflow
18 | uses: RockLib/RockLib.Workflows/.github/workflows/build-and-publish.yml@main
19 | secrets: inherit
20 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-test.yml:
--------------------------------------------------------------------------------
1 | name: Run Unit Test
2 |
3 | ####################################################################################################
4 | ## WORKFLOW TRIGGER
5 | ####################################################################################################
6 | on:
7 | # Workflow will run on pull requests to the main branch.
8 | pull_request:
9 | branches: [ main ]
10 |
11 | ####################################################################################################
12 | ## WORKFLOW JOBS
13 | ####################################################################################################
14 | jobs:
15 | # Calls the shared unit-test workflow.
16 | call_unit_test:
17 | name: Call unit-test workflow
18 | uses: RockLib/RockLib.Workflows/.github/workflows/unit-test.yml@main
19 | secrets: inherit
20 |
--------------------------------------------------------------------------------
/.github/workflows/snyk-test.yml:
--------------------------------------------------------------------------------
1 | name: Run Snyk Test
2 |
3 | ####################################################################################################
4 | ## WORKFLOW TRIGGER
5 | ####################################################################################################
6 | on:
7 | # Workflow will run after unit-test is completed.
8 | workflow_run:
9 | workflows: [ Run Unit Test ]
10 | types: [ completed ]
11 |
12 | ####################################################################################################
13 | ## WORKFLOW JOBS
14 | ####################################################################################################
15 | jobs:
16 | # Calls the shared snyk-test workflow.
17 | call_snyk_test:
18 | name: Call snyk-test workflow
19 | uses: RockLib/RockLib.Workflows/.github/workflows/snyk-test.yml@main
20 | secrets: inherit
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Secret stuff
2 | build/apikey.txt
3 |
4 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
5 | [Bb]in/
6 | [Oo]bj/
7 | docs/output
8 |
9 | # mstest test results
10 | TestResults
11 |
12 | ## Ignore Visual Studio temporary files, build results, and
13 | ## files generated by popular Visual Studio add-ons.
14 |
15 | # User-specific files
16 | *.suo
17 | *.user
18 | *.sln.docstates
19 |
20 | # Build results
21 | [Dd]ebug/
22 | [Rr]elease/
23 | x64/
24 | *_i.c
25 | *_p.c
26 | *.ilk
27 | *.meta
28 | *.obj
29 | *.pch
30 | *.pdb
31 | *.pgc
32 | *.pgd
33 | *.rsp
34 | *.sbr
35 | *.tlb
36 | *.tli
37 | *.tlh
38 | *.tmp
39 | *.log
40 | *.vspscc
41 | *.vssscc
42 | .builds
43 |
44 | # Visual C++ cache files
45 | ipch/
46 | *.aps
47 | *.ncb
48 | *.opensdf
49 | *.sdf
50 |
51 | # Visual Studio profiler
52 | *.psess
53 | *.vsp
54 | *.vspx
55 |
56 | # Guidance Automation Toolkit
57 | *.gpState
58 |
59 | # ReSharper is a .NET coding add-in
60 | _ReSharper*
61 |
62 | # NCrunch
63 | *.ncrunch*
64 | .*crunch*.local.xml
65 |
66 | # Installshield output folder
67 | [Ee]xpress
68 |
69 | # DocProject is a documentation generator add-in
70 | DocProject/buildhelp/
71 | DocProject/Help/*.HxT
72 | DocProject/Help/*.HxC
73 | DocProject/Help/*.hhc
74 | DocProject/Help/*.hhk
75 | DocProject/Help/*.hhp
76 | DocProject/Help/Html2
77 | DocProject/Help/html
78 |
79 | # Click-Once directory
80 | publish
81 |
82 | # Publish Web Output
83 | *.Publish.xml
84 |
85 | # NuGet Packages Directory
86 | packages
87 |
88 | # Windows Azure Build Output
89 | csx
90 | *.build.csdef
91 |
92 | # Windows Store app package directory
93 | AppPackages/
94 |
95 | # Others
96 | [Bb]in
97 | [Oo]bj
98 | sql
99 | TestResults
100 | [Tt]est[Rr]esult*
101 | *.Cache
102 | ClientBin
103 | [Ss]tyle[Cc]op.*
104 | ~$*
105 | *.dbmdl
106 | Generated_Code #added for RIA/Silverlight projects
107 |
108 | # Backup & report files from converting an old project file to a newer
109 | # Visual Studio version. Backup files are not needed, because we have git ;-)
110 | _UpgradeReport_Files/
111 | Backup*/
112 | UpgradeLog*.XML
113 |
114 | # Nuget
115 | *.nupkg
116 | .vs/
117 |
--------------------------------------------------------------------------------
/Examples/Example.Application.Testing/Example.Application.Testing.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0
4 | false
5 | Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | all
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 |
18 |
19 | all
20 | runtime; build; native; contentfiles; analyzers; buildtransitive
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Examples/Example.Application.Testing/ExampleService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Hosting;
2 | using RockLib.Messaging;
3 | using System;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | namespace Example
8 | {
9 | public class ExampleService : IHostedService
10 | {
11 | public ExampleService(IReceiver receiver, IDatabase database)
12 | {
13 | Receiver = receiver ?? throw new ArgumentNullException(nameof(receiver));
14 | Receiver.Error += (obj, args) =>
15 | {
16 | // Do something when the receiver encounters an error
17 | throw new Exception($"Error in Receiver: {args.Exception.Message}");
18 | };
19 |
20 | Database = database ?? throw new ArgumentNullException(nameof(database));
21 | }
22 |
23 | public IReceiver Receiver { get; }
24 |
25 | public IDatabase Database { get; }
26 |
27 | public Task StartAsync(CancellationToken cancellationToken)
28 | {
29 | Receiver.Start(OnMessageReceived);
30 | return Task.CompletedTask;
31 | }
32 |
33 | public Task StopAsync(CancellationToken cancellationToken)
34 | {
35 | Receiver.Dispose();
36 | return Task.CompletedTask;
37 | }
38 |
39 | private async Task OnMessageReceived(IReceiverMessage message)
40 | {
41 | if (message.Headers.TryGetValue("operation", out string operation))
42 | {
43 | if (operation == "create")
44 | {
45 | await Database.CreateAsync(message.StringPayload);
46 | await message.AcknowledgeAsync();
47 | }
48 | else if (operation == "update")
49 | {
50 | await Database.UpdateAsync(message.StringPayload);
51 | await message.AcknowledgeAsync();
52 | }
53 | else if (operation == "delete")
54 | {
55 | await Database.DeleteAsync(message.StringPayload);
56 | await message.AcknowledgeAsync();
57 | }
58 | else
59 | {
60 | // TODO: Send error log - invalid message
61 | await message.RejectAsync();
62 | }
63 | }
64 | else
65 | {
66 | // TODO: Send error log - invalid message
67 | await message.RejectAsync();
68 | }
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/Examples/Example.Application.Testing/FizzBuzzEngine.cs:
--------------------------------------------------------------------------------
1 | using RockLib.Messaging;
2 | using System;
3 | using System.Threading.Tasks;
4 |
5 | namespace Example
6 | {
7 | public class FizzBuzzEngine
8 | {
9 | public FizzBuzzEngine(ISender sender) =>
10 | Sender = sender ?? throw new ArgumentNullException(nameof(sender));
11 |
12 | public ISender Sender { get; }
13 |
14 | public async Task SendFizzBuzzMessage(long value)
15 | {
16 | if (value < 1)
17 | throw new ArgumentOutOfRangeException(nameof(value), "Must be greater than zero.");
18 |
19 | if (value % 15 == 0)
20 | await Sender.SendAsync("fizz-buzz");
21 | else if (value % 3 == 0)
22 | await Sender.SendAsync("fizz");
23 | else if (value % 5 == 0)
24 | await Sender.SendAsync("buzz");
25 | else
26 | await Sender.SendAsync(value.ToString());
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Examples/Example.Application.Testing/IDatabase.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace Example
4 | {
5 | public interface IDatabase
6 | {
7 | Task CreateAsync(string payload);
8 |
9 | Task UpdateAsync(string payload);
10 |
11 | Task DeleteAsync(string payload);
12 | }
13 | }
--------------------------------------------------------------------------------
/Examples/Example.Application.Testing/README.md:
--------------------------------------------------------------------------------
1 | # Testing components that use RockLib.Messaging
2 |
3 | This project is intended to demonstrate how to test classes that have a dependency on an `ISender` or `IReceiver`.
4 |
5 | ---
6 |
7 | The [`FizzBuzzEngine`](FizzBuzzEngine.cs) class is an implementation of [FizzBuzz](https://en.wikipedia.org/wiki/Fizz_buzz#Programming) that sends the result to an `ISender`. [To test it](Tests/FizzBuzzEngineTests.cs), we pass a mock `ISender` to the constructor, then verify that the correct message for the given value was sent to it.
8 |
9 | ---
10 |
11 | This [`ExampleService`](ExampleService.cs) class receives messages from an `IReceiver` and, depending on the value of a message's `Operation` header, creates, updates, or deletes the message's payload using an `IDatabase` interface. [For testing](Tests/ExampleServiceTests.cs), we pass a mock `IReceiver` and a mock `IDatabase` to the constructor. Then we simulate receiving a message by starting the receiver and passing a fake receiver message to the `OnMessageReceivedAsync` method of the receiver's `MessageHandler`. We then verify that the fake receiver message was handled correctly and that the correct method was called on the `IDatabase`.
12 |
--------------------------------------------------------------------------------
/Examples/Example.Common.Tests/CommandSendingServiceTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Moq;
3 | using RockLib.Dynamic;
4 | using RockLib.Messaging;
5 | using System;
6 | using Xunit;
7 |
8 | namespace Example.Common.Tests
9 | {
10 | public class CommandSendingServiceTests
11 | {
12 | [Fact(DisplayName = "Constructor sets properties")]
13 | public void ConstructorHappyPath()
14 | {
15 | // Arrange
16 | var sender = new Mock().Object;
17 |
18 | // Act
19 | var sendingService = new CommandSendingService(sender);
20 |
21 | // Assert
22 | sendingService.Sender.Should().BeSameAs(sender);
23 | }
24 |
25 | [Fact(DisplayName = "Constructor throws when sender parameter is null")]
26 | public void ConstructorSadPath()
27 | {
28 | // Arrange
29 | Action act = () => new CommandSendingService(null);
30 |
31 | // Act/Assert
32 | act.Should().ThrowExactly().WithMessage("*sender*");
33 | }
34 |
35 | [Fact(DisplayName = "Prompt returns correct message")]
36 | public void Prompt()
37 | {
38 | // Arrange
39 | var sender = new Mock().Object;
40 |
41 | var sendingService = new CommandSendingService(sender).Unlock();
42 |
43 | // Act
44 | string prompt = sendingService.Prompt;
45 |
46 | // Assert
47 | prompt.Should().Be("Enter command to send. (Legal values are: Default, UPPER, lower, SpOnGeBoB)");
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Examples/Example.Common.Tests/DataSendingServiceTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Moq;
3 | using RockLib.Dynamic;
4 | using RockLib.Messaging;
5 | using System;
6 | using Xunit;
7 |
8 | namespace Example.Common.Tests
9 | {
10 | public class DataSendingServiceTests
11 | {
12 | [Fact(DisplayName = "Constructor sets properties")]
13 | public void ConstructorHappyPath()
14 | {
15 | // Arrange
16 | var sender = new Mock().Object;
17 |
18 | // Act
19 | var sendingService = new DataSendingService(sender);
20 |
21 | // Assert
22 | sendingService.Sender.Should().BeSameAs(sender);
23 | }
24 |
25 | [Fact(DisplayName = "Constructor throws when sender parameter is null")]
26 | public void ConstructorSadPath()
27 | {
28 | // Arrange
29 | Action act = () => new DataSendingService(null);
30 |
31 | // Act/Assert
32 | act.Should().ThrowExactly().WithMessage("*sender*");
33 | }
34 |
35 | [Fact(DisplayName = "Prompt returns correct message")]
36 | public void Prompt()
37 | {
38 | // Arrange
39 | var sender = new Mock().Object;
40 |
41 | var sendingService = new DataSendingService(sender).Unlock();
42 |
43 | // Act
44 | string prompt = sendingService.Prompt;
45 |
46 | // Assert
47 | prompt.Should().Be("Enter data to send.");
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Examples/Example.Common.Tests/Example.Common.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0
4 | false
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | all
14 | runtime; build; native; contentfiles; analyzers; buildtransitive
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Examples/Example.Common.Tests/SendingServiceTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Moq;
3 | using Moq.Protected;
4 | using RockLib.Dynamic;
5 | using RockLib.Messaging;
6 | using System;
7 | using System.Threading;
8 | using Xunit;
9 |
10 | namespace Example.Common.Tests
11 | {
12 | public class SendingServiceTests
13 | {
14 | [Fact(DisplayName = "Constructor sets properties")]
15 | public void ConstructorHappyPath()
16 | {
17 | // Arrange
18 | var sender = new Mock().Object;
19 |
20 | // Act
21 | var sendingService = new ConcreteSendingService(sender);
22 |
23 | // Assert
24 | sendingService.Sender.Should().BeSameAs(sender);
25 | }
26 |
27 | [Fact(DisplayName = "Constructor throws when sender parameter is null")]
28 | public void ConstructorSadPath()
29 | {
30 | // Arrange
31 | Action act = () => new ConcreteSendingService(null);
32 |
33 | // Act/Assert
34 | act.Should().ThrowExactly().WithMessage("*sender*");
35 | }
36 |
37 | [Fact(DisplayName = "ReadAndSendMessage reads a line and sends it")]
38 | public void ReadAndSendMessage()
39 | {
40 | // Arrange
41 | var mockSender = new Mock();
42 |
43 | var mockSendingService = new Mock(mockSender.Object);
44 |
45 | mockSendingService.Protected().As()
46 | .Setup(m => m.ReadLine()).Returns("test-message");
47 |
48 | var sendingService = mockSendingService.Object.Unlock();
49 |
50 | // Act
51 | sendingService.ReadAndSendMessage();
52 |
53 | // Assert
54 | mockSendingService.Protected().As()
55 | .Verify(m => m.ReadLine(), Times.Once());
56 |
57 | mockSender.Verify(m => m.SendAsync(It.Is(m => m.StringPayload == "test-message"), It.IsAny()),
58 | Times.Once());
59 | }
60 |
61 | private class ConcreteSendingService : SendingService
62 | {
63 | public ConcreteSendingService(ISender sender)
64 | : base(sender)
65 | {
66 | }
67 |
68 | protected override string Prompt => null;
69 | }
70 |
71 | private interface IProtected
72 | {
73 | string ReadLine();
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Examples/Example.Common.Tests/SingleMessageServiceTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Moq;
3 | using Moq.Protected;
4 | using RockLib.Dynamic;
5 | using RockLib.Messaging;
6 | using System;
7 | using System.Threading;
8 | using Xunit;
9 | namespace Example.Common.Tests
10 | {
11 | public class SingleMessageServiceTests
12 | {
13 | [Fact(DisplayName = "Constructor sets properties")]
14 | public void ConstructorHappyPath()
15 | {
16 | // Arrange
17 | var sender = new Mock().Object;
18 | var receiver = new Mock().Object;
19 |
20 | // Act
21 | var service = new SingleMessageService(sender, receiver);
22 |
23 | // Assert
24 | service.Sender.Should().BeSameAs(sender);
25 | service.Receiver.Should().BeSameAs(receiver);
26 | }
27 |
28 | [Fact(DisplayName = "Constructor throws when sender parameter is null")]
29 | public void ConstructorSadPath1()
30 | {
31 | // Arrange
32 | var receiver = new Mock().Object;
33 |
34 | Action act = () => new SingleMessageService(null, receiver);
35 |
36 | // Act/Assert
37 | act.Should().ThrowExactly().WithMessage("*sender*");
38 | }
39 |
40 | [Fact(DisplayName = "Constructor throws when receiver parameter is null")]
41 | public void ConstructorSadPath2()
42 | {
43 | // Arrange
44 | var sender = new Mock().Object;
45 |
46 | Action act = () => new SingleMessageService(sender, null);
47 |
48 | // Act/Assert
49 | act.Should().ThrowExactly().WithMessage("*receiver*");
50 | }
51 |
52 | [Fact(DisplayName = "WaitAndSendMessage waits then sends a message with the current date/time")]
53 | public void WaitAndSendMessage()
54 | {
55 | // Arrange
56 | var mockSender = new Mock();
57 | var receiver = new Mock().Object;
58 |
59 | var mockService = new Mock(mockSender.Object, receiver);
60 |
61 | var now = DateTime.Now;
62 |
63 | mockService.Protected().As()
64 | .Setup(m => m.Now).Returns(now);
65 |
66 | var service = mockService.Object.Unlock();
67 |
68 | // Act
69 | service.WaitAndSendMessage();
70 |
71 | // Assert
72 | mockService.Protected().As()
73 | .Verify(m => m.Wait(), Times.Once());
74 |
75 | mockSender.Verify(m => m.SendAsync(It.Is(m => m.StringPayload == $"[{now:G}] Example message"), It.IsAny()),
76 | Times.Once());
77 | }
78 |
79 | private interface IProtected
80 | {
81 | void Wait();
82 | DateTime Now { get; }
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Examples/Example.Common/Casing.cs:
--------------------------------------------------------------------------------
1 | namespace Example.Common
2 | {
3 | public enum Casing
4 | {
5 | Default,
6 | UPPER,
7 | lower,
8 | SpOnGeBoB
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Examples/Example.Common/CommandSendingService.cs:
--------------------------------------------------------------------------------
1 | using RockLib.Messaging;
2 | using System;
3 |
4 | namespace Example.Common
5 | {
6 | public class CommandSendingService : SendingService
7 | {
8 | public CommandSendingService(ISender sender)
9 | : base(sender)
10 | {
11 | }
12 |
13 | protected override string Prompt => $"Enter command to send. (Legal values are: {string.Join(", ", Enum.GetNames(typeof(Casing)))})";
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Examples/Example.Common/DataSendingService.cs:
--------------------------------------------------------------------------------
1 | using RockLib.Messaging;
2 |
3 | namespace Example.Common
4 | {
5 | public class DataSendingService : SendingService
6 | {
7 | public DataSendingService(ISender sender)
8 | : base(sender)
9 | {
10 | }
11 |
12 | protected override string Prompt => "Enter data to send.";
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Examples/Example.Common/Example.Common.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Examples/Example.Common/SendingService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Hosting;
2 | using RockLib.Messaging;
3 | using System;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | namespace Example.Common
8 | {
9 | public abstract class SendingService : IHostedService
10 | {
11 | private readonly Thread _senderThread;
12 |
13 | protected SendingService(ISender sender)
14 | {
15 | Sender = sender ?? throw new ArgumentNullException(nameof(sender));
16 | _senderThread = new Thread(SendMessages) { IsBackground = true };
17 | }
18 |
19 | public ISender Sender { get; }
20 |
21 | protected abstract string Prompt { get; }
22 |
23 | public Task StartAsync(CancellationToken cancellationToken)
24 | {
25 | _senderThread.Start();
26 | return Task.CompletedTask;
27 | }
28 |
29 | public Task StopAsync(CancellationToken cancellationToken)
30 | {
31 | Sender.Dispose();
32 | return Task.CompletedTask;
33 | }
34 |
35 | private void SendMessages()
36 | {
37 | // Wait a bit to let the startup logs get written to console. Otherwise, logs
38 | // get written after the prompt and it's a weird experience for the user.
39 | Thread.Sleep(500);
40 |
41 | Console.WriteLine(Prompt);
42 | while (true)
43 | {
44 | Console.Write(">");
45 | ReadAndSendMessage();
46 | }
47 | }
48 |
49 | private async Task ReadAndSendMessage()
50 | {
51 | string message = ReadLine();
52 | if (message is object)
53 | await Sender.SendAsync(message);
54 | }
55 |
56 | protected virtual string ReadLine() => Console.ReadLine();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Examples/Example.Common/SingleMessageService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Hosting;
2 | using RockLib.Messaging;
3 | using System;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | namespace Example.Common
8 | {
9 | public class SingleMessageService : IHostedService
10 | {
11 | public SingleMessageService(ISender sender, IReceiver receiver)
12 | {
13 | Sender = sender ?? throw new ArgumentNullException(nameof(sender));
14 | Receiver = receiver ?? throw new ArgumentNullException(nameof(receiver));
15 | }
16 |
17 | public ISender Sender { get; }
18 |
19 | public IReceiver Receiver { get; }
20 |
21 | public Task StartAsync(CancellationToken cancellationToken)
22 | {
23 | Receiver.Start(OnMessageReceived);
24 | ThreadPool.QueueUserWorkItem(async _ => await WaitAndSendMessage());
25 |
26 | return Task.CompletedTask;
27 | }
28 |
29 | public Task StopAsync(CancellationToken cancellationToken)
30 | {
31 | return Task.CompletedTask;
32 | }
33 |
34 | private Task OnMessageReceived(IReceiverMessage message)
35 | {
36 | Console.WriteLine($"Received message: '{message.StringPayload}'");
37 | return message.AcknowledgeAsync();
38 | }
39 |
40 | private async Task WaitAndSendMessage()
41 | {
42 | Wait();
43 | await Sender.SendAsync($"[{Now:G}] Example message");
44 | }
45 |
46 | protected virtual void Wait() => Thread.Sleep(1000);
47 |
48 | protected virtual DateTime Now => DateTime.Now;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Examples/Example.Messaging.CloudEvents/Example.Messaging.CloudEvents.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net8.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | PreserveNewest
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Examples/Example.Messaging.CloudEvents/ExampleOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Example.Messaging.CloudEvents
2 | {
3 | public class ExampleOptions
4 | {
5 | public string Source { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Examples/Example.Messaging.CloudEvents/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Microsoft.Extensions.Hosting;
3 | using RockLib.Messaging.DependencyInjection;
4 | using System.Threading.Tasks;
5 |
6 | namespace Example.Messaging.CloudEvents
7 | {
8 | class Program
9 | {
10 | static Task Main(string[] args)
11 | {
12 | return CreateHostBuilder(args).RunConsoleAsync(options => options.SuppressStatusMessages = true);
13 | }
14 |
15 | static IHostBuilder CreateHostBuilder(string[] args)
16 | {
17 | IHostBuilder hostBuilder = Host.CreateDefaultBuilder(args);
18 | return hostBuilder.ConfigureServices((context, services) =>
19 | {
20 | services.AddNamedPipeSender("user-pipe");
21 | services.AddNamedPipeReceiver("user-pipe");
22 |
23 | services.AddNamedPipeSender("worker-pipe-1");
24 | services.AddNamedPipeReceiver("worker-pipe-1");
25 |
26 | services.AddNamedPipeSender("worker-pipe-2");
27 | services.AddNamedPipeReceiver("worker-pipe-2");
28 |
29 | services.Configure(context.Configuration.GetSection("examplesettings"));
30 | services.AddHostedService();
31 | });
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Examples/Example.Messaging.CloudEvents/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "examplesettings": {
3 | "source": "example.org/abc/123"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/Examples/Example.Messaging.Http/Example.Messaging.Http.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net8.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Always
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Examples/Example.Messaging.Http/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "RockLib.Messaging": {
3 | "senders": {
4 | "type": "RockLib.Messaging.Http.HttpClientSender, RockLib.Messaging.Http",
5 | "value": {
6 | "name": "Sender1",
7 | "url": "http://localhost:5000/api/commands"
8 | }
9 | },
10 | "receivers": {
11 | "type": "RockLib.Messaging.Http.HttpListenerReceiver, RockLib.Messaging.Http",
12 | "value": {
13 | "name": "Receiver1",
14 | "url": "http://localhost:5000/api/commands"
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Examples/Example.Messaging.Kafka/Example.Messaging.Kafka.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net8.0
5 |
6 |
7 |
8 |
9 |
10 |
11 | Always
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Examples/Example.Messaging.Kafka/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "RockLib.Messaging": {
3 | "senders": {
4 | "type": "RockLib.Messaging.Kafka.KafkaSender, RockLib.Messaging.Kafka",
5 | "value": {
6 | "name": "Sender1",
7 | "topic": "test",
8 | "bootstrapServers": "localhost:9092",
9 | "statisticsIntervalMs": 1000
10 | }
11 | },
12 | "receivers": {
13 | "type": "RockLib.Messaging.Kafka.KafkaReceiver, RockLib.Messaging.Kafka",
14 | "value": {
15 | "name": "Receiver1",
16 | "topic": "test",
17 | "groupId": "test-consumer-group",
18 | "bootstrapServers": "localhost:9092",
19 | "statisticsIntervalMs": 1000
20 | }
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/Examples/Example.Messaging.NamedPipes/Example.Messaging.NamedPipes.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net8.0
5 |
6 |
7 |
8 |
9 |
10 |
11 | PreserveNewest
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Examples/Example.Messaging.NamedPipes/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "DataSettings:PipeName": "data_pipe",
3 | "CommandSettings:PipeName": "command_pipe",
4 |
5 | "RockLib.Messaging": {
6 | "Senders": {
7 | "Type": "RockLib.Messaging.NamedPipes.NamedPipeSender, RockLib.Messaging.NamedPipes",
8 | "Value": {
9 | "Name": "SingleMessageSender",
10 | "PipeName": "example_pipe"
11 | }
12 | },
13 | "Receivers": {
14 | "Type": "RockLib.Messaging.NamedPipes.NamedPipeReceiver, RockLib.Messaging.NamedPipes",
15 | "Value": {
16 | "Name": "SingleMessageReceiver",
17 | "PipeName": "example_pipe"
18 | }
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/Examples/Example.Messaging.RabbitMQ/Example.Messaging.RabbitMQ.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net8.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | PreserveNewest
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Examples/Example.Messaging.RabbitMQ/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "RockLib.Messaging": {
3 | "senders": {
4 | "type": "RockLib.Messaging.RabbitMQ.RabbitSender, RockLib.Messaging.RabbitMQ",
5 | "value": {
6 | "name": "Sender1",
7 | "connection": { "hostName": "localhost" },
8 | "routingKey": "task_queue"
9 | }
10 | },
11 | "receivers": {
12 | "type": "RockLib.Messaging.RabbitMQ.RabbitReceiver, RockLib.Messaging.RabbitMQ",
13 | "value": {
14 | "name": "Receiver1",
15 | "connection": { "hostName": "localhost" },
16 | "queue": "task_queue",
17 | "prefetchCount": 1
18 | }
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/Examples/Example.Messaging.SNS/Example.Messaging.SNS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net8.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Examples/Example.Messaging.SQS/Example.Messaging.SQS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net8.0
5 |
6 |
7 |
8 | PreserveNewest
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Examples/Example.Messaging.SQS/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "DataSettings:QueueUrl": "https://sqs.{region}.amazonaws.com/{account-id}/{data-queue-name}",
3 | "CommandSettings:QueueUrl": "https://sqs.{region}.amazonaws.com/{account-id}/{command-queue-name}",
4 |
5 | "RockLib.Messaging": {
6 | "Senders": {
7 | "Type": "RockLib.Messaging.SQS.SQSSender, RockLib.Messaging.SQS",
8 | "Value": {
9 | "Name": "SingleMessageSender",
10 | "QueueUrl": "https://sqs.{region}.amazonaws.com/{account-id}/{example-queue-name}"
11 | }
12 | },
13 | "Receivers": {
14 | "Type": "RockLib.Messaging.SQS.SQSReceiver, RockLib.Messaging.SQS",
15 | "Value": {
16 | "Name": "SingleMessageReceiver",
17 | "QueueUrl": "https://sqs.{region}.amazonaws.com/{account-id}/{example-queue-name}"
18 | }
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (C) 2015-2021 Rocket Mortgage
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RockLib.Messaging
2 |
3 | *A simple API for sending and receiving messages.*
4 |
5 | ## Packages
6 |
7 | ### RockLib.Messaging
8 |
9 | > [!WARNING]
10 | > The 5.0.0 release of this library will be the final version with upgrades and changes. Bug fixes will continue to be released as needed.
11 |
12 | ### RockLib.Messaging.NamedPipes
13 |
14 | > [!WARNING]
15 | > The 5.0.0 release of this library will be the final version with upgrades and changes. Bug fixes will continue to be released as needed.
16 |
17 | ### RockLib.Messaging.SQS
18 |
19 | ### RockLib.Messaging.SNS
20 |
21 | ### RockLib.Messaging.Http
22 |
23 | > [!WARNING]
24 | > The 4.0.0 release of this library will be the final version with upgrades and changes. Bug fixes will continue to be released as needed.
25 |
26 | ### RockLib.Messaging.Kafka
27 |
28 | > [!WARNING]
29 | > The 4.0.0 release of this library will be the final version with upgrades and changes. Bug fixes will continue to be released as needed.
30 |
31 | ### RockLib.Messaging.RabbitMQ
32 |
33 | > [!WARNING]
34 | > The 3.0.0 release of this library will be the final version with upgrades and changes. Bug fixes will continue to be released as needed.
35 |
36 | ### RockLib.Messaging.CloudEvents
37 |
38 | > [!WARNING]
39 | > The 4.0.0 release of this library will be the final version with upgrades and changes. Bug fixes will continue to be released as needed.
40 |
41 | -----
42 |
43 | - [Getting started](docs/GettingStarted.md)
44 | - How to:
45 | - [Send messages](docs/SendingMessages.md)
46 | - [Receive messages](docs/ReceivingMessages.md)
47 | - [Configure and use RockLib.Messaging.NamedPipes](docs/NamedPipes.md)
48 | - [Configure and use RockLib.Messaging.SQS](docs/SQS.md)
49 | - [Configure and use RockLib.Messaging.SNS](docs/SNS.md)
50 | - [Configure and use RockLib.Messaging.Http](docs/Http.md)
51 | - [Configure and use RockLib.Messaging.Kafka](docs/Kafka.md)
52 | - [Configure and use RockLib.Messaging.RabbitMQ](docs/RabbitMQ.md)
53 | - [Configure and use ForwardingReceiver](docs/ForwardingReceiver.md)
54 | - Enables the "fault queue" pattern.
55 | - [Use RockLib.Messaging with a DI/IOC container](docs/DIContainer.md)
56 | - [Send and receive compressed messages](docs/Compressed.md)
57 | - [Send messages transactionally](docs/TransactionalSend.md)
58 | - [Send and receive message as CloudEvents](docs/CloudEvents.md) 🆕
59 | - [Test components that use RockLib.Messaging](https://github.com/RockLib/RockLib.Messaging/tree/main/Examples/Example.Application.Testing) 🆕
60 | - API Reference:
61 | - [RockLib.Messaging](https://www.nuget.org/packages/RockLib.Messaging)
62 | - [RockLib.Messaging.NamedPipes](https://www.nuget.org/packages/RockLib.Messaging.NamedPipes)
63 | - [RockLib.Messaging.SQS](https://www.nuget.org/packages/RockLib.Messaging.SQS)
64 | - [RockLib.Messaging.SNS](https://www.nuget.org/packages/RockLib.Messaging.SNS)
65 | - [RockLib.Messaging.Http](https://www.nuget.org/packages/RockLib.Messaging.Http)
66 | - [RockLib.Messaging.Kafka](https://www.nuget.org/packages/RockLib.Messaging.Kafka)
67 | - [RockLib.Messaging.RabbitMQ](https://www.nuget.org/packages/RockLib.Messaging.RabbitMQ)
68 | - [RockLib.Messaging.CloudEvents](https://www.nuget.org/packages/RockLib.Messaging.CloudEvents)
69 |
--------------------------------------------------------------------------------
/RockLib.Messaging.CloudEvents/CloudEventExtensions.CopyConstructor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 |
4 | namespace RockLib.Messaging.CloudEvents
5 | {
6 | partial class CloudEventExtensions
7 | {
8 | private sealed class CopyConstructor
9 | {
10 | private Func _invokeConstructor;
11 |
12 | private CopyConstructor(ConstructorInfo constructor)
13 | {
14 | // The initial function uses regular reflection.
15 | _invokeConstructor = cloudEvent =>
16 | (CloudEvent)constructor.Invoke(new object[] { cloudEvent });
17 | }
18 |
19 | public static CopyConstructor? Create(Type type)
20 | {
21 | var constructor = GetConstructor(type);
22 |
23 | if (constructor is null)
24 | {
25 | return null;
26 | }
27 |
28 | return new CopyConstructor(constructor);
29 | }
30 |
31 | public CloudEvent Invoke(CloudEvent cloudEvent) =>
32 | _invokeConstructor(cloudEvent);
33 |
34 | private static ConstructorInfo? GetConstructor(Type type) =>
35 | type.GetConstructor(new[] { type });
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/RockLib.Messaging.CloudEvents/CloudEventExtensions.MessageConstructor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 |
4 | namespace RockLib.Messaging.CloudEvents
5 | {
6 | partial class CloudEventExtensions
7 | {
8 | private sealed class MessageConstructor
9 | {
10 | private static readonly Type[] _constructorParameters = new[] { typeof(IReceiverMessage), typeof(IProtocolBinding) };
11 |
12 | private Func _invokeConstructor;
13 |
14 | private MessageConstructor(ConstructorInfo constructor)
15 | {
16 | // The initial function uses regular reflection.
17 | _invokeConstructor = (receiverMessage, protocolBinding) =>
18 | constructor.Invoke(new object[] { receiverMessage, protocolBinding! });
19 | }
20 |
21 | public static MessageConstructor? Create(Type type)
22 | {
23 | var constructor = GetConstructor(type);
24 |
25 | if (constructor is null)
26 | {
27 | return null;
28 | }
29 |
30 | return new MessageConstructor(constructor);
31 | }
32 |
33 | public static bool Exists(Type type) =>
34 | GetConstructor(type) is not null;
35 |
36 | public object Invoke(IReceiverMessage receiverMessage, IProtocolBinding? protocolBinding) =>
37 | _invokeConstructor(receiverMessage, protocolBinding);
38 |
39 | private static ConstructorInfo? GetConstructor(Type type) =>
40 | type.GetConstructor(_constructorParameters);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/RockLib.Messaging.CloudEvents/CloudEventExtensions.ValidateMethod.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 |
4 | namespace RockLib.Messaging.CloudEvents
5 | {
6 | partial class CloudEventExtensions
7 | {
8 | private sealed class ValidateMethod
9 | {
10 | private const BindingFlags _publicStaticFlags = BindingFlags.Public | BindingFlags.Static;
11 | private static readonly Type[] _validateMethodParameters = new[] { typeof(SenderMessage), typeof(IProtocolBinding) };
12 |
13 | private Action _invokeValidateMethod;
14 |
15 | private ValidateMethod(MethodInfo validateMethod)
16 | {
17 | // The initial function uses regular reflection.
18 | _invokeValidateMethod = (senderMessage, protocolBinding) =>
19 | validateMethod.Invoke(null, new object[] { senderMessage, protocolBinding });
20 | }
21 |
22 | public static ValidateMethod? Create(Type type)
23 | {
24 | var validateMethod = GetValidateMethod(type);
25 |
26 | if (validateMethod is null)
27 | {
28 | return null;
29 | }
30 |
31 | return new ValidateMethod(validateMethod);
32 | }
33 |
34 | public void Invoke(SenderMessage senderMessage, IProtocolBinding protocolBinding) =>
35 | _invokeValidateMethod(senderMessage, protocolBinding);
36 |
37 | private static MethodInfo? GetValidateMethod(Type type) =>
38 | type.GetMethod(nameof(CloudEvent.Validate), _publicStaticFlags, null, _validateMethodParameters, null);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/RockLib.Messaging.CloudEvents/CloudEventValidationException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Messaging.CloudEvents
4 | {
5 | ///
6 | /// The exception that is thrown when validation for a CloudEvent fails.
7 | ///
8 | [Serializable]
9 | public class CloudEventValidationException : Exception
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | public CloudEventValidationException()
15 | {
16 | }
17 |
18 | ///
19 | /// Initializes a new instance of the class with a
20 | /// specified error message.
21 | ///
22 | /// The message that describes the error.
23 | public CloudEventValidationException(string message) : base(message)
24 | {
25 | }
26 |
27 | ///
28 | /// Initializes a new instance of the class with a specified
29 | /// error message and a reference to the inner exception that is the cause of this exception.
30 | ///
31 | /// The error message that explains the reason for the exception.
32 | ///
33 | /// The exception that is the cause of the current exception, or a null reference if no inner
34 | /// exception is specified.
35 | public CloudEventValidationException(string message, Exception innerException) : base(message, innerException)
36 | {
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/RockLib.Messaging.CloudEvents/Correlating/CorrelatingExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using static RockLib.Messaging.CloudEvents.CorrelatedEvent;
3 |
4 | namespace RockLib.Messaging.CloudEvents.Correlating
5 | {
6 | ///
7 | /// Extension methods for getting and setting the Correlation ID of an event.
8 | ///
9 | public static class CorrelatingExtensions
10 | {
11 | ///
12 | /// Gets the Correlation ID of the event.
13 | ///
14 | /// The cloud event.
15 | /// The Correlation ID of the event.
16 | ///
17 | /// If is .
18 | ///
19 | public static string GetCorrelationId(this CloudEvent cloudEvent)
20 | {
21 | #if NET6_0_OR_GREATER
22 | ArgumentNullException.ThrowIfNull(cloudEvent);
23 | #else
24 | if (cloudEvent is null) { throw new ArgumentNullException(nameof(cloudEvent)); }
25 | #endif
26 |
27 | if (cloudEvent.Attributes.TryGetValue(CorrelationIdAttribute, out var value) &&
28 | value is string correlationId)
29 | {
30 | return correlationId;
31 | }
32 |
33 | correlationId = NewCorrelationId();
34 | cloudEvent.Attributes[CorrelationIdAttribute] = correlationId;
35 | return correlationId;
36 | }
37 |
38 | ///
39 | /// Sets the Correlation ID of the event.
40 | ///
41 | /// The cloud event.
42 | /// The Correlation ID of the event.
43 | ///
44 | /// If or is .
46 | ///
47 | public static void SetCorrelationId(this CloudEvent cloudEvent, string correlationId)
48 | {
49 | #if NET6_0_OR_GREATER
50 | ArgumentNullException.ThrowIfNull(cloudEvent);
51 | #else
52 | if (cloudEvent is null) { throw new ArgumentNullException(nameof(cloudEvent)); }
53 | #endif
54 |
55 | if (string.IsNullOrEmpty(correlationId))
56 | {
57 | throw new ArgumentNullException(nameof(correlationId));
58 | }
59 |
60 | cloudEvent.Attributes[CorrelationIdAttribute] = correlationId;
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/RockLib.Messaging.CloudEvents/DataSerialization.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Messaging.CloudEvents
2 | {
3 | ///
4 | /// Defines types of serialization to use when getting or setting the data of a as a generic type.
6 | ///
7 | public enum DataSerialization
8 | {
9 | ///
10 | /// JSON serialization.
11 | ///
12 | Json,
13 |
14 | ///
15 | /// XML serialization.
16 | ///
17 | Xml
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/RockLib.Messaging.CloudEvents/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/RockLib.Messaging.CloudEvents/IProtocolBinding.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Messaging.CloudEvents
2 | {
3 | ///
4 | /// Defines how to bind a CloudEvent attribute name to a or
5 | /// header name.
6 | ///
7 | public interface IProtocolBinding
8 | {
9 | ///
10 | /// Gets the header name for a given the specified CloudEvent
11 | /// attribute name.
12 | ///
13 | /// The CloudEvent attribute name.
14 | /// The header name for a .
15 | string GetHeaderName(string attributeName);
16 |
17 | ///
18 | /// Gets the attribute name for a given the specified header name.
20 | ///
21 | /// The header name.
22 | ///
23 | /// When this method returns, a value indicating whether
24 | /// represents a CloudEvent attribute according to this protocol binding. A value of indicates that represents some
26 | /// non-CloudEvent attribute.
27 | ///
28 | /// The CloudEvent attribute name.
29 | string GetAttributeName(string headerName, out bool isCloudEventAttribute);
30 |
31 | ///
32 | /// Binds the attributes of the to the headers of the . Called after has been
34 | /// mapped to .
35 | ///
36 | /// The source .
37 | /// The target .
38 | void Bind(CloudEvent fromCloudEvent, SenderMessage toSenderMessage);
39 |
40 | ///
41 | /// Binds the headers of the to the attributes of the . Called after all of the headers of the have been added to ,
44 | /// or .
45 | ///
46 | /// The source .
47 | /// The target .
48 | void Bind(IReceiverMessage fromReceiverMessage, CloudEvent toCloudEvent);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/RockLib.Messaging.CloudEvents/RockLib.Messaging.CloudEvents.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Embedded
4 | Send and receive messages using the CloudEvents format.
5 | True
6 | true
7 | True
8 | icon.png
9 | RockLib.Messaging.CloudEvents
10 | LICENSE.md
11 | https://github.com/RockLib/RockLib.Messaging
12 | A changelog is available at https://github.com/RockLib/RockLib.Messaging/blob/main/RockLib.Messaging.CloudEvents/CHANGELOG.md.
13 | false
14 | rocklib messaging cloudevents
15 | 4.0.0
16 | True
17 | 4.0.0
18 |
19 |
20 | true
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/RockLib.Messaging.CloudEvents/RockLib.Messaging.CloudEvents.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29926.136
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Messaging.CloudEvents", "RockLib.Messaging.CloudEvents.csproj", "{A40564DD-9C71-444D-B8E9-2098A6AA3445}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Messaging.CloudEvents.Tests", "..\Tests\RockLib.Messaging.CloudEvents.Tests\RockLib.Messaging.CloudEvents.Tests.csproj", "{57E86379-F6E9-4F83-93E5-F4CF68B51049}"
9 | EndProject
10 | Global
11 | GlobalSection(SharedMSBuildProjectFiles) = preSolution
12 | RockLib.Messaging.HttpUtils\RockLib.Messaging.HttpUtils.projitems*{a40564dd-9c71-444d-b8e9-2098a6aa3445}*SharedItemsImports = 5
13 | EndGlobalSection
14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | Debug|Any CPU = Debug|Any CPU
16 | Release|Any CPU = Release|Any CPU
17 | EndGlobalSection
18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
19 | {A40564DD-9C71-444D-B8E9-2098A6AA3445}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {A40564DD-9C71-444D-B8E9-2098A6AA3445}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {A40564DD-9C71-444D-B8E9-2098A6AA3445}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {A40564DD-9C71-444D-B8E9-2098A6AA3445}.Release|Any CPU.Build.0 = Release|Any CPU
23 | {57E86379-F6E9-4F83-93E5-F4CF68B51049}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {57E86379-F6E9-4F83-93E5-F4CF68B51049}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {57E86379-F6E9-4F83-93E5-F4CF68B51049}.Release|Any CPU.ActiveCfg = Release|Any CPU
26 | {57E86379-F6E9-4F83-93E5-F4CF68B51049}.Release|Any CPU.Build.0 = Release|Any CPU
27 | EndGlobalSection
28 | GlobalSection(SolutionProperties) = preSolution
29 | HideSolutionNode = FALSE
30 | EndGlobalSection
31 | GlobalSection(ExtensibilityGlobals) = postSolution
32 | SolutionGuid = {3FEE2AF4-936B-474F-A3C0-7118F5655DCC}
33 | EndGlobalSection
34 | EndGlobal
35 |
--------------------------------------------------------------------------------
/RockLib.Messaging.CloudEvents/SequenceTypes.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Messaging.CloudEvents
2 | {
3 | ///
4 | /// Defines values for the attribute.
5 | ///
6 | public static class SequenceTypes
7 | {
8 | ///
9 | /// If the attribute is set to "Integer",
10 | /// the attribute has the following semantics:
11 | ///
12 | /// - The values of sequence are string-encoded signed 32-bit Integers.
13 | /// - The sequence MUST start with a value of 1 and increase by 1 for each subsequent value
14 | /// (i.e. be contiguous and monotonically increasing).
15 | /// - The sequence wraps around from 2,147,483,647 (2^31 - 1) to -2,147,483,648 (-2^31).
16 | ///
17 | ///
18 | #pragma warning disable CA1720 // Identifiers should not contain type names
19 | public const string Integer = "Integer";
20 | #pragma warning restore CA1720 // Identifiers should not contain type names
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/RockLib.Messaging.Http/DefaultHttpResponseGenerator.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Messaging.Http
2 | {
3 | ///
4 | /// A simple implementation of .
5 | ///
6 | public class DefaultHttpResponseGenerator : IHttpResponseGenerator
7 | {
8 | ///
9 | /// Initializes a new instance of the class.
10 | ///
11 | ///
12 | /// The status code to be returned to the client when a message is acknowledged.
13 | ///
14 | ///
15 | /// The status code to be returned to the client when a message is rolled back.
16 | ///
17 | ///
18 | /// The status code to be returned to the client when a message is acknowledged.
19 | ///
20 | public DefaultHttpResponseGenerator(int acknowledgeStatusCode, int rollbackStatusCode, int rejectStatusCode)
21 | {
22 | AcknowledgeResponse = new HttpResponse(acknowledgeStatusCode);
23 | RollbackResponse = new HttpResponse(rollbackStatusCode);
24 | RejectResponse = new HttpResponse(rejectStatusCode);
25 | }
26 |
27 | ///
28 | /// Gets the response to be returned from calls to .
29 | ///
30 | #pragma warning disable CA1721 // Property names should not match get methods
31 | public HttpResponse AcknowledgeResponse { get; }
32 | #pragma warning restore CA1721 // Property names should not match get methods
33 |
34 | ///
35 | /// Gets the response to be returned from calls to .
36 | ///
37 | #pragma warning disable CA1721 // Property names should not match get methods
38 | public HttpResponse RollbackResponse { get; }
39 | #pragma warning restore CA1721 // Property names should not match get methods
40 |
41 | ///
42 | /// Gets the response to be returned from calls to .
43 | ///
44 | #pragma warning disable CA1721 // Property names should not match get methods
45 | public HttpResponse RejectResponse { get; }
46 | #pragma warning restore CA1721 // Property names should not match get methods
47 |
48 | ///
49 | /// Returns .
50 | ///
51 | public HttpResponse GetAcknowledgeResponse(HttpListenerReceiverMessage message) => AcknowledgeResponse;
52 |
53 | ///
54 | /// Returns .
55 | ///
56 | public HttpResponse GetRollbackResponse(HttpListenerReceiverMessage message) => RollbackResponse;
57 |
58 | ///
59 | /// Returns .
60 | ///
61 | public HttpResponse GetRejectResponse(HttpListenerReceiverMessage message) => RejectResponse;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/RockLib.Messaging.Http/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/RockLib.Messaging.Http/HttpResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace RockLib.Messaging.Http
5 | {
6 | ///
7 | /// Defines an http response.
8 | ///
9 | public class HttpResponse
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The http status code of the response.
15 | /// The http status description of the response.
16 | /// The string content of the response.
17 | public HttpResponse(int statusCode, string? content = null, string? statusDescription = null)
18 | : this(statusCode, statusDescription, (object)content!)
19 | {
20 | }
21 |
22 | ///
23 | /// Initializes a new instance of the class.
24 | ///
25 | /// The http status code of the response.
26 | /// The http status description of the response.
27 | /// The binary content of the response.
28 | public HttpResponse(int statusCode, byte[] content, string? statusDescription = null)
29 | : this(statusCode, statusDescription, content)
30 | {
31 | }
32 |
33 | private HttpResponse(int statusCode, string? statusDescription, object? content)
34 | {
35 | if (statusCode < 100 || statusCode > 999)
36 | {
37 | throw new ArgumentException("statusCode cannot be less than 100 or greater than 999.", nameof(statusCode));
38 | }
39 | StatusCode = statusCode;
40 | StatusDescription = statusDescription;
41 | Content = content;
42 | }
43 |
44 | ///
45 | /// Gets the status code of the response.
46 | ///
47 | public int StatusCode { get; }
48 |
49 | ///
50 | /// Gets the status description of the response.
51 | ///
52 | public string? StatusDescription { get; }
53 |
54 | ///
55 | /// Gets the content of the response.
56 | ///
57 | public object? Content { get; }
58 |
59 | ///
60 | /// Gets a dictionary representing the headers of the response.
61 | ///
62 | public IDictionary Headers { get; } = new Dictionary();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/RockLib.Messaging.Http/IHttpResponseGenerator.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Messaging.Http
2 | {
3 | ///
4 | /// Defines an object that generates http responses for a specified message.
5 | ///
6 | public interface IHttpResponseGenerator
7 | {
8 | ///
9 | /// Get an http response for an http request when the user has acknowledged
10 | /// it.
11 | ///
12 | /// The message that is being acknowledged.
13 | HttpResponse GetAcknowledgeResponse(HttpListenerReceiverMessage message);
14 |
15 | ///
16 | /// Get an http response for an http request when the user has rolled it
17 | /// back.
18 | ///
19 | /// The message that is being rolled back.
20 | HttpResponse GetRollbackResponse(HttpListenerReceiverMessage message);
21 |
22 | ///
23 | /// Get an http response for an http request when the user has rejected it.
24 | ///
25 | /// The message that is being rejected.
26 | HttpResponse GetRejectResponse(HttpListenerReceiverMessage message);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/RockLib.Messaging.Http/RockLib.Messaging.Http.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | RockLib
4 | True
5 | Embedded
6 | An implementation of RockLib.Messaging that sends and receives messages via HTTP.
7 | true
8 | True
9 | RockLib.Messaging.Http
10 | 4.0.0
11 | false
12 | A changelog is available at https://github.com/RockLib/RockLib.Messaging/blob/main/RockLib.Messaging.Http/CHANGELOG.md.
13 | https://github.com/RockLib/RockLib.Messaging
14 | LICENSE.md
15 | icon.png
16 | rocklib messaging http
17 | True
18 | 4.0.0
19 |
20 |
21 | true
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/RockLib.Messaging.Http/RockLib.Messaging.Http.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.1.32228.430
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Messaging.Http", "RockLib.Messaging.Http.csproj", "{C74A231E-DDE2-413F-BBF2-673AE2DB9B51}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Messaging.Http.Tests", "..\Tests\RockLib.Messaging.Http.Tests\RockLib.Messaging.Http.Tests.csproj", "{BA8E64C8-2F6F-468D-91CD-225D0357A43E}"
9 | EndProject
10 | Global
11 | GlobalSection(SharedMSBuildProjectFiles) = preSolution
12 | ..\RockLib.Messaging.HttpUtils\RockLib.Messaging.HttpUtils.projitems*{c74a231e-dde2-413f-bbf2-673ae2db9b51}*SharedItemsImports = 5
13 | EndGlobalSection
14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | Debug|Any CPU = Debug|Any CPU
16 | Release|Any CPU = Release|Any CPU
17 | EndGlobalSection
18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
19 | {C74A231E-DDE2-413F-BBF2-673AE2DB9B51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {C74A231E-DDE2-413F-BBF2-673AE2DB9B51}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {C74A231E-DDE2-413F-BBF2-673AE2DB9B51}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {C74A231E-DDE2-413F-BBF2-673AE2DB9B51}.Release|Any CPU.Build.0 = Release|Any CPU
23 | {BA8E64C8-2F6F-468D-91CD-225D0357A43E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {BA8E64C8-2F6F-468D-91CD-225D0357A43E}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {BA8E64C8-2F6F-468D-91CD-225D0357A43E}.Release|Any CPU.ActiveCfg = Release|Any CPU
26 | {BA8E64C8-2F6F-468D-91CD-225D0357A43E}.Release|Any CPU.Build.0 = Release|Any CPU
27 | EndGlobalSection
28 | GlobalSection(SolutionProperties) = preSolution
29 | HideSolutionNode = FALSE
30 | EndGlobalSection
31 | GlobalSection(ExtensibilityGlobals) = postSolution
32 | SolutionGuid = {DC62819D-E06C-4684-A84D-7F7DA3833A3B}
33 | EndGlobalSection
34 | EndGlobal
35 |
--------------------------------------------------------------------------------
/RockLib.Messaging.Kafka/Constants.cs:
--------------------------------------------------------------------------------
1 | using Confluent.Kafka;
2 |
3 | namespace RockLib.Messaging.Kafka
4 | {
5 | ///
6 | /// Defines constants for the RockLib.Messaging.Kafka package.
7 | ///
8 | public static class Constants
9 | {
10 | ///
11 | /// The default timeout value.
12 | ///
13 | public const int DefaultTimeout = 10_000;
14 |
15 | ///
16 | /// The name of the header where a Kafka message's Key is stored.
17 | /// If a has a header with this name and a sends it, then the Key of the outgoing Kafka is set to that header's value.
20 | /// When a receives a
21 | /// with a non-empty Key, then that Key is found in the
22 | /// header with this name.
23 | ///
24 | public const string KafkaKeyHeader = "Kafka.Key";
25 |
26 | ///
27 | /// The name of the header containing the schema ID.
28 | ///
29 | public const string KafkaSchemaIdHeader = "Kafka.SchemaId";
30 |
31 | ///
32 | /// Byte value to indicate the raw payload contains a schema ID. When the first byte of the payload is the
33 | /// the subsequent 4 bytes compose the schema ID value according to the Confluent
34 | /// wire format
35 | ///
36 | internal const byte SchemaIdLeadingByte = 0;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/RockLib.Messaging.Kafka/DependencyInjection/KafkaReceiverOptions.cs:
--------------------------------------------------------------------------------
1 | using Confluent.Kafka;
2 |
3 | namespace RockLib.Messaging.Kafka.DependencyInjection
4 | {
5 | ///
6 | /// Defines the settings for creating instances of .
7 | ///
8 | public class KafkaReceiverOptions : ConsumerConfig
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | public KafkaReceiverOptions()
14 | : base()
15 | {
16 | EnableAutoOffsetStore = false;
17 | AutoOffsetReset = Confluent.Kafka.AutoOffsetReset.Latest;
18 | }
19 |
20 | ///
21 | /// Gets or sets the topic to subscribe to.
22 | ///
23 | public string? Topic { get; set; }
24 |
25 | ///
26 | /// Gets or sets whether the kafka receiver expects schema information in the payload
27 | ///
28 | public bool SchemaIdRequired { get; set; }
29 | }
30 | }
--------------------------------------------------------------------------------
/RockLib.Messaging.Kafka/DependencyInjection/KafkaSenderOptions.cs:
--------------------------------------------------------------------------------
1 | using Confluent.Kafka;
2 |
3 | namespace RockLib.Messaging.Kafka.DependencyInjection
4 | {
5 | ///
6 | /// Defines the settings for creating instances of .
7 | ///
8 | public class KafkaSenderOptions : ProducerConfig
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | public KafkaSenderOptions()
14 | : base()
15 | {
16 | MessageTimeoutMs = Constants.DefaultTimeout;
17 | }
18 |
19 | ///
20 | /// Gets or sets the topic to subscribe to.
21 | ///
22 | public string? Topic { get; set; }
23 |
24 | ///
25 | /// Gets or sets the schema ID to validate messages against
26 | ///
27 | public int SchemaId { get; set; }
28 | }
29 | }
--------------------------------------------------------------------------------
/RockLib.Messaging.Kafka/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/RockLib.Messaging.Kafka/Exceptions/InvalidMessageException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Messaging.Kafka.Exceptions
4 | {
5 | ///
6 | /// Represents an error that occured when validating a message
7 | ///
8 | [Serializable]
9 | public class InvalidMessageException : Exception
10 | {
11 | ///
12 | /// Construct a new instance of with a specified message
13 | ///
14 | /// Message describing the error
15 | public InvalidMessageException(string message) : base(message)
16 | {
17 | }
18 |
19 | ///
20 | /// Construct a new instance of
21 | ///
22 | public InvalidMessageException()
23 | {
24 | }
25 |
26 | ///
27 | /// Construct a new instance of with a specified message
28 | /// and inner exception
29 | ///
30 | /// Message describing the error
31 | /// The inner exception
32 | public InvalidMessageException(string message, Exception innerException) : base(message, innerException)
33 | {
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/RockLib.Messaging.Kafka/MessageExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using static RockLib.Messaging.Kafka.Constants;
3 |
4 | namespace RockLib.Messaging.Kafka
5 | {
6 | ///
7 | /// Extension methods for and
8 | /// related to Kafka.
9 | ///
10 | public static class MessageExtensions
11 | {
12 | ///
13 | /// Gets the Key of the Kafka message, as stored in the header
14 | /// of the .
15 | ///
16 | /// The .
17 | /// The Key of the Kafka message.
18 | public static string? GetKafkaKey(this IReceiverMessage receiverMessage)
19 | {
20 | #if NET6_0_OR_GREATER
21 | ArgumentNullException.ThrowIfNull(receiverMessage);
22 | #else
23 | if (receiverMessage is null) { throw new ArgumentNullException(nameof(receiverMessage)); }
24 | #endif
25 |
26 | if (receiverMessage.Headers.TryGetValue(KafkaKeyHeader, out string? kafkaKey))
27 | {
28 | return kafkaKey;
29 | }
30 |
31 | return null;
32 | }
33 |
34 | ///
35 | /// Sets the Key of the Kafka message, as stored in the header
36 | /// of the .
37 | ///
38 | /// The .
39 | /// The Key of the Kafka message. If , the
40 | /// Kafka Key header of the message is removed.
41 | /// event is removed.
42 | public static void SetKafkaKey(this SenderMessage senderMessage, string kafkaKey)
43 | {
44 | #if NET6_0_OR_GREATER
45 | ArgumentNullException.ThrowIfNull(senderMessage);
46 | #else
47 | if (senderMessage is null) { throw new ArgumentNullException(nameof(senderMessage)); }
48 | #endif
49 |
50 | if (!string.IsNullOrEmpty(kafkaKey))
51 | {
52 | senderMessage.Headers[KafkaKeyHeader] = kafkaKey;
53 | }
54 | else
55 | {
56 | senderMessage.Headers.Remove(KafkaKeyHeader);
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/RockLib.Messaging.Kafka/RockLib.Messaging.Kafka.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Embedded
4 | Implementation of RockLib.Messaging API that sends and receives messages using Kafka.
5 | True
6 | true
7 | True
8 | icon.png
9 | RockLib.Messaging.Kafka
10 | LICENSE.md
11 | https://github.com/RockLib/RockLib.Messaging
12 | A changelog is available at https://github.com/RockLib/RockLib.Messaging/blob/main/RockLib.Messaging.Kafka/CHANGELOG.md.
13 | false
14 | rocklib messaging kafka
15 | 4.0.0
16 | True
17 | 4.0.0
18 |
19 |
20 | true
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/RockLib.Messaging.Kafka/RockLib.Messaging.Kafka.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29926.136
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Messaging.Kafka", "RockLib.Messaging.Kafka.csproj", "{2D35EA9A-C9C3-4915-894F-815D9CE5700A}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Messaging.Kafka.Tests", "..\Tests\RockLib.Messaging.Kafka.Tests\RockLib.Messaging.Kafka.Tests.csproj", "{BDA37A2A-CA76-4FF1-B08F-2783C637B492}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {2D35EA9A-C9C3-4915-894F-815D9CE5700A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {2D35EA9A-C9C3-4915-894F-815D9CE5700A}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {2D35EA9A-C9C3-4915-894F-815D9CE5700A}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {2D35EA9A-C9C3-4915-894F-815D9CE5700A}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {BDA37A2A-CA76-4FF1-B08F-2783C637B492}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {BDA37A2A-CA76-4FF1-B08F-2783C637B492}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {BDA37A2A-CA76-4FF1-B08F-2783C637B492}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {BDA37A2A-CA76-4FF1-B08F-2783C637B492}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {3FEE2AF4-936B-474F-A3C0-7118F5655DCC}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/RockLib.Messaging.NamedPipes/DependencyInjection/NamedPipeOptions.cs:
--------------------------------------------------------------------------------
1 | using RockLib.Messaging.NamedPipes;
2 |
3 | namespace RockLib.Messaging.DependencyInjection
4 | {
5 | ///
6 | /// Defines the settings for creating instances of and
7 | /// .
8 | ///
9 | public class NamedPipeOptions
10 | {
11 | ///
12 | /// Gets or sets the pipe name. If null, the name of the sender or receiver is used as
13 | /// the pipe name.
14 | ///
15 | public string? PipeName { get; set; }
16 | }
17 | }
--------------------------------------------------------------------------------
/RockLib.Messaging.NamedPipes/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/RockLib.Messaging.NamedPipes/NamedPipeMessage.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace RockLib.Messaging.NamedPipes
4 | {
5 | internal sealed class NamedPipeMessage
6 | {
7 | public string? StringValue { get; set; }
8 | public Dictionary? Headers { get; set; }
9 | }
10 | }
--------------------------------------------------------------------------------
/RockLib.Messaging.NamedPipes/NamedPipeReceiverMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace RockLib.Messaging.NamedPipes
7 | {
8 | ///
9 | /// An implementation of IReceiverMessage for use by the
10 | /// class.
11 | ///
12 | public class NamedPipeReceiverMessage : ReceiverMessage
13 | {
14 | private readonly NamedPipeMessage _namedPipeMessage;
15 |
16 | ///
17 | /// Initializes a new instance of the class.
18 | ///
19 | /// The message that was sent.
20 | internal NamedPipeReceiverMessage(NamedPipeMessage namedPipeMessage)
21 | : base(() => namedPipeMessage.StringValue!)
22 | {
23 | _namedPipeMessage = namedPipeMessage;
24 | }
25 |
26 | ///
27 | protected override Task AcknowledgeMessageAsync(CancellationToken cancellationToken) => Task.CompletedTask;
28 |
29 | ///
30 | protected override Task RollbackMessageAsync(CancellationToken cancellationToken) => Task.CompletedTask;
31 |
32 | ///
33 | protected override Task RejectMessageAsync(CancellationToken cancellationToken) => Task.CompletedTask;
34 |
35 | ///
36 | protected override void InitializeHeaders(IDictionary headers)
37 | {
38 | #if NET6_0_OR_GREATER
39 | ArgumentNullException.ThrowIfNull(headers);
40 | #else
41 | if (headers is null) { throw new ArgumentNullException(nameof(headers)); }
42 | #endif
43 |
44 | if (_namedPipeMessage.Headers is not null)
45 | {
46 | foreach (var header in _namedPipeMessage.Headers)
47 | {
48 | headers.Add(header.Key, header.Value);
49 | }
50 | }
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/RockLib.Messaging.NamedPipes/RockLib.Messaging.NamedPipes.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Embedded
4 | Implementation of RockLib.Messaging API that sends and receives messages to a local named pipe.
5 | True
6 | true
7 | True
8 | icon.png
9 | RockLib.Messaging.NamedPipes
10 | LICENSE.md
11 | https://github.com/RockLib/RockLib.Messaging
12 | A changelog is available at https://github.com/RockLib/RockLib.Messaging/blob/main/RockLib.Messaging.NamedPipes/CHANGELOG.md.
13 | false
14 | rocklib messaging
15 | 5.0.0
16 | True
17 | 5.0.0
18 |
19 |
20 | true
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/RockLib.Messaging.NamedPipes/RockLib.Messaging.NamedPipes.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27004.2009
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Messaging.NamedPipes", "RockLib.Messaging.NamedPipes.csproj", "{C1B97CC2-45AC-4B73-8172-C484FD6871A9}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Messaging.NamedPipes.Tests", "..\Tests\RockLib.Messaging.NamedPipes.Tests\RockLib.Messaging.NamedPipes.Tests.csproj", "{968246FA-5614-4969-97CF-C3E312DE4D28}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {C1B97CC2-45AC-4B73-8172-C484FD6871A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {C1B97CC2-45AC-4B73-8172-C484FD6871A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {C1B97CC2-45AC-4B73-8172-C484FD6871A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {C1B97CC2-45AC-4B73-8172-C484FD6871A9}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {968246FA-5614-4969-97CF-C3E312DE4D28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {968246FA-5614-4969-97CF-C3E312DE4D28}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {968246FA-5614-4969-97CF-C3E312DE4D28}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {968246FA-5614-4969-97CF-C3E312DE4D28}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {3FEE2AF4-936B-474F-A3C0-7118F5655DCC}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/RockLib.Messaging.RabbitMQ/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/RockLib.Messaging.RabbitMQ/RabbitReceiverMessage.cs:
--------------------------------------------------------------------------------
1 | using RabbitMQ.Client.Events;
2 | using RabbitMQ.Client;
3 | using System.Collections.Generic;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using System.Text;
7 | using System;
8 |
9 | namespace RockLib.Messaging.RabbitMQ
10 | {
11 | ///
12 | /// An implementation of IReceiverMessage for use by the
13 | /// class.
14 | ///
15 | public sealed class RabbitReceiverMessage : ReceiverMessage
16 | {
17 | private readonly BasicDeliverEventArgs _args;
18 | private readonly IModel _channel;
19 | private readonly bool _autoAck;
20 |
21 | internal RabbitReceiverMessage(BasicDeliverEventArgs args, IModel channel, bool autoAck)
22 | : base(() => args.Body.ToArray())
23 | {
24 | _args = args;
25 | _channel = channel;
26 | _autoAck = autoAck;
27 | }
28 |
29 | ///
30 | protected override void InitializeHeaders(IDictionary headers)
31 | {
32 | #if NET6_0_OR_GREATER
33 | ArgumentNullException.ThrowIfNull(headers);
34 | #else
35 | if (headers is null) { throw new ArgumentNullException(nameof(headers)); }
36 | #endif
37 |
38 | if (_args.BasicProperties?.Headers == null)
39 | {
40 | return;
41 | }
42 |
43 | foreach (var header in _args.BasicProperties.Headers)
44 | {
45 | if (header.Value is byte[] binary)
46 | {
47 | headers.Add(header.Key, Encoding.UTF8.GetString(binary));
48 | }
49 | else
50 | {
51 | headers.Add(header);
52 | }
53 | }
54 | }
55 |
56 | ///
57 | protected override Task AcknowledgeMessageAsync(CancellationToken cancellationToken)
58 | {
59 | if (!_autoAck)
60 | {
61 | _channel.BasicAck(_args.DeliveryTag, false);
62 | }
63 | return Task.CompletedTask;
64 | }
65 |
66 | ///
67 | protected override Task RollbackMessageAsync(CancellationToken cancellationToken)
68 | {
69 | if (!_autoAck)
70 | {
71 | _channel.BasicNack(_args.DeliveryTag, false, true);
72 | }
73 | return Task.CompletedTask;
74 | }
75 |
76 | ///
77 | protected override Task RejectMessageAsync(CancellationToken cancellationToken)
78 | {
79 | if (!_autoAck)
80 | {
81 | _channel.BasicNack(_args.DeliveryTag, false, false);
82 | }
83 | return Task.CompletedTask;
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/RockLib.Messaging.RabbitMQ/RockLib.Messaging.RabbitMQ.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Embedded
4 | Implementation of RockLib.Messaging API that sends and receives messages using RabbitMQ.
5 | True
6 | true
7 | True
8 | icon.png
9 | RockLib.Messaging.RabbitMQ
10 | LICENSE.md
11 | https://github.com/RockLib/RockLib.Messaging
12 | A changelog is available at https://github.com/RockLib/RockLib.Messaging/blob/main/RockLib.Messaging.RabbitMQ/CHANGELOG.md.
13 | false
14 | rocklib messaging rabbitmq
15 | 3.0.0
16 | True
17 | 3.0.0
18 |
19 |
20 | true
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/RockLib.Messaging.RabbitMQ/RockLib.Messaging.RabbitMQ.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27004.2009
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Messaging.RabbitMQ", "RockLib.Messaging.RabbitMQ.csproj", "{2D35EA9A-C9C3-4915-894F-815D9CE5700A}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {2D35EA9A-C9C3-4915-894F-815D9CE5700A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {2D35EA9A-C9C3-4915-894F-815D9CE5700A}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {2D35EA9A-C9C3-4915-894F-815D9CE5700A}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {2D35EA9A-C9C3-4915-894F-815D9CE5700A}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {3FEE2AF4-936B-474F-A3C0-7118F5655DCC}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/RockLib.Messaging.SNS/DependencyInjection/SNSExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using RockLib.Messaging.SNS;
3 | using System;
4 |
5 | namespace RockLib.Messaging.DependencyInjection
6 | {
7 | ///
8 | /// Extension methods for registering SNS senders.
9 | ///
10 | public static class SNSExtensions
11 | {
12 | ///
13 | /// Adds an to the service collection.
14 | ///
15 | /// The .
16 | /// The name of the sender.
17 | /// A callback for configuring the .
18 | ///
19 | /// Whether to create an SNS sender that automatically reloads itself when its
20 | /// configuration or options change.
21 | ///
22 | /// A builder allowing the sender to be decorated.
23 | public static ISenderBuilder AddSNSSender(this IServiceCollection services, string name,
24 | Action? configureOptions = null, bool reloadOnChange = true)
25 | {
26 | return services.AddSender(name, CreateSNSSender, configureOptions, reloadOnChange);
27 |
28 | ISender CreateSNSSender(SNSSenderOptions options, IServiceProvider serviceProvider) =>
29 | new SNSSender(name, options.TopicArn, options.Region);
30 | }
31 |
32 | ///
33 | /// Adds an to the service collection.
34 | ///
35 | /// The .
36 | /// The name of the sender.
37 | /// A callback for configuring the .
38 | /// A builder allowing the sender to be decorated.
39 | public static ISenderBuilder AddSNSSender(this IServiceCollection services, string name,
40 | Action configureOptions) =>
41 | services.AddSNSSender(name, configureOptions, true);
42 | }
43 | }
--------------------------------------------------------------------------------
/RockLib.Messaging.SNS/DependencyInjection/SNSSenderOptions.cs:
--------------------------------------------------------------------------------
1 | using RockLib.Messaging.SNS;
2 | using System;
3 | using System.Diagnostics.CodeAnalysis;
4 |
5 | namespace RockLib.Messaging.DependencyInjection
6 | {
7 | ///
8 | /// Defines the settings for creating instances of .
9 | ///
10 | public class SNSSenderOptions
11 | {
12 | private string? _topicArn;
13 |
14 | ///
15 | /// Gets or sets the arn of the SNS topic.
16 | ///
17 | public string TopicArn
18 | {
19 | [return: MaybeNull] get => _topicArn!;
20 | set => _topicArn = value ?? throw new ArgumentNullException(nameof(value));
21 | }
22 |
23 | ///
24 | /// Gets or sets the region of the SNS topic.
25 | ///
26 | public string? Region { get; set; }
27 | }
28 | }
--------------------------------------------------------------------------------
/RockLib.Messaging.SNS/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/RockLib.Messaging.SNS/RockLib.Messaging.SNS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Embedded
4 | Implementation of RockLib.Messaging API that sends messages using Amazon SNS.
5 | True
6 | true
7 | True
8 | icon.png
9 | RockLib.Messaging.SNS
10 | LICENSE.md
11 | https://github.com/RockLib/RockLib.Messaging
12 | A changelog is available at https://github.com/RockLib/RockLib.Messaging/blob/main/RockLib.Messaging.SNS/CHANGELOG.md.
13 | false
14 | rocklib messaging sns
15 | 4.0.0
16 | True
17 | 4.0.0
18 |
19 |
20 | true
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/RockLib.Messaging.SNS/RockLib.Messaging.SNS.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27004.2009
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Messaging.SNS", "RockLib.Messaging.SNS.csproj", "{F5558582-F71D-40E3-B47F-66E1F025527E}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Messaging.SNS.Tests", "..\Tests\RockLib.Messaging.SNS.Tests\RockLib.Messaging.SNS.Tests.csproj", "{616A4D6D-44E7-46BB-A080-DECC15D53448}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {F5558582-F71D-40E3-B47F-66E1F025527E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {F5558582-F71D-40E3-B47F-66E1F025527E}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {F5558582-F71D-40E3-B47F-66E1F025527E}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {F5558582-F71D-40E3-B47F-66E1F025527E}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {616A4D6D-44E7-46BB-A080-DECC15D53448}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {616A4D6D-44E7-46BB-A080-DECC15D53448}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {616A4D6D-44E7-46BB-A080-DECC15D53448}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {616A4D6D-44E7-46BB-A080-DECC15D53448}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {3FEE2AF4-936B-474F-A3C0-7118F5655DCC}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/RockLib.Messaging.SQS/DependencyInjection/SQSSenderOptions.cs:
--------------------------------------------------------------------------------
1 | using Amazon.SQS;
2 | using RockLib.Messaging.SQS;
3 | using System;
4 |
5 | namespace RockLib.Messaging.DependencyInjection
6 | {
7 | ///
8 | /// Defines the settings for creating instances of .
9 | ///
10 | public class SQSSenderOptions
11 | {
12 | private Uri? _queueUrl;
13 |
14 | ///
15 | /// Gets or sets the url of the SQS queue.
16 | ///
17 | public Uri? QueueUrl
18 | {
19 | get => _queueUrl;
20 | set => _queueUrl = value ?? throw new ArgumentNullException(nameof(value));
21 | }
22 |
23 | ///
24 | /// Gets or sets the object that communicates with SQS.
25 | ///
26 | public IAmazonSQS? SqsClient { get; set; }
27 |
28 | ///
29 | /// Gets or sets the region of the SQS client.
30 | ///
31 | public string? Region { get; set; }
32 |
33 | ///
34 | /// Gets or sets the tag that specifies that a message belongs to a specific message
35 | /// group. Messages that belong to the same message group are processed in a FIFO manner
36 | /// (however, messages in different message groups might be processed out of order). To
37 | /// interleave multiple ordered streams within a single queue, use MessageGroupId values
38 | /// (for example, session data for multiple users). In this scenario, multiple consumers
39 | /// can process the queue, but the session data of each user is processed in a FIFO
40 | /// fashion.
41 | /// This parameter applies only to FIFO (first-in-first-out) queues.
42 | ///
43 | public string? MessageGroupId { get; set; }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/RockLib.Messaging.SQS/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/RockLib.Messaging.SQS/InternalsVisibleTo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly:InternalsVisibleTo("RockLib.Messaging.SQS.Tests")]
--------------------------------------------------------------------------------
/RockLib.Messaging.SQS/RockLib.Messaging.SQS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Embedded
4 | Implementation of RockLib.Messaging API that sends and receives messages using Amazon SQS.
5 | True
6 | true
7 | True
8 | icon.png
9 | RockLib.Messaging.SQS
10 | LICENSE.md
11 | https://github.com/RockLib/RockLib.Messaging
12 | 5.0.0
13 | A changelog is available at https://github.com/RockLib/RockLib.Messaging/blob/main/RockLib.Messaging.SQS/CHANGELOG.md.
14 | True
15 | false
16 | rocklib messaging sqs
17 | 5.0.0
18 |
19 |
20 | true
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/RockLib.Messaging.SQS/RockLib.Messaging.SQS.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27004.2009
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Messaging.SQS", "RockLib.Messaging.SQS.csproj", "{C4385BD1-2B62-4232-ADC2-896BDBE34142}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Messaging.SQS.Tests", "..\Tests\RockLib.Messaging.SQS.Tests\RockLib.Messaging.SQS.Tests.csproj", "{202FD7C2-2E35-4AA2-81A6-FCA895852CF9}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {C4385BD1-2B62-4232-ADC2-896BDBE34142}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {C4385BD1-2B62-4232-ADC2-896BDBE34142}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {C4385BD1-2B62-4232-ADC2-896BDBE34142}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {C4385BD1-2B62-4232-ADC2-896BDBE34142}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {202FD7C2-2E35-4AA2-81A6-FCA895852CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {202FD7C2-2E35-4AA2-81A6-FCA895852CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {202FD7C2-2E35-4AA2-81A6-FCA895852CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {202FD7C2-2E35-4AA2-81A6-FCA895852CF9}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {3FEE2AF4-936B-474F-A3C0-7118F5655DCC}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/RockLib.Messaging/AssemblyAttributes.cs:
--------------------------------------------------------------------------------
1 | using RockLib.Configuration.ObjectFactory;
2 | using RockLib.Messaging;
3 | using System.Collections.Generic;
4 |
5 | [assembly: ConfigSection("RockLib.Messaging:Senders", typeof(List))]
6 | [assembly: ConfigSection("RockLib.Messaging:Receivers", typeof(List))]
7 |
--------------------------------------------------------------------------------
/RockLib.Messaging/DependencyInjection/DecorationDelegates.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Messaging.DependencyInjection
4 | {
5 | ///
6 | /// Represents a method that creates an decorator.
7 | ///
8 | /// The that is being decorated.
9 | ///
10 | /// The that resolves dependencies necessary for the creation of the
11 | /// decorator.
12 | ///
13 | /// An decorator.
14 | public delegate ISender SenderDecoration(ISender sender, IServiceProvider serviceProvider);
15 |
16 | ///
17 | /// Represents a method that creates an decorator.
18 | ///
19 | /// The that is being decorated.
20 | ///
21 | /// The that resolves dependencies necessary for the creation of the
22 | /// decorator.
23 | ///
24 | /// An decorator.
25 | public delegate ITransactionalSender TransactionalSenderDecoration(ITransactionalSender sender, IServiceProvider serviceProvider);
26 |
27 | ///
28 | /// Represents a method that creates an decorator.
29 | ///
30 | /// The that is being decorated.
31 | ///
32 | /// The that resolves dependencies necessary for the creation of the
33 | /// decorator.
34 | ///
35 | /// An decorator.
36 | public delegate IReceiver ReceiverDecoration(IReceiver receiver, IServiceProvider serviceProvider);
37 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/DependencyInjection/ForwardingExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Microsoft.Extensions.Options;
3 | using System;
4 |
5 | namespace RockLib.Messaging.DependencyInjection
6 | {
7 | ///
8 | /// Extension methods for registering a forwarding receiver.
9 | ///
10 | public static class ForwardingExtensions
11 | {
12 | ///
13 | /// Adds a registration to the builder.
14 | ///
15 | /// The .
16 | /// A callback for configuring the .
17 | /// The same .
18 | public static IReceiverBuilder AddForwardingReceiver(this IReceiverBuilder builder,
19 | Action? configureOptions = null)
20 | {
21 | #if NET6_0_OR_GREATER
22 | ArgumentNullException.ThrowIfNull(builder);
23 | #else
24 | if (builder is null) { throw new ArgumentNullException(nameof(builder)); }
25 | #endif
26 | builder.AddDecorator((receiver, serviceProvider) =>
27 | {
28 | var optionsMonitor = serviceProvider.GetService>();
29 | var options = optionsMonitor?.Get(receiver.Name) ?? new ForwardingReceiverOptions();
30 | configureOptions?.Invoke(options);
31 |
32 | return new ForwardingReceiver(receiver.Name, receiver,
33 | options.GetAcknowledgeForwarder(serviceProvider), options.AcknowledgeOutcome,
34 | options.GetRollbackForwarder(serviceProvider), options.RollbackOutcome,
35 | options.GetRejectForwarder(serviceProvider), options.RejectOutcome);
36 | });
37 |
38 | return builder;
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/DependencyInjection/IForwardingReceiverOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Messaging.DependencyInjection
4 | {
5 | ///
6 | /// Defines the editable settings for creating an instance of .
7 | ///
8 | public interface IForwardingReceiverOptions
9 | {
10 | ///
11 | /// Gets or sets the name of the receiver - as obtained from an
12 | /// - that is used as the forwarder for messages that are acknowledged.
13 | ///
14 | string? AcknowledgeForwarderName { get; set; }
15 |
16 | ///
17 | /// Gets or sets the outcome for received messages that have been forwarded to to the
18 | /// acknowledge forwarder.
19 | ///
20 | ForwardingOutcome AcknowledgeOutcome { get; set; }
21 |
22 | ///
23 | /// Gets or sets the name of the receiver - as obtained from an
24 | /// - that is used as the forwarder for messages that are rolled back.
25 | ///
26 | string? RollbackForwarderName { get; set; }
27 |
28 | ///
29 | /// Gets or sets the outcome for received messages that have been forwarded to to the
30 | /// rollback forwarder.
31 | ///
32 | ForwardingOutcome RollbackOutcome { get; set; }
33 |
34 | ///
35 | /// Gets or sets the name of the receiver - as obtained from an
36 | /// - that is used as the forwarder for messages that are rejected.
37 | ///
38 | string? RejectForwarderName { get; set; }
39 |
40 | ///
41 | /// Gets or sets the outcome for received messages that have been forwarded to to the
42 | /// reject forwarder.
43 | ///
44 | ForwardingOutcome RejectOutcome { get; set; }
45 | }
46 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/DependencyInjection/IReceiverBuilder.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Messaging.DependencyInjection
2 | {
3 | ///
4 | /// A builder used to decorate an .
5 | ///
6 | public interface IReceiverBuilder
7 | {
8 | ///
9 | /// Adds a decoration delegate to the builder.
10 | ///
11 | /// The decoration delegate.
12 | IReceiverBuilder AddDecorator(ReceiverDecoration decoration);
13 | }
14 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/DependencyInjection/ISenderBuilder.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Messaging.DependencyInjection
2 | {
3 | ///
4 | /// A builder used to decorate an .
5 | ///
6 | public interface ISenderBuilder
7 | {
8 | ///
9 | /// Adds a decoration delegate to the builder.
10 | ///
11 | /// The decoration delegate.
12 | ISenderBuilder AddDecorator(SenderDecoration decoration);
13 | }
14 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/DependencyInjection/ITransactionalSenderBuilder.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Messaging.DependencyInjection
2 | {
3 | ///
4 | /// A builder used to decorate an .
5 | ///
6 | public interface ITransactionalSenderBuilder
7 | {
8 | ///
9 | /// Adds a decoration delegate to the builder.
10 | ///
11 | /// The decoration delegate.
12 | ITransactionalSenderBuilder AddDecorator(TransactionalSenderDecoration decoration);
13 | }
14 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/DependencyInjection/LookupDelegates.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Messaging.DependencyInjection
2 | {
3 | ///
4 | /// Represents a method that retrieves an by its name.
5 | ///
6 | /// The name of the to retrieve.
7 | /// The matching .
8 | public delegate ISender SenderLookup(string name);
9 |
10 | ///
11 | /// Represents a method that retrieves an by its name.
12 | ///
13 | /// The name of the to retrieve.
14 | /// The matching .
15 | public delegate ITransactionalSender TransactionalSenderLookup(string name);
16 |
17 | ///
18 | /// Represents a method that retrieves an by its name.
19 | ///
20 | /// The name of the to retrieve.
21 | /// The matching .
22 | public delegate IReceiver ReceiverLookup(string name);
23 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/DependencyInjection/ReceiverBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Messaging.DependencyInjection
4 | {
5 | ///
6 | /// The default implementation of the interface.
7 | ///
8 | public class ReceiverBuilder : IReceiverBuilder
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | ///
14 | /// The registration delegate that is responsible for creating the .
15 | ///
16 | public ReceiverBuilder(ReceiverRegistration registration) =>
17 | Registration = registration ?? throw new ArgumentNullException(nameof(registration));
18 |
19 | ///
20 | /// Gets the registration delegate that is responsible for creating the .
21 | ///
22 | public ReceiverRegistration Registration { get; private set; }
23 |
24 | ///
25 | /// Adds a decoration delegate to the builder.
26 | ///
27 | /// The decoration delegate.
28 | public IReceiverBuilder AddDecorator(ReceiverDecoration decoration)
29 | {
30 | #if NET6_0_OR_GREATER
31 | ArgumentNullException.ThrowIfNull(decoration);
32 | #else
33 | if (decoration is null) { throw new ArgumentNullException(nameof(decoration)); }
34 | #endif
35 |
36 | var registration = Registration;
37 | Registration = serviceProvider =>
38 | decoration.Invoke(registration.Invoke(serviceProvider), serviceProvider);
39 | return this;
40 | }
41 |
42 | ///
43 | /// Creates an instance of using the .
44 | ///
45 | ///
46 | /// The that retrieves the services required to create the .
47 | ///
48 | /// An instance of .
49 | public IReceiver Build(IServiceProvider serviceProvider) => Registration(serviceProvider);
50 | }
51 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/DependencyInjection/RegistrationDelegates.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Messaging.DependencyInjection
4 | {
5 | ///
6 | /// Represents a method that creates an .
7 | ///
8 | ///
9 | /// The that resolves dependencies necessary for the creation of the
10 | /// .
11 | ///
12 | /// An .
13 | public delegate ISender SenderRegistration(IServiceProvider serviceProvider);
14 |
15 | ///
16 | /// Represents a method that creates an .
17 | ///
18 | ///
19 | /// The that resolves dependencies necessary for the creation of the
20 | /// .
21 | ///
22 | /// An .
23 | public delegate ITransactionalSender TransactionalSenderRegistration(IServiceProvider serviceProvider);
24 |
25 | ///
26 | /// Represents a method that creates an .
27 | ///
28 | ///
29 | /// The that resolves dependencies necessary for the creation of the
30 | /// .
31 | ///
32 | /// An .
33 | public delegate IReceiver ReceiverRegistration(IServiceProvider serviceProvider);
34 | }
35 |
--------------------------------------------------------------------------------
/RockLib.Messaging/DependencyInjection/ReloadingSender.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Options;
2 | using System;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace RockLib.Messaging.DependencyInjection
7 | {
8 | internal class ReloadingSender : ISender
9 | where TSenderOptions : class, new()
10 | {
11 | public ReloadingSender(string name, Func createSender,
12 | IOptionsMonitor optionsMonitor, Action? configureOptions)
13 | {
14 | Name = name;
15 | CreateSender = createSender;
16 | ConfigureOptions = configureOptions;
17 |
18 | var options = optionsMonitor.GetOptions(Name, configureOptions);
19 | Sender = CreateSender.Invoke(options);
20 | ChangeListener = optionsMonitor.OnChange(OptionsMonitorChanged)!;
21 | }
22 |
23 | public string Name { get; }
24 |
25 | public ISender Sender { get; private set; }
26 |
27 | public Func CreateSender { get; }
28 |
29 | public Action? ConfigureOptions { get; }
30 |
31 | public IDisposable ChangeListener { get; }
32 |
33 | public Task SendAsync(SenderMessage message, CancellationToken cancellationToken) =>
34 | Sender.SendAsync(message, cancellationToken);
35 |
36 | public void Dispose()
37 | {
38 | ChangeListener.Dispose();
39 | Sender.Dispose();
40 | }
41 |
42 | private void OptionsMonitorChanged(TSenderOptions options, string? name)
43 | {
44 | if (string.Equals(Name, name, StringComparison.OrdinalIgnoreCase))
45 | {
46 | ConfigureOptions?.Invoke(options);
47 |
48 | var oldSender = Sender;
49 | var newSender = CreateSender.Invoke(options);
50 |
51 | Sender = newSender;
52 | oldSender.Dispose();
53 | }
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/DependencyInjection/SenderBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Messaging.DependencyInjection
4 | {
5 | ///
6 | /// The default implementation of the interface.
7 | ///
8 | public class SenderBuilder : ISenderBuilder
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | ///
14 | /// The registration delegate that is responsible for creating the .
15 | ///
16 | public SenderBuilder(SenderRegistration registration) =>
17 | Registration = registration ?? throw new ArgumentNullException(nameof(registration));
18 |
19 | ///
20 | /// Gets the registration delegate that is responsible for creating the .
21 | ///
22 | public SenderRegistration Registration { get; private set; }
23 |
24 | ///
25 | /// Adds a decoration delegate to the builder.
26 | ///
27 | /// The decoration delegate.
28 | public ISenderBuilder AddDecorator(SenderDecoration decoration)
29 | {
30 | #if NET6_0_OR_GREATER
31 | ArgumentNullException.ThrowIfNull(decoration);
32 | #else
33 | if (decoration is null) { throw new ArgumentNullException(nameof(decoration)); }
34 | #endif
35 |
36 | var registration = Registration;
37 | Registration = serviceProvider =>
38 | decoration.Invoke(registration.Invoke(serviceProvider), serviceProvider);
39 | return this;
40 | }
41 |
42 | ///
43 | /// Creates an instance of using the .
44 | ///
45 | ///
46 | /// The that retrieves the services required to create the .
47 | ///
48 | /// An instance of .
49 | public ISender Build(IServiceProvider serviceProvider) => Registration(serviceProvider);
50 | }
51 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/DependencyInjection/TransactionalSenderBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Messaging.DependencyInjection
4 | {
5 | ///
6 | /// The default implementation of the interface.
7 | ///
8 | public class TransactionalSenderBuilder : ITransactionalSenderBuilder
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | ///
14 | /// The registration delegate that is responsible for creating the .
15 | ///
16 | public TransactionalSenderBuilder(TransactionalSenderRegistration registration) =>
17 | Registration = registration ?? throw new ArgumentNullException(nameof(registration));
18 |
19 | ///
20 | /// Gets the registration delegate that is responsible for creating the .
21 | ///
22 | public TransactionalSenderRegistration Registration { get; private set; }
23 |
24 | ///
25 | /// Adds a decoration delegate to the builder.
26 | ///
27 | /// The decoration delegate.
28 | public ITransactionalSenderBuilder AddDecorator(TransactionalSenderDecoration decoration)
29 | {
30 | #if NET6_0_OR_GREATER
31 | ArgumentNullException.ThrowIfNull(decoration);
32 | #else
33 | if (decoration is null) { throw new ArgumentNullException(nameof(decoration)); }
34 | #endif
35 |
36 | var registration = Registration;
37 | Registration = serviceProvider =>
38 | decoration.Invoke(registration.Invoke(serviceProvider), serviceProvider);
39 | return this;
40 | }
41 |
42 | ///
43 | /// Creates an instance of using the .
44 | ///
45 | ///
46 | /// The that retrieves the services required to create the .
47 | ///
48 | /// An instance of .
49 | public ITransactionalSender Build(IServiceProvider serviceProvider) => Registration(serviceProvider);
50 | }
51 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/RockLib.Messaging/DisconnectedEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Messaging
4 | {
5 | ///
6 | /// Provides data for the event.
7 | ///
8 | public class DisconnectedEventArgs : EventArgs
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// The error message that describes the reason for the disconnection.
14 | public DisconnectedEventArgs(string errorMessage) => ErrorMessage = errorMessage;
15 |
16 | ///
17 | /// Gets the error message that describes the reason for the disconnection.
18 | ///
19 | public string ErrorMessage { get; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/RockLib.Messaging/ErrorEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Messaging
4 | {
5 | ///
6 | /// Provides data for error events such as .
7 | ///
8 | public class ErrorEventArgs : EventArgs
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// A message the describes the error.
14 | /// The exception responsible for the error.
15 | public ErrorEventArgs(string message, Exception exception)
16 | {
17 | Message = message;
18 | Exception = exception;
19 | }
20 |
21 | ///
22 | /// Gets the message that describes the error.
23 | ///
24 | public string Message { get; }
25 |
26 | ///
27 | /// Gets the exception responsible for the error.
28 | ///
29 | public Exception Exception { get; }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/RockLib.Messaging/ForwardingMessageHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace RockLib.Messaging
4 | {
5 | ///
6 | /// A decorator for the interface that forwards
7 | /// received messages to configured instances when
8 | /// messages are acknowledged, rolled back, or rejected.
9 | ///
10 | public class ForwardingMessageHandler : IMessageHandler
11 | {
12 | internal ForwardingMessageHandler(ForwardingReceiver forwardingReceiver, IMessageHandler messageHandler)
13 | {
14 | ForwardingReceiver = forwardingReceiver;
15 | MessageHandler = messageHandler;
16 | }
17 |
18 | ///
19 | /// Gets the whose properties determine
20 | /// what happens to messages when they are acknowledged, rolled back, or
21 | /// rejected. The that this decorates is the actual
22 | /// source of messages.
23 | ///
24 | public ForwardingReceiver ForwardingReceiver { get; }
25 |
26 | ///
27 | /// Gets the actual that gets notified when
28 | /// a message has been received.
29 | ///
30 | public IMessageHandler MessageHandler { get; }
31 |
32 | ///
33 | /// Handles a received message.
34 | ///
35 | /// When invoked, this method invokes the
36 | /// method of the property. It passes the
37 | /// property as the receiver argument and
38 | /// a new decorator as the message
39 | /// argument.
40 | ///
41 | ///
42 | /// The instance of that received the message.
43 | /// The message that was received.
44 | public Task OnMessageReceivedAsync(IReceiver receiver, IReceiverMessage message)
45 | {
46 | using var forwardMessage = new ForwardingReceiverMessage(ForwardingReceiver, message);
47 | return MessageHandler?.OnMessageReceivedAsync(ForwardingReceiver, forwardMessage) ?? Task.CompletedTask;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/RockLib.Messaging/ForwardingOutcome.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Messaging
2 | {
3 | ///
4 | /// Defines the outcomes for a forwarded message.
5 | ///
6 | public enum ForwardingOutcome
7 | {
8 | ///
9 | /// A forwarded message should have its method called.
10 | ///
11 | Acknowledge,
12 |
13 | ///
14 | /// A forwarded message should have its method called.
15 | ///
16 | Rollback,
17 |
18 | ///
19 | /// A forwarded message should have its method called.
20 | ///
21 | Reject
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/RockLib.Messaging/GZipCompressor.cs:
--------------------------------------------------------------------------------
1 | using System.IO.Compression;
2 | using System.IO;
3 |
4 | namespace RockLib.Messaging;
5 |
6 | // Lifted from:
7 | // https://github.com/RockLib/RockLib.Compression/blob/main/RockLib.Compression/GZipCompressor.cs
8 | // https://github.com/RockLib/RockLib.Compression/blob/main/RockLib.Compression/CompressionExtensions.cs
9 | internal static class GZipCompressor
10 | {
11 | internal static byte[] Compress(byte[] data)
12 | {
13 | using var inputStream = new MemoryStream(data);
14 | using var outputStream = new MemoryStream();
15 | using (var gzStream = new GZipStream(outputStream, CompressionMode.Compress, true))
16 | {
17 | inputStream.CopyTo(gzStream);
18 | }
19 |
20 | return outputStream.ToArray();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/RockLib.Messaging/GZipDecompressor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.IO.Compression;
4 |
5 | namespace RockLib.Messaging;
6 |
7 | // Lifted from:
8 | // https://github.com/RockLib/RockLib.Compression/blob/main/RockLib.Compression/GZipDecompressor.cs
9 | // https://github.com/RockLib/RockLib.Compression/blob/main/RockLib.Compression/CompressionExtensions.cs
10 | internal static class GZipDecompressor
11 | {
12 | internal static byte[] Decompress(byte[] data)
13 | {
14 | using var inputStream = new MemoryStream(data);
15 | using var outputStream = new MemoryStream();
16 | using (var gzStream = new GZipStream(inputStream, CompressionMode.Decompress, true))
17 | {
18 | gzStream.CopyTo(outputStream);
19 | }
20 |
21 | return outputStream.ToArray();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/RockLib.Messaging/HeaderNames.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Messaging
2 | {
3 | ///
4 | /// Provides the names of standard headers.
5 | ///
6 | public static class HeaderNames
7 | {
8 | ///
9 | /// The name of the header used to identify a message.
10 | ///
11 | public const string MessageId = "core_internal_id";
12 |
13 | ///
14 | /// The name of the header used to mark a message as having its payload compressed.
15 | ///
16 | public const string IsCompressedPayload = "core_compressed_payload";
17 |
18 | ///
19 | /// The name of the header used to indicate which messaging system generated the message.
20 | ///
21 | public const string OriginatingSystem = "core_originating_system";
22 |
23 | ///
24 | /// The name of the header used to indicate that a message was originally constructed with
25 | /// a binary message.
26 | ///
27 | public const string IsBinaryPayload = "core_binary_payload";
28 | }
29 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/IMessageHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace RockLib.Messaging
4 | {
5 | ///
6 | /// Defines an object that handles the messages received by an instance
7 | /// of .
8 | ///
9 | public interface IMessageHandler
10 | {
11 | ///
12 | /// Handles a received message.
13 | ///
14 | /// The instance of that received the message.
15 | /// The message that was received.
16 | Task OnMessageReceivedAsync(IReceiver receiver, IReceiverMessage message);
17 | }
18 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/IReceiver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Messaging
4 | {
5 | ///
6 | /// Defines an interface for receiving messages. To start receiving messages,
7 | /// set the value of the property.
8 | ///
9 | public interface IReceiver : IDisposable
10 | {
11 | ///
12 | /// Gets the name of this instance of .
13 | ///
14 | string Name { get; }
15 |
16 | ///
17 | /// Gets or sets the message handler for this receiver. When set, the receiver is started
18 | /// and will invoke the value's method
19 | /// when messages are received.
20 | ///
21 | ///
22 | /// Implementions of this interface should not allow this property to be set to null or
23 | /// to be set more than once.
24 | ///
25 | IMessageHandler MessageHandler { get; set; }
26 |
27 | ///
28 | /// Occurs when a connection is established.
29 | ///
30 | event EventHandler Connected;
31 |
32 | ///
33 | /// Occurs when a connection is lost.
34 | ///
35 | event EventHandler Disconnected;
36 |
37 | ///
38 | /// Occurs when an error happens.
39 | ///
40 | #pragma warning disable CA1716 // Identifiers should not match keywords
41 | event EventHandler Error;
42 | #pragma warning restore CA1716 // Identifiers should not match keywords
43 | }
44 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/ISender.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace RockLib.Messaging
6 | {
7 | ///
8 | /// Defines an interface for sending messages.
9 | ///
10 | public interface ISender : IDisposable
11 | {
12 | ///
13 | /// Gets the name of this instance of .
14 | ///
15 | string Name { get; }
16 |
17 | ///
18 | /// Asynchronously sends the specified message.
19 | ///
20 | /// The message to send.
21 | /// The token to monitor for cancellation requests.
22 | Task SendAsync(SenderMessage message, CancellationToken cancellationToken);
23 | }
24 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/ISenderTransaction.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Messaging
2 | {
3 | ///
4 | /// Defines an interface for a messaging transaction.
5 | ///
6 | public interface ISenderTransaction
7 | {
8 | ///
9 | /// Adds the specified message to the transaction.
10 | ///
11 | /// The message to add.
12 | void Add(SenderMessage message);
13 |
14 | ///
15 | /// Commits any messages that were added to the transaction.
16 | ///
17 | void Commit();
18 |
19 | ///
20 | /// Rolls back any messages that were added to the transaction.
21 | ///
22 | void Rollback();
23 | }
24 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/ITransactionalSender.cs:
--------------------------------------------------------------------------------
1 | namespace RockLib.Messaging
2 | {
3 | ///
4 | /// Defines an interface for sending messages transactionally.
5 | ///
6 | public interface ITransactionalSender : ISender
7 | {
8 | ///
9 | /// Starts a message-sending transaction.
10 | ///
11 | /// An object representing the new transaction.
12 | ISenderTransaction BeginTransaction();
13 | }
14 | }
--------------------------------------------------------------------------------
/RockLib.Messaging/InternalVisibleTo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: InternalsVisibleTo("RockLib.Messaging.Tests")]
--------------------------------------------------------------------------------
/RockLib.Messaging/NullableAttributes.cs:
--------------------------------------------------------------------------------
1 | #if NET48
2 | // This code is copied from https://source.dot.net/#System.Private.CoreLib/NullableAttributes.cs
3 | // and is only used for .NET 4.8
4 | using System;
5 |
6 | namespace System.Diagnostics.CodeAnalysis
7 | {
8 | ///
9 | /// Specifies that an output may be null even if the corresponding type disallows it.
10 | ///
11 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
12 | public sealed class MaybeNullAttribute
13 | : Attribute { }
14 |
15 | ///
16 | /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it.
17 | ///
18 | [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
19 | public sealed class MaybeNullWhenAttribute : Attribute
20 | {
21 | /// Initializes the attribute with the specified return value condition.
22 | ///
23 | /// The return value condition. If the method returns this value, the associated parameter may be null.
24 | ///
25 | public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
26 |
27 | /// Gets the return value condition.
28 | public bool ReturnValue { get; }
29 | }
30 | }
31 | #endif
--------------------------------------------------------------------------------
/RockLib.Messaging/OnMessageReceivedDelegate.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Threading.Tasks;
4 |
5 | namespace RockLib.Messaging
6 | {
7 | ///
8 | /// Defines a synchronous function that handles a received message.
9 | ///
10 | /// The instance of that received the message.
11 | /// The message that was received.
12 | [Obsolete("Use OnMessageReceivedAsyncDelegate instead.")]
13 | [EditorBrowsable(EditorBrowsableState.Never)]
14 | #pragma warning disable CA1711 // Identifiers should not have incorrect suffix
15 | public delegate void OnMessageReceivedDelegate(IReceiver receiver, IReceiverMessage message);
16 | #pragma warning restore CA1711 // Identifiers should not have incorrect suffix
17 |
18 | ///
19 | /// Defines an asynchronous function that handles a received message.
20 | ///
21 | /// The instance of that received the message.
22 | /// The message that was received.
23 | #pragma warning disable CA1711 // Identifiers should not have incorrect suffix
24 | public delegate Task OnMessageReceivedAsyncDelegate(IReceiver receiver, IReceiverMessage message);
25 | #pragma warning restore CA1711 // Identifiers should not have incorrect suffix
26 | }
27 |
--------------------------------------------------------------------------------
/RockLib.Messaging/RockLib.Messaging.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Embedded
4 | A simple API for sending and receiving messages.
5 | True
6 | True
7 | icon.png
8 | RockLib.Messaging
9 | LICENSE.md
10 | https://github.com/RockLib/RockLib.Messaging
11 | false
12 | A changelog is available at https://github.com/RockLib/RockLib.Messaging/blob/main/RockLib.Messaging/CHANGELOG.md.
13 | rocklib messaging
14 | 5.0.0
15 | True
16 | 5.0.0
17 |
18 |
19 | bin\$(Configuration)\$(TargetFramework)\$(PackageId).xml
20 |
21 |
22 | true
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/RockLib.Messaging/RockLib.Messaging.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27004.2009
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Messaging", "RockLib.Messaging.csproj", "{E1860FD3-54D8-4EDB-82E0-E61ED8725F20}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Messaging.Tests", "..\Tests\RockLib.Messaging.Tests\RockLib.Messaging.Tests.csproj", "{6B81F84C-A768-43DC-8F2E-F6F0EE9A9F32}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {E1860FD3-54D8-4EDB-82E0-E61ED8725F20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {E1860FD3-54D8-4EDB-82E0-E61ED8725F20}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {E1860FD3-54D8-4EDB-82E0-E61ED8725F20}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {E1860FD3-54D8-4EDB-82E0-E61ED8725F20}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {6B81F84C-A768-43DC-8F2E-F6F0EE9A9F32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {6B81F84C-A768-43DC-8F2E-F6F0EE9A9F32}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {6B81F84C-A768-43DC-8F2E-F6F0EE9A9F32}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {6B81F84C-A768-43DC-8F2E-F6F0EE9A9F32}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {3FEE2AF4-936B-474F-A3C0-7118F5655DCC}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/RockLib.Messaging/ValidatingSenderTransaction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Messaging
4 | {
5 | ///
6 | /// A decorator for the interface that invokes a validate delegate
7 | /// before each message is added to the transaction.
8 | ///
9 | public class ValidatingSenderTransaction : ISenderTransaction
10 | {
11 | internal ValidatingSenderTransaction(ISenderTransaction transaction, Action validate)
12 | {
13 | Transaction = transaction;
14 | Validate = validate;
15 | }
16 |
17 | ///
18 | /// Gets the actual .
19 | ///
20 | public ISenderTransaction Transaction { get; }
21 |
22 | ///
23 | /// Gets the delegate that will be invoked before each message added to a transaction.
24 | ///
25 | public Action Validate { get; }
26 |
27 | ///
28 | /// Validates, then adds the specified message to the transaction.
29 | ///
30 | /// The message to add.
31 | public void Add(SenderMessage message)
32 | {
33 | Validate(message);
34 | Transaction.Add(message);
35 | }
36 |
37 | ///
38 | /// Commits any messages that were added to the transaction.
39 | ///
40 | public void Commit() => Transaction.Commit();
41 |
42 | ///
43 | /// Rolls back any messages that were added to the transaction.
44 | ///
45 | public void Rollback() => Transaction.Rollback();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/RockLib.Messaging/ValidatingTransactionalSender.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Messaging
4 | {
5 | ///
6 | /// A decorator for the interface that invokes a validate delegate
7 | /// before each message is sent or added to a transaction.
8 | ///
9 | public class ValidatingTransactionalSender : ValidatingSender, ITransactionalSender
10 | {
11 | ///
12 | /// Initializes a new instances of the class.
13 | ///
14 | /// The name of the validating sender.
15 | ///
16 | /// The that actually sends messages and begins transactions.
17 | ///
18 | ///
19 | /// A delegate that will be invoked before each message is sent or added to a transaction. The delegate
20 | /// should throw an exception if header values are invalid or if required headers are missing. The delegate
21 | /// may also add missing headers that can be calculated at runtime.
22 | ///
23 | public ValidatingTransactionalSender(string name, ITransactionalSender transactionalSender, Action validate)
24 | : base(name, transactionalSender, validate)
25 | {
26 | TransactionalSender = transactionalSender;
27 | }
28 |
29 | ///
30 | /// Gets the that actually sends messages and begins transactions.
31 | ///
32 | public ITransactionalSender TransactionalSender { get; }
33 |
34 | ///
35 | /// Starts a message-sending transaction that validates messages as they are added.
36 | ///
37 | /// An object representing the new validating transaction.
38 | public ISenderTransaction BeginTransaction() =>
39 | new ValidatingSenderTransaction(TransactionalSender.BeginTransaction(), Validate);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.CloudEvents.Tests/AssemblySettings.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.CloudEvents.Tests/CorrelatingExtensionsTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using RockLib.Messaging.CloudEvents.Correlating;
3 | using System;
4 | using Xunit;
5 |
6 | namespace RockLib.Messaging.CloudEvents.Tests
7 | {
8 | public class CorrelatingExtensionsTests
9 | {
10 | [Fact(DisplayName = "GetCorrelationId extension method returns the 'correlationid' attribute")]
11 | public void GetCorrelationIdExtensionMethodHappyPath1()
12 | {
13 | var cloudEvent = new CloudEvent
14 | {
15 | Attributes = { [CorrelatedEvent.CorrelationIdAttribute] = "MyCorrelationId" }
16 | };
17 |
18 | var correlationId = cloudEvent.GetCorrelationId();
19 |
20 | correlationId.Should().Be("MyCorrelationId");
21 | }
22 |
23 | [Fact(DisplayName = "GetCorrelationId extension method adds the 'correlationid' attribute if missing")]
24 | public void GetCorrelationIdExtensionMethodHappyPath2()
25 | {
26 | var cloudEvent = new CloudEvent();
27 |
28 | cloudEvent.Attributes.Should().BeEmpty();
29 |
30 | cloudEvent.GetCorrelationId();
31 |
32 | cloudEvent.Attributes.Should().ContainKey(CorrelatedEvent.CorrelationIdAttribute)
33 | .WhoseValue.Should().NotBeNull();
34 | }
35 |
36 | [Fact(DisplayName = "GetCorrelationId extension method throws if cloudEvent parameter is null")]
37 | public void GetCorrelationIdExtensionMethodSadPath()
38 | {
39 | CloudEvent cloudEvent = null!;
40 |
41 | Action act = () => cloudEvent.GetCorrelationId();
42 |
43 | act.Should().ThrowExactly().WithMessage("*cloudEvent*");
44 | }
45 |
46 | [Fact(DisplayName = "SetCorrelationId extension method sets the 'correlationid' attribute")]
47 | public void SetCorrelationIdExtensionMethodHappyPath()
48 | {
49 | var cloudEvent = new CloudEvent();
50 |
51 | cloudEvent.SetCorrelationId("MyCorrelationId");
52 |
53 | cloudEvent.Attributes.Should().ContainKey(CorrelatedEvent.CorrelationIdAttribute)
54 | .WhoseValue.Should().Be("MyCorrelationId");
55 | }
56 |
57 | [Fact(DisplayName = "SetCorrelationId extension method throws if cloudEvent is null")]
58 | public void SetCorrelationIdExtensionMethodSadPath1()
59 | {
60 | CloudEvent cloudEvent = null!;
61 |
62 | Action act = () => cloudEvent.SetCorrelationId("MyCorrelationId");
63 |
64 | act.Should().ThrowExactly().WithMessage("*cloudEvent*");
65 | }
66 |
67 | [Theory(DisplayName = "SetCorrelationId extension method throws if correlationId is null or empty")]
68 | [InlineData(null)]
69 | [InlineData("")]
70 | public void SetCorrelationIdExtensionMethodSadPath2(string? correlationId)
71 | {
72 | var cloudEvent = new CloudEvent();
73 |
74 | Action act = () => cloudEvent.SetCorrelationId(correlationId!);
75 |
76 | act.Should().ThrowExactly().WithMessage("*correlationId*");
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.CloudEvents.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.CloudEvents.Tests/MockExtensions.cs:
--------------------------------------------------------------------------------
1 | using Moq;
2 | using System;
3 | using System.Text.RegularExpressions;
4 |
5 | namespace RockLib.Messaging.CloudEvents.Tests
6 | {
7 | public static class MockExtensions
8 | {
9 | public static Mock SetupTestProtocolBinding(this Mock mockProtocolBinding)
10 | {
11 | #if NET6_0_OR_GREATER
12 | ArgumentNullException.ThrowIfNull(mockProtocolBinding);
13 | #else
14 | if (mockProtocolBinding is null) { throw new ArgumentNullException(nameof(mockProtocolBinding)); }
15 | #endif
16 |
17 | mockProtocolBinding.Setup(m => m.GetHeaderName(It.IsAny())).Returns(header => "test-" + header);
18 |
19 | mockProtocolBinding.Setup(m => m.GetAttributeName(It.IsAny(), out It.Ref.IsAny))
20 | .Callback(new GetAttributeNameCallback(TestGetAttributeNameCallback))
21 | .Returns(new GetAttributeName(TestGetAttributeName));
22 |
23 | return mockProtocolBinding;
24 | }
25 |
26 | public delegate void GetAttributeNameCallback(string headerName, out bool isCloudEventAttribute);
27 |
28 | public delegate string GetAttributeName(string headerName, out bool isCloudEventAttribute);
29 |
30 | public static void TestGetAttributeNameCallback(string headerName, out bool isCloudEventAttribute)
31 | {
32 | var attributeName = Regex.Replace(headerName, "^test-", "");
33 | isCloudEventAttribute = attributeName != headerName;
34 | }
35 |
36 | public static string TestGetAttributeName(string headerName, out bool isCloudEventAttribute)
37 | {
38 | var attributeName = Regex.Replace(headerName, "^test-", "");
39 | isCloudEventAttribute = attributeName != headerName;
40 | return attributeName;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.CloudEvents.Tests/RockLib.Messaging.CloudEvents.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 | CA2007
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | all
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 |
17 |
18 | all
19 | runtime; build; native; contentfiles; analyzers; buildtransitive
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Http.Tests/AssemblySettings.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Http.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Http.Tests/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "WSL": {
4 | "commandName": "WSL2",
5 | "distributionName": ""
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Http.Tests/RockLib.Messaging.Http.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | false
5 | CA2007
6 |
7 |
8 |
9 |
10 |
11 |
12 | all
13 | runtime; build; native; contentfiles; analyzers
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Kafka.Tests/AssemblySettings.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Kafka.Tests/DependencyInjectionTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Xunit;
4 | using RockLib.Messaging.Kafka.DependencyInjection;
5 |
6 | namespace RockLib.Messaging.Kafka.Tests
7 | {
8 | public static class DependencyInjectionTests
9 | {
10 | [Fact]
11 | public static void KafkaSenderTest()
12 | {
13 | var services = new ServiceCollection();
14 |
15 | services.AddKafkaSender("mySender", options =>
16 | {
17 | options.Topic= "SenderTopic";
18 | options.BootstrapServers = "SenderServer";
19 | options.MessageTimeoutMs = 555;
20 | });
21 |
22 | var serviceProvider = services.BuildServiceProvider();
23 |
24 | var sender = serviceProvider.GetRequiredService();
25 |
26 | var kafkaSender = sender.Should().BeOfType().Subject;
27 |
28 | kafkaSender.Name.Should().Be("mySender");
29 | kafkaSender.Topic.Should().Be("SenderTopic");
30 | kafkaSender.Producer.Should().NotBeNull();
31 | }
32 |
33 | [Fact]
34 | public static void KafkaReceiverTest()
35 | {
36 | var services = new ServiceCollection();
37 |
38 | services.AddKafkaReceiver("myReceiver", options =>
39 | {
40 | options.Topic = "ReceiverTopic";
41 | options.BootstrapServers = "ReceiverServer";
42 | options.GroupId = "ReceiverGroupId";
43 | });
44 |
45 | var serviceProvider = services.BuildServiceProvider();
46 |
47 | var receiver = serviceProvider.GetRequiredService();
48 |
49 | var kafkaReceiver = receiver.Should().BeOfType().Subject;
50 |
51 | kafkaReceiver.Name.Should().Be("myReceiver");
52 | kafkaReceiver.Topic.Should().Be("ReceiverTopic");
53 | kafkaReceiver.Consumer.Should().NotBeNull();
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Kafka.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Kafka.Tests/RockLib.Messaging.Kafka.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 | CA2007
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | all
15 | runtime; build; native; contentfiles; analyzers
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.NamedPipes.Tests/AssemblySettings.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.NamedPipes.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.NamedPipes.Tests/NamedPipesTests.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using Xunit;
4 |
5 | namespace RockLib.Messaging.NamedPipes.Tests
6 | {
7 | public static class NamedPipesTests
8 | {
9 | [Fact]
10 | public static async Task NamedPipeMessagesAreSentAndReceived()
11 | {
12 | using var waitHandle = new AutoResetEvent(false);
13 |
14 | using var receiver = new NamedPipeReceiver("foo", "test-pipe");
15 | var payload = string.Empty;
16 | var headerValue = string.Empty;
17 |
18 | receiver.Start(async m =>
19 | {
20 | payload = m.StringPayload;
21 | headerValue = m.Headers.GetValue("bar");
22 | await m.AcknowledgeAsync();
23 | waitHandle.Set();
24 | });
25 |
26 | using (var sender = new NamedPipeSender("foo", "test-pipe"))
27 | {
28 | await sender.SendAsync(new SenderMessage("Hello, world!") { Headers = { { "bar", "abc" } } });
29 | waitHandle.WaitOne();
30 | }
31 |
32 | Assert.Equal("Hello, world!", payload);
33 | Assert.Equal("abc", headerValue);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.NamedPipes.Tests/RockLib.Messaging.NamedPipes.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 | CA2007
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | all
14 | runtime; build; native; contentfiles; analyzers
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.RabbitMQ.Tests/AssemblySettings.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.RabbitMQ.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.RabbitMQ.Tests/RockLib.Messaging.RabbitMQ.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 | CA2007
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | all
15 | runtime; build; native; contentfiles; analyzers
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.SNS.Tests/AssemblySettings.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.SNS.Tests/DependencyInjectionTests.cs:
--------------------------------------------------------------------------------
1 | using Amazon;
2 | using FluentAssertions;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using RockLib.Dynamic;
5 | using RockLib.Messaging.DependencyInjection;
6 | using System;
7 | using Xunit;
8 |
9 | namespace RockLib.Messaging.SNS.Tests
10 | {
11 | public static class DependencyInjectionTests
12 | {
13 | [Fact]
14 | public static void SendViaSNSSender()
15 | {
16 | var services = new ServiceCollection();
17 | services.Configure(options => { });
18 |
19 | services.AddSNSSender("mySender", options =>
20 | {
21 | options.TopicArn = "myTopicArn";
22 | options.Region = "us-west-2";
23 | }, false);
24 |
25 | var serviceProvider = services.BuildServiceProvider();
26 |
27 | var sender = serviceProvider.GetRequiredService();
28 |
29 | var snsSender = sender.Should().BeOfType().Subject;
30 |
31 | snsSender.Name.Should().Be("mySender");
32 | snsSender.TopicArn.Should().Be("myTopicArn");
33 | snsSender.SnsClient.Config.RegionEndpoint.Should().Be(RegionEndpoint.USWest2);
34 | }
35 |
36 | [Fact]
37 | public static void SendViaReloadingSender()
38 | {
39 | var reloadingSenderType = Type.GetType("RockLib.Messaging.DependencyInjection.ReloadingSender`1, RockLib.Messaging", true)!
40 | .MakeGenericType(typeof(SNSSenderOptions));
41 |
42 | var services = new ServiceCollection();
43 | services.Configure(options => { });
44 |
45 | services.AddSNSSender("mySender", options =>
46 | {
47 | options.TopicArn = "myTopicArn";
48 | options.Region = "us-west-2";
49 | }, true);
50 |
51 | var serviceProvider = services.BuildServiceProvider();
52 |
53 | var sender = serviceProvider.GetRequiredService();
54 |
55 | sender.Should().BeOfType(reloadingSenderType);
56 |
57 | var snsSender = (SNSSender)sender.Unlock().Sender;
58 |
59 | snsSender.Name.Should().Be("mySender");
60 | snsSender.TopicArn.Should().Be("myTopicArn");
61 | snsSender.SnsClient.Config.RegionEndpoint.Should().Be(RegionEndpoint.USWest2);
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.SNS.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.SNS.Tests/RockLib.Messaging.SNS.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 | CA2007
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | all
15 | runtime; build; native; contentfiles; analyzers
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.SNS.Tests/SNSTests.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using Amazon.SimpleNotificationService;
4 | using Amazon.SimpleNotificationService.Model;
5 | using Moq;
6 | using Xunit;
7 |
8 | namespace RockLib.Messaging.SNS.Tests
9 | {
10 | public static class SNSTests
11 | {
12 | [Fact]
13 | public static async Task SnsSenderSendsMessagesToItsIAmazonSqs()
14 | {
15 | var mockSns = new Mock();
16 |
17 | using (var sender = new SNSSender(mockSns.Object, "foo", "http://url.com/foo"))
18 | await sender.SendAsync(
19 | new SenderMessage("Hello, world!") { Headers = { { "bar", "abc" } } })
20 | ;
21 |
22 | mockSns.Verify(m => m.PublishAsync(
23 | It.Is(r => r.Message == "Hello, world!"
24 | && r.MessageAttributes[HeaderNames.OriginatingSystem].StringValue == "SNS"
25 | && r.MessageAttributes["bar"].StringValue == "abc"),
26 | It.IsAny()));
27 | }
28 |
29 | [Fact]
30 | public static async Task SnsSenderSendsMessageWithMessageGroupId()
31 | {
32 | var mockSns = new Mock();
33 |
34 | using (var sender = new SNSSender(mockSns.Object, "foo", "http://url.com/foo"))
35 | await sender.SendAsync(new SenderMessage("Hello, world!") { Headers = {
36 | { "bar", "abc" },
37 | { "messageGroupId", "this is my group id" }
38 | } });
39 |
40 | mockSns.Verify(m => m.PublishAsync(
41 | It.Is(r => r.Message == "Hello, world!"
42 | && r.MessageAttributes[HeaderNames.OriginatingSystem].StringValue == "SNS"
43 | && r.MessageAttributes["bar"].StringValue == "abc"
44 | && r.MessageGroupId == "this is my group id"),
45 | It.IsAny()));
46 | }
47 |
48 | [Fact]
49 | public static async Task SnsSenderSendsMessageWithMessageDeduplicationId()
50 | {
51 | var mockSns = new Mock();
52 |
53 | using (var sender = new SNSSender(mockSns.Object, "foo", "http://url.com/foo"))
54 | await sender.SendAsync(new SenderMessage("Hello, world!") { Headers = {
55 | { "bar", "abc" },
56 | { "messageDeduplicationId", "this is my deduplication id" }
57 | } });
58 |
59 | mockSns.Verify(m => m.PublishAsync(
60 | It.Is(r => r.Message == "Hello, world!"
61 | && r.MessageAttributes[HeaderNames.OriginatingSystem].StringValue == "SNS"
62 | && r.MessageAttributes["bar"].StringValue == "abc"
63 | && r.MessageDeduplicationId == "this is my deduplication id"),
64 | It.IsAny()));
65 | }
66 | }
67 | }
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.SQS.Tests/AssemblySettings.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.SQS.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.SQS.Tests/RockLib.Messaging.SQS.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | false
5 | CA2007
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | all
17 | runtime; build; native; contentfiles; analyzers
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Tests/AssemblySettings.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Tests/CustomConfigFiles/MultipleReceivers_appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "RockLib.Messaging": {
3 | "Receivers": [
4 | {
5 | "Type": "RockLib.Messaging.Tests.FakeReceiver, RockLib.Messaging.Tests",
6 | "Value": {
7 | "Name": "Pipe1",
8 | "PipeName": "PipeName1"
9 | }
10 | },
11 | {
12 | "Type": "RockLib.Messaging.Tests.FakeReceiver, RockLib.Messaging.Tests",
13 | "Value": {
14 | "Name": "Pipe2",
15 | "PipeName": "PipeName2"
16 | }
17 | }
18 | ]
19 | }
20 | }
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Tests/CustomConfigFiles/MultipleSenders_appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "RockLib.Messaging": {
3 | "Senders": [
4 | {
5 | "Type": "RockLib.Messaging.Tests.FakeSender, RockLib.Messaging.Tests",
6 | "Value": {
7 | "Name": "Pipe1",
8 | "PipeName": "PipeName1",
9 | "Compressed": true
10 | }
11 | },
12 | {
13 | "Type": "RockLib.Messaging.Tests.FakeSender, RockLib.Messaging.Tests",
14 | "Value": {
15 | "Name": "Pipe2",
16 | "PipeName": "PipeName2",
17 | "Compressed": false
18 | }
19 | }
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Tests/CustomConfigFiles/SingleReceiver_appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "RockLib.Messaging": {
3 | "Receivers": {
4 | "Type": "RockLib.Messaging.Tests.FakeReceiver, RockLib.Messaging.Tests",
5 | "Value": {
6 | "Name": "Pipe1",
7 | "PipeName": "PipeName1"
8 | }
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Tests/CustomConfigFiles/SingleSender_appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "RockLib.Messaging": {
3 | "Senders": {
4 | "Type": "RockLib.Messaging.Tests.FakeSender, RockLib.Messaging.Tests",
5 | "Value": {
6 | "Name": "Pipe1",
7 | "PipeName": "PipeName1",
8 | "Compressed": true
9 | }
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Tests/FakeMessageHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 |
4 | namespace RockLib.Messaging.Tests
5 | {
6 | public class FakeMessageHandler : IMessageHandler
7 | {
8 | #pragma warning disable CA1002 // Do not expose generic lists
9 | public List<(IReceiver Receiver, IReceiverMessage Message)> ReceivedMessages { get; } = new List<(IReceiver, IReceiverMessage)>();
10 | #pragma warning restore CA1002 // Do not expose generic lists
11 |
12 | public Task OnMessageReceivedAsync(IReceiver receiver, IReceiverMessage message)
13 | {
14 | ReceivedMessages.Add((receiver, message));
15 | return Task.FromResult(0);
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Tests/FakeReceiver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Messaging.Tests
4 | {
5 | public sealed class FakeReceiver : IReceiver
6 | {
7 | #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
8 | public string Name { get; set; }
9 | public string PipeName { get; set; }
10 |
11 | public IMessageHandler MessageHandler { get; set; }
12 |
13 | #pragma warning disable CS0067 // Event is never used
14 | public event EventHandler Connected;
15 | public event EventHandler Disconnected;
16 | public event EventHandler Error;
17 | #pragma warning restore CS0067 // Event is never used
18 | #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
19 |
20 | public void Dispose()
21 | {
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Tests/FakeSender.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace RockLib.Messaging.Tests
6 | {
7 | public sealed class FakeSender : ISender
8 | {
9 | #pragma warning disable CA1002 // Do not expose generic lists
10 | public List SentMessages { get; } = new List();
11 | #pragma warning restore CA1002 // Do not expose generic lists
12 |
13 | #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
14 | public string Name { get; set; }
15 | public string PipeName { get; set; }
16 | #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
17 |
18 | public void Dispose()
19 | {
20 | }
21 |
22 | public Task SendAsync(SenderMessage message, CancellationToken cancellationToken)
23 | {
24 | SentMessages.Add(message);
25 | return Task.CompletedTask;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Tests/ForwardingMessageHandlerTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using System.Threading.Tasks;
3 | using Xunit;
4 |
5 | namespace RockLib.Messaging.Tests
6 | {
7 | public class ForwardingMessageHandlerTests
8 | {
9 | [Fact]
10 | public async Task OnMessageReceivedCallsInnerHandlerOnMessageReceivedWithForwardingReceiverMessage()
11 | {
12 | using var receiver = new FakeReceiver();
13 | using var forwardingReceiver = new ForwardingReceiver("foo", receiver);
14 | var messageHandler = new FakeMessageHandler();
15 |
16 | var handler = new ForwardingMessageHandler(forwardingReceiver, messageHandler);
17 |
18 | using var message = new FakeReceiverMessage("Hello, world!");
19 |
20 | await handler.OnMessageReceivedAsync(receiver, message);
21 |
22 | messageHandler.ReceivedMessages.Should().ContainSingle();
23 | messageHandler.ReceivedMessages[0].Receiver.Should().BeSameAs(forwardingReceiver);
24 | messageHandler.ReceivedMessages[0].Message.Should().BeOfType();
25 | ((ForwardingReceiverMessage)messageHandler.ReceivedMessages[0].Message).Message.Should().BeSameAs(message);
26 | ((ForwardingReceiverMessage)messageHandler.ReceivedMessages[0].Message).ForwardingReceiver.Should().BeSameAs(forwardingReceiver);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Tests/ForwardingReceiverTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Xunit;
3 |
4 | namespace RockLib.Messaging.Tests
5 | {
6 | public class ForwardingReceiverTests
7 | {
8 | [Fact]
9 | public void SettingMessageHandlerSetsTheMessageHandlerOfTheReceiverToAForwardingMessageHandler()
10 | {
11 | using var receiver = new FakeReceiver();
12 | using var forwardingReceiver = new ForwardingReceiver("foo", receiver);
13 |
14 | var messageHandler = new FakeMessageHandler();
15 |
16 | forwardingReceiver.MessageHandler = messageHandler;
17 |
18 | forwardingReceiver.MessageHandler.Should().BeSameAs(messageHandler);
19 |
20 | receiver.MessageHandler.Should().NotBeSameAs(messageHandler);
21 | receiver.MessageHandler.Should().BeOfType();
22 | ((ForwardingMessageHandler)receiver.MessageHandler).MessageHandler.Should().BeSameAs(messageHandler);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Tests/ReceiverExtensionsTests.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Xunit;
3 |
4 | namespace RockLib.Messaging.Tests
5 | {
6 | public class ReceiverExtensionsTests
7 | {
8 | [Fact]
9 | public async Task TheCallbackPassedToStart1IsInvokedWhenAMessageIsReceived()
10 | {
11 | using var receiver = new FakeReceiver();
12 |
13 | var received = false;
14 |
15 | receiver.Start(async m =>
16 | {
17 | received = true;
18 | await m.AcknowledgeAsync();
19 | });
20 |
21 | using var message = new FakeReceiverMessage("Hello, world!");
22 | await receiver.MessageHandler.OnMessageReceivedAsync(receiver, message);
23 |
24 | Assert.True(received);
25 | }
26 |
27 | [Fact]
28 | public async Task TheCallbackPassedToStart2IsInvokedWhenAMessageIsReceived()
29 | {
30 | using var receiver = new FakeReceiver();
31 |
32 | var received = false;
33 |
34 | #pragma warning disable CS0618 // Type or member is obsolete
35 | receiver.Start(m =>
36 | {
37 | received = true;
38 | m.Acknowledge();
39 | });
40 | #pragma warning restore CS0618 // Type or member is obsolete
41 |
42 | using var message = new FakeReceiverMessage("Hello, world!");
43 | await receiver.MessageHandler.OnMessageReceivedAsync(receiver, message);
44 |
45 | Assert.True(received);
46 | }
47 |
48 | [Fact]
49 | public async Task TheCallbackPassedToStart3IsInvokedWhenAMessageIsReceived()
50 | {
51 | using var receiver = new FakeReceiver();
52 |
53 | var received = false;
54 |
55 | receiver.Start(async (r, m) =>
56 | {
57 | received = true;
58 | await m.AcknowledgeAsync();
59 | });
60 |
61 | using var message = new FakeReceiverMessage("Hello, world!");
62 | await receiver.MessageHandler.OnMessageReceivedAsync(receiver, message);
63 |
64 | Assert.True(received);
65 | }
66 |
67 | [Fact]
68 | public async Task TheCallbackPassedToStart4IsInvokedWhenAMessageIsReceived()
69 | {
70 | using var receiver = new FakeReceiver();
71 |
72 | var received = false;
73 |
74 | #pragma warning disable CS0618 // Type or member is obsolete
75 | receiver.Start((r, m) =>
76 | {
77 | received = true;
78 | m.Acknowledge();
79 | });
80 | #pragma warning restore CS0618 // Type or member is obsolete
81 |
82 | using var message = new FakeReceiverMessage("Hello, world!");
83 | await receiver.MessageHandler.OnMessageReceivedAsync(receiver, message);
84 |
85 | Assert.True(received);
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Tests/RockLib.Messaging.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 | CA2007
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | all
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | PreserveNewest
24 |
25 |
26 | Always
27 |
28 |
29 | Always
30 |
31 |
32 | Always
33 |
34 |
35 | Always
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Tests/ValidatingSenderTransactionTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Moq;
3 | using RockLib.Dynamic;
4 | using System;
5 | using System.Collections.Generic;
6 | using Xunit;
7 |
8 | namespace RockLib.Messaging.Tests
9 | {
10 | public class ValidatingSenderTransactionTests
11 | {
12 | [Fact]
13 | public void AddMethodHappyPath()
14 | {
15 | var sentMessages = new List();
16 |
17 | var mockTransaction = new Mock();
18 | Action validate = message => sentMessages.Add(message);
19 |
20 | ValidatingSenderTransaction validatingTransaction = typeof(ValidatingSenderTransaction).New(mockTransaction.Object, validate);
21 |
22 | var message1 = new SenderMessage("Hello, world!");
23 | var message2 = new SenderMessage("Good-bye, cruel world!");
24 |
25 | validatingTransaction.Add(message1);
26 | validatingTransaction.Add(message2);
27 |
28 | mockTransaction.Verify(m => m.Add(message1), Times.Once());
29 | mockTransaction.Verify(m => m.Add(message2), Times.Once());
30 |
31 | sentMessages.Should().HaveCount(2);
32 | sentMessages[0].Should().Be(message1);
33 | sentMessages[1].Should().Be(message2);
34 | }
35 |
36 | [Fact]
37 | public void CommitMethodHappyPath()
38 | {
39 | var mockTransaction = new Mock();
40 | Action validate = message => { };
41 |
42 | ValidatingSenderTransaction validatingTransaction = typeof(ValidatingSenderTransaction).New(mockTransaction.Object, validate);
43 |
44 | validatingTransaction.Commit();
45 |
46 | mockTransaction.Verify(m => m.Commit(), Times.Once());
47 | }
48 |
49 | [Fact]
50 | public void RollbackMethodHappyPath()
51 | {
52 | var mockTransaction = new Mock();
53 | Action validate = message => { };
54 |
55 | ValidatingSenderTransaction validatingTransaction = typeof(ValidatingSenderTransaction).New(mockTransaction.Object, validate);
56 |
57 | validatingTransaction.Rollback();
58 |
59 | mockTransaction.Verify(m => m.Rollback(), Times.Once());
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Tests/RockLib.Messaging.Tests/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "RockLib.Messaging": {
3 | "Senders": [
4 | {
5 | "Type": "RockLib.Messaging.Tests.FakeSender, RockLib.Messaging.Tests",
6 | "Value": {
7 | "Name": "MyFakeSender",
8 | "PipeName": "MyPipeName"
9 | }
10 | },
11 | {
12 | "Type": "RockLib.Messaging.Tests.MessagingExtensionsTests+TestSenderDecorator, RockLib.Messaging.Tests",
13 | "Value": {
14 | "Name": "MyTestSenderDecorator",
15 | "SenderName": "MyFakeSender"
16 | }
17 | }
18 | ],
19 | "Receivers": [
20 | {
21 | "Type": "RockLib.Messaging.Tests.FakeReceiver, RockLib.Messaging.Tests",
22 | "Value": {
23 | "Name": "MyFakeReceiver",
24 | "PipeName": "MyPipeName"
25 | }
26 | },
27 | {
28 | "Type": "RockLib.Messaging.Tests.MessagingExtensionsTests+TestReceiverDecorator, RockLib.Messaging.Tests",
29 | "Value": {
30 | "Name": "MyTestReceiverDecorator",
31 | "ReceiverName": "MyFakeReceiver"
32 | }
33 | }
34 | ]
35 | }
36 | }
--------------------------------------------------------------------------------
/docs/Compressed.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 12
3 | ---
4 |
5 | # Sending and receiving compressed messages
6 |
7 | To send compressed messages with RockLib.Messaging, instantiate the `SenderMessage` object using either of its constructors that has a `payload` parameter. Pass the value of the `payload` parameter uncompressed and pass `true` as the value of the optional `compress` parameter. The payload will be gzip compressed and a header with the name `HeaderNames.IsCompressedPayload` is set to `true`, marking the message as compressed. *Note that if gzip compressing the payload does not make it smaller, then the payload is left uncompressed.*
8 |
9 | ```csharp
10 | ISender sender = // TODO: instantiate
11 |
12 | string stringPayload = // TODO: Load value
13 | await sender.SendAsync(new SenderMessage(stringPayload, compress: true));
14 |
15 | byte[] binaryPayload = // TODO: Load value
16 | await sender.SendAsync(new SenderMessage(binaryPayload, compress: true));
17 | ```
18 |
19 | ---
20 |
21 | On the receiving side, there is nothing to do. All of the implementations of `IReceiverMessage` in this repository inherit from the `ReceiverMessage` base class, which automatically decompresses any messages that have a header named `HeaderNames.IsCompressedPayload` with a value of `true`.
22 |
--------------------------------------------------------------------------------
/docs/SNS.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 6
3 | ---
4 |
5 | # How to use and configure RockLib.Messaging.SNS
6 |
7 | See the [.NET Core example] for a complete demo application.
8 |
9 | ## SNSSender
10 |
11 | The SNSSender class can be directly instantiated and has the following parameters:
12 |
13 | - name
14 | - The name of the instance of SNSSender.
15 | - topicArn
16 | - The ARN (Amazon Resource Name) of the SNS topic.
17 | - region (optional, defaults to null)
18 | - The region of the SNS topic.
19 |
20 | ```csharp
21 | ISender sender = new SNSSender("MySender", "arn:aws:sns:us-west-2:123456789012:MyTopic", "us-west-2");
22 | ```
23 |
24 | ---
25 |
26 | To add an SNSSender to a service collection for dependency injection, use the `AddSNSSender` method, optionally passing in a `configureOptions` callback:
27 |
28 | ```csharp
29 | services.AddSNSSender("MySender", options =>
30 | {
31 | options.Region = "us-west-2";
32 | options.TopicArn = "arn:aws:sns:us-west-2:123456789012:MyTopic";
33 | });```
34 |
35 | To bind `SNSSenderOptions` to a configuration section, use the name of the sender when calling the `Configure` method:
36 |
37 | ```csharp
38 | public void ConfigureServices(IServiceCollection services)
39 | {
40 | services.Configure("MySender", Configuration.GetSection("MySNSSender"));
41 | services.AddSNSSender("MySender");
42 | }
43 |
44 | /* appsettings.json:
45 | {
46 | "MySNSSender": {
47 | "Region": "us-west-2",
48 | "TopicArn": "arn:aws:sns:us-west-2:123456789012:MyTopic"
49 | }
50 | }
51 | */
52 | ```
53 |
54 | ---
55 |
56 | MessagingScenarioFactory configured with an `SNSSender` named "commands" as follows:
57 |
58 | ```json
59 | {
60 | "RockLib.Messaging": {
61 | "Senders": {
62 | "Type": "RockLib.Messaging.SNS.SNSSender, RockLib.Messaging.SNS",
63 | "Value": {
64 | "Name": "commands",
65 | "TopicArn": "arn:aws:sns:us-west-2:123456789012:MyTopic",
66 | "Region": "us-west-2"
67 | }
68 | }
69 | }
70 | }
71 | ```
72 |
73 | ```csharp
74 | // Note that implementations of the ISender interface are somewhat expensive and intended to be
75 | // long-lived. They are thread-safe, so you can use a single instance throughout your application.
76 | // Instances should be disposed before the application exits.
77 |
78 | // MessagingScenarioFactory uses the above JSON configuration to create a SNSSender
79 | // Note that the Value object's properties in the json must map to a valid constructor since CreateSender Creates instances using [RockLib.Configuration.ObjectFactory](https://github.com/RockLib/RockLib.Configuration/tree/main/RockLib.Configuration.ObjectFactory#rocklibconfigurationobjectfactory)
80 | ISender sender = MessagingScenarioFactory.CreateSender("commands");
81 |
82 | // Use the sender (for good, not evil):
83 | sender.Send("DROP DATABASE Production;");
84 |
85 | // Always dispose the sender when done with it.
86 | sender.Dispose();
87 | ```
88 |
89 | ---
90 |
91 | [.NET Core example]: https://github.com/RockLib/RockLib.Messaging/blob/main/Examples/Example.Messaging.SNS/Program.cs
92 |
--------------------------------------------------------------------------------
/docs/SendingMessages.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | ---
4 |
5 | # How to send messages
6 |
7 | To send messages, an instance of the `ISender` interface is needed. Throughout this document, it's assumed that you have an `ISender` variable named `sender` declared and initialized somewhere. Like this:
8 |
9 | ```csharp
10 | ISender sender; // TODO: Initialize the sender variable
11 | ```
12 |
13 | ---
14 |
15 | One way to send a message is by passing the message payload to the `SendAsync` extension method (note that these examples all use `string` payloads, but there are overloads for each method that take a `byte[]`):
16 |
17 | ```csharp
18 | CancellationTokenSource cancellation = new CancellationTokenSource();
19 | await sender.SendAsync("", cancellation.Token);
20 | ```
21 |
22 | ---
23 |
24 | If you don't need the call to be cancellable, omit the `CancellationToken`:
25 |
26 | ```csharp
27 | await sender.SendAsync("");
28 | ```
29 |
30 | ---
31 |
32 | If you need to make the call synchronously, use the `Send` extension method:
33 |
34 | ```csharp
35 | sender.Send("");
36 | ```
37 |
38 | ---
39 |
40 | If you need to set header values in the message, create an instance of `SenderMessage` and pass it to the `SendAsync` method:
41 |
42 | ```csharp
43 | CancellationTokenSource cancellation = new CancellationTokenSource();
44 |
45 | SenderMessage message = new SenderMessage("");
46 | message.Headers["IsDemoCode"] = true;
47 |
48 | await sender.SendAsync(message, cancellation.Token);
49 | ```
50 |
51 | If you need to utilize the `MessageGroupId` and `MessageDeduplicationId` properties, add the values to the header:
52 |
53 | ```csharp
54 | CancellationTokenSource cancellation = new CancellationTokenSource();
55 |
56 | SenderMessage message = new SenderMessage("");
57 | message.Headers["messageGroupId"] = "message group id";
58 | message.Headers["messageDeduplicationId"] = "message deduplication id";
59 |
60 | await sender.SendAsync(message, cancellation.Token);
61 | ```
62 |
63 | ---
64 |
65 | The `CancellationToken` can be omitted from this call as well:
66 |
67 | ```csharp
68 | SenderMessage message = new SenderMessage("");
69 | message.Headers["CodeSnippetIndex"] = 5;
70 |
71 | await sender.SendAsync(message);
72 | ```
73 |
74 | ---
75 |
76 | And there is a synchronous version as expected:
77 |
78 | ```csharp
79 | SenderMessage message = new SenderMessage("");
80 | message.Headers["ExampleDescription"] = "boring";
81 |
82 | sender.Send(message);
83 | ```
84 |
--------------------------------------------------------------------------------
/docs/TransactionalSend.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 13
3 | ---
4 |
5 | # Sending messages transactionally
6 |
7 | The RockLib.Messaging library defines an interface for sending messages tranactionally through the `ITransactionalSender` interface. Sending messages transactionally in this case means that one or more messages, all destined for the same sender, are added to a transaction, then the transaction is committed. The `ITransactionalSender` interface inherits from `ISender`, so it has the same functionality in addition to its `BeginTransaction` method.
8 |
9 | ```csharp
10 | ITransactionalSender transactionalSender = // TODO: Instantiate.
11 |
12 | ISenderTransaction transaction = transactionalSender.BeginTransaction();
13 |
14 | transaction.Add(new SenderMessage("First message"));
15 | transaction.Add(new SenderMessage("Second message"));
16 | transaction.Add(new SenderMessage("Third message"));
17 |
18 | bool rollback = false;
19 |
20 | // Do stuff that may or may not succeed. If it does not, set 'rollback' to true.
21 |
22 | if (!rollback)
23 | transaction.Commit();
24 | else
25 | transaction.Rollback();
26 | ```
27 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RockLib/RockLib.Messaging/f50493a1d94c301cf6876b0337d0126491799e22/icon.png
--------------------------------------------------------------------------------