Retrieving first_name from Facebook Messenger results in [object Promise] - javascript

I'm writing a simple query to return the first name of a user of my Facebook Messenger chat bot, see below:
async queryFB(id) {
const fb_page_access_token = dotenv.FACEBOOK_ACCESS_TOKEN
const response = await get("https://graph.facebook.com/v3.3/"+ id + "?fields=first_name&access_token=" + fb_page_access_token);
const json = await response.json();
return json.first_name
}
async fbFirstName() {
const fbUserID = session.user.id
try {
const firstName = await queryFB(fbUserID);
console.log(firstName);
} catch(e) {
console.log("Error: " + err);
}
}
I was following this post here
The problem is that it only returns [object Promise]. I thought the solve for this was to use async and await but I still have the same problem.

After a lot of fiddling with the code I managed to solve this as follows:
function fbFetch() {
const fb_page_access_token = process.env.FACEBOOK_ACCESS_TOKEN
var fbID = 716540089
fetch('https://graph.facebook.com/v3.3/' + fbID + '?fields=first_name&access_token=' + fb_page_access_token)
.then(function(response) {
return response.json();
})
.then(function(myJson) {
var fbName = JSON.stringify(myJson.first_name);
var fbNameTrim = fbName.slice(1,-1);
console.log(fbNameTrim);
turnContext.sendActivity("Hi " + fbNameTrim + "! From inside fbFetch() before return fbNameTrim");
return fbNameTrim;
})
catch(e) {
console.log("Error: " + err);
}
}
The changes I made are as follows:
I updated the const fb_page_access_token call as it my syntax was wrong in the original post
I added .then statements to ensure that each step of the function was completed before moving on to the next to resolve some [object Promise] issues I was having
I used stringify to turn the JSON object into a string
I have used the slice function to take off the first and last characters of the string that is returned for first_name as they were inverted commas (“first_name”)

Related

Promise returns undefined nodejs

i am back with a same issue for my promise returning undefined please help.
Here, i am using ipfs to save data with savedata() which takes in a json string,a document name and a secret phrase.
i am not quit sure why promise is returning undefined i have checked everything
here is the new code
index.js
exports.savedata = async function savedata(jsondata,Docname,secretuuid){
const docname = Docname
let ipfss = await main();
let datavar = await ipfss.add(jsondata);
//check if input file exists or not?
const fpath = __dirname + "\\"+ "input.json"
if (fs.existsSync(fpath)) {
}
else{
const defobj = {defaultID:"defaultID"}
fs.writeFile("input.json",JSON.stringify(defobj),function(err){
// console.log('saved!')
})
}
//create an object and put an array with defaultid:defaultid to it
//take that object and keep concatenating the new arrays[new documents]
fs.readFile("input.json","utf-8",function(err,data){
if(err){
console.log(err)
}
const rembrk1 = data.replaceAll("{","")
const rembrk2 = rembrk1.replaceAll("}","")
const newstring = JSON.stringify({[docname]: datavar.path})
const URK = uuidv4() + "rkbyavds"
const CAT = CryptoJS.AES.encrypt(String(datavar.path),secretuuid);
var ENCAT = CAT.toString()
const fstring = "{" + rembrk2 + "," + docname + ":" + CAT + "}"
fs.writeFile("input.json", JSON.stringify(fstring),function(err){
if(err){
console.log(err)
}
})
return new Promise((resolve,reject) => {
// console.log('saved')
const retobj = {CAT:ENCAT,URK:URK}
resolve(retobj)
});
})
}
test.js
obj = JSON.stringify({user:"MqwMedz2edemusaaa",age:1291})
const op = savedata(obj,"doc1","verysecretuuid")
op.then(x=>{
console.log(x)
})
Well, the RegisterUser function executes the actions and retrieves the data. What it doesn't do is return any value. This means it'll return a Promise by default, but this Promise won't have any value to resolve.
Depending on which object you want to have returned, you need to return it or create and return a new Promise from which you can call the resolve-function.
You can read more about it here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

How to adjust variable based on fetch result

I am working on a script that will fetch posts from a paginated REST API provided by a website managed by WordPress.
What I realized was that WordPress REST API is paginated and got a cap of 100 objects per request.
Within the code below, I am trying to fetch posts page by page and with an amount of 20 posts per page. In the end, I am joining all the fetched objects into one big object.
My problem is, that the fetch fails with HTTP 404 response, since the last request contain less than 20 posts.
I would like to adjust the variable named 'limitPerPage', if the fetch returns with a 404 and decrement the variable until i get a 200 HTTP response.
My challenge is, that I not experienced working with fetch promise.
Please see my current script below:
console.log('REST API is this: ' + apiUrl);
const getPosts = async function(pageNo = 1) {
let limitPerPage = 20;
let requestUrl = apiUrl + `?page=${pageNo}&per_page=${limitPerPage}`;
let apiResults = await fetch(requestUrl)
.then(function(response){
return response.json();
})
.catch(function(error){
console.log(error.status);
});
return apiResults;
}
const getEntirePostList = async function(pageNo = 1) {
const results = await getPosts(pageNo);
console.log('Retreiving data from API for page : ' + pageNo);
if (results.length > 0) {
return results.concat(await getEntirePostList(pageNo+1));
} else {
return results;
}
};( async () => {
const entireList = await getEntirePostList();
console.log(entireList);
})
();
I expect the code to decrement the variable 'limitPerPage' by 1, if the fetch returns a 404 HTTP response.
I do not necessary ask for a final solution to my problem. I would appreciate a suggestion to another way for me to structure my code, to get the result I need.
Thanks!
I think the following code should work. Always use try...catch block inside async functions
let entireList = [];
let finishEvent = new Event('finished');
document.addEventListener('finished', function (e) {
console.log(entireList);
}, false);
const getPosts = function (pageNo = 1) {
let limitPerPage = 20;
let requestUrl = `${apiUrl}?page=${pageNo}&per_page=${limitPerPage}`;
return fetch(requestUrl)
.then(function (response) {
return response.json();
})
.catch(function (error) {
console.log(error.status);
return false;
});
}
const getEntirePostList = async function (pageNo = 1) {
try {
const results = await getPosts(pageNo);
console.log('Retreiving data from API for page : ' + pageNo);
if (results && (results.length > 0)) {
entireList.concat(results);
getEntirePostList(pageNo + 1);
} else {
document.dispatchEvent(finishEvent);
}
return;
} catch(e) {
console.log(e)
}
};
getEntirePostList();
I managed to solve the issue myself with the help from the suggestions above. Please find the solution here:
// Constant variable with the assigned value of a joined string containing the base URL structure of the REST API request.
const apiUrl = websiteUrl + '/wp-json/wp/v2/punkt/';
// Logging out the base URL of the REST API.
console.log('REST API is this: ' + apiUrl);
// Local variable with the default value of true assigned. Variable is used to control the paginated fetch of the API.
let keepfetchingPosts = true;
// Local variable that contains the limit of posts per request.
let limitPerPage = 20;
// Constant variable, which is assigned a function as the value. The function is async and will return a promise.
// The function takes one argument. The argument holds the value of the page number.
const getPosts = async function(pageNo = 1) {
// Local variable assigned with the base URL string of the REST API request. Additional queries are added to the end of the string.
let requestUrl = apiUrl + `?page=${pageNo}&per_page=${limitPerPage}`;
// Logging out the REST API request
console.log('URL is this: ' + requestUrl);
// Logging out the argument 'pageNo'
console.log('Retreiving data from API for page : ' + pageNo);
// Local variable assigned with a fetch function that returns a promise. The URL request are used as the function argument.
let apiResults = await fetch(requestUrl)
// If request is success, then log and return the following to the local variable.
.then(function(response){
// Logging out the status code of the response.
console.log('HTTP response is: ' + response.status);
// return JSON and status code of the XHR request
return {
data: response.json(),
status: response.status
}
})
// Catch the error and log it out within the console.
.catch(function(error){
console.log('HTTP response is: ' + error.status)
});
// If the length of the request is less than the limitPerPage variable and status code is 200, then...
if (apiResults.length < limitPerPage && apiResults.status === 200){
// Set the boolean to false
keepfetchingPosts = false;
// Return the JSON of the successfull response.
return apiResults.data;
} else if (apiResults.status === 200) {
// If the status code is 200, then return the JSON of the successfull response
return apiResults.data;
} else {
// Otherwise, set the boolean to false
keepfetchingPosts = false;
}
}
// Defining a constant variable that holds and async fynction. An async functon will always return a promise.
// The function takes one argument, which is set to 1 by default.
const getEntirePostList = async function(pageNo = 1) {
// Try and catch statement used to handle the errors that might occur.
try {
// Constant variable which is set to the return variable of the function getPost(). Get post returns the successfull paginated response of the request.
const results = await getPosts(pageNo);
// Logging out a string including the length of the array.
console.log('Current array contain ' + results.length + ' items...');
// Conditional statement that checks if the length of the array named 'results' is less than the variable named limitPerPage. Condition is also checked, if bolean is true.
// If the conditions are met, the code will join the arrays into one big array.
if (results.length < limitPerPage && keepfetchingPosts === true) {
// Logging out a string that indicates an attempt to combine that last array to the existing array.
console.log('Combining last array!');
// Return the combined array.
return results;
} else if (keepfetchingPosts === true) {
// Logging out a string that indicates an attempt to combine the recent fetched array to the existing array.
console.log('Combining arrays!');
// Returning the new combined array and increments the pageNo variable with 1.
return results.concat(await getEntirePostList(pageNo+1));
} else {
// Logging out a string that indicates the script will stop fetching more posts from the REST API.
console.log('Stop fetching posts and return results');
// Returning the complete array.
return results;
}
// Catch statement that takes the argument of the error that occured.
} catch(error) {
// Logging out the error.
console.log(error);
}
};( async () => {
// Constant variable with the assigned value received from the function
const entireList = await getEntirePostList();
// Logging out the enite list of results collected from the REST API
console.log(entireList);
})
();
The code above returns a complete array of the JSON response from all paginated REST API calls.
You could use a while loop, and decrement limitPerPage if the status code isn't 200:
console.log('REST API is this: ' + apiUrl);
const getPosts = async pageNo => {
let limitPerPage = 20;
let requestUrl = apiUrl + `?page=${pageNo}&per_page=${limitPerPage}`;
let res = { status: 0 }
while (res.status !== 200) {
res = await fetch(requestUrl)
.then(r => ({ data: r.json(), status: r.status }))
.catch(({status}) => console.log(status))
limitPerPage--
}
return res.data
}
const getEntirePostList = async (pageNo = 1) => {
const results = await getPosts(pageNo);
console.log('Retreiving data from API for page : ' + pageNo);
return results.length > 0
? results.concat(await getEntirePostList(pageNo + 1))
: results;
}
(async () => {
const entireList = await getEntirePostList();
console.log(entireList);
})()

Async/ await in Firebase Cloud Function says Error Expression has type `void`

I'm trying to use async/await in my firebase function but I am getting an error. I have marked the function as async but when I try to use await inside of it I get error: Expression has type void. Put it on its own line as a statement.
I think this is strange because I think it each await function call is already in its own line as a statement. So, I'm not sure what to do. Any help would be appreciated.
For clarity, I am making a request with the Cheerio web scrape library, and then trying to make two async function calls during each loop with the .each method.
export const helloWorld = functions.https.onRequest((req, response) => {
const options = {
uri: 'https://www.cbssports.com/nba/scoreboard/',
transform: function (body) {
return cheerio.load(body);
}
};
request(options)
.then(($) => {
$('.live-update').each((i, element) => {
const homeTeamAbbr = $(element).find('tbody').children('tr').eq(0).find('a').html().split("alt/").pop().split('.svg')[0];
const awayTeamAbbr = $(element).find('tbody').children('tr').eq(1).find('a').html().split("alt/").pop().split('.svg')[0];
const homeTeam = $(element).find('tbody').children('tr').eq(0).find('a.team').text().trim();
const awayTeam = $(element).find('tbody').children('tr').eq(1).find('a.team').text().trim();
let homeTeamStatsURL = $(element).find('tbody').children('tr').eq(0).find('td').html();
let awayTeamStatsURL = $(element).find('tbody').children('tr').eq(1).find('td').html();
const gameTime = $(element).find('.pregame-date').text().trim();
homeTeamStatsURL = homeTeamStatsURL.match(/href="([^"]*)/)[1] + "roster";
awayTeamStatsURL = awayTeamStatsURL.match(/href="([^"]*)/)[1] + "roster";
const matchupString = awayTeamAbbr + "#" + homeTeamAbbr;
const URLString = "NBA_" + urlDate + "_" + matchupString;
// var docRef = database.collection('NBASchedule').doc("UpcommingSchedule");
// var boxScoreURL = "www.cbssports.com/nba/gametracker/boxscore/" + URLString;
// var setAda = docRef.set({[URLString]:{
// homeTeam: homeTeam,
// awayTeam: awayTeam,
// date: gameTime,
// homeTeamAbbr: homeTeamAbbr,
// awayTeamAbbr: awayTeamAbbr,
// homeTeamStatsURL: homeTeamStatsURL,
// awayTeamStatsURL: awayTeamStatsURL,
// boxScoreURL: boxScoreURL
// }}, { merge: true });
getTeamPlayers(homeTeamStatsURL, matchupString);
getTeamPlayers(awayTeamStatsURL, matchupString);
console.log("retrieved schedule for "+ matchupString + " on " + urlDate)
});
response.send("retrieved schedule");
})
.catch(function (err) {
console.log("error " + err);
});
});
The function I am calling just makes another request and then I'm trying to log some data.
function getTeamPlayers(playerStatsURL, matchupString) {
const options = {
uri: playerStatsURL,
transform: function (body) {
return cheerio.load(body);
}
};
console.log(playerStatsURL + " stats url");
request(options)
.then(($) => {
console.log('inside cheerio')
$('tbody').children('tr').each(function(i, element){
const playerName = $(element).children('td').eq(1).children('span').eq(1).find('a').text().trim();
const injury = $(element).children('td').eq(1).children('span').eq(1).children('.icon-moon-injury').text().trim();
const news = $(element).children('td').eq(1).children('span').eq(1).children('.icon-moon-news').text().trim();
const playerUrl = $(element).children('td').eq(1).children('span').eq(1).find('a').attr('href');
const playerLogsUrl = "https://www.cbssports.com" + playerUrl.replace('playerpage', 'player/gamelogs/2018');
console.log(playerName + ": Inj: " + injury + " News: " + news);
// database.collection('NBAPlayers').add({[playerName]:{
// '01 playerName': playerName,
// '03 playerLogsUrl': playerLogsUrl,
// '04 inj': injury,
// '05 news': news
// }})
// .then(docRef => {
// console.log("ID " + docRef.id);
// //getPlayerLogs(playerLogsUrl, playerName, docRef.id);
// })
// .catch(error => console.error("Error adding document: ", error));
});
});
}
async/await is supported in the Node version 8 (which is deployable as Cloud Functions for Firebase). Specifically, use 8.6.1 (at the time of this writing).
As for awaiting x2 inside a loop - I don't think this is best practice.
Instead, push all these requests into an array, then Promise.all so as to fetch all in parallel.
just in case someone comes after. As Ron stated before, you shouldn't use async/await inside a traditional for loop if the second call doesn't need the value of the first, as you will increase the run time of the code.
Furthermore you can't use async/await inside of a forEach=> or a map=> loop, as it won't stop and wait for the promise to resolve.
The most efficient way is to use Promise.all([]). Here I leave a great youtube video of a crack that explains async/await and Promises => https://www.youtube.com/watch?v=vn3tm0quoqE&t=6s
As to one of the questions in the comments by DarkHorse:
But all I want is for getTeamPlayers() to execute and write to the database so I don't know why I need to return anything.
In Firebase Functions all functions need to return something before the final response.
For example in this case, he has created an http function. Before he ends the function with response.send("retrieved schedule"); you need to finish each and every function triggered. As Firebase Functions will clean up after the final response, erasing and stoping anything that it is still running. So any function that hasn't finish will be killed before doing its job.
Returning a promise is the way Firebase Functions knows when all executions have finished and can clean up.
Hop it helps :)

JavaScript async returns 'then not defined'

Am new to async programming, have read similar threads which all seem to have the same problem of not returning anything, whereas mine does...?
However I am still getting the error message 'Cannot read property 'then' of undefined'
function getWorkItems(iterationPath, projectId) {
var queryClient = VSS_Service.getCollectionClient(TFS_Wit_QueryAPI.WorkItemTrackingHttpClient);
var query = { query: "SELECT [System.Id] FROM WorkItem WHERE [System.IterationPath] = '" + iterationPath + "'" };
var resultOfQuery;
queryClient.queryByWiql(query, projectId).then(
function (resultOfQuery) {
return new Promise((resolve, reject) => {
resolve(resultOfQuery);
console.log("Debug: " + JSON.stringify(resultOfQuery));
})
VSS.notifyLoadSucceeded();
});
}
The above prints the debug message fine, it gets the data from the server, then errors with this when I call it elsewhere
let bar;
getWorkItems(counter.path, projectId).then ( res => {
bar = res;
console.log("Debug: should be output of query " + JSON.stringify(bar));
})
Cannot read property 'then' of undefined
getWorkItems doesn't have a return statement in it, so it's implicitly returning undefined. Also, since queryClient.queryByWiql(query, projectId) is already returning a promise, you don't need to create a promise of your own and can do the following:
function getWorkItems(iterationPath, projectId)
{
var queryClient = VSS_Service.getCollectionClient(TFS_Wit_QueryAPI.WorkItemTrackingHttpClient);
var query = { query: "SELECT [System.Id] FROM WorkItem WHERE [System.IterationPath] = '" + iterationPath + "'" };
return queryClient.queryByWiql(query, projectId);
}
If you wanted to keep that log statement and notifyLoadSucceeded in there, you can, but you still don't need to create your own promise. If you're in a .then callback, you just need to return the value that you want the promise to resolve as:
function getWorkItems(iterationPath, projectId)
{
var queryClient = VSS_Service.getCollectionClient(TFS_Wit_QueryAPI.WorkItemTrackingHttpClient);
var query = { query: "SELECT [System.Id] FROM WorkItem WHERE [System.IterationPath] = '" + iterationPath + "'" };
return queryClient.queryByWiql(query, projectId)
.then(function(resultOfQuery) {
console.log("Debug: " + JSON.stringify(resultOfQuery));
VSS.notifyLoadSucceeded();
return resultOfQuery;
});
}

How can I make sure my promise from postgres gets resolved before proceding?

Im making a Node-js app which needs to look up some info from the database before proceeding with another db-call, but I cant seem to make sure that the first check is resolved before proceeding. How can I make sure that the first query is always fulfilled before proceeding? I've tried nesting .next, but they seem to be skipping anyway. So after the first DB-call I want to check the returned value, but its always undefined.
my async function:
async function GetStuff(text, id){
try {
return await db.query(text, id);
} catch(error) {
console.log('fel inne: ' + error)
logger.error('Fel: ' + error)
return null;
}
}
My Controller-code where I call the method and then try await the call with .next.
router.post('/newStuff', async (req, res) => {
//Check if number exist in DB
const checkUserText = 'select * from public.User WHERE Mobilenumber = $1 limit 1';
const values = [req.body.from];
GetStuff(checkUserText, values)
.then(function(result) {
var user = result.rows[0];
//HERE I GET UNDEFINED for some reason. I need to check the result in order to select the next step.
if(user.userid !== null){
console.log(user)
//do some stuff...
}
else {
res.end('Not OK')
}
}).catch(function(error) {
console.log('fel: ' + error)
logger.error('Fel: ' + error)
res.end('Error')
});
})
Instead of returning null from GetStuff, you should probably throw either the original error or a new error. This will then cause GetStuff.catch to be triggered if something goes wrong with the database call.
Just a tip as well, your controller function is async so you don't need to use Promise based structure in your controller code. You can also use async/await.
With both of those, you'd end up with the following code:
async function GetStuff(text, id){
try {
return await db.query(text, id);
} catch(error) {
console.log('fel inne: ' + error)
logger.error('Fel: ' + error)
throw new Error('Failed to get db record');
}
}
router.post('/newStuff', async (req, res) => {
//Check if number exist in DB
const checkUserText = 'select * from public.User WHERE Mobilenumber = $1 limit 1';
const values = [req.body.from];
let dbResult;
try {
dbResult = await GetStuff(checkUserText, values);
} catch (err) {
console.log('fel: ' + error)
logger.error('Fel: ' + error)
res.sendStatus(500);
}
const user = dbResult.rows[0];
if (user.userid !== null) {
console.log(user);
}
// Do some more things...
res.sendStatus(200); // All good!
}

Categories

Resources