ReBus: Unable to use sagas with handlers decorator - decorator

I use Rebus 3.0.1 with Simpleinjector. I have registered a decorator, for logging, of all the handlers, this way:
container.RegisterDecorator(typeof(IHandleMessages<>), typeof(HandlerLogDecorator<>));
Everything works fine, except the Sagas: because, in LoadSagaDataStep, the following code
var handlerInvokersForSagas = context.Load<HandlerInvokers>()
.Where(l => l.HasSaga)
.ToList();
is not able to find the Saga under the covers.
If I unregister the decorators, saga starts working again.
Any suggestion to conciliate sagas and handlers decorators?

Unfortunately, as you have discovered, Rebus does not expect handlers (including sagas) to be wrapped in decorators.
It uses decorators a lot for various things internally, and it encourages the use of decorators as an extension point for developers, but that only goes for all of Rebus' services like IPipeline, ISubscriptionStorage, etc.
If you want to log stuff in relation to message handling, a better extension point would be to either
a) use Rebus.Events and simply install an event handler like this:
Configure.With(...)
.(...)
.Events(e => {
e.AfterMessageHandled += (bus, headers, message, context, args) => {
// log stuff in here :)
};
})
.Start();
or
b) create an incoming pipeline step that logs what you want to log like this:
Configure.With(...)
.(...)
.Options(o => {
o.Decorate<IPipeline>(c => {
var step = new YourLoggingStep();
var pipeline = c.Get<IPipeline>();
return new PipelineStepInjector(pipeline)
.OnReceive(step, PipelineRelativePosition.After, typeof(DeserializeIncomingMessageStep));
});
})
.Start();
probably wrapping all of the ugly stuff in an extension method that makes usage look more like this:
Configure.With(...)
.(...)
.Options(o => {
o.LogHandledMessages();
})
.Start();

Related

Ways to test if my app is making the correct amount of api calls

I'm testing a react app with cypress, and cypress is really good at checking if atleast n many are calls are made on load, and not so good at checking if only those calls were made. The app itself is a pretty standard react app that uses window.fetch for api calls. Is there another service, like cypress something similar, that's got the ability to test that in an automated fashion?
For instance, if you visit the site logged in we have three config calls. If a developer commits code that accidentally calls one of those twice, ideally I'd like an automated test to catch that.
I don't want to do it in Cypress because it seems like that's not what the product is really intended to do unless you hack it with an undocumented solution.
Seems like you need a hard wait to ensure you check after all calls have been issued.
Also recommend stubbing so that there's no latency waiting for server response.
it('check for excess calls', () => {
cy.intercept('https://jsonplaceholder.typicode.com/todos/1', {}).as('fetch')
cy.visit('/').then(() => {
// wait for expected calls
cy.wait('#fetch')
cy.wait('#fetch')
cy.wait('#fetch')
cy.wait(500)
cy.get('#fetch.all') // check the call count
.should(calls => {
expect(calls).to.have.length(3) // no extra calls
})
})
})
Tested with this app HTML
<script>
fetch('https://jsonplaceholder.typicode.com/todos/1')
fetch('https://jsonplaceholder.typicode.com/todos/1')
fetch('https://jsonplaceholder.typicode.com/todos/1')
</script>
You may want to burn-test to get the right wait time, see Burning Tests with cypress-grep
Alternatively you can wait four times and catch the error
it('check for excess calls', () => {
cy.intercept('https://jsonplaceholder.typicode.com/todos/1', {}).as('fetch')
cy.visit('html/call-limit.html').then(() => {
cy.wait('#fetch')
cy.wait('#fetch')
cy.wait('#fetch')
Cypress.once('fail', (error) => {
// catch ignore failure on 4th cy.wait('#fetch')
if (error.message.includes('No request ever occurred')) {
return
}
throw error // rethrow to fail for another reason
})
cy.wait('#fetch') // should fail and trigger listener above
cy.get('#fetch.all')
.should(calls => {
expect(calls).to.have.length(3)
})
})
})

Rebus RabbitMQ IHandleMessage Not working

I have a .NET Core service publishing events to Rebus with RMQ Transport with the following configuration:
services.AddRebus(configure => configure
.Logging(x => x.Serilog())
.Transport(x => x.UseRabbitMq(rabbitMqConnection, "ServiceA"))
.Routing(x => x.TypeBased()));
When I run it, it appears to publish the event to the RebusTopics exchange. So then service B has config like this:
services.AutoRegisterHandlersFromAssemblyOf<MyHandler1>();
services.AddRebus(configure => configure
.Logging(x => x.Serilog() )
.Transport(x => x.UseRabbitMq(rabbitMqConnection, "ServiceB"))
.Routing(x => x.TypeBased()));
and a handler:
public class MyHandler1: IHandleMessages<ServiceAEvent>
{
public CreateMinisiteWhenPageIsCreated(){}
public Task Handle(PageCreated message)
{
//do stuff..
return Task.CompletedTask;
}
There appears to be a binding to the RebusDirect exchange to a new ServiceB queue, but when I publish an event from ServiceA, the handler never fires in ServiceB... there is no binding on the RebusTopics exchange for that message type also.
Im going crazy wondering why, its quite similar in syntax to NServiceBus so very confused as to why its not working.
just add app.ApplicationServices.UseRebus(); in the Consumer's Starup.cs no need to subscribe also. ProducerApp can bus.Send() instead of bus.Publish();
It sounds to me like your subscriber needs to
await bus.Subscribe<ServiceAEvent>();
If the bus instance with the input queue named ServiceB makes the call above, a binding will be created from a topic, whose name is derived from the ServiceAEvent type, to the bus' input queue.
After that is done, it will receive the event whenever another bus instance calls
await bus.Publish(new ServiceAEvent(...));

Moving Error message from error queue back to original queue with Rebus

Is it possible to move error message from error queue to its original queue, programmatically or via UI?
Update
Questions below on the code below:
1 Does the code below apply to Publiser or Subscriber or both?
The code below:
Configure.With(activator)
.Transport(t => (...)) //< use queue "error" here
.Routing(r =>
{
r.AddTransportMessageForwarder(async transportMessage =>
{
var sourceQueue = transportMessage.Headers.TryGetValue(Headers.SourceQueue, out var result)
? result
: throw new ArgumentException($"Could not find '{Headers.SourceQueue}' header");
return ForwardAction.ForwardTo(sourceQueue);
});
})
.Start();
2 Transport method below works for my code. However, the code above suggests using error queue name, will it work?
Where are the Publiser and Subscriber queue name like below specified if the code above is used?
Please provide code for pub sub pattern.
Publisher:
.Transport(t => t.UseAzureServiceBus(Consts.ServiceBusConnectionString, Consts.Publisher))
Subscriber:
.Transport(t=>t.UseAzureServiceBus(Consts.ServiceBusConnectionString, Consts.Subscriber1))
https://github.com/rebus-org/Rebus/wiki/Transport-message-forwarding
Since Rebus uses ordinary queues as its dead-letter queues, it's quite easy to start a bus instance with error as the input queue – then you can e.g. use Rebus' built-in transport message forwarding capability to do that you want to the messages – e.g. forward them to their source queues:
Configure.With(activator)
.Transport(t => (...)) //< use queue "error" here
.Routing(r =>
{
r.AddTransportMessageForwarder(async transportMessage =>
{
var sourceQueue = transportMessage.Headers.TryGetValue(Headers.SourceQueue, out var result)
? result
: throw new ArgumentException($"Could not find '{Headers.SourceQueue}' header");
return ForwardAction.ForwardTo(sourceQueue);
});
})
.Start();
or whatever you want in there.
There also exists a UI, Fleet Manager, that can do this – it replaces the need for dead-letter queues entirely, as it stores failed messages in its database and makes it possible to return the failed messages to their source queues (or another queue, if that's what you want), but it's only available if you're a Rebus Pro subscriber.
Update (with answers to the questions in your update):
1) AddTransportMessageForwarder is only relevant for an endpoint that receives messages.
2) It's the "queue name" specified as an argument to the .Useblablabla method. For example, with Azure Service Bus it would read
.Transport(t => t.UseAzureServiceBus(Consts.ServiceBusConnectionString, "error"))

Await the asynchronous dispatch of an action in redux-observable

I have an epic which dispatches an action that is handled by a redux middleware, which returns a promise once the action is dispatched. The epic looks approximately like this:
const myEpic = action$ =>
action$.pipe(
ofType(SAVE_ACTION),
switchMap(action => [
saveData(action.payload)
])
)
When the SAVE_ACTION is dispatched, it is picked up by the epic, which dispatches an action created by the saveAction action creator.
This resulting action is intercepted by a redux middleware (specifically redux-axios-middleware) which does an HTTP request and converts the result of dispatching such action to a promise, which has the standard behavior of resolving when the HTTP request succeeds and rejecting when the HTTP request fails.
What I would need to do is do additional work once that promise is resolved, within this epic, because I need data contained both in the payload of the initial SAVE_ACTION action and the payload of the HTTP response.
Because the middleware also dispatches actions upon completion of the underlying HTTP request, it would be easy to write an additional epic to do the additional work I need to do in a distinct epic, but I wouldn't have access to the payload of the initial SAVE_ACTION anymore, so I'm trying to figure out if this can be handled all within a single epic, and I haven't found a way so far. I have found online posts like this one, which is very informative, but still doesn't address the issue of awaiting the dispatch of an action, rather than a plain observable.
One approach would be to use merge like this:
import { merge } from 'rxjs/observable/merge';
const myEpic = action$ => {
let initialPayload = null;
return merge(
action$.pipe(
ofType(SAVE_ACTION),
switchMap(action => {
initialPayload = action.payload;
return [saveData(action.payload)]
})
),
action$.pipe(
ofType(SAVE_ACTION_SUCCESS),
mergeMap(action => {
// Use initialPayload here.
})
),
)
}

How to implement a simple reply in rebus

public static void SendREsbDx(Job job)
{
using (var adapter = new BuiltinContainerAdapter())
{
adapter.Handle<ReplyMsg>(msg =>
{
string mss = msg.message;
});
Configure.With(adapter)
.Logging(l => l.ColoredConsole(LogLevel.Warn))
.MessageOwnership(o => o.FromRebusConfigurationSection())
.Transport(t => t.UseSqlServer("server=.;initial catalog=rebus_test;integrated security=true","consumerx","error")
.EnsureTableIsCreated())
.CreateBus()
.Start();
adapter.Bus.Send<Job>(job);
}
}
I am using the above code to send a message to a consumer. The consumer will use the bus.Reply, but the above code obviously does not work.
I simply want to be able to receive a reply from the consumer. How would this be accomplished?
Sounds like your consumer does not have a handler for Job messages.
In your case, it sounds like you'll need two bus instances - a consumer instance that has an implementation of IHandleMessages<Job> which will bus.Reply(new ReplyMsg {...}), and a producer instance that has an implementation of IHandleMessages<ReplyMsg> which will bus.Send(new Job{...}) and do whatever needs to be done in the reply handler.
If you're interested in looking at some sample code that demonstrates request/reply, take a look at the integration sample in the Rebus samples repository which has some simple request/reply going on between the Client (which would correspond to the producer in your case) and the IntegrationService (which corresponds to the consumer).
The following code snippet demonstrates how it can be done:
var producer = new BuiltinContainerAdapter();
var consumer = new BuiltinContainerAdapter();
consumer.Handle<Job>(job => {
...
consumer.Bus.Reply(new ReplyMsg {...});
});
producer.Handle<ReplyMsg>(reply => {
....
});
Configure.With(producer)
.Transport(t => t.UseSqlServer(connectionString, "producer.input", "error")
.EnsureTableIsCreated())
.MessageOwnership(o => o.FromRebusConfigurationSection())
.CreateBus()
.Start();
Configure.With(consumer)
.Transport(t => t.UseSqlServer(connectionString, "consumer.input", "error")
.EnsureTableIsCreated())
.MessageOwnership(o => o.FromRebusConfigurationSection())
.CreateBus()
.Start();
// for the duration of the lifetime of your application
producer.Bus.Send(new Job {...});
// when your application shuts down:
consumer.Dispose();
producer.Dispose();
and in your app.config there must be an endpoint mapping that maps Job to consumer.input:
<rebus>
<endpoints>
<add messages="SomeNamespace.Job, SomeAssembly" endpoint="consumer.input"/>
</endpoints>
</rebus>
I hope you can see now why your code does not work. Please let me know if I should elaborate further :)
I've added a request/reply sample to the Rebus samples repository to serve as proof that the code shown above can actually run (provided that you remove the .... etc of course - you need a basic understanding of C# to be able to use this code)

Resources