Following the documentation at
http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html
I am trying to write a basic test against my DynamoDB tables using promises to handle the asynchronous response with no luck thus far. It should return an object containing the TableNames.
If I access the DynamoDB object without using promises, I have no problem.
This works:
import AWS from 'aws-sdk'
AWS.config.loadFromPath('./awsConfig.json')
const dynamo = new AWS.DynamoDB({region: 'us-east-2'})
window.test('Test Connection', () => {
return dynamo.listTables({Limit: 10}, (err, data) => {
if (err) {
console.log('err', err)
} else {
console.log('TableNames are:', data)
}
window.expect(data).toEqual(window.expect.anything())
})
})
This does not:
import AWS from 'aws-sdk'
AWS.config.loadFromPath('./awsConfig.json')
const dynamo = new AWS.DynamoDB({region: 'us-east-2'})
window.test('Test Connection', () => {
let tables
const listPromise = dynamo.listTables({Limit: 10}).promise()
listPromise.then((data) => {
tables = data
console.log('TableNames are:', data)
}).catch((err) => {
console.log('Error:', err)
})
window.expect(tables).toEqual(window.expect.anything())
})
The value of data is always undefined. I cannot find any documentation that would indicate that promises are not supported with DynamoDB requests, so I am just about certain that I'm missing something obvious...
Any help is appreciated. This is my first time asking a question in this forum, so feedback on the question itself is also appreciated.
EDIT: Problem Solved - rookie mistake
The issue was not with dynamoDB, but rather with using test. When dealing with promises, the promise must be returned to test for proper evaluation. Otherwise, it cannot be evaluated.
Working Code:
import AWS from 'aws-sdk'
AWS.config.loadFromPath('./awsConfig.json')
const dynamo = new AWS.DynamoDB({region: 'us-east-2'})
window.test('Test Connection', () => {
const listPromise = dynamo.listTables({Limit: 10}).promise()
return listPromise.then((data) => {
window.expect(data).toEqual(window.expect.anything())
}).catch((err) => {
console.log('Error:', err)
})
})
More info: https://facebook.github.io/jest/docs/en/asynchronous.html
Thanks to #Brahma Dev for the assistance.
Probem with test code, not Promise
The issue was not with dynamoDB or promises, but rather with my use of test. When dealing with promises, the promise must be returned to test for proper evaluation. Otherwise, it cannot be evaluated properly.
Working Code:
import AWS from 'aws-sdk'
AWS.config.loadFromPath('./awsConfig.json')
const dynamo = new AWS.DynamoDB({region: 'us-east-2'})
window.test('Test Connection', () => {
const listPromise = dynamo.listTables({Limit: 10}).promise()
return listPromise.then((data) => {
window.expect(data).toEqual(window.expect.anything())
}).catch((err) => {
console.log('Error:', err)
})
})
More info: https://facebook.github.io/jest/docs/en/asynchronous.html
Thanks to #Brahma Dev for the assistance.
EDIT: Corrected working code example based on feedback from #Brahma Dev
Also, removed some unnecessary variable declarations and assignments.
Related
I've got a NodeJS/Express/MongoDB app. For one of my endpoints I'm trying to get some stats from the database and I'm having trouble doing it using promises.
I know that db doesn't get moved between the different .thens but no matter how I re-arrange the code I can't get anything out of the console.log() except the first users count. I've also tried saving db into a variable declared at the start of the function but that doesn't help either.
What's the proper way to make multiple queries to MongoDB in a promisified way?
Node Code:
function getStats(num){
var stats = "";
MongoClient.connect(`${mongoString}/SiteUsers`)
.then(db => {
db.db().collection("Users").find({}).count()
.then( userCount => {
stats += `Users: ${userCount}\n`
return db;
})
.then( adminCount => {
db.db().collection("Users").find({admin:true}).count()
.then( adminCount => {
stats += `Admins: ${adminCount}\n`
})
})
.then( data => {
console.log(`Stats are now: \n${stats}`);
})
})
.catch(err => {
if(err) console.log(err);
});
}
Thanks!
There are several ways that you can handle the order of your promises. One of which is to use async/await functionality.
async function getStats(num){
try {
const conn = await MongoClient.connect(`${mongoString}/SiteUsers`)
const userCount = await conn.db().collection("Users").find({}).count()
const adminCount = await conn.db().collection("Users").find({admin:true}).count()
console.log(`User count: ${userCount}\nAdmin count: ${adminCount}`)
} catch (err) {
console.log(err)
}
}
I'm pretty new on React and I'm learning this language nowadays.
For this purpose I'm building a test project on which I try to encounter the main classical issues and looking to solve it.
For most of React developpers, there are no difficulties in following code but I will give a few details for better comprenhension.
I have a portion of javascript code that is returning a list of articles from a Symfony Backend API only if user is authorized for getting it (Authorization via JWT will be done later). A getArticles function returns a Promise that tries to get the articles from the Symfony backend inside a try {} catch (error) {} block.
Voluntarily, Authorization token is not send to trigger an error in the query.
As the axios.get is located inside a try {} catch (error) {} block, I am surprised that an error appears in the console for the request. It doesn't impact the behavior but it is not very clean to have these errors in the console.
My question(s) :
Why an error appears in the console while the code is inside a try/catch ? To get a cleaner app behavior, is there a way to avoid having this error in the console ? I have found other React try/catch issues but I didn't deduct the similarity with my issue. Am I missing something ?
Thanks in advance ;-)
I am aware that my code could be refactored, do not hesitate to suggest any good practice
componentDidMount(){
/*To be prepared to attach JWT token*/
axios.interceptors.request.use(req => {
return req;
});
const getArticles = async() => { return new Promise( (resolve, reject)=> {
try{
const data = axios.get('https://xxxxx/api/articles');
resolve(data);
} catch (err) {
reject(err);
}
});
}
getArticles().then(res => {
const articles = res.data.data.items;
this.setState( {errorOnArticlesLoading:false, articles: articles } );
})
.catch(error => {
this.setState( {errorOnArticlesLoading:true} );
});
}
You can try in this way and Async functions itself returns a promise, you don't need to return a new Promise manually.
async componentDidMount() {
try {
/*To be prepared to attach JWT token*/
axios.interceptors.request.use(req => req);
const getArticles = async () => {
try {
const data = axios.get('https://xxxxx/api/articles');
this.setState({ errorOnArticlesLoading: false, articles: data.data.items });
} catch (err) {
this.setState( {errorOnArticlesLoading:true} );
}
};
await getArticles()
} catch(err) {
console.log('Handled root error')
}
}
It seems that there are no solutions to avoid the 401 http error code in the console because it it printed by Chrome itself: See discussion here. So the following code cannot avoid the 401 error status to be printed in the console.
componentDidMount(){
/*To be prepared to attach JWT token*/
axios.interceptors.request.use(req => {
return req;
});
const getArticles = async() => {
const data = await axios.get('https://xxxx/api/articles');
return data;
}
getArticles().then(res => {
const articles = res.data.data.items;
this.setState( {errorOnArticlesLoading:false, articles: articles } );
})
.catch(error => {
this.setState( {errorOnArticlesLoading:true} );
});
}
I am trying to do the following, unfortunately to no avail:
1. reading a spreadsheet and get it as a parsed object.
2. use the parsed object to look for a specific value in it. e.g an email.
The spreadsheet serves as a database and the motivation is to determine whether a given email exists there or not.
Apparently, I have misunderstood the use of asynchronous methods as I can't grasp how to return the.
The calling method:
helpers.read('the-spreadsheet-id', 'Sheet1!A:Z');
The reading method # helpers/sheets.js:
exports.read = (spreadsheetId, range) => {
const sheets = google.sheets({ version: 'v4', auth: oAuth2Client });
return sheets.spreadsheets.values.get({
spreadsheetId,
range,
})
.then(_.property('data.values'));
};
what should I do in order to get a json object or similarly parsed data object to keep on working with at the calling method?
Thanks in advance.
For the asynchronous step to work, you have to either declare you are returning a Promise or that the function is async. Async/await is actually using promises under the hood, but it's a cleaner syntax.
For the Promise version, you have to return the value you want with the resolve function, and throw the error with the reject function.
exports.read = (spreadsheetId, range) => {
return new Promise((resolve, reject) => {
const sheets = google.sheets({ version: 'v4', auth: oAuth2Client });
sheets.spreadsheets.values.get({
spreadsheetId,
range,
})
.then(data => {
resolve(data.values);
})
.catch(err => reject(err));
});
};
When you are using async/await, you can wrap the query in a try/catch and throw the error if it fails.
exports.read = async (spreadsheetId, range) => {
const sheets = google.sheets({ version: 'v4', auth: oAuth2Client });
try {
const data = await sheets.spreadsheets.values.get({
spreadsheetId,
range,
})
return data.values;
} catch(err) {
throw err;
}
};
I don't use underscore so I'm not sure what you were trying to return here. I assume that data is the result of the query and you were trying to pass out values?
Background
I am trying to learn how to do a RESTful API following the TDD paradigm by reading a book on the subject (it is in brazillian):
https://leanpub.com/construindo-apis-testaveis-com-nodejs/read
The author encourages the use a sinon.js together with mocha.js.
I am getting close to the end, but I am failling to pass the test for my gnomeController.
Problem
The problem is that I am using sinon to assert that I am calling the gnomeController's get method using the given reponse object, which is in reallity a spy.
This spy is to make sure I call the reponse method with an "Error", but it appears I am calling the response with no arguments whatsoever, which is very confusing.
Code
gnomeController.js
module.exports = aGnomeModel => {
let Gnome = aGnomeModel;
function get(req, res){
return Gnome.find({})
.then(gnomes => res.send(gnomes))
.catch(err => res.status(400).send(err));
}
return Object.freeze({
get
});
};
gnomeTest.js
const sinon = require("sinon");
const gnomesControllerFactory = require("gnomesController.js");
const Gnome = require("gnomeModel.js");
describe("Controllers: Gnomes", () => {
describe("get() gnomes", () => {
it("should return 400 when an error occurs", () => {
const request = {};
const response = {
send: sinon.spy(),
status: sinon.stub()
};
response.status.withArgs(400).returns(response);
Gnome.find = sinon.stub();
Gnome.find.withArgs({}).rejects("Error");
const gnomesController = gnomesControllerFactory(Gnome);
return gnomesController.get(request, response)
.then(arg => {
console.log(arg);
sinon.assert.calledWith(response.send, "Error");
});
});
});
});
Question
I am using the latest versions of both libraries.
What is wrong in my code, why is the reponse being called with no arguments?
Solution
After much debugging, I found out that the solution is to replace:
function get(req, res){
return Gnome.find({})
.then(gnomes => res.send(gnomes))
.catch(err => res.status(400).send(err));
}
with:
function get(req, res){
return Gnome.find({})
.then(gnomes => res.send(gnomes))
.catch(err => res.status(400).send(err.name));
}
Which is not explained in the book. Kinda wish I could give more feedback on it, but so far it is what it is.
I'm using node-strava-v3 with Node 5.7 to retrieve JSON collections from Strava's API. Pretty simple actually, no problem actually.
I just want to take the opportunity to try the ES6 Generators. So far so good I think I get the concept. But in the case of this Node-Strava-V3 API wrapper, the methods never return a value or a promise directly. Only Callbacks are allowed.
Which brings me to the question : how can I insert the ES6 Generators concept into play?
Take this : (this requires access_token you can request on the Strava portal)
var strava = require('strava-v3');
var mainActivity = {id: 504641206};
strava.activities.get(mainActivity, function (err, data){
if(err){ console.log("Strava GET issue: ", err);
/// consume the JSON data here...
};
//rest of the code
but can I use the generators to pause until this "strava.activities.get()" method ends? this method does not return any, no value, no promise.
Thanks for all your suggestions
FJ
You could do this with generators and a library like co, but now we have async functions (ES2017, and can be transpiled for outdated environments).
Tangentially related: Any API that provides callbacks can be promise-ified:
const strava = require('strava-v3');
const mainActivity = {id: 504641206};
new Promise((resolve, reject) => {
strava.activities.get(mainActivity, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
}).then(
result => {
//rest of the code
},
error => {
// handle error
}
);
In fact, that common pattern is codified in util.promisify:
const strava = require("strava-v3");
const promisify = require("util").promisify;
const stravaActivitiesGet = promisify(strava.activities.get);
const mainActivity = {id: 504641206};
stravaActivitiesGet(mainActivity).then(
result => {
//rest of the code
},
error => {
// handle error
}
);
There are multiple libraries that promise-ify a whole API at once (rather than per-function), such as node-promisify.
If we use util.promisify and an async function:
const strava = require("strava-v3");
const promisify = require("util").promisify;
const stravaActivitiesGet = promisify(strava.activities.get);
const mainActivity = {id: 504641206};
(async () => {
try {
const data = await stravaActivitiesGet(mainActivity);
//rest of the code
} catch (error) {
// handle error
}
})();
That mostly makes sense if you're doing other asynchronous things in the logic.