I have about 7 hours and dozens of try's to get a AWS Secret. I have followed the AWS example code as well as other examples found here and on other sites. My current problem is the code returns a Promise in my variable 'p', not the plaintext secret string. I'm pulling my hair out here... If it is returning a promise while still pending a resolve; I don't know why it does not wait. The calling code is in a sequelize.config file and not wrapped in an exported function--thus the async function () => below.
getSecrets.js
const AWS = require ('aws-sdk')
const sm = new AWS.SecretsManager({region: 'us-east-1'});
export default async function getSecret (secretId) {
const data = await sm.getSecretValue({
SecretId: secretId
})
.promise();
console.log ("returning SecretString:", data.SecretString)
return data.SecretString;
Calling code:
if (RUN_ENV != 'local') {
const p = async function () {
await getSecrets (DB_PASSWORD)
.then (function ( value ) {
return value;
})
.catch ((error) => {
console.log ("AWS getSecrets error: ", error);
})
}
password = p;
}
Here's a simple way to get secret strings from Secrets Manager:
const AWS = require('aws-sdk');
const client = new AWS.SecretsManager({ region: "us-east-1" });
const getMySecret = async (SecretId) => {
const s = await client.getSecretValue({ SecretId }).promise();
return s.SecretString;
};
// Async IIFE
(async() => {
const secret_101 = await getMySecret('secret-101');
console.log('My secret:', secret_101);
})();
Your original problem, as #GalAbra suggested, is that your code has password = p when it needs to be const password = p(). Specifically, rather than taking a reference to the function, it needs to invoke the function.
Related
I'm working on a local Node.js app that needs to access the Google Chrome cookies. I've found the chrome-cookies-secure library that seems to do the job but I just can't figure out what's wrong with the code below.
const chrome = require('chrome-cookies-secure');
const domains = [
"google.com"
];
const resolveCookies = async () => {
let result = [];
for(domain of domains) {
await chrome.getCookies(`https://${domain}/`, (err, cookies) => {
result.push(cookies);
// console.log(cookies); //? This is going to correctly print the results
})
}
return result;
}
const final = resolveCookies();
console.log(final); //! This is going to return a Promise { <pending> } object
The idea is that I just want to store the cookies from all the domains in a list but no matter what I cannot resolve the Promise.
I didn't see any examples with the async call for this module but if I don't use it it's going to return me an empty list after the script execution.
My Node Version: v14.4.0
What am I doing wrong?
It looks like the implementation of getCookies is not correctly awaiting the asynchronous processes. You can see in the implementation that although getCookies itself is async, it calls getDerivedKey without awaiting it (and that function isn't async anyway).
Rather than trying to rely on this implementation, I'd suggest using Util.promisify to create a proper promise via the callback:
const util = require('util');
const chrome = require('chrome-cookies-secure');
const getCookies = util.promisify(chrome.getCookies);
// ...
const cookies = await getCookies(`https://${domain}/`);
Note that, as Reece Daniels pointed out in the comments, the getCookies implementation actually takes a profile parameter after the callback; if you need to use that parameter, you can't use the built-in promisify. You'd have to wrap it yourself instead, this could look like e.g.:
const getCookies = (url, format, profile) => new Promise((resolve, reject) => {
chrome.getCookies(url, format, (err, cookies) => {
if (err) {
reject(err);
} else {
resolve(cookies);
}
}, profile);
});
They already tried to fix the promise upstream, but the PR hasn't been merged in nearly nine months.
Note that once you have a working function to call you can also convert:
const resolveCookies = async () => {
let result = [];
for(domain of domains) {
await chrome.getCookies(`https://${domain}/`, (err, cookies) => {
result.push(cookies);
// console.log(cookies); //? This is going to correctly print the results
})
}
return result;
}
to simply:
const resolveCookies = () => Promise.all(domains.map((domain) => getCookies(`https://${domain}/`)));
An async function returns a Promise.
So your resolveCookies function will also return a Promise as you noticed.
You need to either chain the console.log with a .then e.g.
resolveCookies().then(console.log);
Or if you need to set it to a variable like final you need to await that Promise too. In that case you need an async IIFE:
(async () => {
const final = await resolveCookies();
console.log(final);
})();
try this.
const chrome = require('chrome-cookies-secure');
const domains = [
"www.google.com"
];
const resolveCookies = async() => {
let result = [];
for (domain of domains) {
const cookies = await getCookies(domain)
result.push(cookies)
}
return Promise.resolve(result);
}
const getCookies = async (domain) => {
chrome.getCookies(`https://${domain}/`, (err, cookies) => {
return Promise.resolve(cookies);
})
}
resolveCookies().then((resp) => {
console.log('FINAL ',resp)
}).catch((e) => {
console.log('ERROR ', e)
})
I'm working with Ethereum blockchain, but my problem my is JavaScript (async, await function).
Here my code simplified:
In my html
App.addBlockChain(n.username,n.first,n.last,n.email).then(value => {
**//here I need the hash of my transaction**
}).catch(error => {
alert("Errore: " + error );
});
In my App.js file
addBlockChain: async(u,n,c,e) => {
let hash;
const web3 = new Web3(App.web3Provider);
const signed = await web3.eth.accounts.signTransaction(options, account.privateKey);
const receipt = await web3.eth.sendSignedTransaction(signed.rawTransaction)
.on('transactionHash', function(hash_returned){
//I need this hash hash_returned as soon as possible in my html ***
hash= hash_returned;
})
.on('receipt', function(receipt){... })
.on('confirmation', function(confirmationNumber, receipt){ ... })
.on('error', console.error); // If a out of gas error, the second parameter is the receipt.;
return hash; //it is returned only when on('confirmation') is terminated
Any help with any code of example?
Thanks a lot in advance.
Welcome to the fantastic world of asynchronism... One way to do this would be :
const hash_returned = await App.addBlockChain(n.username, n.first, n.last, n.email);
and in your App class :
addBlockChain: async(u, n, c, e) => {
const web3 = new Web3(App.web3Provider);
const signed = await web3.eth.accounts.signTransaction(options, account.privateKey);
return new Promise(resolve => { // addBlockChain must return a Promise, so it can be "await"ed
web3.eth.sendSignedTransaction(signed.rawTransaction)
.on('transactionHash', function(hash_returned) {
resolve(hash_returned); // now that you have hash_returned, you can return it by resolving the Promise with it
})
// or more simply (equivalent) :
// .on('transactionHash', resolve)
})
}
After I have successfully implemented one of methods to fetch some data and run test
Code:
const fetchData = async (url) => {
const response = await axios.get(url);
const contentType = response.headers['content-type'];
if (typeof response.data === 'object') {
return JSON.stringify(response.data);
}
throw new Error('Content-Type is unrecognized');
};
module.exports = fetchData;
And test:
describe('fetchData', () => {
it('should return json string response data on successful request', async () => {
const responseData = await fetchData(url);
const expectedData = JSON.stringify({ key1: 'value1' });
assert.deepEqual(responseData, expectedData, 'Response data doesn\'t match');
});
However, I wanted to implement scheduling to my method. I implemented in by using node-scheduler npm module.
After my modification
scheduler.scheduleJob({ start: startTime, end: endtTime }, async () => {
const fetchData = async (url) => {
const response = await axios.get(url);
}
Tests are failing immadietly, furthermore I noticed that error log is going continuously, therefore I have to kill test.
Does anyone have an idea why adding simple scheduler makes my error not working? I am using:
Node v.8.11.4
chai-as-promised
nock
I have a function, startSurvey, which, when run, checks if there are questions in a .json file. If there are no questions, it fetches some questions from Typeform and writes them to the .json file using saveForm. After it writes, I would like to continue executing some code that reads the .json file and logs its contents. Right now, await saveForm() never resolves.
I have promisified the fs.readFile and fs.writeFile functions.
//typeform-getter.js
const fs = require('fs')
const util = require('util')
const fetch = require('cross-fetch')
require('dotenv').config()
const conf = require('../../private/conf.json')
const typeformToken = conf.tokens.typeform
const writeFile = util.promisify(fs.writeFile)
const getForm = async () => {
const form = await fetch(`https://api.typeform.com/forms/${process.env.FORM_ID}`, {
headers: {
"Authorization": `bearer ${typeformToken}`
}
}).then(res => res.json())
const fields = form.fields
return fields
}
const saveForm = async () => {
const form = await getForm()
return writeFile(__dirname + '/../data/questions.json', JSON.stringify(form))
.then((e) => {
if (e) console.error(e)
else console.log('questions saved')
return
})
}
module.exports = saveForm
//controller.js
const fs = require('fs')
const util = require('util')
const request = require('request')
require('dotenv').config()
const typeformGetter = require('./functions/typeform-getter')
const readFile = util.promisify(fs.readFile)
const saveForm = util.promisify(typeformGetter)
let counter = 1
const data = []
const getQuestions = async() => {
console.log('called')
try {
let data = await readFile(__dirname + '/data/questions.json')
data = JSON.parse(data)
return data
} catch (e) {
console.error('error getting questions from read file', e)
}
}
const startSurvey = async (ctx) => {
try {
const questions = await getQuestions()
if (!questions) await saveForm()
console.log(questions) //NEVER LOGS
} catch (error) {
console.error('error: ', error)
}
}
startSurvey() //function called
I don't know your exact error, but there are multiple things wrong with your code:
You're using incorrectly the promisified version of fs.writeFile, if an error occurs, the promise will be rejected, you won't get a resolved promise with an error as the resolved value, which is what you're doing.
Use path.join instead of concatenating paths.
In startSurvey, you're using console.log(questions) but that wont have any data if questions.json doesn't exists, which should happen the first time you run the program, since it's filled by saveForm, so you probably want to return the questions in saveForm
So saveForm should look something like this:
const saveForm = async () => {
const form = await getForm();
const filePath = path.join(path.__dirname, '..', 'data', 'questions.json');
await writeFile(filePath, JSON.stringify(form));
console.log('questions saved');
return form;
}
And startSurvey
const startSurvey = async (ctx) => {
try {
const questions = await getQuestions() || await saveForm();
// This will be logged, unless saveForm rejects
// In your code getQuestions always resolves
console.log(questions);
} catch (error) {
console.error('error: ', error)
}
}
In your controller.js you're using util.promisify on saveForm when it is already a promise.
So it should be:
const saveForm = require('./functions/typeform-getter')
In node.js, I have a database transaction, where I want to call an async method in then callback, but I get error message the keyword 'await' is reserved.
This is async saveImage function:
const saveImage = async (parsedLink) => {
AWS.config.region = config.awsSettings.region;
AWS.config.accessKeyId = config.awsSettings.accessKeyId;
AWS.config.secretAccessKey = config.awsSettings.secretAccessKey;
const bucket = new AWS.S3({
params: {
Bucket: config.awsSettings.images_bucket_name,
},
});
const currentDateString = new Date().toISOString().replace(/\:|\./g, '-');
const bodystream = new Buffer(parsedLink.imgUrl, 'binary');
const imageUrlDomain = parseDomain(parsedLink.linkUrl).domain;
const params = {
Key: `${parsedLink.id}/${imageUrlDomain}_${currentDateString}${parsedLink.imgType}`,
ContentType: parsedLink.imageMime,
ContentEncoding: 'base64',
Body: bodystream,
};
const resultPromise = await bucket.upload(params).promise();
return resultPromise.Location;
};
If I want to use saveImage function, I get the error message.
module.exports.addTestObject = async (ctx) => {
const testObj = ctx.request.body;
try {
switch (testObj.type) {
case interestT.getType.link: {
const knexTestObject = TestObject.knex();
transaction(knexTestObject, trx =>
TestObject.query(trx)
.insert({
interestDate: testObj.date,
})
.then(newInterest => {
// save image
if (parsedLink.saveImg) {
parsedLink.imgUrl = await saveImage(testObj);
}
newInterest.$relatedQuery('linkInterestsRel', trx).insert({
linkHeader: testObj.linkHeader,
}),
}
),
)
.then((linkInterest) => {
console.log(linkInterest);
})
.catch((err) => {
throw err;
});
break;
}
default:
break;
}
ctx.response.body = interestObj;
} catch (err) {
const statusCode = err.status || 400;
ctx.throw(statusCode, err.message);
}
};
Regular functions run synchronously till they return. Therefore you cannot use await inside them as you cannot wait for an asynchronous event in a synchronous way.
JavaScript also has async functions, which look like regular functions, but are conceptually quite different: They run synchronously till they reach an await, then they stop and continue once the awaited Promise resolves. As such they cannot return their result synchronously, instead they return a Promise which then resolves when the function finished execution.
Therefore you need to convert your function into an async function:
async function getUsername() { // <-- async keyword here
return (await getUser()).name; // <-- await can be used inside
}
Now this does also work inside a .then callback:
getUser().then(async function(user) {
const friends = await getFriends(user);
// ...
})
But this somewhat mixes the abstraction async functions with their underlying primitive Promise. If you would just await the Promise instead of adding a .then callback, the code gets way more readable:
(async function() {
const user = await getUser();
const friends = await getFriends(user);
})();
The concrete question could be rewritten as:
const linkInterest = await transaction(knexTestObject, async trx => {
const newInterest = await TestObject.query(trx)
.insert({ interestDate: testObj.date, });
if (parsedLink.saveImg) {
parsedLink.imgUrl = await saveImage(testObj);
}
await newInterest.$relatedQuery('linkInterestsRel', trx)
.insert({ linkHeader: testObj.linkHeader, }),
});