unable to split Firebase functions in multiple files - javascript

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

Related

Is there a way to consolidate the imports/requires into one line in javascript?

I have many different modules in a project, each module has it's own folder, within each module, there are 3 files:
index.js
index.test.js
data.js
Every module has these files, same file names and different content.
For the index.test.js files, the import statements are all the same for all index.test.js files in all modules, look like this:
const runAnalysis = require('./index');
const { data } = require('./data');
const { formatData } = require('utils');
const formattedData = formatData(data);
Since these import statements are the same for all index.test.js files, is there a way to consolidate these into one line, so it doesn't have to be repeated over and over in every module?
Is there a way to put these import statements in a header file, and then each index.test.js can just import the header file?
Not sure if this is efficient or recommended, but it works.
Create a util js file and it can live at the project root directory, let's name it import-util.js with the following code.
const { formatData } = require('utils');
const getMyImports = (dirname) => {
const runAnalysis = require(`${dirname}/index`);
const { data } = require(`${dirname}/data`);
const formattedData = formatData(data);
return {
runAnalysis,
formattedData
}
};
exports.getMyImports = getMyImports;
Then this file can be used like this to get the runAnalysis and formattedData
const { runAnalysis, formattedData } = require('../import-util').getMyImports(__dirname);
__dirname is the trick to make it work, it provides the directory path for the index.js and data.js to be imported.
This single line can now be used in every module folder, given the folder contains the index.js and data.js, it works the same as the following 4 lines of code.
const runAnalysis = require('./index');
const { data } = require('./data');
const { formatData } = require('utils');
const formattedData = formatData(data);

How do I write a function in a node.js file, and then call that function in a regular js file

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/

How access variable request/ response in another file js

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.

Nodejs pass log instance between modules

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}) {
};

Upload folder(s) on firebase - javascript

Can we upload empty folders or simply folders who contains many files in it on the firebase storage ?
Because actually i can upload one files but too, multiples files, but i didn't find how to do it with folders.
I'd suggest you to go to Google Cloud (Firebase projects live in the Google Cloud as well), and check your storage buckets there. You'll be able to see an upload folder option there, which you can use to upload folders through a GUI. You can drag and drop multiple folders if you wish.
There is no way to upload an entire folder to Cloud Storage for Firebase in one go. You will have to upload the individual files in the folder.
The is no concept of an empty folder in Cloud Storage for Firebase. Folders only exist by the fact that they have files in them.
Also see:
Retrieve multiple photos under a node from Firebase Storage
How can i upload multiple files to firebase storage at once?
How to upload multiple files to Firebase?
To do this programmatically, the best solution is to:
(1) Recursively get a list of all the files in the folder you wish to upload
(2) Upload all files in one hit with Promise.all
This approach works because, inter alia, firebase creates missing storage paths for you
The code (written in TS) for (1) and (2) follows
RECURSIVELY GET A LIST OF FILES
import { Dirent, readdirSync } from 'fs'
import path from 'path'
import { IPath } from '../interfaces/i-path'
import { escapeRegExp } from 'lodash'
interface IDirent {
dirent: Dirent
path: string
}
const getAllFiles = (dir: string): IDirent[] => {
const dirents: IDirent[] = readdirSync(dir, { withFileTypes: true }).map((dirent: Dirent) => ({ dirent, path: path.resolve(dir, dirent.name) }))
return dirents.reduce((acc: IDirent[], dirent: IDirent) => {
if (dirent.dirent.isDirectory()) return [...acc, ...getAllFiles(dirent.path)]
if (dirent.dirent.isFile()) return [...acc, dirent]
return acc
}, [])
}
export const getAllFilesInFolder = (dir: string): IPath[] => {
const regex = new RegExp(`^${escapeRegExp(dir)}`)
return getAllFiles(dir).map((dirent: IDirent) => {
let shortPosixPath: string = dirent.path.replace(regex, '')
shortPosixPath = shortPosixPath.split(path.sep).join(path.posix.sep)
if (shortPosixPath.startsWith(path.posix.sep)) shortPosixPath = shortPosixPath.substring(1)
return { fullPath: dirent.path, shortPosixPath }
})
}
UPLOAD ALL FILES IN ONE HIT
import os from 'os'
import { getAllFilesInFolder } from '../../utils/get-all-files-in-folder'
import { IPath } from '../../interfaces/i-path'
import admin from 'firebase-admin'
import { getObjectPath } from '../../utils/string-utils'
import path from 'path'
import * as functions from 'firebase-functions'
// the following code will live inside some function
const storageBasePath = 'videos-out/test-videos/video-14/hls'
const dir: string = '/temp/my-folder-to-upload'
const files: IPath[] = getAllFilesInFolder(dir)
// object.bucket is just a string and is the bucket you are uploading to - e.g. something.appspot.com
const promises = files.map((file: IPath) => {
const destination = `${storageBasePath}/${file.shortPosixPath}`
return admin.storage().bucket(object.bucket).upload(file.fullPath, { destination })
})
Promise.all(promises).then(
() => console.log('success')
).catch(
() => console.log('failure')
)
Finally, the interface IPath is simple
export interface IPath {
fullPath: string
shortPosixPath: string
}

Categories

Resources