NServiceBus Full duplex bus callback with Web API - asp.net-web-api

Project Source: on github
Client is ASP.net Web API with OWIN and IIS host. Server is console.
I am having a scenario in which current thread has to wait till NSB completes its function. But my thread is waiting indefinitely from NSB reply.
var synchronousHandle = _bus.Send<MyCommand>(m => { m.TokenId = tokenId; })
.Register(r =>
{
var completionResult = r.AsyncState as CompletionResult;
if (completionResult == null || completionResult.Messages.Length <= 0) return;
// Always expecting one IMessage as reply
var response = completionResult.Messages[0];
}, null);
synchronousHandle.AsyncWaitHandle.WaitOne();
In my Handler:
Bus.Reply(new GenericResponseMessage { IsSuccess = true });
Problem:
Code run without any error and client message queue receives response from Bus.Reply. But callback does not receives response.

Will this doco help?
In your code it is commented out (https://github.com/sanelib/WebStarterKit/blob/da796e69826072ae78443c7952cf2669d74a9ee7/DotNetServer/src/NSBus.Server/CommandHandlers/UserGrantAccessHandler.cs#L70)
Another option is to use signalR (would be a more reliable option IMHO) Take a look at Roy's blog post

Related

How to wait for a successful response in Cypress tests

Background
I use 3 back-end servers to provide fault tolerance for one of my online SaaS application. All important API calls, such as getting user data, contact all 3 servers and use value of first successfully resolved response, if any.
export function getSuccessValueOrThrow$<T>(
observables$: Observable<T>[],
tryUntilMillies = 30000,
): Observable<T> {
return race(
...observables$.map(observable$ => {
return observable$.pipe(
timeout(tryUntilMillies),
catchError(err => {
return of(err).pipe(delay(5000), mergeMap(_err => throwError(_err)));
}),
);
})
);
}
getSuccessValueOrThrow$ get called as following:
const shuffledApiDomainList = ['server1-domain', 'server2-domain', 'server3-domain';
const sessionInfo = await RequestUtils.getSuccessValueOrThrow(
...(shuffledApiDomainList.map(shuffledDomain => this.http.get<SessionDetails>(`${shuffledDomain}/file/converter/comm/session/info`))),
).toPromise();
Note: if one request resolve faster than others, usually the case, race rxjs function will cancel the other two requests. On Chrome dev network tab it will look like bellow where first request sent out was cancelled due to being too slow.
Question:
I use /file/converter/comm/session/info (lets call it Endpoint 1) to get some data related to a user. This request dispatched to all 3 back-end servers. If one resolve, then remaining 2 request will be cancelled, i.e. they will return null.
On my Cypress E2E test I have
cy.route('GET', '/file/converter/comm/session/info').as('getSessionInfo');
cy.visit('https://www.ps2pdf.com/compress-mp4');
cy.wait('#getSessionInfo').its('status').should('eq', 200)
This sometimes fails if the since getSessionInfo alias was hooked on to a request that ultimately get cancelled by getSuccessValueOrThrow$ because it wasn't the request that succeeded.Bellow image shows how 1 out of 3 request aliased with getSessionInfo succeeded but the test failed since the first request failed.
In Cypress, how do I wait for a successful i.e. status = 200 request?
Approach 1
Use .should() callback and repeat the cy.wait call if status was not 200:
function waitFor200(routeAlias, retries = 2) {
cy.wait(routeAlias).then(xhr => {
if (xhr.status === 200) return // OK
else if (retries > 0) waitFor200(routeAlias, retries - 1); // wait for the next response
else throw "All requests returned non-200 response";
})
}
// Usage example.
// Note that no assertions are chained here,
// the check has been performed inside this function already.
waitFor200('#getSessionInfo');
// Proceed with your test
cy.get('button').click(); // ...
Approach 2
Revise what it is that you want to test in the first place.
Chances are - there is something on the page that tells the user about a successful operation. E.g. show/hide a spinner or a progress bar, or just that the page content is updated to show new data fetched from the backend.
So in this approach you would remove cy.wait() altogether, and focus on what the user sees on the page - do some assertions on the actual page content.
cy.wait() yields an object containing the HTTP request and response properties of the XHR. The error you're getting is because you're looking for property status in the XHR object, but it is a property of the Response Object. You first have to get to the Response Object:
cy.wait('#getSessionInfo').should(xhr => {
expect(xhr.response).to.have.property('status', 200);
});
Edit: Since our backend uses graphql, all calls use the single /graphql endpoint. So I had to come up with a solution to differentiate each call.
I did that by using the onResponse() method of cy.route() and accumulating the data in Cypress environment object:
cy.route({
method: 'GET',
url: '/file/converter/comm/session/info',
onResponse(xhr) {
if (xhr.status === 200) {
Cypress.env('sessionInfo200') = xhr;
}
}
})
You can then use it like this:
cy.wrap(Cypress.env()).should('have.property', 'sessionInfo200');
I wait like this:
const isOk = cy.wait("#getSessionInfo").then((xhr) => {
return (xhr.status === 200);
});

Twilio awaits response, I don't want server to respond

I am using a Slack webhook to process incoming SMS messages from Twilio. However, the way I have it set up, It seems that Twilio is expecting the web server (slack) to respond to it. This causes errors to be generated in Twilio, and I obviously don't want errors because I'll be getting emails.
I am using the twilio-ruby gem in Ruby to send out the SMS messages, and using the slack-ruby-client to monitor incoming messages from Slack.
How do I stop Twilio from trying to expect a response from the web server when it POSTS to the Slack webhook? Is that even possible or do I have this all configured incorrectly?
EDIT
Here's the function that I have which sends the forwarded SMS to Slack:
const https = require("https");
// Make sure to declare SLACK_WEBHOOK_PATH in your Environment
// variables at
// https://www.twilio.com/console/runtime/functions/configure
exports.handler = (context, event, callback) => {
// Extract the bits of the message we want
const { To, From, Body } = event;
// Construct a payload for slack's incoming webhooks
const slackBody = JSON.stringify({
text: `!asi SMS\nFrom: ${From}\nMessage: ${Body}`
});
// Form our request specification
const options = {
host: "hooks.slack.com",
port: 443,
path: context.SLACK_WEBHOOK_PATH,
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": slackBody.length
}
};
// send the request
const post = https.request(options, res => {
// only respond once we're done, or Twilio's functions
// may kill our execution before we finish.
res.on("end", () => {
// respond with an empty message
callback(null, new Twilio.twiml.MessagingResponse());
});
});
post.write(slackBody);
post.end();
};
Twilio developer evangelist here.
Twilio is always going to expect at least a 200 response or will timeout at 15 seconds for incoming message webhooks.
You could avoid the error messages by using something in between Twilio and Slack, like Zapier (example in this blog post) or using a Twilio Function (as described here) or with Twilio Studio (from the documentation here).
Hope one of those ideas helps!
Update
Further to my earlier answer, and given the code you used to make the call, I have an update.
When making a request using Node's built in https module you will not get the end event until you have read the data. This is what is causing the timeout between Twilio and the Twilio Function, you are never responding to it because you don't consume the data from the request.
In a quick test I found that just listening for the data event meant that the end event did fire. So update your function to:
const post = https.request(options, res => {
// only respond once we're done, or Twilio's functions
// may kill our execution before we finish.
res.on("data", () => {});
res.on("end", () => {
// respond with an empty message
callback(null, new Twilio.twiml.MessagingResponse());
});
});
And it should work.

How to wait for WebSocket STOMP messages in Cypress.io

In one of my tests I want to wait for WebSocket STOMP messages. Is this possible with Cypress.io?
If the websocket you'd like to access is being established by your application, you could follow this basic process:
Obtain a reference to the WebSocket instance from inside your test.
Attach an event listener to the WebSocket.
Return a Cypress Promise that is resolved when your WebSocket receives the message.
This is a bit difficult for me to test out, absent a working application, but something like this should work:
In your application code:
// assuming you're using stomp-websocket: https://github.com/jmesnil/stomp-websocket
const Stomp = require('stompjs');
// bunch of app code here...
const client = Stomp.client(url);
if (window.Cypress) {
// running inside of a Cypress test, so expose this websocket globally
// so that the tests can access it
window.stompClient = client
}
In your Cypress test code:
cy.window() // yields Window of application under test
.its('stompClient') // will automatically retry until `window.stompClient` exists
.then(stompClient => {
// Cypress will wait for this Promise to resolve before continuing
return new Cypress.Promise(resolve => {
const onReceive = () => {
subscription.unsubscribe() // clean up our subscription
resolve() // resolve so Cypress continues
}
// create a new subscription on the stompClient
const subscription = stompClient.subscribe("/something/you're/waiting/for", onReceive)
})
})

MassTransit MessageRequestClient timeout issue

We have two services which exchange messages via MassTransit on top of RabbitMQ.
The goal is to send a message in a request/response way. Here's the code of the service which listens for a message, let's call it Service1:
Bus.Factory.CreateUsingRabbitMq(
sbc =>
{
var host = sbc.Host(new Uri($"rabbitmq://{RabbitMqHost}"), h =>
{
h.ConfigureRabbitMq();//Custom extension to specify credentials
});
sbc.ReceiveEndpoint(host, "CUSTOM_QUEUE_NAME", ep =>
{
ep.Exclusive = false;
ep.AutoDelete = true;
ep.Durable = false;
ep.PrefetchCount = 1;
ep.Handler<EngineStartingMessage>(async context =>
{
//SourceAddress and ResponseAddress are auto generated queues
//Message processing is done here
context.Respond(response);
});
});
});
The code of the service which sends a message and process the result, let's call it Service2:
var requestClient =
new MessageRequestClient<EngineStartingMessage, EngineStartingResponse>(
EntityServiceBus,
new Uri("CUSTOM_QUEUE_NAME?durable=false&autodelete=true&exclusive=false"),
new TimeSpan(0, 0, 30));
var engineStartResponse = requestClient.Request(new EngineStartingMessage() { Version = SystemVersion }).Result;
When I run the above code I can see Service1 gets a request and calls context.Respond(response); but on the Service2 side I always get a Timeout exception. Since, a message can make it from Service2 to Service1 I assume there are no network related issues. The timeout is pretty high as well. The message processing on Service1 end takes less than a second. So I think a response message is just not routed properly and I don't understand why. What is suspicious to me is that SourceAddress and ResponseAddress contain auto generated values and not "CUSTOM_QUEUE_NAME?durable=false&autodelete=true&exclusive=false". Any ideas what I'm doing wrong?
You should start Bus, on your service start, like it shown here
await busControl.StartAsync(source.Token);

Service worker causing CORS issues on Firefox

I'm using service worker for push notifications, following this article. Everything is working with Chrome but with Firefox (v.44.0.2) I have a weird issue.
On successful login to my app, I register the service worker which does nothing but waiting for push events; I see that is correctly registered (from some logging and from about:serviceworkers). Now, if I refresh the page (CTRL+R) all my POST have CORS issues (missing Access-Control-Allow-Origin header) due to this service worker and the user is redirected to login page; from here on all POSTs do not work for the same reason.
Conversely, if I login, unregister the service worker and then refresh, there are no problems at all. Any idea of what's going on? Again my service worker just handles push events, no caching no other processing done and it perfectly works on Chrome.
Here's my service worker code ( SOME_API_URL points to a real API which is not needed for testing purpose cause the issue happens after the service worker registers, no push events needed)
self.addEventListener('push', function(event) {
// Since there is no payload data with the first version
// of push messages, we'll grab some data from
// an API and use it to populate a notification
event.waitUntil(
fetch(SOME_API_URL).then(function(response) {
if (response.status !== 200) {
// Either show a message to the user explaining the error
// or enter a generic message and handle the
// onnotificationclick event to direct the user to a web page
console.log('Looks like there was a problem. Status Code: ' + response.status);
throw new Error();
}
// Examine the text in the response
return response.json().then(function(data) {
if (data.error || !data.notification) {
console.error('The API returned an error.', data.error);
throw new Error();
}
var title = data.notification.title;
var message = data.notification.message;
var icon = data.notification.icon;
var notificationTag = data.notification.tag;
return self.registration.showNotification(title, {
body: message,
icon: icon,
tag: notificationTag
});
});
}).catch(function(err) {
console.error('Unable to retrieve data', err);
var title = 'An error occurred';
var message = 'We were unable to get the information for this push message';
var notificationTag = 'notification-error';
return self.registration.showNotification(title, {
body: message,
tag: notificationTag
});
})
);
});
self.addEventListener('notificationclick', function(event) {
console.log('On notification click: ', event.notification.tag);
// Android doesn't close the notification when you click on it
// See: http://crbug.com/463146
event.notification.close();
// This looks to see if the current is already open and
// focuses if it is
event.waitUntil(
clients.matchAll({
type: 'window'
})
.then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == '/' && 'focus' in client)
return client.focus();
}
if (clients.openWindow) {
return clients.openWindow('/');
}
})
);
});
Firefox 44 has bug 1243453, which causes the Origin header of cross-origin requests to get dropped if the service worker doesn't listen for fetch events.
The bug has been fixed in Firefox 45, which will be released the week of March 8, 2016 (next week, as of the time of this writing). In the meantime, and for users who don't immediately upgrade to the latest Firefox release, you can work around the problem by adding this code to the service worker:
addEventListener('fetch', function(evt) {
evt.respondWith(fetch(evt.request));
});

Resources