בדף הזה מוסבר איך ליצור תוסף פשוט ל-Firebase, שאפשר להתקין בפרויקטים או לשתף עם אחרים. בדוגמה הפשוטה הזו של תוסף ל-Firebase, המערכת תבדוק את המסרים במסד הנתונים בזמן אמת ותמיר אותם לאותיות רישיות.
1. הגדרת הסביבה והפעלת פרויקט
לפני שמתחילים לפתח תוסף, צריך להגדיר סביבה לפיתוח עם הכלים הנדרשים.
מתקינים את Node.js בגרסה 16 ואילך. אחת מהדרכים להתקין את Node היא באמצעות nvm (או nvm-windows).
מתקינים או מעדכנים את הגרסה האחרונה של Firebase CLI. כדי להתקין או לעדכן באמצעות
npm
, מריצים את הפקודה הבאה:npm install -g firebase-tools
עכשיו משתמשים ב-CLI של Firebase כדי לאתחל פרויקט חדש של תוסף:
יוצרים ספרייה לתוסף ומעבירים אליה את
cd
:mkdir rtdb-uppercase-messages && cd rtdb-uppercase-messages
מריצים את הפקודה
ext:dev:init
ב-CLI של Firebase:firebase ext:dev:init
כשמוצגת בקשה, בוחרים ב-JavaScript כשפת הפונקציות (אבל חשוב לזכור שאפשר להשתמש גם ב-TypeScript כשמפתחים תוסף משלכם). כשמוצגת בקשה להתקנת יחסי תלות, עונים 'כן'. (מאשרים את ברירת המחדל של כל האפשרויות האחרות). הפקודה הזו תגדיר קוד בסיס של תוסף חדש, שממנו תוכלו להתחיל לפתח את התוסף.
2. ניסיון של תוסף לדוגמה באמצעות המהדר
כש-Firebase CLI הפעיל את ספריית התוספים החדשה, הוא יצר פונקציית דוגמה פשוטה וספרייה integration-tests
שמכילה את הקבצים הנדרשים להפעלת תוסף באמצעות חבילת המהדמנים של Firebase.
נסו להריץ את התוסף לדוגמה במהדורת האדמין:
עוברים לספרייה
integration-tests
:cd functions/integration-tests
מפעילים את הסימולטור עם פרויקט הדגמה:
firebase emulators:start --project=demo-test
הסימולטור טוען את התוסף לפרויקט 'דמה' מוגדר מראש (
demo-test
). התוסף מורכב עד כה מפונקציה אחת שמופעל על ידי HTTP, greetTheWorld
, שמחזירה את ההודעה 'hello world' כשנכנסים אליה.כשהמכונה הווירטואלית עדיין פועלת, אפשר לנסות את הפונקציה
greetTheWorld
של התוסף על ידי כניסה לכתובת ה-URL שהודפסה כשהפעלתם אותה.בדפדפן תוצג ההודעה 'Hello World from greet-the-world'.
קוד המקור של הפונקציה הזו נמצא בתיקייה
functions
של התוסף. פותחים את המקור בכלי העריכה או בסביבת הפיתוח המשולבת (IDE) שבחרתם:functions/index.js
const functions = require("firebase-functions/v1"); exports.greetTheWorld = functions.https.onRequest((req, res) => { // Here we reference a user-provided parameter // (its value is provided by the user during installation) const consumerProvidedGreeting = process.env.GREETING; // And here we reference an auto-populated parameter // (its value is provided by Firebase after installation) const instanceId = process.env.EXT_INSTANCE_ID; const greeting = `${consumerProvidedGreeting} World from ${instanceId}`; res.send(greeting); });
בזמן שהסימולטור פועל, הוא יטען מחדש באופן אוטומטי את כל השינויים שתבצעו בקוד של Functions. נסו לבצע שינוי קטן בפונקציה
greetTheWorld
:functions/index.js
const greeting = `${consumerProvidedGreeting} everyone, from ${instanceId}`;
שומרים את השינויים. הסימולטור יטען מחדש את הקוד, ועכשיו, כשנכנסים לכתובת ה-URL של הפונקציה, מופיעה ההודעה המעודכנת.
3. הוספת מידע בסיסי לקובץ extension.yaml
עכשיו, אחרי שהגדרתם את סביבת הפיתוח והפעלתם את אמולטור התוספים, תוכלו להתחיל לכתוב תוסף משלכם.
בשלב ראשון פשוט, עורכים את המטא-נתונים המוגדרים מראש של התוסף כך שישקפו את התוסף שרוצים לכתוב במקום greet-the-world
. המטא-נתונים האלה מאוחסנים בקובץ extension.yaml
.
פותחים את הקובץ
extension.yaml
בכלי העריכה ומחליפים את כל תוכן הקובץ בקוד הבא:name: rtdb-uppercase-messages version: 0.0.1 specVersion: v1beta # Firebase Extensions specification version; don't change # Friendly display name for your extension (~3-5 words) displayName: Convert messages to upper case # Brief description of the task your extension performs (~1 sentence) description: >- Converts messages in RTDB to upper case author: authorName: Your Name url: https://your-site.example.com license: Apache-2.0 # Required license # Public URL for the source code of your extension sourceUrl: https://github.com/your-name/your-repo
חשוב לשים לב למוסכמה למתן שמות שמשמשת בשדה
name
: לשמות של התוספים הרשמיים של Firebase מצורף קידומת שמציינת את מוצר Firebase הראשי שבו התוסף פועל, ואחריה תיאור של הפעולות שהתוסף מבצע. מומלץ להשתמש באותה הסכמה בתוספים שלכם.מאחר ששיניתם את שם התוסף, עליכם לעדכן גם את ההגדרות של המהדר בשם החדש:
- ב-
functions/integration-tests/firebase.json
, משנים את הערך שלgreet-the-world
ל-rtdb-uppercase-messages
. - משנים את השם של
functions/integration-tests/extensions/greet-the-world.env
ל-functions/integration-tests/extensions/rtdb-uppercase-messages.env
.
- ב-
עדיין יש כמה שרידים של התוסף greet-the-world
בקוד התוסף, אבל בשלב הזה אפשר להשאיר אותם. בשלבים הבאים נסביר איך מעדכנים את הפרטים האלה.
4. כתיבת פונקציה של Cloud Functions והצהרה עליה כמשאב תוסף
עכשיו אפשר להתחיל לכתוב קוד. בשלב הזה כתבו פונקציית Cloud Functions שתבצע את המשימה המרכזית של התוסף, כלומר לבדוק אם יש הודעות ב-Realtime Database ולהמיר אותן לאותיות רישיות.
פותחים את המקור של הפונקציות של התוסף (בתיקייה
functions
של התוסף) בכלי העריכה או בסביבת הפיתוח המשולבת (IDE) שבחרתם. מחליפים את התוכן שלו בקוד הבא:functions/index.js
import { database, logger } from "firebase-functions/v1"; const app = initializeApp(); // Listens for new messages added to /messages/{pushId}/original and creates an // uppercase version of the message to /messages/{pushId}/uppercase // for all databases in 'us-central1' export const makeuppercase = database .ref("/messages/{pushId}/uppercase") .onCreate(async (snapshot, context) => { // Grab the current value of what was written to the Realtime Database. const original = snapshot.val(); // Convert it to upper case. logger.log("Uppercasing", context.params.pushId, original); const uppercase = original.toUpperCase(); // Setting an "uppercase" sibling in the Realtime Database. const upperRef = snapshot.ref.parent.child("upper"); await upperRef.set(uppercase); });
הפונקציה הישנה, אותה החלפתם, הייתה פונקציה שמופעלת על ידי HTTP, שפועלת כשמתבצעת גישה לנקודת קצה (endpoint) של HTTP. הפונקציה החדשה מופעלת על ידי אירועים בזמן אמת במסד הנתונים: היא מחפשת פריטים חדשים בנתיב מסוים, וכשהיא מזהה פריט כזה היא כותבת את גרסת הערך באותיות רישיות בחזרה למסד הנתונים.
דרך אגב, בקובץ החדש הזה נעשה שימוש בסינטקס של מודול ECMAScript (
import
ו-export
) במקום ב-CommonJS (require
). כדי להשתמש במודולים של ES ב-Node, צריך לציין את"type": "module"
ב-functions/package.json
:{ "name": "rtdb-uppercase-messages", "main": "index.js", "type": "module", … }
כל פונקציה בתוסף צריכה להוצהר בקובץ
extension.yaml
. בתוסף לדוגמה,greetTheWorld
הוכרזה כפונקציית Cloud Function היחידה של התוסף. עכשיו, אחרי שהוחלפת אותה ב-makeuppercase
, צריך גם לעדכן את ההצהרה שלה.פותחים את
extension.yaml
ומוסיפים שדהresources
:resources: - name: makeuppercase type: firebaseextensions.v1beta.function properties: eventTrigger: eventType: providers/google.firebase.database/eventTypes/ref.create # DATABASE_INSTANCE (project's default instance) is an auto-populated # parameter value. You can also specify an instance. resource: projects/_/instances/${DATABASE_INSTANCE}/refs/messages/{pushId}/original runtime: "nodejs18"
מאחר שהתוסף משתמש עכשיו ב-Realtime Database כטריגר, צריך לעדכן את הגדרות הסימולטור כדי להריץ את הסימולטור של RTDB לצד הסימולטור של Cloud Functions:
אם הסימולטור עדיין פועל, לוחצים על Ctrl-C כדי להפסיק אותו.
מהספרייה
functions/integration-tests
, מריצים את הפקודה הבאה:firebase init emulators
כשמתבקשים, מדלגים על הגדרת פרויקט ברירת מחדל ובוחרים את המהדמנים של Functions ושל מסדי נתונים. מאשרים את יציאות ברירת המחדל ומאפשרים לכלי ההגדרה להוריד את כל הקבצים הנדרשים.
מפעילים מחדש את האמולטור:
firebase emulators:start --project=demo-test
בודקים את התוסף המעודכן:
פותחים את ממשק המשתמש של אמולטור מסדי הנתונים באמצעות הקישור שהאמולטור הדפיס כשהפעלתם אותו.
עורכים את צומת הבסיס של מסד הנתונים:
- שדה:
messages
- סוג:
json
- ערך:
{"11": {"original": "recipe"}}
אם הכל מוגדר בצורה נכונה, כששומרים את השינויים במסד הנתונים, הפונקציה
makeuppercase
של התוסף אמורה להפעיל ולהוסיף רשומת צאצא להודעה 11 עם התוכן"upper": "RECIPE"
. בודקים את היומנים ואת הכרטיסיות של מסדי הנתונים בממשק המשתמש של המהדר כדי לוודא שהתוצאות הן כצפוי.- שדה:
נסו להוסיף עוד כמה צאצאים לצומת
messages
({"original":"any text"}
). בכל פעם שמוסיפים רשומה חדשה, התוסף צריך להוסיף שדהuppercase
שמכיל את התוכן של שדהoriginal
באותיות רישיות.
עכשיו יש לכם תוסף מלא, אם כי פשוט, שפועל במכונה של RTDB. בקטעים הבאים נוסיף לתוסף הזה כמה תכונות נוספות. לאחר מכן, תלמדו איך להכין את התוסף להפצה לאחרים, ואז איך לפרסם אותו במרכז התוספים.
5. הצהרת ממשקי API ותפקידים
מערכת Firebase מעניקה לכל מכונה של תוסף מותקן גישה מוגבלת לפרויקט ולנתונים שלו באמצעות חשבון שירות לכל מכונה. לכל חשבון יש את קבוצת ההרשאות המינימלית הנדרשת לתפעול. לכן, עליכם להצהיר באופן מפורש על כל תפקידי ה-IAM הנדרשים לתוסף. כשמשתמשים מתקינים את התוסף, מערכת Firebase יוצרת חשבון שירות עם התפקידים האלה ומשתמשת בו כדי להפעיל את התוסף.
אין צורך להצהיר על תפקידים כדי להפעיל אירועים של מוצרים, אבל צריך להצהיר על תפקיד כדי לבצע איתו פעולות אחרות. מכיוון שהפונקציה שהוספתם בשלב האחרון כותבת ב-Realtime Database, צריך להוסיף את ההצהרה הבאה ל-extension.yaml
:
roles:
- role: firebasedatabase.admin
reason: Allows the extension to write to RTDB.
באופן דומה, מגדירים את ממשקי Google API שבהם התוסף משתמש בשדה apis
. כשמשתמשים מתקינים את התוסף, הם מתבקשים להפעיל את ממשקי ה-API האלה באופן אוטומטי בפרויקט שלהם. בדרך כלל, הדבר נדרש רק לממשקי Google API שאינם של Firebase, ולא נדרש במדריך הזה.
6. הגדרת פרמטרים שניתנים להגדרה על ידי משתמשים
הפונקציה שיצרתם בשני השלבים האחרונים פקדה על מיקום ספציפי ב-RTDB כדי לבדוק אם יש הודעות נכנסות. לפעמים, מעקב אחרי מיקום ספציפי הוא מה שאתם באמת רוצים, למשל כשהתוסף פועל על מבנה מסד נתונים שאתם משתמשים בו באופן בלעדי עבור התוסף. עם זאת, ברוב המקרים כדאי לאפשר למשתמשים שמתקינים את התוסף בפרויקטים שלהם לקבוע את הערכים האלה. כך המשתמשים יוכלו להשתמש בתוסף שלכם עם הגדרות מסד הנתונים הקיימות שלהם.
מאפשרים למשתמש להגדיר את הנתיב שבו התוסף מחפש הודעות חדשות:
בקובץ
extension.yaml
, מוסיפים את הקטעparams
:- param: MESSAGE_PATH label: Message path description: >- What is the path at which the original text of a message can be found? type: string default: /messages/{pushId}/original required: true immutable: false
כך מגדירים פרמטר מחרוזת חדש שהמשתמשים יתבקשו להגדיר כשהם מתקינים את התוסף.
עדיין בקובץ
extension.yaml
, חוזרים להצהרהmakeuppercase
ומשנים את השדהresource
כך:resource: projects/_/instances/${DATABASE_INSTANCE}/refs/${param:MESSAGE_PATH}
האסימון
${param:MESSAGE_PATH}
הוא הפניה לפרמטר שהגדרתם זה עתה. כשהתוסף יפעל, האסימון הזה יוחלף בערך שהמשתמש הגדיר לפרמטר הזה, וכתוצאה מכך הפונקציהmakeuppercase
תקשיב לנתיב שהמשתמש ציין. אפשר להשתמש בתחביר הזה כדי להפנות לכל פרמטר שהוגדר על ידי משתמש בכל מקום ב-extension.yaml
(וגם ב-POSTINSTALL.md
– נרחיב על כך בהמשך).אפשר גם לגשת לפרמטרים בהגדרת המשתמש מקוד הפונקציות.
בפונקציה שכתבתם בקטע הקודם, קבעתם את הנתיב לבדיקה של השינויים באופן קבוע. משנים את הגדרת הטריגר כך שתתייחס לערך שהוגדר על ידי המשתמש:
functions/index.js
export const makeuppercase = database.ref(process.env.MESSAGE_PATH).onCreate
חשוב לזכור שב-Firebase Extensions, השינוי הזה הוא לצורכי תיעוד בלבד: כשפורסים Cloud Function כחלק מתוסף, הוא משתמש בהגדרת הטריגר בקובץ
extension.yaml
ומתעלם מהערך שצוין בהגדרת הפונקציה. עם זאת, מומלץ לתעד בקוד את המקור של הערך הזה.יכול להיות שתתאכזבו מכך ששינוי בקוד לא משפיע על זמן הריצה, אבל הלקח החשוב הוא שאפשר לגשת לכל פרמטר שהוגדר על ידי משתמש בקוד הפונקציה ולהשתמש בו כערך רגיל בלוגיקה של הפונקציה. כדי להדגים את היכולת הזו, מוסיפים את הצהרת היומן הבאה כדי להראות שאתם אכן ניגשים לערך שהמשתמש הגדיר:
functions/index.js
export const makeuppercase = database.ref(process.env.MESSAGE_PATH).onCreate( async (snapshot, context) => { logger.log("Found new message at ", snapshot.ref); // Grab the current value of what was written to the Realtime Database. ...
בדרך כלל, המשתמשים מתבקשים לספק ערכים לפרמטרים כשהם מתקינים תוסף. עם זאת, כשמשתמשים במהדורת האדמולטור לצורכי בדיקה ופיתוח, מדלגים על תהליך ההתקנה, ולכן במקום זאת מספקים ערכים לפרמטרים מוגדרי-משתמש באמצעות קובץ
env
.פותחים את הקובץ
functions/integration-tests/extensions/rtdb-uppercase-messages.env
ומחליפים את ההגדרה שלGREETING
בהגדרה הבאה:MESSAGE_PATH=/msgs/{pushId}/original
שימו לב שהנתיב שלמעלה שונה מנתיב ברירת המחדל ומנתיב ההגדרה שהגדרתם קודם. המטרה של השינוי היא רק להוכיח לכם, כשתנסו את התוסף המעודכן, שההגדרה שלכם נכנסה לתוקף.
עכשיו מפעילים מחדש את האמולטור ונכנסים שוב לממשק המשתמש של אמולטור מסדי הנתונים.
עורכים את צומת הבסיס של מסד הנתונים באמצעות הנתיב שהגדרתם למעלה:
- שדה:
msgs
- סוג:
json
- ערך:
{"11": {"original": "recipe"}}
כששומרים את השינויים במסד הנתונים, הפונקציה
makeuppercase
של התוסף אמורה להפעיל כמו קודם, אבל עכשיו היא אמורה גם להדפיס את הפרמטר שהוגדר על ידי המשתמש ביומן המסוף.- שדה:
7. הוספת אירועי הוק לצורך לוגיקה מוגדרת על ידי משתמש
ככותבי תוספים, כבר ראינו איך מוצר של Firebase יכול להפעיל את הלוגיקה שסיפקתם בתוסף: יצירת רשומות חדשות ב-Realtime Database מפעילה את הפונקציה makeuppercase
. לתוסף יכולה להיות מערכת יחסים דומה עם המשתמשים שמתקינים אותו: התוסף יכול להפעיל לוגיקה שהמשתמש מגדיר.
תוסף יכול לספק וו hooks סינכרוניים, וו hooks אסינכרוניים או את שניהם. באמצעות הוקס סינכרוניים, המשתמשים יכולים לבצע משימות שמונעות את השלמת אחת מהפונקציות של התוסף. אפשר להשתמש באפשרות הזו, למשל, כדי לתת למשתמשים דרך לבצע עיבוד מקדים בהתאמה אישית לפני שהתוסף יבצע את הפעולה שלו.
במדריך הזה תלמדו איך להוסיף ל-extension הוק אסינכרוני, שיאפשר למשתמשים להגדיר את שלבי העיבוד שלהם שיופעלו אחרי שה-extension יכתוב את ההודעה באותיות רישיות ל-Realtime Database. ב-hooks אסינכרונים נעשה שימוש ב-Eventarc כדי להפעיל פונקציות בהגדרת המשתמש. התוספים מכריזים על סוגי האירועים שהם משדרים, וכשהמשתמשים מתקינים את התוסף, הם בוחרים את סוגי האירועים שמעניינים אותם. אם הם יבחרו לפחות אירוע אחד, מערכת Firebase תקצה לערוץ Eventarc עבור התוסף כחלק מתהליך ההתקנה. לאחר מכן, המשתמשים יוכלו לפרוס פונקציות משלהם ב-Cloud שיאזינו לערוץ הזה ויופעלו כשהתוסף יפרסם אירועים חדשים.
כדי להוסיף הוק אסינכרוני:
בקובץ
extension.yaml
, מוסיפים את הקטע הבא, שמצהיר על סוג האירוע היחיד שהתוסף פולט:events: - type: test-publisher.rtdb-uppercase-messages.v1.complete description: >- Occurs when message uppercasing completes. The event subject will contain the RTDB URL of the uppercase message.
סוגי האירועים חייבים להיות ייחודיים באופן אוניברסלי. כדי להבטיח את הייחודיות, תמיד צריך לתת שמות לאירועים לפי הפורמט הבא:
<publisher-id>.<extension-id>.<version>.<description>
. (עדיין אין לכם מזהה בעל תוכן דיגיטלי, לכן תוכלו להשתמש ב-test-publisher
בינתיים).בסוף הפונקציה
makeuppercase
, מוסיפים קוד שמפרסם אירוע מהסוג שהצהרתם עליו:functions/index.js
// Import the Eventarc library: import { initializeApp } from "firebase-admin/app"; import { getEventarc } from "firebase-admin/eventarc"; const app = initializeApp(); // In makeuppercase, after upperRef.set(uppercase), add: // Set eventChannel to a newly-initialized channel, or `undefined` if events // aren't enabled. const eventChannel = process.env.EVENTARC_CHANNEL && getEventarc().channel(process.env.EVENTARC_CHANNEL, { allowedEventTypes: process.env.EXT_SELECTED_EVENTS, }); // If events are enabled, publish a `complete` event to the configured // channel. eventChannel && eventChannel.publish({ type: "test-publisher.rtdb-uppercase-messages.v1.complete", subject: upperRef.toString(), data: { "original": original, "uppercase": uppercase, }, });
קוד הדוגמה הזה מנצל את העובדה שמשתנה הסביבה
EVENTARC_CHANNEL
מוגדר רק כשהמשתמש מפעיל לפחות סוג אירוע אחד. אםEVENTARC_CHANNEL
לא מוגדר, הקוד לא מנסה לפרסם אירועים.אפשר לצרף מידע נוסף לאירוע ב-Eventarc. בדוגמה שלמעלה, לאירוע יש שדה
subject
שמכיל הפניה לערך שנוצר, ועומס נתונים (payload) מסוגdata
שמכיל את ההודעות המקוריות וההודעות באותיות רישיות. פונקציות בהגדרת המשתמש שמפעילות את האירוע יכולות להשתמש במידע הזה.בדרך כלל, משתני הסביבה
EVENTARC_CHANNEL
ו-EXT_SELECTED_EVENTS
מוגדרים על סמך האפשרויות שהמשתמש בחר במהלך ההתקנה. כדי לבדוק באמצעות הסימולטור, מגדירים את המשתנים האלה באופן ידני בקובץrtdb-uppercase-messages.env
:EVENTARC_CHANNEL=locations/us-central1/channels/firebase EXT_SELECTED_EVENTS=test-publisher.rtdb-uppercase-messages.v1.complete
בשלב הזה, סיימתם את השלבים הנדרשים להוספת הוק אירועים אסינכררוני לתוסף.
כדי לנסות את התכונה החדשה שהטמעתם, בשלבים הבאים תיכנסו לתפקיד של משתמש שמתקין את התוסף:
בתיקייה
functions/integration-tests
, מאתחלים פרויקט חדש ב-Firebase:firebase init functions
כשמוצגת בקשה, מבטלים את ההגדרה של פרויקט ברירת מחדל, בוחרים ב-JavaScript כשפת Cloud Functions ומתקינים את יחסי התלות הנדרשים. הפרויקט הזה מייצג פרויקט של משתמש שבו התוסף שלכם מותקן.
עורכים את הקובץ
integration-tests/functions/index.js
ומדביקים את הקוד הבא:import { logger } from "firebase-functions/v1"; import { onCustomEventPublished } from "firebase-functions/v2/eventarc"; import { initializeApp } from "firebase-admin/app"; import { getDatabase } from "firebase-admin/database"; const app = initializeApp(); export const extraemphasis = onCustomEventPublished( "test-publisher.rtdb-uppercase-messages.v1.complete", async (event) => { logger.info("Received makeuppercase completed event", event); const refUrl = event.subject; const ref = getDatabase().refFromURL(refUrl); const upper = (await ref.get()).val(); return ref.set(`${upper}!!!`); } );
זו דוגמה לפונקציית עיבוד נתונים לאחר העיבוד שיכול להיות שמשתמש יכתוב. במקרה הזה, הפונקציה מקשיבה לפרסום אירוע
complete
על ידי התוסף, וכשהיא מופעלת, היא מוסיפה שלוש נקודות קריאה להודעה שעברתה לכתב עליון.מפעילים מחדש את הסימולטור. הסימולטור יטען את הפונקציות של התוסף וגם את פונקציית העיבוד לאחר הניתוח שהגדיר 'המשתמש'.
נכנסים לממשק המשתמש של אמולטור מסד הנתונים ועורכים את צומת הבסיס של מסד הנתונים באמצעות הנתיב שהגדרתם למעלה:
- שדה:
msgs
- סוג:
json
- ערך:
{"11": {"original": "recipe"}}
כששומרים את השינויים במסד הנתונים, הפונקציה
makeuppercase
של התוסף והפונקציהextraemphasis
של המשתמש אמורות להפעיל ברצף, וכתוצאה מכך השדהupper
יקבל את הערךRECIPE!!!
.- שדה:
8. הוספת גורמים שמטפלים באירועים במחזור החיים
התוסף שכתבתם עד עכשיו מעבד הודעות בזמן היצירה שלהן. אבל מה קורה אם למשתמשים כבר יש מסד נתונים של הודעות כשהם מתקינים את התוסף? בתוספים של Firebase יש תכונה שנקראת הקאות לאירועים במחזור החיים, שאפשר להשתמש בה כדי להפעיל פעולות כשהתוסף מותקן, מתעדכן או מוגדר מחדש. בקטע הזה תלמדו איך להשתמש באירועי ה-lifecycle hook כדי למלא את מסד הנתונים הקיים של ההודעות בפרויקט בהודעות באותיות רישיות כשמשתמש מתקין את התוסף.
התוספים של Firebase משתמשים ב-Cloud Tasks כדי להריץ את פונקציות הטיפול באירועים במחזור החיים. מגדירים פונקציות טיפול באירועים באמצעות Cloud Functions. בכל פעם שמכונה של התוסף מגיעה לאחד מאירועי מחזור החיים הנתמכים, אם הגדרתם פונקציית טיפול באירועים, היא תוסיף את הפונקציה הזו לתור של Cloud Tasks. לאחר מכן, Cloud Tasks יבצע את הטיפול באופן אסינכרוני. בזמן שפועל טיפול באירועי מחזור חיים, מסוף Firebase ידווח למשתמש שיש משימה בטיפול במכונה של התוסף. פונקציית הטיפול היא זו שצריכה לדווח למשתמש על הסטטוס המתמשך ועל השלמת המשימה.
כדי להוסיף טיפול באירועי מחזור חיים שממלאים הודעות קיימות, מבצעים את הפעולות הבאות:
מגדירים פונקציית Cloud חדשה שמופעלת על ידי אירועים בתור המשימות:
functions/index.js
import { tasks } from "firebase-functions/v1"; import { getDatabase } from "firebase-admin/database"; import { getExtensions } from "firebase-admin/extensions"; import { getFunctions } from "firebase-admin/functions"; export const backfilldata = tasks.taskQueue().onDispatch(async () => { const batch = await getDatabase() .ref(process.env.MESSAGE_PATH) .parent.parent.orderByChild("upper") .limitToFirst(20) .get(); const promises = []; for (const key in batch.val()) { const msg = batch.child(key); if (msg.hasChild("original") && !msg.hasChild("upper")) { const upper = msg.child("original").val().toUpperCase(); promises.push(msg.child("upper").ref.set(upper)); } } await Promise.all(promises); if (promises.length > 0) { const queue = getFunctions().taskQueue( "backfilldata", process.env.EXT_INSTANCE_ID ); return queue.enqueue({}); } else { return getExtensions() .runtime() .setProcessingState("PROCESSING_COMPLETE", "Backfill complete."); } });
שימו לב שהפונקציה מעבדת רק כמה רשומות לפני שהיא מוסיפה את עצמה חזרה לתור המשימות. זוהי אסטרטגיה נפוצה לטיפול במשימות עיבוד שלא ניתן להשלים בחלון הזמן הקצוב לתפוגה של Cloud Function. לא ניתן לחזות כמה הודעות כבר יכולות להיות במסד הנתונים של משתמש כשהוא מתקין את התוסף, ולכן זוהי אסטרטגיה מתאימה.
בקובץ
extension.yaml
, מגדירים את פונקציית המילוי כמשאב תוסף עם המאפייןtaskQueueTrigger
:resources: - name: makeuppercase ... - name: backfilldata type: firebaseextensions.v1beta.function description: >- Backfill existing messages with uppercase versions properties: runtime: "nodejs18" taskQueueTrigger: {}
לאחר מכן מגדירים את הפונקציה כמתאם לאירועי מחזור החיים
onInstall
:lifecycleEvents: onInstall: function: backfilldata processingMessage: Uppercasing existing messages
אמנם נחמד שאפשר למלא את ההודעות הקיימות, אבל התוסף עדיין יכול לפעול בלי זה. במצבים כאלה, כדאי להגדיר את ההפעלה של הגורמים שמטפלים באירועים במחזור החיים כאופציונלית.
כדי לעשות זאת, מוסיפים פרמטר חדש ל-
extension.yaml
:- param: DO_BACKFILL label: Backfill existing messages description: >- Generate uppercase versions of existing messages? type: select required: true options: - label: Yes value: true - label: No value: false
לאחר מכן, בתחילת פונקציית המילוי, בודקים את הערך של הפרמטר
DO_BACKFILL
ויוצאים מוקדם אם הוא לא מוגדר:functions/index.js
if (!process.env.DO_BACKFILL) { return getExtensions() .runtime() .setProcessingState("PROCESSING_COMPLETE", "Backfill skipped."); }
בעקבות השינויים שלמעלה, התוסף יהפוך הודעות קיימות לאותיות רישיות אחרי ההתקנה.
עד עכשיו השתמשתם במהנתר התוספים כדי לפתח את התוסף ולבדוק את השינויים המתמשכים. עם זאת, אמולטור התוספים מדלג על תהליך ההתקנה, כך שצריך להתקין את התוסף בפרויקט אמיתי כדי לבדוק את פונקציית הטיפול באירוע onInstall
. זה טוב, כי בעקבות הוספת התכונה הזו של מילוי אוטומטי, התוסף במדריך מוכן עכשיו לקוד!
9. פריסה בפרויקט Firebase אמיתי
למרות שמהדמטור של התוספים הוא כלי מצוין ליצירת גרסאות אב מהירות של תוסף במהלך הפיתוח, בשלב מסוים כדאי לנסות אותו בפרויקט אמיתי.
כדי לעשות זאת, קודם צריך להגדיר פרויקט חדש עם כמה שירותים מופעלים:
- מוסיפים פרויקט חדש במסוף Firebase.
- משדרגים את הפרויקט לתוכנית Blaze בתשלום לפי שימוש. כדי להשתמש ב-Cloud Functions for Firebase, נדרש חשבון לחיוב בפרויקט, ולכן נדרש חשבון לחיוב גם כדי להתקין תוסף.
- בפרויקט החדש, מפעילים את Real-time Database.
- כדי לבדוק את היכולת של התוסף למלא נתונים קיימים במהלך ההתקנה, צריך לייבא נתונים לדוגמה למכונה של מסד הנתונים בזמן אמת:
- מורידים נתוני RTDB לזריעה.
- בדף Real-time Database במסוף Firebase, לוחצים על (עוד) > Import JSON ובוחרים את הקובץ שהורדתם.
כדי לאפשר לפונקציית המילוי לאחור להשתמש בשיטה
orderByChild
, צריך להגדיר את מסד הנתונים כך שיוסיף לאינדקס הודעות לפי הערך שלupper
:{ "rules": { ".read": false, ".write": false, "messages": { ".indexOn": "upper" } } }
עכשיו מתקינים את התוסף מהמקור המקומי בפרויקט החדש:
יוצרים ספרייה חדשה לפרויקט Firebase:
mkdir ~/extensions-live-test && cd ~/extensions-live-test
מפעילים את פרויקט Firebase בספריית העבודה:
firebase init database
כשמופיעה בקשה, בוחרים את הפרויקט שיצרתם.
מתקינים את התוסף בפרויקט Firebase המקומי:
firebase ext:install /path/to/rtdb-uppercase-messages
כאן אפשר לראות איך חוויית המשתמש נראית כשמתקינים תוסף באמצעות הכלי Firebase CLI. חשוב לבחור באפשרות 'כן' כשכלי ההגדרה שואל אם רוצים למלא את מסדי הנתונים הקיימים.
אחרי שבוחרים את אפשרויות ההגדרה, ה-CLI של Firebase ישמור את ההגדרות בתיקייה
extensions
ויתעדה את מיקום המקור של התוסף בקובץfirebase.json
. שתי הרשומות האלה נקראות יחד manifest של התוספים. משתמשים יכולים להשתמש במניפסט כדי לשמור את הגדרות התוספים ולפרוס אותן בפרויקטים שונים.פורסים את הגדרות התוסף בפרויקט החי:
firebase deploy --only extensions
אם הכל יתבצע כראוי, ה-CLI של Firebase אמור להעלות את התוסף לפרויקט ולהתקין אותו. בסיום ההתקנה, המשימה של מילוי החסר תפעל תוך כמה דקות מסד הנתונים יתעדכן בהודעות באותיות רישיות. מוסיפים כמה צמתים חדשים למסד הנתונים של ההודעות ומוודאים שהתוסף פועל גם בהודעות חדשות.
10. כתיבת מסמכי תיעוד
לפני שאתם משתפים את התוסף עם משתמשים, חשוב לוודא שאתם מספקים מספיק מסמכי עזרה כדי שהם יוכלו להשתמש בו.
כשאתם מאתחלים את פרויקט התוסף, ה-CLI של Firebase יוצר גרסאות של stubs למסמכי העזרה הנדרשים. מעדכנים את הקבצים האלה כך שישקפו במדויק את התוסף שיצרתם.
extension.yaml
כבר עדכנתם את הקובץ הזה במהלך הפיתוח של התוסף, ולכן אין צורך לבצע עדכונים נוספים כרגע.
עם זאת, אל תזניחו את החשיבות של המסמכים שמופיעים בקובץ הזה. בנוסף למידע המזהה החשוב של התוסף – שם, תיאור, מחבר, מיקום המאגר הרשמי – הקובץ extension.yaml
מכיל מסמכי עזרה למשתמש לגבי כל משאב ופרמטר שניתן להגדרה על ידי המשתמש. המידע הזה מוצג למשתמשים במסוף Firebase, ב-Extensions Hub וב-Firebase CLI.
PREINSTALL.md
בקובץ הזה, עליכם לספק את המידע שהמשתמשים צריכים לפני שהם מתקינים את התוסף: תיאור קצר של הפעולות שהתוסף מבצע, הסבר על תנאי הקבלה ועל ההשלכות של חיוב על התקנת התוסף. אם יש לכם אתר עם מידע נוסף, זה גם מקום טוב לקשר אליו.
הטקסט של הקובץ הזה מוצג למשתמש במרכז התוספים ובאמצעות הפקודה firebase ext:info
.
דוגמה לקובץ PREINSTALL:
Use this extension to automatically convert strings to upper case when added to
a specified Realtime Database path.
This extension expects a database layout like the following example:
"messages": {
MESSAGE_ID: {
"original": MESSAGE_TEXT
},
MESSAGE_ID: {
"original": MESSAGE_TEXT
},
}
When you create new string records, this extension creates a new sibling record
with upper-cased text:
MESSAGE_ID: {
"original": MESSAGE_TEXT,
"upper": UPPERCASE_MESSAGE_TEXT,
}
#### Additional setup
Before installing this extension, make sure that you've
[set up Realtime Database](https://firebase.google.com/docs/database/quickstart)
in your Firebase project.
#### Billing
To install an extension, your project must be on the
[Blaze (pay as you go) plan](https://firebase.google.com/pricing).
- This extension uses other Firebase and Google Cloud Platform services, which
have associated charges if you exceed the service's no-cost tier:
- Realtime Database
- Cloud Functions (Node.js 10+ runtime)
[See FAQs](https://firebase.google.com/support/faq#extensions-pricing)
- If you enable events,
[Eventarc fees apply](https://cloud.google.com/eventarc/pricing).
POSTINSTALL.md
הקובץ הזה מכיל מידע שימושי למשתמשים אחרי שהם מתקינים את התוסף: למשל, שלבי הגדרה נוספים, דוגמה לשימוש בתוסף וכו'.
התוכן של POSTINSTALL.md מוצג במסוף Firebase אחרי שמגדירים ומתקינים את התוסף. אפשר להפנות לפרמטרים של משתמשים בקובץ הזה, והם יוחלפו בערכים שהוגדרו.
דוגמה לקובץ לאחר ההתקנה של התוסף למדריך:
### See it in action
You can test out this extension right away!
1. Go to your
[Realtime Database dashboard](https://console.firebase.google.com/project/${param:PROJECT_ID}/database/${param:PROJECT_ID}/data) in the Firebase console.
1. Add a message string to a path that matches the pattern `${param:MESSAGE_PATH}`.
1. In a few seconds, you'll see a sibling node named `upper` that contains the
message in upper case.
### Using the extension
We recommend adding data by pushing -- for example,
`firebase.database().ref().push()` -- because pushing assigns an automatically
generated ID to the node in the database. During retrieval, these nodes are
guaranteed to be ordered by the time they were added. Learn more about reading
and writing data for your platform (iOS, Android, or Web) in the
[Realtime Database documentation](https://firebase.google.com/docs/database/).
### Monitoring
As a best practice, you can
[monitor the activity](https://firebase.google.com/docs/extensions/manage-installed-extensions#monitor)
of your installed extension, including checks on its health, usage, and logs.
CHANGELOG.md
בנוסף, כדאי לתעד את השינויים שביצעתם בין גרסאות של התוסף בקובץ CHANGELOG.md
.
מאחר שתוסף הדוגמה לא פורסם מעולם, ביומן השינויים יש רק רשומה אחת:
## Version 0.0.1
Initial release of the _Convert messages to upper case_ extension.
README.md
ברוב התוספים יש גם קובץ readme למשתמש שמבקר במאגר של התוסף. אפשר לכתוב את הקובץ הזה באופן ידני או ליצור קובץ readme באמצעות הפקודה.
במסגרת המדריך הזה, נדלג על כתיבת קובץ readme.
מסמכים נוספים
המסמכים שצוינו למעלה הם קבוצת המסמכים המינימלית שצריך לספק למשתמשים. כדי שמשתמשים יוכלו להשתמש בהם בצורה יעילה, תוספים רבים דורשים מסמכי עזרה מפורטים יותר. במקרה כזה, כדאי לכתוב מסמכי עזרה נוספים ולארח אותם במקום שבו תוכלו להפנות אליהם משתמשים.
במסגרת המדריך הזה, נוותר על כתיבת תיעוד מפורט יותר.
11. פרסום ב-Extensions Hub
עכשיו, אחרי שהקוד של התוסף הושלם ונערך עבורו מסמך תיעוד, אתם מוכנים לשתף אותו עם העולם ב-Extensions Hub. אבל מכיוון שזהו רק מדריך, אל תעשו זאת בפועל. עכשיו אפשר להתחיל לכתוב תוסף משלכם, בעזרת מה שלמדתם כאן ובשאר מסמכי התיעוד של Firebase Extensions לבעלי תוכן דיגיטלי, ובאמצעות בדיקת המקור של התוספים הרשמיים שנכתבו על ידי Firebase.
כשתהיו מוכנים לפרסם את העבודה שלכם במרכז התוספים, תוכלו לפעול לפי השלבים הבאים:
- אם אתם מפרסמים את התוסף הראשון שלכם, עליכם להירשם כמפרסמים של תוספים. כשאתם נרשמים כבעלים של תוספי Chrome, אתם יוצרים מזהה בעל תוכן דיגיטלי שמאפשר למשתמשים לזהות אתכם במהירות ככותבים של התוספים.
אירוח קוד המקור של התוסף במיקום שאפשר לאמת באופן ציבורי. כשהקוד זמין ממקור שניתן לאמת, מערכת Firebase יכולה לפרסם את התוסף ישירות מהמיקום הזה. כך תוכלו לוודא שאתם מפרסמים את הגרסה העדכנית של התוסף, ותוכלו לאפשר למשתמשים לבדוק את הקוד שהם מתקינים בפרויקטים שלהם.
נכון לעכשיו, המשמעות היא שהתוסף צריך להיות זמין במאגר ציבורי ב-GitHub.
מעלים את התוסף ל-Extensions Hub באמצעות הפקודה
firebase ext:dev:upload
.עוברים למרכז הבקרה של בעלי האפליקציות במסוף Firebase, מחפשים את התוסף שהעליתם ולוחצים על 'פרסום ב-Extensions Hub'. הבקשה תישלח לבדיקה של צוות הבדיקה שלנו, והתהליך עשוי להימשך כמה ימים. אם הבקשה תאושר, התוסף יפורסם במרכז התוספים. אם הבקשה תידחה, תקבלו הודעה עם הסבר על הסיבה לדחייה. לאחר מכן תוכלו לטפל בבעיות שדווחו ולשלוח את הבקשה לבדיקה מחדש.