Error message in `Firebase` function log: - firebase

I am trying to read data in Firebase - Cloud Firestore database in Dialogflow. But not able to connect to the database and getting the following error in log:
Warning, estimating Firebase Config based on GCLOUD_PROJECT.
Initializing firebase-admin may fail
Code is in welcome intent fulfillment - Inline Editor(Powered by Cloud Functions for Firebase)
Collection Name in Cloud Firestore - users
document - 10 and 11 (totally 2)
field - book_id and text
Can someone please help me resolve this one or face similar issue.?
I have tried different solutions provided for this error faced by others, but none of it has solved mine.
Tried changing node version to 8 in package.json
Tried Initializeapp as below:
const admin = require('firebase-admin');
const app = dialogflow({debug: true});
admin.initializeApp();
Its not going inside app.intent(welcome) itself
index.js
// See https://github.com/dialogflow/dialogflow-fulfillment-nodejs
// for Dialogflow fulfillment library docs, samples, and to report issues
'use strict';
const axios = require('axios');
const {dialogflow} = require('actions-on-google');
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
const admin = require('firebase-admin');
const app = dialogflow({debug: true});
admin.initializeApp();
const db = admin.firestore();
db.settings({timestampsInSnapshots: true});
const collectionRef = db.collection('users');
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
/*function welcome(agent) {
agent.add(`Welcome to my agent!`);
}*/
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
app.intent('welcome', (conv) => {
console.log("welcome agent invoked");
const book = (agent.parameters.book).toString();
const termRef = collectionRef.doc('10');
return termRef.get()
.then((snapshot) => {
const {text, book_id} = snapshot.data();
conv.ask(`Here you go, ${text}, ${book_id}. ` +
`What else do you want to know?`);
console.log('text:', +text);
console.log('book_id:', +book_id);
}).catch((e) => {
console.log('error:', e);
conv.close('Sorry, try again');
});
});
});
package.json
{
"name": "dialogflowFirebaseFulfillment",
"description": "This is the default fulfillment for a Dialogflow agents using Cloud Functions for Firebase",
"version": "0.0.1",
"private": true,
"license": "Apache Version 2.0",
"author": "Google Inc.",
"engines": {
"node": "8"
},
"scripts": {
"start": "firebase serve --only functions:dialogflowFirebaseFulfillment",
"deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment"
},
"dependencies": {
"actions-on-google": "^2.2.0",
"firebase-admin": "^5.13.1",
"firebase-functions": "^2.0.2",
"dialogflow": "^0.6.0",
"dialogflow-fulfillment": "^0.5.0"
}
}
Trying to get book_id value and text value from collection users in cloud firestore

Related

How to upload files to firebase storage using rest api in unity

I want to upload files to firebase storage using the rest API from unity. It is like clicking the browse button, browsing on local storage, selecting files, and then uploading.
How do I do this?
Create a cloud function with busyboy
Create a sample firebase function
Copy the code for package.json
Run the command to install the node modules
npm run
Copy the code for index.js
To generate a private key file for your service account:
In the Firebase console, open Settings > Service Accounts.
Click Generate New Private Key, then confirm by clicking Generate Key.
Choose Node Js
Securely store the JSON file containing the key.
Copy the firebase-admin-sdk.json file inside the function directory
Deploy the function to firebase
firebase deploy --only functions
Get the http endpoint from the firebase console
Index.js
const functions = require("firebase-functions");
const os = require("os");
const path = require("path");
const spawn = require("child-process-promise").spawn;
//To enable Cross AXIS
//Change to False to avoid DDOS
const cors = require("cors")({ origin: true });
//Parse Files
const Busboy = require("busboy");
//Files System
const fs = require("fs");
var gcconfig = {
// Get the project ID from firebaserc
projectId: "<project_id>",
// Write the name of the file in the root director which contains the private key of firebase-admin-sdk
keyFilename: "firebase-admin-sdk.json"
};
// const gcs = require("#google-cloud/storage")(gcconfig);
const {Storage} = require('#google-cloud/storage');
const gcs = new Storage(gcconfig);
exports.uploadFile = functions.https.onRequest((req, res) => {
//Allowing CROSS SITE
cors(req, res, () => {
if (req.method !== "POST") {
return res.status(500).json({
message: "Not allowed"
});
}
console.log("Starting BusBOY");
const busboy = Busboy({ headers: req.headers});
let uploadData = null;
//File parsing
busboy.on("file", (fieldname, file, filename, encoding, mimetype) => {
// Firebase cloudfunction will have some tmpdirectory tmpdir
// It will be cleaned up after execution
console.log("File function reached ");
console.log("Temp folder is "+os.tmpdir());
console.log("File name is "+filename.filename);
const filepath = path.join(os.tmpdir(),filename.filename);
console.log("Location of file is "+filepath);
uploadData = { file: filepath, type: mimetype };
console.log("Writing to temp file storage");
//Writing file to storage
file.pipe(fs.createWriteStream(filepath));
//Extra Details such as limit error
file.on('limit', () => {
console.log("Reached size limit");
debugLog(options, `Size limit reached for ${field}->${filename.filename}, bytes:${getFilesizeInBytes(filename)}`);
});
file.on('end', () => {
const size = getFilesizeInBytes(filename.filename);
console.log("File size is "+size+" bytes");
});
file.on('error', (err) => {
console.log("File format error");
});
});
//For Form data Listener
// busboy.on("field",()=>{
// });
// Finishes the whole process, only upload after that
busboy.on("finish", () => {
// Firebase storage, Inside the console itself
// Copy the folder location
// gs://<Project_id>.appspot.com
// Remove the gs String
console.log("Finished BusBoy");
var your_project_id="<your_project_id>.appspot.com";
const bucket = gcs.bucket(your_project_id);
console.log("Uploading Image to firebase");
bucket
.upload(uploadData.file, {
uploadType: "media",
metadata: {
metadata: {
contentType: uploadData.type
}
}
})
.then(() => {
// Success
console.log("Uploaded Successfully");
res.status(200).json({
message: "It worked!"
});
})
.catch(err => {
// Error
console.log("Error while uploading");
res.status(500).json({
error: err
});
});
});
//End the parsing
console.log("End Parsing");
busboy.end(req.rawBody);
});
});
//Finding the file size from the filename
function getFilesizeInBytes(filename) {
var stats = fs.statSync(filename);
var fileSizeInBytes = stats.size;
return fileSizeInBytes;
}
package.json
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions:uploadFile",
"logs": "firebase functions:log"
},
"engines": {
"node": "14"
},
"main": "index.js",
"dependencies": {
"firebase-admin": "^9.8.0",
"firebase-functions": "^3.14.1",
"#google-cloud/storage": "^6.0.1",
"busboy": "^1.6.0",
"child-process-promise": "^2.2.1",
"cors": "^2.8.5"
},
"devDependencies": {
"firebase-functions-test": "^0.2.0"
},
"private": true
}
For more details
Checkout https://github.com/katmakhan/firebase-course/tree/master/Firebase%20Cloud%20Function/Image%20Uploader

Code problem in DialogFlow Fulfillment as it does not recognize Firebase asynchronous functions

I'm having problems because the code I made in the DialogFlow Fulfillment index.js when I took the context parameters he was unable to send talking to the DialogFlow support I was informed that the DialogFlow Fulfillment does not recognize asynchronous functions so when I use the "push" from Firebase to send the parameters he doesn't send anything I believe he expects some parameter from the context but because he doesn't receive it he skips the push function and ends up not executing and doesn't send anything.
DialogFlow Fulfillment index.js code:
const functions = require('firebase-functions');
const { WebhookClient } = require('dialogflow-fulfillment');
const { Card, Suggestion } = require('dialogflow-fulfillment');
const admin = require('firebase-admin');
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: 'https://testechatbot-2020.firebaseio.com/'
});
process.env.DEBUG = 'dialogflow:debug';
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
function Mensagem(agent) {
var context = agent.context.get('awainting_nome');
var nome = context.parameters.nome;
var mensagem = agent.parameters.mensagem;
let teste = nome + " " + mensagem;
try {
admin.database().ref('Dados/').push({
Nome: nome,
Mensagem: mensagem
});
} catch (err) {
console.error(err);
return;
}
}
let intentMap = new Map();
intentMap.set('EntradaMensagem', Mensagem);
agent.handleRequest(intentMap);
});
DialogFlow Fulfillment package.json code:
{
"name": "dialogflowFirebaseFulfillment",
"description": "Fluxo com envio de parametros para o Firebase",
"version": "1.0.0",
"private": true,
"license": "Apache Version 2.0",
"author": "Google Inc.",
"esversion": 8,
"engines": {
"node": ">=10.0.0"
},
"scripts": {
"start": "firebase serve",
"deploy": "firebase deploy"
},
"dependencies": {
"#google-cloud/firestore": "^0.16.1",
"firebase-admin": "^8.13.0",
"actions-on-google": "^2.2.0",
"firebase-functions": "^3.7.0",
"dialogflow": "^1.2.0",
"dialogflow-fulfillment": "^0.6.0",
"#google-cloud/dialogflow": "^3.0.0",
"node-fetch": "^2.6.0"
}
}
Image with response from DialogFlow support about asynchronous functions
response from DialogFlow support
I'm not sure where you heard that the Intent Handler can't support async functions. They most certainly can. If you're using an async function (or a function that returns a Promise - same thing), you either must declare it an async function or return the Promise.
Your handler function should look something more like
function Mensagem(agent) {
var context = agent.context.get('awainting_nome');
var nome = context.parameters.nome;
var mensagem = agent.parameters.mensagem;
let teste = nome + " " + mensagem;
return admin.database().ref('Dados/').push({
Nome: nome,
Mensagem: mensagem
})
.then( snapshot => {
agent.add( "pushed" );
})
.catch (err => {
console.error(err);
agent.add( "Error." );
})
}

Firestore automatic backup using cloud functions?

Firebase docs recommends that you deploy an App Engine application to handle automatic firestore exports.
https://firebase.google.com/docs/firestore/solutions/schedule-export
app.js
const axios = require('axios');
const dateformat = require('dateformat');
const express = require('express');
const { google } = require('googleapis');
const app = express();
// Trigger a backup
app.get('/cloud-firestore-export', async (req, res) => {
const auth = await google.auth.getClient({
scopes: ['https://www.googleapis.com/auth/datastore']
});
const accessTokenResponse = await auth.getAccessToken();
const accessToken = accessTokenResponse.token;
const headers = {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + accessToken
};
const outputUriPrefix = req.param('outputUriPrefix');
if (!(outputUriPrefix && outputUriPrefix.indexOf('gs://') == 0)) {
res.status(500).send(`Malformed outputUriPrefix: ${outputUriPrefix}`);
}
// Construct a backup path folder based on the timestamp
const timestamp = dateformat(Date.now(), 'yyyy-mm-dd-HH-MM-ss');
let path = outputUriPrefix;
if (path.endsWith('/')) {
path += timestamp;
} else {
path += '/' + timestamp;
}
const body = {
outputUriPrefix: path
};
// If specified, mark specific collections for backup
const collectionParam = req.param('collections');
if (collectionParam) {
body.collectionIds = collectionParam.split(',');
}
const projectId = process.env.GOOGLE_CLOUD_PROJECT;
const url = `https://firestore.googleapis.com/v1beta1/projects/${projectId}/databases/(default):exportDocuments`;
try {
const response = await axios.post(url, body, { headers: headers });
res
.status(200)
.send(response.data)
.end();
} catch (e) {
if (e.response) {
console.warn(e.response.data);
}
res
.status(500)
.send('Could not start backup: ' + e)
.end();
}
});
// Index page, just to make it easy to see if the app is working.
app.get('/', (req, res) => {
res
.status(200)
.send('[scheduled-backups]: Hello, world!')
.end();
});
// Start the server
const PORT = process.env.PORT || 6060;
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
console.log('Press Ctrl+C to quit.');
});
package.json
{
"name": "solution-scheduled-backups",
"version": "1.0.0",
"description": "Scheduled Cloud Firestore backups via AppEngine cron",
"main": "app.js",
"engines": {
"node": "8.x.x"
},
"scripts": {
"deploy": "gcloud app deploy --quiet app.yaml cron.yaml",
"start": "node app.js"
},
"author": "Google, Inc.",
"license": "Apache-2.0",
"dependencies": {
"axios": "^0.18.0",
"dateformat": "^3.0.3",
"express": "^4.16.4",
"googleapis": "^38.0.0"
},
"devDependencies": {
"prettier": "^1.16.4"
}
}
cron.yaml
cron:
- description: "Daily Cloud Firestore Export"
url: /cloud-firestore-export?outputUriPrefix=gs://BUCKET_NAME[/PATH]&collections=test1,test2
target: cloud-firestore-admin
schedule: every 24 hours
QUESTION
But I was wondering, is it possible to achieve the same thing with an HTTP cloud function and the Cloud Scheduler ?
Is there anything in this App Engine code that I couldn't reproduce or access with an HTTP cloud function? I mean, is the App Engine project really necessary here?
NOTE: This is not an opinion-based question, nor it's too broad. I want to know if I need the App Engine to achieve this behavior or not, and why.
I wouldn't need to set it as an express server, obviously. Just a normal HTTP cloud function, that when called, does the exporting.
I would add something like the following cron job into the Cloud Scheduler:
You will not need App Engine at all if you use Cloud Scheduler to trigger a function that does the backup.

How to write to a cloud storage bucket with a firebase cloud function triggered from firestore?

To make sure this is as clean and isolated as possible, I created a new test project with nothing in it other than these steps.
I then enabled cloud firestore in the console. I created a new collection called "blarg" (great name, eh!). I added a document to it, and used the auto-id, then added a field named "humpty" with the string dumpty. It looks like this in the console:
I created a directory called signatures in the storage section of the firebase console. I want to write a file into this directory when my function (below) triggers.
I then added a cloud function with the following code. It shows up (I assume) correctly in the functions section, with the name testItOut and triggering on the event document.update for /blarg/{eventId}.:
const functions = require('firebase-functions');
const os = require('os');
const fs = require('fs');
const path = require('path');
require('firebase-admin').initializeApp();
exports.testItOut = functions.firestore
.document('blarg/{docId}')
.onUpdate((change, context) => {
console.log( "Inside #testItOut" );
const projectId = 'testing-60477';
const Storage = require('#google-cloud/storage');
const storage = new Storage({
projectId,
});
let bucketName = 'signatures';
let fileName = 'temp.txt';
const tempFilePath = path.join(os.tmpdir(), fileName);
console.log( `Writing out to ${tempFilePath}` );
fs.writeFileSync(tempFilePath, "something!" );
return storage
.bucket( bucketName )
.upload( tempFilePath )
.then( () => fs.unlinkSync(tempFilePath) )
.catch(err => console.error('ERROR inside upload: ', err) );
});
The package.json looks like this:
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase serve --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"dependencies": {
"#google-cloud/storage": "^1.7.0",
"firebase-admin": "~5.12.1",
"firebase-functions": "^1.0.3"
},
"devDependencies": {
"eslint": "^4.12.0",
"eslint-plugin-promise": "^3.6.0"
},
"private": true
}
If I change the value of the key "humpty" I see the function invocation. But, I get the error inside the logs.
ERROR inside upload: { ApiError: testing-60477#appspot.gserviceaccount.com does not have storage.objects.create access to signatures/temp.txt.
at Object.parseHttpRespBody (/user_code/node_modules/#google-cloud/storage/node_modules/#google-cloud/common/src/util.js:193:30)
at Object.handleResp (/user_code/node_modules/#google-cloud/storage/node_modules/#google-cloud/common/src/util.js:131:18)
...
This is as simple as it can get, I'd assume. What am I doing wrong? I thought calling initializeApp() gave my function rights to write to the storage bucket for the account automatically?
The only strange error I see is that billing is not setup. I thought this was necessary only if you use external APIs. Is Google Storage an "external API?" The log message does not indicate that's the issue.
The issue was that I mistakenly thought the call to bucket was to set the subdirectory inside the bucket. Instead of bucket('signatures') I should have made it an empty call like bucket() and then provided an options parameter (like { destination: '/signatures/temp.txt' }) for the upload call:
const functions = require('firebase-functions');
const os = require('os');
const fs = require('fs');
const path = require('path');
const admin = require('firebase-admin');
admin.initializeApp();
exports.testItOut = functions.firestore
.document('blarg/{docId}')
.onUpdate((change, context) => {
console.log( "Inside #testItOut" );
const storage = admin.storage()
let fileName = 'temp.txt';
let destination = '/signatures/temp.txt';
const tempFilePath = path.join(os.tmpdir(), fileName);
console.log( `Writing out to ${tempFilePath}` );
fs.writeFileSync(tempFilePath, "something!" );
return storage
.bucket()
.upload( tempFilePath, { destination } )
.then( () => fs.unlinkSync(tempFilePath) )
.catch(err => console.error('ERROR inside upload: ', err) );
});
I got confused because I saw lots of examples in firebase docs where there are calls to bucket("albums") (like this: https://cloud.google.com/nodejs/docs/reference/storage/1.6.x/Bucket#getFiles) and assumed this meant the bucket can be used to set a subdirectory for uploads. It's obvious to me now the difference, but I was thrown off because the calls to bucket did not look like bucketname-12345.appspot.com as I see in the firebase storage console.
Don't bother trying to use the Google Cloud Storage SDK like this:
const Storage = require('#google-cloud/storage');
const storage = new Storage({
projectId,
});
Instead, just use the Admin SDK after you initialize it. The Admin SDK wraps the Google Cloud Storage SDK. When you initialize admin, you are also implicitly initializing all of the components it wraps.
const admin = require('firebase-admin')
admin.initializeApp()
const storage = admin.storage()
// Now use storage just like you would use #google-cloud/storage
The default service account that's used when you init admin with no args like this should have write access to your project's default storage bucket.

Firebase app not accessing functions (e.g. firebase.auth())

I'm having this problem that I've spent hours looking into but can't seem to figure out. I'm trying to use Firebase with Node.js, but am unable to access the different Firebase functions (e.g. firebase.auth() and firebase.firestore()). However, when I initialize via the admin SDK, those end up working. Below is my simple code as well as the error:
const functions = require('firebase-functions');
const firebase = require('firebase');
const app = express();
firebase.initializeApp(functions.config().firebase);
const auth = firebase.auth();
const db = firebase.firestore();
And the resulting error: TypeError: firebase.auth is not a function
Note I also had a few lines initializing express and handlebars but doubt those would have had any impact.
I've also tried including "require("firebase/auth")" but that hasn't worked. Below is also my package.json, specifically the modules I have installed:
"dependencies": {
"consolidate": "^0.15.1",
"express": "^4.16.3",
"firebase": "^5.0.4",
"firebase-admin": "~5.12.1",
"firebase-functions": "^1.0.4",
"firebase-tools": "^3.18.6",
"firebaseauth": "^1.0.0",
"handlebars": "^4.0.11"
},
Any help please? I'm a bit of a novice web developer so any advice would be much appreciated. Thank you!
Cloud Functions triggers have a format like this:
exports.myFunctionName = functions.firestore
.document('users/marie').onWrite((change, context) => {
// ... Your code here
});
You cannot access the client-side SDKs from inside a Cloud Function. Instead, if there are features you want to access inside a Cloud Function, you can use the Admin SDKs.
var admin = require('firebase-admin');
var app = admin.initializeApp();
Short answer: Use the admin SDK, that's what it's for - Cloud Functions to interact with the rest of your backend.
Longer answer:
To help you along, here is an example of a working index.js file for a Firebase Cloud Function that accesses both the database (not firestore) and the auth portion of Firebase... with some comments to provide context. It's an API endpoint that accepts a user's token, verifies that the user is an 'admin' in my system, and then creates a new user account before responding with the new UID.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.createUser = functions.https.onRequest( (req, res) => {
// Look at the accessToken provided in the request, and have Firebase verify whether it's valid or not
admin.auth().verifyIdToken(req.body.accessToken).then(decodedIdToken => {
// Token was valid, pull the UID out of the token for the user making this request
let requestorUid = decodedIdToken.uid;
// Make a query to the database to see what that user's role is
admin.database().ref(`userBasicInfo/${requestorUid}/role`).once('value').then( dataSnapshot => {
if (dataSnapshot.val() === 'admin') {
// If their role is "admin" then let's create a user!
let userEmail = `${req.body.newuserid}#company.org`;
admin.auth().createUser({
email: userEmail,
password: req.body.password
}).then(userRecord => {
// See the UserRecord reference doc for the contents of userRecord.
res.send({uid: userRecord.uid});
}).catch( error => {
console.log("Error creating user:");
console.log(error)
res.send(false);
});
} else {
console.log('Checked the database for the UID provided in the access token, role was not "admin".');
res.send(false);
}
}).catch( err => {
console.log('Error finding user in database:');
console.log(err);
res.send(false);
})
}).catch( error => {
console.error('Error while verifying Firebase ID token:');
console.log(error);
res.send(false);
});
});
And the package.json would be:
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"serve": "firebase serve --only functions",
"shell": "firebase experimental:functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"dependencies": {
"firebase-admin": "^5.11.0",
"firebase-functions": "^1.0.2"
},
"private": true
}

Resources