I'm trying to integrate the Paypal Smart Button and Firestore. Everything was working fine until I tried to add in a check to see if the user document already existed in Firestore to throw an error so users can't register twice.
The next Paypal action (onApproval) requires the subscription id, which is created by the actions.subscription.create() function.
Just to make sure it wasn't a problem with the way the function fired, I also ommitted it, and just had paypalCreate = 'success'. But when logging the result of paypalCreate, at different levels of the nested functions, these were the results and their order:
On Fail (user exists):
Third Creation (undefined)
First Creation error
Second Creation error
On Success (user doesn't exist):
Third Creation (undefined)
First Creation success
So why is the 'Third Creation' always logged first? I'm guessing because the promise hasn't completed. Do I need to add a wait somewhere? And why is it undefined?
And on success, why isn't the 'Second Creation' returned? How is it any different to the failed result?
I need paypalCreate returned at the end of this createSubscription function, so the next step can use the result of it.
The code simplified:
createSubscription: function(data, actions) {
// Get snapshot
db.collection('users').doc(email).get().then((querySnapshot) => {
let paypalCreate;
// Check if email exists
if (querySnapshot.exists) {
// Register as error if user exists <- This works
paypalCreate = 'error';
console.log('First Creation ' + paypalCreate);
} else {
console.log("user doesn't exists");
// Saving new user to Firestore <- This works
db.collection('users').doc(email).set({
name: name,
email: email,
created: firebase.firestore.Timestamp.now()
}),
// Choose plan to subscribe to in PayPal
paypalCreate = actions.subscription.create({
'plan_id': 'XXXX'
});
console.log('First Creation ' + paypalCreate);
return paypalCreate;
};
console.log('Second Creation ' + paypalCreate);
return paypalCreate;
}),
console.log('Third Creation ' + paypalCreate);
return paypalCreate;
},
Related
I am very new with firebase and javascript.
My project: Build a private messaging app. To do that, I want to define a sub collection in firestore for private messaging using the current user id and the destination user id.
Here is the function that allows this:
// generate the right SubCollection depending on current User and the User he tries to reach
function dmCollection(toUid) {
if (toUid === null) {
// If no destination user is definer, we set it to the below value
toUid = 'fixed_value';
};
const idPair = [firebase.auth().currentUser.uid, toUid].join('_').sort();
return firebase.firestore().collection('dms').doc(idPair).collection('messages');
};
My problem: I want to use the firebase.auth().currentUser.uid attribute, but it looks like the function is not waiting for firebase.auth initialization. How can I fix this problem?
Additional information:
I have two functions that are calling the first one (dmCollection):
// retrieve DMs
function messagesWith(uid) {
return dmCollection(uid).orderBy('sent', 'desc').get();
};
// send a DM
function sendDM(toUid, messageText) {
return dmCollection(toUid).add({
from: firebase.auth().currentUser.uid,
text: messageText,
sent: firebase.firestore.FieldValue.serverTimestamp(),
});
};
If I correctly understand your problem ("it looks like the function is not waiting for firebase.auth initialization"), you have two possible solutions:
Solution 1: Set an observer on the Auth object
As explained in the documentation, you can set an observer on the Auth object with the onAuthStateChanged() method:
By using an observer, you ensure that the Auth object isn't in an
intermediate state—such as initialization—when you get the current
user.
So you would modify your code as follows:
// retrieve DMs
function messagesWith(uid) {
return dmCollection(uid).orderBy('sent', 'desc').get();
};
// send a DM
function sendDM(toUid, messageText) {
return dmCollection(toUid).add({
from: firebase.auth().currentUser.uid,
text: messageText,
sent: firebase.firestore.FieldValue.serverTimestamp(),
});
};
// generate the right SubCollection depending on current User and the User he tries to reach
function dmCollection(toUid) {
if (toUid === null) {
// If no destination user is definer, we set it to the below value
toUid = 'fixed_value';
};
const idPair = [firebase.auth().currentUser.uid, toUid].join('_').sort();
return firebase.firestore().collection('dms').doc(idPair).collection('messages');
};
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
var messageText = '....';
sendDM(user.uid, messageText)
} else {
// No user is signed in.
}
});
Solution 2: Use the currentUser property
You could also "get the currently signed-in user by using the currentUser property" as explained in the same doc. "If a user isn't signed in, currentUser is null".
In this case you would do:
var user = firebase.auth().currentUser;
if (user) {
var messageText = '....';
sendDM(user.uid, messageText);
} else {
// No user is signed in.
// Ask the user to sign in, e.g. redirect to a sign in page
}
Which solution to choose?
It depends how you want to call the function(s) based on the user uid.
If you want to call the function(s) immediately after the user is signed in, use Solution 1.
If you want to call the function(s) at another specific moment (e.g. following a user action), use Solution 2.
I'm trying to update a property in a record in Firebase Database, with AngularJS. I can set up a query to find my record:
firebase.database().ref('en/').orderByChild('word').equalTo('the').once('value')
.then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
console.log(childSnapshot.val())
});
})
I can update my property, if I hardcode in the record's key:
firebase.database().ref('en/-KloeQHDC-mugPjJMAG4').update({ wordFrequency: 111 })
But if I set up a query to find the record and then update it, I get an error message update is not a function:
firebase.database().ref('en/').orderByChild('word').equalTo('the').update({ wordFrequency: 9001 })
Another answer suggests calling update() from inside a forEach loop:
firebase.database().ref('en/').orderByChild('word').equalTo('the').once('value')
.then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
console.log(childSnapshot.val()); // this works
childSnapshot.ref().update({ wordFrequency: 9001 });
});
});
That returns an error message TypeError: childSnapshot.ref is not a function. I don't see how childSnapshot is a Firebase ref.
Another answer says
When you call update() on a location, Firebase loops over the data
that you pass in (in your case asJson) and for each key performs a
ref.child(key).set(value).
If update() loops over the data, why should I call update() from inside a forEach loop? The documentation doesn't show calling update() from inside a forEach loop.
The Firebase Database SDK provides a Reference.update() method to update data in a single location in a database. Key here is that a Reference is a single location in the database, so it is clear what to update.
My pseudo-code explanation about how multi-path updates work applies to how the database server implements it: given a single location/DatabaseReference it updates each path in the update() call based on that.
A Query can match multiple locations in the database, so it doesn't have an update() method (or set() or remove() for that matter).
To update each location matched by a query, you execute the query and then call update() on each result - either by a child_added listener, or with a value listener and a loop like in your last snippet.
After I posted this question I walked the dog, ate dinner, and then the solution came to me. My new rule is, "The key to Firebase queries is to keep track of the key."
This template is for users to update records in the database. They enter a search term in a form field and click the "Search" button. The $scope.search handler queries the Firebase database and then populates the form fields with the record's properties:
$scope.search = function() {
myFirebase_ref.orderByChild('word').equalTo($scope.word).once('value')
.then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
$scope.wordKey = childSnapshot.key;
$scope.audioArray = childSnapshot.val().audio;
$scope.ipaArray = childSnapshot.val().ipa;
$scope.language = childSnapshot.val().language;
$scope.longLanguage = childSnapshot.val().longLanguage;
$scope.phonemeArray = childSnapshot.val().phonemes;
$scope.translationArray = childSnapshot.val().translations;
$scope.word = childSnapshot.val().word;
$scope.wordFrequency = childSnapshot.val().wordFrequency;
$scope.$apply();
});
})
.catch(function(error) {
console.error("Authentication failed:", error.message);
});
};
Note at the top of the property assignments I have $scope.wordKey = childSnapshot.key;. I'm keeping track of the record's key.
The user then updates a field. Each field has a button next to it for "Update". Each button goes to a handler. For example, to update the wordFrequency field I have this handler:
$scope.updateFrequencyRank = function() {
firebase.database().ref('en/' + $scope.wordKey).update({ wordFrequency: $scope.wordFrequency })
};
One line of code and it works! Even better, I made an onComplete function to tell me if the update succeeded:
$scope.updateFrequencyRank = function() {
var onComplete = function(error) {
if (error) {
console.log('Update failed');
} else {
console.log('Update succeeded');
}
};
firebase.database().ref('en/' + $scope.wordKey).update({ wordFrequency: $scope.wordFrequency }, onComplete);
};
I am trying to get the value of a queried ID called from another class however when I call the function it gives me a promise chain and not the value I am looking for.
The method in the class 'Helper' is below
function querySF() {
var conn = new jsforce.Connection({
// you can change loginUrl to connect to sandbox or prerelease env.
loginUrl: 'https://www.salesforce.com'
});
return conn.login('someusername', 'password')
.then(function(userInfo) {
// Now you can get the access token and instance URL information.
// Save them to establish connection next time.
console.log(conn.accessToken);
console.log(conn.instanceUrl);
// logged in user property
console.log("User ID: " + userInfo.id);
console.log("Org ID: " + userInfo.organizationId);
// ...
return conn.query("SELECT Id FROM some place Where name = 'some name'")
})
.then(function(result) {
console.log("total : " + result.totalSize);
console.log("fetched : " + result.records.length);
// is returning the id
console.log(result.records[0].Id);
return result.records[0].Id; // can see the id here when debugging
})
.catch(function(err) {
console.error(err);
});
}
I am exporting the module like this at the bottom of the class:
exports.querySF = querySF();
The other class called 'BookingEvent' calls the method like this: var theId = Helper.querySF; and it returns a promise, I have printed the promise to console console.log(Helper.querySF); to see the results:
Promise {
_45: 0,
_81: 1,
_65: 'a0L46111001LyvsEAC', // This is the value I need
_54: null,
then: [Function],
stream: [Function: createRequest] }
It was thought that I should be able to just use
helpers.querySF().then(function(value){
console.log(value);
})
and be able to get the value however I am getting this error:
Failed: helpers.querySF is not a function
I am quite new to promises and no one at my company can seem to solve the issue. I have research many different ways of resolving promises however they do not work and I also do not understand. Could someone help me resolve this promise so the id accessible whenever I call this method, which will be multiple times with different queries I will be sending in.
If the promises are not related each other, you may find better Promise.all() here instead of chaining promises in this way. Then resolve all of them. If actually you get the name of the second query from the first promise, actually you need to chain them.
Then you are missing a catch in order to catch errors for the first promise.
And maybe a refactor using function may help the code to look better.
return conn.login('someusername', 'password')
.then(elaborateUserInfo)
.catch(catchErrors)
.then(elaborateResults)
.catch(catchErrors);
function elaborateUserInfo(userInfo) {
// Now you can get the access token and instance URL information.
// Save them to establish connection next time.
console.log(conn.accessToken);
console.log(conn.instanceUrl);
// logged in user property
console.log("User ID: " + userInfo.id);
console.log("Org ID: " + userInfo.organizationId);
// ...
return conn.query("SELECT Id FROM some place Where name = 'some name'");
}
function elaborateResults(result) {
console.log("total : " + result.totalSize);
console.log("fetched : " + result.records.length);
// is returning the id
console.log(result.records[0].Id);
return result.records[0].Id; // can see the id here when debugging
}
function catchErrors(err) {
console.log(err);
}
It looks better, doesn't it?
Instead, the only reason for this error Failed: helpers.querySF is not a function is that you don't have that method in your object. Are you sure you really exported it in order to be visible outside your module?
I would like to know how how to get the current user. I am making a function where the user is creating a group and would like to add the user making the group to it at the same time. I can make the group fine, that was simple enough. But I do not know how to get to the user object outside of the simple login object.
I'm sorry if there are several topics stating this already, but I have been looking for hours and have not been able to find anything that explains it. Any help would be appreciated.
The currently logged in user is returned from Simple Login's callback. This callback runs when your user authenticates, or if your user is already authenticated, it runs at the time of page load.
Take this code form the simple login docs:
var myRef = new Firebase("https://<your-firebase>.firebaseio.com");
var authClient = new FirebaseSimpleLogin(myRef, function(error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
// user authenticated with Firebase
console.log("User ID: " + user.uid + ", Provider: " + user.provider);
} else {
// user is logged out
}
});
The user object is exposed in the callback. It's only in scope during the execution of that callback, so if you want to use it outside, store it in a variable for reuse later like this:
var currentUser = {};
var myRef = new Firebase("https://<your-firebase>.firebaseio.com");
var authClient = new FirebaseSimpleLogin(myRef, function(error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
// user authenticated with Firebase
currentUser = user;
} else {
// user is logged out
}
});
...
// Later on in your code (that runs some time after that login callback fires)
console.log("User ID: " + currentUser.uid + ", Provider: " + currentUser.provider);
The question title is rather vague, but here's my situation. I have roughly 700+ lines of jQuery for a web application, each function and "major point of interest" in the code noted by a log to the console when it fires. For example, I have a few functions that use an AJAX call to a servlet to retrieve some information. I log when the AJAX request begins, if it's succeeded (then print what data it gathered), etc. So, by the look of what my console has logged when I open the page, it seems to stop after the first AJAX call. Granted, the call seemed to work just fine, and the data it returned was perfect. As you'll see, it even populated the select box as intended. However, the console logs stop shortly after, making me believe that for some reason, the other functions are not being called...
jQuery
$(document).ready(function() {
Initialize();
});
function Initialize() {
console.log("Initializing...");
User();
Widgets();
if($.cookie("fogbugzId") != null) {
console.log("Stored ID: " + $.cookie("fogbugzId"));
$("#userSelect").val($.cookie("fogbugzId")).trigger("change");
$("#userSelect").hide();
} else console.log("No ID Stored!");
}
function User() {
console.log("Initializing User...");
$.each(FetchUsers(), function(index, user) {
$("#userSelect").append($("<option>").val(user.id).text(user.name));
});
$("#userSelect").change(function() {
if($("#userSelect").val() != "") {
console.log("User Changed to " + $("#userSelect").val() + ": " + $("#userSelect").text());
$.cookie("fogbugzId", $("#userSelect").val(), { expires: 365 });
}
Update();
});
console.log("User Initialized!");
}
function FetchUsers() {
console.log("Loading Users...");
$("#loading").show();
$.get(servlet, { command: "getUsers" }, function(data) {
var users = new Array();
$(data).find("user").each(function() {
users.push({
id: $(this).find("id").text(),
name: $(this).find("name").text()
});
});
$.each(users, function(index, user) {
console.log(">> " + user.id + ": " + user.name);
});
console.log("Users Loaded!");
return(users);
}, "xml").complete(function() {
$("#loading").hide();
}).error(function() {
console.log("Loading Users Failed!");
});
}
function Widgets() {
console.log("Initializing Widgets...");
// More Code
console.log("Widgets Initialized!");
}
Console
Initializing...
Initializing User...
Loading Users...
>> 267: Alex Molthan
>> 35: Bill Brinkoetter
>> 100: Bob Yoder
>> 189: Brian Cutler
>> 559: Brian Ormond
>> 400: Corey Nakamura
Users Loaded!
But the logging stops right there. So the AJAX call to fetch the users from the database works fine, but apparently the User() function doesn't manage to finish properly. The only error that the JavaScript console gives me is one within my jquery.min.js file:
Uncaught TypeError: Cannot read property 'length' of undefined jquery.min.js:16
f.e.extend.each jquery.min.js:16
User modifytime.js:14
Initialize modifytime.js:3
(anonymous function) modifyTime.jsp:21
f.extend._Deferred.e.resolveWith jquery.min.js:16
f.e.extend.ready jquery.min.js:16
f.c.addEventListener.B jquery.min.js:16
It looks as though it is breaking on the $.each() that iterates through the array of users returned by the FetchUsers() function. I know the function returns usable array, so I'm not sure what it's getting stuck on. Can anyone see something I'm missing right off the bat? I tried assigning the users[] returned by the FetchUsers() function into a variable first, then passing that into the $.each(), but it still didn't work. Any suggestions?
Edit: After replacing the minified version of jQuery with the uncompressed version, it seems as though the array of users that I pass into the $.each() function has now .length property, which is why it's breaking. Just to check, before I call that particular $.each() function, I placed a log of the users[].length returned from the FetchUsers() function to see that it still had no .length property. I then went to the FetchUsers() function itself and placed a log of the users[].length just before I return it. This log, however, works perfectly fine (though my example doesn't show it, it returns 40 users). So is my users[] not being returned as an array or something?
FetchUsers does not return anything, it does not even have a return statement. Additionally, $.get is an asynchronous function, so you cannot return the value it passes to its callback from the FetchUsers function. Instead, you could make FetchUsers take a callback it calls when it has received data of a user (and in this case doing that change would be relatively trivial):
function User() {
console.log("Initializing User...");
FetchUsers(function(user) { // Changed!
$("#userSelect").append($("<option>").val(user.id).text(user.name));
});
<...>
}
function FetchUsers(callback) { // Changed!
console.log("Loading Users...");
$("#loading").show();
$.get(servlet, { command: "getUsers" }, function(data) {
//var users = new Array(); No longer necessary.
$(data).find("user").each(function() {
callback({ // Changed!
id: $(this).find("id").text(),
name: $(this).find("name").text()
});
});
<...>
}
Changing those three lines with the "Changed!" comment should be enough to make it work correctly. (Though your logging of the users gotten will need to be slightly altered as they are no longer pushed into an array.)
I confess I have not read and understood every part of your source (nor checked if all the braces are closed), but FetchUsers clearly does NOT return anything (contrary to your claim) - so a call to FetchUsers() evaluates to 'undefined'. Fixing it will require some rewriting as in Javascript you cannot really return a result of asynchronous operation (like $.get) from a synchronous function (like FetchUsers()) - this would require multithreading (some kind of blocking, waiting etc).