Await inside an await doesn't give correct output - javascript

Below is my code where I want to check if a token exists. If yes, then I will check if the wallet owner is the token owner. Problem now is it doesn't check the second function "contract.methods.ownerOf(tokenId).call(function (err, res)" thus the final result is not the correct outcome.
async function doesTokenIdExist(tokenId, contract, walletAddress) {
var tokenExists = false;
await contract.methods.exists(tokenId).call(async function (err, res) {
if (res) {
await contract.methods.ownerOf(tokenId).call(function (err, res) {
if (!err) {
tokenAddress = res.toLowerCase();
walletAddress = walletAddress.toLowerCase();
if (tokenAddress.localeCompare(walletAddress) == 0){
tokenExists = true;
} else {
tokenExists = false;
}
} else {
tokenExists = false;
}
});
} else {
tokenExists = false;
}
});
return tokenExists;
}

Change this,
await contract.methods.exists(tokenId).call(function (err, res) {
to this,
await contract.methods.exists(tokenId).call(async function (err, res) {

Related

Redis client.get not working but client.set is working

async function getRepos(req, res, next) {
try {
console.log('Fetching Data...');
const { username } = req.params;
const response = await fetch(`https://api.github.com/users/${username}`);
const data = await response.json();
const repos = data.public_repos;
// Set data to Redis
await client.setex(username, 3600, repos);
res.send(setResponse(username, repos));
} catch (err) {
console.error(err);
res.status(500);
}
}
// Cache middleware
async function cache(req, res, next) {
const { username } = req.params;
console.log(username)
await client.get(username, (err, data) => {
if (err) throw err;
if (data !== null) {
res.send(setResponse(username, data));
} else {
next();
}
});
}
When this code executes the set function works(checked by using redis-cli).But the get function does not work when I send the request to the api.
You're mixing the Redis client's promise API with the callback API. Don't set a callback if you want to use await and vice versa.
// Cache middleware
async function cache(req, res, next) {
const {username} = req.params;
console.log(username);
const data = await client.get(username);
if (data !== null) {
res.send(setResponse(username, data));
} else {
next();
}
}

How to make the program wait for the if statement to finish before continuing in javascript?

I'm new to Javascript. I want to make this block run after the if statement is finished (asynchronous). The reason I want that is that I want to make some changes to update them if it falls into the if statement
let params = {
TableName: "storepedia-test",
Item: updatedItem
};
docClient.put(params, function (err, data) {
if (err) {
console.log(err);
} else {
res.redirect('/devices');
}
});
Here is my whole code
const { id } = req.params;
const file = req.file;
let updatedItem = { ...req.body};
updatedItem.id = id;
if (file !== undefined){
const deleteParams = {
Key: updatedItem.image,
Bucket: bucketName
}
s3.deleteObject(deleteParams, async (err, data) => {
if (err) {
console.log(err)
} else {
const result = await uploadFile(file);
console.log('result', result);
await unlinkFile(file.path);
updatedItem.image = result.Key;
let params = {
TableName: "storepedia-test",
Item: updatedItem
};
docClient.put(params, function (err, data) {
if (err) {
console.log(err);
} else {
res.redirect('/devices');
}
});
}
})
}
let params = {
TableName: "storepedia-test",
Item: updatedItem
};
docClient.put(params, function (err, data) {
if (err) {
console.log(err);
} else {
res.redirect('/devices');
}
});
Just to run something after the if? I think this is the best spot:
docClient.put(params, function(err, data) {
if (err) {
console.log(err);
} else {
// run async code here.
// when done do the redirect.
// for example:
s3.do_something(function(err, data) {
if (err) {
console.log(err)
} else {
console.log(data)
res.redirect('/devices');
}
})
}
});

SINON - an issue mocking a middleware

I have the following middleware in a Node.js REST API
const Authorized = (req, res, next) => {
if (!req.headers["authorization"]) {
res.status(401).send("Unauthorized");
} else {
jwt.verify(req.headers["authorization"], PRIVATE_KEY, (err, decoded) => {
if (err) {
res.status(403).send("Forbidden");
} else {
req.businessId = decoded.businessId;
req.roleId = decoded.roleId;
next();
}
});
}
};
As you can see I'm adding to variables to the request object
In the mockup of my tests I'm trying to do this:
sandbox = sinon.createSandbox();
sandbox.stub(Authorized, "Authorized")
.callsFake(async (req, res, next) => {
req.businessId = businessAux.id;
return next();
});
But this doesn't work and my actual function to be tested needs this variable:
listPermissionAndRolesByPk() {
this.app.get(`${config.API_PATH}/permissionrolebypk`, Authorized.Authorized, async (req, res) => {
const id = req.query.id;
const businessId = req.businessId;
if (!id) return res.status(400).send(messages[23].message.replace("${object}", "Permission Id"));
try {
const permission = await PermissionDAO.getPermissionAndRolesByPk(id, businessId ? businessId : 0);
if (permission) {
return res.status(200).json(permission);
} else {
return res.status(404).send(messages[8].message.replace("${object}", "Permission"));
}
} catch (error) {
return res.status(error.httpCode).send(error.message);
}
});
}
Any help will be appreciated.

Array is blank after functions ran? [duplicate]

This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 2 years ago.
app.post('/api/edit-profile', regularFunctions, async function (req, res) {
let email = req.body.email
let password_current = req.body.password_current
connection.query('SELECT * FROM accounts WHERE id = ?', req.body.id, async function (err, results) {
if (results.length > 0) {
bcrypt.compare(password_current, results[0].password, async function (err, isMatch) {
if (err) {
res.send('Unable to save settings')
res.end();
throw err
} else if (!isMatch) {
res.send('Password doesn\'t match.')
res.end();
} else {
let changed = []
// Password matches
if (req.body.password_new) {
let newPassword = req.body.password_new
let hashed_password = await hashPassword(newPassword)
connection.query('UPDATE accounts SET password = ? WHERE id = ?', [hashed_password, req.body.id], async function (error, results) {
if (results.affectedRows && results.affectedRows > 0) {
changed.push('password')
} else {
res.send('Unable to save settings')
res.end();
}
})
}
if (req.body.license_key) {
let newKey = req.body.license_key
axios.get(`https://voltcloud.net/api/hosting/check-key/${newKey}`, {
headers: {
authorization: 'Y1wUo3joP99JHiGM2orji0UYTey9gdqY'
}
}).then(function (response) {
let data = response.data
if (typeof data === 'object') {
if (data.active === 1) {
axios({
method: 'post',
url: `https://voltcloud.net/api/hosting/activate-key/${newKey}`,
headers: {
authorization: 'Y1wUo3joP99JHiGM2orji0UYTey9gdqY'
}
}).then(async function (response) {
if (response.data === 'Success') {
connection.query('UPDATE accounts SET license_key = ? WHERE id = ?', [newKey, req.body.id], async function (error, results) {
if (results.affectedRows && results.affectedRows > 0) {
changed.push('license key')
} else {
res.send('Unable to save settings')
res.end();
}
})
} else if (data === 'License already active!') {
res.send('License key is already active!')
res.end();
} else if (data === 'Failed to update key.') {
res.send('Unable to save settings')
res.end();
} else {
res.send('Unable to save settings')
res.end();
}
});
}
}
})
}
connection.query('UPDATE accounts SET email = ? WHERE id = ?', [email,req.body.id], async function (error, results) {
if (results.affectedRows && results.affectedRows > 0) {
changed.push('email')
} else {
res.send('Unable to save settings')
res.end();
}
});
let finalTxt = 'Successfully changed, '
if (changed.length > 1) {
changed.forEach(function (txt, index) {
if (index === 0) {
finalTxt = finalTxt + txt
} else if (index === 2) {
finalTxt = finalTxt + `and ${txt}.`
}
})
} else if (changed.length === 1) {
finalTxt = `Successfully changed ${changed[0]}.`
}
res.send(finalTxt)
res.end();
}
})
}
})
});}
I know this might seem like a very easy problem to some expert coders, but I am sort of new to this whole async and synchronous thing. Why is it that the "changed" array doesn't update even though it's being pushed to after the functions run? What I'm trying to do is have it only return one string that can be shown on the client-side but it doesn't seem to be changing it and only returning the "Successfully changed, "
This function confused me, as it has a lot of responsabilities as Mike 'Pomax' Kamermans pointed out, but I found the problem:
The connection.query method is non-blocking, meaning it will not wait for it's execution to end for it to advance to the next instructions.
When you are using async methods and Promise, it's nice to try and keep consistency over the methods (avoid mixing callback functions and async/await). I've refactored it over what it should look like if using async/await:
app.post('/api/edit-profile', regularFunctions, async function (req, res) {
let email = req.body.email
let password_current = req.body.password_current
let results = await executeQuery(connection, 'SELECT * FROM accounts WHERE id = ?', [req.body.id]);
if (results.length > 0) {
let isMatch = await comparePassword(password_current, results[0].password);
if (!isMatch) {
throw new Error(`Password doesn't match`);
}
let changed = []
// Password matches
if (req.body.password_new) {
let newPassword = req.body.password_new
let hashed_password = await hashPassword(newPassword)
let results = await executeQuery(connection, 'UPDATE accounts SET password = ? WHERE id = ?', [hashed_password, req.body.id]);
if (results.affectedRows && results.affectedRows > 0) {
changed.push('password')
} else {
throw new Error('Unable to save settings');
}
}
if (req.body.license_key) {
let newKey = req.body.license_key
let response = await axios.get(`https://voltcloud.net/api/hosting/check-key/${newKey}`, {
headers: {
authorization: '<redacted>'
}
});
let data = response.data
if (typeof data === 'object') {
if (data.active === 1) {
let response = await axios({
method: 'post',
url: `https://voltcloud.net/api/hosting/activate-key/${newKey}`,
headers: {
authorization: '<redacted>'
}
})
if (response.data === 'Success') {
let results = await executeQuery(connection, 'UPDATE accounts SET license_key = ? WHERE id = ?', [newKey, req.body.id]);
if (results.affectedRows && results.affectedRows > 0) {
changed.push('license key')
} else {
throw new Error('Unable to save settings');
}
} else if (data === 'License already active!') {
throw new Error('License key is already active!');
} else if (data === 'Failed to update key.') {
throw new Error('Unable to save settings');
} else {
throw new Error('Unable to save settings');
}
}
}
}
let results = await executeQuery(connection, 'UPDATE accounts SET email = ? WHERE id = ?', [email,req.body.id]);
if (results.affectedRows && results.affectedRows > 0) {
changed.push('email')
} else {
throw new Error('Unable to save settings');
}
let finalTxt = 'Successfully changed, '
if (changed.length > 1) {
changed.forEach(function (txt, index) {
if (index === 0) {
finalTxt = finalTxt + txt
} else if (index === 2) {
finalTxt = finalTxt + `and ${txt}.`
}
})
} else if (changed.length === 1) {
finalTxt = `Successfully changed ${changed[0]}.`
}
res.send(finalTxt)
res.end();
}
});
function executeQuery(conn, sql, params) {
return new Promise((resolve, reject) => {
conn.query(sql, params, function (err, data) {
if (err != null) {
return reject(err);
}
return resolve(data);
});
});
}
function comparePassword(val1, val2) {
return new Promise((resolve, reject) => {
bcrypt.compare(val1, val2, function (err, isMatch) {
if (err != null) {
return reject(err);
}
resolve(isMatch);
});
})
}
Notice that we're not using callback functions at all, and even where we don't have native Promise-based functions (i.e. mysql connection), we're delegating to a function that proxies the callback to deliver a Promise and keep consistency over the final implementation.
The original code isn't waiting for the two if branches to complete before sending the response. It's hard to structure code like this in callbacks, due to the nesting.
Try using async functions and await wherever possible. It allows for much more readable code and error handling is much easier. So this answer is more code review than a simple fix for your issue.
Split out some generic helper code that will be useful in other routes:
// Generate errors for the web, with context
function responseError(message, status, data){
const error = new Error(message)
error.status = status
for (const key in data){
error[key] = data[key]
}
return error
}
// Turn mysql callbacks into promises (or use util.promisify)
async function runQuery(query, values){
return new Promise((resolve, reject) => {
connection.query(query, values, function(error, results){
if (error) return reject(error)
return resolve(results)
})
})
}
async function runUpdateQuery(query, values){
const results = await runQuery(query, values)
if (!results) throw responseError('No update result', 500, { query })
if (!results.affectedRows) throw responseError('No affected rows', 400, { query })
return results
}
The code from the two if conditions can be easily separated, as well as the other account operations.
async function apiAuthUserId(id, password){
const results = await runQuery('SELECT * FROM accounts WHERE id = ?', id)
if (!results.length) throw responseError('No account', 400, { id })
const isMatch = await bcrypt.compare(password_current, results[0].password)
if (!isMatch) throw responseError('Password doesn\'t match', 400)
return true
}
async function apiUpdatePassword(id, password){
let newPassword = req.body.password_new
let hashed_password = await hashPassword(newPassword)
await runUpdateQuery('UPDATE accounts SET password = ? WHERE id = ?', [hashed_password, req.body.id])
return id
}
async function apiUpdateEmail(id, email){
await runUpdateQuery('UPDATE accounts SET email = ? WHERE id = ?', [email, id])
return email
}
async function apiUpdateLicenseKey(id, licenseKey){
const response_license = await axios.get(`https://voltcloud.net/api/hosting/check-key/${licenseKey}`, {
headers: {
authorization: 'somekey'
}
})
const data = response_license.data
if (!data) {
throw responseError('No license key response data', 500, { response: response_license })
}
if (data.active !== 1) {
throw responseError('License key not active', 400, { key: licenseKey })
}
const response_activate = await axios({
method: 'post',
url: `https://voltcloud.net/api/hosting/activate-key/${licenseKey}`,
headers: {
authorization: 'somekey'
}
})
switch (response_activate.data){
case 'License already active!':
throw responseError('License key is already active!', 400, { response: response_activate })
case 'Failed to update key.':
throw responseError('Unable to save settings!', 400, { response: response_activate })
case 'Success':
await runUpdateQuery('UPDATE accounts SET license_key = ? WHERE id = ?', [licenseKey, req.body.id])
return licenseKey
default:
throw responseError('Unable to save settings!', 500, { response: response_activate })
}
}
Then your route code can be a bit cleaner and show what needs to be done, rather than how to do it all.
app.post('/api/edit-profile', regularFunctions, async function (req, res) {
const changed = []
try {
const { id, email, password_current } = req.body
await apiAuthUserId(id, password_current)
// Password matches
if (req.body.password_new) {
await apiUpdatePassword(id, req.body.password_new)
changed.push('password')
}
// License key
if (req.body.license_key) {
await apiUpdateLicenseKey(id, req.body.license_key)
changed.push('license key')
}
await apiUpdateEmail(id, email)
changed.push('email')
let finalTxt = `Successfully changed ${changed.join(' and ')}.`
res.send(finalTxt)
}
catch (error) {
// If your not using transactions, might need to deal with partial `changed` responses here.
console.error('Error /api/edit-profile', error)
res.status(error.status||500).send(`Error: ${error.message}`)
}
});

Getting result from MySQL

My backend is consist of Api and DB. When I want to get response from DB I have had delayed output by 1 query.
API (I think api is ok. Start read DB first)
app.post('/api/query', (req, res) => {
console.log(`\n Query input : ${JSON.stringify(req.body)}`);
let queryInput = (Object.values(req.body).join(' '));
if(!dbApi.checkArray(queryInput)){ //If array is not made from clear strings
res.json(dbApi.queryFromUser(queryInput));
}
else{
res.json(dbApi.queryOutput);
}
});
app.listen(dbConfig.server.port, () =>
console.log(`Server running on port ${dbConfig.server.port}`));
DB
queryOutput = [];
const receivingQuery =(queryInput) => {
db.query(queryInput, (err, result) =>{
if(err) throw err+' : '+queryInput;
queryOutput = result;
console.log("\nQuery output "+ JSON.stringify(queryOutput)); //Output (result) is ok
});
return queryOutput //Here is Output from previous query (sends to API)
}
module.exports = {
queryOutput: queryOutput,
queryFromUser: receivingQuery,
}
I tryied callback method and I rewrite it couple of times. But I dont have enough skill to solve it.
If You want to return result of query so simply do following things:
add query method to db module:
function query(sql, args = []) {
return new Promise(function(resolve, reject) {
db.query(sql, args, (err, result) => {
if (err) return reject(err);
resolve(result);
});
});
}
// extra feature, getting user by id
async function getUserById(id) {
const result = await query('SELECT * FROM users WHER id = ? LIMIT 1', [id]);
if (Array.isArray(result) && result[0]) return result[0];
return null;
}
module.exports = {
query,
getUserById, // export user by id
queryOutput,
queryFromUser: receivingQuery,
}
use it (with async and await):
app.post('/api/query', async (req, res) => {
try {
console.log('Query input:', req.body);
const queryInput = Object.values(req.body).join(' ');
const result = await dbApi.query(queryInput);
res.json(result);
}
catch (error) {
console.error(error);
res.status(500).json({message: 'Please try again soon'});
}
});
app.get('/api/users/:id', async (req, res) => {
try {
const user = await dbApi.getUserById(req.params.id);
if (!user) return res.status(404).json({message: 'User not found'});
res.status(200).json(user);
}
catch (error) {
console.error(error);
res.status(500).json({message: 'Please try again soon'});
}
});
app.listen(dbConfig.server.port, () =>
console.log('Server running on port', dbConfig.server.port));

Categories

Resources