What is the proper way of detecting network errors in stream downloads using Axios in Nest.js? - axios

I'm working with the HttpService module from Nest.js to make the HTTP calls. I'm able to download an image from https://unsplash.com; when there is no network interruptions the code is working as expected.
This is the code I have for making the download call and start writing into the desired file
const urlDownload = 'https://unsplash.com/photos/xiie4XeSzTU/download?force=true';
let response = await this.httpService.get(urlDownload, {
responseType: 'stream'
}).toPromise();
response.data.pipe(writer);
And this is the code where I'm trying to handle the possible events of the writer and returning a response
let downloadFile = path.resolve(__dirname,'../../files/landscape.jpg');
let writer = fs.createWriteStream(downloadFile);
return new Promise((resolve, reject) => {
writer.on('finish', ()=>{
resolve('Image downloaded');
});
writer.on('error', ()=>{
reject('Image downloaded failed');
});
});
I'm deliberately turning off the wifi during the download to try the server response with Image downloaded failed (what I have in the writer error handler), but instead I'm getting an 500 statusCode, internal server error. When I go to the Nest console to whatch the error it appears
[Nest] 11220 - 2020-05-22 18:16:45 [ExceptionsHandler] getaddrinfo ENOTFOUND unsplash.com +439536ms
Error: getaddrinfo ENOTFOUND unsplash.com
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:64:26)
How can I solve this and catch correcty the network error from Nest to return a friendly message?

I could solve it. I let it here with the hope of helping somebody in the future.
It is not firing the error handler function because that handler is attached to the writter, and there is not writting error, it just stops writing because the cut of the connection but that is not an error.
I re-writed the response variable to stop being a promise and better I started treating it like an observer.
let response = this.httpService.get(urlDownload, {
responseType: 'stream',
});
And then it is the response in previus Promise format
return new Promise((resolve, reject) => {
writer.on('error', () => {
resolve('error due to, possibly, an unexisting file path');
})
response.subscribe({
next(response) { response.data.pipe(writer) },
error(err) {
console.error('More details: ' + err);
resolve('Error in the download :/')
},
complete() { resolve('Completed'); }
})
});
I'm not using the reject function of the promise but it is perfectly doable

Related

While testing error responses, the test fails with the expected error (React/Jest/ReactQuery/Axios/MSW)

I am trying to test error states of the following MSW rest endpoint:
import { rest } from 'msw'
export const exceptionHandlers = [
rest.post(config.accountApiUrl + '/login', (req, res, ctx) => {
return res(
ctx.status(500),
ctx.json({ data: { message: 'Mock Error Message' } })
)
})
]
This endpoint is called in a custom hook return function thats using React Query's mutateAsync:
const { mutateAsync } = useMutation(AuthApi.login)
const handleLogin = async (props): Promise<void> => {
await mutateAsync(props, {
onSuccess: async () => {
// this block tests fine
}
onError: async () => {
console.log('!!!')
// it reaches this block, '!!!' is logged to the console,
// but the test still fails with `Request failed with status code 500`
}
})
}
return handleLogin
In a test file:
it('handles network errors', async () => {
mswServer.use(...exceptionHandlers)
const user = userEvent.setup()
const screen = render(<LoginForm />)
const submitButton = screen.getByTestId('Login.Submit')
// Complete form
await user.click(submitButton)
})
It doesnt matter what comes after that, the test always fails with
Request failed with status code 500
at createError (node_modules/axios/lib/core/createError.js:16:15)
at settle (node_modules/axios/lib/core/settle.js:17:12)
at XMLHttpRequestOverride.onloadend (node_modules/axios/lib/adapters/xhr.js:54:7)
at XMLHttpRequestOverride.trigger (node_modules/#mswjs/interceptors/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts:176:17)
at node_modules/#mswjs/interceptors/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts:354:16
But its supposed to fail with status 500. That's the whole point. If I change the handler to return another error, ie ctx.status(404), then the test just fails with that error code.
I've tried wrapping the assertion in a try/catch block but the same thing results. I see examples online of people doing (apparently) exactly this and it works fine, so I'm quite confused what's causing this. All other tests that check success states work as expected.
i've had the same problem.
As far as i could understand, the problem is that in test environment there is no handler for the rejected promise.
https://github.com/TanStack/query/issues/4109

Service Worker Fails on caches.open()

I'm trying to get a basic service worker up and running.
The problem I have is that when I run "caches.open()", the browser throws a
sw.js:1 Uncaught (in promise) DOMException: Unexpected internal error
Commenting out the caches.open removes the exception.
How can I get more information from the browser to tell me what's wrong?
Here's the service worker and registration code.
var CACHE_NAME = 'pwacache-v1';
var urlsToCache = [
'/',
'main.css'
];
self.addEventListener('install', function (event) {
// Perform install steps
console.log('install');
try {
event.waitUntil(getFiles());
} catch (ex) {
console.log(ex);
}
});
function getFiles() {
console.log('opening: ' + CACHE_NAME );
/*
triggers Uncaught (in promise) DOMException: Unexpected internal error
*/
caches.open(CACHE_NAME).then(function (cache) {
return Promise.all(
urlsToCache.map(function (url) {
console.log(url);
return cache.add(url).catch(function (reason) {
console.log([url + "failed: " + String(reason)]);
});
}) // end of map
);
});
console.log('waiting 3...')
}
And the registration code
// https://developers.google.com/web/fundamentals/primers/service-workers/registration
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('sw.js');
});
}
I do see the sw.js registered in Chrome's 'Application' tab.
You need to return a promise from getFiles() in order for the event.waitUntil() to actually wait for the async work. So I recommend returning the promise returned by your promise chain started with caches.open().
Without this its possible the service worker is being terminated before the async work can complete.

Mongo connection occasionally makes the lambda function timeout

I have been using MLab MongoDB and mongoose library to create a db connection inside a serverless (Lambda) handler. It works smoothly on local machine. But sometimes it doesn't work after deployment.The request returns an Internal server error. The weird thing is sometimes it works. But If I remove the database connection code, the handler works. The serverless log just says Process exited before completing request. No real errors so no idea what to do.
The db connection looks like this:
handler.js
// Connect to database
mongoose.connect(process.env.DATABASE_URL, {
useMongoClient: false
}).then((ee) => {
console.log('------------------------invoke db ', ee);
})
.catch(err => console.error('-----------error db ', err));
No error in here too. Any idea what's happening?
When you get Process exited before completing request, it means that the node process has crashed before Lambda was able to call callback. If you go to Cloudwatch logs, there would be an error and stack trace of what happened.
You should connect to the MongoDB instance inside your handler and before you call callback(), disconnect first.
It would be like this...
exports.handler = (event, context, callback) => {
let response;
return mongoose.connect(process.env.DATABASE_URL, {
useMongoClient: false
}).then((ee) => {
// prepare your response
response = { hello: 'world' }
}).then(() => {
mongoose.disconnect()
}).then(() => {
// Success
callback(null, response)
}).catch((err) => {
console.error(err);
callback(err);
})
};
Here is an article explaining with details how lambda work with node and an example of how to implement DB connection.
Differently of #dashmug suggested, you should NOT disconnect your DB since connecting every time will decrease your performance.

sails-mongo does not throw errors when mongodb gets down

I am using sails-mongo with Sails v0.12.13 and realize that if mongodb stops, when I do some request to the controller (using curl) the request get stucked for a while and then returns no error in the application logs. I just get the message on the curl console:
curl: (52) Empty reply from server
Here is my code snippet:
model.find()
.then((result) => {
options.status = true;
return callback(null, getResponse(null, name, options));
})
.catch((err) => {
options.status = false;
return callback(err, getResponse(err, name, options));
});
Tried to put logs into the catch blocks but nothing was printed.
Am I missing something or is there a problem with sails-mongo adapter on dealing with a connection loss?

Getting and "Error, wrong validation token" when trying to create a Facebook Chatbot

I'm trying to create a Facebook chatbot with NodeJS, Express, and a Heroku server.
I created my webhook on heroku and had it verified and saved by facebook. I then started adding code that would reply to the incoming messages and I can't seem to get it connected. It keeps saying "Error, wrong validation token" when I try to load my webhook in my browser. And when I try to send my bot a message I get no response. Even though I already had it verified and didn't change the code.
Here is my code:
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
var port = process.env.PORT || 3000;
// body parser middleware
app.use(bodyParser.urlencoded({ extended: true }));
// test route
//app.get('/', function (req, res) { res.status(200).send('Hello world!') });
app.get('/', function (req, res) {
if (req.query['hub.verify_token'] === '8FKU9XWeSjnZN4ae') {
res.send(req.query['hub.challenge']);
}
res.send('Error, wrong validation token');
})
app.post('/', function (req, res) {
messaging_events = req.body.entry[0].messaging;
for (i = 0; i < messaging_events.length; i++) {
event = req.body.entry[0].messaging[i];
sender = event.sender.id;
if (event.message && event.message.text) {
text = event.message.text;
sendTextMessage(sender, "Text received, echo: "+ text.substring(0, 200));
}
}
res.sendStatus(200);
});
// error handler
app.use(function (err, req, res, next) {
console.error(err.stack);
res.status(400).send(err.message);
});
app.listen(port, function () {
console.log('Listening on port ' + port);
});
var token = <myToken>;
function sendTextMessage(sender, text) {
messageData = {
text:text
}
request({
url: 'https://graph.facebook.com/v2.6/me/messages',
qs: {access_token:token},
method: 'POST',
json: {
recipient: {id:sender},
message: messageData,
}
}, function(error, response, body) {
if (error) {
console.log('Error sending message: ', error);
} else if (response.body.error) {
console.log('Error: ', response.body.error);
}
});
}
So I'm confused as to why nothing is happening and why I'm getting that error. I feel like I'm missing a whole step. I am following this tutorial by the way: https://developers.facebook.com/docs/messenger-platform/quickstart
Any help is appreciated. Thanks!
Edit: Here are my heroku logs
Do not post your full access tokens here!
Have you tested the output of the challenge? Since it's just a GET and you know all values you can try it yourself: your-app-domain.com/your-callback-url?hub_mode=subscribe&hub_verify_token=the_token_you_set_in_your_app_config&hub_challenge=ping which sould print 'ping' if everything work fine.
Make sure you add sendStatus(200) to the hub challenge response, too.
You need to subscribe your page to the app first. To do so make a POST request to /your-page-id/subscribed_apps which should return "success". You can make a GET request to the same endpoint afterwards to double check your app is subscribed to your page
You did not mention which events you subscribed to (needs to be message_deliveries, messages, messaging_optins, messaging_postbacks)
Make sure the webhooks tab in your app dashboard now says "complete"
Test again
You are actually using "request" but you are never importing it anywhere. Here's how to fix it:
var request = require("request")
Once you have added that to your index.js or app.js file (basically whatever this file is), make sure you do:
npm install request --save
This should fix it. Unfortunately, Heroku doesn't error out and say that it does not know what "request" is and that's why it was so hard to figure this out in the first place!