javascript async/await catch block trapping error instead of mocha - javascript

There is a mocha test as follows:
it('(t4) should assert that issues a GET request by john should fetch his own records via svc1', done => {
let filter = { where: { key: 'john' }};
let testHelperUrl = `${svc1}/api/TestHelper?filter=${encodeURIComponent(JSON.stringify(filter))}`;
let fetchToken = cb => {
req('GET', testHelperUrl, (err, resp) => {
if(err) {
return cb(err);
}
cb(null, resp.data[0]);
});
};
let getWithToken = ({ value: token }, cb) => {
let consumerUrl = `${svc1}/api/Consumers?access_token=${token}`;
req('GET', consumerUrl, (err, resp) => {
if(err) {
return cb(err);
}
cb(null, resp.data);
})
};
async.seq(fetchToken, getWithToken)((err, data) => {
if(err) {
return done(err);
}
expect(data.length).to.equal(2);
done();
});
});
It calls some APIs via the req(), and, it is defined as follows:
const req = (method, url, data, cb) => {
if (method.toLowerCase() === 'get') {
if (typeof cb === 'undefined') {
cb = data;
data = null;
}
(async () => {
try {
let response = await got(url, { responseType: 'json', https: { rejectUnauthorized: false } });
cb(null, { code: response.statusCode, data: response.body });
} catch (err) {
cb(err);
}
})();
}
else {
(async () => {
try {
let response = await got.post(url, { json: data, responseType: 'json', https: { rejectUnauthorized: false } });
cb(null, { code: response.statusCode, data: response.body });
} catch (err) {
cb(err);
}
})();
}
};
The above code uses got for doing http requests. Its design is promised-based, and hence, the reason to why async/await pattern is used here.
I am also using the async library here. The following line: expect(data.length).to.equal(2) should fail, and, the expectation is that mocha should report that error correctly.
However, the error is actually trapped by the async library. It should report an error saying callback already was called. But instead the program just dies. However, through debugging I am able to confirm that it was an error trapped in the async module. No idea why it dies abruptly though.
Am I doing something wrong? Or should I modify mocha code to properly handle the assertion error like wrapping that in a try/catch block or something?
Update:
Solution is to invoke the callback function to req() inside of a process.nextTick(). E.g.
try {
let response = await got(url, { responseType: 'json', https: { rejectUnauthorized: false } });
process.nextTick(() => cb(null, { code: response.statusCode, data: response.body }));
} catch (err) {
process.nextTick(() => cb(err));
}

Related

readFile returns undefined

I have this situation where I'm trying to send a request with the content of a file, but the problem is that the content arrives undefined. How can I solve this? I tried multiple versions from stackoverflow, but nothing worked so far.
const ifExists = (filePath) => {
try {
if (fs.existsSync(filePath)) {
return true;
}
} catch (err) {
console.log(err);
}
return false;
}
const readMyFile = async (filePath) => {
const fileExists = ifExists(filePath);
if (fileExists) {
fs.readFile(filePath, (err, data) => {
if (err) {
console.log("Error occurred when trying to read the file.");
return false;
}
console.log("File successfully read.");
return data; // data has the right content here
});
} else {
console.log("File not found");
return false;
}
}
const getFile = async function (req, res, next) {
try {
const content = await readMyFile(filePath); // the content is undefined here
res.writeHead(200, { "Content-Type": "application/json" });
res.write(JSON.stringify(content));
} catch (err) {
console.log("Error occurred.");
res.status(500).send("Error");
} finally {
res.end();
}
};
Thank you for your time!
fs.readFile uses a callback and does not return a promise which means it can't be used properly in an async function. If you want to use an async function I suggest returning a promise.
const readFile = async (filePath) => {
return new Promise((resolve, reject) => {
if (!exists(filePath)) {
reject(Error("File not found"));
}
fs.readFile(filePath, (err, data) => {
if (err) {
reject(err);
}
resolve(data)
});
})
}

How to add a new then after fetch has run

I have a method that runs a fetch request and then saves the result or error like this:
saveTema() {
this.gateway.editTema(this.state.tema)
.then(tema => {
this.setState({
tema,
error: null,
isDirty: false,
});
})
.catch(httpOrOtherError => {
if (httpOrOtherError.status) {
if (httpOrOtherError.status === 400) {
httpOrOtherError.json().then(result => {
const serverValidationfailures =
this.transformValideringsfeil(result.valideringsfeil);
this.setState({
error: {
valideringsfeil: {...serverValidationfailures},
},
showActivationDialog: false,
})
});
} else {
this.setState({
error: {httpError: {status: httpOrOtherError.status, statusText: httpOrOtherError.statusText}},
showActivationDialog: false,
});
}
} else {
this.setState({
error: {fetchReject: {message: httpOrOtherError.message}},
showActivationDialog: false,
})
}
})
}
And this is the fetch request itself:
editTema(tema) {
return fetch(
this.temaUrl(tema.id),
{
method: 'PUT',
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(tema)
})
.then(res => {
if (res.ok) {
return res.json();
}
throw res;
}
);
}
I would like to run this method from another one, and check if everything went ok with this method and based on that do further actions. Something like this:
this.saveTema().then(() => {
this.props.history.push({
pathname: '/tema',
state: {
successMessage: `Tema ${this.state.tema.id} ble oppdatert`,
}
}}));
But, this is of course wrong, I am not sure how can I do this, to run some code after the fetch handling of the fetch request has finished. What is the right way to do it?
saveTema() {
return this.gateway.editTema(this.state.tema)
...
Return the promise and then you'll be able to do exactly what you are trying to do.
Return the editThema result after setting up the handlers:
saveTema() {
let prom = this.gateway.editTema(this.state.tema)
prom.then(tema => {
// .. success handling code
})
.catch(httpOrOtherError => {
// .. error handling code
})
return prom;
}
Now you can call your function exactly like you wanted to.
You can achieve that by two approaches
Using async/await
Using native Promise
1. async/await way
userController.js
const userUtils = require('./userUtils');
const userCtr = {};
userCtr.searchUser = async (req, res) => {
try {
const { userName } = req.query;
const result = await userUtils.searchUser(userName);
return res.status(200).json(result);
} catch (err) {
return res.status(err.code).json({ error: err.error });
}
};
module.exports = userCtr;
userUtils.js
const userUtils = {};
userUtils.searchUser = async (userName) => {
try {
if (userName) {
// ...Do some cool stuff
const result = [];
return result;
}
const errorObj = { code: 400, error: 'ERR_VALID_PARAM' };
throw errorObj;
} catch (err) {
console.error(err);
throw err;
}
};
module.exports = userUtils;
2. Promise way
userController.js
const userUtils = require('./userUtils');
const userCtr = {};
userCtr.searchUser = (req, res) => {
const { userName } = req.query;
userUtils.searchUser(userName)
.then((result) => {
return res.status(200).json(result);
})
.catch((err) => {
return res.status(err.code).json({ error: err.error });
});
};
module.exports = userCtr;
userUtils.js
const userUtils = {};
userUtils.searchUser = (userName) => {
return new Promise((resolve, reject) => {
if (userName) {
// ...Do some cool stuff
const result = [];
return resolve(result);
} else {
const error = { code: 400, error: 'Please provide valid data!' }
return reject(error);
}
});
};
module.exports = userUtils;
In both approaches you can hold further execution (in both approach Promise are used directly or indirectly), In a second approach you can achieve by .then().catch() whereas in the first approach just you need to put a keyword await and put async on your function, I suggest you to use async/await. Because when you need to wait for the completion of more than 3 promises and yo go with Native Promise then your code will be so messy like .then().then().then() Whereas in a first approach you just need to put a keyword await on starting of your function, Using async/await approach your code will neat and clean and easily understandable and easy to debug.

keep looping a function until the callback inside resolves to true

I've the two interconnected functions.
module.exports = async function newRunner() {
console.log('No Config found. Requesting new Runner ID');
if (!process.env.ADDRESS) {
const a = await inquirer.prompt([
{
type: 'input',
message: 'Enter the runner api address',
name: 'address',
},
]);
config.ADDRESS = a.address;
} else {
config.ADDRESS = process.env.ADDRESS;
}
config.save();
const res = await auth.checkAddress();
console.log('New Runner ID', res.id);
config.ID = res.id;
config.TOKEN = res.token;
config.save();
};
The line const res = await auth.newRunner(); call the following function.
exports.checkAddress = args => new Promise((resolve, reject) => {
const cArgs = {
data: args,
path: {},
parameters: {},
headers: {
'Content-Type': 'application/json',
},
};
client.post(`${config.ADDRESS}/new-runner`, cArgs, (data, response) => {
if (response.statusCode !== 200) return reject(new Error(`Status code ${response.statusCode}`));
return resolve(data);
})
.on('responseTimeout', (res) => {
console.log('Update Response Timeout!', res);
reject(new Error('response has expired'));
})
.on('error', (err) => {
reject(err);
});
});
I need to keep the first function running until the address entered is correct and the second function resolves to true. I've tried the following.
await newRunner();
while (!await auth.newRunner()) {
await newRunner();
}
The functions runs as it should if I enter the correct address, wrong address breaks it, but I want it to keep looping until the address is correct. What do I need to change do archive the correct behaviour?
Your "main" newRunner (at the top of your question) only fulfills when auth.newRunner fulfills, and rejects otherwise. auth.newRunner only fulfills when it successfully got data. So to loop until you've successfully got data, you need to wrap the call in a try/catch:
let failed;
do {
try {
await newRunner();
failed = false;
} catch {
failed = true;
}
} while (failed);
Might be best to put some limits on that, though, so it can't cycle infinitely.

JavaScript: Converting RSS into JSON in AWS Lambda Node app

I'm currently working on writing a function on AWS Lambda. I want to convert a RSS feed into a JSON and give that as a response in the body when making to the Lambda endpoint.
I'm using an npm package to convert the RSS to JSON. However, when I run the code. I see that I get undefined in the conversion of the RSS URL. Here is the following code:
const feed = require('rss-to-json');
exports.handler = async (event) => {
let rssFeed = event.queryStringParameters.rssFeed;
let rssAsJsonData = convertRssIntoJson(rssFeed);
return sendRes(200, rssAsJsonData);
};
const sendRes = (status, body) => {
var response = {
isBase64Encoded: true|false,
statusCode: status,
headers: {
"Content-Type": "application/json"
},
body: body,
};
return response;
};
function convertRssIntoJson (rssFeed) {
console.log(rssFeed);
return feed.load(rssFeed, function(err, rss){
if(err) {
console.log("Error: ${err}");
return;
}
console.log(rss)
return rss;
});
};
However, in the logs I get undefined when console.log(rssAsJsonData).
However, when debugging I was able to see console.log(rss) working when I change body to be body: json.stringify("TESTING")
However, it only worked when logging to the console not when I tried to pass it to the body body: body, I can't seem to find what the error is. I'm moving from Ruby to JavaScript for this project maybe I'm missing something.
I'm using Postman to make the calls:
function convertRssIntoJson (rssFeed) {
console.log(rssFeed);
return feed.load(rssFeed, function(err, rss){
if(err) {
console.log("Error: ${err}");
return;
}
console.log(rss)
return rss;
});
};
The piece of code above is a callback. Under the hood, feed.load is asynchronous, which makes your callback be executed asynchronously.
Now, when you invoke your function like this
let rssAsJsonData = convertRssIntoJson(rssFeed);
your rss object inside convertRssIntoJson does not hold any value yet, because the callback hasn't been populated up to now. This is where your undefined comes from.
Callbacks themselves don't make code asynchronous by default, but NodeJS works with a non-blocking IO model and, since feed.load is an IO call, it will be executed asynchronously.
You have a few options now, but I will list only two. A not-so-nice and a nice solution:
1) The not-so-nice way to fix it is to add a callback as argument to your convertRssIntoJson function and pass the value of that rss object upstream. The not-so-nice full code can be found below:
const feed = require('rss-to-json');
exports.handler = async (event) => {
let rssFeed = event.queryStringParameters.rssFeed;
convertRssIntoJson(rssFeed, (err, data) => {
if (err) {
return sendRes(500, { message: 'There was an err: ' + err.message })
}
return sendRes(200, data)
})
};
const sendRes = (status, body) => {
var response = {
isBase64Encoded: true | false,
statusCode: status,
headers: {
"Content-Type": "application/json"
},
body: body,
};
return response;
};
const convertRssIntoJson = (rssFeed, callback) => {
console.log(rssFeed);
feed.load(rssFeed, function (err, rss) {
if (err) {
console.log("Error: ${err}");
callback(err, undefined)
}
console.log(rss)
callback(undefined, rss)
});
};
2) The nice, clean, elegant and recommended solution is this one. Wrap your callback in a Promise, like this
function convertRssIntoJson(rssFeed) {
console.log(rssFeed);
return new Promise((res, rej) => {
feed.load(rssFeed, function (err, rss) {
if (err) {
console.log("Error: ${err}");
return rej(err)
}
console.log(rss)
return res(rss)
});
})
};
Since your handler is async, it means it can just await on Promises.
So your client code is now as simple as:
return sendRes(200, await convertRssIntoJson(rssFeed));
Your final code will look like (I have refactored a little bit to make use of arrow functions):
const feed = require('rss-to-json');
exports.handler = async (event) => {
let rssFeed = event.queryStringParameters.rssFeed;
return sendRes(200, await convertRssIntoJson(rssFeed));
};
const sendRes = (status, body) => {
var response = {
isBase64Encoded: true | false,
statusCode: status,
headers: {
"Content-Type": "application/json"
},
body: body,
};
return response;
};
const convertRssIntoJson = (rssFeed) => {
console.log(rssFeed);
return new Promise((res, rej) => {
feed.load(rssFeed, (err, rss) => {
if (err) {
console.log("Error: ${err}");
return rej(err)
}
console.log(rss)
return res(rss)
});
})
};
If you want to know more about async/await, you can see it in here.
EDIT: Code refactor and code added for solution 1)

AWS Cognito returning undefined error async/await

My question is very similar to this one as I am attempting to wrap the authenticateUser SDK method in a Promise and resolve/reject that.
Code:
async function cognitoAuth(credentials, next, res) {
const userData = {
Username: credentials.email,
Pool: userPool
};
const authenticationDetails = getAuthenticationDetails(credentials);
let userCredentials;
let authenticatedUserResponse;
cognitoUser = new CognitoUser(userData);
try {
userCredentials = await checkIfAuthenticated(credentials.email);
if (userCredentials.UserStatus === CognitoUserStatus.FORCE_CHANGE_PASSWORD) {
res
.status(203)
.send(userCredentials);
} else if (userCredentials.UserStatus === CognitoUserStatus.CONFIRMED) {
authenticatedUserResponse = await authenticateUser(authenticationDetails);
console.log(authenticatedUserResponse);
}
} catch(err) {
if (err.message === CognitoErrorMessages.USER_NOT_EXIST) {
next({
name: err.message,
status: 404
});
}
}
}
As you can see, I have 2 awaited functions (checkIfAuthenticated and authenticateUser). If checkIfAuthenticated throws a an error and rejects the promise, then the catch is fine with a valid err object.
However, if authenticateUser throws an error, the catch is invoked but err is undefined.
This is my authenticateUser:
function authenticateUser(authenticationDetails) {
return new Promise((resolve, reject) => {
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: (result) => {
resolve(merge(err, {
res: "SUCCESS"
}));
},
onFailure: (err) => {
reject(merge(err, {
status: 401
}));
},
newPasswordRequired: (userAttrs, requiredAttrs) => {
resolve(merge(userAttrs, {
res: "NEW_PASS_REQ"
}));
}
});
});
}
Using breakpoints, the onFailure gets invoked and it is the correct err object so am unsure as to why it is undefined in the catch
Figured it out. One of my colleagues pointed out that I running babel over my server code by having this in my index.js (my entry point for the server):
require("#babel/polyfill");
require("#babel/register");
A transpilation issue in Node. As soon as I took that out and switched to ES5 imports/exports it worked fine

Categories