I have a lot of function in my index.js for my webhook, and i wish split functions to different js files to my code is more clean.
I have not problem to use agent, but I don't know how I can have an access of variable "request" (to have the parameters receipt) in another file.
I tried this :
Index.js
// See https://github.com/dialogflow/dialogflow-fulfillment-nodejs
// for Dialogflow fulfillment library docs, samples, and to report issues
'use strict';
const functions = require('firebase-functions');
const { WebhookClient } = require('dialogflow-fulfillment');
const { Payload } = require('dialogflow-fulfillment');
const Test = require("./Test"); //File with an function
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
}
let intentMap = new Map();
intentMap.set("intent-test", Test.welcome);
intentMap.set("intent-test", fallback);
agent.handleRequest(intentMap);
});
Test.js
exports.welcome = (agent) => {
agent.add("Hello World !");
console.log(request); // How can I have a access to the variable "request" / "response
};
Do you have a solution please ?
While I understand that you are not having any problems with your agent, there are some limitations when using the DialogFlow Inline editor. As you can see below, one of the limitations is that you can only work with two files: index.js and package.json. That means that all your Fulfillments should be inside your index.js file and you can't split it in more files.
Related
I am trying to use pino for logging in to my node app Server and I do have some large logs coming, so rotating the files every day would be more practical.
So I used pino.multistream() and require('file-stream-rotator')
My code works, but for performance reasons, I would not like to use the streams in the main thread.
according to the doc, I should use pino.transport():
[pino.multistream()] differs from pino.transport() as all the streams will be executed within the main thread, i.e. the one that created the pino instance.
https://github.com/pinojs/pino/releases?page=2
However, I can't manage to combine pino.transport() and file-stream-rotator.
my code that does not work completely
-> logs the first entries, but is not exportable because it blocks the script with the error
throw new Error('the worker has exited')
Main file
const pino = require('pino')
const transport = pino.transport({
target: './custom-transport.js'
})
const logger = pino(transport)
logger.level = 'info'
logger.info('Pino: Start Service Logging...')
module.exports = {
logger
}
custom-transport.js file
const { once } = require('events')
const fileStreamRotator = require('file-stream-rotator')
const customTransport = async () => {
const stream = fileStreamRotator.getStream({ filename: 'myfolder/custom-logger.log', frequency: 'daily' })
await once(stream, 'open')
return stream
}
module.exports = customTransport
I have initialized node in a folder, created my file, and written my function. In another folder, higher up the file system hierarchy, I wrote a script tag, with the source being the node.js file. I called the function, but as soon as I test it in a browser, I get this error:
resetPassword is not defined
Here is my node.js file code:
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
var admin = require("firebase-admin");
var serviceAccount = require("./troop-30-elections-web-app-firebase-adminsdk-obsmr-61cc4bb59e.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://troop-30-elections-web-app.firebaseio.com"
});
const resetPassword =(docId,newPass) => {
return admin.auth().updateUser(docId, {
password: newPass
})
.then(() => {
return {"text": "User Password Successfully Updated"}; // this is what gets sent back to the app
});
}
Here is my regular js code:
resetForm.addEventListener('submit', (e) => {
console.log("Step 1");
e.preventDefault();
let newPass = resetForm['reset-password'].value;
resetPassword({docId: docId, newPass: newPass}).then(() => {
const modal = document.querySelector('#modal-reset');
M.Modal.getInstance(modal).close();
resetForm.reset();
});
console.log("Step 1.5");
});
I think node uses ES5 syntax. I believe you haven't imported the function from one file to the other.
First, you might have to export the function from its home file, so that you can then import it in whatever file you want.
I think this link will help you out:
https://nodejs.org/en/knowledge/getting-started/what-is-require/
I’ve logger which I initiate using a constractor in the index.js file. Now I need
To pass the logger instance to other files, and I do it like this
index.js
const books = require(“./books”);
books(app, logger);
logger = initLogger({
level: levels.error,
label: “app”,
version: "0.0.1",
});
app.listen(port, () => logger.info(`listening on port ${port}`));
And inside the books.js file I use it like following, get the logger from the index.js file and use it
inside the books.js file, also pass it to another file with the function isbn.get(books, logger);,
Is it recommended to do it like this? Is there a cleaner way in nodes ?
books.js
const isbn = require(“./isbn”);
module.exports = async function (app, logger) {
…
try {
Let books = await getBooks();
logger.info(“get “books process has started”);
} catch (err) {
logger.error("Failed to fetch books", err);
return;
}
…
// this function is from the file “isbn” and I should pass the logger to it also
try {
let url = await isbn.get(books, logger);
} catch (e) {
res.send(e.message);
}
}
Try creating a module specifically for your logger configuration, then you can import that into your modules instead of using a side-effect of your business module to create a logger.
This will help if you ever need/want to change your logger configuration - instead of following a chain of business methods, you can just update the log configuration.
Example
logger.js
'use strict';
// Any setup you need can be done here.
// e.g. load log libraries, templates etc.
const log = function(level, message) {
return console.log(level + ": " + message);
};
module.exports = log;
business-logic.js
'use strict';
var log = require('./logger');
var stuff = require('./stuff');
const do_stuff = function (thing) {
// do stuff here
log("INFO", "Did stuff");
}
This is a pretty clean way of doing it, however it could be awkward when trying to share more variables or adding more requires. So, you could put all the variables in an object and destructure only the variables you need in books.js:
index.js:
const state = {app, logger, some, other, variables};
require("./books")(state);
require("./another_file")(state);
books.js:
module.exports = async function ({app, logger}) {
};
I have updated the question as found the root cause of the issue.
As I have hosted my React SSR app which uses firebase database in the client serving by one of the cloud function named app throwing an error of Error: FIREBASE FATAL ERROR: Database initialized multiple times. Please make sure the format of the database URL matches with each database() call.. When I comment out one by one and deploy, works perfectly. But when I deploy together doesn't work. How do I separate these two keeping both at the same repo?
ORIGINAL Question: Why firebase cloud function throwing an error of 'The default Firebase app does not exist.'?
So I am trying out firebase function for the first time. admin.messaging() throwing me the following error. Help me figure out why?
If I look at the console I get results till console.log('deviceToken', deviceToken);
so whats wrong in const messageDone = await admin.messaging().sendToDevice(deviceToken, payload);?
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.updateUnreadCount = functions.database.ref('/chats/{chatId}/{messageId}')
.onCreate(async(snap, context) => {
const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
appOptions.databaseAuthVariableOverride = context.auth;
const adminApp = admin.initializeApp(appOptions, 'app');
const { message, senderId, receiverUid } = snap.val();
console.log(message, senderId, receiverUid);
console.log('------------------------');
const deleteApp = () => adminApp.delete().catch(() => null);
try {
const db = adminApp.database();
const reciverUserRef = await db.ref(`users/${receiverUid}/contacts/${senderId}/`);
console.log('reciverUserRef', reciverUserRef);
const deviceTokenSnapshot = await reciverUserRef.child('deviceToken').once('value');
const deviceToken = await deviceTokenSnapshot.val();
console.log('deviceToken', deviceToken);
const payload = {
notification: {
title: 'Test Notification Title',
body: message,
sound: 'default',
badge: '1'
}
};
const messageDone = await admin.messaging().sendToDevice(deviceToken, payload);
console.log('Successfully sent message: ', JSON.stringify(messageDone));
return deleteApp().then(() => res);
} catch (err) {
console.log('error', err);
return deleteApp().then(() => Promise.reject(err));
}
});
Update1: According to this https://firebase.google.com/docs/cloud-messaging/send-message#send_to_a_topic, admin.messaging().sendToDevice(deviceToken, payload) APIs are only available in the Admin Node.js SDK?
So switched to
const payload = {
data: {
title: 'Test Notification Title',
body: message,
sound: 'default',
badge: '1'
},
token: deviceToken
};
const messageDone = await admin.messaging().send(payload);
Which is not working either. Getting an error Error: The default Firebase app does not exist. Make sure you call initializeApp() before using any of the Firebase services. Any lead will be helpful.
EDIT: Finally got the function working.
My index.js is exporting to functions, follwoing
exports.app = functions.https.onRequest(app); //React SSR
exports.updateChat = functions.database.ref('/chats/{chatId}/{messageId}').onCreate(updateChat);
exports.app is a react ssr function, which I am using to host my site. This uses database too. and throwing error of multiple database instance.
When I comment out one by one and deploy, works perfectly. But when I deploy together doesn't work. How do I separate these two keeping both at the same repo? Any suggestions, please?
You can initialise db outside export function.
const admin = require('firebase-admin');
const adminApp = admin.initializeApp(appOptions, 'app')
//continue code
Update:
const admin = require('firebase-admin');
const adminApp = admin.initializeApp(options);
async function initialize(options, apps = 'app') {
try {
const defaultApp = adminApp.name
if(defaultApp) {
const adminApp1 = admin.initializeApp(apps);
}else {
const adminApp1 = admin.initializeApp(options, apps);
}
}catch(err) {
console.error(err);
}
}
Modify this snippet as per your need and try it out
It abstracts initialize of app in another function. Just call this function at appropriate place in your code.
I'm working with firebase functions and arrived to hundreds of functions, and now it is very hard to manage it in single index.js file as shown in their lots of examples
I tried to split that functions in multiple files like:
--firebase.json
--functions
--node_modules
--index.js
--package.json
--app
--groupFunctions.js
--authFunctions.js
--storageFunctions.js
In this structure i divide my functions in three categories and put in that three files groupFunctions.js, authFunctions.js, and storageFunctions.js. I then tried to import thise files in index.js, but I don't know why it is not working for me.
Here is groupFunctions.js
var functions = require('firebase-functions');
module.exports = function(){
exports.onGroupCreate = functions.database.ref('/groups/{groupId}')
.onWrite(event => {
console.log(`A group is created in database named:${event.params.groupId}.`);
// some logic...
//...
})
}
Here is index.js file:
var functions = require('firebase-functions');
module.exports = require("./app/groupFunctions")();
My editor not giving any warning in this code. But when I deploy this code with firebase deploy --only functions, it does not deploy function. If some functions already exist on firebase console, it remove all functions on deploy.
here is deployment logs:
question is also asked on github
Working code example:
file structure:
--firebase.json
--functions
--node_modules
--index.js
--package.json
--src
--groupFunctions.js
--authFunctions.js
--storageFunctions.js
index.js file:
require('./src/groupFunctions.js')(exports);
require('./src/authFunctions.js')(exports);
require('./src/storageFunctions.js')(exports);
groupFunctions.js file:
var functions = require('firebase-functions');
module.exports = function (e) {
e.onGroupCreate = functions.database.ref('/groups/{groupId}')
.onWrite(event => {
console.log(`A group is created in database named:${event.params.groupId}.`);
// some logic...
//...
})
}
UPDATE: now I have better solution
The full working code is located at https://github.com/malikasinger1/firebase-functions-with-typescript and it's written with cutting edge tech like typescript and webpack. You may use this as a boilerplate/starter.
You can pass the exports object to the function in groupFunctions.js, like this:
var functions = require('firebase-functions');
module.exports = function (e) {
e.onGroupCreate = functions.database.ref('/groups/{groupId}')
.onWrite(event => {
console.log(`A group is created in database named:${event.params.groupId}.`);
// some logic...
//...
})
}
Now, in index.js:
var functions = require('firebase-functions');
require("./app/groupFunctions")(module.exports);
The way it works is that modules.exports is a regular JavaScript object, so you can add new properties to that from wherever you want.
//index.js
const glob = require('glob')
const files = glob.sync('./**/*.functions.js', { cwd: __dirname,
ignore: './node_modules/**' })
files.forEach(file => {
const functionModule = require(file)
const functionNames = Object.keys(functionModule)
functionNames.forEach(functionName => {
if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME ===
functionName) {
exports[functionName] = functionModule[functionName]
}
})
})
Folders like so.. will work
//Example Home.functions.js :
exports.Home = functions.https..
If you want support for multiple functions inside a file, this Gist show a real example of how to do it
https://gist.github.com/saintplay/3f965e0aea933a1129cc2c9a823e74d7#file-index-js
You can dynamically import functions for every *.function.js