Firebase Function onCreate does not work - firebase

I want to send an email when a new order is created in my firebase database, but nothing happens when I create an order. My function:
exports.sendEmailConfirmation = functions.database.ref('/orders').onCreate(event => {
const mailOptions = {
from: '"Someone." <noreply#firebase.com>',
to: 'someone#gmail.com',
};
// Building Email message.
mailOptions.subject = 'New order from mobile app!';
mailOptions.text = 'John Doe lorem ipsum';
return mailTransport.sendMail(mailOptions)
.then(() => console.log('¡¡¡ Enail sent !!!'))
.catch((error) => console.error('Error!!', error));
});
This code works using onWrite()....

Your function isn't triggering because /orders already exists. onCreate triggers will only run when the path you specify is newly created.
If you want to know when a child is newly added under /orders, you should use a wildcard in the path:
functions.database.ref('/orders/{orderId}')

Related

firebase onSnapshot gets update before create is complete

I have a "post" that listens to changes on its comments in react like so:
// React hook state
const [comments, setComments] = useState([])
// My listener in useEffect
db.collection(`users/${userId}/posts/${postId}/comments`)
.onSnapshot((querySnapshot) => {
let newComments = []
querySnapshot.forEach(function (doc) {
newComments.push({
id: doc.id,
...doc.data()
})
})
setComments(newComments)
})
When the user creates a new comments, I set a loading state and disable the comment section
// React hook
const [isLoading, setLoading] = useState(false)
// Add comment
const addComment = () => {
const comment = {text:"hello"}
setSaving(true)
db.collection(`users/${postUid}/posts/${postId}/comments`).doc()
.set(comment)
.then(()=>{
setSaving(false)
})
}
My problem is (a good problem to have), the subscription onSnapshot gets the new comment before my addComment callback is completed, creating some visual issues:
- Makes the app look buggy when the comment input is still loading but the comment already there
- If there is an error (ex: database permission issue), the comment shows up in the list and then disappears...
Any idea what I can change to not have the onSnapshot update before the create is done?
As explained here in the doc:
Local writes in your app will invoke snapshot listeners immediately.
This is because of an important feature called "latency compensation."
When you perform a write, your listeners will be notified with the new
data before the data is sent to the backend.
Retrieved documents have a metadata.hasPendingWrites property that
indicates whether the document has local changes that haven't been
written to the backend yet.
See also the following remark in the "Listen to multiple documents in a collection" section:
As explained above under Events for local changes, you will receive
events immediately for your local writes. Your listener can use the
metadata.hasPendingWrites field on each document to determine whether
the document has local changes that have not yet been written to the
backend.
So you can use this property to display the change only if it has been written to the back-end, something along the following lines (untested):
db.collection(`users/${userId}/posts/${postId}/comments`)
.onSnapshot((querySnapshot) => {
let newComments = []
querySnapshot.forEach(function (doc) {
if (!doc.metadata.hasPendingWrites) {
newComments.push({
id: doc.id,
...doc.data()
})
}
})
setComments(newComments)
})

How to use Sign-In User ID to send push notifications

I have some users signed into my actions-on-google app via Google Sign-In ( https://developers.google.com/actions/identity/google-sign-in )
I want to sent push notifications to one of those users.
For getting push notifications work with actions in the first place, I tried this sample: https://github.com/actions-on-google/dialogflow-updates-nodejs/blob/master/functions/index.js but I only can get this to work without this commit: https://github.com/actions-on-google/dialogflow-updates-nodejs/commit/c655062047b49e372da37af32376bd06d837fc7f#diff-1e53ef2f51bd446c876676ba83d7c888
It works fine, but I think const userID = conv.user.id; returns the deprecated Anonymous User ID. The commit suggests to use const userID = conv.arguments.get('UPDATES_USER_ID'); which returns undefined.
I use this nodejs code to send the push notifications.
const request = require('request');
const {JWT} = require('google-auth-library');
const serviceAccount = require('./service-account.json');
let jwtClient = new JWT(
serviceAccount.client_email, null, serviceAccount.private_key,
['https://www.googleapis.com/auth/actions.fulfillment.conversation'],
null
);
jwtClient.authorize((authErr, tokens) => {
let notification = {
userNotification: {
title: process.argv[2],
},
target: {
userId: USERID,
intent: 'tell_latest_status',
// Expects a IETF BCP-47 language code (i.e. en-US)
locale: 'en-US'
},
};
request.post('https://actions.googleapis.com/v2/conversations:send', {
'auth': {
'bearer': tokens.access_token,
},
'json': true,
'body': {
'customPushMessage': notification, 'isInSandbox': true
},
}, (reqErr, httpResponse, body) => {
console.log(httpResponse.statusCode + ': ' + httpResponse.statusMessage);
});
});
I simply can't get this to work with the const userID = conv.arguments.get('UPDATES_USER_ID'); version, because as I said
When I use conv.user.profile.payload.sub as suggested here: https://developers.google.com/actions/identity/user-info the AoG API returns "SendToConversation response: Invalid user id for target."
Is there any way to make this work with Google Sign-In?
Has anyone made this work? I mean with the UPDATES_USER_ID field?
I already created an issue on the samples repo: https://github.com/actions-on-google/dialogflow-updates-nodejs/issues/15 but I was sent here.
Thanks!
While researching why I sometimes got undefined I found an answer on this question that solved my issue.
I've found solution for this problem. While getting UPDATES_USER_ID
conv.arguments.get() only works for first attempt. So, while building
your action you must save it. If you didn't store or save, you can
reset your profile and try again, you will be able to get.
You can reset your user profile for the action here.

Firebase Storage download url not available after upload

I am using Angular and AngularFire2. I am trying to upload an image to firebase storage, then once that is done I am take that reference and get the download url and upload it to the database. For some reason even though the upload is complete and I have the snapshot, when I try to use that in order to get the URL it's giving me an error that the object does not exist. Any thoughts on what I might be doing wrong?
task.snapshotChanges().pipe(
concatMap(snap => {
return snap.ref.getDownloadURL()
}),
concatMap(url => this.db.collection('library').add({
name: this.image.name,
path: path,
largeUrl: url
}))
).subscribe(ref => {
this.completed = true;
}, error => console.log(error));
error:
Firebase Storage: Object 'library/1542515976022_lemonade-smoothie.jpg' does not exist.
Ok, so my issue was not really understanding concatMap. I thought it wasn't called until the last onNext() of the upload Observable. It was being called on the first onNext(), which means the file had not completely updated. Below is what I ended up doing, although it seems like there should be another way. What I would like is to only switch to the new Observable track if the downloaded bytes equals the total bytes. I'm not sure how to do this with RxJS though. If anyone had any thoughts let me know.
task
.snapshotChanges()
.pipe(finalize(() => this.uploadToDb(path)))
.subscribe();
uploadToDb(path: string) {
this.storage
.ref(path)
.getDownloadURL()
.pipe(
concatMap(url =>
this.db.collection('library').add({
name: this.image.name,
path: path,
largeUrl: url
})
)
)
.subscribe(
ref => (this.completed = true),
error => {
console.log(error);
this.error = true;
}
);
}

How to check Item exists in Firebase Database? - react-native-firebase

Currently, I am using https://github.com/invertase/react-native-firebase for my project. I have a custom database for users and I want to check if the user exists or not by email.
Here is a screenshot of the database:
Here's a generic firebase method but you may need to reconfigure the method to suit your data structure. Please refer to the official docs if you wish to know more.
firebase.database()
.ref(`/users`)
.orderByChild("email")
.equalTo(email)
.once("value")
.then(snapshot => {
if (snapshot.val()) {
// data exist, do something else
}
})
You can also query the registration status with hasChild method. Refer to your root path and query with .once and check the result returned.
export function checkUserExist(email) {
return(dispatch) => {
firebase.database().ref(`/ExistingUser/`)
.once('value', snapshot => {
if(snapshot.hasChild(email)) {
dispatch({
type: FIREBASE_USER_EXISTED
});
} else {
dispatch({
type: FIREBASE_USER_NOT_EXISTED,
});
}
});
}
}
Another preferred method would be using the fetchProvidersForEmail method provided by Firebase. It takes an email and returns a promise that resolves with the list of providers linked to that email if it is already registered, refer here.
Is there a good reason to store users credential in your database? In my daily practice, I would use the createUserWithEmailAndPassword provided by Firebase for security purposes, refer here. Just make sure rules are defined properly to prevent unauthorized access.

Getting an open graph action approved - change publish_stream to publish_action

UPDATED CODE:
I have an open graph action pending approval. I received a message back from Facebook saying this:
Your code is currently configured to publish a stream story. You must change your code so that when the test user triggers the action it produces an open graph story. Please make the appropriate changes and resubmit.
I followed all the tutorials regarding publishing actions and my tests all published successfully to my app timeline. The problem is that my app (which is a page tab) is already up and running - so I want to update it and add these new actions.
Are Facebook looking at the code in my current page tab - which is using the fmbl posttofeed share button - or are they looking at the tests I carried out with the new action? Is anyone able to shed some light on this?
This is the code I have in my test page that I used to publish the actions:
function postShare()
{
FB.api(
'/me/namespace:share',
'post',
{ photo: 'https://domain.com' },
function(response) {
if (!response || response.error) {
alert('Error occurred : ' + response.error);
} else {
alert('Share was successful! Action ID: ' + response.id);
}
});
}
// Load the SDK Asynchronously
(function(d){
var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
if (d.getElementById(id)) {return;}
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
ref.parentNode.insertBefore(js, ref);
}(document));
// Init the SDK upon load
window.fbAsyncInit = function() {
FB.init({
appId : 'APP ID', // App ID
channelUrl : '//channel url', // Path to your Channel File
status : true, // check login status
cookie : true, // enable cookies to allow the server to access the session
xfbml : true // parse XFBML
});
// listen for and handle auth.statusChange events
FB.Event.subscribe('auth.statusChange', function(response) {
if (response.authResponse) {
// user has auth'd your app and is logged into Facebook
FB.api('/me', function(me){
if (me.name) {
document.getElementById('auth-displayname').innerHTML = me.name;
}
})
document.getElementById('auth-loggedout').style.display = 'none';
document.getElementById('auth-loggedin').style.display = 'block';
} else {
// user has not auth'd your app, or is not logged into Facebook
document.getElementById('auth-loggedout').style.display = 'block';
document.getElementById('auth-loggedin').style.display = 'none';
}
});
// respond to clicks on the login and logout links
document.getElementById('auth-loginlink').addEventListener('click', function(){
FB.login();
});
document.getElementById('auth-logoutlink').addEventListener('click', function(){
FB.logout();
});
}
function loginUser() {
FB.login(function(response) { }, {scope:'publish_actions, email'});
}
I can't see how this is configured to publish a stream story and not an open graph story? Can anyone help with this is it's driving me insane and can't find anything out there to suggest what I'm doing is not publishing an action.
If, however when they are reviewing my actions they are looking at the code in my live app then of course it is not set up to trigger any open graph stories - as they haven't been approved yet!
Any help would be hugely appreciated.
Many thanks
Your question isn't entirely clear, but both the publish_actions and publish_stream Permissions both allow you to post Open Graph actions. The publish_stream permission however covers many other publishing types and is also optional, and if users remove that permission you won't be able to post OG actions for those users.
Update your authentication code to request publish_actions instead / as well
Finally got it working. Steps:
1. Added "Publish_action" Permission
2. Tested on FB Graph API Explorer successfully
3. Modified my Javascript (similar code as the postShare() method above)
FB.api('/me/namespace:purchase',
'post',
{ product: 'samples.ogp.me/367683346642550'; },
function(response) {
if (!response || response.error) {
alert('Error occured'+response.error);
} else {
alert('Post was successful! Action ID: ' + response.id);
}
});
The Facebook testers need the actual code running at your production server. They are going to use a Facebook test user to execute all the steps you described when you submitted the action. They won't use the already published stories. They will probably use the "Open Graph Test User".
You have two options here:
Try to publish the action with every user and if it doesn't work, publish the stream (so that the test user get the action published but your real user publish using the old code)
--- OR ---
Identify if the user is a test user (by recording the test users ids) and serve him the new code.
Anyway, the real action flow must be executable on the production server.
Basically you cannot post something to an album or any other kind of post when you are using an open graph story. For example the following is not allowed:
$data = $facebook->api('/me/photos', 'post', $args);//disallowed
$facebook->api(
'me/invig-test:share',
'POST',
array(
'app_id' => $configParam['appId'],
'type' => "test:photo",
'photo' => "http://samples.ogp.me/574859819237737",
'title' => "a photo",
'image[0][url]'=>"http://www.testapp.com/".$imgFile,
'image[0][user_generated]'=>'true',
'message' => $comments,
'fb:explicitly_shared'=>true,
)
);
Instead only do the "share":
$facebook->api(
'me/invig-test:share',
'POST',
array(
'app_id' => $configParam['appId'],
'type' => "test:photo",
'photo' => "http://samples.ogp.me/574859819237737",
'title' => "a photo",
'image[0][url]'=>"http://www.testapp.com/".$imgFile,
'image[0][user_generated]'=>'true',
'message' => $comments,
'fb:explicitly_shared'=>true,
)
);

Resources