משנים את הייעוד של קוד Cloud Functions כתוסף ל-Firebase

1. לפני שמתחילים

תוסף ל-Firebase מבצע משימה ספציפית או קבוצה של משימות בתגובה לבקשות HTTP או לאירועים שמופעלים ממוצרים אחרים של Firebase ו-Google, כמו העברת הודעות בענן ב-Firebase, ‏ Cloud Firestore או Pub/Sub.

מה תפַתחו

בקודלאב הזה תלמדו איך ליצור תוסף ל-Firebase לגאו-האש (geohashing). לאחר הפריסה, התוסף ממיר קואורדינטות X ו-Y ל-geohash בתגובה לאירועים ב-Firestore או באמצעות הפעלות של פונקציות שניתן לקרוא להן. אפשר להשתמש באפשרות הזו כחלופה להטמעת ספריית GeoFire בכל פלטפורמות היעד שלכם לאחסון נתונים, וכך לחסוך זמן.

התוסף של geohash שמוצג במסוף Firebase

מה תלמדו

  • איך הופכים קוד קיים של Cloud Functions לתוסף Firebase שניתן להפצה
  • איך מגדירים קובץ extension.yaml
  • איך מאחסנים מחרוזות רגישות (מפתחות API) בתוסף
  • איך מאפשרים למפתחים של התוסף להגדיר אותו בהתאם לצרכים שלהם
  • איך בודקים ומפרסים את התוסף

מה צריך להכין

  • Firebase CLI (התקנה והתחברות)
  • חשבון Google, כמו חשבון Gmail
  • Node.js ו-npm
  • סביבת הפיתוח המועדפת עליכם

2. טיפים להגדרה

קבלת הקוד

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

  1. פותחים את קובץ ה-ZIP שהורדתם.
  2. כדי להתקין את יחסי התלות הנדרשים, פותחים את הטרמינל בספרייה functions ומריצים את הפקודה npm install.

הגדרת Firebase

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

רוצים לדלג קדימה?

אפשר להוריד גרסה מושלמת של הקוד. אם נתקעתם בדרך או אם אתם רוצים לראות איך נראה תוסף שהושלם, תוכלו לבדוק את ההסתעפות codelab-end במאגר GitHub או להוריד את קובץ ה-zip המלא.

3. בדיקת הקוד

  • פותחים את הקובץ index.ts מקובץ ה-zip. שימו לב שהיא מכילה שתי הצהרות של Cloud Functions.

מה הפונקציות האלה עושות?

פונקציות הדגמה האלה משמשות ליצירת גיאוהאש. הם מקבלים זוג קואורדינטות וממירים אותו לפורמט שמותאם לשאילתות גיאוגרפיות ב-Firestore. הפונקציות מדמות את השימוש בקריאת API כדי שתוכלו לקבל מידע נוסף על הטיפול בסוגי מידע אישי רגיש בתוספים. מידע נוסף זמין במאמר הרצת שאילתות גיאוגרפיות על נתונים ב-Firestore.

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

צריך להצהיר על קבועים בשלב מוקדם, בחלק העליון של הקובץ index.ts. חלק מהקבועים האלה מופיעים בטריגרים שהוגדרו בתוסף.

index.ts

import {firestore} from "firebase-functions";
import {initializeApp} from "firebase-admin/app";
import {GeoHashService, ResultStatusCode} from "./fake-geohash-service";
import {onCall} from "firebase-functions/v1/https";
import {fieldValueExists} from "./utils";

const documentPath = "users/{uid}";
const xField = "xv";
const yField = "yv";
const apiKey = "1234567890";
const outputField = "hash";

initializeApp();

const service = new GeoHashService(apiKey);

טריגר של Firestore

הפונקציה הראשונה בקובץ index.ts נראית כך:

index.ts

export const locationUpdate = firestore.document(documentPath)
  .onWrite((change) => {
    // item deleted
    if (change.after == null) {
      return 0;
    }
    // double check that both values exist for computation
    if (
      !fieldValueExists(change.after.data(), xField) ||
      !fieldValueExists(change.after.data(), yField)
    ) {
      return 0;
    }
    const x: number = change.after.data()![xField];
    const y: number = change.after.data()![yField];
    const hash = service.convertToHash(x, y);
    // This is to check whether the hash value has changed. If
    // it hasn't, you don't want to write to the document again as it
    // would create a recursive write loop.
    if (fieldValueExists(change.after.data(), outputField)
      && change.after.data()![outputField] == hash) {
      return 0;
    }
    return change.after.ref
      .update(
        {
          [outputField]: hash.hash,
        }
      );
  });

הפונקציה הזו היא טריגר של Firestore. כשאירוע כתיבת נתונים מתרחש במסד הנתונים, הפונקציה מגיבה לאירוע הזה על ידי חיפוש של שדה xv ושדה yv. אם שני השדות האלה קיימים, היא מחשבת את ה-geohash וכותבת את הפלט למיקום פלט מוגדר של מסמך. מסמך הקלט מוגדר על ידי הקבוע users/{uid}, כלומר הפונקציה קוראת כל מסמך שנכתב באוסף users/ ולאחר מכן מעבדת גיאוהאש למסמכים האלה. לאחר מכן, הוא מוציא את הגיבוב לשדה גיבוב באותו מסמך.

פונקציות שניתן להפעיל

הפונקציה הבאה בקובץ index.ts נראית כך:

index.ts

export const callableHash = onCall((data, context) => {
  if (context.auth == undefined) {
    return {error: "Only authorized users are allowed to call this endpoint"};
  }
  const x = data[xField];
  const y = data[yField];
  if (x == undefined || y == undefined) {
    return {error: "Either x or y parameter was not declared"};
  }
  const result = service.convertToHash(x, y);
  if (result.status != ResultStatusCode.ok) {
    return {error: `Something went wrong ${result.message}`};
  }
  return {result: result.hash};
});

שימו לב לפונקציה onCall. הוא מציין שהפונקציה הזו היא פונקציה שניתן להפעיל, שאפשר להפעיל אותה מתוך הקוד של אפליקציית הלקוח. הפונקציה הניתנת לקריאה מקבלת את הפרמטרים x ו-y ומחזירה geohash. הפונקציה הזו לא תיקרא ישירות ב-codelab הזה, אבל היא כלולה כאן כדוגמה למשהו שצריך להגדיר בתוסף של Firebase.

4. הגדרת קובץ extension.yaml

עכשיו, אחרי שהבנתם מה הקוד של Cloud Functions בתוסף עושה, אתם מוכנים לארוז אותו ולהפיץ אותו. כל תוסף של Firebase מגיע עם קובץ extension.yaml שמתאר את הפעולות של התוסף ואת האופן שבו הוא פועל.

קובץ extension.yaml דורש מטא-נתונים ראשוניים לגבי התוסף. כל אחד מהשלבים הבאים יעזור לכם להבין את המשמעות של כל השדות ולמה אתם צריכים אותם.

  1. יוצרים קובץ extension.yaml בספריית הבסיס של הפרויקט שהורדתם מקודם. מתחילים בהוספת הפרטים הבאים:
name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

השם של התוסף משמש כבסיס למזהה המופע של התוסף (משתמשים יכולים להתקין כמה מופעים של תוסף, לכל אחד מהם מזהה משלו). לאחר מכן, מערכת Firebase יוצרת את השם של חשבונות השירות של התוסף ואת המשאבים הספציפיים לתוסף באמצעות מזהה המכונה הזה. מספר הגרסה מציין את גרסת התוסף. היא צריכה לפעול בהתאם לשיטת ניהול גרסאות סמנטית, וצריך לעדכן אותה בכל פעם שמבצעים שינויים בפונקציונליות של התוסף. גרסת המפרט של התוסף משמשת לקביעת המפרט של תוספי Firebase שצריך לפעול לפיו. במקרה הזה, נעשה שימוש ב-v1beta.

  1. מוסיפים לקובץ YAML פרטים ידידותיים למשתמש:
...

displayName: Latitude and longitude to GeoHash converter
description: A converter for changing your Latitude and longitude coordinates to geohashes.

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

התוסף Geohash Converter כפי שהוא מופיע ב-extensions.dev

  1. מציינים את הרישיון של הקוד בתוסף.
...

license: Apache-2.0  # The license you want for the extension
  1. מציינים מי כתב את התוסף ואם נדרש חיוב כדי להתקין אותו:
...

author:
  authorName: AUTHOR_NAME
  url: https://github.com/Firebase

billingRequired: true

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

זהו המספר המינימלי של שדות שנדרשים בקובץ extension.yaml כדי לזהות את התוסף הזה. פרטים נוספים על פרטי זיהוי אחרים שאפשר לציין בתוסף זמינים במסמכי התיעוד.

5. המרת הקוד של Cloud Functions למשאב Extensions

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

מיקום פריסה שהוגדר על ידי המשתמש

  1. מאפשרים למשתמש לציין את המיקום שבו הוא רוצה לפרוס את התוסף הזה ולהחליט אם עדיף לארח את התוסף קרוב יותר למשתמשי הקצה או קרוב יותר למסד הנתונים שלו. בקובץ extension.yaml, כוללים את האפשרות לבחור מיקום.

extension.yaml

עכשיו אפשר לכתוב את ההגדרות של משאב הפונקציה.

  1. בקובץ extension.yaml, יוצרים אובייקט משאב לפונקציה locationUpdate. מוסיפים את הטקסט הבא לקובץ extension.yaml:
resources:
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/users/{uid}

מגדירים את name בתור שם הפונקציה שמוגדר בקובץ index.ts של הפרויקט. מציינים את type של הפונקציה שנפרסת. בשלב הזה, הערך צריך להיות תמיד firebaseextensions.v1beta.function. לאחר מכן מגדירים את properties של הפונקציה הזו. הנכס הראשון שמגדירים הוא eventTrigger שמשויך לפונקציה הזו. כדי לשקף את התמיכה הנוכחית של התוסף, משתמשים ב-eventType של providers/cloud.firestore/eventTypes/document.write, שמופיע במסמכי העזרה של כתיבת פונקציות Cloud עבור התוסף. מגדירים את resource כמיקום של המסמכים. מכיוון שהמטרה הנוכחית היא לשקף את מה שקיים בקוד, נתיב המסמך מקשיב ל-users/{uid}, כאשר מיקום מסד הנתונים שמוגדר כברירת מחדל מופיע לפניו.

  1. לתוסף נדרשות הרשאות קריאה וכתיבה למסד הנתונים של Firestore. בסוף קובץ extension.yaml, מציינים את תפקידי ה-IAM שלגביהם לתוסף צריכה להיות גישה כדי לעבוד עם מסד הנתונים בפרויקט Firebase של המפתח.
roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

התפקיד datastore.user מגיע מרשימת תפקידי IAM נתמכים לתוספים. מכיוון שהתוסף יקרא ויכתוב, התפקיד datastore.user מתאים כאן.

  1. צריך להוסיף גם את הפונקציה שניתן לבצע קריאה אליה. בקובץ extension.yaml, יוצרים משאב חדש בנכס resources. המאפיינים האלה ספציפיים לפונקציה שניתן לבצע קריאה אליה:
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

בקוד הקודם השתמשנו ב-eventTrigger, אבל כאן צריך להשתמש ב-httpsTrigger, שמכסה גם פונקציות שניתן להפעיל וגם פונקציות HTTPS.

בדיקת קוד

זוהי הגדרה רבה כדי ש-extension.yaml יתאים לכל מה שהקוד בקובץ index.ts עושה. קובץ extension.yaml המלא אמור להיראות כך בשלב הזה:

extension.yaml

name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

displayName: Latitude and Longitude to GeoHash converter
description: A converter for changing your Latitude and Longitude coordinates to geohashes.

license: Apache-2.0  # The license you want for the extension

author:
  authorName: Sparky
  url: https://github.com/Firebase

billingRequired: true

resources:
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/users/{uid}
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

בדיקת סטטוס

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

  1. אם עדיין לא עשיתם זאת, צריך להפעיל את npm run build בתיקיית הפונקציות של פרויקט התוספים שהורדתם.
  2. יוצרים ספרייה חדשה במערכת המארחת ומקשרים אותה לפרויקט Firebase באמצעות firebase init.
cd ..
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
    This command creates a `firebase.json` file in the directory. In the following steps, you push the configuration specified in this file to Firebase.
  1. מאותה ספרייה, מריצים את firebase ext:install. מחליפים את /path/to/extension בנתיב המוחלט לספרייה שמכילה את קובץ extension.yaml.
firebase ext:install /path/to/extension
    This command does two things:
  • תוצג בקשה לציין את ההגדרות למכונה של התוסף, וייווצר קובץ *.env שמכיל את פרטי ההגדרה של המכונה.
  • הוא מוסיף את מופע התוסף לקטע extensions ב-firebase.json. הוא משמש כמפה של מזהה המכונה לגרסה של התוסף.
  • מכיוון שהפרויקט מופעל באופן מקומי, אפשר לציין שרוצים להשתמש בקובץ מקומי במקום ב-Google Cloud Secret Manager.

צילום מסך של תהליך התקנת התוסף, שבו מוצג שהמערכת משתמשת בקובץ מקומי לשמירת סודות בזמן התקנת התוסף

  1. מפעילים את המהדמנים של Firebase עם התצורה החדשה:
firebase emulators:start
  1. אחרי שמריצים את emulators:start, עוברים לכרטיסייה Firestore בתצוגת האינטרנט של המהדמנים.
  2. מוסיפים מסמך לאוסף users עם שדה מספר xv ושדה מספר yv.

תיבת דו-שיח שמוצגת במהכלי ההדמיה של Firebase כדי להתחיל אוסף עם מזהה האוסף שמכיל את הביטוי

  1. אם ההתקנה של התוסף הושלמה, התוסף יוצר במסמך שדה חדש בשם hash.

האוסף users עם מסמך משתמש שיש בו שדות xv,‏ yv ו-hash.

ניקוי כדי למנוע התנגשויות

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

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

firebase ext:uninstall geohash-ext

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

6. איך מאפשרים למשתמשים להגדיר את התוסף

בשלב הזה בקודלאב, יש לכם תוסף שמוגדר לשימוש עם ההגדרה המוגדרת מראש של הפונקציות שכבר כתבתם, אבל מה קורה אם המשתמש רוצה להשתמש בקו הרוחב ובקו האורך במקום ב-y וב-x בשדות שמציינים את המיקום במישור קרטוזי? בנוסף, איך אפשר לגרום למשתמש הקצה לספק מפתח API משלו, במקום לאפשר לו לצרוך את מפתח ה-API שסופק? אתם עלולים לחרוג במהירות מהמכסה של ה-API הזה. במקרה כזה, מגדירים פרמטרים ומשתמשים בהם.

מגדירים פרמטרים בסיסיים בקובץ extension.yaml

מומלץ להתחיל בהמרה של הפריטים שלגביהם יכול להיות למפתחים הגדרה מותאמת אישית. הפרמטרים הראשונים הם XFIELD ו-YFIELD.

  1. בקובץ extension.yaml, מוסיפים את הקוד הבא, שמשתמש בפרמטר השדה XFIELD ובפרמטר השדה YFIELD. הפרמטרים האלה נמצאים בתוך מאפיין ה-YAML params שהוגדר קודם:

extension.yaml

params:
  - param: XFIELD
    label: The X Field Name
    description: >-
      The X Field is also known as the **longitude** value. What does
      your Firestore instance refer to as the X value or the longitude
      value. If no value is specified, the extension searches for
      field 'xv'.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: xv
    required: false
    immutable: false
    example: xv
  - param: YFIELD
    label: The Y Field Name
    description: >-
      The Y Field is also known as the **latitude** value. What does
      your Firestore instance refer to as the Y value or the latitude
      value. If no value is specified, the extension searches for
      field 'yv'.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: yv
    required: false
    immutable: false
    example: yv
  • param נותן שם לפרמטר באופן שגלוי לכם, יצרני התוסף. יש להשתמש בערך הזה בהמשך, כאשר מציינים את ערכי הפרמטרים.
  • label הוא מזהה שקריא למפתח, כדי להסביר לו מה הפרמטר עושה.
  • description – תיאור מפורט של הערך. מכיוון שהתג הזה תומך ב-Markdown, אפשר לקשר אותו למסמכי עזרה נוספים או להדגיש מילים שעשויות להיות חשובות למפתח.
  • type מגדיר את מנגנון הקלט של האופן שבו משתמש מגדיר את ערך הפרמטר. יש הרבה סוגים, כולל string,‏ select,‏ multiSelect,‏ selectResource ו-secret. מידע נוסף על כל אחת מהאפשרויות האלה זמין במסמכי התיעוד.
  • validationRegex מגביל את הערך שהמפתח מזין לערך ביטוי רגולרי מסוים (בדוגמה, הוא מבוסס על ההנחיות לבחירת שמות שדות פשוטים שזמינות כאן). אם הפעולה הזו נכשלת…
  • validationErrorMessage מתריע למפתח על ערך הכשל.
  • default הוא הערך שיהיה אם המפתח לא מזין טקסט.
  • הערך required מציין שהמפתח לא חייב להזין טקסט.
  • הערך immutable מאפשר למפתח לעדכן את התוסף הזה ולשנות את הערך הזה. במקרה כזה, המפתח אמור להיות מסוגל לשנות את שמות השדות בהתאם לשינויים בדרישות.
  • example מאפשרת לכם לראות איך נראה קלט תקין.

זה היה הרבה מידע להבין!

  1. יש לכם עוד שלושה פרמטרים להוסיף לקובץ extension.yaml לפני שמוסיפים פרמטר מיוחד.
  - param: INPUTPATH
    label: The input document to listen to for changes
    description: >-
      This is the document where you write an x and y value to. Once
      that document has received a value, it notifies the extension to
      calculate a geohash and store that in an output document in a certain
      field. This accepts function [wildcard parameters](https://firebase.google.com/docs/functions/firestore-events#wildcards-parameters)
    type: string
    validationRegex: ^[^/]+(/[^/]*/[^/]*)*/[^/]+$
    validationErrorMessage: >-
      This must point to a document path, not a collection path from the root
      of the database. It must also not start or end with a '/' character.
    required: true
    immutable: false
    example: users/{uid}
  - param: OUTPUTFIELD
    label: Geohash field
    description: >-
      This specifies the field in the output document to store the geohash in.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    required: false
    default: hash
    immutable: false
    example: hash

הגדרת פרמטרים רגישים

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

  • בקובץ extension.yaml, מוסיפים את הקוד הבא:

extension.yaml

  - param: APIKEY
    label: GeohashService API Key
    description: >-
      Your geohash service API Key. Since this is a demo, and not a real
      service, you can use : 1234567890.
    type: secret
    required: true
    immutable: false

מעדכנים את המאפיינים של resource כך שישתמשו בפרמטרים

כפי שצוין קודם, המשאב (ולא הפונקציה) מגדיר את אופן המעקב אחרי המשאב, לכן צריך לעדכן את המשאב locationUpdate כדי להשתמש בפרמטר החדש.

  • בקובץ extension.yaml, מוסיפים את הקוד הבא:

extension.yaml

## Change from this
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/users/{uid}]

## To this
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/${INPUTPATH}

בודקים את הקובץ extension.yaml

  • בודקים את הקובץ extension.yaml. הוא אמור להיראות כך:

extension.yaml

name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

displayName: Latitude and Longitude to GeoHash converter
description: A converter for changing your Latitude and Longitude coordinates to geohashes.

license: Apache-2.0  # The license you want to use for the extension

author:
  authorName: Sparky
  url: https://github.com/Firebase

billingRequired: true

params:
  - param: XFIELD
    label: The X Field Name
    description: >-
      The X Field is also known as the **longitude** value. What does
      your Firestore instance refer to as the X value or the longitude
      value. If you don't provide a value for this field, the extension will use 'xv' as the default value.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: xv
    required: false
    immutable: false
    example: xv
  - param: YFIELD
    label: The Y Field Name
    description: >-
      The Y Field is also known as the **latitude** value. What does
      your Firestore instance refer to as the Y value or the latitude
      Value. If you don't provide a value for this field, the extension will use 'yv' as the default value.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: yv
    required: false
    immutable: false
    example: yv
  - param: INPUTPATH
    label: The input document to listen to for changes
    description: >-
      This is the document where you write an x and y value to. Once
      that document has been modified, it notifies the extension to
      compute a geohash and store that in an output document in a certain
      field. This accepts function [wildcard parameters](https://firebase.google.com/docs/functions/firestore-events#wildcards-parameters)
    type: string
    validationRegex: ^[^/]+(/[^/]*/[^/]*)*/[^/]+$
    validationErrorMessage: >-
      This must point to a document path, not a collection path from the root
      of the database. It must also not start or end with a '/' character.
    required: true
    immutable: false
    example: users/{uid}
  - param: OUTPUTFIELD
    label: Geohash field
    description: >-
      This specifies the field in the output document to store the geohash in.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    required: false
    default: hash
    immutable: false
    example: hash
  - param: APIKEY
    label: GeohashService API Key
    description: >-
      Your geohash service API Key. Since this is a demo, and not a real
      service, you can use : 1234567890.
    type: secret
    required: true
    immutable: false

resources:
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/${INPUTPATH}
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

גישה לפרמטרים בקוד

עכשיו, אחרי שכל הפרמטרים מוגדרים בקובץ extension.yaml, מוסיפים אותם לקובץ index.ts.

  • בקובץ index.ts, מחליפים את ערכי ברירת המחדל ב-process.env.PARAMETER_NAME, שמאחזר את ערכי הפרמטרים המתאימים ומאכלס אותם בקוד הפונקציה שנפרס בפרויקט Firebase של המפתח.

index.ts

// Replace this:
const documentPath = "users/{uid}";
const xField = "xv";
const yField = "yv";
const apiKey = "1234567890";
const outputField = "hash";

// with this:
const documentPath = process.env.INPUTPATH!; // this value is ignored since its read from the resource
const xField = process.env.XFIELD!;
const yField = process.env.YFIELD!;
const apiKey = process.env.APIKEY!;
const outputField = process.env.OUTPUTFIELD!;

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

7. יצירת מסמכי עזרה למשתמש

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

  1. מתחילים ביצירת הקובץ PREINSTALL.md, שמשמשים לתיאור הפונקציונליות, הדרישות המוקדמות להתקנה וההשלכות האפשריות על החיוב.

PREINSTALL.md

Use this extension to automatically convert documents with a latitude and
longitude to a geohash in your database. Additionally, this extension includes a callable function that allows users to make one-time calls
to convert an x,y coordinate into a geohash.

Geohashing is supported for latitudes between 90 and -90 and longitudes
between 180 and -180.

#### Third Party API Key

This extension uses a fictitious third-party API for calculating the
geohash. You need to supply your own API keys. (Since it's fictitious,
you can use 1234567890 as an API key).

#### Additional setup

Before installing this extension, make sure that you've [set up a Cloud
Firestore database](https://firebase.google.com/docs/firestore/quickstart) in your Firebase project.

After installing this extension, you'll need to:

- Update your client code to point to the callable geohash function if you
want to perform arbitrary geohashes.

Detailed information for these post-installation tasks are provided after
you install this extension.

#### 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:
 - Cloud Firestore
 - Cloud Functions (Node.js 16+ runtime. [See
FAQs](https://firebase.google.com/support/faq#extensions-pricing))
 - [Cloud Secret Manager](https://cloud.google.com/secret-manager/pricing)
  1. כדי לחסוך זמן בכתיבת README.md לפרויקט הזה, אפשר להשתמש בשיטת הנוחות:
firebase ext:info . --markdown > README.md

הקובץ הזה משלב את התוכן של הקובץ PREINSTALL.md עם פרטים נוספים על התוסף מהקובץ extension.yaml.

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

  1. יוצרים קובץ POSTINSTALL.md ומוסיפים את הפרטים הבאים לאחר ההתקנה:

POSTINSTALL.md

Congratulations on installing the geohash extension!

#### Function information

* **Firestore Trigger** - ${function:locationUpdate.name} was installed
and is invoked when both an x field (${param:XFIELD}) and y field
(${param:YFIELD}) contain a value.

* **Callable Trigger** - ${function:callableHash.name} was installed and
can be invoked by writing the following client code:
 ```javascript
import { getFunctions, httpsCallable } from "firebase/functions";
const functions = getFunctions();
const geoHash = httpsCallable(functions, '${function:callableHash.name}');
geoHash({ ${param:XFIELD}: -122.0840, ${param:YFIELD}: 37.4221 })
  .then((result) => {
    // Read result of the Cloud Function.
    /** @type {any} */
    const data = result.data;
    const error = data.error;
    if (error != null) {
        console.error(`callable error : ${error}`);
    }
    const result = data.result;
    console.log(result);
  });

מעקב

מומלץ לעקוב אחרי הפעילות של התוסף שהותקן, כולל בדיקות של התקינות, השימוש והיומנים שלו.

The output rendering looks something like this when it's deployed:

<img src="img/82b54a5c6ca34b3c.png" alt="A preview of the latitude and longitude geohash converter extension in the firebase console"  width="957.00" />


## Test the extension with the full configuration
Duration: 03:00


It's time to make sure that the user-configurable extension is working the way it is intended.

* Change into the functions folder and ensure that the latest compiled version of the extensions exists. In the extensions project functions directory, call:

```console
npm run build

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

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

התקנה ובדיקה באמצעות המהדמנים של Firebase

  1. יוצרים ספרייה חדשה במערכת המארחת ומקשרים אותה לפרויקט Firebase באמצעות firebase init.
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. בספרייה הזו, מריצים את firebase ext:install כדי להתקין את התוסף. מחליפים את /path/to/extension בנתיב המוחלט לספרייה שמכילה את קובץ ה-extension.yaml. הפקודה הזו מפעילה את תהליך ההתקנה של התוסף ויוצרת קובץ .env שמכיל את ההגדרות שלכם, לפני שההגדרות מועברות אל Firebase או אל המהדמנים.
firebase ext:install /path/to/extension
  • מכיוון שמפרסים את הפרויקט באופן מקומי, מציינים שרוצים להשתמש בקובץ מקומי במקום ב-Google Cloud Secret Manager.

da928c65ffa8ce15.png

  1. מפעילים את חבילת הכלים לאמולטור המקומי:
firebase emulators:start

התקנה ובדיקה באמצעות פרויקט Firebase אמיתי

אפשר להתקין את התוסף בפרויקט Firebase אמיתי. מומלץ להשתמש בפרויקט בדיקה לצורך הבדיקה. מומלץ להשתמש בתהליך העבודה הזה לבדיקה אם אתם רוצים לבדוק את התהליך מקצה לקצה של התוסף, או אם הטריגר של התוסף עדיין לא נתמך בחבילת הסימולטורים של Firebase (ראו האפשרות 'סימולטור של תוספים'). בשלב הזה, בתוכנות ההדמיה יש תמיכה בפונקציות שמופעל בהן טריגר של בקשת HTTP ובפונקציות שמופעל בהן טריגר של אירוע ברקע ב-Cloud Firestore, ב-Realtime Database וב-Pub/Sub.

  1. יוצרים ספרייה חדשה במערכת המארחת ומקשרים אותה לפרויקט Firebase באמצעות firebase init.
cd ..
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. לאחר מכן, מריצים את firebase ext:install מהספרייה הזו כדי להתקין את התוסף. מחליפים את /path/to/extension בנתיב המוחלט לספרייה שמכילה את קובץ ה-extension.yaml. הפקודה הזו מפעילה את תהליך ההתקנה של התוסף ויוצרת קובץ .env שמכיל את ההגדרות שלכם, לפני שההגדרות מועברות אל Firebase או אל המהדמנים.
firebase ext:install /path/to/extension
  • מכיוון שאתם רוצים לפרוס ישירות ל-Firebase ולהשתמש ב-Google Cloud Secret Manager, עליכם להפעיל את Secret Manager API לפני שתתקינו את התוסף.
  1. פורסים את האפליקציה בפרויקט Firebase.
firebase deploy

בדיקת התוסף

  1. אחרי שמריצים את firebase deploy או את firebase emulators:start, עוברים לכרטיסייה Firestore במסוף Firebase או בתצוגת האינטרנט של המהדמנים, לפי הצורך.
  2. הוספת מסמך לאוסף שצוין בשדה x ובשדה y. במקרה כזה, המסמכים המעודכנים נמצאים ב-u/{uid} עם שדה x של xv ושדה y של yv.

המסך של אמולטור Firebase להוספת רשומה ב-Firestore

  1. אם התוסף הוטמע בהצלחה, הוא יוצר שדה חדש בשם hash במסמך אחרי שמשמרים את שני השדות.

מסך של מסד נתונים ב-Firestore מאמולטור שבו מוצג גיבוב שנוסף

8. כל הכבוד!

הפונקציה הראשונה של Cloud Functions הומרתה בהצלחה לתוסף ל-Firebase!

הוספתם קובץ extension.yaml והגדרתם אותו כך שמפתחים יוכלו לבחור איך לפרוס את התוסף. לאחר מכן יצרתם מסמכי עזרה למשתמש עם הנחיות למפתחים של התוסף לגבי הפעולות שהם צריכים לבצע לפני הגדרת התוסף, וגם לגבי השלבים שהם עשויים להידרש לבצע אחרי התקנת התוסף.

עכשיו אתם יודעים מהם השלבים העיקריים שצריך לבצע כדי להמיר פונקציית Firebase לתוסף Firebase שניתן להפצה.

מה השלב הבא?