How to properly query data in AWS Amplify - javascript

I run the following function in my login screen to see if there is an user in the database and if not to run signup, But this function doesn't work correctly sometimes. I want to know whether my function is correct.
const Function1 = async () => {
const user = await DataStore.query(User, d => d.Phonenumb("eq", phoneNumb))
if(user.length !== 0){
signIn();
} else if (user.length === 0){
signup();
} else {
return
}
}
useEffect(() => {
Function1();
}, []);

The phoneNumb value used by Function1 should be added to the your useEffect's dependency list to re-run should that change.
Where does phoneNumb come from? If you have it, then you've seen a user before on that device and may want to just show signIn. If you don't, then signUp.
Using the predicate d => d.Phonenumb("eq", phoneNumb) will search through all users and can return multiple users. I think it'd be better if you could use the User.id instead so you can just pass that in and get that exact user (or not).
Also Function1 is async, so you should await it.

Related

Check two statements simultaneously

The idea:
I am building a bot to log into Instagram using puppeteer.If the login is successful, nothing happens. When an error with ID slfErrorAlert occurs, the bot stops (because the LogIn was not successful).
The problem:
After pressing the LogIn button, the first and afterwards the second statement is checked.
However, it should be checked at the same time whether one or the other statement is true.
This could be the pseudo code:
If (Login == true) continue;
else if (slfErrorAlert == visible) stop;
Current code snippet
if ((await instagram.page.url() !== loginURL) || (await instagram.page.waitForSelector('#slfErrorAlert'))) {
if (await instagram.page.url() !== loginURL) {
log.info("LogIn successfull")
log.info(instagram.page.url())
}
if (await instagram.page.waitForSelector('#slfErrorAlert')) {
let loginMessage = await instagram.page.$eval('#slfErrorAlert', element => element.innerHTML)
log.warn(`Client error - LogIn not possible: '${loginMessage}'`)
await instagram.browser.close()
return
}
}
Full code is here: https://github.com/JueK3y/Instagram-automated-commenting/blob/main/public/src/js/instagram.js
If i understand you correctly, you want to test both async calls simultaneously. You can do that with the function Promise.race.
let promise1 = instagram.page.url();
let promise2 = instagram.page.waitForSelector('#slfErrorAlert');
Promise.race([promise1, promise2]).then((result) => {
// do your checks here, either of the 2 promises was resolved
// the result is either the one return from the first or second promise
})
For a detailed explanation and an extended example on how to use it, see MDN web docs: Promise.race()

Firebase Functions: Why do they sometimes fail? Why do they often complete without error but don't fulfill all tasks?

This perplexes me. I'm six months into a firebase project and have been using Javascript for firebase-functions. I've learned a lot along the way by adding transactions, promises, batch writes and neat tricks. However, it seems like complete luck for a function to execute correctly. More often than not, the functions do execute correctly, but there are strange periods when bursts of consecutive function calls where functions half complete with no errors in the logs.
For example. I have a function for when a new user joins my app. It does a little bit of server data construction and also notifies the two admins that a new user has joined. Last night I did a test run with two new users and got no notification, but their user profiles constructed correctly on the server database. I checked the function logs and there were no errors.
Am I not handling Promises in the correct way? If a firebase function hangs, does it mess up the next few function calls?
exports.onNewUser = functions.firestore
.document('/users/{userId}')
.onCreate(async (snapshot, context) => {
user = snapshot.data().username;
//Notification payload
const payload = {
notification: {
title: `New user!`,
body: `${user} has joined [AppName]`
}
};
var promises = [];
//Check if usename unique
var passed = true;
promises.push(db.runTransaction(async t => {
const docRef = db.collection('users').doc('index');
const doc = await t.get(docRef);
var newIndex = doc.data().usernames;
if (newIndex[user.toUpperCase()] == true) {
t.delete(snapshot.ref);
passed = false;
return null;
} else {
newIndex[user.toUpperCase()] = true;
t.set(docRef, { 'usernames': newIndex });
}
}));
if (!passed) return Promise.all(promises);
//add new user to Algolia database
const algoliasearch = require('algoliasearch');
const algoliaClient = algoliasearch(functions.config().algolia.appid, functions.config().algolia.apikey);
const collectionIndex = algoliaClient.initIndex(collectionIndexName);
await saveDocumentInAlgolia(snapshot, collectionIndex);
//Notify Admins
db.collection('notificationTokens')
.doc(admin1)
.get().then((doc) => {
if (doc.exists && doc.data().notificationToken != null)
promises.push(pushNotification(doc.data().notificationToken, payload));
});
db.collection('notificationTokens')
.doc(admin2)
.get().then((doc) => {
if (doc.exists && doc.data().notificationToken != null)
promises.push(pushNotification(doc.data().notificationToken, payload));
});
return Promise.all(promises);
});
Just change
return Promise.all(promises);
to
return await Promise.all(promises);
You have to wait till the promises resolve before you return the function, as that would stop the instance of the cloud function.

Race condition in Amplify.Hub signIn handler when using oath

Example code:
Hub.listen('auth', event => {
const { event: type, data } = event.payload;
if (type === 'signIn') {
const session = data.signInUserSession;
console.log('SESSION', data.signInUserSession);
setTimeout(() => {
console.log('SESSION', data.signInUserSession);
}, 100);
}
});
When using oath, after the provider redirects to my app, the Hub fires a signIn event. However, the signInUserSession property is null when the event is fired, but gets a value some time later (within 100 ms). This does not seem to occur when using Auth.signIn(email, password) directly; signInUserSession is populated when the event is fired.
What is happening here, and how can I get around it? Currently, I have an explicit delay in the code, which is a terrible hack.
Perhaps the old way of JavaScript for waiting for value to be populated is useful to ensure that code does not fail even if the it takes longer than expected in populating the value.
Here is a sample code that I normally use when no other options are available.
waitForValue(){
if(myVar!= null && typeof myVar !== "undefined"){
//value exists, do what you want
console.log(myVar)
}
else{
setTimeout(() => {this.waitForValue()}, 100);
}
}
You can refactor this sample code as per your need.
Alternatively, AWS Amplify also have other ways to get current logged in user session. e.g. Auth.currentAuthenticatedUser() and Auth.currentSession() return promise. They can be used like this
private async getUser(){
let user = null;
try {
user = await Auth.currentAuthenticatedUser();
//console.log(user);
} catch (err) {
//console.log(err);
}
//return user;
}
i am not used to aws amplify - just read some github and so far i can see we will need info about your userPool implementation - i guess some weird callback issue
But for a workaround you can proxy the reference:
const event = {type: "signIn", data: {signInProperty: "null"}}
setTimeout(()=>event.data.signInProperty = "{Stack: Overflow}", 1000)
// mock events
function emit(type, args){
console.log(type, args)
}
//initialize
let watchedValue = event.data.signInProperty
document.getElementById("app").innerHTML = event.data.signInProperty
// protect reference
Object.defineProperty(event.data, "signInProperty", {
set(newValue){
watchedValue = newValue
document.getElementById("app").innerHTML = newValue
emit("event:signInCompleted", event.data)
},
get(){
return watchedValue
}
})
<div id="app"></div>

How to pause a Javascript asynchronous function without setTimeout?

I have a function that checks the existence of token in the database. The problem is it takes some amount of time to return that bool value and I kinda need to pause the function so that the function realizes that the token has existed and run the query again.
const registerToken = dispatch => {
var tokenExisted = null
do {
let token = generateRandomToken();
firebase.database().ref(`/Token`).orderByChild("token").equalTo(token).once("value", snapshot => { // check whether token exists
if (!snapshot.val()) { // if token not exist
token = false;
// register token to firebase
} else {
token = true; // continue the loop to generate a new token and query again
}
})
} while (tokenExisted === true);
}
My setup is basically a do-while loop, when the function first gets call
tokenExisted = null, then a random 4 digit token will be generated and a query will be dispatched to firebase and verify it token has existed.
If token has existed, then tokenExisted = true. I expect it the assignment to be executed but the single threaded nature of Javascript will reach the end of the loop before the query return anything.
I figured to use setTimeout and periodically add some small amount of time whenever tokenExisted = null to kinda safe guard so that the function will always catch when query function returns anything.
Has anyone had a better approach to achieve the same thing?
You might want to call the function itself recursively, as such.
const registerToken = dispatch => {
let token = generateRandomToken();
const tokenObjectRef = firebase.database().ref(`/Token`);
tokenObjectRef.orderByChild("token").equalTo(token).once("value")
.then(snapshot => {
if (!snapshot.val()) {
// success!
} else {
registerToken(dispatch) // call itself again
}
})
.catch(error => {} ))
}
The logic is that token will be refreshed during each new iteration, should the process fails and a new query is needed (if this is what you need).
Note: avoid using do-while in async logic. Plan carefully ahead as you might encounter lots of logic error and it is hard to trace.
Call the function recursively.
function get_token_then(callback_when_token_found) {
firebase.database().etc.etc(function (data) {
if (data == what_you_want) {
callback_when_token_found(data);
} else {
// You might want to wrap this in setTimeout in order to throttle your database calls
get_token_then(callback_when_token_found);
}
}
}

Passing callback twice in single function in NodeJS

I have created a function to fetch values from firebase. Now the variables in which I stored result of the firebase query is only accessible inside firebase operation. However I require those variables outside the function so I created callback function to overcome this problem.
My code looks like this:
I have two firebase databases.
One is to store registered users (ref1) and another one is to store paid users(paidRef). I need to check the one who has login is a registered user or a paid user.
var paidRef=new Firebase("https://app.firebaseio.com/paidUsers");
var ref1=new Firebase("https://app.firebaseio.com/tempUser");
function checkPaidUsers(res,callback){
ref1.orderByChild('userId').equalTo(jsonData.userId).once('child_added', function(snap) {
registeredUser=true;
paidRef.on('child_added',function(snapshot) {
if(snapshot.child('userId').val()==jsonData.userId )
{
paidFlag=true;
return callback(registeredUser,paidFlag,res);
}
else
{
paidFlag=false;
return callback(registeredUser,paidFlag,res);
}
})
})
}
checkPaidUsers( res,function(registeredUser,paidFlag) {
if(registeredUser!=true)
{
newUser=true;
}
return res.send({paidFlag:paidFlag,registeredUser:registeredUser,newUser:newUser});})
This code gives error as below:
Can't set headers after they are sent.
This error is coming because the callback function is called as many times as no. of children paidRef has because in case the user is not found in paidRef database it goes to else block and execute callback function.
Whats the best possible way to solve the problem of getting all the information of registered users as well as paid users from a single callback function.
Your issue is that you are calling the callback once for every user in paidRef, which doesn't seem to be your intention.
This code should only call the callback once.
var paidRef=new Firebase("https://app.firebaseio.com/paidUsers");
var ref1=new Firebase("https://app.firebaseio.com/tempUser");
function checkPaidUsers(res,callback){
ref1.orderByChild('userId').equalTo(jsonData.userId).once('child_added', function(snap) {
registeredUser=true;
paidRef.child(jsonData.userId).once('value', function(snapshot) {
var paidFlag = false;
if (snapshot.val() !== null) {
paidFlag = true;
}
callback(registeredUser, paidFlag, res)
})
})
}
checkPaidUsers( res,function(registeredUser,paidFlag) {
if(registeredUser!=true)
{
newUser=true;
}
return res.send({paidFlag:paidFlag,registeredUser:registeredUser,newUser:newUser});})

Categories

Resources