I'm sorry for asking yet another Promise question however, I can't quite grasp it and after reading tons of questions on here and explanations I'm still struggling.
So in my nodejs project, I'm trying to do three things.
1) Get user info from the Facebook API
graph.get(message.user, function getUserInfo(err, res) {
console.log(res)
}
2) Get a list of users from another API
request.get('https://api-url/api/users', {
'auth': {
'bearer': 'bearerAuth'
}
})
3) Check the name from the Facebook user matches a name in the JSON data I get back from my API then hand it to the user.
let aPieceOfData = "";
Bluebird.promisifyAll(graph.get(message.user))
.then(function(res) {
// this should give me the response from the Facebook API which is the user
// Then pass the response to the next .then(function(){})
})
.then(function(res) {
request.get('https://api-url/api/users', {
'auth': {
'bearer': 'bearerAuth'
}
const apiData = JSON.parse(response.body);
for (i in apiData) {
if (res.username == apiData[i].username) {
// if the username matches the user name then save some data to a variable outside this scope so I can access it
aPieceOfData = apiData[i].item;
}
}
})
})
.catch(function(err) {
console.log(err, "<<<<<<<");
})
Formatting might be a little off. But I'm struggling to understand how promises work and how I can pass the data between my chained functions than at the end save it outside my function so I can use it.
Can someone give a bit of explanation and/or some links to beginner friendlier explanations.
Based on the example from the doc
var fs = Promise.promisifyAll(require("fs"));
fs.readFileAsync("myfile.js", "utf8").then(function(contents) {
console.log(contents); }).catch(function(e) {
console.error(e.stack); });
I believe it should be like this:
var g = Bluebird.promisifyAll(graph);
g.getAsync(message.user)
.then(function (res) {
// this should give me the response from the Facebook API which is the user
// Then pass the response to the next .then(function(){})
return res;
})
.then(function (res) {
return request.get('https://api-url/api/users', {
'auth': {
'bearer': 'bearerAuth'
}
});
})
.then(function (response) {
const apiData = JSON.parse(response.body);
for (i in apiData) {
if (res.username == apiData[i].username) {
// if the username matches the user name then save some data to a variable outside this scope so I can access it
aPieceOfData = apiData[i].item;
}
}
})
.catch(function (err) {
console.log(err, "<<<<<<<");
});
Related
For a research study, I am trying to record user's interactions with a web interface. I setup a back end with Strapi to store that data. Connection and setting up of a new participant ID is not a problem, but when I try to update the data I get an interesting behavior that is unfamiliar to me.
Basically, it seems that I am setting the participants ID but when accessing it, my logs show that I have a duplicate with no data stored. Below the logs I am getting while debugging:
//Initialising study. connectStrapi.js?t=1668867760343:11
//No participant ID existing. Creating.. connectStrapi.js?t=1668867760343:15
//Confirming, Participant ID: 121 interactionLogger.js?t=1668867760343:199
//On Event: participant id is: 121. interactionLogger.js?t=1668867760343:81
//On Event: participant id is: undefined interactionLogger.js:81
The connectStrapi.js sets up the study and participant data. When no id is present at the start, I am creating new data. The interactionLogger.js is responsible to log the data and update Strapi when something happens.
I am not completely new to JavaScript, but I go to admit, that I have no idea what the different identifiers mean. I am only updating once, but I the same line twice - once for interactionLogger.js?t=1668867760343:81 and a second time for interactionLogger.js:81. I am not exactly sure, what the .js?t=1668867760343 means and how I can ensure passing the right value at the right time.
Currently, I when I am updating the data, the undefined value is being passed resulting in no data being updated. I am thinking, I am missing something basic here but can't figure it out.. In case it matters, I have implemented the JavaScript files as type=modules and import functions accordingly.
InitialiseStudy function from connectStrapi.js:
import * as interactionLogger from './interactionLogger.js/'
//...
export const initialiseStudy = async (participantData, id) => {
console.log("Initialising study.")
if(id === undefined) {
await axios.post(apiUrl, participantData)
.then( response =>{
console.log('No participant ID existing. Creating..')
//setupParticipant(response.data.data);
interactionLogger.setParticipantID(response.data.data.id)
})
.catch( error => {
if (error.response){
console.log('error:', error.response)
}
else if (error.request){
console.log('error:', error.request)
}
else if (error.message){
console.log('error:', error.message)
}
else {
console.log('error:', error)
}
})
} else {
axios.put(apiUrl+ id, participantData)
.then(response => {
console.log('response.data:', response.data)
})
.catch( error => {
console.log('error:', error)
})
}
}
My updateData function from connectStrapi.js:
export const updateData = (data, id) => {
if (id === undefined) {
alert("No Participant ID")
} else {
axios.put(apiUrl + '/' + id, data)
.then( response => {
console.log('response.data:', response.data)
} )
.catch( error => {
console.err('error:', error)
})
}
}
The setter from interactionLogger.js is a normal setter:
let participantID
//...
export const setParticipantID = (id) => {
participantID = id
}
The logData function uses updateData, which is imported before it based on events in interactionLogger.js:
import * as connectStrapi from './connectStrapi.js'
//...
const logData = (data) => {
connectStrapi.updateData(data, participantID) //<- This is a local variable initialized as undefined but updated using the setParticipantID in connectStrapi.js
}
Thanks in advance!
I'm adding the claim to a user's profile that he or she paid for something, though, after the payment this attribute isn't visible. I'm running the functions on an emulator on a local host.
This is the code I'm using:
If the paypal function has been handled succesfully through paypalHandleOrder, then the function addPaidClaim is invoked.
onApprove: (data, actions) => {
paypalHandleOrder({ orderId: data.orderID }).then(
addPaidClaim(currentUser).then(
alert("THANKS FOR ORDERING!"),
// currentUser.getIdTokenResult().then(idTokenResult => {
// console.log(idTokenResult.claims)
// })
)
.catch((err) => {
return err;
})
);}
addPaidClaim is a firebase cloud function, which goes as follows:
exports.addPaidClaim = functions.https.onCall((data, context) => {
// get user and add custom claim (paid)
return admin.auth().setCustomUserClaims(data.uid, {
paid: true,
}).then(() => {
return {
message: `Success! ${data.email} has paid the course`,
};
}).catch((err) => {
return err;
});
});
I've refreshed the page and checked the user attributes afterwards through console.log on the user to see if the attribute had been added, but this is not the case. I can't find attribute paid inside the idTokenResult object. What should I do? I also find it hard to make sense of what's happening inside the function addPaidClaim. It's not returning an error when I look at the logs on my firebase console, and not much information is given, besides that the function has been invoked.
Okay, I know this question is pretty old. But I found a way just yesterday after 3 days searching over the solution. After we set up a new claim for a new user using, we need to refresh the client's getIdTokenResult(true) in the app. These are the ways I did it in Flutter Dart until a new user with updated claim managed to use it:
FirebaseAuth auth = FirebaseAuth.instance;
Future<Map<String, dynamic>> signInWithGoogle() async {
Map<String, dynamic> output = {};
final googleUser = await googleSignIn.signIn();
if (googleUser == null) {
log("Firebase => Gmail account doesn't exist");
} else {
final googleAuth = await googleUser.authentication;
final credential = GoogleAuthProvider.credential(
idToken: googleAuth.idToken,
accessToken: googleAuth.accessToken,
);
await auth.signInWithCredential(credential).then((values) async {
await userAuth(credential).then((value) =>
value.addAll(output));
});
}
return output;
}
Future<Map<String, dynamic> userAuth (OAuthCredential credential) async {
Map<String, dynamic> output = {};
await auth.currentUser!.reauthenticateWithCredential(credential);
await auth.currentUser!.reload();
await auth.currentUser!.getIdTokenResult().then((result) => {
if(result.claims!.isNotEmpty){
//check your claim here
} else {
//assign log here
}
});
return output;
}
Any idea how we can write graphQL resolver so that I can cache API response in the redis and on the next call it takes data from the redis instead of hitting the backend API response ?
Here user name is unique in the API. i.e. 1. getify and 2. bradtraversy
/// Middleware Function to Check Cache
checkCache = (username) => {
redis.get(username, (err, data) => {
if (err) {
console.log("====111111111111==========");
console.log(err);
}
if (data !== null) {
personInfo = data;
// console.log(data);
console.log("============222222222222=========");
return personInfo;
}
});
};
// Running Code
const resolvers = {
Query: {
getPerson: async (_, { username }) => {
await checkCache(username);
console.log(username);
if(null != personInfo) {
console.log("=======333333333=======")
console.log(personInfo);
return JSON.parse(personInfo);
}
else {
console.log("Fetching Data from API")
console.log(username);
const response = await fetch(`https://api.github.com/users/${username}`).then(response => response.json());
redis.SETEX(username, 300, JSON.stringify(response));
// console.log(response);
return response;
}
}
}
I think you might want something like Apollo's Data Sources, assuming you are getting data from other REST APIs. They have a section specifically about using Redis/memcached as a cache instead of an in-memory one.
So the gist of this answer is, if you're using Apollo Server and wanting to cache responses from REST APIs, you can use Data Sources with apollo-server-cache-redis
I am having a slightly odd issue, and due to the lack of errors, I am not exactly sure what I am doing wrong. What I am trying to do is on an onCreate event, make an API call, and then update a field on the database if the field is not set to null. Based on my console logs for cloud functions, I can see the API call getting a ok, and everything is working properly, but after about 2-5 minutes, it will update. A few times, it didnt update after 15 mins. What is causing such a slow update?
I have eliminated the gaxios call as the bottleneck simply from the functions logs, and local testing.
Some context: I am on the firebase blaze plan to allow for egress and my dataset isnt really big. I am using gaxios because it is already part of firebase-funcstions npm install.
The code is:
const functions = require('firebase-functions');
const { request } = require('gaxios');
const { parse } = require('url');
exports.getGithubReadme = functions.firestore.document('readmes/{name}').onCreate((snapshot, context) => {
const toolName = context.params.name;
console.log(toolName);
const { name, description, site } = snapshot.data();
console.log(name, description, site);
const parsedUrl = parse(site);
console.log(parsedUrl);
if (description) return;
if (parsedUrl.hostname === 'github.com') {
let githubUrl = `https://api.github.com/repos${parsedUrl.path}/readme`;
request({
method : 'GET',
url : githubUrl
})
.then((res) => {
let { content } = res.data;
return snapshot.ref.update({ description: content });
})
.catch((error) => {
console.log(error);
return null;
});
}
return null;
});
When you execute an asynchronous operation (i.e. request() in your case) in a background triggered Cloud Function, you must return a promise, in such a way the Cloud Function waits that this promise resolves in order to terminate.
This is very well explained in the official Firebase video series here (Learning Cloud Functions for Firebase (video series)). In particular watch the three videos titled "Learn JavaScript Promises" (Parts 2 & 3 especially focus on background triggered Cloud Functions, but it really worth watching Part 1 before).
So you should adapt your code as follows, returning the promise returned by request():
const functions = require('firebase-functions');
const { request } = require('gaxios');
const { parse } = require('url');
exports.getGithubReadme = functions.firestore.document('readmes/{name}').onCreate((snapshot, context) => {
const toolName = context.params.name;
console.log(toolName);
const { name, description, site } = snapshot.data();
console.log(name, description, site);
const parsedUrl = parse(site);
console.log(parsedUrl);
if (description) return null;
if (parsedUrl.hostname === 'github.com') {
let githubUrl = `https://api.github.com/repos${parsedUrl.path}/readme`;
return request({
method: 'GET',
url: githubUrl
})
.then((res) => {
let { content } = res.data;
return snapshot.ref.update({ description: content });
})
.catch((error) => {
console.log(error);
return null;
});
} else {
return null;
}
});
web3.eth, web3.eth.personal, web3.eth.accounts look similar to me. As they all have same functions - sign, sendtransaction and etc. What is a good way to apply those packages on certain situations? I mean, how could we decide when to use each of the packages?
When I looked through the documentation, it tells me
1) web3.eth - interacting with ethereum blockchain and smartcontract
2) web3.eth.personal - interact with ethereum' nodes account
3) web3.eth.accounts - generate ethereum accounts and sign transactions and data
Does that mean I could manage a local node with personal package and others with accounts?
I included a reference to a more thorough medium article below on the topic.
But for the short answer.
When using the web3.eth.accounts package, actions should be performed on the local node, because when the operations are performed on the local, the private keys will not be sent out to the network and they are safe.
You can use web3.eth.personal when you are working with another entity's account. Whatever password/information you send will be used by another node, as such you don't use this package to create user accounts or store keys;
https://medium.com/#andthentherewere0/should-i-use-web3-eth-accounts-or-web3-eth-personal-for-account-creation-15eded74d0eb
This is how I implemented reflecting answer from Legman. I used ganache and web3js to transfer ether from one to another. PrivateKey is changing every time when it is connected to ganache.
// (Web3js) Test making transaction from one account to another in ganache
web3.eth.getAccounts().then(function (accounts) { // NOTE : to reduce latency, add relevant data to database
var senderAddress = accounts[0]
var receiverAddress = accounts[1]
// Balance before transaction
var checkSenderBalance = function () {
return web3.eth.getBalance(senderAddress)
.then(function (balance) {console.log(balance)})
.catch(function (error) {console.log(error)})
}
var checkReceiverBalance = function () {
return web3.eth.getBalance(receiverAddress)
.then(function (balance) {console.log(balance)})
.catch(function (error) {console.log(error)})
}
var rawTx = {
from: senderAddress,
to: receiverAddress,
gasPrice: '200',
gas: '210000',
value: '1000',
data: '' // NOTE : need to serialize and make it as HEX code to send data
}
// Case1 : Log into account with privateKey and signTransaction
var privateKey = '0xf8d19b3c72f27a9db1a71f73d229afe5980419928b0b33232efb4033494f1562'
var sender = web3.eth.accounts.privateKeyToAccount(privateKey) // Object to call signTransaction
var makeTransaction = function () {
return sender.signTransaction(rawTx)
.then(function (signedTx) {
return web3.eth.sendSignedTransaction(signedTx.rawTransaction)
})
.then(function (receipt) {
console.log(receipt)
})
.catch(function (error) {
console.log(error)
})
}
// Case2 : signTransaction using privateKey
var privateKey = '0xf8d19b3c72f27a9db1a71f73d229afe5980419928b0b33232efb4033494f1562'
var makeTransaction = function () {
return web3.eth.accounts.signTransaction(rawTx, privateKey)
.then(function (signedTx) {
return web3.eth.sendSignedTransaction(signedTx.rawTransaction)
})
.then(function (receipt) {
console.log(receipt)
})
.catch(function (error) {
console.log(error)
})
}
// Case3 : Using Personal package
// var makeTransaction = web3.eth.personal.unlockAccount(senderAddress, '')
// .then(function (result) {
// if (result) return web3.eth.personal.signTransaction(rawTx, '')
// })
// .then(function (signedTx) {
// return web3.eth.personal.sendTransaction(signedTx.rawTransaction)
// })
// .catch(function (error) {
// console.log(error)
// })
checkSenderBalance()
.then(checkReceiverBalance)
.then(makeTransaction)
.then(checkSenderBalance)
.then(checkReceiverBalance)
.catch(function (error) {console.log(error)})
})