GraphQL Sent Proper Data but Variable is Undefined - node.js

Hopefully I'm not overlooking something stupid here, but here's the problem.
I'm fetching this Smartsheet data using promise:
var getWorkspaces = async function(args) {
var smartsheet = client.createClient({ accessToken: "key" });
await smartsheet.workspaces.listWorkspaces()
.then(async function(workspaceList) {
var data = await workspaceList.data;
console.log("returning");
console.log(data);
return data;
})
.catch(function(error) {
console.log(error);
});
}
and this is my resolvers:
var resolvers = {
Query: {
workspaces: async () => {
var workspaceData = await getWorkspaces();
console.log("sending");
console.log(workspaceData);
return workspaceData;
}
}
};
Why is this the console output:
returning
[
{
id: 000,
name: 'nname',
accessLevel: 'ADMIN',
permalink: 'link'
},
{
id: 000,
name: 'name',
accessLevel: 'ADMIN',
permalink: 'link'
},
{
id: 000,
name: 'name',
accessLevel: 'ADMIN',
permalink: 'link'
}
]
sending
undefined
And more specifically, why is workspaceList undefined? I've been at this for hours and cannot find a solution.

The issue here is that your return workspaceData is within a callback function of .then(), so you're returning in the callback, not in the getWorkspaces() function. When you say .next(), the request happens asynchronously. When you use await, the current thread waits for the response and executes it without needing a callback–– a synchronous call. await implicitly calls .next(), and adding try/catch is equivalent to .catch().
var getWorkspaces = async function(args) {
try {
var smartsheet = client.createClient({ accessToken: "key" });
var workspaceList = await smartsheet.workspaces.listWorkspaces();
var data = workspaceList.data;
console.log("returning");
return data;
} catch (err) {
console.log(err);
}
}

Related

How to refactor promises all chain using async await Node js and sequelize ORM

I want to refactor code for chain of promises by async, await. I have sequelize ORM for DB management and the code is written in AWS Lambda function having multiple middleware. In such cases I have to traverse code for multiple entries using sequelize transactions. It is easy to manage using promise.all() but need to change it to async await syntax for cleaner code.
Here are my demo code.
/* get all invoice where user_id === current logged in user, and where status != "paid" */
db.Invoice.findAll({
where: {
user_id: currentLoggedInUser,
status: {
$ne: "paid"
}
}
}).then(invoices => {
if (!invoices || invoices === null) {
return false;
}
function addScheduledTransactionAttempts(invoice, tryPayOnDate, t) {
return new Promise((resolve, reject) => {
/* check If any ScheduledTransactionAttempts exists for this invoice.id */
db.ScheduledTransactionAttempts.find({
where: {
invoice_id: invoice.id
}
})
.then(function(attempts) {
if (attempts) {
attempts
.destroy({}, {
transaction: t
})
.then(deletedAttempts => {
console.log("Attempts Record Deleted: ", deletedAttempts);
})
.catch(error => {
reject(error);
t.rollback();
});
}
return db.ScheduledTransactionAttempts.create({
invoice_id: invoice.id,
payment_source_id: PaymentMethodId,
try_pay_on_date: tryPayOnDate,
stripe_customer_id: currentLogInStripeCustomerId
}, {
transaction: t
})
.then(function(attempt) {
resolve(attempt.id);
})
.catch(error => {
reject(error);
t.rollback();
});
})
.catch(error => {
reject(error);
t.rollback();
});
});
}
//Run transaction to addScheduledTransactionAttempts
return db.sequelize.transaction().then(function(t) {
let promiseArr = [];
var i = 0;
invoices.forEach(function(invoice) {
var schedulePaymentDate = moment(paymentDate);
if (invoice) {
let tryPayOnDate = schedulePaymentDate
.add(i, "month")
.format("YYYY-MM-DD");
promiseArr.push(
addScheduledTransactionAttempts(invoice, tryPayOnDate, t) //calling above function
);
i++;
}
});
//now execute promise all
Promise.all(promiseArr)
.then(function(result) {
t.commit();
return true;
})
.catch(function(err) {
t.rollback();
return false;
});
});
});
In the above code I want to change
Promise.all(promiseArr)
which is calling
addScheduledTransactionAttempts
function to do DB queries to simple async function await process to make it easy simpler understandable without having multiple .then or .then inside then promises.
Any help regarding would be appreciated,
Thanks.
It's quite simple. await is valid when invoking methods that return a Promise. All of your SDK methods already return a promise, so refactoring should be quite straight forward. Here's something to get you off ground:
const processInvoices = async currentLoggedInUser {
const invoices = await db.Invoice.findAll({
where: {
user_id: currentLoggedInUser,
status: {
$ne: 'paid',
},
},
});
if (yourOwnLogicForInvoicesObject) {
for (const invoice of invoices) {
const potentiallyFoundInvoice = await db.ScheduledTransactionAttempts.find({
where: {
invoice_id: invoice.id,
},
});
if (potentiallyFoundInvoice) {
await addScheduledTransactionAttempts(potentiallyFoundInvoice)
}
}
}
}
const addScheduledTransactionAttempts = async invoice => {
console.log('Do something with your invoice', invoice)
}
Long story short: refactor the code inside your functions into smaller functions and make these new functions async, just like I did with addScheduledTransactionAttempts and processInvoices
More on async/await

wait for result from an async method

I'm trying to call an async method from for loop, but it doesn't wait for the result from that method.
Below is my code:
async function fetchActivityHandler (req, reply) {
esClient.search({
index: 'user_activity',
type: 'document',
body: {
_source : ["userId","appId","activity","createdAt","updatedAt"],
query: {
bool : {
must:[
{match : { 'userId': req.params.id }}
]
}
}
}
},async function (error, response, status) {
if (error){
console.log('search error: '+error)
}
else {
var activities = [];
//await Promise.all(response.hits.hits.map(async function(hit){
for (const hit of response.hits.hits) {
var activity = hit._source
var app = await fetchAppDetails(activity.appId);
console.log(app);
activity = {...activity,app : app}
activities.push(activity);
console.log(activity);
}
reply.status(200).send(activities);
}
});
}
async function fetchAppDetails (appId) {
esClient.get({
index: 'app',
type: 'document',
id: appId
}, function (err, response) {
console.log(response._source);
return (response._source);
});
}
What may be the problem. I'm using async and await, but it is not working.
Await works with promise. You should wrap your function with promise to get this work. Hope this will help you. Also you do not need to use async on fetchActivityHandler function. Only in the callback which you have already used.
function fetchAppDetails (appId) {
return new Promise((resolve,reject)=>{
esClient.get({
index: 'app',
type: 'document',
id: appId
}, function (err, response) {
if(err){
reject(err);
}
else{
resolve(response)
}
});
});
}

Stubbing a mongoose exec method using Bluebird and sinon in node.js

I have a service layer that calls a database layer where a query is run. I am trying to stub the data layer returning a value but I keep getting this error
TypeError: User.find(...).exec is not a function.
I am using the bluebird module and am also using sinon for stubbing
//Service Layer
var dbLayer = require('../dbLayer');
exports.getUsers = function (req, res) {
dbLayer.GetUsersThatNeedToBeApproved(req.body.domain)
.then(function (data) {
res.json(data);
})
.catch(function(error){
routesLogger.logError(req.session.user.role, "getUsers", error);
res.status(400).send({error: StatusCodes.DBERROR});
});
};
//DB Layer
module.exports.GetUsersThatNeedToBeApproved = function (domain) {
return new Promise.resolve(User.find({$and: [{'domain.upgradeRequest': true}, {'domain.access': constants.ACCESSPRIVATE}, {'domain.name': domain}]}).exec())
.then(function (data) {
return data;
}).catch(function (error) {
routesLogger.logError("userDAL", "GetUsersThatNeedToBeApproved", error);
return error;
});
This is my test case
describe("Suite to get users in portal that need to be approved",function(){
var request = null;
var response = httpMocks.createResponse();
it("Should get a list of all users that need to be approved for a specific domain",function(done){
request = {
session: {
user: {
domain:{
name:'domain'
},
email: 'testUser#test.com',
role: 'Admin'
}
}
};
var expectedFindResponse = [
{
email: "test#xxx.com",
role: "T",
domain:{
upgradeRequest:true,
access:'private',
name:'domain'
}
},
{
"email": "test1#xxx.com",
"role": "T",
domain:{
upgradeRequest:true,
access:'private',
name:'domain'
}
}
];
var find = sinon.stub(mongoose.Model, "find").returns(Promise.resolve(expectedFindResponse));
admin.getUsers(request, response);
var responseData = response._getData();
});
});

Bluebird promises - each function

Thank in advance for the help.
While using Bluebird promises, I have a series of promises running. During the last promise, I want to run one function multiple times for each object in an array.
Below there is the pseudocode:
var userArray = [
{
name: "John",
email: "John#email.com"
},
{
name: "Jane",
email: "jane#email.com"
}];
var functionOne = function() {
//returns Promsie object
};
var functionTwo = function() {
//returns promise object
};
var createUser = function(user) {
return User.findOrCreate({email: user.email},{
name: user.name,
email: user.email
});
};
functionOne()
.then(functionTwo)
.each(createUser(userArray))
.then(function onComplete() {
console.log("Complete");
})
.catch(function onError() {
console.log("Um...it's not working");
});
I know I'm not using the each function correctly. What's the correct way to implement this using Bluebird?
As I understand you want to take some asynchronous actions for elements from array. Then please check the following example:
var Promise = require('bluebird');
function createUsersFromArray(userArray){
return Promise.each(userArray, function(signleUser){
return createUserFunction(signleUser);
});
}
or
return Promise.each(userArray, createUserFunction);
functionOne()
.then(functionTwo)
.then(function(){
return createUsersFromArray(userArray);
})
//or just .then(createUsersFromArray) if functionTwo return this array
.then(function(createdUsers){
//here you may retrieve users and make some magic with them
console.log(createdUsers);
})
.then(function onComplete() {
console.log("Complete");
})
.catch(function onError() {
console.log("Um...it's not working");
});
I also recommend using "all" instead of "each"
Check the examples below:
return Promise.all(userArray.map(function(singleUser){
return doSomethingWithUser(singleUser);
}));
or
return Promise.all(userArray.map(doSomethingWithUser));
'all' will notify you if all the actions are taken correctly.
How to use promises (best practice):
http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
https://blog.domenic.me/youre-missing-the-point-of-promises/
The most straightforward implementation:
functionOne()
.then(functionTwo)
.then(function(){
return bluebird.each(userArray, createUser);
})
.then(function onComplete() {
console.log("Complete");
})
.catch(function onError() {
console.log("Um...it's not working");
});
You should use .map instead of .each if you want to access the results of all of those creates.
Thanks #Roman #Yuri for the help! My now working code is below:
var userArray = [
{
name: "John",
email: "John#email.com"
},
{
name: "Jane",
email: "jane#email.com"
}];
var functionOne = function() {
//returns Promise object
};
var functionTwo = function() {
//returns Promise object
};
var createUser = function(singleUser) {
//returns Promise object containing creating User
};
functionOne()
.then(functionTwo)
.then(function() {
return Promise.map(userArray, createUser);
})
.then(function onComplete(response) {
console.log("Complete:" + JSON.stringify(response));
})
.catch(function onError() {
console.log("Um...it's not working");
});

Sequelize return response with nested loops

I am looping through and inserting elements in my addAll method. Once this has finished, I would like to return a response. My code currently works, but I am not sure how to get the response to happen.
Route
.post(function (req, res) {
var screening_module = Screening_module.build();
screening_module.screening_module = req.body.module;
screening_module.screening_module.organization_id = req.user.organization_id;
screening_module.addAll(function (success) {
res.json({message: 'screening_module created!'});
},
function (err) {
res.status(err).send(err);
}); });
AddAll
addAll: function (onSuccess, onError) {
var screening_module = this.screening_module;
screening_module.selectedUser.forEach(function (user) {
Screening_module.create({
organization_id: screening_module.organization_id,
supervisor: screening_module.supervisor.id,
name: screening_module.name,
description: screening_module.description,
deadline: screening_module.deadline,
category_id: screening_module.category.id,
screening_module_type_id: screening_module.type.id,
user_id: user.id
}).then(function (createdScreeningModule) {
Screening.create({
user_id: createdScreeningModule.dataValues.user_id,
screening_module_id: createdScreeningModule[null]
}).then(function (createdScreening) {
screening_module.selectedSkillsets.forEach(function (skillset) {
Screening_has_skillset.create({
screening_id: createdScreening[null],
skillset_id: skillset.id
})
})
})
})
});
},
Promise is your friend here
var Promise = require("bluebird");
addAll: function (onSuccess, onError) {
var screening_module = this.screening_module;
Promise.each(screening_module.selectedUser, function (user) {
return Screening_module.create({
organization_id: screening_module.organization_id,
supervisor: screening_module.supervisor.id,
name: screening_module.name,
description: screening_module.description,
deadline: screening_module.deadline,
category_id: screening_module.category.id,
screening_module_type_id: screening_module.type.id,
user_id: user.id
}).then(function (createdScreeningModule) {
return Screening.create({
user_id: createdScreeningModule.dataValues.user_id,
screening_module_id: createdScreeningModule[null]
}).then(function (createdScreening) {
return Promise.each(screening_module.selectedSkillsets, function (skillset) {
return Screening_has_skillset.create({
screening_id: createdScreening[null],
skillset_id: skillset.id
});
});
});
});
}).then(onSuccess, onError);
},
Untested, but hopefully give you an idea of how to handle. IMO, this kind of deep nesting is a code smell, and you might need to consider the design again

Resources