Как мне организовать облачные функции для военнослужащих, чтобы развернуть несколько функций из нескольких файлов?
Я хотел бы создать несколько облачных функций для военнослужащих и развернуть их все одновременно из одного проекта. Я также хотел бы разделить каждую функцию в отдельный файл. В настоящее время я могу создать несколько функций, если я помещаю их в индекс.js такие как:
exports.foo = functions.database.ref('/foo').onWrite(event => {
...
});
exports.bar = functions.database.ref('/bar').onWrite(event => {
...
});
однако я хотел бы поместить foo и bar в отдельные файлы. Я попробовал это:
/functions
|--index.js (blank)
|--foo.js
|--bar.js
|--package.json
где Foo.js-это
exports.foo = functions.database.ref('/foo').onWrite(event => {
...
});
и бар.js-это
exports.bar = functions.database.ref('/bar').onWrite(event => {
...
});
есть способ сделать это, не помещая все функции в индекс.Джей?
8 ответов:
Ах, облачные функции для модулей узла загрузки Firebase обычно, так что это работает
структура:
.js:/functions |--index.js |--foo.js |--bar.js |--package.jsonconst functions = require('firebase-functions'); const fooModule = require('./foo'); const barModule = require('./bar'); exports.foo = functions.database.ref('/foo').onWrite(fooModule.handler); exports.bar = functions.database.ref('/bar').onWrite(barModule.handler);фу.js:
exports.handler = (event) => { ... };бар.js:
exports.handler = (event) => { ... };
ответ @jasonsirota был очень полезным. Но может быть полезно увидеть более подробный код, особенно в случае HTTP-триггерных функций.
используя ту же структуру, что и в ответе @jasonsirota, скажем, вы хотите иметь две отдельные функции триггера HTTP в двух разных файлах:
каталог структуру:
.js:/functions |--index.js |--foo.js |--bar.js |--package.json`'use strict'; const fooFunction = require('./foo'); const barFunction = require('./bar'); // Note do below initialization tasks in index.js and // NOT in child functions: const functions = require('firebase-functions'); const admin = require('firebase-admin'); admin.initializeApp(functions.config().firebase); const database = admin.database(); // Pass database to child functions so they have access to it exports.fooFunction = functions.https.onRequest((req, res) => { fooFunction.handler(req, res, database); }); exports.barFunction = functions.https.onRequest((req, res) => { barFunction.handler(req, res, database); });фу.js:
exports.handler = function(req, res, database) { // Use database to declare databaseRefs: usersRef = database.ref('users'); ... res.send('foo ran successfully'); }бар.js:
exports.handler = function(req, res, database) { // Use database to declare databaseRefs: usersRef = database.ref('users'); ... res.send('bar ran successfully'); }
вот как я лично сделал это с typescript:
/functions |--src |--index.ts |--http-functions.ts |--main.js |--db.ts |--package.json |--tsconfig.jsonпозвольте мне предисловие к этому, давая два предупреждения, чтобы сделать эту работу:
- порядок импорта / экспорта имеет значение в .ТС
- БД должна быть отдельным файлом
для пункта 2 я не уверен, почему. Secundo вы должны уважать мою конфигурацию index, main и db ровно (хотя бы попробовать из.)
.ТС : занимается экспортом. Я считаю, что это чище, чтобы индекс.ts занимается экспортом.
// main must be before functions export * from './main'; export * from "./http-functions";главная.ТС: имеет дело с инициализацией.
import { config } from 'firebase-functions'; import { initializeApp } from 'firebase-admin'; initializeApp(config().firebase); export * from "firebase-functions";db.ТС: только как реэкспорт дБ, поэтому его имя короче, чем
database()import { database } from "firebase-admin"; export const db = database();http-функции.ТС
// de must be imported like this import { db } from './db'; // you can now import everything from index. import { https } from './index'; // or (both work) // import { https } from 'firebase-functions'; export let newComment = https.onRequest(createComment); export async function createComment(req: any, res: any){ db.ref('comments').push(req.body.comment); res.send(req.body.comment); }
в случае с Бабель/поток это будет выглядеть так:
Вид Каталога
. ├── /build/ # Compiled output for Node.js 6.x ├── /src/ # Application source files │ ├── db.js # Cloud SQL client for Postgres │ ├── index.js # Main export(s) │ ├── someFuncA.js # Function A │ ├── someFuncA.test.js # Function A unit tests │ ├── someFuncB.js # Function B │ ├── someFuncB.test.js # Function B unit tests │ └── store.js # Firebase Firestore client ├── .babelrc # Babel configuration ├── firebase.json # Firebase configuration └── package.json # List of project dependencies and NPM scripts
src/index.js- основной экспортный(с)export * from './someFuncA.js'; export * from './someFuncB.js';
src/db.js- облачный SQL клиент для Postgresimport { Pool } from 'pg'; import { config } from 'firebase-functions'; export default new Pool({ max: 1, user: '<username>', database: '<database>', password: config().db.password, host: `/cloudsql/${process.env.GCP_PROJECT}:<region>:<instance>`, });
src/store.js- Firebase Firestore Clientimport firebase from 'firebase-admin'; import { config } from 'firebase-functions'; firebase.initializeApp(config().firebase); export default firebase.firestore();
src/someFuncA.js- функция Аimport { https } from 'firebase-functions'; import db from './db'; export const someFuncA = https.onRequest(async (req, res) => { const { rows: regions } = await db.query(` SELECT * FROM regions WHERE country_code = `, ['US']); res.send(regions); });
src/someFuncB.js- Функция Bimport { https } from 'firebase-functions'; import store from './store'; export const someFuncB = https.onRequest(async (req, res) => { const { docs: regions } = await store .collection('regions') .where('countryCode', '==', 'US') .get(); res.send(regions); });
.babelrc{ "presets": [["env", { "targets": { "node": "6.11" } }]], }
firebase.json{ "functions": { "source": ".", "ignore": [ "**/node_modules/**" ] } }
package.json{ "name": "functions", "verson": "0.0.0", "private": true, "main": "build/index.js", "dependencies": { "firebase-admin": "^5.9.0", "firebase-functions": "^0.8.1", "pg": "^7.4.1" }, "devDependencies": { "babel-cli": "^6.26.0", "babel-core": "^6.26.0", "babel-jest": "^22.2.2", "babel-preset-env": "^1.6.1", "jest": "^22.2.2" }, "scripts": { "test": "jest --env=node", "predeploy": "rm -rf ./build && babel --out-dir ./build src", "deploy": "firebase deploy --only functions" } }
$ yarn install # Install project dependencies $ yarn test # Run unit tests $ yarn deploy # Deploy to Firebase
этот формат позволяет вашей точке входа автоматически находить дополнительные функциональные файлы и экспортировать каждую функцию в каждый файл.
Главная Точка Входа Скрипта
находит все .JS-файлы внутри папки функций и экспортирует каждую функцию, экспортированную из каждого файла.
const fs = require('fs'); const path = require('path'); // Folder where all your individual Cloud Functions files are located. const FUNCTIONS_FOLDER = './scFunctions'; fs.readdirSync(path.resolve(__dirname, FUNCTIONS_FOLDER)).forEach(file => { // list files in the folder. if(file.endsWith('.js')) { const fileBaseName = file.slice(0, -3); // Remove the '.js' extension const thisFunction = require(`${FUNCTIONS_FOLDER}/${fileBaseName}`); for(var i in thisFunction) { exports[i] = thisFunction[i]; } } });пример экспорта нескольких функций из одного файла
const functions = require('firebase-functions'); const query = functions.https.onRequest((req, res) => { let query = req.query.q; res.send({ "You Searched For": query }); }); const searchTest = functions.https.onRequest((req, res) => { res.send({ "searchTest": "Hi There!" }); }); module.exports = { query, searchTest }http доступные конечные точки имеют соответствующие имена
✔ functions: query: http://localhost:5001/PROJECT-NAME/us-central1/query ✔ functions: helloWorlds: http://localhost:5001/PROJECT-NAME/us-central1/helloWorlds ✔ functions: searchTest: http://localhost:5001/PROJECT-NAME/us-central1/searchTestодним файлом
если у вас есть только несколько дополнительных файлов (например, только один), вы можете использовать:
const your_functions = require('./path_to_your_functions'); for (var i in your_functions) { exports[i] = your_functions[i]; }
чтобы быть простым (но делает работу), я лично структурировал свой код так.
планировка
├── /src/ │ ├── index.ts │ ├── foo.ts │ ├── bar.ts └── package.jsonфу.ТС
export const fooFunction = functions.database()......... { //do your function. } export const someOtherFunction = functions.database().......... { // do the thing. }бар.ТС
export const barFunction = functions.database()......... { //do your function. } export const anotherFunction = functions.database().......... { // do the thing. }.ТС
import * as fooFunctions from './foo'; import * as barFunctions from './bar'; module.exports = { ...fooFunctions, ...barFunctions, };работает для каталогов любых уровней вложенности. Просто следуйте шаблону внутри каталогов тоже.
С узлом 8 LTS теперь доступны функции Cloud / Firebase вы можете сделать следующее с операторами распространения:
/пакет.json
"engines": { "node": "8" },индекс/.js
module.exports = { ...require("./lib/foo.js"), // ...require("./lib/bar.js") // add as many as you like };/ shared.js
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); if (process.env.GCLOUD_PROJECT == "production-app") { // production stuff } else { // dev stuff } module.exports = { functions, admin };/ lib / foo.js
const { functions, admin } = require("../shared.js"); exports.fooHandler = functions.database .ref("/food/{id}") .onCreate((snap, context) => { let id = context.params["id"]; return admin .database() .ref(`/bar/${id}`) .set(true); });
Я использую загрузчик vanilla JS для автоматического включения всех функций, которые я хочу использовать.
├── /functions │ ├── /test/ │ │ ├── testA.js │ │ └── testB.js │ ├── index.js │ └── package.json.js (бутлоадера)
/** * The bootloader reads all directories (single level, NOT recursively) * to include all known functions. */ const functions = require('firebase-functions'); const fs = require('fs') const path = require('path') fs.readdirSync(process.cwd()).forEach(location => { if (!location.startsWith('.')) { location = path.resolve(location) if (fs.statSync(location).isDirectory() && path.dirname(location).toLowerCase() !== 'node_modules') { fs.readdirSync(location).forEach(filepath => { filepath = path.join(location, filepath) if (fs.statSync(filepath).isFile() && path.extname(filepath).toLowerCase() === '.js') { Object.assign(exports, require(filepath)) } }) } } })
этот пример индекса.js-файл только автоматически включает каталоги в корне. Он может быть расширен, чтобы ходить каталоги, честь .gitignore, etc. Но мне этого было достаточно.
с индексным файлом на месте, добавление новых функций тривиальный.
/ test / testA.js
const functions = require('firebase-functions'); exports.helloWorld = functions.https.onRequest((request, response) => { response.send("Hello from Firebase!"); });/ test / testB.js
const functions = require('firebase-functions'); exports.helloWorld2 = functions.https.onRequest((request, response) => { response.send("Hello again, from Firebase!"); });
npm run serveвыходы:λ ~/Workspace/Ventures/Author.io/Firebase/functions/ npm run serve > functions@ serve /Users/cbutler/Workspace/Ventures/Author.io/Firebase/functions > firebase serve --only functions === Serving from '/Users/cbutler/Workspace/Ventures/Author.io/Firebase'... i functions: Preparing to emulate functions. Warning: You're using Node.js v9.3.0 but Google Cloud Functions only supports v6.11.5. ✔ functions: helloWorld: http://localhost:5000/authorio-ecorventures/us-central1/helloWorld ✔ functions: helloWorld2: http://localhost:5000/authorio-ecorventures/us-central1/helloWorld2этот рабочий процесс в значительной степени просто "писать и запускать", без необходимости изменять индекс.js-файл каждый раз, когда новая функция/файл добавляется/изменяется/удаляется.
Comments