Skip to content

Map Wolverine TenantId from incoming MassTransit messages#3167

Closed
outofrange-consulting wants to merge 2 commits into
JasperFx:mainfrom
outofrange-consulting:feat/masstransit-map-tenant-id
Closed

Map Wolverine TenantId from incoming MassTransit messages#3167
outofrange-consulting wants to merge 2 commits into
JasperFx:mainfrom
outofrange-consulting:feat/masstransit-map-tenant-id

Conversation

@outofrange-consulting

Copy link
Copy Markdown
Contributor

What

Adds a MapTenantIdFrom<T> hook to the MassTransit interop so applications consuming MassTransit messages can populate Wolverine's Envelope.TenantId from each incoming message:

opts.ListenToRabbitQueue("orders")
    .UseMassTransitInterop(mt =>
    {
        // from the strongly-typed message body...
        mt.MapTenantIdFrom<OrderPlaced>(env => env.Message?.TenantId);

        // ...or from MassTransit envelope metadata (headers, addresses, ids)
        mt.MapTenantIdFrom<OrderShipped>(env =>
            env.Headers.TryGetValue("tenant-id", out var v) ? v?.ToString() : null);
    });

The lambda receives a new public read-only view, IMassTransitEnvelope<T>, exposing the deserialized Message alongside the surrounding MassTransit metadata. The concrete MassTransitEnvelope<T> stays internal, so the mutable serialization DTO is not leaked onto the public surface.

Behavior:

  • Multiple message types compose — each mapper only fires for its own T (re-registering the same T replaces the prior mapping).
  • A null/empty result never overwrites an existing TenantId.
  • Inbound (deserialization) path only.

Drive-by fix

While wiring this up I found that UseMassTransitInterop(configure) dropped the configure lambda on the Rabbit MQ and Amazon SQS listeners: Rabbit MQ called InteropWithMassTransit() with no argument, and the SQS MassTransitMapper never received it. As a result any serializer customization — including the existing UseSystemTextJsonForSerialization, and now MapTenantIdFrom — silently no-opped on those transports while only Azure Service Bus honored it. This is threaded through on both, in its own commit so it can be reviewed (or split) independently.

Tests (no broker required)

  • MassTransitTenantMappingTests (CoreTests) — body mapping, header mapping, unregistered-type pass-through, empty-no-overwrite, multi-type dispatch, full-chain fall-through.
  • masstransit_interop_map_tenant_id (RabbitMQ.Tests) — drives the full deserialization pipeline with a real MassTransit wire payload through TryDeserializeEnvelope, proving the configure lambda reaches the listener. Uses stubbed transports.
  • masstransit_mapper_tenant_mapping (AmazonSqs.Tests) — confirms the SQS mapper applies the configure lambda.

Docs updated under docs/guide/messaging/transports/rabbitmq/interoperability.md. dotnet build wolverine.slnx -c Release is clean locally.

Geoffrey MARC added 2 commits June 19, 2026 15:54
…essages

Add IMassTransitInterop.MapTenantIdFrom<T>(env => ...) so applications consuming
MassTransit messages can derive Wolverine's Envelope.TenantId from either the
message body or the MassTransit envelope metadata (headers, addresses, ids).

The lambda receives a new public read-only view, IMassTransitEnvelope<T>, exposing
the deserialized Message alongside the MassTransit metadata. The concrete
MassTransitEnvelope<T> stays internal so the mutable serialization DTO is not
leaked. Multiple message types compose; each mapper only fires for its own T and a
null/empty result never clobbers an existing tenant id. Inbound (read) path only.
…nd SQS listeners

UseMassTransitInterop(configure) dropped the configure lambda on the Rabbit MQ and
Amazon SQS listeners: Rabbit MQ called InteropWithMassTransit() with no argument,
and the SQS MassTransitMapper never received it. As a result any serializer
customization (UseSystemTextJsonForSerialization, and now MapTenantIdFrom) silently
no-opped on those transports while only Azure Service Bus honored it.

Thread the configure action through to the MassTransit serializer on both. Covered
by no-broker tests driving the Rabbit MQ deserialization pipeline end to end and the
SQS mapper construction.
@jeremydmiller

Copy link
Copy Markdown
Member

Thanks @outofrange-consulting! Superseded by #3192, which retains your original commits (and authorship) and adds a small public-API rework on top: rather than introducing a new IMassTransitEnvelope<T> interface, it exposes the concrete MassTransitEnvelope/MassTransitEnvelope<T> type publicly and removes the interface, so MapTenantIdFrom<T> takes Func<MassTransitEnvelope<T>, string?>.

Closing in favor of #3192 — please follow along there. 🙏

jeremydmiller added a commit that referenced this pull request Jun 22, 2026
…-3167

Map Wolverine TenantId from incoming MassTransit messages (supersedes #3167)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants