ארגון מספר פונקציות


כשמשלבים את Cloud Functions בפרויקט, אפשר להרחיב את הקוד כך שיכלול פונקציות עצמאיות רבות. יכול להיות שיש לכם יותר מדי פונקציות מכדי שתוכלו להכניס אותן בקובץ אחד, או שצוותים שונים יכולים לפרוס קבוצות שונות של פונקציות, וכך להגדיל את הסיכון שצוות אחד יחליף או ימחק בטעות את הפונקציות של צוות אחר. ב-Cloud Functions יש כמה דרכים לארגן את הקוד כדי שיהיה קל יותר לנווט בפונקציות ולנהל אותן.

ארגון פונקציות בקוד

אפשר להשתמש במאפיין codebase של אובייקט ההגדרה של הפונקציות ב-firebase.json כדי לנהל אוסף גדול של פונקציות במספר מאגרים או חבילות משנה בתוך הגדרת מונו-רפו אחת של מאגר:

# firebase.json
"functions": {
  "codebase": "my-codebase"
  # NOTE: Codebase must be less than 63 characters and can contain only
  # lowercase letters, numeric characters, underscores, and dashes.
}

הנכס codebase נתמך ב-Firebase CLI גרסה 10.7.1 ואילך.

ניהול של כמה מאגרים

בעזרת המאפיין codebase תוכלו לפשט את הניהול של מספר מאגרים. נבחן מקרה שבו יש לכם שני מאגרים שונים הפורסים פונקציות לאותו פרויקט ב-Firebase:

$  tree .
├── repoA
│   ├── firebase.json
│   └── functions
│       ├── index.js
│       └── package.json
└── repoB
    ├── firebase.json
    └── functions
        ├── index.js
        └── package.json

בלי הערות Codebase, ה-CLI של Firebase ינחה אתכם למחוק פונקציות שהוגדרו במאגר האחר בזמן הפריסה:

$ (cd repoA && firebase deploy --only functions)
...
i  functions: preparing functions directory for uploading...
✔  functions: functions folder uploaded successfully
The following functions are found in your project but do not exist in your local source code:
        fn1FromRepoB
        fn2FromRepoB
        ...
? Would you like to proceed with deletion? Selecting no will continue the rest of the deployments. (y/N)

כדי למנוע את הבעיה הזו, אפשר להוסיף הערה ייחודית לגבי קוד הבסיס בקטע ההגדרות של הפונקציות ב-firebase.json בכל מאגר פרויקטים:

# repoA/firebase.json
"functions": {
  "codebase": "repo-a"
}

# repoB/firebase.json
"functions": {
  "codebase": "repo-b"
}

כשמשתמשים בהערות בקוד, כבר לא מופיעה ב-Firebase CLI בקשה למחוק פונקציות שהוגדרו מחוץ למאגר המיידי:

$ (cd repoA && firebase deploy --only functions)
...
i  functions: preparing functions directory for uploading...
✔  functions: functions folder uploaded successfully
#  Gleefully ignores functions from repoB
i  functions: creating Node.js 16 function fnFromRepoA (us-central1)...
✔  Deploy Complete!

ניהול חבילות מקור מרובות (מונורפו)

המאפיין codebase יכול לעזור לפשט את הניהול של כמה חבילות מקור במאגר אחד. נניח שיש לכם ספריית פרויקט ב-firebase עם הגדרות של פונקציות שמפוזרות בין כמה חבילות משנה:

$  tree .
├── firebase.json
├── teamA
│   ├── index.js
│   └── package.json
└── teamB
    ├── index.js
    └── package.json

ההגדרה הזו מתאימה לתרחישים הבאים לדוגמה:

  • יש לכם הגדרת מונורפו וכל צוותים מנהלים את הגדרות הפונקציות שלהם בחבילה מבודדת.
  • יש פונקציה עם תלות חיצונית משמעותית ואתחול ממושך. אתם רוצים לבודד את הפונקציה הזו מפונקציות אחרות שרגישות לזמן אחזור.

כדי לתמוך בהגדרה של monrepo כזו, צריך להגדיר כמה הגדרות של פונקציות ב-firebase.json:

"functions": [
  {
    "source": "teamA",
    "codebase": "team-a"
  },
  {
    "source": "teamB",
    "codebase": "team-b"
  },
]

בתצורה הזו, Firebase CLI פורס פונקציות מכל החבילות בפקודת פריסה אחת:

$ firebase deploy --only functions
i  deploying functions
i  functions: preparing codebase team-a for deployment
i  functions: preparing codebase team-b for deployment
i  functions: creating Node.js 16 function team-a:helloATeam(us-central1)...
i  functions: creating Node.js 16 function team-b:helloBTeam(us-central1)...
...

אפשר גם לפרוס קוד בסיס ספציפי:

$ firebase deploy --only functions:team-b
i  deploying functions
i  functions: preparing codebase team-b for deployment
i  functions: updating Node.js 16 function team-b:helloBTeam(us-central1)...
...

כתיבת פונקציות במספר קבצים

בתחילת העבודה עם Cloud Functions, כדאי לשמור את הפונקציות הראשונות בקובץ אחד:

index.js

const functions = require('firebase-functions/v1');
exports.foo = functions.https.onRequest((request, response) => {
  // ...
});
exports.bar = functions.https.onRequest((request, response) => {
  // ...
});

Main.py

from firebase_functions import https_fn

@https_fn.on_request()
def foo(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello foo!")

@https_fn.on_request()
def bar(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello bar!")

אם יש יותר מכמה פונקציות, יכול להיות שיהיה קשה לנהל את הדברים. במקום זאת, אתם יכולים לשמור את כל הלוגיקה של כל פונקציה בקובץ נפרד ולהשתמש בקובץ המקור כרשימה של פעולות ייצוא:

Node.js

foo.js

const functions = require('firebase-functions/v1');
exports.foo = functions.https.onRequest((request, response) => {
  // ...
});

bar.js

const functions = require('firebase-functions/v1');
exports.bar = functions.https.onRequest((request, response) => {
  // ...
});

index.js

const foo = require('./foo');
const bar = require('./bar');
exports.foo = foo.foo;
exports.bar = bar.bar;

Python

foo.py

from firebase_functions import https_fn

@https_fn.on_request()
def foo(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello foo!")

bar.py

from firebase_functions import https_fn

@https_fn.on_request()
def bar(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello foo!")

main.py

from fn_impl.foo import *
from fn_impl.bar import *

ההגדרה הזו מבוססת על מבנה ספריות של פרויקט כמו זה:

my-project
├── firebase.json
└── functions
    ├── fn_impl
    │   ├── __init__.py
    │   ├── foo.py
    │   └── bar.py
    ├── main.py
    └── requirements.txt

fn_impl: יכול להיות כל שם

__init__.py: שדה חובה, אבל יכול להיות ריק

פונקציות קבוצתיות

בפרויקטים רבים אפשר להפריד פונקציות לקבוצות לוגיות, שצריך לפרוס ולתחזק יחד. לדוגמה, יכול להיות שיש לכם קבוצה של פונקציות שמשמשות לדיווח על מדדים:

metrics.js

const functions = require('firebase-functions/v1');
exports.usageStats = functions.https.onRequest((request, response) => {
  // ...
});
exports.nightlyReport = functions.https.onRequest((request, response) => {
  // ...
});

אפשר להוסיף את הפונקציות האלה לקבוצה כשמייצאים אותן לקובץ index.js:

index.js

// Export both functions from metrics.js in the "metrics" group:
//  - metrics-usageStats
//  - metrics-nightlyReport
exports.metrics = require('./metrics');

כשפורסים, הפונקציות יופיעו בתחילת שם הקבוצה שלהן, כך שבדוגמה הזו הפונקציות יהיו metrics-usageStats ו-metrics-nightlyReport.

כשפורסים פונקציות, אפשר להגביל את הפעולה לקבוצה אחת:

firebase deploy --only functions:metrics

השלבים הבאים

מידע נוסף על Cloud Functions זמין במאמרים הבאים: