I know this was already asked here, BUT it does not answer my question. My question is how can we break apart index.js for Cloud Functions including the onWrite calls and such.
I realize you can use "require" and bring in outside code. It still leaves a bit of code ( in Franks OCR example, for example) , in index.js.
Ideally I'd love to be able to move an entire onWrite event trigger to another file.
EXAMPLE in index.js:
exports.sysQueueUserNew = functions.database.ref("/sys/queue/user_new/{queueId}").onWrite((event) => {
// do something
})
How can I move the entire function event watch/call to another js file, and call it from index.js?
My index.js has grown quite large, and reading it for organizational purposes has become a PAIN.
Ideally I'd like my index.js to be very organized such as:
--in index.js --
/// User cleanup
userHelpers()
/// SYS Logs
sysLogs()
--and in userHelpers.js have the onWrite trigger for example---
functions.database.ref("/sys/queue/user_new/{queueId}").onWrite((event) => {
// create user
})
etc....
Is this possible without having to have code written like so (a' la Franks OCR example):
var test = require('./test')
exports.sysQueueUserNew = functions.database.ref("/sys/queue/user_new/{queueId}").onWrite((event) => {
// do something
test.doCleanup()
})
Thanks in advance....
You can easily spread your functions across multiple files. Here's an example:
////////////// index.js
exports.sysQueueUserNew = require('./sys-queue-user-new');
exports.userCleanup = require('./user-cleanup');
///////////// sys-queue-user-new.js
const functions = require('firebase-functions');
module.exports = functions.database
.ref("/sys/queue/user_new/{queueId}")
.onWrite(e => {
// do something
});
///////////// user-cleanup.js
const functions = require('firebase-functions');
module.exports = functions.auth.user().onDelete(e => {
// do something
});
As a variant of Michael's answer, I found this organization very neat and kind of object-oriented.
// index.js
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)
const user = require('./user')(admin)
exports.sendWelcomeEmail = user.sendWelcomeEmail
// user.js
const functions = require('firebase-functions')
module.exports = function(admin) {
let sendWelcomeEmail = functions.auth.user().onCreate(event => {
// return admin.database().ref().update({})
})
return {
sendWelcomeEmail: sendWelcomeEmail
}
}
Related
I wanted to write a understandable code in node.js, so I want to put some functions, which are used very often, into other node.js files, and access them from there.
So I get a function, which calls a function from another node.js file and in this other node.js file, also another one is called.
Important to know, if I put all in one file, the code works, so it should be an issue with module export and using functions in another file.
I have one file, getting quotes from a decentralised exchange. Looking like this (quoter_uni_v2.js):
module.exports = function quotes_uni_v2(tokenIn, tokenOut, amountIn, router) {
const quotedAmountOut = router.getAmountsOut(amountIn.toString(), [
tokenIn,
tokenOut,
]);
return quotedAmountOut;
};
And I am importing this function in my second helper file (quotes_5.js) (It is splitted in two files, because in the second one I have to call the function multiple times):
var quotes_uni_v2 = require("./quotes_uni_v2");
module.exports = async function (router1, router2, route, amount_wei) {
console.log(route);
var amount_Out = await quotes_uni_v2.quotes_uni_v2(
route[1],
route[2],
amount_wei,
router1
);
...
return (
Math.round(ethers.utils.formatEther(amount_Out[1].toString()) * 100) / 100
);
};
After that I try to call the function in my main.js:
const quotes_uni_v2 = require("./quotes_uni_v2");
const quotes_5 = require("./quotes_5");
async function calc(route) {
amountOut = await new quotes_5(
quickswap_router,
sushiswap_router,
route,
amount_wei
);
return amountOut;
};
But calling the quotes function does not work... The error is:
TypeError: quotes_5 is not a constructor...
Can someone help me?
Thanks!
I am trying to set up a simple serverless function on Netlify just to test out usage of environment variables. I have defined the following two environment variables in Netlify for my site:
Variable Name
Value
ALPHABET_SEPARATION
2
CHARS_BETWEEN
3
I have also updated my functions directory as follows:
Functions directory: myfunctions
I am using continuous deployment from github. As I do not know the use of npm at present and finding it convenient to directly test the production deploy, I have defined a subdirectory called myfunctions inside my root directory and have placed my javascript file containing the "serverless" function inside it on my local machine. I have built in logic so that the "serverless" function gets called only when a "netlify" flag is set, otherwise, an alternate function gets executed client-side. Basically it works as follows:
const deploy = "netlify" //Possible valid values are "local" and "netlify"
async function postRandomString() {
const stringToUpdate = "THISISATESTSTRING"
var stringToPost = "DUMMYINITIALVALUE";
if (deploy === "local") {
stringToPost = updateString(stringToUpdate); //updateString is a function defined elsewhere and executes client-side;
}
else if (deploy === "netlify") {
const config = {
method: 'GET',
headers: {
'Accept': 'application/json',
}
};
const res = await fetch(`myfunctions/serverUpdateString?input=${stringToUpdate}`, config);
const data = await res.json();
stringToPost = data.retVal;
console.log(data.retVal);
}
else {
stringToPost = "##ERROR##";
}
postString(stringToPost); //postString is a function defined elsewhere and executes client-side;
}
The serverless function file serverUpdateString.js is coded as follows (it basically sets a character at a certain position (determined by CHARS_BETWEEN) in the string to an alphabetical character which is a certain number (determined by ALPHABET_SEPARATION) of places in the alphabet after the first character of the string (don't ask why - the point is that it never even receives/handles the request):
exports.handler = async function (event) {
const { CHARS_BETWEEN, ALPHABET_SEPARATION } = process.env;
const charsBetween = CHARS_BETWEEN;
const alphabetSeparation = ALPHABET_SEPARATION;
const initString = event.queryStringParameters.input;
const rootUnicode = initString.charCodeAt(0);
const finalUnicode = "A".charCodeAt(0) + (rootUnicode - "A".charCodeAt(0) + alphabetSeparation) % 26;
const finalChar = String.fromCharCode(finalUnicode);
const stringArray = initString.split("");
stringArray[charsBetween + 1] = finalChar;
const stringToReturn = stringArray.join("");
const response = {
statusCode: 200,
retVal: stringToReturn,
}
return JSON.stringify(response);
}
When I run it, I get a 404 error for the GET request:
In the above image, script.js:43 is the line const res = await fetch(myfunctions/serverUpdateString?input=ATESTSTRIN, config); in the calling file, as shown in the first code block above.
What am I doing incorrectly? Surely Netlify should be able to pick up the serverless function file given that I have specified the folder alright and have placed it at the right place in the directory structure? I have given the whole code for completeness but the problem seems quite elementary. Look forward to your help, thanks.
I got assistance from Netlify forums. Basically the following changes needed to be made:
The fetch request -- line 43 in the calling code (script.js) -- needed to be changed to
const res = await fetch(`https://netlifytestserverless.netlify.app/.netlify/functions/serverUpdateString?input=${stringToUpdate}`, config);
The return statement in the lambda function needed to be changed to:
const response = {
statusCode: 200,
body: JSON.stringify(stringToReturn),
}
Other minor changes such as using parseInt with the environment variables.
The code works now.
------------program1.ts
consumerGroup.on('message',function(msgc){
uuid=uuid();
program2.func2(uuid,msgc);
});
------Program2.ts
function func2(uuid,msgc){
logger.log(uuid,msgc);
program3.func3(uuid,msgc);
}
-----------program3.ts
function func(uuid,msgc){
logger.log(uuid,msgc);
}
I have to implement UUID in the logger for each message read from the consumer in an existing project. I came up similar to the above code approach but there are lots of sub-functions and I would have to do it for each and every function/logger call.
Is there any better way to do it?
If you use Node 14 and above, you can use the built-in AsyncLocalStorage. Logging with IDs is a typical use-case of this new API. Here's an example of the use-case:
const { AsyncLocalStorage } = require('async_hooks');
const asyncLocalStorage = new AsyncLocalStorage();
function logWithId(msg) {
const id = asyncLocalStorage.getStore();
logger.log(id, msg);
}
asyncLocalStorage.run(uuid(), () => {
consumerGroup.on('message',function(msgc){
program2.func2(msgc);
});
})
// ------Program2.ts
function func2(uuid,msgc){
logWithId(msgc);
program3.func3(msgc);
}
// -----------program3.ts
function func(uuid,msgc){
logWithId(msgc);
}
Read the docs to understand more about it.
i built an app on 1 page, and now i want to oragenize all of its functions and objects in seperate files and directories, and import them to the app.js file. but im having some problems with global objects, the the imported functions dosent recognize them.
example:
//app.js
const req_obj = require("./objectFile.js")
const tool = {
number:4
}
req_obj.obj.addFive() // this will print "cant read number of undefined"
//objectFile.js
const req_func = require("./function.js")
exports.obj = {
addFive:req_func.addFive
}
//function.js
exports.addFive = function(){
return tool.number + 5
}
i know that it might be that im doing all wrong, and this is not how you orgenize an app and call its function. i whould be really glad if some1 will explaine my how to orgenize an app, and call its functions the right way, if what i did is wrong.
The only possibility you have to get access to variables from multiple files is to have them in a separate file and export them like this:
// objects.js
const tool = {
number:4
}
exports.tool = tool
Which then makes your app.js look like:
//app.js
const req_obj = require("./objectFile.js")
const tool = require("./objects.js").tool
// this will read 9
console.log(req_obj.obj.addFive())
And your function.js then becomes:
const tool = require("./objects.js").tool
exports.addFive = function(){
return tool.number + 5
}
Hope it helps.
So, I have a directory:
mods/
-core/
--index.js
--scripts/
---lots of stuff imported by core/index
This works with typical rollup fashion if you want to rollup to for example mods/core/index.min.js
But I have many of these mods/**/ directories and I want to take advantage of the fact that they are rollup'd into iifes. Each mods/**/index.js will, rather than export, assign to a global variable that we presume is provided:
mods/core/index.js
import ui from './scripts/ui/'
global.ui = ui
mods/someMod/scripts/moddedClass.js
export default class moddedClass extends global.ui.something { /* some functionality extension */}
mods/someMod/index.js
import moddedClass from './scripts/moddedClass'
global.ui.something = moddedClass
So hopefully you can see how each mod directory can be rollup'd in typical fashion but, I need to then put the actual iifes inside another one so that:
mods/compiled.js
(function compiled() {
const global = {};
(function core() {
//typical rollup iife
})();
(function someMod() {
//typical rollup iife
})();
//a footer like return global or global.init()
})();
Any help towards this end would be greatly appreciated. The simplest possible answer, I think, is how I can simply get a string value for each mod's iife instead of rollup writing it to a file.
At that point I could just iterate the /mods/ directory, in an order specified by some modlist.json or something, and call rollup on each /mod/index.js, then build the outer iife myself from strings.
However, I suppose this would not be a full solution for sourcemapping? Or can multiple inline sourcemaps be included? With source mapping in mind, I wonder if another build step might be necessary, where each mod is transpiled before this system even gets to it.
Use rollup's bundle.generate api to generate multiple iifes and write them into one file using fs.appendFile.
For the sourcemaps you can use this moduleļ¼it's from the same author of rollup) https://github.com/rich-harris/sorcery
Okay so the way I ended up solving this was using source-map-concat
It basically does what I described, right out of the box. The only thing I had to do was to asynchronously iterate the mod directory and rollup each mod, before passing the results to source-map-concat, since rollup.rollup returns a Promise.
I also ended up wanting in-line sourcemaps so that the code can be directly injected rather than written to a file, so I used convert-source-map for that.
The only issue left to solve is sub-source mapping. Sorcery would work great for that, if I was generating files, but I would like to keep it as string sources. For now it will at least show me what mod an error came from, but not the sub-file it came from. If anyone has info on how to do a sorcery-style operation on strings let me know.
Here's the relevant final code from my file:
const rollup = require("rollup")
const concat = require("source-map-concat")
const convert = require("convert-source-map")
const fs = require("fs")
const path = require("path")
const modsPath = path.join(__dirname, "mods")
const getNames = _ => JSON.parse(fs.readFileSync(path.join(modsPath, "loadList.json"), "utf8"))
const wrap = (node, mod) => {
node.prepend("\n// File: " + mod.source + "\n")
}
const rolls = {}
const bundles = {}
const rollupMod = (modName, after) => {
let dir = path.join(modsPath, modName),
file = path.join(dir, "index.js")
rollup.rollup({
entry: file,
external: "G",
plugins: []
}).then(bundle => {
rolls[modName] = bundle.generate({
format: "iife",
moduleName: modName,
exports: "none",
useStrict: false,
sourceMap: true
})
after()
})
}
const rollupMods = after => {
let names = getNames(), i = 0,
rollNext = _ => rollupMod(names[i++], _ => i < names.length - 1? rollNext() : after())
rollNext()
}
const bundleCode = after => {
rollupMods(_ => {
let mods = concat(getNames().map(modName => {
let mod = rolls[modName]
return {
source: path.join(modsPath, modName),
code: mod.code,
map: mod.map
}
}), {
delimiter: "\n",
process: wrap
})
mods.prepend("(function(){\n")
mods.add("\n})();")
let result = mods.toStringWithSourceMap({
file: path.basename('.')
})
bundles.code = result.code + "\n" + convert.fromObject(result.map).toComment()
after(bundles.code)
})
}
exports.bundleCode = bundleCode