Accessing the content of events in Javascript? - node.js

I'm trying to get a print out of the events my cloud function is privy to. I tried console.log(event)in the Atom console but I get this error:
Uncaught Error: Cannot find module 'firebase-functions'
at Module._resolveFilename (module.js:455:15)
at Module._resolveFilename
I'm not familiar with Javascript or Atom, so I don't know why I'm not getting the expected behaviors, my guess is that I need to authorize the script that is trying to access my secure backend on Firebase.
Here's my cloud function so far:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendPushNotification = functions.database.ref('/
iMessenger/{Messages}/{id}').onWrite(event => {
//I really want to see all that's inside of 'event'. How do I see this info??
console.log(event)
const payload = {
notification: {
title:'New message arrived',
body:'Hello World',
badge:'1',
sound:'default',
}
};
return admin.database().ref('/iMessenger/{Messages}/{id}/toUserDisplayName').once('value').then(allToken => {
if (allToken.val()){
const token = Object.keys(allToken.val());
return admin.messaging().sendToDevice(token, payload).then(response => {
});
};
});
});

using console.dir(event) will list every available method and parameter within event

Console.log(event) shows up in your cloud function logs in firebase. And the missing module was because my editor couldn’t exactly interface with the firebase modules I figure, as firebase accepts my functions and the notification are coming through; a testament to successful implementation.

Related

How to pass arguments from Dart to Cloud functions written in Typescript and it also gives me error code UNAUTHENTICATED

Dart function (passing token to sendToDevice):
Future<void> _sendNotification() async {
CloudFunctions functions = CloudFunctions.instance;
HttpsCallable callable = functions.getHttpsCallable(functionName: "sendToDevice");
callable.call({
'token': await FirebaseMessaging().getToken(),
});
}
index.ts file where I have defined sendToDevice method.
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
const fcm = admin.messaging();
export const sendToDevice = functions.firestore
.document('users/uid')
.onCreate(async snapshot => {
const payload: admin.messaging.MessagingPayload = {
notification: {
title: 'Dummy title',
body: `Dummy body`,
click_action: 'FLUTTER_NOTIFICATION_CLICK'
}
};
return fcm.sendToDevice(tokens, payload); // how to get tokens here passed from above function?
}
);
Questions:
How can I receive tokens passed from my Dart function _sendNotification to Typescript's sendToDevice function.
When I was directly passing tokens inside index.ts file, I was getting this exception:
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: PlatformException(functionsError, Cloud function failed with exception., {code: UNAUTHENTICATED, details: null, message: UNAUTHENTICATED})
Can anyone please explain if I am supposed to authenticate something here? The command firebase login shows I am already signed in. I am very new to Typescript so please bear with these stupid questions.
Your Flutter side of code seems right, what's wrong is on the Cloud Function.
The sendToDevice function is not a callable function. It is a Cloud Firestore Triggers, it is only meant to be automatically called whenever a document matches users/{uid} is created.
Instead, you'll want to create a Callable Function, see below
export const sendToDevice = functions.https
.onCall(async (data) => {
const { token } = data; // Data is what you'd send from callable.call
const payload: admin.messaging.MessagingPayload = {
notification: {
title: 'Dummy title',
body: `Dummy body`,
click_action: 'FLUTTER_NOTIFICATION_CLICK'
}
};
return fcm.sendToDevice(token, payload);
}
);
You have created a database trigger, what you should do is create a callable function as shown below
exports.sendToDevice = functions.https.onCall(async (data, context) => {
const payload: admin.messaging.MessagingPayload = {
notification: {
title: 'Dummy title',
body: `Dummy body`,
click_action: 'FLUTTER_NOTIFICATION_CLICK'
}
};
return await fcm.sendToDevice(data.token, payload);
});
There are few things to mention here:
1st The function used in 'getHttpsCallable' must be triggered by https trigger (reference here). Here we have a function triggered by firestore document create, so it won't work.
2nd You do not have parameter of your function, but you call it with parameters. If you need example of calling cloud function with parameter you can find it on pud.dev
3rd I do not have at the moment possibility to play with it, but I think that if you implement https triggered function with token parameter you should be able to pass this parameter.
I hope it will help!
UPDATE:
According to doc https triggered function has to be created with functions.https. There is a nice example in the doc. To function triggered this way you can add request body when you can pass needed data.
This answer might not solve your problem but will give you a few things to try, and you'll learn along the way. Unfortunately I wasn't able to get the callable https working with the emulator. I'll probably submit a github issue about it soon. The flutter app keeps just getting different types of undecipherable errors depending on the local URL I try.
It's good that you've fixed one of the problems: you were using document trigger (onCreate) instead of a https callable. But now, you're running a https callable and the Flutter apps needs to communicate with your functions directly. In the future, you could run the functions emulator locally, and do a lot of console.log'ing to understand if it actually gets triggered.
I have a few questions/ things you can try:
Is your user logged in the flutter app? FirebaseAuth.instance.currentUser() will tell you.
Does this problem happen on both iOS and android?
Add some logs to your typescript function, and redeploy. Read the latest logs through StackDriver or in terminal, firebase functions:log --only sendToDevice. (sendToDevice is your callable function name)
Are you deploying to the cloud and testing with the latest deployment of your functions? You can actually test with a local emulator. On Android, the url is 10.0.2.2:5001 as shown above. You also need to run adb reverse tcp:5001 tcp:5001 in the terminal. If you're on the cloud, then firebase login doesn't matter, I think your functions should already have the credentials.
To call the emulator https callable:
HttpsCallable callable = CloudFunctions.instance
.useFunctionsEmulator(origin: "http://10.0.2.2:5001")
.getHttpsCallable(functionName: "sendToDevice");
And iOS you need to follow the solution here.
One mistake I spotted. You should at least do return await fcm.sendToDevice() where you wait for the promise to resolve, because otherwise the cloud function runtime will terminate your function before it resolves. Alternatively, for debugging, instead of returning sendToDevice in your cloud function, you could have saved it into a variable, and console.log'd it. You would see its actually a promise (or a Future in dart's terminology) that hadn't actually resolved.
const messagingDevicesResponse: admin.messaging.MessagingDevicesResponse = await fcm.sendToDevice(
token,
payload
);
console.log({ messagingDevicesResponse });
return;
Make the function public
The problem is asociated with credentials. You can change the security policy of the CF and sheck if the problem is fixed. Se how to manage permisions on CF here

Is there a way to instantiate a new client on server side by firebase cloud function?

I am developing an app and trying to implement news feed by getstream.io using react native and firebase.
Is there a way to generate user token by using firebase cloud function. If there is, would you please give me a pointer how i can do so? (the snippet of codes in cloud function side and client side would be super helpful..)
I have seen similar questions, only to find out no specific tutorial.. any help is appreciated!
For the cloud function side you need to create a https.onRequest endpoint that calls createUserToken like so:
const functions = require('firebase-functions');
const stream = require('getstream');
const client = stream.connect('YOUR_STREAM_KEY', 'YOUR_STREAM_SECRET', 'YOUR_STREAM_ID');
exports.getStreamToken = functions.https.onRequest((req, res) => {
const token = client.createUserToken(req.body.userId);
return { token };
});
After that, deploy with firebase deploy --only functions in the terminal & get the url for the function from your firebase dashboard.
Then you can use the url in a POST request with axios or fetch or whatever like this:
const { data } = axios({
data: {
userId: 'lukesmetham', // Pass the user id for the user you want to generate the token for here.
},
method: 'POST',
url: 'CLOUD_FUNC_URL_HERE',
});
Now, data.token will be the returned stream token and you can save it to AsyncStorage or wherever you want to store it. Are you keeping your user data in firebase/firestore or stream itself? With a bit more background I can add to the above code for you depending on your setup! 😊 Hopefully this helps!
UPDATE:
const functions = require('firebase-functions');
const stream = require('getstream');
const client = stream.connect('YOUR_STREAM_KEY', 'YOUR_STREAM_SECRET', 'YOUR_STREAM_ID');
// The onCreate listener will listen to any NEW documents created
// in the user collection and will only run when it is created for the first time.
// We then use the {userId} wildcard (you can call this whatever you like.) Which will
// be filled with the document's key at runtime through the context object below.
exports.onCreateUser = functions.firestore.document('user/{userId}').onCreate((snapshot, context) => {
// Snapshot is the newly created user data.
const { avatar, email, name } = snapshot.val();
const { userId } = context.params; // this is the wildcard from the document param above.
// you can then pass this to the createUserToken function
// and do whatever you like with it from here
const streamToken = client.createUserToken(userId);
});
Let me know if that needs clearing up, these docs are super helpful for this topic too 😊
https://firebase.google.com/docs/functions/firestore-events

How to get external api data using inline editor in dialogflow

I've got a Dialogflow agent for which I'm using the Inline Editor (powered by Cloud Functions for Firebase). When I try to get external api data by using request-promise-native I keep getting Ignoring exception from a finished function in my firebase console.
function video(agent) {
agent.add(`You are now being handled by the productivity intent`);
const url = "https://reqres.in/api/users?page=2";
return request.get(url)
.then(jsonBody => {
var body = JSON.parse(jsonBody);
agent.add(body.data[0].first_name)
return Promise.resolve(agent);
});
}
Your code looks correct. The exception in this case might be that you're not using a paid account, so network access outside Google is blocked. You can probably see the exact exception by adding a catch block:
function video(agent) {
agent.add(`You are now being handled by the productivity intent`);
const url = "https://reqres.in/api/users?page=2";
return request.get(url)
.then(jsonBody => {
var body = JSON.parse(jsonBody);
agent.add(body.data[0].first_name)
return Promise.resolve(agent);
})
.catch(err => {
console.error('Problem making network call', err);
agent.add('Unable to get result');
return Promise.resolve(agent);
});
}
(If you do this, you may want to update your question with the exact error from the logs.)
Inline Editor uses Firebase. If you do not have a paid account with Firebase, you will not be able to access external APIs.

Client Callable Firebase Function fails when deployed to regions other than us-central1

Client Callable Firebase Function fails with "Error: The data couldn’t be read because it isn’t in the correct format." when deployed to regions other than us-central1 (tried Europe West and Asia).
Server Code
exports.getName = functions.region('europe-west1').https.onCall((data, context) => {
return { name: "Testopoulos" };
});
Client Code (React Native Firebase)
const httpsCallableUserName = firebase.functions().httpsCallable('getName');
getUserName = () => {
httpsCallableUserName()
.then(({ data }) => {
console.log(data.name);
})
.catch(httpsError => {
console.log(httpsError);
})
}
There is a note in the docs saying that "[when] using HTTP functions to serve dynamic content for hosting, you must use us-central1" but I am just returning an object to our frontend React Native client and not using Firebase Hosting. I read other posts about the regions but I don't think it is the same case. Any ideas?
According to the documentation "the client can also specify a region, and must do so if the function runs in any region other than us-central1.".
As you have found (see comments above), it has to be done with:
var functions = firebase.initializeApp(config).functions('europe-west1');

Google Action connected to Firebase function unable to call external api?

Currently I have a google action built using the ActionSDK with NodeJS where the fulfillment is hosted on Firebase cloud functions. All I am trying to do right now is just pass the input from google action to an external api but it seems like it is unable to send it through?
'use strict';
const {actionssdk} = require('actions-on-google');
const functions = require('firebase-functions');
const XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
const app = actionssdk({debug: true});
app.intent('actions.intent.MAIN', (conv) => {
conv.ask('Hello, this is Patrick.');
});
app.intent('actions.intent.TEXT', (conv, input) => {
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://www.googleapis.com/customsearch/v1?key={apikey}&cx={searchID}&q=" + input, true);
xhr.send();
xhr.onreadystatechange = function () {
conv.ask(this.readyState.toString());
}
exports.myFunction = functions.https.onRequest(app);
But this always gives me the response of:
My test app isn't responding right now. Try again soon.
And in the error tab it displays:
{
"error": "No response has been set. Is this being used in an async call that was not returned as a promise to the intent handler?"
}
I have no idea why this happens as I'm new to this whole thing. Any help is appreciated!
If you are using a free tier of firebase you will not be allowed to use external APIs. You will need to enable billing and upgrade to a paid tier to access.
UPDATE
You may need to use Promise to send back a response. Check out this issue.

Resources