I have a react native app that uses Firebase/firestore. For uploading images, I am using "react-native-fetch-blob" to create a Blob.
I am trying to fetch doc from firestore, but my app is blocked and not getting any response from firestore (not catch / nothing => just passing thru the code).
Is there anything I can do for getting docs from firestore?
Below is my code
import firebase from "../../../firebaseConfig";
import RNFetchBlob from "react-native-fetch-blob";
// Prepare Blob support
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
window.Blob = Blob;
const originalXMLHttpRequest = window.XMLHttpRequest;
const originalBlob = window.Blob;
var firebaseUid = "";
componentDidMount = async () => {
firebaseUid = await firebase.auth().currentUser.uid;
this.getMyStory();
};
getMyStory = async () => {
window.XMLHttpRequest = originalXMLHttpRequest;
window.Blob = originalBlob;
var docRef = await firebase
.firestore()
.collection("demo")
.doc(firebaseUid)
.collection("ArrayDoc");
docRef
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
console.log(doc.id, " => ", doc.data());
});
})
.catch(error => {
console.log("error", JSON.stringify(error));
});
};
Error Screen
Related
I am also using getStaticprops from next.js.
I am trying to fetch data , I am following the code on firebase documentation, but i keep running into a error
export const getStaticProps = async () => {
const res = await fetch(
"https://jsonplaceholder.typicode.com/photos?_limit=4"
);
const images = await res.json();
console.log(auth.currentUser);
//
const userRef = ref(database, "users/" + auth.currentUser.uid);
onValue(userRef, (snapshot) => {
const properties = snapshot.val();
console.log(properties);
});
//
return {
props: {
images,
properties,
},
};
};
I'm trying to load data in to Firestore using firebase functions from the function storeCategories(), it currently just runs on start but I plan to schedule it once I have it working.
This function works fine on the emulators but in production I'm getting the either 'socket hang up' or 'Client network socket disconnected before secure TLS connection was established'
storeTrending() works fine so I'm not sure what is causing it.
/** #format */
const functions = require("firebase-functions");
const cors = require("cors");
const express = require("express");
const fetch = require("node-fetch");
const apiKey = functions.config().tmdbapi.key;
const admin = require("firebase-admin");
admin.initializeApp();
const db = admin.firestore();
var app = express();
app.use(cors());
const storeCategories = async () => {
try {
// GET CATEGORY IDS
const categoriesIDRes = await fetch(
`https://api.themoviedb.org/3/genre/movie/list?api_key=${apiKey}`
);
const categoriesInfo = await categoriesIDRes.json();
// CREATE CATEGORY API LINKS
const categoriesURLs = categoriesInfo.genres.map(
(el) =>
`https://api.themoviedb.org/3/discover/movie?api_key=${apiKey}&language=en-US&sort_by=popularity.desc&include_adult=true&include_video=true&page=1&with_genres=${el.id}&with_watch_monetization_types=flatrate`
);
// RETRIEVE POPULAR FILMS FROM EACH CATEGORY
const categoriesRes = await Promise.all(
categoriesURLs.map((el) => fetch(el))
);
const data = await Promise.all(
categoriesRes.map((el) => el.json())
);
const batch = db.batch();
// WRITE TO FIRESTORE FOR FASTER INITIAL LOAD
data.forEach((category, idx) => {
const ref = db
.collection("categories")
.doc(categoriesInfo.genres[idx].name);
batch.set(ref, category);
});
const categoriesInfoRef = db.collection("categoryIDs").doc("IDs");
batch.set(categoriesInfoRef, categoriesInfo);
return batch.commit();
} catch (err) {
console.log(err);
return;
}
};
storeCategories();
const storeTrending = async () => {
try {
const res = await fetch(
`https://api.themoviedb.org/3/trending/all/week?api_key=${apiKey}`
);
const data = await res.json();
db.collection("categories").doc("trending").set(data);
} catch (err) {
console.log(err);
return;
}
};
storeTrending();
Any help with this would be greatly appreciated!
Edit: screenshot of the error
I am trying to use a Firebase callable function to create a Twilio token for a React project. The project should allow video calls using Twilio's webRTC service. The code is based on the example here https://www.twilio.com/blog/video-chat-react-hooks. I am attempting to move the server code to obtain the Twilio token for the video service into the Firebase callable function.
My console gives the following output and errors:
error check 1: handleSubmit called
error check 2: getToken function retrieved from functions ƒ (r){return n.call(e,r,t||{})}
error check 4: token set
POST https://us-central1-vid-chat-app.cloudfunctions.net/getToken 500
Uncaught (in promise) Error: INTERNAL
at new m (error.ts:66)
at b (error.ts:175)
at P.<anonymous> (service.ts:244)
at tslib.es6.js:100
at Object.next (tslib.es6.js:82)
at a (tslib.es6.js:71)
Here is the error from the Firebase console
Unhandled error Error: accountSid is required
at new AccessToken (/srv/node_modules/twilio/lib/jwt/AccessToken.js:213:28)
at generateToken (/srv/index.js:9:12)
at videoToken (/srv/index.js:23:19)
at exports.getToken.functions.https.onCall (/srv/index.js:42:19)
at func (/srv/node_modules/firebase-functions/lib/providers/https.js:273:32)
at corsHandler (/srv/node_modules/firebase-functions/lib/providers/https.js:293:44)
at cors (/srv/node_modules/cors/lib/index.js:188:7)
at /srv/node_modules/cors/lib/index.js:224:17
at originCallback (/srv/node_modules/cors/lib/index.js:214:15)
at /srv/node_modules/cors/lib/index.js:219:13
These are the two main files I believe are relevant
React file
import React, { useState, useCallback } from 'react';
import firebase from './firebase'
import Lobby from './Lobby';
import Room from './Room';
const VideoChat = () => {
const [username, setUsername] = useState('');
const [roomName, setRoomName] = useState('');
const [token, setToken] = useState(null);
const handleUsernameChange = useCallback(event => {
setUsername(event.target.value);
}, []);
const handleRoomNameChange = useCallback(event => {
setRoomName(event.target.value);
}, []);
const handleSubmit = useCallback(async event => {
console.log("error check 1: handleSubmit called")
event.preventDefault();
const getToken = firebase.functions().httpsCallable('getToken')
console.log("error check 2: getToken function retrieved from functions", getToken)
getToken({ identity: username, room: roomName })
.then(result => {
console.log("error check 3: calling .then")
setToken(result.data)
})
console.log("error check 4: token set")
}, [username, roomName]);
const handleLogout = useCallback(event => {
setToken(null);
}, []);
let render;
if (token) {
render = (
<Room roomName={roomName} token={token} handleLogout={handleLogout} />
);
} else {
render = (
<Lobby
username={username}
roomName={roomName}
handleUsernameChange={handleUsernameChange}
handleRoomNameChange={handleRoomNameChange}
handleSubmit={handleSubmit}
/>
);
}
return render;
};
export default VideoChat;
Firebase function
const twilio = require("twilio");
const functions = require('firebase-functions');
const config = require('./config');
const AccessToken = twilio.jwt.AccessToken;
const { VideoGrant } = AccessToken;
const generateToken = config => {
return new AccessToken(
config.twilio.accountSid,
config.twilio.apiKey,
config.twilio.apiSecret
);
};
const videoToken = (identity, room, config) => {
let videoGrant;
if (typeof room !== "undefined") {
videoGrant = new VideoGrant({ room });
} else {
videoGrant = new VideoGrant();
}
const token = generateToken(config);
token.addGrant(videoGrant);
token.identity = identity;
return token;
};
const sendTokenResponse = (token, res) => {
res.set('Content-Type', 'application/json');
res.send(
JSON.stringify({
token: token.toJwt()
})
);
};
exports.getToken = functions.https.onCall((data, context)=>{
console.log('Peter error check getToken function called')
const identity = data.identity;
const room = data.room;
const token = videoToken(identity, room, config);
sendTokenResponse(token, res);
console.log('here is the token', token)
return token
});
Config file also contained in Firebase cloud function folder
module.exports = {
twilio: {
accountSid: process.env.TWILIO_ACCOUNT_SID,
apiKey: process.env.TWILIO_API_KEY,
apiSecret: process.env.TWILIO_API_SECRET,
chatService: process.env.TWILIO_CHAT_SERVICE_SID,
outgoingApplicationSid: process.env.TWILIO_TWIML_APP_SID,
incomingAllow: process.env.TWILIO_ALLOW_INCOMING_CALLS === "true"
}
};
my approach to do this is:
1) Environment Configuration: store your environment data.
firebase functions:config:set twilio.sid="The Account SID" twilio.apikey="The api key" twilio.apisecret="The api secret"
2) Firebase Function: use https.onCall
const functions = require('firebase-functions');
const AccessToken = require('twilio').jwt.AccessToken;
const VideoGrant = AccessToken.VideoGrant;
*//call environment data*
const accountSid = functions.config().twilio.accountsid;
const apiKey = functions.config().twilio.apikey;
const apiSecret = functions.config().twilio.secret;
*//function to generate token*
const generateToken = () => {
return new AccessToken(accountSid, apiKey, apiSecret);
};
*//function to generate video grant*
const videoToken = (identity, room) => {
videoGrant = new VideoGrant({ room });
const token = generateToken();
token.addGrant(videoGrant);
token.identity = identity;
return token;
};
*//firebase function*
exports.twilio = functions.https.onCall((data, context) => {
const identity = data.identity;
const room = data.room;
const token = videoToken(identity, room);
return token.toJwt();
}});
3) React code:
const handleSubmit = useCallback( async () => {
try {
const twilioRequest = firebase
.functions()
.httpsCallable('twilio');
const response = await twilioRequest({
identity: username,
room: roomName,
});
setToken(response.data);
} catch (err) {
console.log(err);
}
},[username, roomName]);
4) Allowing unauthenticated HTTP function invocation
Be sure you have permissions granted https://cloud.google.com/functions/docs/securing/managing-access-iam#allowing_unauthenticated_function_invocation
My method does manage to load the image from expo to firebase storage but I can't seem to get the download URL.
const uploadImage = async (uri) => {
const uniqid = () => Math.random().toString(36).substr(2, 9);
const ext = uri.split('.').pop(); // Extract image extension
const filename = `${uniqid()}.${ext}`; // Generate unique name
const response = await fetch(uri);
const blob = await response.blob();
var ref = firebase
.storage()
.ref()
.child('images/' + filename);
ref.getDownloadURL().then((url) => console.log(url));
return ref.put(blob);
};
Here is the error I get
FirebaseStorageError {
"code_": "storage/object-not-found",
"message_": "Firebase Storage: Object 'images/gebwu7tnh.jpg' does not exist.",
"name_": "FirebaseError",
"serverResponse_": "{
"error": {
"code": 404,
"message": "Not Found. Could not get object",
"status": "GET_OBJECT"
}
}"
This is what I found that helped me from researching. I did refactor my firebase to use a higher order component. Here is my firebase method.
uploadImageAsync: async (uri) => {
const uniqid = () => Math.random().toString(36).substr(2, 9);
const ext = uri.split('.').pop(); // Extract image extension
const filename = `${uniqid()}.${ext}`; // Generate unique name
const ref = firebase
.storage()
.ref()
.child('images/' + filename);
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
console.log(e);
reject(new TypeError('Network request failed'));
};
xhr.responseType = 'blob';
xhr.open('GET', uri, true);
xhr.send(null);
});
const snapshot = await ref.put(blob);
blob.close();
const imgUrl = await snapshot.ref.getDownloadURL();
console.log(imgUrl);
return imgUrl;
},
};
Here is how I implemented it in my component
const setImage = async (uri) => {
try {
return await firebase.uploadImageAsync(uri);
} catch (error) {
console.log(error);
}
};
I am trying to have a flexible Cloud Function that executes on different end points.
My original Cloud Function looks like this:
const functions = require('firebase-functions')
const admin = require('firebase-admin')
const _ = require('lodash')
const { getObjectValues } = require('./helper-functions.js')
admin.initializeApp()
const json2csv = require('json2csv').parse
exports.csvJsonReport = functions.https.onRequest((request, response) => {
const db = admin.firestore()
const userAnswers = db.collection('/surveys/CNA/submissions')
return (
userAnswers
.get()
// eslint-disable-next-line promise/always-return
.then(querySnapshot => {
let surveySubmissions = []
querySnapshot.forEach(doc => {
const userSubmission = doc.data()
surveySubmissions.push({
..._.mapValues(userSubmission.answers, getObjectValues), // format answers
...userSubmission.anonUser,
})
})
const csv = json2csv(surveySubmissions)
response.setHeader('Content-disposition', 'attachment; filename=cna.csv')
response.set('Content-Type', 'text/csv')
response.status(200).send(csv)
})
.catch(error => {
console.log(error)
})
)
})
I am trying to extend this function to work on multiple collections. In the above function I am targeting the CNA collection. so instead of db.collection('/surveys/CNA/submissions/') I would like it to be db.collection('/surveys/:surveyId/submissions/')
Below is my attempt at trying to extend my original Cloud Function:
const functions = require('firebase-functions')
const admin = require('firebase-admin')
const express = require('express')
const bodyParser = require('body-parser')
const _ = require('lodash')
const { getObjectValues } = require('./helper-functions.js')
admin.initializeApp(functions.config().firebase)
const db = admin.firestore()
const app = express()
const main = express()
main.use('/api/v1', app)
main.use(bodyParser.json())
exports.webApi = functions.https.onRequest(main)
app.get('surveys/:id', (request, response) => {
const surveyId = request.query
const userAnswers = db.collection(`/survey/${surveyId}/submissions`)
return (
userAnswers
.get()
// eslint-disable-next-line promise/always-return
.then(querySnapshot => {
let surveySubmissions = []
querySnapshot.forEach(doc => {
const userSubmission = doc.data()
surveySubmissions.push({
..._.mapValues(userSubmission.answers, getObjectValues), // format answers
...userSubmission.anonUser,
})
})
const csv = json2csv(surveySubmissions)
response.setHeader('Content-disposition', 'attachment; filename=cna.csv')
response.set('Content-Type', 'text/csv')
response.status(200).send(csv)
})
.catch(error => {
console.log(error)
})
)
})
When I request my endpoint: myapp.firebaseapp.com/api/v1/surveys/CNA
Cannot GET /api/v1/surveys/CNA is shown in my browser.
Could someone please point me in the right direction?
To crate a GET /survey/:id endpoint in order to fetch a submission by id, use the following code in your new Cloud Function:
app.get('surveys/:id', (request, response) => {
const surveyId = request.params.id
const userAnswers = db.collection(`/survey/${surveyId}/submissions`)
Let me know if it works for you.