NestJs/ClientProxy: Manual listen to events without decorators - rabbitmq

Is there a manual way to subscribe to a event message pattern without using decorators on a function?
In a service, I send a message to a different microservice.
The call return nothing just void but asynchronously triggers an event, which i want to listen to in the service right afterwards
Example given, would maybe work like this
// An event will be triggered later, after first value
await lastValueFrom(
this.rabbitmqClient.send(RmqPatterns.DO_STUFF, payload),
);
// Now listening for that async event
this.rabbitmqClient.listen(RmqPatterns.DID_STUFF, async msg => {
console.log(`Received message: ${msg.content.toString()}`);
});

Related

What is the best way for exiting a loop inside an actor?

A rendering actor is drawing a large amount of shapes onto a bitmap. I'd like to stop this process from a supervisor actor
object. Neither Kill, nor Stop do the job as they wait for the message being handled to finish. To Ask an actor from an actor seems to be advised against. What is the right approach in this situation?
When using an async method, that should be cancelable, you'd normally use a CancellationToken. If you already received an cancellation token into your method and you're going to call another async method that should be cancelled by another constraint you can setup your own CancellationTokenSource, merge the incoming token into it and forward the new token to the inner function.
If you then need to cancel the inner function, you call the .Cancel() method of your token source and your inner function must be polite enough to regulary check the state of the incoming token.
A rough sketch of this approach would be something like this:
public async Task DoSomething(CancellationToken cancellationToken)
{
var tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
var token = tokenSource.Token;
var task = WorkHard(token);
task.Start();
await Task.Delay(1000);
tokenSource.Cancel();
}
private async Task WorkHard(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
Console.WriteLine("Make some hard work");
await Task.Delay(100);
}
}
A more elaborated example can be found at https://learn.microsoft.com/en-us/dotnet/standard/threading/how-to-listen-for-multiple-cancellation-requests.

How to postpone Observable subscription until certain event has been triggered and skip that check on subsequent subscriptions

I have an Observable<Item>.
The task is that this observable should emit values only after external event notifies it that it is allowed to (let's say some data should be validated prior to proceeding with emitting Item's). So each subscriber should wait to that event before receiving Item's. But after event has been delivered all subsequent subscriptions should not wait and receive Item's right away ( so publish/connect doesn't work in this case). This also means that itemSubject should not be even subscribed to prior the event.
My solution so far look like so :
var itemSubject = Observable<Item> ....
var triggerSubject = PublishSubject.create<Item>()
...
fun observeItem() : Observable<Item> {
return triggerSubject.concatWith(itemSubject)
}
...
fun notifyTrigger() {
triggerSubject.onComplete()
}
Is there any idiomatic way of doing this in Rx?

Is there any way to catch an event fired within another contract which is called by low level 'call' opcode from main contract in solidity

I have a Multisig contract that when reaches the minimum quorum, it can execute a low level call Transaction which may be held on another contract.
function _execute(Transaction storage transaction) internal {
//some code
// solhint-disable-next-line
(bool success, ) = transaction.target.call{value: transaction.value}(callData); // FIRES AN EVENT IN OTHER CONTRACT
if (success) {
emit TransactionExecuted( // FIRES SECOND
//some code
);
} else {
emit TransactionFailed(
//some code
);
//some code
}
}
My execute function fires an event after execution of the Transaction (call) whether it was successful or not, in the meanwhile if call function request has an event to fire, I can catch the event fired by the contract, but event parameters are not there, The second contract which is called by _execute() is written as follows:
function _addMember(
address memberAddress,
bytes32 memberName,
Membership _membership
)
internal
{
//some code
// Fire an event
emit MembershipChanged(memberAddress, true, _membership); // FIRES FIRST
}
The following is the test written in typescript, I can get the event fired on called contract, but there is no data in it
it("should contain two events from previous transaction, adding a new core member and running a Transaction by multisig", async () => {
//r is the receipt of the caller (multisig) contract
expect(r.events!.length).to.be.eq(2); // MembershipChanged, TransactionExecuted
//NOTE: r.events![0].address === memberReg.address // memReg is the callee contract
/*THE FOLLOWING DOESN'T CONTAIN EVENT DATA NOR TOPICS OF memReg CONTRACT*/
expect(r.events![0].event).to.be.eq("MembershipChanged"); //faild
expect(r.events![0].args!.member).to.be.eq(coreCandidateAddr) //faild
expect(r.events![0].args!.isMember).to.be.true; //fails
expect(r.events![0].args!.membership).to.be.eq(Membership.Core); //faild
/* THE FOLLOWING WORKS WELL */
expect(r.events![1].event).to.be.eq("TransactionExecuted"); //passed
//some code
})
I guess it would be possible to catch those events in production easily by listening to that deployed contract, but I don't know how to do this in test environment
Ok, Thanks to #bbbbbbbbb here is the simple solution, it should listen on before the transaction call
memberReg.once("MembershipChanged", (member, isMember, membership, event) => {
expect(event.event).to.be.eq("MembershipChanged"); //passed
expect(member).to.be.eq(coreCandidateAddr); //passed
expect(isMember).to.be.true; //passed
expect(membership).to.be.eq(Membership.Core); //passed
});
r = await awaitTx(multisig.execute(id)); // execution happens here
Your functions _execute and _addMember are marked as internal, it should be mark as external or public to be called in testing environment, or create a public function that call it.
If you are using hardhat the easy way is using the testing pattern for events suggested by hardhat documentation, this also works for low level calls.
const tx = await YourContract.execute(foo);
//Check that the event fired by a low level call is correct
//withArgs receive the expected values for the event
expect(tx).to.emit(YourContract, "MembershipChanged").withArgs(foo, bar...);
For more info check the implementation for hardhat docs: https://hardhat.org/tutorial/testing-contracts#full-coverage

Twilio Studio/Functions and Conference calls

I'm using the Studio flow to manage authenticating an end user calling into our number, and assuming they pass authentication, they then get added to a conference call - however so that I can set the various parameters for starting the conference call, I'm trying to initiate the join conference function in a Functions called from Studio.
So example:
End user if confirmed, and the next step in the Studio flow calls a Function called "Start Call". A variable passed to the start call function includes the Conference Name.
exports.handler = function(context, event, callback) {
console.log('Function - /startCall');
const conference_id = event.conference_id;
let twiml = new Twilio.twiml.VoiceResponse();
twiml.say('Please wait while we dial you into the call.');
twiml.dial().conference(conference_id);
console.log('TWIML',twiml);
return callback(null, twiml);
};
This then returns back to the Studio Flow, so as a test, my next part is to dial a 3rd party into the same conference call - so another request from the flow to a function called conferenceOperator:
exports.handler = function (context, event, callback) {
console.log('Function - /conferenceOperator');
const conference_id = event.conference_id;
console.log('CONFERENCE',conference_id );
const twilioClient = context.getTwilioClient();
console.log(twilioClient.studio);
twilioClient.studio.flows('FWxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx').executions.create({
to: '+44xxxxxxxxxxxx',
from: '+44xxxxxxxxxx',
parameters: JSON.stringify({
call_id: conference_id
})
})
.then(function(execution) {
console.log(execution.sid);
callback(null, execution.sid);
})
.catch(error => {
console.error(`problem with request: ${error.message}`);
callback(error.message);
});
The number is dialed, and is put on hold waiting for the conference to start. However the moment this flow starts, the original inbound call is dropped. Looking at the logs for the Studio flow, it shows as still executing.
So questions:
why does the inbound call drop?
am I handling transferring the inbound call across to the conference via the function correctly?
what gotchas have I missed?
Thanks
I have now resolved this - there was an odd error in another part of the flow, which was returning an http-error 500 - this caused the whole flow to fail, and thus end the call without reporting back!

Is there an easy way to subscribe to the default error queue in EasyNetQ?

In my test application I can see messages that were processed with an exception being automatically inserted into the default EasyNetQ_Default_Error_Queue, which is great. I can then successfully dump or requeue these messages using the Hosepipe, which also works fine, but requires dropping down to the command line and calling against both Hosepipe and the RabbitMQ API to purge the queue of retried messages.
So I'm thinking the easiest approach for my application is to simply subscribe to the error queue, so I can re-process them using the same infrastructure. But in EastNetQ, the error queue seems to be special. We need to subscribe using a proper type and routing ID, so I'm not sure what these values should be for the error queue:
bus.Subscribe<WhatShouldThisBe>("and-this", ReprocessErrorMessage);
Can I use the simple API to subscribe to the error queue, or do I need to dig into the advanced API?
If the type of my original message was TestMessage, then I'd like to be able to do something like this:
bus.Subscribe<ErrorMessage<TestMessage>>("???", ReprocessErrorMessage);
where ErrorMessage is a class provided by EasyNetQ to wrap all errors. Is this possible?
You can't use the simple API to subscribe to the error queue because it doesn't follow EasyNetQ queue type naming conventions - maybe that's something that should be fixed ;)
But the Advanced API works fine. You won't get the original message back, but it's easy to get the JSON representation which you could de-serialize yourself quite easily (using Newtonsoft.JSON). Here's an example of what your subscription code should look like:
[Test]
[Explicit("Requires a RabbitMQ server on localhost")]
public void Should_be_able_to_subscribe_to_error_messages()
{
var errorQueueName = new Conventions().ErrorQueueNamingConvention();
var queue = Queue.DeclareDurable(errorQueueName);
var autoResetEvent = new AutoResetEvent(false);
bus.Advanced.Subscribe<SystemMessages.Error>(queue, (message, info) =>
{
var error = message.Body;
Console.Out.WriteLine("error.DateTime = {0}", error.DateTime);
Console.Out.WriteLine("error.Exception = {0}", error.Exception);
Console.Out.WriteLine("error.Message = {0}", error.Message);
Console.Out.WriteLine("error.RoutingKey = {0}", error.RoutingKey);
autoResetEvent.Set();
return Task.Factory.StartNew(() => { });
});
autoResetEvent.WaitOne(1000);
}
I had to fix a small bug in the error message writing code in EasyNetQ before this worked, so please get a version >= 0.9.2.73 before trying it out. You can see the code example here
Code that works:
(I took a guess)
The screwyness with the 'foo' is because if I just pass that function HandleErrorMessage2 into the Consume call, it can't figure out that it returns a void and not a Task, so can't figure out which overload to use. (VS 2012)
Assigning to a var makes it happy.
You will want to catch the return value of the call to be able to unsubscribe by disposing the object.
Also note that Someone used a System Object name (Queue) instead of making it a EasyNetQueue or something, so you have to add the using clarification for the compiler, or fully specify it.
using Queue = EasyNetQ.Topology.Queue;
private const string QueueName = "EasyNetQ_Default_Error_Queue";
public static void Should_be_able_to_subscribe_to_error_messages(IBus bus)
{
Action <IMessage<Error>, MessageReceivedInfo> foo = HandleErrorMessage2;
IQueue queue = new Queue(QueueName,false);
bus.Advanced.Consume<Error>(queue, foo);
}
private static void HandleErrorMessage2(IMessage<Error> msg, MessageReceivedInfo info)
{
}