Getting the following error:
"Cannot read property 'userName' of undefined
at Promise.all.then.result"
Also Getting Error
"The behavior for Date objects stored in Firestore is going to change
AND YOUR APP MAY BREAK.
To hide this warning and ensure your app does not break, you need to add the
following code to your app before calling any other Cloud Firestore methods:
const firestore = new Firestore();
const settings = {/* your settings... */ timestampsInSnapshots: true};
firestore.settings(settings);
With this change, timestamps stored in Cloud Firestore will be read back as
Firebase Timestamp objects instead of as system Date objects. So you will also
need to update code expecting a Date to instead expect a Timestamp. For example:
// Old:
const date = snapshot.get('created_at');
// New:
const timestamp = snapshot.get('created_at');
const date = timestamp.toDate();
Please audit all existing usages of Date when you enable the new behavior. In a
future release, the behavior will change to the new behavior, so if you do not
follow these steps, YOUR APP MAY BREAK."
However in my android project the place where i have defined the "Date" variable i have place the "#ServerTimestamp" on top.
Appreciate the help guys.
Code:
/*eslint-disable */
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.firestore.document('notifications/{userEmail}/userNotifications/{notificationId}').onWrite((change, context) => {
const userEmail = context.params.userEmail;
const notificationId = context.params.notificationId;
return admin.firestore().collection("notifications").doc(userEmail).collection("userNotifications").doc(notificationId).get().then(queryResult => {
const senderUserEmail = queryResult.data().senderUserEmail;
const notificationMessage = queryResult.data().notificationMessage;
const fromUser = admin.firestore().collection("users").doc(senderUserEmail).get();
const toUser = admin.firestore().collection("users").doc(userEmail).get();
return Promise.all([fromUser, toUser]).then(result => {
const fromUserName = result[0].data().userName;
const toUserName = result[1].data().userName;
const tokenId = result[1].data().tokenId;
const notificationContent = {
notification: {
title: fromUserName + " is shopping",
body: notificationMessage,
icon: "default"
}
};
return admin.messaging().sendToDevice(tokenId, notificationContent).then(result => {
console.log("Notification sent!");
//admin.firestore().collection("notifications").doc(userEmail).collection("userNotifications").doc(notificationId).delete();
});
});
});
});
Make sure the document you're request actually exists. data() will return undefined if it doesn't. You can use the exists property on the resulting DataSnapshot to check if a document was actually found.
Related
I have a Firebase cloud function that does this:
const admin = require('firebase-admin')
admin.initializeApp()
exports.setSessionState = functions.https.onCall(async (data, context) => {
const stateId = data.stateId
const details = data.details
const stateRef = admin.database().ref(`state/${stateId}`)
stateRef.transaction((state) => {
if (state) {
state.details = details
}
return state
})
})
The code works well in the actual application and the state is updated but I run into problems when running this in a test, where the state is always null. (In other words, the details are never set.) The test uses the Mocha framework and is run against a real Firebase project and I can't see how anything would differ here but the behaviour is consistently different when calling this cloud function from the client application and when calling it from the test suite:
const chai = require('chai')
const assert = chai.assert
const test = require('firebase-functions-test')({
databaseURL: '<redacted>',
storageBucket: '<redacted>',
projectId: '<redacted>',
}, '../service-account-credentials.json')
describe('CFsetSessionState', () => {
let wrapped
let cloudFunctions
let admin
before(() => {
cloudFunctions = require('../index')
admin = require('firebase-admin')
wrapped = test.wrap(cloudFunctions.CFsetSessionState)
})
it('Test setting state', async () => {
const stateId = 'abc'
const data = {
stateId: stateId,
details: {
name: 'New name'
}
}
const context = {
auth: {
uid: 123
}
}
const stateRef = admin.database().ref(`state/${stateId}`)
await stateRef.set({
name: 'Previous name',
active: true
})
await wrapped(data, context)
const snapshot = await stateRef.once('value')
const state = snapshot.val()
// Details do not exist here, why?
assert.equal(state.details.name, 'New name')
})
})
I leave the database state as is after the tests so I can see there is indeed data in the state object but the details have not been set. Experimenting a bit with setting and fetching data (using .once()) before and after the call to the cloud function can change the behaviour, making me think it might be some cache issue, but this experimenting does not give me any particular stable end state. I have no idea how the equivalent of a local cache works in cloud functions, it currently shows random behaviour.
What could cause this?
I haven't tried running your Cloud Function with your test, but most probably the problem comes from the fact that you incorrectly manage the life cycle of your Cloud Function. Since it is Callable one, you need to terminate it by returning a Promise (or a value when all the asynchronous work is completed). More details here and here in the doc.
You should therefore adapt your CF as follows:
exports.setSessionState = functions.https.onCall(async (data, context) => {
const stateId = data.stateId
const details = data.details
const stateRef = admin.database().ref(`state/${stateId}`)
return stateRef.transaction((state) => { // Note the return here
if (state) {
state.details = details
}
return state
})
})
We are returning the Promise returned by the Transaction.
Hi I'm very new to this.
Here is my data tree on Firebase:
I'm trying to access the newly created nodes under "sightings" and here is my cloud function code.
const functions = require('firebase-functions');
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)
exports.announceSighting = functions.database
.ref('sightings/{sightingId}')
.onCreate(event => {
const sighting = event.data.val()
sendNotification(sighting)
})
function sendNotification(sighting) {
const name = sighting.name
const payload = {
notification: {
title: 'New Sighting Available',
body: 'Hi',
// body: title + name,
sound: 'default'
}
}
console.log(payload)
const topic = "Sightings"
admin.messaging().sendToTopic(topic, payload)
}
From my understanding, I'm listening to new nodes by .ref('sightings/{sightingId}') sightingId is what I've interpreted from a tutorial given the name of the parent is sightings, but I don't know what I should put here to indicate that I am looking for children under sighting
This is the error I've been getting this:
Your help is greatly appreciaated.
As per the docs, you should use the snapshot to get the data of the created object like so:
exports.announceSighting = functions.database.ref('/sightings/{sightingId}')
.onCreate((snapshot, context) => {
const sighting = snapshot.val();
sendNotification(sighting)
});
I want to get data from my database on fire base and want to save that data to amount amount: snapshot, I did apply this const snapshot = firestore.collection('payment').doc(context.params.amount).get(); does that works in the same way? but I am getting an error that context is undefined.
I actually want to get data from database.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const firestore= admin.firestore();
const stripe = require('stripe')('');
const snapshot = firestore.collection('payment').doc(context.params.amount).get();
const customer = stripe.customers.create({
email: 'customer#example1.com',
});
stripe.customers
.create({
email: 'foo-customer#example.com',
})
.then((customer) => {
return stripe.customers.createSource(customer.id, {
source: 'tok_visa',
});
})
.then((source) => {
return stripe.charges.create({
amount: snapshot,
currency: 'usd',
customer: source.customer,
});
})
.then((charge) => {
// New charge created on a new customer
})
.catch((err) => {
// Deal with an error
});
you are trying to get amount through accessing params through context,
depends on your error, this means context is undefined which means you are trying to get params of undefined. you need to explain what is context means here, is it a global variable? is this code inside a cloud function? if yes you need to move this declaration const snapshot = firestore.collection('payment').doc(context.params.amount).get();
inside your cloud function ,
this is an example of firebase cloud function
I'm trying to follow the GraphQL tutorial (https://www.apollographql.com/docs/tutorial/resolvers/), but I'm getting an error in the playground when I try to book a trip, with a stack trace starting like this:
"TypeError: Cannot read property 'id' of null",
" at UserAPI.bookTrips (/Users/kurtpeek/Documents/Scratch/fullstack-tutorial/start/server/src/datasources/user.js:35:38)",
In src/index.js, the ApolloServer (https://www.apollographql.com/docs/apollo-server/api/apollo-server/) is defined with an asynchronous context() function like so:
const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');
const { createStore } = require('./utils');
const resolvers = require('./resolvers');
const LaunchAPI = require('./datasources/launch');
const UserAPI = require('./datasources/user');
const isEmail = require('isemail');
const store = createStore();
const server = new ApolloServer({
context: async ({ req }) => {
debugger;
const auth = (req.headers && req.headers.authorization) || '';
const email = Buffer.from(auth, 'base64').toString('ascii');
if (!isEmail.validate(email)) return { user: null };
const users = await store.users.findOrCreate({ where: { email }});
const user = users && users[0] ? users[0] : null;
return { user: { ...user.dataValues }};
},
typeDefs,
resolvers,
dataSources: () => ({
launchAPI: new LaunchAPI(),
userAPI: new UserAPI({ store })
})
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
Since the error I'm seeing is the result of the context's user being null, I would like to step through the context() method to inspect the headers and see whether it contains a valid (encoded) email; as you can see I've set a debugger breakpoint at the start of that function.
However, when I run node inspect src/index.js, I immediately enter the debugger for an IntrospectionQuery:
If I press the 'Resume script execution' button, I just get back immediately to the same breakpoint.
Does this have something to do with the fact that context() is an async function? How can I step through the context() method with the debugger?
In the end, I just debugged it using console.log() commands. I had accidentally put the authorization in the 'Query Variables' section rather than the 'HTTP Headers' one. Moving it to 'HTTP Headers' produces the desired result:
I am following a tutorial where I am adding some Firebase Cloud Functions to my project (step 5). I have successfully deployed my cloud function to firebase but nothing happens when I add a new product manually in the Firebase Database console. I discovered that the Firebase cloud function is triggered but it is getting an error: "TypeError: Cannot read property 'productId' of undefined"
What am I doing wrong?
const functions = require('firebase-functions');
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.sendMessage = functions.firestore
.document('products/{productId}')
.onCreate(event => {
const docId = event.params.productId; // <-- error here
const name = event.data.data().name;
const productRef = admin.firestore().collection('products').doc(docId)
return productRef.update({ message: `Nice ${name}! - Love Cloud Functions`})
});
That tutorial must be out of date. Some things have changed in the Functions SDK when it released version 1.0. You can read about those changes here.
Database triggers are now passed two parameters instead of one. The new context parameter contains the value of wildcards in the reference path:
exports.sendMessage = functions.firestore
.document('products/{productId}')
.onCreate((snapshot, context) => {
const docId = context.params.productId;
If you want to continue with that tutorial, you'll have to manually convert all of its old stuff to new stuff.
OK. So thanks to Dough Stevensson's answer notifying me that the syntax was old I have now a solution:
const admin = require('firebase-admin');
const functions = require('firebase-functions');
admin.initializeApp(functions.config().firebase);
var db = admin.firestore();
exports.sendMessage = functions.firestore
.document('products/{productId}')
.onCreate((snapshot, context) => {
const docId = context.params.productId;
const productRef = db.collection('products').doc(docId)
return productRef.update({ message: `Nice ${name}!`})
});