firebase admin sdk to create new user - javascript

**In my crud project the admin adds the user in the docs as well as in auth by normal sdk would replace the current user so i tried admin sdk but writing the cloud functions and calling is getting complex as im new to firebase. i got this got from fellow stackoverflow's thread modified it for my convenience but doesn't seems to be working. **
I deployed the function locally using "firebase serve"
cloud function
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.createUser = functions.firestore
.document('Teamchers/{userId}')
.onCreate(async (snap, context) => {
const userId = context.params.userId;
const newUser = await admin.auth().createUser({
disabled: false,
username: snap.get('UserName'),
email: snap.get('email'),
password: snap.get('password'),
subjectname: snap.get('subjectname')
});
return admin.firestore().collection('Teamchers').doc(userId).delete();
});
calling it
const createUser = firebase.functions().httpsCallable('createUser');
const handleadd = async (e) =>{
e.preventDefault();
try{
createUser({userData: data}).then(result => {
console.log(data);
});
addDoc(collection(db, "Courses" , "Teachers", data.subjectname ), {
...data,
timestamp: serverTimestamp(),
});
alert("Faculty added succesfully")
} catch (e){
console.log(e.message)
}
}

In addition to the potential typo mentioned by coderpolo and Frank, there are several other errors in your codes:
1. JS SDK versions mixing-up
It seems that you are mixing up JS SDK V9 syntax with JS SDK V8 syntax.
Defining the Callable Cloud Function in your front-end as follows is V8 syntax:
const createUser = firebase.functions().httpsCallable('createUser');
While doing addDoc(collection(...)) is V9 syntax.
You have to choose one version of the JS SDK and unify your code (see V9 example below, #3).
2. Wrong Cloud Function definition
Defining your function with:
exports.createUser = functions.firestore
.document('Teamchers/{userId}')
.onCreate(async (snap, context) => {..})
is not the way you should define a Callable Cloud function.
With onCreate() you are defining a background triggered Cloud Function that is to be triggered when a new doc is created in Firestore and not a Callable CF.
You need to adapt it as follows:
exports.createUser = functions.https.onCall((data, context) => {
try {
// ....
// Return data that can be JSON encoded
} catch (e) {
console.log(e.message)
// !!!! See the doc: https://firebase.google.com/docs/functions/callable#handle_errors
}
});
3. Use await since your handleadd() function is async
This is not stricto sensu an error but you should avoid using then() and async/await together.
I would refactor it as follows (V9 syntax):
import { getFunctions, httpsCallable } from "firebase/functions";
const functions = getFunctions();
const createUser = httpsCallable(functions, 'createUser');
const handleadd = async (e) => {
e.preventDefault();
try {
await createUser({ userData: data })
await addDoc(collection(db, "Courses", "Teachers", data.subjectname), {
...data,
timestamp: serverTimestamp(),
});
alert("Faculty added succesfully")
} catch (e) {
console.log(e.message)
}
}

Related

Interacting with firebase firestore from firebase cloud functions

I am trying to interact with firebase firestore from my cloud function. The cloud functions looks like the following:
const admin = require("firebase-admin");
const functions = require("firebase-functions");
admin.initializeApp();
const db = admin.firestore();
exports.addVote = functions.https.onCall((data, context) => {
return db
.doc("sdd-enheter/enhet/votes/voteID")
.set({ user: "user", vote: 0 });
});
When calling the function from the client side I get a firebase internal error, indicating that the function has been called but throws an error. What should I do to fix this?
Your function needs to return a promise or otherwise terminate by throwing an https error. Throwing the https error will give the client back a relevant error that it can handle, so consider making that a habit. The function below covers both of those bases. If you still get an error then share that error with us as it appears in the console's function log.
exports.addVote = functions.https.onCall((_data, _context) => {
const db = admin.firestore();
try {
return db.doc("sdd-enheter/enhet/votes/voteID").set({user: "user", vote: 0});
} catch (error) {
throw new functions.https.HttpsError("unknown", "Failed to add vote.", error);
}
});

React / Stripe / createPortalLink() with firebase v9

I'm using Stripe extension for Firebase with firebase functions.
Since I refactored a bit my code for firebase v9 modular SDK, I'm getting the following console error with my Stripe createPortalLink() function:
Uncaught (in promise) TypeError: _firebase__WEBPACK_IMPORTED_MODULE_2__.default.functions is not a function
at createPortalLink (Subscription.js:99:1)
Here is my function:
async function createPortalLink() {
const functionRef = app
.functions("europe-west1")
.httpsCallable("ext-firestore-stripe-payments-createPortalLink");
const { data } = await functionRef({
returnUrl: `${window.location.origin}/dashboard-pro/abonnement/`,
locale: "auto",
});
window.location.assign(data.url);
}
Can anyone please advise?
Thanks
You need to use the getFunctions() and httpsCallable() functions in the new Modular SDK as shown below:
import { getFunctions, httpsCallable } from "firebase/functions";
// after initializing Firebase
const functions = getFunctions();
const functionRef = httpsCallable(functions, 'ext-firestore-stripe-payments-createPortalLink');
functionRef({
returnUrl: `${window.location.origin}/dashboard-pro/abonnement/`,
locale: "auto",
})
.then((result) => {
const data = result.data;
});
Checkout the documentation for more details.

How to list all subcollections of a Firestore document?

Hi,
I have a problem with downloading all collections from the document. I would like after finding the id (userUid) document to be able to download all its collections, I need the id of each of these collection
export const getAllMessagesByUserId = async (userUid) => {
const result = await firebase
.firestore()
.collection('messages')
.doc(userUid)
.onSnapshot((snapshot) => {
console.log(snapshot);
});
};
I wrote an article which proposes solutions to this problem: How to list all subcollections of a Cloud Firestore document? As a matter of fact, "retrieving a list of collections is not possible with the mobile/web client libraries" as explained in the Firestore documentation.
I would suggest you use the second method proposed in the article, using a Cloud Function.
Here is the code copied from the article.
Cloud Function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.getSubCollections = functions.https.onCall(async (data, context) => {
const docPath = data.docPath;
const collections = await admin.firestore().doc(docPath).listCollections();
const collectionIds = collections.map(col => col.id);
return { collections: collectionIds };
});
Example of calling the Cloud Function from a web app:
const getSubCollections = firebase
.functions()
.httpsCallable('getSubCollections');
getSubCollections({ docPath: 'collectionId/documentId' })
.then(function(result) {
var collections = result.data.collections;
console.log(collections);
})
.catch(function(error) {
// Getting the Error details.
var code = error.code;
var message = error.message;
var details = error.details;
// ...
});

Firebase Cloud Functions does not create document in a subcollection

When I use Firebase Cloud Functions in my Flutter app to create a document inside a collection it works:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.onCreatePost = functions.firestore
.document("/posts/{postId}")
.onCreate(async (snap, context) => {
const doc = snap.data()
const creatorId = doc.creatorId
admin.firestore().collection('feeds').doc(creatorId).set({
Id: creatorId,
isRead: false,
timestamp: admin.firestore.FieldValue.serverTimestamp(),
})
});
But when I try to add the same document inside a subcollection in that document, it does not work:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.onCreatePost = functions.firestore
.document("/posts/{postId}")
.onCreate(async (snap, context) => {
const doc = snap.data()
const creatorId = doc.creatorId
admin.firestore().collection('feeds').doc(creatorId).collection('feedItems').doc(context.params.postId).set({
Id: creatorId,
isRead: false,
timestamp: admin.firestore.FieldValue.serverTimestamp(),
})
});
What am I doing wrong? I do see that the cloud function was completed successfully in the logs, but the docment is not created in my Cloud Firestore.
I would expect neither function to work reliably, because you aren't returning a promise that resolves after the asynchronous work is complete. If you don't return a promise, then Cloud Functions might terminate your function before it's done.
Minimally, you should return the promise returned by set().
return admin.firestore()
.collection('feeds')
.doc(creatorId)
.collection('feedItems')
.doc(context.params.postId)
.set(...)
You should also check the Cloud Functions log for errors. Errors will not show up in your app since the code is running completely outside of it.
I suggest also reviewing the documentation on this.

Payment not being charged, because of some error in the firebase cloud function

I'm making a react-redux app with firetore as database.
Now, I wanted to use firebase cloud functions for handling stripe payments.
Here is the setup:
Below is the action method for checkout, after receiving token and amount from react side.
export const checkoutFunc = (token, amount) => {
return (dispatch, getState, { getFirebase, getFirestore }) => {
const uid = getState().firebase.auth.uid;
const ref = database.ref();
ref.child(`payments/${uid}`).push({
token: token,
amount: amount
});
};
};
This function creates a payment and saves token and amount.
Now, here is the cloud function which should "charge" the payment, after the above payment is created.
exports.stripeCharge = functions.database
.ref("/payments/{userId}/{paymentId}")
.onWrite((change, context) => {
const payment = change.after.val();
const userId = context.params.userId;
const paymentId = context.params.paymentId;
if (!payment || payment.charge) return;
return admin
.database()
.ref(`/teachers/${userId}`)
.once("value")
.then(snap => {
return snap.val();
})
.then(customer => {
const amount = payment.amount;
const idempotency_key = paymentId;
const source = payment.token.id;
const currency = "usd";
const charge = { amount, currency, source };
return stripe.charges.create(charge, { idempotency_key });
})
.then(charge => {
admin
.database()
.ref(`/payments/${userId}/${paymentId}/charge`)
.set(charge)
.then(charge => {
return true;
});
});
});
The creation of payment works and the token and amount is saved in payments table. But, the cloud function is not doing its job of charging the token.
Expected Result:
https://i.ibb.co/Fq9Zfhq/image.png
Actual result:
https://i.ibb.co/Krk7cGL/image.png
Though the answer provided by #Doug Stevenson is helpful, it was not the main problem. So, I am writing the solution here for other people struggling with it.
I was using the wrong public key and secret key pair in my app, that when I used correctly, it worked.
You're not returning the promise returned from set() using the Admin SDK. The function is terminating and cleaning up before that async work is complete.
.then(charge => {
return admin // add a return here
.database()
.ref(`/payments/${userId}/${paymentId}/charge`)
.set(charge)
.then(charge => {
return true;
});
});
FYI these promise chains are easier to visualize if you use async/await syntax instead of then/catch.

Categories