How to get data from firestore to google cloud functions? - javascript

My index.js file:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const Firestore = require('#google-cloud/firestore');
const firestore = new Firestore();
admin.initializeApp();
const db = admin.firestore();
exports.getName = functions.https.onCall((data, context) => {
var docRef = db.collection("dogs").doc("{data.id}");
var getDoc = docRef.get().then(doc => {
return doc.get("name");
})
});
Code in the flutter project:
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable("getName");
var temp = await callable({"id": "11"});
print(temp.data);
The program prints out null, even though document in collection "dogs" with id "11" with a field name exists. I'm trying to get specific data from firestore and return it.
Console doesn't show any errors and if I'd return anything else it would print out normally.
Couldn't find any documentations about getting data from firestore to cloud function other than ones that use triggers like: onWrite.

Have you tried to make the cloud function async?
exports.getName = functions.https.onCall(async (data, context) => {
var doc = await db.collection("dogs").doc("{data.id}").get();
return doc.data().name;
});

andi2.2's answer is correct, but let me explain why it doesn't work with your initial code using then().
By doing:
exports.getName = functions.https.onCall((data, context) => {
var docRef = db.collection("dogs").doc("{data.id}");
var getDoc = docRef.get().then(doc => {
return doc.get("name");
})
});
You actually don't return doc.get("name"); in your Callable Cloud Function. The then() method does return Promise.resolve(doc.get("name")), as explained in the then() doc, but you don't return the Promise chain.
The following will work:
exports.getName = functions.https.onCall((data, context) => {
var docRef = db.collection("dogs").doc("{data.id}");
return docRef.get().then(doc => {
return doc.get("name");
})
});
BTW, are you sure that db.collection("dogs").doc("{data.id}"); is correct? Shouldn't it be db.collection("dogs").doc(data.id);?

Related

Firebase Realtime database query from v8 to v9 SDK

I have a Firebase Realtime database query written using Firebase version 8 that I'm looking to migrate over to the v9 SDK.
export const getSingleDataWithQuery = async ({ key, email, criteria }) => {
if (!criteria) return;
const snapshot = await realTimeDb
.ref()
.child(key)
.orderByChild(query)
.equalTo(criteria)
.get();
const val = snapshot.val();
if (val) {
const keys = Object.keys(val);
return val[keys[0]];
}
return null;
};
In this example:
key would be the 'users' collection
the email field is looking for users by their email
and the criteria is the user's actual email (jane.doe#gmail.com)
Using Firebase's Read data once and Sorting data documentation I managed to narrow it down to perhaps being this, but I'm not sure if it's correct:
export const getSingleDataWithQuery = async ({ key, query, criteria }) => {
if (!criteria) return;
const dbRef = query(ref(realTimeDb, key), orderByChild(email), equalTo(criteria));
get(dbRef).then(snapshot => {
if (snapshot.exists()) {
const val = snapshot.val();
if (val) {
const keys = Object.keys(val);
return val[keys[0]];
}
}
});
return null;
};
Aside from the fact that you may have swapped query and email in the fragments, the only difference is in the way you handle the asynchronous database call and that likely explains the difference. Since you use then in the second snippet, the function doesn't actually return a promise and so calling it with await it in the caller won't have any effect.
To make those the same again, use await in the second snippet too, instead of then:
export const getSingleDataWithQuery = async ({ key, query, criteria }) => {
if (!criteria) return;
const dbRef = query(ref(realTimeDb, key), orderByChild(email), equalTo(criteria));
const snapshot = await get(dbRef); // šŸ‘ˆ
if (snapshot.exists()) {
const val = snapshot.val();
if (val) {
const keys = Object.keys(val);
return val[keys[0]];
}
}
return null;
};

AWS Secrets Manager and javascript

I have about 7 hours and dozens of try's to get a AWS Secret. I have followed the AWS example code as well as other examples found here and on other sites. My current problem is the code returns a Promise in my variable 'p', not the plaintext secret string. I'm pulling my hair out here... If it is returning a promise while still pending a resolve; I don't know why it does not wait. The calling code is in a sequelize.config file and not wrapped in an exported function--thus the async function () => below.
getSecrets.js
const AWS = require ('aws-sdk')
const sm = new AWS.SecretsManager({region: 'us-east-1'});
export default async function getSecret (secretId) {
const data = await sm.getSecretValue({
SecretId: secretId
})
.promise();
console.log ("returning SecretString:", data.SecretString)
return data.SecretString;
Calling code:
if (RUN_ENV != 'local') {
const p = async function () {
await getSecrets (DB_PASSWORD)
.then (function ( value ) {
return value;
})
.catch ((error) => {
console.log ("AWS getSecrets error: ", error);
})
}
password = p;
}
Here's a simple way to get secret strings from Secrets Manager:
const AWS = require('aws-sdk');
const client = new AWS.SecretsManager({ region: "us-east-1" });
const getMySecret = async (SecretId) => {
const s = await client.getSecretValue({ SecretId }).promise();
return s.SecretString;
};
// Async IIFE
(async() => {
const secret_101 = await getMySecret('secret-101');
console.log('My secret:', secret_101);
})();
Your original problem, as #GalAbra suggested, is that your code has password = p when it needs to be const password = p(). Specifically, rather than taking a reference to the function, it needs to invoke the function.

Cannot stub admin functions with firebase cloud functions

When i try to call the wrapped function on my data I get the following error message:
Error: The default Firebase app does not exist. Make sure you call initializeApp() before using any of the Firebase services.
My code looks like the following:
const {expect} = require("chai")
const sinon = require('sinon');
const admin = require('firebase-admin');
const test = require('firebase-functions-test')()
describe('Unit tests', () => {
let myFunctions, adminInitStub;
before(() => {
adminInitStub = sinon.stub(admin, 'initializeApp');
myFunctions = require('../index');
});
after(() => {
adminInitStub.restore();
test.cleanup()
})
describe("addSchoolVerified", () => {
it('should create school with value-pair "name":"placeholder"', async () => {
const wrapped = test.wrap(myFunctions.addSchoolVerified)
const data = {
name: 'placeholder'
}
// Call the wrapped function with data and context
const result = await wrapped(data)
// Check that the result looks like we expected.
expect(result).to.not.be.undefined
})
})
})
so far I tried using different ways of calling the stub like calling it outside of the before() but that too does result in the above error
index.js
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp()
exports.addSchoolVerified = functions.https.onCall(async (data, context) => {
/*if (!context.auth) {
// Throwing an HttpsError so that the client gets the error details.
throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
'while authenticated.');
}*/
const schoolName = data.name;
const writeResult = await admin.firestore().collection('Schule').add({name: schoolName})
return {result: `School with id: ${writeResult.id} successfully added.`}
});

How to get PromiseValue with PromiseĀ {<pending>} Firebase?

Good afternoon. I'm new to working with Firebase batabase. Please tell me how to get data from my database to work with them farther.
Now my function returns Promise {} with the date that I need inside PromiseValue. How do I get it right.
Now my code looks like this
let firebaseConfig = {
....
};
firebase.initializeApp(firebaseConfig);
let ref = firebase.database().ref('/data')
function getDataPromise() {
return ref.once('value').then(function(snapshot) {
return snapshot.val();
});
}
let res = getDataPromise()
console.log(res)
I will be glad to any answer
You have to use then() to get the data,like below
let firebaseConfig = {
....
};
firebase.initializeApp(firebaseConfig);
let ref = firebase.database().ref('/data')
function getDataPromise() {
return ref.once('value').then(function(snapshot) {
return snapshot.val();
});
}
let res = getDataPromise()
// Use then() with a callback to get data
res.then(data => {
console.log(data)
})
console.log(res)

How to make a Firebase messaging function asynchronous

I have a firebase messaging function, but the return function seems to execute before the rest of the functions. Here is the code (sorry its long):
exports.newMessageNotification = functions.firestore
.document(`messages/{messageId}`) // wildcard for the msg id
.onCreate(async (change, context) => {
const db = admin.firestore();
const messageId: string = context.params.messageId;
const messageRef = db.collection('messages').doc(messageId);
const tokens = [];
// get the message
const message: Message = await messageRef
.get()
.then(q => q.data() as Message);
const recipients: any = await message.recipients;
const user: User = await db
.collection('users')
.doc(message.senderId)
.get()
.then(q => {
return q.data() as User;
});
// Notification content
const payload = await {
notification: {
title: `${user.name}`,
body: `${message.message}`,
},
};
console.log(payload);
// loop though each recipient, get their devices and push to tokens
Object.keys(recipients).forEach(async recipient => {
const devicesRef = db
.collection('devices')
.where('userId', '==', recipient);
const devices = await devicesRef.get();
devices.forEach(res => {
const token: string = res.data().token;
console.log(token);
tokens.push(token);
});
});
console.log(tokens); // logs empty
return await sendToCloud(tokens, payload);
});
I think the issue is that the forEach is not asynchronous so the final line of code is executing before waiting for forEach to finish?
Ugh. I had this problem somewhere recently. You are correct, at least in my experience: forEach does not seem to obey the async directive. I had to use the for ... in ... syntax to:
Get it to obey async (from the parent scope)
Process sequentially, as order was important to me at the time
In your case, it would probably look like for (const recipient in recipients) { ... }
Apparently, this is caused by forEach calling the callback on each item without awaiting a response. Even if the callback is asynchronous, forEach doesn't know to await its response on each loop.
Source: https://codeburst.io/javascript-async-await-with-foreach-b6ba62bbf404
A solution from the comments on the blog linked above:
await Promise.all(
Object.keys(recipients).map(async recipient => {
const devicesRef = db
.collection('devices')
.where('userId', '==', recipient);
const devices = await devicesRef.get();
devices.forEach(res => {
const token: string = res.data().token;
console.log(token);
tokens.push(token);
});
});
)
Note the forEach has been replaced by a map, and it's within Promise.all(...).

Categories