firebase pubsub function invoked but not writing to firestore - firebase

I have a simple pub sub cloud function
var serviceAccount = require("./serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
exports.updateNews = functions.pubsub
.topic("firebase-schedule-cronForNews-us-central1")
.onPublish(message => {
axios
.get(
"https://newsapi.org/v2/top-headlines?apiKey=241414&sources=espn-cric-info"
)
.then(result => {
return result.data.articles.forEach(article => {
db.collection("news").add(article);
});
})
.then(result => {
console.log(result);
return result;
})
.catch(error => {
console.log(error);
return error;
});
return null;
});
The function is being invoked but it is not writing to firestore the same code works when I convert this to http function.

You may try returning the promises chain and using a batched write, as follows:
exports.updateNews = functions.pubsub
.topic("firebase-schedule-cronForNews-us-central1")
.onPublish(message => {
return axios // Note the return here
.get(
"https://newsapi.org/v2/top-headlines?apiKey=241414&sources=espn-cric-info"
)
.then(result => {
const batch = admin.firestore().batch();
result.data.articles.forEach(article => {
const docRef = admin.firestore().collection("news").doc();
batch.set(docRef, article);
});
return batch.commit();
})
.then(result => { // You don't need this then if you don't need the console.log
console.log(result);
return null;
});
});

Related

Promise not working, but Async/Await method working

I'm trying to figure out why I'm not able to retrieve data from the postgres database.
It works when I use async await, but when I try to to use Promise with .then(result).catch(error), it's not working.
Console log gives me Promise { <pending> }
getUsers
your text`const db = require("../config/db");
const getUsers = () => {
const query = "SELECT * FROM users";
const users = db
.query(query)
.then((result) => {
return result.rows;
})
.catch((error) => {
return error.message;
});
return users;
};
module.exports = {
getUsers,
};
index.js (Using Promise) -- Doesn't work.
const { getUsers } = require('../helpers/users')
export default function Home(props) {
return (
<ul>
{props.name}
{props.users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
export function getServerSideProps(context) {
const users = getUsers()
.then((result) => {
return result.rows;
})
.catch((error) => {
return error.message;
})
return {
props: {
users
}
}
}
index.js (Using Async/Await) -- Works.
export async function getServerSideProps(context) {
const users = await getUsers()
return {
props: {
users
}
}
}
That should work (according the Next doc getServerSideProps is async function). So just add async keyword and return promise from that function:
export async function getServerSideProps(context) {
return getUsers()
.then((result) => {
return {props:{user:result.rows}};
})
.catch((error) => {
return {props:{user:error.message}};
})
}
Thank you Andrey and Bergi.
Since it's Promise, I did not have to include the async, but the rest of the code did indeed work!
export function getServerSideProps(context) {
getUsers()
.then((result) => {
return {
props: {
users: result
}
}
})
.catch((error) => {
return {
props: {
error: error.message
}
}
})
}

Firestore batch delete don't work while using emulator with react-native

I want to try some code with firestore emulator before using it in production, I want basically to retrieve a collection documents sort them and set them again in the collection:
I have this error while doing a batch delete :
[Error: [firestore/permission-denied] The caller does not have permission to execute the specified operation.]
the code:
useEffect(() => {
(async () => {
await admin_sortUserRanksDB()
})()
}, [])
const admin_sortUserRanksDB = async () => {
const usersData = await admin_getUserDataDBAndClean()
populateUserCollection(usersData)
}
const admin_getUserDataDBAndClean = async () => {
try {
const querySnapshot = await firestore()
.collection('users')
.orderBy('experience_amount', 'desc')
.get();
let rank = 1;
let newDataUsers = [];
for (const user of querySnapshot.docs) {
const userData = user.data();
userData.rank = rank;
newDataUsers.push(userData)
rank++
}
await deleteUserCollection(querySnapshot)
return newDataUsers;
} catch (error) {
if (!__DEV__) {
crashlytics().log(
`error getUserDataDB()
userActions.js ===>> ${error.message}`
);
}
console.log('error getUserDataDB ', error)
return null
}
}
const deleteUserCollection = async (usersQuerySnapshot) => {
// Create a new batch instance
const batch = firestore().batch();
usersQuerySnapshot.forEach(documentSnapshot => {
batch.delete(documentSnapshot.ref);
});
console.log('==============')
return batch.commit();
}
const populateUserCollection = usersData => {
if (usersData) {
const batch = firestore().batch();
usersData.forEach(doc => {
let docRef = firestore()
.collection('users')
.doc(); //automatically generate unique id
batch.set(docRef, doc);
});
batch
.commit()
.catch(error => {
console.log('error populating users', error)
});
}
}
After posting an issue to react-native-firebase repo i was suggested to modify my rules to be open (only locally) and the batch delete worked.
I used the allow read, write: if true in firestore.rules file
link to issue on GitHub

How can I return the download URL of an image uploaded to firebase storage

I want to upload an image to firebase (which is working), then return the download URL of the image and store it as a string.
Here is my code:
uploadImage = async (uri, imageName) => {
const response = await fetch(uri);
const blob = await response.blob();
firebase.storage().ref().child(imageName).put(blob)
.then(snap => {
return snap.ref.getDownloadURL();
})
.then(downloadURL => {
return downloadURL;
})
.catch(error => {
console.log(`An error occurred while uploading the file.\n\n${error}`);
});
}
The image uploads to firebase storage just fine. At the moment it just shows this when I try write the URL of the uploaded image to the database:
https://ibb.co/WHHHxBY
Here is the block of code where I create the user record:
firebase
.auth()
.createUserWithEmailAndPassword(this.state.email, this.state.password)
.then(userCredentials => {
let imageUrl = '';
let db = firebase.database().ref('users/' + userCredentials.user.uid);
if (this.state.image) {
imageUrl = this.uploadImage(this.state.image.uri, `images/user-${userCredentials.user.uid}`);
}
db.set({
email: this.state.email,
imageUrl: imageUrl,
username: this.state.username
});
return userCredentials.user.updateProfile({
displayName: this.state.username
});
})
.catch(error => this.setState({ errorMessage: error.message }));
In your uploadImage function, you are chaining the promises but you don't return the chain. You should adapt it as follows:
uploadImage = async (uri, imageName) => {
const response = await fetch(uri);
const blob = await response.blob();
return firebase.storage().ref().child(imageName).put(blob) // <-- Here return the chain
.then(snap => {
return snap.ref.getDownloadURL();
})
.then(downloadURL => {
return downloadURL;
})
.catch(error => {
console.log(`An error occurred while uploading the file.\n\n${error}`);
});
}
However, you could transform this code in async/await "style", as follows:
uploadImage = async (uri, imageName) => {
try {
const response = await fetch(uri);
const blob = await response.blob();
const snap = await firebase.storage().ref().child(imageName).put(blob);
const downloadURL = await snap.ref.getDownloadURL();
return downloadURL;
} catch (e) {
console.error(e);
throw e;
}
}
Then, since this uploadImage function is asynchronous you should adapt the way you call it. I suggest to modify the other part of your code as follows:
try {
const userCredentials = await firebase
.auth()
.createUserWithEmailAndPassword(this.state.email, this.state.password);
let imageUrl = '';
const db = firebase.database().ref('users/' + userCredentials.user.uid);
if (this.state.image) {
imageUrl = await this.uploadImage(this.state.image.uri, `images/user-${userCredentials.user.uid}`);
await db.set({
email: this.state.email,
imageUrl: imageUrl,
username: this.state.username
});
return userCredentials.user.updateProfile({
displayName: this.state.username
});
}
//You should probably manage the else case
} catch (e) {
this.setState({ errorMessage: e.message })
}

Can't access data base from a Firebase function

I tried everything , I have this cloud function (that otherwise works) :
exports.contentServer = functions.https.onRequest((request, response) => {
admin.database().ref('/list/' + "abc").once('value').then(function(snapshot) {
console.log(snapshot.val() );
return null;
}).catch(function(error) {
console.log("Error getting document:", error);
return response.send(error);
});
});
or also this :
admin.database().ref('/list').once('value').then(function(snapshot) {
var event = snapshot.val();
app.tell('Result: '+event);
});
and this :
exports.contentServer = functions.https.onRequest((request, response) => {
var db = admin.database();
db.ref("list/abc").once("value").then(snap => {
var store = snap.val().description;
return store;
}).then(() => {
var store = snap.val().description;
return store;
}).then(snap => {
var store = snap.val().description;
return store;
}).catch(err => {
console.log(err);
response.send("error occurred");
});
});
and always get back the error :
"Could not handle the request"
Or I get error on deploy that :
Each then() should return a value or throw
I have a collection called list, inside I have a document named "abc".
Is there something I have to include ? something I have to setup in Firebase to make it work ? anything basic nobody write on the docs ?
Modified following the comments above explaining the OP uses Firestore and not the Realtime Database
You should do as follows. You have to wait that the promise returned by the get() method resolves before sending back the response. For this you need to use the then() method, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
exports.contentServer = functions.https.onRequest((request, response) => {
admin.firestore().collection('list').doc('abc').get()
.then(docSnapshot => {
console.log(docSnapshot.data());
return response.send(docSnapshot.data()); // or any other value, like return response.send( {result: "success"} );
})
.catch(error => {
console.log("Error getting document:", error);
return response.status(500).send(error);
});
});
As written in the comments above, I would suggest that you watch the 3 videos about "JavaScript Promises" from the Firebase video series: https://firebase.google.com/docs/functions/video-series/
Try this
Updated. Return the response inside then() as what #Renaud Tarnec pointed out.
Using realtime database
exports.contentServer = functions.https.onRequest((request, response) => {
var database = admin.database();
database.ref('list').child('abc').once("value", snapshot => {
const data = snapshot.val();
return response.send(data);
}).catch(error => {
return response.status(500).send(error);
});
});
If you are using firestore.
exports.contentServer = functions.https.onRequest((request, response) => {
const firestore = admin.firestore();
firestore.collection("list").doc('abc').get().then(doc => {
console.log(doc.data());
return response.send(doc.data());
}).catch(error => {
return response.status(500).send(error);
});
});
Important: Don't forget to terminate the request by calling response.redirect(), response.send(), or responses.end() so you can avoid excessive charges from functions that run for too long

Firebase Cloud Functions / Each then() should return a value or throw promise/always-return

I was following the official firebase tutorial on promises (https://www.youtube.com/watch?v=7IkUgCLr5oA) but in my case, I cannot make it work.
const promise = userRef.push({text:text});
const promise2 = promise.then((snapshot) => {
res.status(200).json({message: 'ok!'});
});
promise2.catch(error => {
res.status(500).json({message: 'error'});
});
What am I doing wrong? Each then() should have its response in case something goes wrong, but that is why I am writing the promise2 catch.
Just add the return before sending the response.
const promise = userRef.push({text:text});
const promise2 = promise.then((snapshot) => {
return res.status(200).json({message: 'ok!'});
});
promise2.catch(error => {
return res.status(500).json({message: 'error'});
});
Also you can chain the promises as follows:
return userRef.push({text:text})
.then((snapshot) => {
return res.status(200).json({message: 'ok!'});
}).catch(error => {
return res.status(500).json({message: 'error'});
});

Resources